/user/kayd @ devops :~$ cat ansible.md

Ansible Cheatsheet Ansible Cheatsheet

QR Code linking to: Ansible Cheatsheet
Karandeep Singh
Karandeep Singh
• 6 minutes

Summary

Quick-reference Ansible guide. Inventories, ad-hoc commands, playbooks, module table, loops and conditionals, roles layout, Vault, and Jinja2 templating.

Ansible is what I grab the moment a server count goes above one. These notes are the stuff I actually type: inventory patterns, the module table I scan for every task, and the Vault commands I keep mixing up. I usually run these playbooks from a Jenkins stage against hosts provisioned by CloudFormation.

Inventory

Docs: How to build your inventory, Ansible docs

# inventory.ini (INI format)
[web]
web1.example.com
web2.example.com ansible_host=10.0.1.12

[db]
db1.example.com ansible_user=postgres

[prod:children]
web
db

[prod:vars]
ansible_user=deploy
env_name=prod
# inventory.yaml (YAML format)
all:
  children:
    web:
      hosts:
        web1.example.com:
        web2.example.com: { ansible_host: 10.0.1.12 }
    db:
      hosts:
        db1.example.com:
      vars: { ansible_user: postgres }

Per-host/per-group variable files auto-load from group_vars/all.yaml, group_vars/<group>.yaml (or a group_vars/<group>/ directory, vault files included), and host_vars/<host>.yaml next to the inventory or playbook.

ansible-inventory -i inventory.yaml --graph     # visualise groups

Ad-hoc Commands

Docs: Introduction to ad hoc commands, Ansible docs

ansible all  -i inventory.yaml -m ping                       # connectivity check
ansible web  -a 'uptime'                                     # default module: command
ansible db   -b -m service -a 'name=postgresql state=restarted'   # -b = become (sudo)
ansible all  -b -m apt -a 'name=htop state=present update_cache=yes'
ansible 'web1*' -m shell -a 'ss -tlnp | grep 443'            # shell when you need pipes
ansible all  --limit 'prod:!db' -m ping                      # pattern: prod except db

Useful flags: -f 20 (forks), -K (ask sudo password), -u deploy (remote user), --one-line.

Playbook Anatomy

Docs: Ansible playbooks, Ansible docs

---
- name: Configure web tier
  hosts: web
  become: true
  vars_files:
    - vars/common.yaml
  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true
      tags: [packages]

    - name: Deploy config
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: Restart nginx
      tags: [config]

  handlers:
    - name: Restart nginx
      ansible.builtin.service:
        name: nginx
        state: restarted
ansible-playbook -i inventory.yaml site.yaml
ansible-playbook site.yaml --limit web --tags config --check --diff
ansible-playbook site.yaml --syntax-check --list-tasks --list-hosts

Common Modules

Docs: ansible.builtin collection index, Ansible docs

ModulePurposeKey params
apt / yum / dnfPackage management (package = distro-agnostic)name, state, update_cache
copyPush static file/contentsrc, dest, mode, content
templateRender Jinja2 to remotesrc (.j2), dest, validate
fileFiles, dirs, links, permspath, state (touch/directory/link/absent), mode, owner
service / systemdManage servicesname, state, enabled, daemon_reload
userManage usersname, groups, shell, state
lineinfileEdit single line idempotentlypath, regexp, line, state
gitClone/update reposrepo, dest, version
uriHTTP calls / health checksurl, method, status_code, body_format
commandRun command (no shell)cmd, creates, chdir
shellRun through /bin/shneeded for pipes, redirects, $VAR

command vs shell: prefer command (safer, no shell injection); use shell only when you need pipes/globs/env expansion. Neither is idempotent by default; guard with creates:, changed_when:, or better, find a real module.

Variables & Precedence

Docs: Using variables, Ansible docs

Low → high (abridged to the ones that matter day-to-day): role defaults/main.yamlgroup_vars/allgroup_vars/<group>host_vars/<host> → play vars:/vars_files: → task vars:set_fact/registered vars → extra vars -e key=value (always wins).

- name: Register output for later
  ansible.builtin.command: git rev-parse --short HEAD
  register: git_sha
  changed_when: false
# use later as: {{ git_sha.stdout }}

Facts

Docs: Discovering variables: facts and magic variables, Ansible docs

ansible web1 -m setup                              # everything
ansible web1 -m setup -a 'filter=ansible_mem*'     # filtered

Common ones: ansible_hostname, ansible_distribution, ansible_default_ipv4.address, ansible_processor_vcpus, ansible_memtotal_mb. Set gather_facts: false on plays that don’t need them; big speedup on large inventories.

Loops & Conditionals

Docs: Loops, Ansible docs

- name: Install packages
  ansible.builtin.apt:
    name: "{{ item }}"
    state: present
  loop: [nginx, certbot, jq]

