MeshWorld India Logo MeshWorld.
Cheatsheet Ansible DevOps IaC Automation Linux Configuration Management 8 min read

Ansible Cheat Sheet: Inventory, Playbooks, Roles & Ad-hoc Commands

Cobie
By Cobie
| Updated: May 13, 2026
Ansible Cheat Sheet: Inventory, Playbooks, Roles & Ad-hoc Commands
TL;DR
  • Ansible is agentless config management — SSH into machines and apply YAML playbooks
  • Inventory defines hosts; Playbooks define what to do; Modules do the work
  • Idempotent by default — running the same playbook twice is safe
  • ansible-playbook runs playbooks; ansible runs single ad-hoc commands
  • Roles organize playbooks into reusable components
  • Vault encrypts sensitive data (passwords, API keys) in playbooks
  • No agent needed on remote hosts — only Python and SSH

Quick reference tables

Installation

PlatformCommand
macOSbrew install ansible
Ubuntu / Debiansudo apt install ansible
pippip install ansible
pipxpipx install ansible-core
Verifyansible --version

Core CLI commands

CommandWhat it does
ansible all -m pingPing all hosts in inventory
ansible all -m shell -a "uptime"Run shell command on all hosts
ansible all -m copy -a "src=x dest=y"Copy file to all hosts
ansible-playbook site.ymlRun a playbook
ansible-playbook site.yml --checkDry-run (diff without applying)
ansible-playbook site.yml --tags webRun only tasks tagged “web”
ansible-playbook site.yml --start-at-task="Deploy"Start from a specific task
ansible-vault encrypt secrets.ymlEncrypt a file with vault
ansible-galaxy init myroleScaffold a new role
ansible-inventory -i inv.ini --listList all inventory hosts

Essential modules

ModuleUse for
shellRun arbitrary shell commands
commandRun commands (no shell special chars)
copyCopy files to remote hosts
templateCopy template files (Jinja2)
fileCreate/delete files and directories
lineinfileInsert or replace a line in a file
apt / yum / dnfPackage management
service / systemdStart/stop services
gitClone and manage git repos
fetchPull files from remote to local
userCreate and manage users
firewalld / ufwFirewall management
docker_containerManage Docker containers
set_factSet variables for a host

Inventory

INI format (inventory.ini)

ini
[webservers]
web1.example.com ansible_user=ubuntu
web2.example.com ansible_user=ubuntu

[dbservers]
db1.example.com ansible_user=postgres
db2.example.com ansible_user=postgres

[production:children]
webservers
dbservers

[production:vars]
ansible_python_interpreter=/usr/bin/python3
environment=production

YAML format (inventory.yml)

yaml
all:
  children:
    webservers:
      hosts:
        web1.example.com:
          ansible_user: ubuntu
        web2.example.com:
          ansible_user: ubuntu
    dbservers:
      hosts:
        db1.example.com:
          ansible_user: postgres
  vars:
    ansible_python_interpreter: /usr/bin/python3
    environment: production

Dynamic inventory

bash
# Use AWS EC2 dynamic inventory
ansible-playbook site.yml -i ec2.py

# Use a custom script
ansible-playbook site.yml -i inventory script.py --list-hosts

Playbooks

Minimal playbook

yaml
---
- name: Basic setup
  hosts: webservers
  become: yes                # Run as root
  vars:
    app_dir: /opt/myapp

  tasks:
    - name: Install nginx
      apt:
        name: nginx
        state: present
        update_cache: yes

    - name: Copy config file
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      notify: Restart nginx

    - name: Start nginx
      service:
        name: nginx
        state: started
        enabled: yes

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

Roles

Role structure

plaintext
roles/
└── webserver/
    ├── defaults/
    │   └── main.yml          # Default variables (lowest priority)
    ├── files/
    │   └── nginx.conf        # Files to copy
    ├── handlers/
    │   └── main.yml          # Handlers
    ├── meta/
    │   └── main.yml          # Role dependencies
    ├── tasks/
    │   └── main.yml          # Main tasks
    ├── templates/
    │   └── nginx.conf.j2     # Jinja2 templates
    └── vars/
        └── main.yml          # Higher-priority variables

tasks/main.yml

yaml
---
- name: Deploy web application
  block:
    - name: Create application directory
      file:
        path: "{{ app_dir }}"
        state: directory
        owner: "{{ app_user }}"
        mode: '0755'

    - name: Clone repository
      git:
        repo: "{{ app_repo }}"
        dest: "{{ app_dir }}"
        version: "{{ app_branch }}"
        force: yes

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

    - name: Start application
      systemd:
        name: "{{ app_service }}"
        state: started
        enabled: yes
        daemon_reload: yes
      register: app_started

  rescue:
    - name: Rollback notification
      debug:
        msg: "Application deployment failed. Rolling back."

Playbook using roles

yaml
---
- name: Production deployment
  hosts: production
  become: yes

  roles:
    - role: common           # Standard roles
    - role: webserver
      vars:
        app_dir: /opt/myapp
        app_service: myapp
    - role: database
      when: "'dbservers' in group_names"

Conditionals and loops

Conditionals

