Infrastructure Automation Reference
Ansible
$ ansible --version · Complete Cheat Sheet
01 — Core Concepts
📖Key Terminology
| Term | Description |
|---|---|
| Control Node | Machine where Ansible is installed and runs from |
| Managed Node | Remote host Ansible manages — no agent required |
| Inventory | File listing hosts and groups to manage |
| Playbook | YAML file with ordered list of plays and tasks |
| Play | Maps a set of hosts to a list of tasks |
| Task | Single unit of work — calls exactly one module |
| Module | Reusable unit of code (apt, copy, template…) |
| Role | Structured, reusable unit of playbook content |
| Handler | Task triggered only when notified by another task |
| Fact | System info auto-gathered from managed nodes |
| Template | Jinja2 file rendered with variable substitution |
| Vault | Encryption for sensitive data in playbooks |
| Collection | Distributable package of roles, modules, plugins |
⚙️Installation & Config
# Install on Ubuntu/Debian
sudo apt update && sudo apt install -y ansible
# Install via pip
pip3 install ansible
# Verify installation
ansible --version
# Config file search order
1. ANSIBLE_CONFIG (env variable)
2. ./ansible.cfg (current directory)
3. ~/.ansible.cfg (home directory)
4. /etc/ansible/ansible.cfg
# ansible.cfg example
[defaults]
inventory = ./inventory
remote_user = ubuntu
private_key_file = ~/.ssh/id_rsa
host_key_checking = False
forks = 10
timeout = 30
02 — Inventory
📄INI Formathosts
[webservers]
web1.example.com
web2.example.com ansible_port=2222
[dbservers]
db1.example.com
db2.example.com
[datacenter:children]
webservers
dbservers
[all:vars]
ansible_user = ubuntu
ansible_ssh_private_key_file= ~/.ssh/id_rsa
📄YAML Formatinventory.yml
all:
children:
webservers:
hosts:
web1:
ansible_host: 192.168.1.10
web2:
ansible_host: 192.168.1.11
dbservers:
hosts:
db1:
ansible_host: 192.168.1.20
vars:
ansible_user: ubuntu
# Dynamic inventory commands
ansible-inventory -i aws_ec2.yml --list
ansible-inventory -i inventory/ --graph
🔑Connection Variables
| Variable | Purpose |
|---|---|
| ansible_host | IP / hostname to connect to |
| ansible_port | SSH port (default 22) |
| ansible_user | Remote user for SSH |
| ansible_password | SSH password — use vault! |
| ansible_ssh_private_key_file | Path to private key |
| ansible_become | Enable privilege escalation |
| ansible_become_user | User to become (default root) |
| ansible_become_method | sudo, su, pbrun, pfexec… |
| ansible_connection | ssh, local, docker, winrm… |
| ansible_python_interpreter | Python path on remote host |
🎯Host Patterns
| Pattern | Matches |
|---|---|
| all / * | All hosts in inventory |
| webservers | All hosts in a group |
| web1.example.com | Specific host |
| web* | Wildcard name match |
| web:db | Union of two groups |
| web:&db | Intersection (in both) |
| web:!staging | web minus staging group |
| webservers[0] | First host in group |
| webservers[0:2] | First three hosts |
| ~web.*\.com | Regex pattern |
03 — Ad-Hoc Commands
⚡Common Commands
# Syntax
ansible <pattern> -m <module> -a "args" [options]
# Ping all hosts
ansible all -m ping
# Run shell command
ansible webservers -m shell -a "uptime"
# Copy a file
ansible all -m copy -a "src=/tmp/f dest=/tmp/f"
# Install package (with sudo)
ansible webservers -m apt \
-a "name=nginx state=present" -b
# Gather all facts
ansible web1 -m setup
# Filter specific fact
ansible web1 -m setup -a "filter=ansible_os_family"
🚩Common Flags
| Flag | Description |
|---|---|
| -i inventory | Inventory file, directory or script |
| -u user | Remote SSH user |
| -b | Become (sudo / privilege escalation) |
| -K | Prompt for become password |
| --ask-pass | Prompt for SSH password |
| -f 10 | Forks / parallelism level |
| -v / -vvv | Verbosity (1–4 levels) |
| --check | Dry run — no changes made |
| --diff | Show diffs for changed files |
| -l host1,host2 | Limit to specific hosts |
| --tags tag1 | Run only tagged tasks |
04 — Playbooks
📝Full Playbook ExampleYAML
---
# Full playbook structure
- name: Configure web servers
hosts: webservers
become: true
gather_facts: true
vars:
http_port: 80
app_version: "1.2.3"
vars_files:
- vars/main.yml
- vault/secrets.yml
pre_tasks:
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
tasks:
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
tags: [install, nginx]
notify: Restart nginx
- name: Deploy config
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
mode: '0644'
handlers:
- name: Restart nginx
ansible.builtin.service:
name: nginx
state: restarted
# Run a playbook
ansible-playbook site.yml
# Useful flags
-i inventory inventory source
--check dry run (no changes)
--diff show file diffs
-v / -vvvv verbosity level 1–4
--tags "web,db" run tagged tasks only
--skip-tags "x" skip these tags
-l webservers limit to host/group
--start-at-task "name"
--step confirm each task
-e "key=val" extra vars (highest prio)
--list-tasks show tasks, no run
--list-hosts show target hosts
--syntax-check validate YAML syntax
# Variable precedence (low → high)
1. role defaults
2. inventory vars
3. inventory group_vars
4. inventory host_vars
5. playbook group_vars
6. playbook host_vars
7. host facts / cached facts
8. play vars / vars_files
9. role vars
10. block / task vars
11. include_vars
12. set_facts / registered vars
13. -e extra vars ← WINS
05 — Essential Modules
📁Files & Templates
# copy
- name: Copy config file
ansible.builtin.copy:
src: files/app.conf
dest: /etc/app/app.conf
owner: root
group: root
mode: '0644'
# template — Jinja2 rendering
- name: Render nginx config
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
# file — dirs, perms, symlinks
- name: Create directory
ansible.builtin.file:
path: /var/www/html
state: directory
mode: '0755'
# lineinfile — manage single lines
- name: Ensure line exists
ansible.builtin.lineinfile:
path: /etc/sysctl.conf
regexp: '^net.ipv4.ip_forward'
line: 'net.ipv4.ip_forward = 1'
state: present
📦Package Management
# apt — Debian / Ubuntu
- name: Install packages
ansible.builtin.apt:
name:
- nginx
- curl
- git
state: present
update_cache: true
# dnf — RHEL / CentOS / Fedora
- name: Install httpd
ansible.builtin.dnf:
name: httpd
state: latest
# pip — Python packages
- name: Install Python deps
ansible.builtin.pip:
name: [flask, gunicorn]
virtualenv: /opt/myapp/venv
Package States
present
absent
latest
removed
purged
⚙️Services & System
# service
- name: Start and enable nginx
ansible.builtin.service:
name: nginx
state: started # stopped|restarted|reloaded
enabled: true
# command — no shell features
- name: Initialize app
ansible.builtin.command:
cmd: /usr/bin/myapp --init
creates: /etc/myapp/done # skip if exists
# shell — pipes, redirection, globs
- name: Get OS info
ansible.builtin.shell:
cmd: cat /etc/os-release | grep ID
# cron — schedule a job
- name: Daily backup at 2am
ansible.builtin.cron:
name: backup
minute: "0"
hour: "2"
job: /opt/backup.sh
👤Users & SSH Keys
# user
- name: Create deploy user
ansible.builtin.user:
name: deploy
shell: /bin/bash
groups: sudo,docker
append: true
create_home: true
state: present
# authorized_key
- name: Add SSH key
ansible.posix.authorized_key:
user: deploy
state: present
key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
# fetch — pull file from remote
- name: Download log file
ansible.builtin.fetch:
src: /var/log/app.log
dest: ./logs/
flat: false
06 — Control Flow
🔄Loops
# Simple list loop
- name: Install packages
ansible.builtin.apt:
name: "{{ item }}"
state: present
loop:
- nginx
- redis
- postgresql
# Loop over list of dicts
- name: Create users
ansible.builtin.user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
loop:
- { name: alice, uid: 1001 }
- { name: bob, uid: 1002 }
# loop_control — clean output
loop_control:
label: "{{ item.name }}" # display name only
loop_var: user # rename 'item'
index_var:idx # current index
# with_sequence
- name: Create numbered dirs
ansible.builtin.file:
path: "/opt/app{{ item }}"
state: directory
with_sequence: start=1 end=5
🔀Conditionals & Error Handling
# when — single condition
- name: Install on Debian only
ansible.builtin.apt:
name: nginx
when: ansible_os_family == "Debian"
# when — multiple conditions (AND)
when:
- ansible_os_family == "Debian"
- ansible_distribution_major_version | int >= 20
# register + use result
- name: Check service status
ansible.builtin.command:
cmd: systemctl is-active nginx
register: nginx_status
ignore_errors: true
- name: Show result
ansible.builtin.debug:
msg: "nginx: {{ nginx_status.rc == 0 | ternary('up','down') }}"
# block / rescue / always
- block:
- name: Try risky task
ansible.builtin.command: /may/fail
rescue:
- name: Handle failure
ansible.builtin.debug:
msg: "Task failed, recovering"
always:
- name: Always runs
ansible.builtin.debug:
msg: "Cleanup done"
07 — Roles & Collections
🎭Role Structure
# Create role skeleton
ansible-galaxy role init myrole
myrole/
├── defaults/main.yml ← lowest priority vars
├── vars/main.yml ← high priority vars
├── tasks/main.yml ← main task list
├── handlers/main.yml ← handlers
├── templates/ ← Jinja2 .j2 files
├── files/ ← static files
├── meta/main.yml ← metadata & deps
└── README.md
# Use roles in a playbook
- hosts: webservers
roles:
- common
- role: nginx
vars:
nginx_port: 8080
tags: [web]
when: deploy_web | bool
🌐Galaxy & Collections
# Install role from Galaxy
ansible-galaxy role install geerlingguy.nginx
ansible-galaxy role install -r requirements.yml
# requirements.yml
roles:
- name: geerlingguy.nginx
version: 3.1.0
collections:
- name: community.general
version: ">=4.0.0"
- name: amazon.aws
# Useful collections
ansible.posix POSIX utilities
community.general Broad extra modules
community.docker Docker management
amazon.aws AWS cloud modules
google.cloud GCP modules
azure.azcollection Azure modules
kubernetes.core Kubernetes management
08 — Vault & Security
🔐Ansible Vault Commands
# Create encrypted file
ansible-vault create secrets.yml
# Encrypt existing file
ansible-vault encrypt vars/passwords.yml
# View / edit encrypted file
ansible-vault view secrets.yml
ansible-vault edit secrets.yml
# Decrypt file
ansible-vault decrypt secrets.yml
# Encrypt a single string
ansible-vault encrypt_string 'mysecret' --name db_pass
🗝️Using Vault & Utilities
# Run with vault password prompt
ansible-playbook site.yml --ask-vault-pass
# Use a password file
ansible-playbook site.yml \
--vault-password-file ~/.vault_pass
# Set in ansible.cfg
[defaults]
vault_password_file = ~/.vault_pass
# ansible-doc — module help
ansible-doc ansible.builtin.apt
ansible-doc -l # list all modules
# ansible-config
ansible-config dump --only-changed
ansible-config list
# ansible-lint
pip install ansible-lint
ansible-lint site.yml
09 — Jinja2 Templating
🧪Variables & Filters
# Variable access
"{{ variable }}"
"{{ dict.key }}"
"{{ list[0] }}"
"{{ nested['key'] }}"
# Filters
"{{ var | default('fallback') }}"
"{{ var | upper }}"
"{{ var | lower }}"
"{{ var | trim }}"
"{{ var | replace('a', 'b') }}"
"{{ list | join(',') }}"
"{{ list | length }}"
"{{ list | first }}"
"{{ list | sort }}"
"{{ list | unique }}"
"{{ num | int }}"
"{{ var | bool }}"
"{{ var | to_json }}"
"{{ var | from_json }}"
"{{ var | b64encode }}"
"{{ var | b64decode }}"
"{{ var | hash('sha256') }}"
"{{ var | password_hash('sha512') }}"
🔍Tests, Lookups & Control
# Tests — used in when:
var is defined
var is undefined
var is none
var is string
var is number
path is file
path is directory
path is exists
# Lookups
"{{ lookup('file', '/etc/hostname') }}"
"{{ lookup('env', 'HOME') }}"
"{{ lookup('pipe', 'date +%Y') }}"
# Conditionals in templates
{% if env == 'prod' %}
workers 8;
{% elif env == 'staging' %}
workers 2;
{% else %}
workers 1;
{% endif %}
# Loops in templates
{% for host in groups['webservers'] %}
server {{ hostvars[host].ansible_host }};
{% endfor %}
🪄Magic Variables
| Variable | Description |
|---|---|
| hostvars | All variables for all hosts |
| groups | All inventory groups and their hosts |
| group_names | Groups the current host belongs to |
| inventory_hostname | Current host's inventory name |
| play_hosts | Active hosts in the current play |
| ansible_play_batch | Hosts in the current serial batch |
| role_path | Filesystem path of current role |
| playbook_dir | Directory containing the playbook |
10 — Advanced Features
⚡Performance & Strategy
# Serial — rolling updates
- hosts: webservers
serial: 2 # 2 hosts at a time
serial: "20%" # percentage
serial: [1, 5, 10] # ramp-up
max_fail_percentage: 25
# Strategy plugins
strategy: linear # default: task by task
strategy: free # each host runs ASAP
strategy: debug # interactive debugger
# Async — fire and forget
- name: Long running task
ansible.builtin.command: /opt/long.sh
async: 600 # max seconds
poll: 0 # 0 = fire & forget
register: job
- name: Wait for job
ansible.builtin.async_status:
jid: "{{ job.ansible_job_id }}"
register: result
until: result.finished
retries: 30
delay: 20
🔁Delegation, Retries & Caching
# delegate_to
- name: Run on localhost
ansible.builtin.command: /opt/notify.sh
delegate_to: localhost
# run_once — first host only
- name: DB migration
ansible.builtin.command: migrate.sh
run_once: true
delegate_to: db1.example.com
# Retry until success
- name: Poll health endpoint
ansible.builtin.uri:
url: http://localhost/health
register: health
until: health.status == 200
retries: 10
delay: 5
# Fact caching (ansible.cfg)
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/facts_cache
fact_caching_timeout = 86400 # 1 day
11 — Common Facts Reference
📊Frequently Used Ansible Facts
| Fact | Example |
|---|---|
| ansible_hostname | web1 |
| ansible_fqdn | web1.example.com |
| ansible_default_ipv4.address | 10.0.0.5 |
| ansible_all_ipv4_addresses | [10.0.0.5] |
| Fact | Example |
|---|---|
| ansible_os_family | Debian |
| ansible_distribution | Ubuntu |
| ansible_distribution_version | 22.04 |
| ansible_distribution_major_version | 22 |
| ansible_kernel | 5.15.0-91 |
| Fact | Example |
|---|---|
| ansible_architecture | x86_64 |
| ansible_processor_vcpus | 4 |
| ansible_memtotal_mb | 7962 |
| ansible_pkg_mgr | apt |
| ansible_service_mgr | systemd |