Files

7.5 KiB

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

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

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: BOFBOF 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

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-activeactive and is-enabledenabled; 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.