← Back to posts

Automating AD Computer Object Deletion on Linux Decommission

How to use Ansible and adcli to safely remove a Linux server's computer object from Active Directory during decommissioning.

Case Snapshot

Situation

During Ansible-driven lifecycle automation and controlled release windows, this case came from work related to "Automating AD Computer Object Deletion on Linux Decommission."

Issue

Needed a repeatable way to use Ansible and adcli to safely remove a Linux server's computer object from Active Directory during decommissioning.

Solution

Implemented a practical runbook/automation pattern with clear safety checks, execution steps, and verification points.

Used In

Used in Ansible playbook pipelines for provisioning, patching, and decommissioning with audit requirements.

Impact

Reduced manual steps and change variance while improving deployment consistency.

Situation

When you decommission a Linux server that was integrated into an Active Directory (AD) domain, simply deleting the virtual machine is not enough. You leave behind a “ghost” computer object in AD. Over time, this clutters the directory, consumes licenses in identity management systems, and poses a minor security risk.

To achieve full end-to-end lifecycle automation, your decommissioning playbook should clean up AD. Here is how to do it using adcli via Ansible.

Task 1 – The Concept

The adcli command-line tool is excellent for managing AD joins on Linux. While it’s usually run on the server being joined, you can also run it from a centralized management node (or Ansible control node) to delete other computers from the domain, provided you have the right credentials.

Task 2 – The Ansible Playbook

We use the ansible.builtin.command module to execute adcli delete-computer.

Because we don’t want to leave plaintext passwords in logs or process lists, we pass the password via standard input (stdin) and use the --stdin-password flag.

- name: "Delete computer object from AD via management console"
  ansible.builtin.command: >-
    adcli delete-computer
    --domain={{ realmd_default_realm }}
    --login-user={{ realmd_join_user | quote }}
    --stdin-password
    {{ target_computer_name }}
  args:
    stdin: "{{ realmd_join_user_pass }}"
  delegate_to: localhost
  become: false # Run as the Ansible user on the control node
  register: __adcli_delete_output
  failed_when:
    - __adcli_delete_output.rc != 0
    - '"Couldn''t find" not in __adcli_delete_output.stderr' # Ignore if already deleted
  changed_when: __adcli_delete_output.rc == 0

Why This Implementation is Robust

  1. delegate_to: localhost: You don’t run this command on the server being decommissioned (which might already be offline or inaccessible). You run it from the Ansible control node.
  2. --stdin-password: This is a critical security practice. It prevents the AD join password (realmd_join_user_pass) from being captured if someone runs ps aux while the command is executing.
  3. Idempotency / Graceful Failure (failed_when): If the playbook is run twice, or if a sysadmin already manually deleted the object, adcli will return a non-zero exit code because it can’t find the computer. We intercept this error: if the stderr complains that it "Couldn't find" the object, we treat the task as successful, because the desired state (object is gone) is achieved.

Architecture Diagram

Automating AD Computer Object Deletion on Linux Decommission execution diagram

This diagram supports Automating AD Computer Object Deletion on Linux Decommission and highlights where controls, validation, and ownership boundaries sit in the workflow.

Post-Specific Engineering Lens

For this post, the primary objective is: Increase automation reliability and reduce human variance.

Implementation decisions for this case

  • Chose a staged approach centered on ansible to avoid high-blast-radius rollouts.
  • Used active-directory checkpoints to make regressions observable before full rollout.
  • Treated decommission documentation as part of delivery, not a post-task artifact.

Practical command path

These are representative execution checkpoints relevant to this post:

ansible-playbook site.yml --limit target --check --diff
ansible-playbook site.yml --limit target
ansible all -m ping -o

Validation Matrix

Validation goalWhat to baselineWhat confirms success
Functional stabilitymanual steps, execution time, and failure retry countansible-playbook --check --diff highlights only intended drift
Operational safetyrollback ownership + change windowidempotency run returns changed=0 for stable hosts
Production readinessmonitoring visibility and handoff notesrollback playbook is executable without ad-hoc edits

Failure Modes and Mitigations

Failure modeWhy it appears in this type of workMitigation used in this post pattern
Inventory scope errorWrong hosts receive a valid but unintended changeUse explicit host limits and pre-flight host list confirmation
Role variable driftDifferent environments behave inconsistentlyPin defaults and validate required vars in CI
Undocumented manual stepAutomation appears successful but remains incompleteMove manual steps into pre/post tasks with assertions

Recruiter-Readable Impact Summary

  • Scope: convert manual runbooks into deterministic automation.
  • Execution quality: guarded by staged checks and explicit rollback triggers.
  • Outcome signal: repeatable implementation that can be handed over without hidden steps.