Cheatsheets

Ansible

Ansible

Comprehensive Ansible cheatsheet covering playbooks, inventories, roles, tasks, variables, handlers, ad-hoc commands, modules, and configuration options.

10 Categories 36 Sections 72 Examples

Getting Started

Installation & Setup

Install Ansible and configure basic settings

Install Ansible on Ubuntu

Code
Terminal window
sudo apt update
sudo apt install -y ansible

Install Ansible via pip

Code
Terminal window
pip install ansible
pip install ansible==2.10.7

Verify Ansible Installation

Displays installed Ansible version and configuration details

Code
Terminal window
ansible --version
Execution
Terminal window
ansible 2.10.7
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
executable location = /usr/bin/ansible
python version = 3.10.12
  • Version should be 2.9+
  • Python 3.6+ required

Ansible Configuration File

Main configuration file setup at ansible.cfg

Create Basic ansible.cfg

Code
[defaults]
inventory = ./hosts
remote_user = ubuntu
private_key_file = ~/.ssh/id_rsa
host_key_checking = False
gather_facts = True

Configure Parallel Execution

forks increases parallelism for faster execution

Code
[defaults]
forks = 10
timeout = 30
retries = 3
  • Default forks value is 5
  • Higher forks = more parallelism = higher memory usage

First Playbook Run

Execute your first Ansible playbook

Simple Hello World Playbook

Basic playbook that prints a debug message

Code
---
- name: Hello World
hosts: all
tasks:
- name: Print Hello
debug:
msg: "Hello from Ansible"
Execution
Terminal window
PLAY [Hello World] *****
TASK [Print Hello] *****
ok: [localhost] => {
"msg": "Hello from Ansible"
}
PLAY RECAP *****
localhost : ok=1 changed=0 failed=0
  • Playbooks must be valid YAML
  • [object Object]

Test Host Connectivity

Verify connectivity to managed hosts

Ping All Hosts

Tests connectivity to all hosts without running tasks

Code
Terminal window
ansible all -i hosts -m ping
Execution
Terminal window
webserver01 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
database01 | SUCCESS => {
"ping": "pong"
}
  • ping module requires Python on remote
  • SUCCESS indicates host is reachable

Inventory Management

Inventory Formats

Different ways to define managed hosts

INI Format Inventory

INI format with groups and variables

