LEARN ANSIBLE DEEP DIVE
Learn Ansible: From Zero to Automation Master
Goal: Deeply understand Ansible—from basic server configuration and ad-hoc commands to building complex, reusable automation roles, managing secrets, and orchestrating zero-downtime deployments.
Why Learn Ansible?
Ansible is a radically simple IT automation engine that automates cloud provisioning, configuration management, application deployment, intra-service orchestration, and many other IT needs. It’s agentless, meaning it communicates over standard protocols like SSH, requiring no special software to be installed on the managed nodes. This makes it incredibly easy to get started with.
After completing these projects, you will:
- Write clean, efficient, and idempotent playbooks.
- Structure your automation code into reusable, shareable roles.
- Manage dynamic infrastructure in cloud environments.
- Securely handle sensitive data like passwords and API keys.
- Orchestrate complex, multi-tier application deployments.
- Build your own custom Ansible modules to extend its functionality.
Core Concept Analysis
Ansible Architecture
┌──────────────────┐
│ │
│ CONTROL NODE │
│ (Your Laptop/CI) │
│ │
│ • Ansible │
│ • Playbooks │
│ • Inventory │
│ │
└────────┬─────────┘
│
│ SSH (or WinRM)
│
▼
┌──────────────────────────────────────────┐
│ MANAGED NODES │
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐
│ │ WebServer │ │ DB Server │ │ Cloud │
│ └───────────┘ └───────────┘ └───────────┘
│ │
└──────────────────────────────────────────┘
Fundamental Concepts
- Control Node vs. Managed Node: You run Ansible from a
control node(like your laptop) to managemanaged nodes(the target servers). - Inventory: A list of managed nodes. It can be a static file (
.inior.ymlformat) or a dynamic script that fetches hosts from a cloud provider (like AWS, Azure). - Playbooks: The heart of Ansible. YAML files that define a set of tasks to be executed on managed nodes. A playbook is a list of
plays. - Play: A mapping between a set of hosts (from the inventory) and the tasks to run on them.
- Task: A single action to be executed, like installing a package or starting a service. Each task calls an Ansible
Module. - Module: A reusable, standalone script that Ansible runs on a managed node. Ansible comes with thousands of modules (e.g.,
apt,yum,copy,template,service). - Handler: A special kind of task that only runs when “notified” by another task. Used for actions like restarting a service after a configuration change.
- Roles: A standardized way to organize and reuse Ansible content (tasks, handlers, variables, templates). A role is a self-contained unit of automation.
- Facts: Information gathered about managed nodes, like their IP address, OS version, or memory details. Accessed via the
ansible_factsvariable. - Vault: A feature for encrypting sensitive data (like passwords or API keys) so it can be safely stored in version control.
- Idempotency: The core principle of Ansible. Running a playbook multiple times has the same effect as running it once. If a server is already in the desired state, Ansible makes no changes.
Project List
The following 12 projects will guide you from writing your first command to orchestrating a full application deployment.
Project 1: Ad-Hoc Command Server Audit
- File: LEARN_ANSIBLE_DEEP_DIVE.md
- Main Programming Language: Ansible (YAML/CLI)
- Alternative Programming Languages: Shell
- Coolness Level: Level 2: Practical but Forgettable
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 1: Beginner
- Knowledge Area: Server Administration / Ad-Hoc Commands
- Software or Tool: Ansible, SSH
- Main Book: “Ansible: Up and Running, 3rd Edition” by Lorin Hochstein & Bas Meijer
What you’ll build: A simple command-line workflow to quickly audit the state of multiple servers using Ansible’s ad-hoc commands, checking things like uptime, disk space, and running processes.
Why it teaches Ansible: This is the “Hello, World!” of Ansible. It teaches the most fundamental concepts: setting up an inventory, ensuring SSH connectivity, and executing a single task across multiple machines without writing a full playbook.
Core challenges you’ll face:
- Creating a static inventory file → maps to defining which servers Ansible manages
- Ensuring SSH key-based authentication → maps to how Ansible connects without passwords
- Using the
pingmodule to verify connectivity → maps to the basic connection test - Running ad-hoc commands with
-m(module) and-a(args) → maps to executing quick, one-off tasks
Key Concepts:
- Inventory: Ansible Documentation - Building an inventory
- Ad-Hoc Commands: Ansible Documentation - Introduction to ad-hoc commands
- Common Modules:
ping,command,shell,setup
Difficulty: Beginner Time estimate: 1-2 hours Prerequisites: Basic Linux command line, access to 2-3 VMs or containers (e.g., Docker, Vagrant) to act as managed nodes.
Real world outcome:
# First, create an inventory file named 'hosts'
# [servers]
# server1 ansible_host=192.168.1.10
# server2 ansible_host=192.168.1.11
# Check if all servers in the inventory are reachable
$ ansible all -i hosts -m ping
server1 | SUCCESS => {
"ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" },
"changed": false,
"ping": "pong"
}
server2 | SUCCESS => {
"ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" },
"changed": false,
"ping": "pong"
}
# Get uptime for all servers
$ ansible all -i hosts -a "uptime"
server1 | CHANGED | rc=0 >>
10:30:00 up 5 days, 2:15, 1 user, load average: 0.00, 0.01, 0.05
server2 | CHANGED | rc=0 >>
10:30:01 up 10 days, 4:30, 0 users, load average: 0.02, 0.03, 0.00
# Check free memory on all servers
$ ansible all -i hosts -a "free -h"
server1 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 3.8Gi 1.2Gi 2.1Gi 23Mi 500Mi 2.4Gi
...
Implementation Hints:
- Setup: You’ll need a control node (your machine) and at least one managed node (a VM or container).
- SSH Keys: Ensure you can
ssh user@managed_node_ipwithout a password from your control node. If not, usessh-copy-id user@managed_node_ipto copy your public key. - Inventory File: Create a file named
hostsorinventory.ini. The simplest format is an INI-like structure. Group your servers under a name like[webservers]. - Command Structure: The ad-hoc command structure is
ansible <host-pattern> -i <inventory-file> -m <module_name> -a "<module_args>".<host-pattern>can beall, a group name likewebservers, or a single host.-mspecifies the module to use.commandis the default if omitted.shellallows pipes and redirection.-aprovides arguments to the module.
Learning milestones:
ansible all -m pingsucceeds → You have a working inventory and valid SSH connection.- You can run a shell command like
uptime→ You understand ad-hoc command execution. - You can gather facts using the
setupmodule → You see the wealth of data Ansible collects automatically.
Project 2: Idempotent Web Server Installation
- File: LEARN_ANSIBLE_DEEP_DIVE.md
- Main Programming Language: Ansible (YAML)
- Alternative Programming Languages: N/A
- Coolness Level: Level 2: Practical but Forgettable
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 1: Beginner
- Knowledge Area: Configuration Management / Automation
- Software or Tool: Ansible, Nginx/Apache
- Main Book: “Ansible for DevOps” by Jeff Geerling
What you’ll build: A playbook that installs and starts a web server (e.g., Nginx) on a set of managed nodes. Crucially, running the playbook a second time will do nothing if the server is already installed and running.
Why it teaches Ansible: This is your first playbook. It introduces the core concepts of tasks, modules, and idempotency. You’ll learn the difference between “do this” (a shell script) and “ensure this state” (Ansible).
Core challenges you’ll face:
- Writing your first playbook YAML file → maps to understanding basic playbook structure (hosts, tasks)
- Using the
packageorapt/yummodule to install software → maps to managing system packages - Using the
servicemodule to ensure a service is running → maps to managing system services - Running a playbook with
ansible-playbook→ maps to the standard way to execute automation
Key Concepts:
- Playbook Structure: Ansible Documentation - Playbooks
- Idempotency: “Ansible for DevOps” Chapter 1 - Geerling
- Package Modules:
apt,yum,dnf,package - Service Modules:
service,systemd
Difficulty: Beginner Time estimate: 2-3 hours Prerequisites: Project 1, basic understanding of YAML syntax.
Real world outcome:
You run ansible-playbook -i hosts playbook.yml. On the first run, you see “changed” statuses. On all subsequent runs, you see “ok” statuses, proving your playbook is idempotent and the server is in the correct state. You can then curl http://<managed_node_ip> and see the default Nginx welcome page.
# playbook.yml
# This is a conceptual example. You will build this.
---
- name: Install and configure web server
hosts: webservers
become: yes # This tells Ansible to use sudo
tasks:
- name: Install nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Ensure nginx is running and enabled on boot
service:
name: nginx
state: started
enabled: yes
# First run
$ ansible-playbook -i hosts playbook.yml
...
TASK [Install nginx] **********************
changed: [server1]
TASK [Ensure nginx is running] ************
changed: [server1]
...
# Second run
$ ansible-playbook -i hosts playbook.yml
...
TASK [Install nginx] **********************
ok: [server1]
TASK [Ensure nginx is running] ************
ok: [server1]
...
Implementation Hints:
become: yes: Most system configuration tasks require root privileges.become: yestells Ansible to usesudo(by default) for the tasks in that play.- Module
stateparameter: The key to idempotency is thestateparameter in most Ansible modules.state: presentfor a package means “install it if it’s not there, otherwise do nothing.”state: startedfor a service means “start it if it’s stopped, otherwise do nothing.”state: absentwould uninstall the package.state: stoppedwould stop the service.
- Descriptive Names: Always give your plays and tasks a
name. This makes the playbook output readable and self-documenting.
Learning milestones:
- Playbook runs without syntax errors → You understand YAML structure.
- Nginx installs and starts successfully → You’ve used modules to change system state.
- Running the playbook a second time shows “ok” for all tasks → You’ve achieved idempotency.
Project 3: Templated Configuration & Handlers
- File: LEARN_ANSIBLE_DEEP_DIVE.md
- Main Programming Language: Ansible (YAML), Jinja2
- Alternative Programming Languages: N/A
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 2: Intermediate
- Knowledge Area: Templating / Service Management
- Software or Tool: Ansible, Jinja2
- Main Book: “Ansible: Up and Running, 3rd Edition”
What you’ll build: A playbook that deploys a custom Nginx configuration file from a template. The template will use Ansible facts (like the node’s IP address) to generate a unique configuration. When the configuration changes, a handler will be triggered to gracefully reload Nginx.
Why it teaches Ansible: Static files are limited. This project teaches you how to create dynamic configurations using the Jinja2 templating engine. It also introduces handlers, the correct way to restart services only when necessary.
Core challenges you’ll face:
- Creating a Jinja2 template file (
.j2) → maps to mixing static text with dynamic variables - Using Ansible facts like
ansible_default_ipv4.addressin a template → maps to making configurations node-specific - Using the
templatemodule to deploy the file → maps to the standard way to manage templated configs - Defining a handler and notifying it from a task → maps to decoupling configuration changes from service restarts
Key Concepts:
- Templates (Jinja2): Ansible Documentation - Templating (Jinja2)
- Handlers: Ansible Documentation - Handlers: running operations on change
- Ansible Facts:
ansible_factsvariable,setupmodule
Difficulty: Intermediate Time estimate: Weekend Prerequisites: Project 2, basic knowledge of Nginx configuration.
Real world outcome:
You’ll have a playbook that generates a custom nginx.conf on each server. If you change the template and re-run the playbook, the task will report “changed”, and you will see the “RUNNING HANDLER [reload nginx]” message. If you run it without changes, nothing happens.
# templates/nginx.conf.j2
# This is a conceptual template you'll create.
events {}
http {
server {
listen 80;
# This will be replaced by the fact from each server
server_name {{ ansible_fqdn }};
location / {
return 200 "This server's IP is {{ ansible_default_ipv4.address }}\\n";
add_header Content-Type text/plain;
}
}
}
# playbook.yml
---
- name: Deploy templated Nginx config
hosts: webservers
become: yes
tasks:
- name: Deploy Nginx config from template
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
notify: Reload Nginx # This name must match the handler's name
handlers:
- name: Reload Nginx
service:
name: nginx
state: reloaded
Implementation Hints:
- Directory Structure: A good practice is to create a
templates/directory next to your playbook to hold your.j2files. notifykeyword: This is a list of handler names. The task that usesnotifywill trigger the handler at the end of the play, not immediately. This is efficient; if 10 tasks all notify the same handler, it only runs once.- Handler
name: Thenamein the handler section must be globally unique within the playbook run. reloadedvsrestarted: For services like Nginx,state: reloadedis preferred as it reloads the configuration without dropping connections.restartedis a full stop/start.
Learning milestones:
- The
nginx.conffile is created correctly on the managed node → You’ve successfully used thetemplatemodule. - The file content is unique for each server → You understand how to use Ansible facts in templates.
- The handler runs only when the template file changes → You’ve mastered the task/handler notification system.
Project 4: Refactor to a Reusable Role
- File: LEARN_ANSIBLE_DEEP_DIVE.md
- Main Programming Language: Ansible (YAML)
- Alternative Programming Languages: N/A
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 2: Intermediate
- Knowledge Area: Software Engineering / Modularity
- Software or Tool: Ansible
- Main Book: “Ansible for DevOps” by Jeff Geerling
What you’ll build: You will take the Nginx installation and configuration logic from the previous projects and package it into a self-contained, reusable “nginx” role.
Why it teaches Ansible: Single-file playbooks don’t scale. Roles are the fundamental organizational unit for building complex and maintainable Ansible automation. This project teaches you to think in terms of reusable components, not just scripts.
Core challenges you’ll face:
- Creating the standard role directory structure → maps to
tasks,handlers,templates,defaults,vars - Moving tasks and handlers into the role → maps to refactoring a monolithic playbook
- Using
defaults/main.ymlfor overridable variables → maps to creating flexible and configurable roles - Calling the role from a master playbook → maps to composing automation from building blocks
Key Concepts:
- Role Directory Structure: Ansible Documentation - Role directory structure
- Role Variables:
defaults/main.ymlvs.vars/main.yml - Ansible Galaxy: A public repository for finding and sharing roles.
Difficulty: Intermediate Time estimate: Weekend Prerequisites: Project 3.
Real world outcome:
You will have a clean, organized directory structure for your nginx role. Your main playbook will be dramatically simplified, containing just a call to the new role. You’ll be able to reuse this role in any future project.
New Directory Structure:
.
├── roles/
│ └── nginx/
│ ├── tasks/
│ │ └── main.yml
│ ├── handlers/
│ │ └── main.yml
│ ├── templates/
│ │ └── nginx.conf.j2
│ └── defaults/
│ └── main.yml
└── site.yml
Simplified Playbook (site.yml):
---
- name: Configure webservers
hosts: webservers
become: yes
roles:
- role: nginx
# You can override default variables here
# nginx_port: 8080
Implementation Hints:
ansible-galaxy init roles/nginx: This command will create a skeleton directory structure for you.main.yml: This is the “entry point” file for each section. Ansible automatically looks fortasks/main.yml,handlers/main.yml, etc., inside a role.defaults/main.ymlvs.vars/main.yml:defaults/main.yml: Contains the default values for variables your role uses. These have the lowest precedence and are easily overridden by the user. Example:nginx_port: 80.vars/main.yml: For variables that the role needs internally and that the user should not override. These have a higher precedence.
- Role Path: Ansible will automatically look for roles in a
roles/directory relative to your playbook, or in system-wide locations like/etc/ansible/roles.
Learning milestones:
ansible-galaxy initcreates the correct folders → You know the standard role layout.- The refactored playbook works identically to the old one → You have successfully encapsulated the logic.
- You can override a default variable from the playbook → You understand role configurability and variable precedence.
- You can upload your role to a git repository → Your automation is now shareable and version-controlled.
Project 5: Dynamic Inventory from a Cloud Provider
- File: LEARN_ANSIBLE_DEEP_DIVE.md
- Main Programming Language: Ansible (YAML), Python (for script)
- Alternative Programming Languages: Go, Shell
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 3: Advanced
- Knowledge Area: Cloud Automation / Dynamic Infrastructure
- Software or Tool: Ansible, AWS/Azure/GCP, Docker
- Main Book: “Cloud Native DevOps with Kubernetes” (for concepts)
What you’ll build: An Ansible setup that doesn’t use a static hosts file. Instead, it will query a cloud provider’s API (like AWS EC2 or DigitalOcean) or a local Docker daemon to discover running instances/containers and manage them automatically.
Why it teaches Ansible: Modern infrastructure is dynamic. Servers are created and destroyed constantly. This project teaches you how to manage ephemeral resources and moves you from managing pets (named servers) to managing cattle (interchangeable servers).
Core challenges you’ll face:
- Configuring cloud credentials for Ansible → maps to securely giving Ansible API access
- Using a dynamic inventory plugin (e.g.,
aws_ec2) → maps to querying an API for hosts - Grouping hosts dynamically based on tags or properties → maps to creating logical groups like ‘tag_group_db_servers’
- Running a playbook against a dynamically created host → maps to a full cloud automation loop
Key Concepts:
- Dynamic Inventory: Ansible Documentation - Working with dynamic inventory
- Inventory Plugins: A modern, more flexible alternative to inventory scripts.
- Cloud Provider Integration: Specific documentation for
aws_ec2,azure_rm,gcp_compute,docker_containersinventory plugins.
Difficulty: Advanced Time estimate: Weekend Prerequisites: Project 4, an account with a cloud provider (or Docker installed), familiarity with cloud concepts like tags.
Real world outcome:
You will be able to launch a new EC2 instance with a specific tag (e.g., app: web), and then run ansible -i inventory_aws_ec2.yml tag_app_web -m ping and have it automatically find and connect to the new instance without ever manually editing an inventory file.
# inventory_aws_ec2.yml
# This is an inventory plugin configuration file.
---
plugin: aws_ec2
regions:
- us-east-1
# Use tags to create groups automatically
keyed_groups:
- key: tags.App
prefix: tag_app
- key: tags.Environment
prefix: tag_env
# List hosts discovered by the dynamic inventory
$ ansible-inventory -i inventory_aws_ec2.yml --graph
@all:
|--@ungrouped:
| |--ec2-34-229-10-5.compute-1.amazonaws.com
|--@tag_app_web: # <-- Group created from tag!
| |--ec2-34-229-10-5.compute-1.amazonaws.com
|--@tag_env_production:
| |--ec2-34-229-10-5.compute-1.amazonaws.com
...
# Ping all servers with the tag App=web
$ ansible -i inventory_aws_ec2.yml tag_app_web -m ping
Implementation Hints:
- Plugin vs. Script: Historically, this was done with Python/Bash scripts that output a specific JSON format. Modern Ansible uses inventory plugins, which are configured via YAML files and are much easier to use. Stick with plugins.
- Credentials: Never hardcode credentials. Use environment variables (
AWS_ACCESS_KEY_ID, etc.) or standard credential files (~/.aws/credentials). - Caching: Dynamic inventory can be slow as it makes API calls. Most plugins have a caching option to speed up subsequent runs.
- Filtering: You can filter which hosts are added to the inventory based on properties like region, VPC, or running state to avoid managing unwanted instances.
Learning milestones:
ansible-inventory --graphshows your cloud instances → The plugin is correctly configured and authenticating.- Hosts are automatically placed into groups based on tags → You understand how to organize a dynamic inventory.
- You can run a playbook against a dynamically created group → You’ve mastered managing dynamic infrastructure.
Project 6: Secure Secret Management with Vault
- File: LEARN_ANSIBLE_DEEP_DIVE.md
- Main Programming Language: Ansible (YAML)
- Alternative Programming Languages: N/A
- Coolness Level: Level 2: Practical but Forgettable
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 2: Intermediate
- Knowledge Area: Security / Secrets Management
- Software or Tool: Ansible Vault
- Main Book: “Mastering Ansible, 4th Edition” by James Freeman & Jesse Keating
What you’ll build: A playbook that creates a database and a user with a password. The password will be stored in an encrypted file using Ansible Vault, ensuring it’s never exposed in plain text in your git repository.
Why it teaches Ansible: Storing secrets in plain text is a cardinal sin of DevOps. Vault is Ansible’s native solution for this problem. This project teaches you the complete lifecycle of creating, using, and managing encrypted secrets within your automation.
Core challenges you’ll face:
- Creating an encrypted file with
ansible-vault create→ maps to initializing a secure variable file - Editing an encrypted file with
ansible-vault edit→ maps to safely modifying secrets - Using vaulted variables in a playbook → maps to seamlessly integrating secrets into tasks
- Providing the vault password during a playbook run → maps to unlocking the secrets for execution
Key Concepts:
- Ansible Vault: Ansible Documentation - Protecting sensitive data with Ansible Vault
- Vault Password: Prompting vs. password files vs. environment variables.
- Encrypting Single Variables: Using
ansible-vault encrypt_string.
Difficulty: Intermediate Time estimate: 2-3 hours Prerequisites: Project 2.
Real world outcome:
Your git repository will contain an encrypted file like secrets.yml. A git grep for the password will find nothing. When you run the playbook, Ansible will prompt for the vault password and then successfully create the database user with the correct, decrypted password.
Encrypted file (secrets.yml):
# You create this with 'ansible-vault create secrets.yml'
$ANSIBLE_VAULT;1.1;AES256
34326139366632643534346162353932626132393233323062313364303434316631376435663731
62333633646564616631326435316335343639643463620a32333533616630323335333139383633
...
This file contains something like db_password: "MySuperSecretPassword123".
Playbook (playbook.yml):
---
- name: Setup secure database
hosts: db_servers
become: yes
vars_files:
- secrets.yml # Load the encrypted variables
tasks:
- name: Create a new database user
postgresql_user:
name: myapp_user
password: "{{ db_password }}" # Use the vaulted variable
# Running the playbook
$ ansible-playbook -i hosts playbook.yml
Vault password: # <-- Prompts for the password
...
TASK [Create a new database user] **********
changed: [db-server-1]
Implementation Hints:
- Vault Password Management:
- Prompt (default): Secure, but not great for CI/CD.
- Password File: Create a file with the password,
chmod 400it, and run with--vault-password-file <path>. Better for automation. - Environment Variable: Set
ANSIBLE_VAULT_PASSWORD. Also good for CI/CD.
ansible-vault encrypt_string: A useful command for encrypting a single value that you can paste directly into a non-encrypted YAML file.- Team Workflows: For teams, you can use multiple vault passwords (
--vault-id) to manage different environments (dev, prod) or give different users access.
Learning milestones:
- You create and edit an encrypted file → You understand the basic vault workflow.
- A playbook successfully uses a variable from the vault → You can integrate secrets into tasks.
- Your secrets are not visible in your git history → You’ve successfully decoupled secrets from your code.
Project 7: Zero-Downtime Rolling Updates
- File: LEARN_ANSIBLE_DEEP_DIVE.md
- Main Programming Language: Ansible (YAML)
- Alternative Programming Languages: N/A
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 3: Advanced
- Knowledge Area: DevOps / High Availability / Orchestration
- Software or Tool: Ansible, HAProxy/Nginx (as load balancer)
- Main Book: “Site Reliability Engineering: How Google Runs Production Systems” (for concepts)
What you’ll build: A playbook that deploys a new version of a web application across a fleet of servers behind a load balancer. The playbook will take servers out of the load balancer pool one by one, update them, and add them back, ensuring the service remains available throughout the process.
Why it teaches Ansible: This is true orchestration. It’s not just configuring a single server; it’s managing the lifecycle of a service across a distributed system. It teaches you how to control the flow of execution and interact with different parts of your infrastructure within a single playbook.
Core challenges you’ll face:
- Using
serialto perform updates in batches → maps to controlling the number of concurrent updates - Using
delegate_toto control the load balancer → maps to running a task on a different host (the LB) - Creating a workflow to disable, update, and re-enable a server → maps to designing a safe deployment process
- Using
wait_forto check if the application is healthy before proceeding → maps to adding health checks to your automation
Key Concepts:
- Rolling Updates: Ansible Documentation - Rolling update
- Delegation:
delegate_toandlocal_action - Error Handling:
block,rescue,always
Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 4, a working load balancer setup with at least 2-3 web server backends.
Real world outcome:
You can run the playbook while continuously sending requests to your load balancer (while true; do curl http://lb_ip; sleep 0.1; done). You will see no failed requests during the entire update process. You’ll observe the backend servers being taken out, updated, and brought back online seamlessly.
Conceptual Playbook Structure:
---
- name: Perform a zero-downtime rolling update
hosts: webservers
serial: 1 # Process one server at a time
tasks:
- name: Disable server in load balancer
haproxy_server:
backend: web_backend
server: "{{ inventory_hostname }}"
state: disabled
delegate_to: load_balancer_host
- name: Wait for connections to drain
wait_for:
timeout: 30
- name: Deploy new application code
git:
repo: 'https://github.com/my/app.git'
dest: /var/www/myapp
version: '{{ app_version }}' # A variable for the new version
- name: Restart application service
service:
name: myapp
state: restarted
- name: Wait for application to become healthy
uri:
url: "http://{{ inventory_hostname }}/health"
status_code: 200
- name: Re-enable server in load balancer
haproxy_server:
backend: web_backend
server: "{{ inventory_hostname }}"
state: enabled
delegate_to: load_balancer_host
Implementation Hints:
serial: 1: This is the magic keyword for rolling updates. It tells Ansible to run the entire play on one host from thewebserversgroup before moving to the next. You can set it to a larger number (e.g.,serial: 2) or a percentage (serial: "30%").delegate_to: This task-level keyword is incredibly powerful. It runs the specified task on the delegated host, but in the context of the original host. This lets you use facts and variables fromwebserver1while running a command on theload_balancer.- The Load Balancer: You’ll need a load balancer that has an API or a command-line tool that can be used to disable/enable backend servers. HAProxy has modules, or you can use
shellcommands. - Health Checks: A robust deployment relies on health checks. The
uriorwait_formodules are essential for ensuring an updated server is healthy before it’s returned to service.
Learning milestones:
- The playbook correctly updates one server at a time → You understand the
serialkeyword. - Tasks are correctly run on the load balancer → You’ve mastered
delegate_to. - The entire deployment completes with zero failed client requests → You have successfully orchestrated a zero-downtime update.
Project 8: Create a Custom Ansible Module
- File: LEARN_ANSIBLE_DEEP_DIVE.md
- Main Programming Language: Python
- Alternative Programming Languages: PowerShell, Go, Ruby
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 2. The “Micro-SaaS / Pro Tool”
- Difficulty: Level 4: Expert
- Knowledge Area: Ansible Internals / Python Scripting
- Software or Tool: Ansible, Python
- Main Book: “Python for DevOps” by Noah Gift
What you’ll build: A simple, custom Ansible module in Python. The module will take a hostname and a port as arguments and check if that port is open and listening. It will return a JSON object indicating the status and whether it made a change (it won’t, in this case).
Why it teaches Ansible: Sometimes, there isn’t a module for what you need to do. This project demystifies Ansible’s internals and shows you that modules are just standalone scripts that follow a simple contract: take arguments, do a thing, and return JSON.
Core challenges you’ll face:
- Creating the
library/directory for custom modules → maps to how Ansible finds custom code - Using the
AnsibleModulehelper class in Python → maps to parsing arguments and returning results correctly - Implementing the module’s logic (a socket connection) → maps to the core work of the module
- Returning the correct JSON output (
changed,failed, and custom data) → maps to communicating status back to the Ansible engine
Key Concepts:
- Custom Modules: Ansible Documentation - Developing modules
AnsibleModuleclass: The standard boilerplate for writing Python modules.- Module Locations:
library/dir,ANSIBLE_LIBRARYenv var, or as part of a role (my_role/library/).
Difficulty: Expert Time estimate: 1-2 weeks Prerequisites: Project 4, strong Python scripting skills.
Real world outcome: You will have a Python file for your module, and you’ll be able to call it from a playbook just like any built-in module. The output will show your custom return data.
Custom Module (library/check_port.py):
#!/usr/bin/python
from ansible.module_utils.basic import AnsibleModule
import socket
def run_module():
# Define available arguments
module_args = dict(
host=dict(type='str', required=True),
port=dict(type='int', required=True),
)
# Boilerplate: setup module object
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
result = dict(
changed=False,
is_open=False
)
# The module's main logic
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
try:
status = s.connect_ex((module.params['host'], module.params['port']))
if status == 0:
result['is_open'] = True
finally:
s.close()
# In check mode, exit before making any changes
if module.check_mode:
module.exit_json(**result)
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()
Playbook using the module:
- name: Test custom module
hosts: localhost
connection: local
tasks:
- name: Check if google.com has port 443 open
check_port:
host: google.com
port: 443
register: port_status
- name: Display the result
debug:
var: port_status
Implementation Hints:
- Shebang: Always start your module script with
#!/usr/bin/python(or the correct interpreter). AnsibleModule: This class is your best friend. It handles argument parsing, check mode, and formatting the return JSON. Use it.exit_jsonvs.fail_json:module.exit_json(**result): The module finished successfully. Pass your return data as keyword arguments.module.fail_json(msg='Something went wrong', **result): The module failed. Provide an error message.
changedflag: This boolean is critical. Set it toTrueif your module made a change to the system,Falseotherwise. This is essential for idempotency and notifying handlers.- Check Mode:
supports_check_mode=Trueand theif module.check_mode:block are best practices. They allow users to run a “dry run” of your module.
Summary
| Project | Main Language | Difficulty | Time Estimate |
|---|---|---|---|
| 1. Ad-Hoc Command Server Audit | Ansible (CLI) | Beginner | 1-2 hours |
| 2. Idempotent Web Server Installation | Ansible (YAML) | Beginner | 2-3 hours |
| 3. Templated Configuration & Handlers | Ansible (YAML), Jinja2 | Intermediate | Weekend |
| 4. Refactor to a Reusable Role | Ansible (YAML) | Intermediate | Weekend |
| 5. Dynamic Inventory from Cloud | Ansible (YAML), Python | Advanced | Weekend |
| 6. Secure Secret Management with Vault | Ansible (YAML) | Intermediate | 2-3 hours |
| 7. Zero-Downtime Rolling Updates | Ansible (YAML) | Advanced | 1-2 weeks |
| 8. Create a Custom Ansible Module | Python | Expert | 1-2 weeks |
```