Introduction
One playbook can select different work for each host without duplicating the entire task list. You will combine Boolean policy inputs with OS facts, process every item in a structured list, and use real registered inspection results for an independent approval decision. Real files and explicit cleanup will make both selected and skipped behavior observable.
Select Tasks with Boolean Inputs and Facts
In this step, you will make one reusable playbook act differently on the two hosts and explicitly remove outputs when policy becomes disabled.
The prepared environment has a separate approval input at /home/automation/maintenance-approved on web1 only. Leave that input unchanged for now; the first policy decision uses a Boolean variable and discovered OS facts instead.
Enter the controller project:
cd /home/labex/project
Host variable files associate data with each inventory name. Write YAML Boolean values without quotes so they remain true/false values rather than strings:
cat > host_vars/web1.yml <<'YAML'
maintenance_enabled: true
YAML
cat > host_vars/web2.yml <<'YAML'
maintenance_enabled: false
YAML
The when keyword evaluates a Boolean expression for the current host. Do not wrap the expression in Jinja braces. Here and requires both an enabled policy and Debian-family facts. not reverses a Boolean and or allows either reason to select the cleanup branch.
cat > maintenance.yml <<'YAML'
- name: Apply a host-specific maintenance policy
hosts: all
gather_facts: true
tasks:
- name: Prepare the common policy directory
ansible.builtin.file:
path: /home/automation/maintenance-policy
state: directory
mode: "0750"
- name: Record enabled Debian maintenance
ansible.builtin.copy:
dest: /home/automation/maintenance-policy/enabled.txt
content: "Enabled maintenance\n"
mode: "0640"
when: maintenance_enabled and ansible_facts.os_family == "Debian"
- name: Remove the marker when the policy is disabled
ansible.builtin.file:
path: /home/automation/maintenance-policy/enabled.txt
state: absent
when: not maintenance_enabled or ansible_facts.os_family != "Debian"
YAML
The common directory exists on both hosts. Only enabled Debian hosts receive the marker; the complementary absent task removes it when policy is disabled. A skipped creation task alone would leave an old marker behind, so cleanup is a separate desired-state task.
Validate and apply the policy:
ansible-playbook maintenance.yml --syntax-check
ansible-playbook maintenance.yml
The output should show different changed/ok/skipped decisions for web1 and web2. Inspect actual marker metadata on both hosts:
ansible all -m ansible.builtin.stat -a 'path=/home/automation/maintenance-policy/enabled.txt'
Stat should report exists true on web1 and false on web2. To exercise cleanup, override the Boolean for both hosts. JSON extra variables preserve the Boolean false type; the enclosing single quotes protect the JSON from shell interpretation:
ansible-playbook maintenance.yml -e '{"maintenance_enabled":false}'
ansible all -m ansible.builtin.stat -a 'path=/home/automation/maintenance-policy/enabled.txt'
Both markers should now be absent. Restore the saved per-host policy by running without the temporary override:
ansible-playbook maintenance.yml
Web1 should regain the marker while web2 remains disabled. The override did not edit the saved host variable files.
Apply Every Item in a Structured Loop
In this step, you will produce a real maintenance check record for every declared item on the enabled host.
A YAML list contains multiple entries introduced with a dash. Each entry below is a mapping with name and purpose fields. Group variables for all make this list available to both hosts, while the existing Boolean condition still decides which host receives outputs.
cat > group_vars/all.yml <<'YAML'
maintenance_checks:
- name: disk
purpose: Inspect disk space
- name: logs
purpose: Inspect application logs
- name: packages
purpose: Inspect package state
YAML
loop repeats one task for each list entry. Within an iteration, item.name and item.purpose read that entry's fields. The when condition is evaluated for each host and item. Append these tasks to the existing playbook, keeping the indentation aligned with the earlier tasks:
cat >> maintenance.yml <<'YAML'
- name: Prepare checks only for enabled Debian hosts
ansible.builtin.file:
path: /home/automation/maintenance-policy/checks
state: directory
mode: "0750"
when: maintenance_enabled and ansible_facts.os_family == "Debian"
- name: Write every declared maintenance check
ansible.builtin.copy:
dest: "/home/automation/maintenance-policy/checks/{{ item.name }}.txt"
content: |
host={{ inventory_hostname }}
check={{ item.name }}
purpose={{ item.purpose }}
mode: "0640"
loop: "{{ maintenance_checks }}"
when: maintenance_enabled and ansible_facts.os_family == "Debian"
- name: Remove check outputs when the policy is disabled
ansible.builtin.file:
path: /home/automation/maintenance-policy/checks
state: absent
when: not maintenance_enabled or ansible_facts.os_family != "Debian"
YAML
The cleanup task removes the entire check directory on disabled hosts, including old outputs from an earlier enabled policy. The loop writes stable content and explicit permissions on enabled hosts.
Apply the extended playbook:
ansible-playbook maintenance.yml
Web1 should have three loop results, one per check. Web2 should skip those writes and retain no checks directory. Read all three actual web1 artifacts rather than treating one successful item as evidence for the whole list:
ansible web1 -m ansible.builtin.command -a 'cat /home/automation/maintenance-policy/checks/disk.txt /home/automation/maintenance-policy/checks/logs.txt /home/automation/maintenance-policy/checks/packages.txt'
The output should identify host=web1 for each record and show the corresponding check name and purpose. Inspect the disabled host independently:
ansible web2 -m ansible.builtin.stat -a 'path=/home/automation/maintenance-policy/checks'
Its stat result should report exists false. These records declare maintenance check instructions; they do not pretend to execute the disk, log or package inspections themselves.
Branch on a Registered Inspection Result
In this step, you will use a real stat result as an independent decision input and confirm that the complete policy is stable on repeat.
The approval input is separate from maintenance_enabled. A stat task always inspects each host, then register stores that host's result. Its stat.exists Boolean safely distinguishes an existing input from a missing path. Append the inspection and complementary output tasks:
cat >> maintenance.yml <<'YAML'
- name: Inspect the independent approval input
ansible.builtin.stat:
path: /home/automation/maintenance-approved
register: approval_info
- name: Record an existing approval input
ansible.builtin.copy:
dest: /home/automation/maintenance-policy/approved.txt
content: "Approval input exists\n"
mode: "0640"
when: approval_info.stat.exists
- name: Remove the output when approval input is absent
ansible.builtin.file:
path: /home/automation/maintenance-policy/approved.txt
state: absent
when: not approval_info.stat.exists
YAML
The copy task consumes the registered exists value. The absent task covers its opposite so a removed approval input would also remove stale approval output. This branch is based on inspected state rather than a fixed debug message.
Run the complete policy:
ansible-playbook maintenance.yml
The prepared approval input exists on web1 only, so only web1 should have approved.txt. Inspect both actual output paths:
ansible all -m ansible.builtin.stat -a 'path=/home/automation/maintenance-policy/approved.txt'
Web1 should report exists true and web2 false. Repeat the playbook:
ansible-playbook maintenance.yml
Both recap rows should report changed=0, failed=0 and unreachable=0. Skipped tasks are expected where a condition is false; every required loop item and selected output must still exist with the correct content.
Summary
You used per-host Boolean data and discovered facts to select tasks, processed each mapping in a loop, and branched on registered stat metadata. Complementary absent tasks removed stale outputs instead of assuming a skipped creation undoes old state. Both hosts converged to their distinct policy outcomes without unnecessary repeated changes.


