Sustainable Infrastructure Automation - Configuration management and orchestration for the future
Essential commands and configuration for daily Ansible usage
# Ad-hoc commands
ansible <host-pattern> -m <module> -a "<arguments>"
ansible all -m ping
ansible webservers -m shell -a "uptime"
ansible all -m copy -a "src=/etc/hosts dest=/tmp/hosts"
# Playbook execution
ansible-playbook playbook.yml
ansible-playbook -i inventory.ini playbook.yml
ansible-playbook playbook.yml --limit webservers
ansible-playbook playbook.yml --tags "install,config"
ansible-playbook playbook.yml --skip-tags "testing"
ansible-playbook playbook.yml -e "version=1.2.3"
# Check and diff modes
ansible-playbook playbook.yml --check # Dry run
ansible-playbook playbook.yml --diff # Show diffs
ansible-playbook playbook.yml --check --diff # Both
# Syntax validation
ansible-playbook playbook.yml --syntax-check
ansible-playbook playbook.yml --list-hosts
ansible-playbook playbook.yml --list-tasks
ansible-playbook playbook.yml --list-tags
# Vault operations
ansible-vault encrypt secrets.yml
ansible-vault decrypt secrets.yml
ansible-vault edit secrets.yml
ansible-vault view secrets.yml
ansible-vault rekey secrets.yml
# Playbook with vault
ansible-playbook playbook.yml --ask-vault-pass
ansible-playbook playbook.yml --vault-password-file ~/.vault_pass
# Galaxy commands
ansible-galaxy role init myrole
ansible-galaxy role install username.rolename
ansible-galaxy collection install community.general
ansible-galaxy install -r requirements.yml
[defaults]
inventory = ./inventory
remote_user = ansible
host_key_checking = False
retry_files_enabled = False
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
forks = 10
timeout = 30
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
Managing hosts, groups, and inventory structure
# inventory.ini
# Individual hosts
web1.example.com
web2.example.com
# Grouped hosts
[webservers]
web1.example.com
web2.example.com
web3.example.com
[databases]
db1.example.com
db2.example.com
# Host with connection variables
[appservers]
app1.example.com ansible_host=10.0.0.5 ansible_port=2222
# Range patterns
[webfarm]
web[01:20].example.com
# Nested groups
[production:children]
webservers
databases
# Group variables
[webservers:vars]
http_port=80
max_clients=200
[production:vars]
ansible_user=deploy
ansible_become=yes
---
# inventory.yml
all:
children:
webservers:
hosts:
web1.example.com:
ansible_host: 192.168.1.10
web2.example.com:
ansible_host: 192.168.1.11
vars:
http_port: 80
max_clients: 200
databases:
hosts:
db1.example.com:
db2.example.com:
vars:
db_port: 5432
production:
children:
webservers:
databases:
vars:
ansible_user: deploy
ansible_become: yes
inventory/
├── hosts # Main inventory file
├── host_vars/
│ ├── web1.example.com.yml # Variables for specific host
│ └── db1.example.com.yml
└── group_vars/
├── all.yml # Variables for all hosts
├── webservers.yml # Variables for webservers group
└── production.yml # Variables for production group
group_vars/all.yml:
---
ntp_server: ntp.example.com
dns_servers:
- 8.8.8.8
- 8.8.4.4
host_vars/web1.example.com.yml:
---
nginx_worker_processes: 4
app_environment: production
ssl_certificate: /etc/ssl/certs/web1.crt
| Pattern | Description |
|---|---|
| all | All hosts |
| web1.example.com | Single host |
| webservers | Group |
| webservers:databases | Multiple groups (OR) |
| webservers:&production | Intersection (AND) |
| webservers:!staging | Exclusion (NOT) |
| web*.example.com | Wildcard |
| webservers:&production:!web1.example.com | Complex pattern |
Quick one-off tasks without writing playbooks
ansible <host-pattern> -m <module> -a "<module-arguments>" [options]
Ping module:
# Test connectivity
ansible all -m ping
ansible webservers -m ping
Shell module:
# Execute shell commands (supports pipes, redirects)
ansible all -m shell -a "uptime"
ansible webservers -m shell -a "ps aux | grep nginx"
ansible all -m shell -a "echo $TERM"
Command module:
# Execute commands (no shell features)
ansible all -m command -a "uptime"
ansible all -m command -a "df -h"
ansible all -m command -a "/usr/bin/foo" -a "creates=/path/file"
Copy module:
# Copy files to remote hosts
ansible webservers -m copy -a "src=/etc/hosts dest=/tmp/hosts"
ansible all -m copy -a "src=/app/config.yml dest=/etc/app/config.yml owner=app group=app mode=0644"
ansible all -m copy -a "content='Hello World\n' dest=/tmp/hello.txt"
File module:
# Manage files and directories
ansible all -m file -a "path=/tmp/test state=touch"
ansible all -m file -a "path=/tmp/testdir state=directory mode=0755"
ansible all -m file -a "path=/tmp/test state=absent"
ansible all -m file -a "src=/etc/hosts dest=/tmp/hosts state=link"
Package management:
# Apt (Debian/Ubuntu)
ansible webservers -m apt -a "name=nginx state=present" -b
ansible all -m apt -a "name=git state=latest" -b
ansible all -m apt -a "update_cache=yes" -b
# Yum (RHEL/CentOS)
ansible webservers -m yum -a "name=nginx state=present" -b
ansible all -m yum -a "name=httpd state=latest" -b
Service module:
# Service management
ansible webservers -m service -a "name=nginx state=started" -b
ansible webservers -m service -a "name=nginx state=restarted" -b
ansible webservers -m service -a "name=nginx enabled=yes" -b
User module:
# User management
ansible all -m user -a "name=deploy state=present" -b
ansible all -m user -a "name=deploy shell=/bin/bash groups=sudo append=yes" -b
ansible all -m user -a "name=deploy state=absent remove=yes" -b
| Option | Description |
|---|---|
| -i INVENTORY | Specify inventory file |
| -u USERNAME | Remote user |
| -b | Become (sudo) |
| -K | Ask for become password |
| -k | Ask for SSH password |
| -f FORKS | Number of parallel processes |
| -e "var=value" | Extra variables |
| --limit SUBSET | Limit to specific hosts |
Declarative automation with YAML playbooks
---
# webserver.yml
- name: Configure web servers
hosts: webservers
become: yes
vars:
http_port: 80
max_clients: 200
tasks:
- name: Install nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Start nginx service
service:
name: nginx
state: started
enabled: yes
---
- name: Configure nginx
hosts: webservers
become: yes
tasks:
- name: Install nginx
apt:
name: nginx
state: present
- name: Copy nginx config
copy:
src: files/nginx.conf
dest: /etc/nginx/nginx.conf
notify:
- Restart nginx
- Check nginx status
- name: Copy site config
template:
src: templates/site.conf.j2
dest: /etc/nginx/sites-available/default
notify: Restart nginx
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
- name: Check nginx status
command: nginx -t
---
- name: Complete application setup
hosts: appservers
become: yes
tasks:
- name: Install packages
apt:
name: "{{ item }}"
state: present
loop:
- git
- python3-pip
tags:
- install
- packages
- name: Copy application config
template:
src: app.conf.j2
dest: /etc/app/config.conf
tags:
- config
- name: Run database migrations
command: /opt/app/migrate.sh
tags:
- deploy
- database
- name: Restart application
service:
name: myapp
state: restarted
tags:
- deploy
- never # Only runs when explicitly called
Run with tags:
ansible-playbook app.yml --tags "install"
ansible-playbook app.yml --tags "config,deploy"
ansible-playbook app.yml --skip-tags "database"
ansible-playbook app.yml --tags "never"
---
- name: Conditional tasks
hosts: all
become: yes
tasks:
- name: Install Apache (Debian/Ubuntu)
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"
- name: Install Apache (RHEL/CentOS)
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"
- name: Shut down Debian 10 systems
command: /sbin/shutdown -t now
when:
- ansible_os_family == "Debian"
- ansible_distribution_major_version == "10"
---
- name: Loop examples
hosts: webservers
become: yes
tasks:
# Simple loop
- name: Install packages
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- git
- vim
# Loop with dictionaries
- name: Create users
user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
loop:
- { name: 'alice', groups: 'sudo' }
- { name: 'bob', groups: 'developers' }
- { name: 'charlie', groups: 'developers,docker' }
# Loop with conditional
- name: Install packages on Debian only
apt:
name: "{{ item }}"
state: present
loop:
- apache2
- php
when: ansible_os_family == "Debian"
Managing data and system information in Ansible
---
- name: Variable examples
hosts: webservers
# Play-level variables
vars:
http_port: 80
app_version: "1.2.3"
packages:
- nginx
- git
- vim
# Variables from files
vars_files:
- vars/common.yml
- vars/{{ ansible_os_family }}.yml
# Prompt for variables
vars_prompt:
- name: deploy_version
prompt: "Which version to deploy?"
private: no
- name: db_password
prompt: "Database password?"
private: yes
tasks:
- name: Show variables
debug:
msg: "Port: {{ http_port }}, Version: {{ app_version }}"
---
- name: Register examples
hosts: webservers
tasks:
- name: Check if file exists
stat:
path: /etc/app/config.conf
register: config_file
- name: Show file info
debug:
msg: "File exists: {{ config_file.stat.exists }}"
- name: Copy config if missing
copy:
src: files/config.conf
dest: /etc/app/config.conf
when: not config_file.stat.exists
- name: Get service status
command: systemctl status nginx
register: nginx_status
ignore_errors: yes
- name: Show nginx status
debug:
var: nginx_status.stdout_lines
---
- name: Set fact examples
hosts: all
tasks:
- name: Set deployment timestamp
set_fact:
deploy_timestamp: "{{ ansible_date_time.iso8601 }}"
- name: Calculate total memory
set_fact:
total_memory_gb: "{{ (ansible_memtotal_mb / 1024) | round(2) }}"
- name: Build complex fact
set_fact:
server_info:
hostname: "{{ ansible_hostname }}"
os: "{{ ansible_distribution }}"
version: "{{ ansible_distribution_version }}"
ip: "{{ ansible_default_ipv4.address }}"
- name: Use facts
debug:
msg: "Deployed at {{ deploy_timestamp }} on {{ server_info.hostname }}"
| Fact | Description |
|---|---|
| ansible_hostname | Short hostname |
| ansible_fqdn | Fully qualified domain name |
| ansible_os_family | Debian, RedHat, etc. |
| ansible_distribution | Ubuntu, CentOS, etc. |
| ansible_distribution_version | OS version number |
| ansible_processor_vcpus | Number of CPUs |
| ansible_memtotal_mb | Total memory in MB |
| ansible_default_ipv4.address | Primary IP address |
| ansible_date_time.iso8601 | Current timestamp |
---
- name: Magic variables
hosts: all
tasks:
- name: Show inventory info
debug:
msg: |
Inventory hostname: {{ inventory_hostname }}
Groups: {{ group_names }}
All groups: {{ groups }}
Play hosts: {{ ansible_play_hosts }}
- name: Access hostvars
debug:
msg: "Web1 IP: {{ hostvars['web1.example.com'].ansible_default_ipv4.address }}"
- name: Loop through group
debug:
msg: "Host {{ item }} in webservers"
loop: "{{ groups['webservers'] }}"
Dynamic configuration with Jinja2 templates and file management
templates/nginx.conf.j2:
# Nginx configuration
user {{ nginx_user }};
worker_processes {{ nginx_worker_processes }};
pid /run/nginx.pid;
events {
worker_connections {{ nginx_worker_connections }};
}
http {
sendfile on;
keepalive_timeout {{ nginx_keepalive_timeout }};
# Conditional content
{% if nginx_gzip_enabled %}
gzip on;
gzip_vary on;
gzip_types text/plain text/css application/json;
{% endif %}
# Loop through upstream servers
upstream backend {
{% for server in backend_servers %}
server {{ server.host }}:{{ server.port }} weight={{ server.weight }};
{% endfor %}
}
include /etc/nginx/sites-enabled/*;
}
Using the template:
---
- name: Configure nginx
hosts: webservers
become: yes
vars:
nginx_user: www-data
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_gzip_enabled: true
backend_servers:
- { host: '10.0.1.10', port: 8080, weight: 3 }
- { host: '10.0.1.11', port: 8080, weight: 2 }
- { host: '10.0.1.12', port: 8080, weight: 1 }
tasks:
- name: Deploy nginx config
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
backup: yes
validate: nginx -t -c %s
notify: Restart nginx
| Filter | Example | Result |
|---|---|---|
| lower | "HELLO" | lower | hello |
| upper | "hello" | upper | HELLO |
| default | undefined_var | default('fallback') | fallback |
| length | [1,2,3] | length | 3 |
| join | [1,2,3] | join(',') | 1,2,3 |
| to_json | my_dict | to_json | JSON string |
| to_yaml | my_dict | to_yaml | YAML string |
| basename | '/path/to/file.txt' | basename | file.txt |
| dirname | '/path/to/file.txt' | dirname | /path/to |
---
- name: File management
hosts: webservers
become: yes
tasks:
# Create file
- name: Create empty file
file:
path: /tmp/testfile
state: touch
owner: www-data
group: www-data
mode: '0644'
# Create directory
- name: Create directory
file:
path: /opt/myapp/config
state: directory
owner: deploy
group: deploy
mode: '0755'
recurse: yes
# Create symlink
- name: Create symbolic link
file:
src: /opt/myapp/current
dest: /opt/myapp/app
state: link
# Delete file/directory
- name: Remove file
file:
path: /tmp/testfile
state: absent
---
- name: Lineinfile examples
hosts: all
become: yes
tasks:
# Add line if not present
- name: Ensure line in sudoers
lineinfile:
path: /etc/sudoers
line: 'deploy ALL=(ALL) NOPASSWD: ALL'
validate: '/usr/sbin/visudo -cf %s'
# Replace line
- name: Set hostname
lineinfile:
path: /etc/hostname
regexp: '^.*$'
line: 'webserver01'
# Remove line
- name: Remove line
lineinfile:
path: /etc/hosts
regexp: '^192\.168\.1\.100'
state: absent
---
- name: Blockinfile examples
hosts: all
become: yes
tasks:
# Insert block
- name: Add SSH config block
blockinfile:
path: /etc/ssh/sshd_config
block: |
Match User ansible
PasswordAuthentication no
PubkeyAuthentication yes
notify: Restart sshd
# Add hosts entries
- name: Add hosts entries
blockinfile:
path: /etc/hosts
insertafter: EOF
block: |
192.168.1.10 web1.example.com
192.168.1.11 web2.example.com
192.168.1.12 db1.example.com
Organizing and sharing reusable automation content
roles/
└── nginx/
├── README.md
├── defaults/
│ └── main.yml # Default variables (lowest precedence)
├── files/
│ └── nginx.conf # Static files for copy module
├── handlers/
│ └── main.yml # Handlers
├── meta/
│ └── main.yml # Role metadata and dependencies
├── tasks/
│ ├── main.yml # Main task file
│ ├── install.yml # Task includes
│ └── configure.yml
├── templates/
│ └── site.conf.j2 # Jinja2 templates
├── tests/
│ ├── inventory
│ └── test.yml # Test playbook
└── vars/
└── main.yml # Role variables (high precedence)
# Initialize role structure
ansible-galaxy role init nginx
ansible-galaxy role init roles/myapp
# Custom skeleton
ansible-galaxy role init --init-path roles/ --offline myapp
roles/nginx/defaults/main.yml:
---
nginx_port: 80
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_remove_default_site: true
nginx_sites:
- name: default
server_name: localhost
root: /var/www/html
roles/nginx/tasks/main.yml:
---
- name: Include OS-specific variables
include_vars: "{{ ansible_os_family }}.yml"
- name: Install nginx
include_tasks: install.yml
- name: Configure nginx
include_tasks: configure.yml
- name: Ensure nginx is running
service:
name: "{{ nginx_service }}"
state: started
enabled: yes
roles/nginx/handlers/main.yml:
---
- name: Restart nginx
service:
name: "{{ nginx_service }}"
state: restarted
- name: Reload nginx
service:
name: "{{ nginx_service }}"
state: reloaded
---
# Simple role usage
- name: Configure web servers
hosts: webservers
roles:
- nginx
# Roles with variables
- name: Configure nginx with custom port
hosts: webservers
roles:
- role: nginx
nginx_port: 8080
nginx_worker_processes: 4
# Pre and post tasks
- name: Deploy application
hosts: appservers
pre_tasks:
- name: Take server out of load balancer
command: /usr/local/bin/remove_from_lb.sh
roles:
- nginx
- application
post_tasks:
- name: Add server back to load balancer
command: /usr/local/bin/add_to_lb.sh
---
# Roles from Galaxy
roles:
- name: geerlingguy.nginx
version: 3.1.4
- name: geerlingguy.mysql
- src: https://github.com/user/ansible-role-custom.git
name: custom
version: master
# Collections from Galaxy
collections:
- name: community.general
version: ">=4.0.0"
- name: ansible.posix
version: 1.5.4
- name: community.mysql
Installing:
# Install roles
ansible-galaxy role install -r requirements.yml
# Install collections
ansible-galaxy collection install -r requirements.yml
# Force reinstall
ansible-galaxy collection install -r requirements.yml --force
---
- name: Using collections
hosts: all
collections:
- community.general
- ansible.posix
tasks:
# Fully qualified collection name (FQCN)
- name: Use docker_container module
community.docker.docker_container:
name: myapp
image: nginx
state: started
# Short name (when collection is imported)
- name: Manage firewall
firewalld:
port: 80/tcp
permanent: yes
state: enabled
Error handling, delegation, and execution control
---
- name: Error handling with blocks
hosts: webservers
become: yes
tasks:
- name: Handle deployment with error recovery
block:
- name: Download application
get_url:
url: https://example.com/app.tar.gz
dest: /tmp/app.tar.gz
- name: Extract application
unarchive:
src: /tmp/app.tar.gz
dest: /opt/app
remote_src: yes
- name: Start application
service:
name: myapp
state: started
rescue:
- name: Log error
debug:
msg: "Deployment failed, rolling back"
- name: Restore from backup
command: /usr/local/bin/restore_backup.sh
always:
- name: Cleanup temp files
file:
path: /tmp/app.tar.gz
state: absent
---
- name: Delegation examples
hosts: webservers
tasks:
# Run task on different host
- name: Add server to monitoring
command: /usr/local/bin/add_to_monitoring.sh {{ inventory_hostname }}
delegate_to: monitoring.example.com
# Run on localhost (control node)
- name: Check API availability
uri:
url: https://api.example.com/health
return_content: yes
delegate_to: localhost
register: api_status
# Local action (shortcut)
- name: Download file locally
local_action:
module: get_url
url: https://example.com/file.txt
dest: /tmp/file.txt
---
- name: Run once examples
hosts: webservers
tasks:
# Run only on first host
- name: Run database migration (once)
command: /opt/app/migrate.sh
run_once: yes
# Run once with delegation
- name: Update DNS
command: /usr/local/bin/update_dns.sh
delegate_to: dns.example.com
run_once: yes
---
- name: Rolling deployment
hosts: webservers
serial: 2 # Process 2 hosts at a time
tasks:
- name: Remove from load balancer
command: /usr/local/bin/remove_from_lb.sh {{ inventory_hostname }}
delegate_to: loadbalancer.example.com
- name: Deploy new version
copy:
src: app-v2.tar.gz
dest: /opt/app/
- name: Restart application
service:
name: myapp
state: restarted
- name: Wait for application
wait_for:
port: 8080
delay: 5
timeout: 60
- name: Add back to load balancer
command: /usr/local/bin/add_to_lb.sh {{ inventory_hostname }}
delegate_to: loadbalancer.example.com
You can use percentages (serial: "20%") or multiple batches (serial: [1, 25%, 100%]) for flexible rolling updates.
---
# import_tasks (static, processed at parse time)
- name: Import tasks example
hosts: all
tasks:
- name: Import installation tasks
import_tasks: tasks/install.yml
- name: Import with variables
import_tasks: tasks/configure.yml
vars:
config_file: /etc/app/prod.conf
# include_tasks (dynamic, processed at runtime)
- name: Include tasks example
hosts: all
tasks:
- name: Include tasks conditionally
include_tasks: tasks/database.yml
when: setup_database
- name: Include tasks in loop
include_tasks: tasks/create_user.yml
loop:
- alice
- bob
- charlie
loop_control:
loop_var: username
Encrypting sensitive data with Ansible Vault
# Create encrypted file
ansible-vault create secrets.yml
# Encrypt existing file
ansible-vault encrypt vars/passwords.yml
# Decrypt file
ansible-vault decrypt secrets.yml
# Edit encrypted file (opens in editor)
ansible-vault edit secrets.yml
# View encrypted file (read-only)
ansible-vault view secrets.yml
# Change vault password (rekey)
ansible-vault rekey secrets.yml
# Encrypt specific string
ansible-vault encrypt_string 'mysecretpassword' --name 'db_password'
ansible-vault encrypt_string --stdin-name 'api_key'
vars/secrets.yml (encrypted):
---
db_password: "supersecret"
api_key: "abc123xyz789"
ssl_key_password: "keypass123"
Encrypt it:
ansible-vault encrypt vars/secrets.yml
Using in playbook:
---
- name: Deploy application
hosts: appservers
vars_files:
- vars/secrets.yml
tasks:
- name: Configure database connection
template:
src: db_config.j2
dest: /etc/app/db.conf
# Template can use {{ db_password }}
Run with vault:
# Prompt for password
ansible-playbook deploy.yml --ask-vault-pass
# Use password file
ansible-playbook deploy.yml --vault-password-file ~/.vault_pass
~/.vault_pass:
myVaultPassword123
chmod 600 ~/.vault_pass
ansible.cfg:
[defaults]
vault_password_file = ~/.vault_pass
# Encrypt string interactively
ansible-vault encrypt_string 'secretvalue' --name 'my_secret'
# Output:
my_secret: !vault |
$ANSIBLE_VAULT;1.1;AES256
66386439653765...
vars/passwords.yml:
---
# Plain text variables
db_host: db.example.com
db_port: 5432
# Encrypted password
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
66386439653765653938383866383764623066303764326436613631326634653034366361393834
3439656537653036383766626239646662363431613134310a383238356266663061363261626635
# Create with vault ID
ansible-vault create --vault-id dev@prompt secrets_dev.yml
ansible-vault create --vault-id prod@prompt secrets_prod.yml
# Or use password files
ansible-vault create --vault-id dev@~/.vault_pass_dev secrets_dev.yml
# Run with multiple vault IDs
ansible-playbook deploy.yml --vault-id dev@prompt --vault-id prod@prompt
Dynamic inventory, callbacks, and advanced techniques
inventory/ec2.py:
#!/usr/bin/env python3
import json
import boto3
def get_inventory():
ec2 = boto3.client('ec2', region_name='us-east-1')
response = ec2.describe_instances(
Filters=[{'Name': 'instance-state-name', 'Values': ['running']}]
)
inventory = {
'all': {'hosts': []},
'_meta': {'hostvars': {}}
}
for reservation in response['Reservations']:
for instance in reservation['Instances']:
hostname = instance.get('PublicDnsName') or instance['InstanceId']
inventory['all']['hosts'].append(hostname)
# Add to groups based on tags
tags = {tag['Key']: tag['Value'] for tag in instance.get('Tags', [])}
if 'Environment' in tags:
env = tags['Environment']
if env not in inventory:
inventory[env] = {'hosts': []}
inventory[env]['hosts'].append(hostname)
# Set host variables
inventory['_meta']['hostvars'][hostname] = {
'ansible_host': instance.get('PublicIpAddress'),
'instance_type': instance['InstanceType'],
'instance_id': instance['InstanceId']
}
return inventory
if __name__ == '__main__':
import sys
if len(sys.argv) == 2 and sys.argv[1] == '--list':
print(json.dumps(get_inventory(), indent=2))
elif len(sys.argv) == 3 and sys.argv[1] == '--host':
print(json.dumps({}))
else:
print(json.dumps({}))
inventory/aws_ec2.yml:
---
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
- us-west-2
filters:
instance-state-name: running
keyed_groups:
- key: tags.Environment
prefix: env
- key: tags.Application
prefix: app
- key: instance_type
prefix: type
hostnames:
- dns-name
- private-ip-address
compose:
ansible_host: public_ip_address
Use it:
ansible-inventory -i inventory/aws_ec2.yml --list
ansible-playbook -i inventory/aws_ec2.yml playbook.yml
ansible.cfg:
[defaults]
callback_whitelist = profile_tasks, timer, yaml
# Enable custom callback
callback_plugins = ./plugins/callback
# Setup cron job on managed nodes to pull and run playbooks
ansible-pull -U https://github.com/user/ansible-repo.git -i hosts local.yml
# With specific branch
ansible-pull -U https://github.com/user/ansible-repo.git -C production local.yml
# Run every 30 minutes
*/30 * * * * ansible-pull -U https://github.com/user/ansible-repo.git -o local.yml
local.yml (in git repo):
---
- name: Local configuration
hosts: localhost
connection: local
become: yes
tasks:
- name: Ensure packages are installed
package:
name:
- vim
- git
state: present
- name: Pull latest app code
git:
repo: https://github.com/company/app.git
dest: /opt/app
version: main
ansible.cfg:
[defaults]
strategy = free # or linear (default), debug
In playbook:
---
- name: Fast deployment
hosts: webservers
strategy: free # Don't wait for all hosts to complete each task
tasks:
- name: Deploy application
copy:
src: app.tar.gz
dest: /opt/app/
Best practices and performance optimization
---
# BAD - Always reports changed
- name: Append to file (bad)
shell: echo "line" >> /etc/config
# GOOD - Idempotent
- name: Ensure line in file (good)
lineinfile:
path: /etc/config
line: "line"
state: present
# Use creates/removes with command module
- name: Extract archive
command: tar xzf /tmp/app.tar.gz -C /opt/app
args:
creates: /opt/app/app.bin
# Use changed_when to control change status
- name: Check service status
command: systemctl status nginx
register: nginx_status
changed_when: false
failed_when: false
---
- name: Check mode support
hosts: all
tasks:
# This task supports check mode
- name: Install package
apt:
name: nginx
state: present
# Force task to run in check mode
- name: Always check
command: /usr/bin/check_script.sh
check_mode: yes
# Never run in check mode (always execute)
- name: Always run
command: /usr/bin/must_run.sh
check_mode: no
# Custom check mode behavior
- name: Custom check
command: /usr/bin/deploy.sh
when: not ansible_check_mode
Run check mode:
ansible-playbook playbook.yml --check
ansible-playbook playbook.yml -C
---
- name: Debugging techniques
hosts: localhost
gather_facts: yes
vars:
app_version: "2.1.0"
tasks:
# Simple debug message
- name: Show message
debug:
msg: "Deploying version {{ app_version }}"
# Debug variable
- name: Show variable
debug:
var: ansible_facts
# Debug with verbosity control
- name: Verbose debug
debug:
msg: "Only shown with -v or higher"
verbosity: 1
# Assert for validation
- name: Validate configuration
assert:
that:
- app_version is defined
- ansible_distribution in ['Ubuntu', 'Debian']
fail_msg: "Invalid configuration"
success_msg: "Configuration valid"
Verbosity levels:
ansible-playbook playbook.yml -v # Verbose
ansible-playbook playbook.yml -vv # More verbose
ansible-playbook playbook.yml -vvv # Debug (connection info)
ansible-playbook playbook.yml -vvvv # SSH debug
ansible.cfg optimization:
[defaults]
# Increase parallel execution
forks = 20
# Disable fact gathering if not needed
gathering = explicit
# Enable fact caching
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 3600
# Reduce SSH overhead
host_key_checking = False
retry_files_enabled = False
[ssh_connection]
# Enable pipelining (requires 'requiretty' disabled in sudoers)
pipelining = True
# Reuse SSH connections
ssh_args = -o ControlMaster=auto -o ControlPersist=3600s
# Increase SSH timeout for slow networks
timeout = 30
---
- name: Async task examples
hosts: webservers
tasks:
# Fire and forget (poll: 0)
- name: Long background task
command: /usr/bin/background_task.sh
async: 3600
poll: 0
register: background_job
# Async with polling
- name: Task with timeout
command: /usr/bin/slow_task.sh
async: 600 # 10 minute timeout
poll: 10 # Check every 10 seconds
# Run multiple async tasks
- name: Start multiple tasks
command: "/usr/bin/task.sh {{ item }}"
async: 300
poll: 0
loop:
- task1
- task2
- task3
register: async_tasks
# Wait for all to complete
- name: Wait for tasks
async_status:
jid: "{{ item.ansible_job_id }}"
register: async_results
until: async_results.finished
retries: 30
delay: 5
loop: "{{ async_tasks.results }}"
---
- name: Error handling
hosts: all
tasks:
# Ignore errors
- name: Try to stop service
service:
name: optional-service
state: stopped
ignore_errors: yes
# Custom failure condition
- name: Check disk space
shell: df -h / | tail -1 | awk '{print $5}' | sed 's/%//'
register: disk_usage
failed_when: disk_usage.stdout|int > 90
# Multiple failure conditions
- name: Run command
command: /usr/bin/check.sh
register: check_result
failed_when:
- check_result.rc != 0
- "'WARNING' not in check_result.stderr"
# Retry on failure
- name: Download file
get_url:
url: https://example.com/file.tar.gz
dest: /tmp/file.tar.gz
retries: 3
delay: 5
register: download
until: download is success
---
- name: Testing playbook
hosts: webservers
tasks:
# Validate before making changes
- name: Check syntax of config
command: nginx -t -c {{ config_file }}
changed_when: false
check_mode: no
# Wait for service
- name: Restart nginx
service:
name: nginx
state: restarted
- name: Wait for nginx
wait_for:
port: 80
delay: 2
timeout: 30
# Validate service is responding
- name: Check nginx is serving
uri:
url: http://localhost
status_code: 200
retries: 3
delay: 5
# Smoke tests
- name: Run smoke tests
command: /usr/local/bin/smoke_test.sh
changed_when: false