Code
[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11
[databases]
db1 ansible_host=192.168.1.20
db2 ansible_host=192.168.1.21
[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_rsa
  • Groups defined with [groupname]
  • [all:vars] applies to all hosts

YAML Format Inventory

YAML format for complex hierarchies

Code
all:
children:
webservers:
hosts:
web1:
ansible_host: 192.168.1.10
web2:
ansible_host: 192.168.1.11
databases:
hosts:
db1:
ansible_host: 192.168.1.20
vars:
ansible_user: ubuntu
  • Better for nested groups
  • More readable for complex inventories

Host Patterns

Select specific hosts or groups

Target Specific Groups

webservers targets group, !web2 excludes host

Code
Terminal window
ansible webservers -i hosts -m ping
ansible databases -i hosts -m ping
ansible 'webservers:!web2' -i hosts -m ping
Execution
Terminal window
web1 | SUCCESS => {"ping": "pong"}
web2 | SUCCESS => {"ping": "pong"}
  • webservers selects entire group
  • negates selection

Wildcard and Range Patterns

Wildcards match patterns, [1:2] matches range

Code
Terminal window
ansible 'web*' -i hosts -m ping
ansible 'db[1:2]' -i hosts -m ping
ansible 'webservers[0]' -i hosts -m ping
  • * matches any characters
  • [1:2] includes indexes 1 and 2

Dynamic Inventory

Generate inventory from external sources

EC2 Dynamic Inventory

AWS EC2 plugin pulls instances from AWS

Code
plugin: aws_ec2
regions:
- us-east-1
filters:
tag:Environment: production
keyed_groups:
- key: placement.region_name
prefix: aws_region
  • Requires boto3 library
  • Cache results for performance

Custom Inventory Script

Custom script outputs JSON inventory format

Code
#!/bin/bash
cat << EOF
{
"webservers": {
"hosts": ["web1", "web2"]
}
}
EOF
  • Script must be executable
  • Output must be valid JSON

Host Variables & Priorities

Set variables at different levels

Host-Level Variables

Variables specific to individual hosts

Code
all:
children:
webservers:
hosts:
web1:
ansible_host: 192.168.1.10
http_port: 80
server_role: primary
  • Host variables override group variables
  • Highest precedence level

Group Variables File

Load variables from files by host/group name

Code
Terminal window
group_vars/webservers.yml
group_vars/webservers/main.yml
host_vars/web1.yml
  • group_vars/groupname.yml for entire group
  • host_vars/hostname.yml for single host

Ad-Hoc Commands

Basic Syntax

Run single tasks without playbook

Run Adhoc Command

Execute shell command on all hosts

Code
Terminal window
ansible all -i hosts -m shell -a "uptime"
Execution
Terminal window
web1 | CHANGED | rc=0 >>
10:45:23 up 5 days, 3:21, 2 users, load average: 0.05, 0.10, 0.08
web2 | CHANGED | rc=0 >>
10:45:24 up 3 days, 1:15, 1 user, load average: 0.02, 0.06, 0.05
  • -m specifies module
  • -a passes arguments

Use copy Module

Copy file from control node to remote

Code
Terminal window
ansible webservers -i hosts -m copy -a "src=/etc/hosts dest=/tmp/hosts"
Execution
Terminal window
web1 | CHANGED => {
"changed": true,
"checksum": "5d41402abc4b2a76b9719d911017c592",
"dest": "/tmp/hosts",
"gid": 0,
"mode": "0644",
"owner": "root",
"size": 158,
"src": "/tmp/ansible.87a8vz/hosts",
"state": "file"
}
  • src path on control node
  • dest path on remote

Common Ad-Hoc Modules

Frequently used modules for ad-hoc tasks

Package Management

Install nginx package

Code
Terminal window
ansible webservers -i hosts -m apt -a "name=nginx state=present"
Execution
Terminal window
web1 | CHANGED => {
"changed": true,
"stderr": "",
"stdout": "Reading package lists..."
}
  • apt for Debian/Ubuntu
  • yum for Red Hat/CentOS

Service Management

Start nginx service and enable on boot

Code
Terminal window
ansible webservers -i hosts -m service -a "name=nginx state=started enabled=yes"
Execution
Terminal window
web1 | CHANGED => {
"changed": true,
"enabled": true,
"name": "nginx",
"state": "started"
}
  • [object Object]
  • enabled=yes enables service on boot

File Permissions

Set file permissions and ownership

Code
Terminal window
ansible all -i hosts -m file -a "path=/tmp/test.txt mode=0644 owner=root"
Execution
Terminal window
web1 | CHANGED => {
"changed": true,
"mode": "0644",
"owner": "root",
"path": "/tmp/test.txt"
}
  • mode in octal format
  • owner and group can be specified

Parallelism & Forks

Control how many hosts execute simultaneously

Limit Concurrent Execution

Execute on 2 hosts at a time instead of default

Code
Terminal window
ansible all -i hosts -m ping -f 2
  • -f or --forks sets parallelism
  • Lower for safety, higher for speed

Serial Execution

Execute one at a time (serial)

Code
Terminal window
ansible all -i hosts -m shell -a "systemctl restart app" -f 1
  • Useful for rolling restarts
  • Prevents service downtime

Playbooks

Playbook Structure

Create and organize playbooks

Basic Playbook Structure

Complete playbook with all major sections

Code
---
- name: Deploy Web Application
hosts: webservers
become: yes
vars:
app_port: 8080
app_user: www-data
tasks:
- name: Install dependencies
apt:
name: python3-pip
state: present
- name: Start application
command: /opt/app/start.sh
  • --- indicates YAML document start
  • [object Object]
  • [object Object]

Multiple Plays in Single Playbook

Multiple plays execute sequentially

Code
---
- name: Configure Databases
hosts: databases
tasks:
- debug: msg="Setting up database"
- name: Configure Web Servers
hosts: webservers
tasks:
- debug: msg="Setting up web server"
  • Each play is independent
  • Plays execute in order

Playbook Execution

Run playbooks with various options

Run Basic Playbook

Execute playbook against specified inventory

Code
Terminal window
ansible-playbook playbook.yml -i hosts
Execution
Terminal window
PLAY [Deploy Web Application] *****
TASK [Install dependencies] *****
ok: [web1]
ok: [web2]
TASK [Start application] *****
changed: [web1]
changed: [web2]
PLAY RECAP *****
web1 : ok=2 changed=1 failed=0
web2 : ok=2 changed=1 failed=0
  • ok = task already in desired state
  • changed = task made changes

Check Mode (Dry Run)

Preview changes without executing

Code
Terminal window
ansible-playbook playbook.yml -i hosts --check
  • Shows what would change
  • Useful before production runs

Run with Extra Variables

Pass variables from command line

Code
Terminal window
ansible-playbook playbook.yml -i hosts -e "env=production app_version=2.0"
  • -e or --extra-vars for variables
  • Overrides defaults in playbook

Verbose Output

Show detailed execution information

Code
Terminal window
ansible-playbook playbook.yml -i hosts -vvv
  • -v = info, -vv = debug, -vvv = extra debug

Conditionals

Execute tasks based on conditions

Simple When Condition

Skip task if condition false

Code
- name: Install nginx if not exists
apt:
name: nginx
state: present
when: ansible_distribution == "Ubuntu"
Execution
Terminal window
TASK [Install nginx if not exists] *****
skipped: [web1] => (item=centos)
ok: [web2] => (item=ubuntu)
  • Conditions use when keyword
  • Access facts with ansible_variablename

Multiple Conditions

All conditions must be true (AND logic)

Code
- name: Restart service
service:
name: nginx
state: restarted
when:
- ansible_os_family == "Debian"
- ansible_distribution_version >= "20.04"
  • List format uses AND logic
  • [object Object]

Tasks & Handlers

Task Structure

Define and organize tasks

Basic Task Definition

Task with module, arguments, and tags

Code
- name: Install web server
apt:
name: nginx
state: present
become: yes
tags:
- webserver
- packages
  • [object Object]
  • [object Object]

Register Task Output

Save task output in variable for later use

Code
- name: Get service status
command: systemctl status nginx
register: nginx_status
changed_when: false
- name: Display status
debug:
msg: "{{ nginx_status.stdout }}"
  • register stores full result
  • Access with variable name

Handlers

Run tasks on change events

Basic Handler

Handler executes if task notifies it

Code
- name: Deploy application
hosts: webservers
tasks:
- name: Copy app config
copy:
src: app.conf
dest: /etc/app/app.conf
notify: restart app
handlers:
- name: restart app
service:
name: app
state: restarted
  • Handlers only run if task changed
  • Run at end of play by default

Multiple Handlers

Single task can notify multiple handlers

Code
tasks:
- name: Update config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify:
- reload nginx
- log config change
handlers:
- name: reload nginx
service:
name: nginx
state: reloaded
- name: log config change
command: logger "nginx config updated"
  • Handlers run in defined order
  • Duplicate handler calls only run once

Loops

Repeat tasks with different variables

Loop Over List

Item variable changes per loop iteration

Code
- name: Install multiple packages
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- php-fpm
- mysql-client
Execution
Terminal window
TASK [Install multiple packages] *****
ok: [web1] => (item=nginx)
ok: [web1] => (item=php-fpm)
ok: [web1] => (item=mysql-client)
  • [object Object]
  • item is implicit variable name

Loop Over Dict

Loop over dictionary structures

Code
- name: Create users
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
state: present
loop:
- { name: 'alice', uid: 1001 }
- { name: 'bob', uid: 1002 }
  • Access dict items with dot notation
  • Useful for complex configurations

Variables & Facts

Variable Definition

Define and use variables

Play-Level Variables

Define variables at play scope

Code
- name: My playbook
hosts: all
vars:
db_host: localhost
db_port: 3306
db_name: myapp
env: production
tasks:
- name: Connect to database
debug:
msg: "Connecting to {{ db_host }}:{{ db_port }}"
  • Accessible in all tasks
  • {{ }} for variable substitution

Task-Level Variables

Variables local to specific task

Code
- name: Configure service
service:
name: nginx
state: started
vars:
nginx_user: www-data
nginx_workers: 4
  • Only available in this task's context

Gathering Facts

Collect system information from hosts

Auto-Gather Facts

Facts gathered by default unless disabled

Code
- name: Display fact
hosts: all
tasks:
- name: Show OS
debug:
msg: "OS: {{ ansible_os_family }}, Memory: {{ ansible_memtotal_mb }}"
Execution
Terminal window
TASK [Show OS] *****
ok: [web1] => {
"msg": "OS: Debian, Memory: 4096"
}
  • ansible_* variables are facts
  • [object Object]

Custom Facts

Define custom facts in JSON files

Code
Terminal window
/etc/ansible/facts.d/custom.fact
{
"app_version": "2.1.0",
"deployment_date": "2026-01-15"
}
  • Store in /etc/ansible/facts.d/
  • Extension must be .fact

Variable Precedence

Order of variable resolution

Precedence Levels

Variables override based on source location

Code
# Lowest to highest precedence:
# 1. defaults/main.yml in role
# 2. group_vars/all
# 3. group_vars/groupname
# 4. host_vars/hostname
# 5. vars in playbook
# 6. vars_files
# 7. task vars
# 8. -e extra vars (highest)
  • Command line (-e) always wins
  • Host vars override group vars

Override with Extra Vars

Extra vars override all others

Code
Terminal window
ansible-playbook playbook.yml -e "db_host=newhost db_port=5432"
  • -e highest precedence
  • Useful for overriding defaults

Variable Templating

Template and manipulate variable values

Jinja2 Filters

Transform variables with filters

Code
- name: Use filters
debug:
msg: |
Uppercase: {{ service_name | upper }}
Lowercase: {{ domain | lower }}
Default: {{ undefined_var | default('localhost') }}
List join: {{ servers | join(',') }}
Execution
Terminal window
TASK [Use filters] *****
ok: [localhost] => {
"msg": "Uppercase: NGINX\nLowercase: example.com\nDefault: localhost\nList join: web1,web2,web3"
}
  • | for multiline strings
  • | filters modify variable values

Roles

Role Structure

Organize code with roles

Role Directory Structure

Standard role directory structure

Code
Terminal window
roles/webserver/
├── tasks/
└── main.yml
├── handlers/
└── main.yml
├── vars/
└── main.yml
├── defaults/
└── main.yml
├── files/
├── templates/
└── meta/
└── main.yml
  • tasks/ contains main task execution
  • defaults/ provides variable defaults

Role Definition

Define tasks in role

Code
roles/webserver/tasks/main.yml
---
- name: Install web server
apt:
name: nginx
state: present
become: yes
- name: Copy config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
  • Include handler definitions
  • Keep role single-purpose

Role Options & Includes

Include and configure roles

Include Role in Playbook

Include multiple roles

Code
- name: Deploy web application
hosts: webservers
roles:
- webserver
- php
- mysql
  • Roles execute in order
  • Most common role usage

Role with Variables

Pass variables to roles

Code
- name: Deploy web application
hosts: webservers
roles:
- role: webserver
vars:
nginx_port: 8080
nginx_workers: 8
- role: php
when: "'php' in group_names"
  • Vars override role defaults
  • when can conditionally include roles

Role Dependencies

Manage role dependencies

Declare Dependencies

Specify required roles

Code
roles/php/meta/main.yml
---
dependencies:
- role: webserver
- role: database
vars:
db_type: mysql
  • Dependencies execute first
  • Can pass variables to dependencies

Modules

System Modules

System administration modules

User Management

Create system user with specified properties

Code
- name: Create user
user:
name: appuser
uid: 2000
home: /opt/app
shell: /bin/bash
state: present
Execution
Terminal window
ok: [web1] => {
"changed": false,
"comment": "",
"home": "/opt/app",
"name": "appuser",
"shell": "/bin/bash",
"uid": 2000
}
  • [object Object]
  • uid specifies numeric user id

File Management

Create directory with permissions

Code
- name: Create directory
file:
path: /opt/app/data
state: directory
mode: '0755'
owner: appuser
recurse: yes
Execution
Terminal window
changed: [web1] => {
"changed": true,
"mode": "0755",
"owner": "appuser",
"path": "/opt/app/data",
"state": "directory"
}
  • [object Object]
  • [object Object]

Package Management

Install and manage packages

APT Package Manager

Install multiple packages with cache update

Code
- name: Install packages
apt:
name:
- curl
- wget
- git
state: present
update_cache: yes
  • update_cache updates apt database
  • [object Object]

PIP Package Manager

Install Python packages in virtualenv

Code
- name: Install Python packages
pip:
name:
- django==3.2
- requests>=2.25
virtualenv: /opt/app/venv
  • virtualenv creates/uses virtualenv
  • Version pinning with == or >=

Command Execution

Execute commands on remote hosts

Command Module (Preferred)

Execute command without shell processing

Code
- name: Get nginx version
command: nginx -v
register: nginx_version
changed_when: false
Execution
Terminal window
ok: [web1] => {
"cmd": ["nginx", "-v"],
"rc": 0,
"stderr": "nginx version: nginx/1.18.0",
"stdout": ""
}
  • No shell expansion (*, |, &)
  • More predictable

Shell Module

Execute with shell processing

Code
- name: Check if file exists
shell: |
if [ -f /opt/app/config.yml ]; then
echo "exists"
else
echo "missing"
fi
register: config_check
  • Supports pipes, redirects
  • Less secure, use with care

Web & Net Modules

HTTP and network operations

HTTP Request

Make HTTP request and validate response

Code
- name: Health check
uri:
url: http://localhost:8080/health
method: GET
status_code: 200
register: health_check
Execution
Terminal window
ok: [web1] => {
"changed": false,
"content": "{\"status\":\"ok\"}",
"status": 200
}
  • status_code validates response
  • body can parse JSON response

Advanced Features

Blocks & Error Handling

Group tasks and handle errors

Block with Error Handling

Block groups tasks with error handling

Code
- name: Database operations
block:
- name: Connect to database
debug: msg="Connecting"
- name: Run migrations
command: /opt/app/migrations.sh
rescue:
- name: Rollback on error
command: /opt/app/rollback.sh
always:
- name: Cleanup
file:
path: /tmp/app.lock
state: absent
  • [object Object]
  • [object Object]
  • [object Object]

Validate Task Results

Control when task is marked failed/changed

Code
- name: Run test
shell: npm test
failed_when: "npm_test.rc != 0"
changed_when: false
register: npm_test
  • failed_when override failure detection
  • changed_when override change detection

Asynchronous Execution

Run long-running tasks in background

Async with Polling

Run 1-hour task, check every 10 seconds

Code
- name: Long running task
shell: /opt/app/long-job.sh
async: 3600
poll: 10
register: long_job
Execution
Terminal window
ASYNC OK on web1 (job_id=123456789.12345)
  • [object Object]
  • [object Object]

Fire and Forget

Start task and don't wait for completion

Code
- name: Start background service
shell: /opt/app/service.sh
async: 0
poll: 0
  • async: 0 and poll: 0 for fire-and-forget

Advanced Variable Usage

Complex variable patterns

Access Host Variables

Access variables from other hosts

Code
- name: Reference another host
debug:
msg: "Database host: {{ hostvars['db1']['ansible_default_ipv4']['address'] }}"
  • hostvars dict contains all host info
  • Useful in multi-host plays

Nested Variable Access

Access deeply nested structures

Code
vars:
services:
web:
port: 8080
workers: 4
db:
port: 5432
replicas: 3
tasks:
- debug: msg="Web port {{ services.web.port }}"
  • Dot notation for nested access
  • Bracket notation also works

Plugins & Extensions

Extend Ansible functionality

Custom Filters

Apply custom filter plugins

Code
- name: Use custom filter
debug:
msg: "{{ 'hello' | custom_uppercase }}"
  • Place filters in filter_plugins/
  • Extend Jinja2 capabilities

Lookup Plugins

Use lookup plugins for dynamic data

Code
- name: Read file content
set_fact:
file_content: "{{ lookup('file', '/etc/config.yml') }}"
- name: Get environment variable
debug:
msg: "{{ lookup('env', 'HOME') }}"
  • file, env, pipe lookups common
  • Results available mid-playbook

Vault & Security

Vault Basics

Encrypt sensitive data

Create Vault File

Create encrypted YAML file

Code
Terminal window
ansible-vault create secrets.yml
# Enter password, then edit:
# db_password: "secret123"
# api_key: "abc123xyz"
Execution
Terminal window
New Vault password:
Confirm New Vault password:
  • Creates .yml with encrypted content
  • Prompts for password

View Vault Content

Decrypt and display vault file

Code
Terminal window
ansible-vault view secrets.yml
# Password prompt
  • Requires vault password
  • Doesn't save decrypted content

Edit Vault File

Edit encrypted vault file safely

Code
Terminal window
ansible-vault edit secrets.yml
  • Opens in editor
  • Re-encrypts on save

Using Vault in Playbooks

Reference vault variables in playbooks

Include Vault Variables

Load vault variables in playbook

Code
- name: Deploy app
hosts: webservers
vars_files:
- secrets.yml
tasks:
- name: Configure database
template:
src: db.conf.j2
dest: /etc/app/db.conf
vars:
db_password: "{{ vault_db_password }}"
  • [object Object]
  • Automatic decryption on run

Run Playbook with Vault

Run playbook requiring vault password

Code
Terminal window
ansible-playbook playbook.yml -i hosts --ask-vault-pass
Execution
Terminal window
Vault password:
PLAY [Deploy app] *****
TASK [Configure database] *****
ok: [web1]
  • --ask-vault-pass prompts for password
  • --vault-password-file for automation

Encrypt Individual Files

Encrypt specific files or variables

Encrypt Single Variable

Encrypt individual secret for playbook

Code
Terminal window
ansible-vault encrypt_string --ask-vault-pass 'mypassword'
Execution
Terminal window
Reading plaintext input from stdin.
New Vault password:
Confirm New Vault password:
!vault |
$ANSIBLE_VAULT;1.1;AES256
66386...
  • Generates encrypted string
  • Paste into playbook vars

Encrypt Specific Files

Encrypt individual variable files

Code
Terminal window
ansible-vault encrypt host_vars/prod.yml
  • Encrypts existing file in place

Vault Best Practices

Security practices with vault

Password File Not in Repo

Store vault password file locally, not in git

Code
Terminal window
.gitignore:
.vault_pass
Run playbook:
ansible-playbook playbook.yml --vault-password-file ~/.vault_pass
  • Keep .vault_pass outside repository
  • Use --vault-password-file for automation

Different Vaults for Environments

Separate encrypted files per environment

Code
Terminal window
group_vars/prod/secrets.yml (encrypted)
group_vars/dev/secrets.yml (encrypted)
group_vars/staging/secrets.yml (encrypted)
  • Different passwords per environment
  • Enhanced security isolation