DevOpsInterviewPrep logo
← 🏗️ Declarative Infrastructure
Foundational

Ansible architecture: inventory, modules, handlers and repeatable change

Understand how an Ansible control node targets inventory and executes modules. Work through configuration change, handlers, check mode and rolling execution without assuming every task is idempotent.

TL;DR: Ansible evaluates a playbook on a control node, selects hosts from inventory and executes tasks through connection plugins and modules. Repeatability depends on task behavior and inputs. A playbook written in YAML is not automatically idempotent or safe to run across the entire fleet at once.

Inventory connects intent to actual machines

Inventory supplies hosts, groups and variables. It can be static or obtained from dynamic sources such as a cloud inventory plugin. A play selects a host pattern and defines tasks under an execution strategy. Connection plugins determine how Ansible reaches the target, commonly SSH for Linux, with other transports for Windows and network devices.

The control node is where the automation is evaluated. Managed nodes execute supported module operations and return results. Some actions are delegated or execute locally, so determine where a particular task really runs before assuming the control node's filesystem or credentials are available on the target.

The Ansible basic concepts guide defines inventory, plays, tasks and modules. Agentless operation still requires an authenticated connection and the dependencies required by the chosen modules.

rendering diagram…

A small configuration change

This excerpt assumes Linux hosts in inventory group web, privilege escalation configured by policy, Nginx already installed, and a reviewed template file. It updates configuration one host at a time. The template is a full Nginx configuration suitable for the validation command.

- name: Update the web tier configuration
  hosts: web
  become: true
  serial: 1
  tasks:
    - name: Render validated Nginx configuration
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
        validate: 'nginx -t -c %s'
      notify: Reload nginx
    - name: Apply notified changes before verification
      ansible.builtin.meta: flush_handlers
    - name: Verify local HTTP health
      ansible.builtin.uri:
        url: http://127.0.0.1/health
        status_code: 200
  handlers:
    - name: Reload nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

The example explicitly flushes handlers before the health check; otherwise a later handler could apply the change after a premature verification. The endpoint and reload behavior are assumptions to verify for the service you operate. A local HTTP check also does not prove the host has rejoined the external load balancer correctly.

Changed status drives later behavior

Many state-oriented modules compare the current state with the requested state and report whether a change occurred. A notified handler can then reload a service only when the relevant task reports a change. This avoids unnecessary restarts, but depends on accurate task results.

Task designRepeat-run behavior to establishRisk
Ensure a package is at an explicitly selected versionAlready-matching host stays unchangedRepository availability and downgrade policy still matter
Render a stable templateSame rendered bytes require no updateVolatile template values can trigger constant changes
Run an arbitrary shell commandDepends entirely on the commandAppending a line or creating users may repeat effects
Invoke an external APIDepends on API identity and retry designLost responses can cause duplicate effects

The playbook introduction distinguishes idempotent modules from playbooks that do not preserve that property. A changed_when: false statement changes reporting; it does not make the underlying command safe to repeat.

Check mode has a boundary

Check mode predicts changes for modules that support it. Tasks with unsupported behavior, runtime dependencies or external side effects may not produce a complete simulation. Inspect module support and validate in a disposable environment before relying on the output for a fleet-wide action.

Diff output can expose credentials embedded in templates. Restrict logs and use appropriate secret-handling settings. Ansible Vault encrypts selected data at rest; it does not prevent a task from printing decrypted material during execution. Distinguish that feature from HashiCorp Vault, a separate secret-management service.

Rolling changes still need failure policy

serial: 1 bounds the play's batch size. It does not automatically drain customer traffic or prove the remaining hosts have capacity. A production sequence may remove a host from service, update it, verify it and rejoin it, with an explicit decision on whether the next batch can proceed.

Choose what happens after a failed validation. Continuing through every host can turn a single configuration mistake into a fleet outage. Conversely, stopping safely requires knowing whether a failed host remains drained and who restores it. Record per-host outcomes so a retry can distinguish unchanged, completed and partially changed machines.

Check the repeatability claim

For reusable automation, roles and collections define the package boundary, caller inputs and dependency versions.

Self-check: a playbook uses shell: echo setting=true >> /etc/example.conf, and its author says a second run is safe because the task has changed_when: false. What happens?

The line is appended again. Reporting no change does not undo the write. Use a module or template that declares the required file state, preserve any unrelated configuration deliberately, and verify that two runs produce the same intended result.

Compare Ansible's host configuration role with Terraform provisioning, and use the idempotency concept to reason about retries that cross external systems.

RELATED CONCEPTS
PRACTICE THIS IN REAL QUESTIONS