Files

200 lines
7.5 KiB
Markdown

# Assignment
The Nautilus DevOps team want to install and set up a simple httpd web server on all app servers in Stratos DC. They also want to deploy a sample web page using Ansible. Therefore, write the required playbook to complete this task as per details mentioned below.
We already have an inventory file under /home/thor/ansible directory on jump host. Write a playbook playbook.yml under /home/thor/ansible directory on jump host itself. Using the playbook perform below given tasks:
Install httpd web server on all app servers, and make sure its service is up and running.
Create a file /var/www/html/index.html with content:
This is a Nautilus sample file, created using Ansible!
Using lineinfile Ansible module add some more content in /var/www/html/index.html file. Below is the content:
Welcome to Nautilus Group!
Also make sure this new line is added at the top of the file.
The /var/www/html/index.html file's user and group owner should be apache on all app servers.
The /var/www/html/index.html file's permissions should be 0655 on all app servers.
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 — httpd + sample page with `lineinfile`
Create `/home/thor/ansible/playbook.yml` so `ansible-playbook -i inventory playbook.yml` installs
httpd, starts it, and builds an `index.html` with a line prepended at the top — 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 — The inventory already exists; leave it alone
```bash
cat /home/thor/ansible/inventory
```
Confirm it lists the app servers with their connection variables, and that
`ansible_become_pass` is present — the play needs root and you can't pass `-K`.
## Step 1 — Playbook
```bash
cat > /home/thor/ansible/playbook.yml <<'EOF'
---
- name: Install httpd and deploy sample web page
hosts: all
become: yes
tasks:
- name: Install httpd package
ansible.builtin.yum:
name: httpd
state: present
- name: Start and enable httpd service
ansible.builtin.service:
name: httpd
state: started
enabled: yes
- name: Create index.html with sample content
ansible.builtin.copy:
dest: /var/www/html/index.html
content: |
This is a Nautilus sample file, created using Ansible!
owner: apache
group: apache
mode: '0655'
- name: Add welcome line at the top of index.html
ansible.builtin.lineinfile:
path: /var/www/html/index.html
line: 'Welcome to Nautilus Group!'
insertbefore: BOF
owner: apache
group: apache
mode: '0655'
EOF
```
Resulting file content:
```
Welcome to Nautilus Group!
This is a Nautilus sample file, created using Ansible!
```
## How it works
### Task order is a hard dependency
The four tasks must run in this sequence:
1. **Install httpd** — this creates the `apache` user *and* the `/var/www/html/` directory. Both are
needed by later tasks.
2. **Start/enable the service** — httpd must exist before it can be managed.
3. **Create the file** — the directory now exists and `apache` is a valid owner.
4. **Prepend the line** — the file must exist before `lineinfile` can modify it.
Reordering breaks things concretely: a `copy` before the install fails with "Destination directory
does not exist" or "failed to look up user apache"; a `lineinfile` before the `copy` has nothing to
insert into.
### Task 1 & 2 — install and run
- **`yum` with `state: present`** — installs if missing, no-ops otherwise (idempotent: `changed`
first run, `ok` after). `state: latest` would upgrade on every run, which isn't what's asked.
- **`service` with `state: started` *and* `enabled: yes`** — two independent settings.
`started` = running now; `enabled` = starts on boot. "Up and running" needs `started`; `enabled`
makes it survive reboots. Both are standard for this requirement.
### Task 3 — `copy` with inline `content`
Using `copy` with the **`content:`** parameter writes a literal string to `dest` — no source file on
the controller needed. The `|` block scalar preserves the text exactly and appends a trailing
newline, so the next task's line lands cleanly above it.
(`copy` also accepts `src:` for pushing an existing file; `content:` is the right choice when the
text is defined inline, as here.)
### Task 4 — `lineinfile` with `insertbefore: BOF`
This is the crux of requirement 3. `lineinfile` ensures a **single line** is present in a file:
- **`line: 'Welcome to Nautilus Group!'`** — the exact text to guarantee.
- **`insertbefore: BOF`** — `BOF` is a special value meaning **Beginning Of File**. It places the
line as the very first line, which is precisely "added at the top." Without it, `lineinfile`
appends to the end by default — the file would still contain both lines, but in the wrong order,
and validation would fail.
(The mirror value is `insertafter: EOF` for the end of the file.)
`lineinfile` is also **idempotent**: on re-runs it finds the line already present and reports `ok`
rather than inserting a duplicate.
### `copy` vs `lineinfile` — why both
They do different jobs. `copy` establishes the file's whole content from scratch (requirement 2);
`lineinfile` surgically inserts one line into an existing file (requirement 3). The task explicitly
asks for `lineinfile` for the second piece, so this two-step approach matches the requirements
directly rather than just writing both lines in one `copy`.
### Ownership and permissions
- **`owner: apache` / `group: apache`** — the `apache` account exists only *after* the httpd package
is installed, which is why task 1 must come first.
- **`mode: '0655'`** — **quoted**. Unquoted octal like `0655` is a classic YAML misparse that
silently produces the wrong permissions. Note this is `0655`, not the more familiar `0644` or
`0755` — copy it exactly.
Both are set on the `copy` **and** the `lineinfile` task. Setting them on the final task is what
guarantees the end state, since `lineinfile` rewrites the file; specifying them on both makes the
result deterministic regardless of which task last touched the file.
### `become: yes`
Installing packages, managing systemd services, writing under root-owned `/var/www/html/`, and
running `chown` all require root. The play escalates once at play level, with the sudo password
coming from `ansible_become_pass` in the inventory.
## Verify
```bash
cd /home/thor/ansible
# Connectivity
ansible -i inventory all -m ping
# The actual validation command
ansible-playbook -i inventory playbook.yml
# Service running and enabled
ansible -i inventory all -b -m command -a "systemctl is-active httpd"
ansible -i inventory all -b -m command -a "systemctl is-enabled httpd"
# Content order, ownership, permissions
ansible -i inventory all -b -m command -a "cat /var/www/html/index.html"
ansible -i inventory all -b -m command -a "ls -l /var/www/html/index.html"
```
Expected — playbook `failed=0`; `is-active``active` and `is-enabled``enabled`; `cat` showing
**`Welcome to Nautilus Group!` on the first line** followed by the sample-file line; and `ls -l`
showing `-rw-r-xr-x` (0655) with `apache apache`.
> The line **order** is the thing to check most carefully — if the welcome line appears at the
> bottom, `insertbefore: BOF` is missing or misspelled. "failed to look up user apache" ⇒ a file task
> ran before the httpd install.