Files
kodekloud-engineer/100 - days of devops/devops-84.md

161 lines
6.4 KiB
Markdown

# Assignment
The Nautilus DevOps team needs to copy data from the jump host to all application servers in Stratos DC using Ansible. Execute the task with the following details:
a. Create an inventory file /home/thor/ansible/inventory on jump_host and add all application servers as managed nodes.
b. Create a playbook /home/thor/ansible/playbook.yml on the jump host to copy the /usr/src/dba/index.html file to all application servers, placing it at /opt/dba.
Note: Validation will run the playbook using the command ansible-playbook -i inventory playbook.yml. Ensure the playbook functions properly without any extra arguments.
# Solution
# Ansible Inventory + Playbook — copy `index.html` to all App Servers
Set up the jump host so `ansible-playbook -i inventory playbook.yml` copies
`/usr/src/dba/index.html` to `/opt/dba` on **all three** Stratos DC application servers — 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 > /home/thor/ansible/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 > /home/thor/ansible/playbook.yml <<'EOF'
---
- name: Copy index.html to all application servers
hosts: all
become: yes
tasks:
- name: Ensure /opt/dba exists
ansible.builtin.file:
path: /opt/dba
state: directory
mode: '0755'
- name: Copy index.html to /opt/dba
ansible.builtin.copy:
src: /usr/src/dba/index.html
dest: /opt/dba/index.html
mode: '0644'
EOF
```
## How it works
### The inventory — all three managed nodes
Each app server in Stratos DC gets its own line under the `[app_servers]` group, keyed by the
**server name from the wiki** (`stapp01`, `stapp02`, `stapp03`). Those names resolve from the jump
host, so the inventory name doubles as the connection target — **no `ansible_host` needed**. Confirm
with `getent hosts stapp01` if you want certainty.
| Variable | Purpose |
|----------|---------|
| `ansible_user` | SSH username, different per server. |
| `ansible_ssh_pass` | SSH password (these servers use password auth, not keys). |
| `ansible_become_pass` | The **sudo** password, needed because the playbook uses `become: yes`. |
| `ansible_connection` | Transport plugin — set once for the whole group via `[app_servers:vars]`. |
> **Verify every credential against your lab's wiki.** The user/password pairs shown follow the
> common Stratos DC pattern, but treat them as values to confirm rather than facts.
The `[app_servers:vars]` block sets `ansible_connection` once for all members instead of repeating
it on every line — the same result, less duplication.
Because validation runs the bare command (no `-u`, `-k`, `-K`, or `--private-key`), **all**
connection *and* escalation details must live in the inventory.
### Why `become: yes` is required here
`/opt/dba` is root-owned. The SSH users (`tony`, `steve`, `banner`) can't write there directly, so
the play escalates to root with `become: yes`. That in turn means sudo may demand a password —
which is why `ansible_become_pass` is set per host in the inventory. Without it, the run would hang
or fail with "Missing sudo password," and you can't pass `-K` because no extra arguments are
allowed.
> If your lab's app-server users have **passwordless** sudo, `ansible_become_pass` is harmless and
> simply unused. Including it covers both cases.
### The two tasks
1. **`ansible.builtin.file` with `state: directory`** — guarantees `/opt/dba` exists before the
copy. The `copy` module won't create missing parent directories, so if `/opt/dba` weren't already
present the copy would fail with "Destination directory does not exist." This task makes the
playbook robust either way, and it's idempotent (reports `ok` when the directory already exists).
2. **`ansible.builtin.copy`** — transfers the file:
- **`src: /usr/src/dba/index.html`** — a path on the **control node** (the jump host). The `copy`
module reads from the controller by default; that's exactly what "copy data from the jump host"
means here. (Copying between two paths on the *remote* machine would instead need
`remote_src: yes` — not the case here.)
- **`dest: /opt/dba/index.html`** — the explicit target path. Writing the filename out is clearer
than relying on `dest: /opt/dba/` directory-expansion behavior.
- **`mode: '0644'`** — quoted so YAML treats it as a string; unquoted octal like `0644` is a
classic misparse.
### Why `hosts: all`
The inventory contains only the three app servers, so `all` targets exactly them — and it can't
break if the group name and the playbook's `hosts:` value ever drift apart. Using
`hosts: app_servers` also works given this inventory; `all` is simply the more failure-proof
choice.
### Host key checking
First-time SSH connections can fail on host-key verification. The safest fix — no extra
command-line arguments needed during validation — is an `ansible.cfg` beside the playbook:
```bash
cat > /home/thor/ansible/ansible.cfg <<'EOF'
[defaults]
host_key_checking = False
EOF
```
`sshpass` must also be installed on the jump host for password auth; it usually is, and Ansible's
error names it explicitly if not.
## Verify
```bash
cd /home/thor/ansible
# All three hosts listed
ansible-inventory -i inventory --list
# Connectivity + credentials across every server at once
ansible -i inventory all -m ping
# The actual validation command
ansible-playbook -i inventory playbook.yml
# Confirm the file landed on all three
ansible -i inventory all -b -m command -a "ls -l /opt/dba/index.html"
```
Expected — `ping` returning `SUCCESS`/`pong` for stapp01, stapp02, and stapp03; the playbook
finishing with `failed=0` for all three hosts; and the final check listing `/opt/dba/index.html` on
each server.
> `UNREACHABLE` means a hostname didn't resolve or credentials don't match the wiki. "Missing sudo
> password" means `ansible_become_pass` is absent or wrong for that host. Permission denied on the
> copy means `become: yes` didn't take effect.