7.7 KiB
Assignment
The Nautilus DevOps team had a discussion about, how they can train different team members to use Ansible for different automation tasks. There are numerous ways to perform a particular task using Ansible, but we want to utilize each aspect that Ansible offers. The team wants to utilise Ansible's conditionals to perform the following task:
An inventory file is already placed under /home/thor/ansible directory on jump host, with all the Stratos DC app servers included.
Create a playbook /home/thor/ansible/playbook.yml and make sure to use Ansible's when conditionals statements to perform the below given tasks.
Copy blog.txt file present under /usr/src/data directory on jump host to App Server 1 under /opt/data directory. Its user and group owner must be user tony and its permissions must be 0644 .
Copy story.txt file present under /usr/src/data directory on jump host to App Server 2 under /opt/data directory. Its user and group owner must be user steve and its permissions must be 0644 .
Copy media.txt file present under /usr/src/data directory on jump host to App Server 3 under /opt/data directory. Its user and group owner must be user banner and its permissions must be 0644.
NOTE: You can use ansible_nodename variable from gathered facts with when condition. Additionally, please make sure you are running the play for all hosts i.e use - hosts: all.
Note: Validation will try to run the playbook using command ansible-playbook -i inventory playbook.yml, so please make sure the playbook works this way without passing any extra arguments.
Solution
Ansible Playbook — per-server copies using when conditionals
Create /home/thor/ansible/playbook.yml so ansible-playbook -i inventory playbook.yml copies a
different file to each app server, selected with when conditionals on a single
hosts: all play — with no extra arguments.
Note: this is an Ansible task, not Kubernetes — no manifests to pipe into
kubectl. The heredoc below writes the playbook.
Step 0 — Confirm the inventory hostnames
The conditionals branch on the hostnames as written in your inventory, so read them first:
cat /home/thor/ansible/inventory
Use exactly the names it lists (stapp01, stapp02, stapp03 in the standard setup) in the
when: lines below. Also confirm ansible_become_pass is present, since the play needs root and
-K can't be passed.
Step 1 — Playbook
cat > /home/thor/ansible/playbook.yml <<'EOF'
---
- name: Copy files to app servers using conditionals
hosts: all
become: yes
tasks:
- name: Copy blog.txt to App Server 1
ansible.builtin.copy:
src: /usr/src/data/blog.txt
dest: /opt/data/blog.txt
owner: tony
group: tony
mode: '0644'
when: inventory_hostname == "stapp01"
- name: Copy story.txt to App Server 2
ansible.builtin.copy:
src: /usr/src/data/story.txt
dest: /opt/data/story.txt
owner: steve
group: steve
mode: '0644'
when: inventory_hostname == "stapp02"
- name: Copy media.txt to App Server 3
ansible.builtin.copy:
src: /usr/src/data/media.txt
dest: /opt/data/media.txt
owner: banner
group: banner
mode: '0644'
when: inventory_hostname == "stapp03"
EOF
How it works
The conditional pattern
With hosts: all, every task is evaluated against every host. The when: clause is what
narrows each one:
- On stapp01, task 1's condition is true → it runs; tasks 2 and 3 are false → skipped.
- On stapp02, only task 2 runs. On stapp03, only task 3.
So the play visits all three servers, and each executes exactly the one task meant for it. The
PLAY RECAP will show roughly ok=2 changed=1 skipped=2 per host — skipped tasks are expected
and correct here, not a failure.
This is the opposite structural choice from writing three separate plays (hosts: stapp01, etc.).
Both produce the same end state; the task explicitly asks for the conditional form, which is what
you'd reach for when the differences are small and you want one play covering the fleet.
Why inventory_hostname
inventory_hostname is a magic variable holding the host's name exactly as written in the
inventory file. That makes it the most reliable thing to branch on:
- You can read its value directly (
cat inventory) — no guessing, no remote lookup. - It doesn't depend on fact gathering.
- It can't drift from what the inventory says, because it is what the inventory says.
The alternative — ansible_nodename: the task's note mentions it, and it also works, but it's a
gathered fact reporting whatever name the operating system is configured with. That's often a
fully-qualified domain name rather than the short one, and the exact value depends on how the lab's
hosts are configured. Comparing it against the wrong string matches nothing, every task silently
skips, and the playbook reports success with no work done — a failure mode that looks like a
pass until you inspect the files.
If you prefer that route, get the real values first rather than assuming them:
ansible -i inventory all -m setup -a "filter=ansible_nodename"
then paste the exact output into the conditions:
when: ansible_nodename == "<exact value printed for stapp01>"
Either variable satisfies the requirement — the task says you can use ansible_nodename, not that
you must, and validation checks the resulting files rather than which variable you branched on.
inventory_hostname is simply the version with nothing left to verify.
The copy task
src: /usr/src/data/blog.txt— a path on the control node (the jump host).copyreads from the controller by default, which is exactly "copy from jump host to app server." (Copying between two paths on the remote machine would needremote_src: yes— not the case here.)dest: /opt/data/blog.txt— the explicit target path, filename included. Clearer than relying ondest: /opt/data/directory-expansion.owner/group— the required per-server user. Hardcoded here because each task is already pinned to one host by itswhen:, so the literal value is unambiguous and matches the requirement text directly.mode: '0644'— quoted. Unquoted octal like0644is a classic YAML misparse that silently yields wrong permissions.
Why become: yes
/opt/data is root-owned, so writing there requires escalation — and setting owner/group runs
chown, which is privileged regardless. The play escalates once at play level, with the sudo
password supplied by ansible_become_pass from the inventory.
If
/opt/datadoesn't already exist on the targets,copyfails with "Destination directory does not exist" — it won't create missing parents. Add afile/state: directorytask before the copies if you hit that.
Verify
cd /home/thor/ansible
# Connectivity
ansible -i inventory all -m ping
# The actual validation command
ansible-playbook -i inventory playbook.yml
# Each file, ownership and permissions
ansible -i inventory stapp01 -b -m command -a "ls -l /opt/data/blog.txt"
ansible -i inventory stapp02 -b -m command -a "ls -l /opt/data/story.txt"
ansible -i inventory stapp03 -b -m command -a "ls -l /opt/data/media.txt"
Expected — playbook failed=0 with skipped=2 on each host (normal for this pattern), and ls -l
showing -rw-r--r-- with tony tony, steve steve, and banner banner respectively.
If every task shows
skippedand no files appear, thewhen:strings don't match the actual hostnames — recheck them againstcat inventory. That's the classic failure mode for conditionals: a run that reports success while doing nothing.