I used to re-run is_absent.yml and get annoyed that it still said changed. That file is not the idempotency test. Present, twice, is the test. Absent is the removal, and two of its tasks are marked changed because they are the removal.
roles/install_harbor/tasks/is_absent.yml stops and disables the harbor unit with failed_when: false. The unit may already be gone. That is not a failure.
It then slurps harbor_runtime_marker and sets harbor_remove_runtime from that file, not from the inventory. I had a host where the inventory said podman and the marker said docker. Removal has to follow the marker. If I follow the inventory, I run the wrong compose command against a registry that is still up.
Docker removal is docker compose down in harbor_install_dir, only if docker-compose.yml exists, changed_when: true. Podman removal is harbor_podman_compose_bin -p harbor_compose_project -f podman-compose.yml down, with DOCKER_HOST set to the podman socket, changed_when: true. I marked those changed because down is the work. A second absent run may still report them if the compose file is still there. I do not chase that. I do not run absent twice and call the role broken.
The units that go away are harbor.service, harbor-cert-sync.path, and harbor-cert-sync.service, then a daemon reload. The generated files that go away are docker-compose.yml, podman-compose.yml, .installed_version, and .installed_runtime. harbor.yml stays. The comment in the file says the next deploy regenerates its configuration and reloads its images. I left harbor.yml because the values in it are the ones I set, and the template copy on the next present run is force: false.
The install directory is removed only when harbor_purge_install is true. The data volume is emptied only when harbor_purge_data is true, and the role refuses to empty /etc, /usr, /var, /home, /root, /opt, /boot, or /tmp. The volume is usually a mount. Removing the mount point fails and the mount stays, so the role lists the entries and deletes those, not the mount.
Upgrade is the other place I mark a stop as changed. upgrade.yml runs docker compose down with changed_when: true before it moves the old install aside. The database copy and the mv use creates, so a repeated upgrade to the same version does not copy the database again or move the directory again. The down still reports changed. That is a stop, not a drift.
If the second present run is dirty, I am in is_present.yml, not here.
github.com/noahfarshad/harbor-airgap-kit, roles/install_harbor/tasks/is_absent.yml.
