0% found this document useful (0 votes)
179 views19 pages

Ansible Automation Handbook 2025 SCC

The Ansible Automation Handbook (2025 Edition) is a comprehensive guide designed for self-study, covering Ansible from basic concepts to advanced automation techniques. It emphasizes practical examples and best practices, enabling users to confidently apply Ansible in real-world projects. The handbook includes sections on installation, playbook creation, inventory management, and advanced topics like dynamic inventories and CI/CD integration.

Uploaded by

Serguei
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
179 views19 pages

Ansible Automation Handbook 2025 SCC

The Ansible Automation Handbook (2025 Edition) is a comprehensive guide designed for self-study, covering Ansible from basic concepts to advanced automation techniques. It emphasizes practical examples and best practices, enabling users to confidently apply Ansible in real-world projects. The handbook includes sections on installation, playbook creation, inventory management, and advanced topics like dynamic inventories and CI/CD integration.

Uploaded by

Serguei
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 19

# Ansible Automation Handbook (2025 Edition)

Welcome to **Ansible Automation Handbook**, a friendly and hands-on guide to


learning Ansible from the ground up. This book is designed for self-study: you
can follow it at your own pace, try the examples on your own infrastructure or
in virtual machines, and gradually master automation. Each section builds upon
the previous one – start with the basics and progress toward more advanced
topics like dynamic inventories, secrets management, custom modules and
integrating Ansible into CI/CD pipelines. I emphasise practical examples,
diagrams and best practices so that you can apply Ansible confidently in
real-world projects.

---

## Table of Contents

