Ansible
Configuration management and automation with Ansible - ad-hoc commands, playbooks, roles, vault, and common modules.
Ad-Hoc Commands
ansible all -i inventory -m ping
ansible webservers -m shell -a 'uptime'
web-01 | CHANGED | rc=0 >> 14:32:01 up 42 days, 3:15, 1 user, load average: 0.08, 0.03, 0.01
ansible all -m setup -a 'filter=ansible_os_family'
ansible all -m copy -a 'src=resolv.conf dest=/etc/resolv.conf' --become
ansible all -m package -a 'name=htop state=present' --become
ansible all -m systemd -a 'name=sshd state=restarted' --become
PATTERN: Ad-hoc for one-off tasks. Playbooks for anything you will run twice.
Playbook Execution
ansible-playbook site.yml
ansible-playbook site.yml --check --diff
ansible-playbook site.yml --limit webservers
ansible-playbook site.yml --limit 'web-01,web-02'
ansible-playbook site.yml --tags deploy
ansible-playbook site.yml --skip-tags slow
ansible-playbook site.yml -e "env=prod version=2.1"
ansible-playbook site.yml -e @vars.yml
ansible-playbook site.yml -v # Minimal
ansible-playbook site.yml -vvv # Connection debug
ansible-playbook site.yml -vvvv # Full wire-level
ansible-playbook site.yml --list-tasks
ansible-playbook site.yml --list-hosts
ansible-playbook site.yml --start-at-task="Deploy app"
MUSCLE MEMORY: --check --diff before every real run.
Playbook Structure
---
- name: Configure web servers
hosts: webservers
become: true
tasks:
- name: Install nginx
ansible.builtin.package:
name: nginx
state: present
- name: Start and enable nginx
ansible.builtin.systemd:
name: nginx
state: started
enabled: true
- name: Deploy config
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
notify: Reload nginx
handlers:
- name: Reload nginx
ansible.builtin.systemd:
name: nginx
state: reloaded
---
- name: Common setup
hosts: all
become: true
roles:
- common
- security-baseline
- name: Web tier
hosts: webservers
become: true
roles:
- nginx
- certbot
STRUCTURE: Handlers only fire when notified by a changed task, and run once at end of play.
Inventory
[webservers]
web-01 ansible_host=10.50.1.101
web-02 ansible_host=10.50.1.102
[dbservers]
db-01 ansible_host=10.50.1.110
[all:vars]
ansible_user=ansible
ansible_ssh_private_key_file=~/.ssh/id_ed25519
[prod:children]
webservers
dbservers
all:
children:
webservers:
hosts:
web-01:
ansible_host: 10.50.1.101
web-02:
ansible_host: 10.50.1.102
dbservers:
hosts:
db-01:
ansible_host: 10.50.1.110
vars:
ansible_user: ansible
# Tree view
ansible-inventory --graph
# JSON export with jq
ansible-inventory --list | jq '.webservers.hosts'
GOTCHA: YAML inventory is cleaner for complex grouping. INI is fine for flat lists.
Roles
ansible-galaxy init roles/nginx
roles/nginx/ ├── defaults/main.yml # Default variables (lowest precedence) ├── files/ # Static files for copy module ├── handlers/main.yml # Handler definitions ├── meta/main.yml # Role metadata, dependencies ├── tasks/main.yml # Task entry point ├── templates/ # Jinja2 templates └── vars/main.yml # Variables (high precedence)
- name: Deploy stack
hosts: webservers
become: true
roles:
- common
- { role: nginx, nginx_port: 8080 }
- role: certbot
when: env == 'prod'
# requirements.yml
ansible-galaxy install -r requirements.yml
# requirements.yml
roles:
- name: geerlingguy.docker
version: "6.1.0"
collections:
- name: community.general
version: ">=7.0.0"
Ansible Vault
ansible-vault create secrets.yml
ansible-vault encrypt vars.yml
ansible-vault edit secrets.yml
ansible-vault encrypt_string 'supersecret' --name 'db_password'
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
61626364656667...
# Prompt for password
ansible-playbook site.yml --ask-vault-pass
# Password file (CI/CD)
ansible-playbook site.yml --vault-password-file ~/.vault_pass
SECURITY: Never commit --vault-password-file targets. Use ANSIBLE_VAULT_PASSWORD_FILE env var in CI.
Common Modules
- name: Create directory
ansible.builtin.file:
path: /opt/app
state: directory
owner: app
mode: '0755'
- name: Template config
ansible.builtin.template:
src: app.conf.j2
dest: /etc/app/app.conf
backup: true
notify: Restart app
- name: Download file
ansible.builtin.get_url:
url: https://example.com/binary
dest: /usr/local/bin/binary
mode: '0755'
checksum: sha256:abc123...
- name: Create service user
ansible.builtin.user:
name: app
system: true
shell: /sbin/nologin
create_home: false
- name: Add SSH key
ansible.posix.authorized_key:
user: ansible
key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
- name: Install packages
ansible.builtin.package:
name:
- nginx
- certbot
- python3-certbot-nginx
state: present
- name: Enable and start service
ansible.builtin.systemd:
name: nginx
state: started
enabled: true
daemon_reload: true
- name: Run only if file missing
ansible.builtin.command:
cmd: /opt/app/setup.sh
creates: /opt/app/.installed
- name: Shell with pipe
ansible.builtin.shell:
cmd: journalctl -u app --no-pager | tail -20
register: app_logs
changed_when: false
Conditionals & Loops
- name: Install on Debian family
ansible.builtin.apt:
name: nginx
state: present
when: ansible_os_family == 'Debian'
- name: Install on RedHat family
ansible.builtin.dnf:
name: nginx
state: present
when: ansible_os_family == 'RedHat'
- name: Create users
ansible.builtin.user:
name: "{{ item }}"
state: present
loop:
- deploy
- monitor
- backup
- name: Create directories with specific owners
ansible.builtin.file:
path: "{{ item.path }}"
owner: "{{ item.owner }}"
state: directory
loop:
- { path: /opt/app, owner: app }
- { path: /var/log/app, owner: app }
- { path: /etc/app, owner: root }
- name: Check if config exists
ansible.builtin.stat:
path: /etc/app/app.conf
register: config_file
- name: Generate config if missing
ansible.builtin.template:
src: app.conf.j2
dest: /etc/app/app.conf
when: not config_file.stat.exists
Debugging
- name: Print variable
ansible.builtin.debug:
var: ansible_default_ipv4.address
- name: Print message
ansible.builtin.debug:
msg: "Deploying version {{ app_version }} to {{ inventory_hostname }}"
ansible-playbook site.yml --step
ANSIBLE_CALLBACKS_ENABLED=timer,profile_tasks ansible-playbook site.yml
PATTERN: register + debug to inspect module output during development.
Quick Reference
| Task | Command |
|---|---|
Ping all hosts |
|
Ad-hoc shell command |
|
Gather specific fact |
|
Run playbook |
|
Dry run with diffs |
|
Limit to group |
|
Run tagged tasks only |
|
Extra variables |
|
Vault create |
|
Vault encrypt string |
|
Scaffold role |
|
Install requirements |
|
Inventory graph |
|
List tasks |
|
Verbose debug |
|