A Complete Project and Series Wrap-Up
You've reached the end
The final article pulls everything into a complete project, running for real on a server, following the best practices from Article 15. Then we clean up the infrastructure and look back over the whole journey.
The project: deploying an application with a role
Apply the structure from Article 15 — a thin playbook calling a fat role:
project/
├── ansible.cfg
├── inventory.ini # target host
├── deploy.yml # playbook (thin — only calls the role)
└── roles/
└── webserver/ # role (fat — holds the logic)
├── defaults/main.yml # site_name (parameter, low priority)
├── tasks/main.yml # install nginx, enable service, render template
├── templates/index.html.j2
└── handlers/main.yml # Restart nginx (notify)
deploy.yml — concise, declarative, calls the role and passes variables:
---
- name: Deploy web app (capstone)
hosts: web
become: true
roles:
- role: webserver
vars:
site_name: "KKloud — Capstone Deploy"
Run it:
ansible-playbook deploy.yml
RUNNING HANDLER [webserver : Restart nginx] *
changed: [lab]
PLAY RECAP **********************************
lab : ok=4 changed=1 failed=0
Run it a second time to prove idempotency (Article 5):
lab : ok=4 changed=0 failed=0
changed=0 — the second run changed nothing. And curl http://<host>/ returns:
<h1>KKloud — Capstone Deploy — role webserver</h1>
One command, from a blank machine to a running web server — declarative, idempotent, reusable. This is the whole series distilled.
A real lesson from the capstone: the lab server had accumulated configuration from earlier articles (two
conf.d/*.conffiles both settingserver_tokens→ nginx reports a duplicate directive → it won't start). The lesson: accumulated state is the enemy — which is exactly why we use Ansible (describe the full desired state) and why a clean/idempotent environment matters. In a real project, a role should fully manage the configuration it owns (including deleting stray files), and molecule (Article 14) tests on a clean container to catch this class of bug.
Extending the project in practice
From this skeleton, a real project adds:
- Multiple roles:
common(users, firewall, baseline) +webserver+database+monitoring, called insite.yml. - Environment separation (Article 15):
inventories/production/andinventories/staging/with their owngroup_vars/. - Secrets via Vault (Article 10): the DB password in
group_vars/.../vault.yml. - Rolling deploy (Article 13):
serial+ a load balancer for zero-downtime. - CI:
ansible-lint+molecule(Articles 14, 15) running on every change.
🧹 Cleanup: terminate the EC2 instance
The series is ending — clean up the infrastructure so it doesn't cost money (recall the habit from the AWS series). Terminate the EC2 lab and delete the key/security group:
# Get the id from AWS (or use the tag Name=ansible-lab)
aws ec2 terminate-instances --instance-ids <INSTANCE_ID>
aws ec2 wait instance-terminated --instance-ids <INSTANCE_ID>
aws ec2 delete-key-pair --key-name <KEY_NAME>
aws ec2 delete-security-group --group-id <SG_ID>
Interesting aside: you can also use Ansible itself to clean up (the amazon.aws.ec2_instance module with state: absent) — Ansible can manage the full lifecycle of infrastructure, not just configuration. All hands-on code for the series is at github.com/nghiadaulau/ansible-series.
Series wrap-up
Seventeen articles, from "what is Ansible" to writing your own modules and designing projects:
- Foundations (Articles 0–7): the agentless architecture (ship modules over SSH), installation + ad-hoc, inventory, playbooks, idempotency, variables/facts/templates, flow control.
- Organization & extension (Articles 8–12): roles, Galaxy/collections, Vault, writing custom modules, plugins.
- Advanced (Articles 13–16): optimization & rolling updates, Molecule testing, best practices, a complete project.
A few core ideas worth keeping:
- Declare state, not commands — this is the Ansible mindset. You say "it must be this way", and Ansible takes care of bringing the system there.
- Idempotency is everything — being safe to re-run is what separates Ansible from a script. Everything you write (task, role, module) must be idempotent.
- Agentless over SSH — nothing to install on the host; the module is shipped over and runs on the spot (Article 1).
- Organize for reuse — roles + collections + per-environment inventory + lint/test in CI.
Where to go next
- Ansible Automation Platform (AWX): a web UI + RBAC + scheduling + centralized logging for Ansible at organizational scale.
- CI/CD integration: run playbooks automatically on deploy (combine the DevOps with AWS series — GitHub Actions).
- Dynamic inventory at scale (Article 3): automatically manage a constantly changing cloud fleet.
- Execution Environments: package Ansible + dependencies into a container to run consistently everywhere.
- Connecting the other series: Ansible configures EC2 (AWS series), prepares Docker hosts (Docker series), replaces SSH/manual scripts (Linux series), and builds on the SSH/network knowledge you've gained (Networking series).
Thank you for following the whole series. You can now not only run playbooks, but understand Ansible from the mechanism underneath (shipping modules over SSH) to standard project design — enough to automate for real and move on into the broader ecosystem.
You've reached the end