# 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) ```bash 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 ```bash 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: ```yaml 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. > `group` uses the same value because these systems create a matching primary group for each user > (user `tony` → group `tony`), which is the Linux default. If your lab's groups differ, set `group` > explicitly per host instead. The alternative — hardcoding with conditionals — would look like this and is strictly worse: ```yaml # 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 like `0755` is 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: ```bash cat > ~/playbook/ansible.cfg <<'EOF' [defaults] host_key_checking = False EOF ``` `sshpass` must also be installed on the jump host for password auth. ## Verify ```bash 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_pass` absent or wrong. A chown failure ⇒ `become: yes` > didn't take. Wrong owner ⇒ check that `ansible_user` in the inventory matches the required owner > for that host.