# 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.