yaml
- name: Start service based on OS
  service:
    name: "{{ service_name }}"
    state: started
  when: ansible_os_family == "Debian"

- name: Create user if not exists
  user:
    name: deploy
    state: present
    shell: /bin/bash
  when: "'dbservers' in group_names"

- name: Check if file exists
  stat:
    path: /etc/app.conf
  register: config_file

- name: Copy config only if missing
  copy:
    src: app.conf
    dest: /etc/app.conf
  when: not config_file.stat.exists

Loops

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

# Loop with index
- name: Create users
  user:
    name: "{{ item.user }}"
    uid: "{{ item.uid }}"
    shell: /bin/bash
  loop:
    - { user: alice, uid: 1001 }
    - { user: bob, uid: 1002 }
    - { user: carol, uid: 1003 }

# Loop over dict
- name: Set facts
  set_fact:
    "{{ item.key }}": "{{ item.value }}"
  loop:
    - { key: env, value: production }
    - { key: tier, value: backend }

Error handling (blocks)

yaml
- name: Deploy application
  block:
    - name: Pull latest code
      git:
        repo: "{{ app_repo }}"
        dest: "{{ app_dir }}"
        version: "{{ app_version }}"
    - name: Run migrations
      command: "{{ app_dir }}/migrate.sh"
      changed_when: true
  rescue:
    - name: Notify on failure
      debug:
        msg: "Deployment failed — check logs"
    - name: Run rollback script
      command: "{{ app_dir }}/rollback.sh"

Vault (secrets)

bash
# Create an encrypted file
ansible-vault encrypt secrets.yml

# Edit an encrypted file
ansible-vault edit secrets.yml

# Create a new encrypted file
ansible-vault create secrets.yml

# Decrypt to view
ansible-vault decrypt secrets.yml

# Change password
ansible-vault rekey secrets.yml

# Encrypt a string (for CLI use)
ansible-vault encrypt_string "mypassword" --name "db_password"

Using vault in playbooks

bash
# Run playbook with vault password
ansible-playbook site.yml --ask-vault-pass

# Use a password file
ansible-playbook site.yml --vault-password-file ~/.vault_pass
yaml
# secrets.yml (encrypted)
---
db_host: prod-db.example.com
db_user: appuser
db_password: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  616263646566...  # encrypted value
yaml
# playbook.yml
- name: Deploy application
  hosts: dbservers
  vars_files:
    - secrets.yml           # Decrypted automatically

  tasks:
    - name: Configure database connection
      template:
        src: db.conf.j2
        dest: /etc/app/db.conf

Common patterns

Check mode (dry run)

bash
ansible-playbook site.yml --check      # Show what would change
ansible-playbook site.yml --check --diff   # Show diffs

Tags

yaml
tasks:
  - name: Install dependencies
    apt:
      name: "{{ item }}"
    loop:
      - nginx
      - git
    tags:
      - setup
      - web

  - name: Deploy app
    git:
      repo: "{{ app_repo }}"
      dest: "{{ app_dir }}"
    tags:
      - deploy
bash
ansible-playbook site.yml --tags setup        # Run only setup tasks
ansible-playbook site.yml --skip-tags deploy  # Skip deploy tasks

Delegation and local actions

yaml
- name: Deploy to web servers one by one
  command: /opt/deploy.sh
  hosts: webservers
  serial: 1                    # Run on one host at a time

- name: Notify localhost
  debug:
    msg: "Deployment complete on {{ inventory_hostname }}"
  delegate_to: localhost

- name: Wait for web server to be ready
  wait_for:
    host: "{{ item }}"
    port: 80
    delay: 5
    timeout: 30
  loop: "{{ groups['webservers'] }}"

Jinja2 filters

yaml
- name: Set fact with filters
  set_fact:
    app_version_clean: "{{ app_version | trim | upper }}"
    config_dict: "{{ config_json | from_json }}"
    filtered_list: "{{ items_list | selectattr('active') | list }}"
    joined: "{{ domains | join(', ') }}"

Summary

  • Inventory defines hosts; Playbooks define tasks; Modules do the work
  • Roles organize tasks, handlers, templates, and files into reusable units
  • become: yes runs tasks as root; when: applies conditionals
  • Vault encrypts secrets; --ask-vault-pass or --vault-password-file unlocks them
  • block/rescue handles errors gracefully with rollback logic
  • Check mode (--check) and --tags are your best debugging tools

FAQ

What is the difference between shell and command modules? command runs the command directly (no shell processing). shell runs through /bin/sh, so it supports pipes, redirects, and environment variables. Use command when possible for security and predictability.

How do I handle host key verification errors with SSH? Add to ansible.cfg: host_key_checking = False. For production, pre-authorize host keys or use sshpass with a known-hosts file.

Can Ansible manage Windows hosts? Yes. WinRM (over PowerShell) is the primary method. Install pywinrm on the control node and configure WinRM on Windows hosts. Supported since Ansible 2.4.

How do I run a task on localhost without SSH? Use hosts: localhost and set connection: local in the play, or delegate_to: localhost for specific tasks.

Is Ansible idempotent? Yes, by design. Running a playbook twice should produce the same result as running it once. Use state: present/absent instead of raw apt install / apt remove to get idempotency.