← Back to all cheatsheets
DevOps
ansibleautomationdevopsconfiguration-managementinfrastructure

Ansible Cheat Sheet

Basic Syntax

ansible [pattern] -m [module] -a "[module options]"
ansible-playbook [options] playbook.yml

Ad-Hoc Commands

Basic Command Structure

# Ping all hosts
ansible all -m ping

# Ping specific host group
ansible webservers -m ping

# Run command on all hosts
ansible all -a "uptime"

# Run command as sudo
ansible all -a "apt update" --become

# Run command as specific user
ansible all -a "whoami" --become --become-user=www-data

Common Modules in Ad-Hoc

# Shell commands
ansible all -m shell -a "df -h"

# Copy files
ansible all -m copy -a "src=/local/file dest=/remote/file"

# Install package
ansible all -m apt -a "name=nginx state=present" --become

# Restart service
ansible all -m service -a "name=nginx state=restarted" --become

# Create user
ansible all -m user -a "name=deploy state=present" --become

# Gather facts
ansible all -m setup
ansible all -m setup -a "filter=ansible_distribution*"

Targeting Hosts

# Single host
ansible host1 -m ping

# Multiple hosts
ansible host1:host2 -m ping

# Host group
ansible webservers -m ping

# All hosts in multiple groups
ansible webservers:databases -m ping

# Exclude hosts
ansible webservers:!databases -m ping

# Intersection of groups
ansible webservers:&staging -m ping

# Pattern matching
ansible web* -m ping
ansible *.example.com -m ping

# Range of hosts
ansible web[1:5] -m ping

ansible-playbook Command

Basic Playbook Execution

# Run playbook
ansible-playbook playbook.yml

# Run with specific inventory
ansible-playbook -i inventory.ini playbook.yml
ansible-playbook -i hosts playbook.yml

# Check syntax
ansible-playbook --syntax-check playbook.yml

# Dry run (check mode)
ansible-playbook --check playbook.yml

# Show differences when changing files
ansible-playbook --check --diff playbook.yml

# Run with verbose output
ansible-playbook -v playbook.yml      # Verbose
ansible-playbook -vv playbook.yml     # More verbose
ansible-playbook -vvv playbook.yml    # Very verbose
ansible-playbook -vvvv playbook.yml   # Debug level

Targeting and Limiting

# Limit to specific hosts
ansible-playbook playbook.yml --limit webservers
ansible-playbook playbook.yml --limit host1,host2
ansible-playbook playbook.yml --limit @failed_hosts.txt

# Start at specific task
ansible-playbook playbook.yml --start-at-task="Install nginx"

# Run specific tags
ansible-playbook playbook.yml --tags "configuration,deployment"

# Skip specific tags
ansible-playbook playbook.yml --skip-tags "testing"

# List all tasks (don't execute)
ansible-playbook playbook.yml --list-tasks

# List all tags
ansible-playbook playbook.yml --list-tags

# List all hosts
ansible-playbook playbook.yml --list-hosts

Variables and Extra Vars

# Pass extra variables
ansible-playbook playbook.yml --extra-vars "version=1.2.3"
ansible-playbook playbook.yml -e "env=production"

# Pass multiple variables
ansible-playbook playbook.yml -e "foo=bar baz=qux"

# Load variables from file
ansible-playbook playbook.yml -e "@vars.yml"
ansible-playbook playbook.yml -e "@vars.json"

Privilege Escalation

# Run with sudo
ansible-playbook playbook.yml --become

# Specify sudo user
ansible-playbook playbook.yml --become --become-user=root

# Ask for sudo password
ansible-playbook playbook.yml --become --ask-become-pass

# Ask for SSH password
ansible-playbook playbook.yml --ask-pass

# Use specific connection method
ansible-playbook playbook.yml --connection=local

Step and Interactive

# Step through tasks one by one
ansible-playbook playbook.yml --step

# Set number of parallel forks
ansible-playbook playbook.yml --forks=10

# Timeout for connections
ansible-playbook playbook.yml --timeout=30

Playbook Structure

Basic Playbook Example

