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:
- Install httpd — this creates the
apacheuser and the/var/www/html/directory. Both are needed by later tasks. - Start/enable the service — httpd must exist before it can be managed.
- Create the file — the directory now exists and
apacheis a valid owner. - Prepend the line — the file must exist before
lineinfilecan 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
yumwithstate: present— installs if missing, no-ops otherwise (idempotent:changedfirst run,okafter).state: latestwould upgrade on every run, which isn't what's asked.servicewithstate: startedandenabled: yes— two independent settings.started= running now;enabled= starts on boot. "Up and running" needsstarted;enabledmakes 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—BOFis 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,lineinfileappends 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: EOFfor 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— theapacheaccount exists only after the httpd package is installed, which is why task 1 must come first.mode: '0655'— quoted. Unquoted octal like0655is a classic YAML misparse that silently produces the wrong permissions. Note this is0655, not the more familiar0644or0755— 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-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: BOFis missing or misspelled. "failed to look up user apache" ⇒ a file task ran before the httpd install.