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

7.3 KiB

Assignment

The Nautilus DevOps team wants to install and set up a simple httpd web server on all app servers in Stratos DC. Additionally, they want to deploy a sample web page for now using Ansible only. Therefore, write the required playbook to complete this task. Find more details about the task below.

We already have an inventory file under /home/thor/ansible directory on jump host. Create a playbook.yml under /home/thor/ansible directory on jump host itself. Using the playbook, install httpd web server on all app servers. Additionally, make sure its service should up and running. Using blockinfile Ansible module add some content in /var/www/html/index.html file. Below is the content:

Welcome to XfusionCorp! This is Nautilus sample file, created using Ansible! Please do not modify this file manually! 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 0777 on all app servers.

Note: i. 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. ii. Do not use any custom or empty marker for blockinfile module.

Solution

Ansible Playbook — install httpd + deploy sample page on all App Servers

Create /home/thor/ansible/playbook.yml so ansible-playbook -i inventory playbook.yml installs httpd, starts it, and writes a sample index.html with specific ownership and permissions — 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

The task states an inventory is already present. Do not recreate or overwrite it — just confirm what's in it:

cat /home/thor/ansible/inventory

Check that it lists the app servers with their connection variables. If it lacks ansible_become_pass and the app-server users require a sudo password, add that per host — the playbook needs privilege escalation and you can't pass -K at runtime.

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: Add sample content to index.html
      ansible.builtin.blockinfile:
        path: /var/www/html/index.html
        create: yes
        block: |
          Welcome to XfusionCorp!
          This is  Nautilus sample file, created using Ansible!
          Please do not modify this file manually!
        owner: apache
        group: apache
        mode: '0777'
EOF

How it works

Why become: yes

Every task here is privileged: installing a package writes to system directories and the RPM database, managing a systemd service requires root, and /var/www/html/ is root-owned. The SSH users are unprivileged, so the whole play escalates with become: yes at the play level rather than repeating it per task.

Since validation runs the bare command with no -K, the sudo password must come from ansible_become_pass in the existing inventory. If the play fails with "Missing sudo password," that variable is what's missing.

Task 1 — install httpd

ansible.builtin.yum with state: present installs the package if absent and does nothing otherwise, making it idempotent (changed on first run, ok after). state: latest would upgrade on every run, which isn't what's asked.

Task 2 — service up and running

ansible.builtin.service handles both halves of the requirement:

  • state: started — ensures httpd is running right now.
  • enabled: yes — ensures it starts automatically on boot.

These are independent settings; "up and running" is satisfied by started, and enabled makes it durable across reboots. Including both is the standard, complete answer.

Task 3 — blockinfile with default markers

blockinfile inserts (or updates) a block of text delimited by marker comments, so the block can be managed idempotently on re-runs — Ansible finds its own markers and replaces the content between them rather than appending duplicates.

  • No marker: parameter is specified, per note (ii). Omitting it uses the default, # {mark} ANSIBLE MANAGED BLOCK, which produces # BEGIN ANSIBLE MANAGED BLOCK and # END ANSIBLE MANAGED BLOCK around your text. Supplying a custom or empty marker would violate the requirement — and an empty marker would break idempotency, since Ansible could no longer locate its block.

  • create: yes — creates /var/www/html/index.html if it doesn't exist. A fresh httpd install typically has no index.html, and without this flag blockinfile would fail with "Destination does not exist."

  • block: | — the literal block scalar preserves the text exactly, including line breaks. Note the double space in This is Nautilus sample file — that's reproduced verbatim from the requirement, since validation may compare the content character for character.

  • owner: apache / group: apache — the required ownership. apache is the user httpd runs as on RHEL/CentOS-family systems, and the account exists only after the httpd package is installed — which is why the install task must come first. Reordering would fail with "chown failed: failed to look up user apache."

  • mode: '0777' — quoted so YAML parses it as a string. Unquoted octal (0777) is a classic misparse that silently yields the wrong permissions.

Ordering matters

The three tasks form a dependency chain: install creates the apache user and /var/www/html/; service brings httpd up; blockinfile then writes into a directory that exists and assigns ownership to a user that exists. Ansible runs tasks top to bottom, so this order is the correctness requirement, not just style.

hosts: all

The inventory holds the app servers, so all targets exactly them and can't break if the group name and the playbook's hosts: value drift apart.

Verify

cd /home/thor/ansible

# Connectivity first
ansible -i inventory all -m ping

# The actual validation command
ansible-playbook -i inventory playbook.yml

# httpd installed and running
ansible -i inventory all -b -m command -a "systemctl is-active httpd"

# File content, ownership, and permissions
ansible -i inventory all -b -m command -a "ls -l /var/www/html/index.html"
ansible -i inventory all -b -m command -a "cat /var/www/html/index.html"

Expected — the playbook finishing with failed=0; systemctl is-active httpd returning active on each server; ls -l showing -rwxrwxrwx with apache apache; and cat showing the three lines wrapped in # BEGIN ANSIBLE MANAGED BLOCK / # END ANSIBLE MANAGED BLOCK.

Those BEGIN/END marker lines are expected and correct — they're the default markers required by note (ii). A "failed to look up user apache" error means the blockinfile task ran before httpd was installed. "Missing sudo password" means the inventory needs ansible_become_pass.