---
- 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: Copy configuration file
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
      notify: Restart nginx

  handlers:
    - name: Restart nginx
      service:
        name: nginx
        state: restarted

Multiple Plays

---
- name: Configure databases
  hosts: databases
  become: yes
  tasks:
    - name: Install PostgreSQL
      apt:
        name: postgresql
        state: present

- name: Configure web servers
  hosts: webservers
  become: yes
  tasks:
    - name: Install nginx
      apt:
        name: nginx
        state: present

Common Modules

Package Management

# APT (Debian/Ubuntu)
- name: Install package
  apt:
    name: nginx
    state: present
    update_cache: yes

# YUM (RedHat/CentOS)
- name: Install package
  yum:
    name: httpd
    state: present

# DNF (Fedora)
- name: Install package
  dnf:
    name: nginx
    state: present

# Generic package module
- name: Install package
  package:
    name: vim
    state: present

File Operations

# Copy files
- name: Copy file
  copy:
    src: /local/path/file.txt
    dest: /remote/path/file.txt
    owner: user
    group: group
    mode: '0644'

# Copy with backup
- name: Copy with backup
  copy:
    src: config.conf
    dest: /etc/app/config.conf
    backup: yes

# Template files (Jinja2)
- name: Template configuration
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    mode: '0644'
  notify: Restart nginx

# Create file
- name: Create file
  file:
    path: /tmp/myfile
    state: touch
    mode: '0644'

# Create directory
- name: Create directory
  file:
    path: /opt/myapp
    state: directory
    mode: '0755'

# Create symlink
- name: Create symlink
  file:
    src: /path/to/source
    dest: /path/to/link
    state: link

# Delete file/directory
- name: Remove file
  file:
    path: /tmp/oldfile
    state: absent

Service Management

# Start service
- name: Start nginx
  service:
    name: nginx
    state: started

# Stop service
- name: Stop nginx
  service:
    name: nginx
    state: stopped

# Restart service
- name: Restart nginx
  service:
    name: nginx
    state: restarted

# Reload service
- name: Reload nginx
  service:
    name: nginx
    state: reloaded

# Enable service at boot
- name: Enable nginx
  service:
    name: nginx
    enabled: yes

# Using systemd module
- name: Restart and enable service
  systemd:
    name: nginx
    state: restarted
    enabled: yes
    daemon_reload: yes

Command Execution

# Run command
- name: Run command
  command: /usr/bin/somecommand arg1 arg2

# Run shell command (with pipes, redirects)
- name: Run shell command
  shell: cat /etc/passwd | grep root

# Run script
- name: Run script
  script: /local/path/script.sh

# Run command only if file doesn't exist
- name: Initialize database
  command: /usr/bin/init-db
  args:
    creates: /var/lib/db/initialized

# Run command with specific working directory
- name: Build application
  command: make build
  args:
    chdir: /opt/myapp

User and Group Management

# Create user
- name: Create user
  user:
    name: deploy
    state: present
    shell: /bin/bash
    groups: sudo
    append: yes

# Remove user
- name: Remove user
  user:
    name: olduser
    state: absent
    remove: yes

# Create group
- name: Create group
  group:
    name: developers
    state: present

# Add SSH key
- name: Add SSH key
  authorized_key:
    user: deploy
    state: present
    key: "{{ lookup('file', '/path/to/id_rsa.pub') }}"

Git Operations

# Clone repository
- name: Clone repository
  git:
    repo: https://github.com/user/repo.git
    dest: /opt/myapp
    version: main

# Update repository
- name: Update repository
  git:
    repo: https://github.com/user/repo.git
    dest: /opt/myapp
    update: yes
    force: yes

Inventory Management

INI Format Inventory

# hosts.ini or inventory.ini

# Ungrouped hosts
host1.example.com
192.168.1.10

# Host with custom SSH port
host2.example.com ansible_port=2222

# Groups
[webservers]
web1.example.com
web2.example.com
web[3:5].example.com

[databases]
db1.example.com
db2.example.com

# Group variables
[webservers:vars]
http_port=80
ansible_user=deploy

# Group of groups
[production:children]
webservers
databases

