Ansible
Comprehensive Ansible cheatsheet covering playbooks, inventories, roles, tasks, variables, handlers, ad-hoc commands, modules, and configuration options.
No commands found
Try adjusting your search term
Getting Started
Installation & Setup
Install Ansible and configure basic settings
Install Ansible on Ubuntu
sudo apt updatesudo apt install -y ansibleInstall Ansible via pip
pip install ansiblepip install ansible==2.10.7Verify Ansible Installation
Displays installed Ansible version and configuration details
ansible --versionansible 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
[defaults]inventory = ./hostsremote_user = ubuntuprivate_key_file = ~/.ssh/id_rsahost_key_checking = Falsegather_facts = TrueConfigure Parallel Execution
forks increases parallelism for faster execution
[defaults]forks = 10timeout = 30retries = 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
---- name: Hello World hosts: all tasks: - name: Print Hello debug: msg: "Hello from Ansible"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
ansible all -i hosts -m pingwebserver01 | 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
[webservers]web1 ansible_host=192.168.1.10web2 ansible_host=192.168.1.11
[databases]db1 ansible_host=192.168.1.20db2 ansible_host=192.168.1.21
[all:vars]ansible_user=ubuntuansible_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
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
ansible webservers -i hosts -m pingansible databases -i hosts -m pingansible 'webservers:!web2' -i hosts -m pingweb1 | SUCCESS => {"ping": "pong"}web2 | SUCCESS => {"ping": "pong"}- webservers selects entire group
- negates selection
Wildcard and Range Patterns
Wildcards match patterns, [1:2] matches range
ansible 'web*' -i hosts -m pingansible 'db[1:2]' -i hosts -m pingansible '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
plugin: aws_ec2regions: - us-east-1filters: tag:Environment: productionkeyed_groups: - key: placement.region_name prefix: aws_region- Requires boto3 library
- Cache results for performance
Custom Inventory Script
Custom script outputs JSON inventory format
#!/bin/bashcat << 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
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
group_vars/webservers.ymlgroup_vars/webservers/main.ymlhost_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
ansible all -i hosts -m shell -a "uptime"web1 | CHANGED | rc=0 >> 10:45:23 up 5 days, 3:21, 2 users, load average: 0.05, 0.10, 0.08web2 | 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
ansible webservers -i hosts -m copy -a "src=/etc/hosts dest=/tmp/hosts"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
ansible webservers -i hosts -m apt -a "name=nginx state=present"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
ansible webservers -i hosts -m service -a "name=nginx state=started enabled=yes"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
ansible all -i hosts -m file -a "path=/tmp/test.txt mode=0644 owner=root"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
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)
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
---- 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
---- 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
ansible-playbook playbook.yml -i hostsPLAY [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=0web2 : ok=2 changed=1 failed=0- ok = task already in desired state
- changed = task made changes
Check Mode (Dry Run)
Preview changes without executing
ansible-playbook playbook.yml -i hosts --check- Shows what would change
- Useful before production runs
Run with Extra Variables
Pass variables from command line
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
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
- name: Install nginx if not exists apt: name: nginx state: present when: ansible_distribution == "Ubuntu"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)
- 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
- 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
- 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
- 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
tasks: - name: Update config template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf notify: - reload nginx - log config changehandlers: - 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
- name: Install multiple packages apt: name: "{{ item }}" state: present loop: - nginx - php-fpm - mysql-clientTASK [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
- 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
- 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
- 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
- name: Display fact hosts: all tasks: - name: Show OS debug: msg: "OS: {{ ansible_os_family }}, Memory: {{ ansible_memtotal_mb }}"TASK [Show OS] *****ok: [web1] => { "msg": "OS: Debian, Memory: 4096"}- ansible_* variables are facts
- [object Object]
Custom Facts
Define custom facts in JSON files
/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
# 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
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
- name: Use filters debug: msg: | Uppercase: {{ service_name | upper }} Lowercase: {{ domain | lower }} Default: {{ undefined_var | default('localhost') }} List join: {{ servers | join(',') }}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
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
---- 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
- 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
- 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
---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
- name: Create user user: name: appuser uid: 2000 home: /opt/app shell: /bin/bash state: presentok: [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
- name: Create directory file: path: /opt/app/data state: directory mode: '0755' owner: appuser recurse: yeschanged: [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
- 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
- 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
- name: Get nginx version command: nginx -v register: nginx_version changed_when: falseok: [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
- 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
- name: Health check uri: url: http://localhost:8080/health method: GET status_code: 200 register: health_checkok: [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
- 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
- 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
- name: Long running task shell: /opt/app/long-job.sh async: 3600 poll: 10 register: long_jobASYNC OK on web1 (job_id=123456789.12345)- [object Object]
- [object Object]
Fire and Forget
Start task and don't wait for completion
- 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
- 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
vars: services: web: port: 8080 workers: 4 db: port: 5432 replicas: 3tasks: - 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
- 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
- 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
ansible-vault create secrets.yml# Enter password, then edit:# db_password: "secret123"# api_key: "abc123xyz"New Vault password:Confirm New Vault password:- Creates .yml with encrypted content
- Prompts for password
View Vault Content
Decrypt and display vault file
ansible-vault view secrets.yml# Password prompt- Requires vault password
- Doesn't save decrypted content
Edit Vault File
Edit encrypted vault file safely
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
- 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
ansible-playbook playbook.yml -i hosts --ask-vault-passVault 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
ansible-vault encrypt_string --ask-vault-pass 'mypassword'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
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
.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
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