- name: Create users with attributes
  ansible.builtin.user:
    name: "{{ item.name }}"
    groups: "{{ item.groups }}"
  loop:
    - { name: alice, groups: sudo }
    - { name: bob,   groups: docker }

- name: Only on Debian prod hosts   # list items are AND-ed
  ansible.builtin.apt:
    name: unattended-upgrades
  when:
    - ansible_os_family == 'Debian'
    - env_name == 'prod'

Retry pattern: until: result.status == 200 with retries: 10 and delay: 5 on a registered task.

Handlers & notify

Docs: Handlers: running operations on change, Ansible docs

Handlers run once at the end of the play, only if something notified them, in the order they are defined (not notification order).

tasks:
  - name: Update nginx config
    ansible.builtin.template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify: Reload nginx

handlers:
  - name: Reload nginx
    ansible.builtin.service:
      name: nginx
      state: reloaded

Force handlers to fire mid-play with a ansible.builtin.meta: flush_handlers task.

Roles Layout

Docs: Roles, Ansible docs

roles/nginx/
├── defaults/main.yaml     # overridable defaults (lowest precedence)
├── vars/main.yaml         # role constants (high precedence)
├── tasks/main.yaml        # entry point
├── handlers/main.yaml
├── templates/ + files/    # nginx.conf.j2, static assets
└── meta/main.yaml         # dependencies, galaxy info
ansible-galaxy init roles/myrole              # scaffold the layout above
ansible-galaxy install -r requirements.yaml   # pull external roles/collections

Apply in a play with roles: [common, nginx], or role: nginx plus inline vars: to override defaults.

Vault

Docs: Protecting sensitive data with Ansible Vault, Ansible docs

ansible-vault create  group_vars/prod/vault.yaml     # new encrypted file
ansible-vault edit    group_vars/prod/vault.yaml
ansible-vault encrypt secrets.yaml
ansible-vault decrypt secrets.yaml
ansible-vault encrypt_string 's3cret' --name db_password   # inline single value

ansible-playbook site.yaml --ask-vault-pass
ansible-playbook site.yaml --vault-password-file ~/.vault_pass   # chmod 600, gitignore it

Convention: keep secrets in vault.yaml prefixed vault_, reference them from a plaintext vars.yaml (db_password: "{{ vault_db_password }}") so grep still finds variable names.

Tags, Check Mode & Diff

Docs: Tags, Ansible docs

ansible-playbook site.yaml --tags 'config,certs'
ansible-playbook site.yaml --skip-tags packages
ansible-playbook site.yaml --check --diff        # dry run + show file changes

--check predicts changes without applying; --diff shows template/lineinfile deltas. Tasks that must always/never run: tags: always / tags: never.

0

ansible.cfg Essentials

Docs: Ansible configuration settings, Ansible docs

1
# ansible.cfg (project root wins over ~/.ansible.cfg)
[defaults]
inventory = inventory/inventory.yaml
remote_user = deploy
forks = 20
host_key_checking = False        # convenient for cattle; understand the tradeoff
stdout_callback = yaml

[privilege_escalation]
become = True

[ssh_connection]
pipelining = True                # big speedup
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

Jinja2 Templating Basics

Docs: Templating (Jinja2), Ansible docs

# templates/nginx.conf.j2
worker_processes {{ ansible_processor_vcpus }};
events { worker_connections {{ nginx_worker_connections | default(1024) }}; }
{% for site in sites %}
server {
  listen 80;
  server_name {{ site.domain }};
{% if site.redirect_https | default(false) %}
  return 301 https://{{ site.domain }}$request_uri;
{% endif %}
}
{% endfor %}

Filters I use constantly: default('x'), join(','), to_yaml / to_nice_json, regex_replace('a', 'b'), b64encode, ternary('yes', 'no'). Test any expression fast with ansible localhost -m debug -a "msg={{ expr }}".

2
  • AWS CloudFormation: intrinsic functions, change sets, and drift detection to provision the infrastructure these playbooks configure
  • Jenkins: wire ansible-playbook into declarative pipelines with credential binding, parameters, and parallel stages
  • Bash Basics: quoting, exit codes, and error handling for every command/shell task and dynamic-inventory script

Everything else is in the DevOps Tooling group, or start from the full cheatsheet library.

Similar Articles

More from devops

tmux Cheatsheet

Quick-reference tmux cheatsheet: sessions, windows, panes, copy mode, synchronize-panes, .tmux.conf …

Jenkins Cheatsheet

Jenkins cheatsheet with declarative pipeline syntax, credentials, parameters, parallel stages, …

Docker Cheatsheet

Docker cheatsheet with the commands I use daily as a DevOps engineer: image builds, run flags, …

Bash Basics Cheatsheet

Quick-reference Bash basics cheatsheet: variables, parameter expansion, conditionals, loops, arrays, …