# Variables for group of groups
[production:vars]
env=production

YAML Format Inventory

# inventory.yml
all:
  hosts:
    host1.example.com:
    host2.example.com:
      ansible_port: 2222

  children:
    webservers:
      hosts:
        web1.example.com:
        web2.example.com:
      vars:
        http_port: 80
        ansible_user: deploy

    databases:
      hosts:
        db1.example.com:
        db2.example.com:

    production:
      children:
        webservers:
        databases:
      vars:
        env: production

Inventory Commands

# List all hosts
ansible-inventory --list

# Show inventory graph
ansible-inventory --graph

# Show host variables
ansible-inventory --host web1.example.com

# Use specific inventory file
ansible -i inventory.ini all -m ping
ansible-playbook -i inventory.yml playbook.yml

Variables

Variable Precedence (lowest to highest)

  1. Role defaults
  2. Inventory file/script group vars
  3. Inventory group_vars/all
  4. Playbook group_vars/all
  5. Inventory group_vars/*
  6. Playbook group_vars/*
  7. Inventory file/script host vars
  8. Inventory host_vars/*
  9. Playbook host_vars/*
  10. Host facts
  11. Play vars
  12. Play vars_prompt
  13. Play vars_files
  14. Role vars
  15. Block vars
  16. Task vars
  17. Extra vars (—extra-vars)

Using Variables

# Define in playbook
- hosts: webservers
  vars:
    http_port: 80
    app_version: "1.2.3"

  tasks:
    - name: Use variable
      debug:
        msg: "Port is {{ http_port }}"

# Load from file
- hosts: webservers
  vars_files:
    - vars/common.yml
    - vars/production.yml
  tasks:
    - name: Show variable
      debug:
        var: app_version

# Register task output
- name: Check if file exists
  stat:
    path: /etc/myapp/config
  register: config_file

- name: Use registered variable
  debug:
    msg: "File exists"
  when: config_file.stat.exists

# Facts (automatically gathered)
- name: Show OS distribution
  debug:
    msg: "OS is {{ ansible_distribution }} {{ ansible_distribution_version }}"

Variable Files

# group_vars/webservers.yml
http_port: 80
max_clients: 200

# host_vars/web1.example.com.yml
server_id: 1

Conditionals and Loops

When Conditions

# Simple condition
- name: Install on Ubuntu only
  apt:
    name: nginx
    state: present
  when: ansible_distribution == "Ubuntu"

# Multiple conditions (AND)
- name: Install on Ubuntu 20.04
  apt:
    name: nginx
    state: present
  when:
    - ansible_distribution == "Ubuntu"
    - ansible_distribution_version == "20.04"

# Multiple conditions (OR)
- name: Install on Debian or Ubuntu
  apt:
    name: nginx
    state: present
  when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"

# Check variable defined
- name: Run if variable is defined
  debug:
    msg: "Running"
  when: my_var is defined

# Check boolean
- name: Run if enabled
  debug:
    msg: "Enabled"
  when: feature_enabled | bool

Loops

# Simple loop
- name: Install packages
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - git
    - vim

# Loop with variables
- name: Create users
  user:
    name: "{{ item.name }}"
    state: present
    groups: "{{ item.groups }}"
  loop:
    - { name: 'alice', groups: 'sudo' }
    - { name: 'bob', groups: 'developers' }

# Loop with dictionary
- name: Set file permissions
  file:
    path: "{{ item.key }}"
    mode: "{{ item.value }}"
  loop: "{{ files | dict2items }}"
  vars:
    files:
      /etc/app/config: "0644"
      /opt/app/script.sh: "0755"

# Loop with range
- name: Create numbered directories
  file:
    path: "/tmp/dir{{ item }}"
    state: directory
  loop: "{{ range(1, 6) | list }}"

# Loop with until
- name: Wait for service
  uri:
    url: http://localhost:8080/health
    status_code: 200
  register: result
  until: result.status == 200
  retries: 10
  delay: 5

Handlers

Basic Handlers

---
- hosts: webservers
  tasks:
    - name: Update nginx config
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      notify: Restart nginx

    - name: Update app config
      template:
        src: app.conf.j2
        dest: /etc/app/config.conf
      notify:
        - Restart nginx
        - Clear cache

  handlers:
    - name: Restart nginx
      service:
        name: nginx
        state: restarted

    - name: Clear cache
      command: /usr/local/bin/clear-cache

Handler Execution

# Force handlers to run immediately
- name: Update config
  copy:
    src: config.conf
    dest: /etc/app/config.conf
  notify: Restart app

- meta: flush_handlers

- name: This runs after handler
  debug:
    msg: "Handler has executed"

Roles

Role Directory Structure

roles/
  webserver/
    tasks/
      main.yml
    handlers/
      main.yml
    templates/
      nginx.conf.j2
    files/
      index.html
    vars/
      main.yml
    defaults/
      main.yml
    meta/
      main.yml

Using Roles

---
- hosts: webservers
  roles:
    - common
    - webserver
    - { role: database, db_type: postgresql }

# With variables
- hosts: webservers
  roles:
    - role: nginx
      vars:
        http_port: 8080

# With tags
- hosts: webservers
  roles:
    - { role: webserver, tags: ['web'] }

# Include role in tasks
- hosts: webservers
  tasks:
    - include_role:
        name: webserver
      vars:
        http_port: 8080

Creating a Role

# Create role structure
ansible-galaxy init webserver

# Install role from Galaxy
ansible-galaxy install geerlingguy.nginx

# Install from requirements file
ansible-galaxy install -r requirements.yml

requirements.yml

# From Ansible Galaxy
- src: geerlingguy.nginx
  version: 3.1.4

# From GitHub
- src: https://github.com/user/ansible-role-name
  name: custom-role
  version: main

Templates (Jinja2)

Basic Template

{# nginx.conf.j2 #}
server {
    listen {{ http_port }};
    server_name {{ server_name }};

    root {{ document_root }};

    {% if ssl_enabled %}
    ssl_certificate {{ ssl_cert_path }};
    ssl_certificate_key {{ ssl_key_path }};
    {% endif %}

    {% for location in locations %}
    location {{ location.path }} {
        proxy_pass {{ location.backend }};
    }
    {% endfor %}
}

Template Filters

- name: Use template filters
  debug:
    msg: "{{ 'hello' | upper }}"              # HELLO

- debug:
    msg: "{{ my_list | join(', ') }}"          # Join list items

- debug:
    msg: "{{ my_var | default('default') }}"   # Default value

- debug:
    msg: "{{ '/path/to/file' | basename }}"    # file

- debug:
    msg: "{{ my_string | regex_replace('old', 'new') }}"

Ansible Vault

Managing Secrets

# Create encrypted file
ansible-vault create secrets.yml

# Edit encrypted file
ansible-vault edit secrets.yml

# Encrypt existing file
ansible-vault encrypt vars.yml

# Decrypt file
ansible-vault decrypt secrets.yml

# View encrypted file
ansible-vault view secrets.yml

# Rekey (change password)
ansible-vault rekey secrets.yml

# Encrypt string
ansible-vault encrypt_string 'secret_password' --name 'db_password'

Using Vault in Playbooks

# Run playbook with vault
ansible-playbook playbook.yml --ask-vault-pass

# Use password file
ansible-playbook playbook.yml --vault-password-file ~/.vault_pass

# Multiple vault IDs
ansible-playbook playbook.yml --vault-id prod@prompt --vault-id dev@~/.vault_dev

Vault in Variables

# Encrypted variable in playbook
db_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          66386439653235336166353036626163...

Configuration

ansible.cfg

[defaults]
# Inventory file
inventory = ./inventory.ini

# Number of parallel processes
forks = 10

# SSH timeout
timeout = 10

# Disable host key checking
host_key_checking = False

# Log file
log_path = /var/log/ansible.log

# Role path
roles_path = ./roles:/usr/share/ansible/roles

# Gathering facts
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400

# Callbacks
stdout_callback = yaml
callbacks_enabled = timer, profile_tasks

[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

Configuration Priority

  1. ANSIBLE_CONFIG environment variable
  2. ./ansible.cfg (in current directory)
  3. ~/.ansible.cfg (in home directory)
  4. /etc/ansible/ansible.cfg

Common Use Cases

Deploy Application

---
- name: Deploy web application
  hosts: webservers
  become: yes

  vars:
    app_version: "1.2.3"
    app_path: /opt/myapp

  tasks:
    - name: Pull latest code
      git:
        repo: https://github.com/user/app.git
        dest: "{{ app_path }}"
        version: "{{ app_version }}"
      notify: Restart application

    - name: Install dependencies
      pip:
        requirements: "{{ app_path }}/requirements.txt"
        virtualenv: "{{ app_path }}/venv"

    - name: Copy configuration
      template:
        src: config.j2
        dest: "{{ app_path }}/config.py"
      notify: Restart application

    - name: Collect static files
      command: "{{ app_path }}/venv/bin/python manage.py collectstatic --noinput"
      args:
        chdir: "{{ app_path }}"

  handlers:
    - name: Restart application
      systemd:
        name: myapp
        state: restarted

System Hardening

---
- name: Harden servers
  hosts: all
  become: yes

  tasks:
    - name: Update all packages
      apt:
        upgrade: dist
        update_cache: yes

    - name: Disable root SSH login
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^PermitRootLogin'
        line: 'PermitRootLogin no'
      notify: Restart sshd

    - name: Configure firewall
      ufw:
        rule: allow
        port: "{{ item }}"
      loop:
        - 22
        - 80
        - 443

    - name: Enable firewall
      ufw:
        state: enabled

  handlers:
    - name: Restart sshd
      service:
        name: sshd
        state: restarted

Testing and Debugging

Debug Module

- name: Show variable
  debug:
    var: my_variable

- name: Show message
  debug:
    msg: "Value is {{ my_var }}"

- name: Show all variables
  debug:
    var: vars

- name: Show hostvars
  debug:
    var: hostvars[inventory_hostname]

Assert Module

- name: Verify conditions
  assert:
    that:
      - ansible_distribution == "Ubuntu"
      - ansible_distribution_version is version('20.04', '>=')
    fail_msg: "Ubuntu 20.04 or higher required"
    success_msg: "OS version check passed"

Molecule (Testing Framework)

# Install Molecule
pip install molecule molecule-docker

# Initialize new role with Molecule
molecule init role my_role

# Test role
cd my_role
molecule test

# Test steps individually
molecule create    # Create test instance
molecule converge  # Run playbook
molecule verify    # Run tests
molecule destroy   # Destroy instance

Tips and Best Practices

  1. Use roles to organize complex playbooks into reusable components
  2. Tag tasks for selective execution: --tags and --skip-tags
  3. Use --check mode to dry-run playbooks before execution
  4. Use --diff mode to see what will change
  5. Store secrets in Ansible Vault, never in plain text
  6. Use ansible-lint to check playbook syntax and best practices
  7. Name all tasks for better readability and debugging
  8. Use handlers for service restarts to avoid unnecessary restarts
  9. Set gather_facts: no if you don’t need facts (faster execution)
  10. Use async and poll for long-running tasks
  11. Keep playbooks idempotent - safe to run multiple times
  12. Use version control for all playbooks and roles
  13. Use delegate_to to run tasks on different hosts
  14. Cache facts to speed up playbook execution
  15. Use serial to control rolling updates
  16. Document your playbooks with comments and README files

Quick Reference

# Ad-hoc commands
ansible all -m ping
ansible all -a "command"
ansible all -m module -a "args"

# Playbook execution
ansible-playbook playbook.yml
ansible-playbook playbook.yml --check
ansible-playbook playbook.yml --check --diff
ansible-playbook playbook.yml -vvv

# Inventory
ansible-inventory --list
ansible-inventory --graph

# Vault
ansible-vault create|edit|view|encrypt|decrypt file.yml

# Galaxy
ansible-galaxy init role_name
ansible-galaxy install role_name
ansible-galaxy install -r requirements.yml

# Configuration
ansible-config list
ansible-config dump
ansible-config view