6.5 KiB
Assignment
The Nautilus DevOps team is testing various Ansible modules on servers in Stratos DC. They're currently focusing on file creation on remote hosts using Ansible. Here are the details:
a. Create an inventory file ~/playbook/inventory on jump host and include all app servers.
b. Create a playbook ~/playbook/playbook.yml to create a blank file /opt/app.txt on all app servers.
c. Set the permissions of the /opt/app.txt file to 0755.
d. Ensure the user/group owner of the /opt/app.txt file is tony on app server 1, steve on app server 2 and banner on app server 3.
Note: Validation will execute the playbook using the command ansible-playbook -i inventory playbook.yml, so ensure the playbook functions correctly without any additional arguments.
Solution
Ansible Inventory + Playbook — create /opt/app.txt with per-host ownership
Set up the jump host so ansible-playbook -i inventory playbook.yml creates a blank
/opt/app.txt on all three Stratos DC app servers, mode 0755, owned by a different user on
each host — with no extra arguments.
Note: this is an Ansible task, not Kubernetes — no manifests to pipe into
kubectl. The heredocs below write the two files.
Step 1 — Inventory (all three app servers)
cat > ~/playbook/inventory <<'EOF'
[app_servers]
stapp01 ansible_user=tony ansible_ssh_pass=Ir0nM@n ansible_become_pass=Ir0nM@n
stapp02 ansible_user=steve ansible_ssh_pass=Am3ric@ ansible_become_pass=Am3ric@
stapp03 ansible_user=banner ansible_ssh_pass=BigGr33n ansible_become_pass=BigGr33n
[app_servers:vars]
ansible_connection=ssh
EOF
Step 2 — Playbook
cat > ~/playbook/playbook.yml <<'EOF'
---
- name: Create /opt/app.txt on all app servers
hosts: all
become: yes
tasks:
- name: Create blank file with correct mode and ownership
ansible.builtin.file:
path: /opt/app.txt
state: touch
mode: '0755'
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
EOF
How it works
The neat trick: {{ ansible_user }} for per-host ownership
Requirement (d) wants a different owner per server — tony on stapp01, steve on stapp02,
banner on stapp03. Notice those are exactly the SSH users already defined per host in the
inventory. So instead of writing three separate tasks with when: conditionals, the playbook
references the variable:
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
Ansible evaluates ansible_user per host during the play, so it resolves to tony on stapp01,
steve on stapp02, and banner on stapp03 automatically. One task, three correct outcomes — and it
stays correct if a host is added or a username changes, since the inventory is the single source of
truth.
groupuses the same value because these systems create a matching primary group for each user (usertony→ grouptony), which is the Linux default. If your lab's groups differ, setgroupexplicitly per host instead.
The alternative — hardcoding with conditionals — would look like this and is strictly worse:
# NOT recommended, shown for contrast
- name: Set owner on stapp01
ansible.builtin.file:
path: /opt/app.txt
owner: tony
when: inventory_hostname == "stapp01"
# ...repeated for each host
The inventory
Each app server is keyed by its wiki server name (stapp01, stapp02, stapp03), which
resolves from the jump host — so the inventory name doubles as the connection target and no
ansible_host is needed. Confirm with getent hosts stapp01 if you want certainty.
| Variable | Purpose |
|---|---|
ansible_user |
SSH username — and the file owner, via the templating above. |
ansible_ssh_pass |
SSH password (password auth, not keys). |
ansible_become_pass |
Sudo password, needed because the play uses become: yes. |
ansible_connection |
Transport plugin, set once for the group via [app_servers:vars]. |
Verify every credential against your lab's wiki. The pairs shown follow the common Stratos DC pattern but should be confirmed rather than assumed.
Since validation runs the bare command (no -u, -k, -K), all connection and escalation
details must live in the inventory.
Why become: yes is required
/opt is root-owned, so the SSH users can't create a file there directly — the play escalates with
become: yes. Escalation is also what makes owner:/group: work at all: changing a file's
ownership requires root (chown is privileged). Without become, you'd get permission errors on
both the create and the chown.
Because sudo may prompt for a password and you can't pass -K, ansible_become_pass is set per
host in the inventory. (If the lab has passwordless sudo, the variable is simply unused — harmless
either way.)
The file module options
state: touch— creates the file if absent, leaving it blank. Exactly "create a blank file."mode: '0755'— quoted so YAML reads it as a string; unquoted octal like0755is a classic misparse that silently produces the wrong permissions.owner/group— applied by the module after creation, which is why root privileges are needed.
Note state: touch bumps timestamps on every run, so re-runs report changed rather than ok.
That's fine here; add modification_time: preserve and access_time: preserve if you want strict
idempotency.
Host key checking
First-time SSH connections can fail on host-key verification. The safest fix — needing no extra
command-line arguments — is an ansible.cfg beside the playbook:
cat > ~/playbook/ansible.cfg <<'EOF'
[defaults]
host_key_checking = False
EOF
sshpass must also be installed on the jump host for password auth.
Verify
cd ~/playbook
# All three hosts listed
ansible-inventory -i inventory --list
# Connectivity + credentials across every server
ansible -i inventory all -m ping
# The actual validation command
ansible-playbook -i inventory playbook.yml
# Confirm mode and per-host ownership
ansible -i inventory all -b -m command -a "ls -l /opt/app.txt"
Expected — ping returning SUCCESS for all three; the playbook finishing with failed=0; and the
final listing showing -rwxr-xr-x with owner/group tony tony on stapp01, steve steve on
stapp02, and banner banner on stapp03.
"Missing sudo password" ⇒
ansible_become_passabsent or wrong. A chown failure ⇒become: yesdidn't take. Wrong owner ⇒ check thatansible_userin the inventory matches the required owner for that host.