1. [Basic Concepts](#basic-concepts)
- [1.1 What Is Ansible?](#11-what-is-ansible)
- [1.2 Why Use Ansible?](#12-why-use-ansible)
- [1.3 Installing Ansible and Setting Up Your First Project](#13-installing-
ansible-and-setting-up-your-first-project)
- [1.4 Understanding YAML Syntax](#14-understanding-yaml-syntax)
- [1.5 Inventory Basics](#15-inventory-basics)
- [1.6 Your First Playbook](#16-your-first-playbook)
- [1.7 Mini-Project: Automate Package Installation](#17-mini-project-
automate-package-installation)

2. [Intermediate Topics](#intermediate-topics)
- [2.1 Working with Variables](#21-working-with-variables)
- [2.2 Templating with Jinja2](#22-templating-with-jinja2)
- [2.3 Organising Code into Roles](#23-organising-code-into-roles)
- [2.4 Commonly Used Modules](#24-commonly-used-modules)
- [2.5 Managing Inventory: Hosts, Groups and Patterns](#25-managing-
inventory-hosts-groups-and-patterns)
- [2.6 Best Practices for Playbooks](#26-best-practices-for-playbooks)
- [2.7 Mini-Project: Deploying a Multi-Tier Application](#27-mini-project-
deploying-a-multi-tier-application)

3. [Advanced Automation](#advanced-automation)
- [3.1 Dynamic Inventory](#31-dynamic-inventory)
- [3.2 Protecting Secrets with Ansible Vault](#32-protecting-secrets-with-
ansible-vault)
- [3.3 Developing Custom Modules](#33-developing-custom-modules)
- [3.4 Performance Optimisation](#34-performance-optimisation)
- [3.5 Integrating Ansible into CI/CD Pipelines](#35-integrating-ansible-
into-cicd-pipelines)
- [3.6 Event-Driven Ansible and AI-Assisted Automation](#36-event-driven-
ansible-and-ai-assisted-automation)
- [3.7 Mini-Project: Cloud Provisioning with Secrets and CI/CD](#37-mini-
project-cloud-provisioning-with-secrets-and-cicd)

4. [Appendix](#appendix)
- [A. Ansible 2.16 Highlights](#a-ansible-216-highlights)
- [B. Further Reading and Resources](#b-further-reading-and-resources)

---

## Basic Concepts

Ansible is an open-source automation platform that helps you **provision**,


**configure** and **manage** computers across your infrastructure. It is
agentless – instead of requiring a daemon running on every machine, the control
node connects to remote hosts over SSH (or Windows Remote Management on Windows
systems) and executes tasks on demand. Because Ansible uses human-readable YAML
files called *playbooks*, you can describe desired state in plain language and
version it alongside your application code. Key characteristics of Ansible
include simplicity, scalability and idempotence: tasks can be applied repeatedly
without causing unintended side effects【468687567525964†L109-L149】.

### 1.1 What Is Ansible?

*Ansible* allows you to define how systems **should** look and automatically
enforce that state. Whether you need to install packages, manage users, deploy
applications or orchestrate entire environments, you write declarative
*playbooks* that contain tasks and Ansible ensures the target machines reach the
desired configuration. Unlike other automation tools that rely on a persistent
agent, Ansible connects over SSH, executes modules on remote machines and
disconnects. This reduces overhead and security risks because nothing is
permanently installed on the managed nodes【468687567525964†L109-L149】.

Ansible works through a few core concepts:

* **Control node** – the machine where you run the Ansible CLI tools. It sends
commands to your infrastructure but does not need to be a dedicated server
【447534196468510†L144-L190】.
* **Managed nodes** – the hosts (servers, network devices, containers, cloud
instances) you manage. Ansible communicates with them over SSH or another
transport【447534196468510†L144-L190】.
* **Inventory** – a file listing the managed nodes and grouping them. The
inventory can be static (INI/YAML) or dynamic (fetched from a cloud provider)
【447534196468510†L144-L190】.
* **Playbook** – a YAML document containing one or more *plays*. Each play maps
a group of hosts to a series of *tasks*. Tasks invoke *modules* to perform
actions like installing packages or copying files【514170298441643†L109-L176】.
* **Modules** – discrete units of code shipped with Ansible or written by you.
Modules perform specific functions (package management, file operations, service
control, etc.)【514170298441643†L109-L176】.
* **Roles and collections** – mechanisms for organising and distributing
reusable automation code (we will explore them later)【447534196468510†L144-
L190】.

#### Diagram: Ansible Architecture

```text
┌───────────────────────┐
│ Control Node │
│ (ansible-playbook) │
└─────────┬─────────────┘
│ SSH/WinRM
┌───────────┴───────────┐
│ │
┌─────▼─────┐ ┌─────▼─────┐
│ Managed │ │ Managed │
│ Node 1 │ ... │ Node N │
└───────────┘ └───────────┘
```

The control node reads your inventory, connects to the target hosts, runs the
tasks defined in the playbook and reports the results back to you. Because
Ansible uses SSH by default, you benefit from your existing authentication
mechanisms and can leverage features like SSH key forwarding and bastion hosts.

### 1.2 Why Use Ansible?

Automation reduces repetitive work, eliminates configuration drift and ensures


that systems are provisioned consistently. According to the official
documentation, Ansible helps **eliminate repetition**, **manage system
configuration**, **perform continuous deployment** and even enables
**zero-downtime updates**【468687567525964†L109-L149】. Other benefits include:

* **Human-readable syntax** – playbooks are written in YAML, making them


accessible to operators and developers alike.
* **Agentless architecture** – there is no agent to install or maintain on
managed nodes.
* **Idempotence** – repeating a task does not change the system beyond the first
run; this property makes your automation safe to rerun.
* **Extensibility** – you can extend Ansible with custom modules, plugins and
dynamic inventories.
* **Large ecosystem** – thousands of modules support operating systems, cloud
providers, network devices and more.

### 1.3 Installing Ansible and Setting Up Your First Project

Ansible runs on Linux or macOS. The easiest installation method is using


Python’s package manager, `pip`:

```bash
python3 -m pip install --user ansible
```

When you install Ansible it provides the `ansible` and `ansible-playbook`


commands plus a collection of modules. You may also install Ansible via your
distribution’s package manager (for example, `dnf install ansible` on Fedora or
`apt-get install ansible` on Ubuntu).

#### Creating a Project Directory

Create a workspace directory for your automation:

```bash
mkdir ansible_quickstart
cd ansible_quickstart
```

Inside this directory you will store your inventory file, playbooks and any
additional files. The *Start automating with Ansible* guide suggests this
structure as a minimal starting point【222138213786001†L109-L148】.

#### Building the Inventory

Create an inventory file named `inventory.ini` (INI format) and add hosts under
a group:

```ini
[webservers]
192.0.2.10
192.0.2.11

[dbservers]
192.0.2.20
```

Each group name is enclosed in square brackets. You can define multiple groups
or nested groups. To verify the inventory and see how Ansible interprets it,
run:

```bash
ansible-inventory --list -i inventory.ini
```

To test connectivity, use the built-in `ping` module:

```bash
ansible all -m ping -i inventory.ini
```

Ansible will attempt an SSH connection to each host. Ensure your SSH keys are
installed on the managed hosts and that the control node can reach them
【174484689979159†L113-L179】.

#### Using YAML Files

Throughout this book we use YAML for playbooks. YAML is indentation-sensitive


and uses spaces (never tabs). Lists are denoted with hyphens (`-`) and
dictionaries (maps) with `key: value` pairs. Here is a quick primer:

```yaml
---
# A YAML document
name: Example
hosts:
- server1
- server2
vars:
package: httpd
settings:
port: 80
ssl: false
```

Indentation must be consistent (typically 2 spaces). Comments begin with `#`.


As you write playbooks, remember that YAML is a data serialization format –
there is no logic built-in. Ansible provides conditional logic and loops
through its own syntax (`when`, `with_items`, etc.).

### 1.4 Understanding YAML Syntax

Ansible playbooks start with three dashes (`---`) to denote the beginning of a
YAML document. Playbooks are lists of plays; each play has keys like `name`,
`hosts`, `tasks`, `vars` and so on. Tasks are themselves a list where each item
calls a module. Here is an annotated example:

```yaml
---
- name: Install Apache on web servers
hosts: webservers
become: true
vars:
http_port: 80
tasks:
- name: Install the httpd package
dnf:
name: httpd
state: present

- name: Start and enable the httpd service


service:
name: httpd
state: started
enabled: true
- name: Create a simple index.html
copy:
dest: /var/www/html/index.html
content: "<h1>Welcome to Ansible!</h1>"
```

In this play:

* `name` provides a human-readable description.


* `hosts` defines the target group (`webservers`).
* `become: true` tells Ansible to use privilege escalation (sudo) to run the
tasks.
* `vars` defines variables available within the play.
* `tasks` is a list of actions using modules (`dnf`, `service`, `copy`).

### 1.5 Inventory Basics

The inventory is the cornerstone of Ansible. It groups hosts and assigns


variables to them. Inventories can be static (defined in files) or dynamic
(generated at runtime). Here we focus on static inventories.

#### INI vs YAML Formats

The INI format is widely used because it is simple and supports grouping hosts.
However, Ansible also supports YAML inventory files. Here is the same inventory
expressed in YAML:

```yaml
all:
children:
webservers:
hosts:
192.0.2.10:
192.0.2.11:
dbservers:
hosts:
192.0.2.20:
```

You can choose whichever format you prefer. To use a YAML inventory, specify
its path with `-i inventory.yaml` when running Ansible commands. Remember to
add your SSH public key to managed hosts so that Ansible can connect to them
【174484689979159†L113-L179】.

### 1.6 Your First Playbook

With an inventory in place, let us create a simple playbook. Create a file


called `hello.yml`:

```yaml
---
- name: Say hello
hosts: all
tasks:
- name: Print a friendly message
debug:
msg: "Hello from {{ inventory_hostname }}!"
```

Here we use the `debug` module to print a message. `{{ inventory_hostname }}`
is a built-in variable representing the name of each host. Run the playbook
with:
```bash
ansible-playbook -i inventory.ini hello.yml
```

The output will show each host and the printed message. This example
demonstrates the structure of a playbook: a list of plays, each containing
tasks, and tasks invoking modules to perform work【514170298441643†L109-L176】.

### 1.7 Mini-Project: Automate Package Installation

To solidify your understanding, let us create a mini-project that installs and


configures a web service. We will write a playbook to install Nginx on a group
of servers.

1. **Define the inventory**: ensure your `inventory.ini` contains a group


`webservers` with the IP addresses of your target machines.

2. **Create the playbook** (`install_nginx.yml`):

```yaml
---
- name: Install and configure Nginx on web servers
hosts: webservers
become: true
tasks:
- name: Install Nginx package
package:
name: nginx
state: present

- name: Ensure Nginx is running and enabled


service:
name: nginx
state: started
enabled: true

- name: Deploy an index page


copy:
dest: /usr/share/nginx/html/index.html
content: |
<html>
<body>
<h1>Welcome to the Ansible Nginx server on
{{ inventory_hostname }}</h1>
</body>
</html>
mode: '0644'

- name: Open firewall port 80 (RHEL/Fedora only)


when: ansible_facts['os_family'] == 'RedHat'
firewalld:
port: 80/tcp
permanent: true
state: enabled
immediate: yes
```

3. **Run the playbook**:

```bash
ansible-playbook -i inventory.ini install_nginx.yml
```
This mini-project uses a few modules: `package` to install software, `service`
to manage services, `copy` to deploy a file and `firewalld` to open a port. It
also uses a conditional (`when`) to run the firewall task only on Red Hat-family
systems. Congratulations – you have automated the installation of a web
service!

---

## Intermediate Topics

In this section we build on the basics and explore Ansible’s features for
writing flexible, reusable automation. We will work with variables, templates,
roles, common modules and inventory patterns. You will also learn best
practices that make playbooks maintainable and safe.

### 2.1 Working with Variables

Variables enable you to manage differences between systems. They can hold
strings, lists, dictionaries, integers and other data types. You can define
variables in many places: in playbooks, inventories, external files, roles or
even at the command line. After definition you can use them in module
arguments, conditions, loops, templates and more【98624939005778†L145-L160】.

#### Defining Variables

1. **Inline variables within a play**:

```yaml
- hosts: webservers
vars:
app_port: 8080
tasks:
- name: Show the port number
debug:
msg: "The application will listen on port {{ app_port }}"
```

2. **Inventory variables**: assign variables to groups or hosts in the inventory


file. For example, in INI format:

```ini
[webservers]
192.0.2.10 app_port=8080
192.0.2.11 app_port=8081

[webservers:vars]
package=nginx
```

3. **External variable files**: store variables in a YAML file and include it


with `vars_files`:

```yaml
- hosts: all
vars_files:
- vars/common.yml
tasks: ...
```

4. **Registering variables from task results**:

```yaml
- hosts: localhost
tasks:
- name: Run a command
command: uptime
register: uptime_result

- debug:
var: uptime_result.stdout
```

#### Using Variables

Variables are referenced with the Jinja2 `{{ }}` syntax. You can perform
operations, call filters and use conditional logic. For example:

```yaml
msg: "Current time is {{ ansible_date_time.time }}"
user_list: "{{ some_list | join(', ') }}"
```

When writing tasks, avoid hard-coding values. Place them in variables so they
can be overridden via inventories or command-line extra vars (`-e`).

### 2.2 Templating with Jinja2

Ansible uses the **Jinja2** templating engine to generate files dynamically.


Templates allow you to embed variables, control structures and filters inside
text files. The processing happens on the control node, which means target
machines do not need Python or Jinja installed【282567482043076†L133-L153】.

#### Creating a Template

Suppose you want to generate an Nginx configuration file with a variable port.
Create a template file under a directory called `templates` and name it
`nginx.conf.j2`:

```nginx
server {
listen {{ app_port }};
server_name {{ inventory_hostname }};
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
```

Now write a task to deploy this template:

```yaml
- hosts: webservers
vars:
app_port: 8080
tasks:
- name: Deploy Nginx configuration from template
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/conf.d/default.conf
mode: '0644'
```

When the playbook runs, Ansible reads the Jinja2 template, substitutes variables
and writes the resulting file to the destination on each target host.
### 2.3 Organising Code into Roles

As your automation grows, you will want to reuse code and share it with others.
**Roles** provide a standard way to package tasks, variables, files, templates
and handlers into a self-contained directory structure. According to best
practices, roles make collaboration easier and help keep your playbooks clean
【647884764329840†L121-L190】.

You can create a role skeleton with:

```bash
ansible-galaxy init myrole
```

This command creates the following structure:

```text
myrole/
├── defaults/ # Default variables (lowest priority)
│ └── main.yml
├── files/ # Static files to copy
├── handlers/ # Handlers triggered by notify
│ └── main.yml
├── meta/ # Role metadata (dependencies)
│ └── main.yml
├── tasks/ # Main list of tasks
│ └── main.yml
├── templates/ # Jinja2 templates
├── tests/ # Test inventory and playbook
└── vars/ # Variables with higher priority
└── main.yml
```

To use a role in a playbook, include it under the `roles` key:

```yaml
- hosts: webservers
roles:
- myrole
```

Each role can include its own tasks and can use variables defined in
`defaults/main.yml` or `vars/main.yml`. Roles can also depend on other roles;
dependencies are defined in `meta/main.yml`. This approach encourages modular
automation and allows you to publish your roles on Ansible Galaxy for others to
reuse.

#### Diagram: Role Directory Structure

```text
myrole/
├── defaults/
│ └── main.yml
├── files/
├── handlers/
│ └── main.yml
├── meta/
│ └── main.yml
├── tasks/
│ └── main.yml
├── templates/
├── tests/
└── vars/
└── main.yml
```

### 2.4 Commonly Used Modules

Ansible includes hundreds of modules; learning all of them is not necessary.


Here are some essential modules you will use frequently along with example
tasks:

| Module | Purpose | Example |


|-------|--------|--------|
| `package` (`dnf`, `yum`, `apt`) | Manage packages on different OS families. |
`package:\n name: nginx\n state: present` installs Nginx
【116745786427291†L90-L108】. |
| `service` | Start/stop/restart services and enable them at boot. | `service:\n
name: nginx\n state: started\n enabled: true` ensures Nginx is running
【116745786427291†L129-L150】. |
| `copy` | Copy files from the control node to targets. | `copy:\n src:
files/index.html\n dest: /usr/share/nginx/html/index.html\n owner: root\n
mode: '0644'`【116745786427291†L215-L258】. |
| `file` | Create/remove files or directories, set permissions. | `file:\n
path: /opt/mydir\n state: directory\n owner: ansible\n mode: '0755'`
【116745786427291†L215-L258】. |
| `lineinfile` | Ensure a line is present/absent or replace patterns in files. |
`lineinfile:\n path: /etc/ssh/sshd_config\n regexp: '^PermitRootLogin'\n
line: 'PermitRootLogin no'`【116745786427291†L272-L311】. |
| `debug` | Print variables or messages during a play. | `debug:\n var:
some_variable`【116745786427291†L215-L258】. |
| `git` | Clone or update Git repositories. | `git:\n repo:
https://github.com/example/app.git\n dest: /opt/app\n version: master`
【116745786427291†L272-L311】. |
| `archive` | Create compressed archives (tar). | `archive:\n path:
/var/log\n dest: /tmp/logs.tar.gz\n format: gz`【116745786427291†L215-
L258】. |
| `command` / `shell` | Execute arbitrary commands. Use `command` for
idempotent commands and `shell` when you need shell features. | `command:
/usr/bin/uptime`【116745786427291†L272-L311】. |
| `cli_command` (from the network collection) | Run commands on network devices
via SSH. | `cli_command:\n command: show version`【116745786427291†L272-
L311】. |

While these modules cover common tasks, you will encounter many others as you
automate different technologies. Always consult the [module
index](https://docs.ansible.com/ansible/latest/modules/modules_by_category.html)
for details.

### 2.5 Managing Inventory: Hosts, Groups and Patterns

Static inventories scale by grouping hosts and assigning variables at different


levels. When targeting hosts, you can use patterns:

* `ansible webservers -m ping` – run against the `webservers` group.


* `ansible 'webservers:&dbservers' -m ping` – run against hosts that are in both
`webservers` and `dbservers` groups.
* `ansible 'webservers:!dbservers' -m ping` – run against hosts in `webservers`
but not in `dbservers`.
* `ansible all -m ping --limit 192.0.2.10` – limit to a specific host.

Group variables can be defined under `[group:vars]` in an INI file or in a


separate YAML file such as `group_vars/webservers.yml`. Host variables can be
defined under `[host:vars]` or in `host_vars/hostname.yml`. Organising
variables this way allows you to separate environment-specific settings from
playbooks.

When your environment changes rapidly – for example, instances created and
destroyed in the cloud – static inventories become cumbersome. In the advanced
section we will explore dynamic inventory plugins that fetch host lists from
external sources like AWS, Azure, VMware, etc.

### 2.6 Best Practices for Playbooks

Writing maintainable playbooks requires discipline. The following best


practices are gathered from experienced operators【807064649843584†L515-L550】
【807064649843584†L556-L590】:

* **Idempotence matters** – ensure your tasks can be run multiple times without
changing anything when the state is already correct. Use modules instead of
`shell` whenever possible.
* **Use roles and include_tasks** – break your automation into reusable roles
and include files. Avoid monolithic playbooks.
* **Separate variables** – store variables in `vars` or `defaults` files, not
embedded in tasks. This makes your code easier to override.
* **Tag tasks** – assign `tags` to tasks so you can run specific portions of a
playbook with `--tags` or skip others with `--skip-tags`.
* **Use check mode and diff** – run playbooks with `--check` to perform a dry
run and `--diff` to see what will change before applying it.
* **Limit hosts for testing** – use `--limit` to target a subset of hosts during
development. This reduces blast radius.
* **Employ rolling updates** – use the `serial` keyword to control how many
hosts are updated at a time (e.g., `serial: 2` will update two hosts
concurrently). This minimises downtime during deployments
【807064649843584†L515-L550】.
* **Enforce security** – avoid logging secrets, use Ansible Vault for sensitive
data and restrict privilege escalation【807064649843584†L556-L590】.

### 2.7 Mini-Project: Deploying a Multi-Tier Application

Imagine you need to deploy a two-tier web application: a front-end served by


Nginx and a back-end database (MariaDB). We will create roles for each layer
and demonstrate how variables and templates make this easy.

1. **Create roles**:

```bash
ansible-galaxy init webserver
ansible-galaxy init database
```

2. **Configure the `webserver` role**:

* Edit `webserver/tasks/main.yml`:
```yaml
---
- name: Install Nginx
package:
name: nginx
state: present

- name: Deploy configuration template


template:
src: nginx.conf.j2
dest: /etc/nginx/conf.d/default.conf
notify: restart nginx
- name: Deploy index page
copy:
dest: /usr/share/nginx/html/index.html
content: "App connecting to DB at {{ db_host }}"
```

* Create `webserver/templates/nginx.conf.j2` as shown earlier.


* Add a handler in `webserver/handlers/main.yml`:
```yaml
---
- name: restart nginx
service:
name: nginx
state: restarted
```

3. **Configure the `database` role**:

* Edit `database/tasks/main.yml`:
```yaml
---
- name: Install MariaDB server
package:
name: mariadb-server
state: present

- name: Start and enable MariaDB


service:
name: mariadb
state: started
enabled: true

- name: Create database and user


mysql_db:
name: app_db
state: present

- name: Create DB user


mysql_user:
name: app_user
password: "{{ db_password }}"
priv: 'app_db.*:ALL'
state: present
```

4. **Define group variables**:

Create `group_vars/webservers.yml`:

```yaml
---
db_host: 192.0.2.20
```

Create `group_vars/dbservers.yml`:

```yaml
---
db_password: strongpassword
```

5. **Write the playbook** (`site.yml`):


```yaml
---
- name: Deploy the application stack
hosts: all
become: true
roles:
- role: database
when: inventory_hostname in groups['dbservers']

- role: webserver
when: inventory_hostname in groups['webservers']
```

6. **Run the playbook**:

```bash
ansible-playbook -i inventory.ini site.yml
```

This project demonstrates using roles, variables, templates and handlers to


deploy a multi-tier application. You can extend it by adding firewall rules,
TLS configuration or high availability as exercises.

---

## Advanced Automation

In the advanced section we move beyond static inventories and basic playbooks.
You will learn how to fetch hosts dynamically, secure secrets with
Ansible Vault, write your own modules, tune performance, integrate with CI/CD
and explore new capabilities like event-driven automation and generative AI.

### 3.1 Dynamic Inventory

Static inventory files do not scale well when infrastructure is created and
destroyed dynamically. In cloud environments you want Ansible to pull the
latest list of hosts from your provider. Ansible achieves this through
**inventory plugins** and scripts. A dynamic inventory plugin runs before your
playbook and queries an external source (AWS, Azure, VMware, etc.) to build the
host list and group variables【614492783535894†L122-L145】.

The official documentation recommends using plugins rather than scripts because
plugins support caching and variable composition【933224521356787†L140-L153】.
Many plugins are built in: `aws_ec2`, `azure_rm`, `gcp_compute`, `openstack`,
`kubernetes`, `docker_swarm` and more. You enable a plugin by creating a YAML
file ending in `.yaml` and specifying the plugin name and options. For example,
an AWS EC2 dynamic inventory file (`aws_ec2.yaml`):

```yaml
plugin: aws_ec2
regions:
- us-east-1
keyed_groups:
- key: tags.Role
prefix: ''
filters:
instance-state-name: running
```

Running `ansible-inventory -i aws_ec2.yaml --graph` will query AWS and show the
hosts grouped by the `Role` tag. If you use Red Hat Ansible Automation Platform
(AAP) or AWX, you can define dynamic inventory sources via the web UI and
schedule synchronisations【437227952386536†L122-L146】.
#### Implementing a Custom Inventory Plugin

If no built-in plugin matches your needs, you can write your own. A custom
plugin inherits from `BaseInventoryPlugin`, `Constructable` and `Cacheable` and
implements `parse()` and `verify_file()` methods【933224521356787†L221-L237】.
Although beyond the scope of this book, the plugin interface lets you integrate
with proprietary CMDBs or API endpoints.

#### Diagram: Dynamic Inventory Flow

```text
┌─────────────┐
│ Playbook │
└──────┬──────┘

│ call inventory

┌────────────────────┐
│ Inventory Plugin │
│ (e.g., aws_ec2) │
└──────┬─────────────┘
│ queries API

┌────────────────────┐
│ Cloud/CMDB API │
└────────────────────┘
```

### 3.2 Protecting Secrets with Ansible Vault

Storing plaintext passwords or keys in playbooks is dangerous.


**Ansible Vault** encrypts sensitive variables and files. You can provide a
password to decrypt the secrets at runtime or configure multiple vault IDs for
different environments【773452551917097†L110-L149】.

#### Encrypting Variables

To encrypt individual variables, use the `ansible-vault encrypt_string` command:

```bash
ansible-vault encrypt_string --vault-id dev@prompt 'supersecret' --name
db_password
```

Ansible prompts for a password and outputs an encrypted block:

```yaml
db_password: !vault |
$ANSIBLE_VAULT;1.2;AES256;dev
643465333765323631396133306237663963353964303036333761663731623036
3530666338636134396337333438663730363866653664370a3365336536346339
...
```

You can paste this into your variable file or playbook. At runtime, provide the
vault password using `--vault-id dev@prompt` or a password file. Encrypting
variables keeps the rest of the file readable【970125717263838†L125-L154】.

#### Encrypting Entire Files

Use `ansible-vault encrypt` to encrypt an entire file:


```bash
ansible-vault encrypt group_vars/dbservers.yml
```

If the file does not exist, `ansible-vault create secrets.yml` opens an editor
where you can add content. To view or edit encrypted files, use `ansible-vault
view` and `ansible-vault edit`. You can change the vault password with
`ansible-vault rekey`. File-level encryption makes secrets easier to manage at
the cost of making the whole file unreadable in version control
【407927793429946†L205-L220】【407927793429946†L337-L449】.

#### Best Practices for Vault

* Use separate vault IDs for different environments (e.g., `dev`, `prod`).
* Store vault passwords in a secure storage system (HashiCorp Vault, AWS Secrets
Manager, etc.) rather than in your repository【407927793429946†L280-L331】.
* Encrypt only what is secret (use variable encryption for values, file
encryption for large sets of secrets)【970125717263838†L125-L154】.
* Use CI/CD tools to inject vault passwords through environment variables
instead of prompting interactively.

### 3.3 Developing Custom Modules

Ansible’s built-in modules cover many use cases, but you may need to automate
tasks that are not supported out of the box. You can write a custom module in
Python. Place your module in a `library` directory next to your playbook. The
module must accept arguments, perform an action and return a JSON structure
describing the result. The development guide outlines the process
【417607270033536†L195-L229】:

1. **Create a `library` directory** inside your project.


2. **Write a Python module** (`my_service.py`) with required documentation and
argument specification:

```python
#!/usr/bin/python
from ansible.module_utils.basic import AnsibleModule

DOCUMENTATION = r'''
---
module: my_service
short_description: Manage a fictitious service
version_added: "1.0"
description: This module starts or stops a fictitious service.
options:
name:
description: Name of the service
required: true
type: str
state:
description: Desired state (started or stopped)
required: true
choices: [ started, stopped ]
type: str
author:
- You
'''

EXAMPLES = r'''
# Start myservice
- my_service:
name: myservice
state: started
'''

def run_module():
module_args = dict(
name=dict(type='str', required=True),
state=dict(type='str', required=True, choices=['started', 'stopped'])
)

module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)

result = dict(changed=False)

name = module.params['name']
desired = module.params['state']

# Example logic (replace with real commands)


if desired == 'started':
# Start the service here
result['changed'] = True
result['msg'] = f"Service {name} started"
else:
# Stop the service here
result['changed'] = True
result['msg'] = f"Service {name} stopped"

module.exit_json(**result)

def main():
run_module()

if __name__ == '__main__':
main()
```

3. **Call the module** in your playbook:

```yaml
- hosts: localhost
tasks:
- name: Start myservice
my_service:
name: myservice
state: started
```

Ansible automatically loads modules from the `library` directory. Use


`check_mode` to simulate changes. Return meaningful messages and handle errors
gracefully. Writing modules expands Ansible’s functionality to suit your
organisation’s needs.

### 3.4 Performance Optimisation

Large playbooks or slow network connections can cause long execution times.
Open source contributors recommend several techniques to speed up Ansible runs
【345425124648387†L148-L169】【345425124648387†L164-L191】:

* **SSH connection reuse** – set `ssh_args = -o ControlMaster=auto -o


ControlPersist=60s` in your `ansible.cfg` to enable persistent SSH connections.
This reduces the overhead of establishing a new connection for every task.
* **Enable pipelining** – set `pipelining = True` in `ansible.cfg`. Pipelining
sends module code over the existing SSH connection instead of copying a
temporary script file, cutting down round-trip time.
* **Adjust forks** – the `forks` setting controls how many hosts Ansible manages
in parallel. Increasing it from the default (5) can accelerate execution, but
be mindful of network and CPU limits.
* **Disable facts gathering** – setting `gather_facts: false` in a play disables
automatic collection of system facts, which speeds up tasks when those facts are
unnecessary【345425124648387†L148-L169】.
* **Use async and poll** – asynchronous tasks allow Ansible to start a
long-running task on a host and move on to the next host without waiting. For
example:

```yaml
- name: Compile software asynchronously
command: make all
async: 3600
poll: 0
```

The `async` parameter specifies the maximum time (seconds) the job may run;
`poll: 0` tells Ansible not to wait. You can later check the result with the
`async_status` module【345425124648387†L164-L191】.

* **Pull mode** – `ansible-pull` inverts the model: each node pulls playbooks
from a Git repository and executes them locally. This decentralises work and
can improve scalability for large clusters【345425124648387†L164-L191】.

* **Rolling updates** – using `serial` ensures only a few hosts are updated at a
time. Combine this with health checks and handlers for robust deployments.

### 3.5 Integrating Ansible into CI/CD Pipelines

Continuous Integration/Continuous Delivery (CI/CD) pipelines bring automation


into the software development lifecycle. You can use Ansible in your existing
CI tools (Jenkins, GitHub Actions, GitLab CI, Azure DevOps) to deploy
infrastructure or validate playbooks. A typical workflow suggested by
practitioners includes【634009329914428†L202-L233】【634009329914428†L246-L272】:

1. **Lint and syntax check** – run `ansible-lint` and `ansible-playbook --


syntax-check` on pull requests to enforce style and catch errors early.
2. **Dry run in staging** – execute playbooks in `--check` mode against a
staging inventory to preview changes without affecting production.
3. **Automated testing** – use test frameworks (Molecule, Testinfra) to verify
that tasks produce the desired state.
4. **Deployment** – after checks pass, run the playbook on the target
environment. Use tags and `--limit` to control scope. Notify relevant teams
about the deployment.
5. **Credentials and secrets** – integrate vault passwords or cloud credentials
securely via environment variables or secret management services. Avoid
hardcoding them in the pipeline【634009329914428†L246-L272】.

Red Hat Ansible Automation Platform (AAP) and its open-source upstream AWX
provide a web UI, job scheduling, role-based access control (RBAC), audit trails
and credential management. AWX/AAP can synchronise dynamic inventories, manage
project repositories and expose a REST API for launching jobs
【437227952386536†L122-L146】. When you need enterprise features such as
multi-tenant access, SAML/OIDC integration and analytics, consider running AWX
or subscribing to AAP.

#### Diagram: CI/CD Pipeline with Ansible

```text
Developer push


CI Pipeline
┌──────────────────────────────────────────────┐
│ 1. Lint & syntax check (ansible-lint, │
│ ansible-playbook --syntax-check) │
│ 2. Dry run on staging (--check, --diff) │
│ 3. Automated tests (Molecule/Testinfra) │
│ 4. Deployment to production (ansible-playbook) │
│ 5. Notifications and cleanup │
└──────────────────────────────────────────────┘


Target Environment
```

Integrating Ansible into your pipelines ensures that infrastructure changes


undergo the same rigorous review and testing as application code.

### 3.6 Event-Driven Ansible and AI-Assisted Automation

In 2024–2025 Ansible introduced **event-driven automation**, allowing playbooks


to be triggered automatically based on observed events rather than manual
commands. Red Hat’s Event-Driven Ansible listens for events from observability
tools or custom sources and evaluates them against rules to determine which
playbook to run. The system provides a web UI, auditing and RBAC to design,
operate and scale event-driven workflows【330454119573773†L54-L76】. With
event-driven automation, a change in a monitoring alert can automatically
trigger remediation actions, reducing response time and human error.

Another emerging feature is **Ansible Lightspeed**, a generative AI service that


helps users write playbooks. Lightspeed leverages IBM’s Watson technology to
suggest code snippets, explain modules and generate tasks based on natural
language input【595856967600130†L52-L70】【595856967600130†L89-L113】. While
generative AI should be used responsibly and reviewed by humans, it can
accelerate the creation of playbooks and serve as an educational assistant.

### 3.7 Mini-Project: Cloud Provisioning with Secrets and CI/CD

This comprehensive project ties together advanced topics:

1. **Create a dynamic inventory** for AWS using the `aws_ec2` plugin. Tag your
EC2 instances with roles such as `Role=webserver` and `Role=dbserver`.
Configure the plugin as shown earlier.

2. **Encrypt secrets**: store your database password and AWS credentials in


`group_vars` using `ansible-vault encrypt_string` with separate vault IDs for
development and production.

3. **Develop custom logic**: write a simple custom module that queries an


internal API to retrieve the latest application version. Place the module in
the `library` directory and call it in your playbook.

4. **Optimise performance**: enable pipelining and persistent SSH connections in


`ansible.cfg`, set `forks` appropriately and disable facts gathering when not
needed.

5. **Integrate with CI/CD**: create a Jenkins or GitHub Actions pipeline that


lints your playbooks, performs a dry run against a staging environment and, upon
approval, deploys the application to your AWS environment using the dynamic
inventory. Pass vault passwords and AWS keys securely via environment secrets.
Use `serial` to perform rolling updates.
6. **Add event-driven remediation**: configure Event-Driven Ansible (if
available in your environment) to listen for high CPU alerts and automatically
scale your EC2 cluster by invoking a playbook that adds new instances.

This project demonstrates how Ansible integrates with modern cloud, security and
DevOps practices. Adjust the scope based on your environment – you could
substitute AWS with Azure, on-premises servers or containers.

---

## Appendix

### A. Ansible 2.16 Highlights

Released in late 2024, Ansible 2.16 includes improvements such as support for
**Python 3.12**, the retirement of Python 3.5 for modules and Python 3.9 for the
controller, improved CLI argument parsing, enhanced documentation and the
removal of long-deprecated features【698678219570606†L12-L99】. The default
transport has changed from `smart` to `ssh` to simplify configuration. New
remote execution environments support Alpine 3.18, Fedora 38, FreeBSD 13.2 and
RHEL 8.8/9.2, and deprecation warnings have been removed or converted into
errors【698678219570606†L12-L99】. Staying current with Ansible versions
ensures you benefit from performance improvements, security patches and new
modules.

### B. Further Reading and Resources

* **Official documentation**: <https://docs.ansible.com/> – comprehensive


reference for modules, plugins and best practices.
* **Ansible Galaxy**: <https://galaxy.ansible.com/> – discover and share
community roles and collections.
* **Event-Driven Ansible**:
<https://www.redhat.com/en/technologies/management/ansible/event-driven> – learn
about event-driven automation and try the developer preview.
* **Molecule**: <https://molecule.readthedocs.io/> – test infrastructure roles
locally with Docker or Vagrant.
* **Testinfra**: <https://testinfra.readthedocs.io/> – write Python tests for
server state.
* **Ansible Lint**: <https://ansible-lint.readthedocs.io/> – enforce style
guidelines and catch common pitfalls.

Thank you for reading this handbook. Ansible empowers you to turn manual
processes into repeatable, reliable automation. Use this knowledge to build
robust infrastructure and share your roles with the community. Happy
automating!

You might also like