Ansible Quick Reference
Agentless automation – from ad‑hoc commands to complex, idempotent infrastructure
as code.
Installation & Setup
# Install via pip (recommended) pip install ansible pip install ansible-core # Verify ansible --version # Install via package manager (Ubuntu/Debian) sudo apt update sudo apt install ansible # macOS brew install ansible # Configuration file hierarchy (lowest to highest precedence) # 1. ANSIBLE_CONFIG (env var) # 2. ./ansible.cfg # 3. ~/.ansible.cfg # 4. /etc/ansible/ansible.cfg
Inventory
Static Inventory (INI format)
# hosts.ini [webservers] web1 ansible_host=192.168.1.10 web2 ansible_host=192.168.1.11 [dbservers] db1 ansible_host=192.168.1.20 ansible_user=admin [all:vars] ansible_python_interpreter=/usr/bin/python3 # Group of groups [production:children] webservers dbservers
Inventory in YAML
# hosts.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
Dynamic Inventory
- AWS EC2: use
aws_ec2plugin. - Azure:
azure_rm. - GCP:
gcp_compute. - Custom scripts: executable that outputs JSON.
# Test inventory
ansible-inventory -i hosts.ini --list
ansible all -i hosts.ini --list-hosts
Ad‑hoc Commands
# Ping (ICMP + Python availability) ansible all -i hosts.ini -m ping # Shell / Command modules ansible webservers -m shell -a "uptime" ansible dbservers -m command -a "df -h" # Copy file ansible webservers -m copy -a "src=/local/file dest=/remote/file mode=0644" # Install package ansible webservers -m apt -a "name=nginx state=present" -b ansible webservers -m yum -a "name=httpd state=latest" -b # Service management ansible webservers -m service -a "name=nginx state=restarted" -b # Gather facts ansible webservers -m setup ansible webservers -m setup -a "filter=ansible_os_family"
Playbooks – Structure
---
- name: Deploy web application
hosts: webservers
become: true
vars:
app_port: 8080
app_env: production
tasks:
- name: Ensure nginx is installed
apt:
name: nginx
state: present
- name: Start and enable nginx
service:
name: nginx
state: started
enabled: true
handlers:
- name: Reload nginx
service:
name: nginx
state: reloaded
Common Modules
| Category | Module | Purpose |
|---|---|---|
| Files | copy | Copy file from control to target |
| Files | template | Render Jinja2 template |
| Files | file | Manage files, dirs, symlinks, permissions |
| System | apt / yum | Package management |
| System | service | Manage systemd/init services |
| System | user | Manage user accounts |
| System | firewalld | Manage firewall rules |
| Networking | uri | HTTP requests |
| Cloud | ec2, azure_rm | Provision cloud resources |
| Git | git | Clone / pull repositories |
| Docker | docker_container | Manage containers |
Variables
Definition Sources (precedence high → low)
- Extra vars (
-ecommand line) - Playbook
vars - Inventory host / group vars
- Facts (gathered automatically)
- Role defaults
# In playbook vars: app_name: myapp versions: backend: 1.2.3 frontend: 2.0.1 # Access {{ app_name }} {{ versions.backend }} # Host vars (in inventory dir: host_vars/web1.yml) --- ansible_host: 192.168.1.10 app_env: staging # Group vars (group_vars/webservers.yml) --- ntp_server: pool.ntp.org
Facts (System Information)
# Disable fact gathering for performance - name: Fast playbook hosts: all gather_facts: false # Use facts {{ ansible_os_family }} {{ ansible_distribution_version }} {{ ansible_default_ipv4.address }} {{ ansible_memtotal_mb }}
Conditionals
- name: Install Apache on Debian family
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"
- name: Install httpd on RedHat family
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"
- name: Conditional with multiple conditions
debug:
msg: "Production environment"
when:
- env == "production"
- app_port is defined
- name: Condition with failed task
shell: /usr/bin/which nginx
register: nginx_exists
ignore_errors: true
- name: Only run if nginx exists
debug:
msg: "nginx is installed"
when: nginx_exists.rc == 0
Loops
# Simple loop - name: Create multiple users user: name: "{{ item }}" state: present loop: - alice - bob - charlie # Loop with dict - name: Install packages apt: name: "{{ item.name }}" state: "{{ item.state }}" loop: - { name: 'nginx', state: 'present' } - { name: 'postgresql', state: 'latest' } # Loop over list of dicts with when - name: Configure firewalls firewalld: port: "{{ item.port }}/{{ item.protocol }}" permanent: true immediate: true state: enabled loop: - { port: 80, protocol: tcp } - { port: 443, protocol: tcp } # Until loop (retry) - name: Wait for service to start shell: curl -s http://localhost:8080/health register: result until: result.stdout == "OK" retries: 10 delay: 5
Templates (Jinja2)
# templates/nginx.conf.j2 server { listen {{ app_port }}; server_name {{ server_name }}; root /var/www/{{ app_name }}; location / { try_files $uri $uri/ =404; } } # Playbook task - name: Render nginx config template: src: nginx.conf.j2 dest: /etc/nginx/sites-available/default notify: Reload nginx
Common Jinja2 Filters
{{ value | default('fallback') }}{{ my_list | join(',') }}{{ my_dict | to_yaml }}{{ my_string | upper }}{{ ip | ipaddr('address') }}{{ my_var | bool }}
Roles
Directory Structure
roles/
common/
tasks/
main.yml
handlers/
main.yml
vars/
main.yml
defaults/
main.yml
files/
templates/
meta/
main.yml
webserver/
tasks/
main.yml
...
Using Roles in Playbook
---
- name: Apply base configuration
hosts: all
roles:
- common
- name: Setup web servers
hosts: webservers
roles:
- webserver
- { role: nginx, app_port: 8080 } # with parameter
Ansible Galaxy (Public Roles)
# Install role from Galaxy ansible-galaxy role install geerlingguy.docker # Create new role skeleton ansible-galaxy role init myrole # List installed roles ansible-galaxy role list
Handlers
Handlers run only when notified by a task (typically after a change).
- name: Restart nginx
service:
name: nginx
state: restarted
listen: "restart nginx" # alternative to named handler
- name: Reload systemd
systemd:
name: myapp
daemon_reload: true
state: restarted
# In task:
- name: Update config
template:
src: app.conf.j2
dest: /etc/myapp/app.conf
notify:
- Restart nginx
- Reload systemd
Ansible Vault (Secrets Management)
# Encrypt a file ansible-vault encrypt secrets.yml # Decrypt ansible-vault decrypt secrets.yml # View encrypted file ansible-vault view secrets.yml # Edit encrypted file ansible-vault edit secrets.yml # Run playbook with vault password file ansible-playbook playbook.yml --vault-password-file .vault_pass # Encrypt a variable (--ask-vault-pass) ansible-vault encrypt_string 'mysecret' --name 'db_password'
Tags and Includes
# Tag tasks - name: Install packages apt: name: nginx state: present tags: - packages - nginx - name: Configure app template: src: app.conf.j2 dest: /etc/app.conf tags: - config # Run specific tags ansible-playbook playbook.yml --tags "nginx,config" ansible-playbook playbook.yml --skip-tags "packages" # Include / import tasks - name: Include common setup import_tasks: common/tasks/main.yml - name: Include with variables include_tasks: setup.yml vars: setup_env: production
Best Practices
- Use roles – for reusable, organised code.
- Leverage variables – separate data from logic (group_vars, host_vars).
- Make playbooks idempotent – run multiple times without side effects.
- Use
--check(dry‑run) – test changes before applying. - Use
--diff– see what files changed. - Encrypt secrets – with Ansible Vault; never store plaintext passwords.
- Use dynamic inventory – for cloud environments.
- Set
gather_factstofalsewhen not needed for speed. - Use
ansible-lint– validate playbook syntax and style. - Version control everything – playbooks, roles, inventory, vault files.
- Use modules over shell/command – they are idempotent and safer.
- Set
retry_files_enabled = falsein production to avoid leftover .retry files.
Debugging & Troubleshooting
# Verbose output ( -v, -vv, -vvv, -vvvv ) ansible-playbook playbook.yml -vvv # Debug module - name: Print variable debug: var: my_var - name: Print message debug: msg: "The app port is {{ app_port }}" # Check syntax ansible-playbook playbook.yml --syntax-check # Dry-run ansible-playbook playbook.yml --check # Step-by-step execution ansible-playbook playbook.yml --step # Limit to specific hosts ansible-playbook playbook.yml --limit web1,web2
Performance Tuning
- Increase
forks– inansible.cfg(default 5, try 20–50). - Use
pipelining– reduces SSH overhead (enable inansible.cfg). - Use
control_persist– reuses SSH connections. - Disable fact gathering – with
gather_facts: false. - Use
strategy: free– faster parallel execution (instead of linear). - Cache facts – use
cacheplugin (Redis, Memcached, JSON).
# ansible.cfg performance settings
[defaults]
forks = 30
pipelining = True
timeout = 30
[ssh_connection]
control_path = /tmp/ansible-%%h-%%p
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
📌 Quick Reference
Ad‑hoc:
Playbook:
Inventory: INI / YAML / dynamic plugins
Key modules: copy, template, file, apt/yum, service, user, git
Secrets: ansible‑vault encrypt / edit / view
Debug: -vvv, debug module, --check, --step
Best practice: Roles + group_vars + vault + idempotency
ansible all -m pingPlaybook:
ansible-playbook site.ymlInventory: INI / YAML / dynamic plugins
Key modules: copy, template, file, apt/yum, service, user, git
Secrets: ansible‑vault encrypt / edit / view
Debug: -vvv, debug module, --check, --step
Best practice: Roles + group_vars + vault + idempotency