Playbooks, roles, inventories, modules, variables, vault, and automation patterns for configuration management.
# ── Static Inventory ──
[webservers]
web1.example.com ansible_user=ubuntu ansible_python_interpreter=/usr/bin/python3
web2.example.com ansible_user=ubuntu
web3.example.com
[dbservers]
db1.example.com ansible_user=admin
db2.example.com
[loadbalancers]
lb1.example.com
# --- vars for all hosts ---
ansible_ssh_common_args=-o StrictHostKeyChecking=no
ansible_python_interpreter=auto_silent
# --- vars for webservers ---
http_port=80
nginx_worker_processes=auto
# --- production group children ---
webservers
dbservers
loadbalancers---
# ── Playbook: Deploy Web Application ──
- name: Configure and deploy web servers
hosts: webservers
become: true
vars:
app_name: myapp
deploy_path: /var/www/myapp
node_version: "18"
tasks:
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Install required packages
ansible.builtin.apt:
name:
- nginx
- curl
- git
- unzip
state: present
- name: Create deploy directory
ansible.builtin.file:
path: "{{ deploy_path }}"
state: directory
owner: www-data
group: www-data
mode: "0755"
- name: Clone application repository
ansible.builtin.git:
repo: https://github.com/user/myapp.git
dest: "{{ deploy_path }}"
version: main
force: true
- name: Install Node.js dependencies
community.general.npm:
path: "{{ deploy_path }}"
production: true
- name: Build application
ansible.builtin.shell: npm run build
args:
chdir: "{{ deploy_path }}"
- name: Configure Nginx
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/myapp
owner: root
mode: "0644"
notify: Reload Nginx
- name: Enable Nginx site
ansible.builtin.file:
src: /etc/nginx/sites-available/myapp
dest: /etc/nginx/sites-enabled/myapp
state: link
handlers:
- name: Reload Nginx
ansible.builtin.service:
name: nginx
state: reloaded
- name: Restart Nginx
ansible.builtin.service:
name: nginx
state: restarted| Command | Purpose |
|---|---|
| ansible all -m ping | Test connectivity |
| ansible webservers -m apt -a "update_cache=true" | Update apt cache |
| ansible all -m shell -a "uptime" | Run shell command |
| ansible all -m copy -a "src=file dest=/tmp/" | Copy file |
| ansible all -m user -a "name=john" | Create user |
| ansible all -m service -a "name=nginx state=restarted" | Restart service |
| ansible all -m setup | Gather system facts |
| ansible all -m file -a "path=/tmp/test state=directory" | Create directory |
| Module | Purpose | Key Params |
|---|---|---|
| apt / yum / dnf | Package management | name, state, update_cache |
| copy | Copy files to hosts | src, dest, mode, owner |
| template | Render Jinja2 templates | src, dest |
| file | Manage files/dirs/links | path, state, mode |
| service | Manage services | name, state, enabled |
| user / group | Manage users/groups | name, state, groups |
| shell / command | Execute commands | cmd, chdir, creates |
| git | Manage git repos | repo, dest, version |
---
# ── Role: webserver ──
# roles/webserver/defaults/main.yml
nginx_worker_processes: auto
nginx_worker_connections: 1024
http_port: 80
server_name: localhost
document_root: /var/www/html
client_max_body_size: 10m
# roles/webserver/vars/main.yml
nginx_packages:
- nginx
- nginx-extras---
# roles/webserver/tasks/main.yml
- name: Install Nginx
ansible.builtin.apt:
name: "{{ nginx_packages }}"
state: present
update_cache: true
- name: Create document root
ansible.builtin.file:
path: "{{ document_root }}"
state: directory
owner: www-data
mode: "0755"
- name: Configure Nginx
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
mode: "0644"
notify: Restart Nginx
- name: Configure virtual host
ansible.builtin.template:
src: vhost.conf.j2
dest: /etc/nginx/sites-available/default
mode: "0644"
notify: Reload Nginx
- name: Ensure Nginx is running
ansible.builtin.service:
name: nginx
state: started
enabled: true---
- name: Deploy full stack
hosts: production
become: true
vars_files:
- group_vars/vault.yml
pre_tasks:
- name: Check OS compatibility
ansible.builtin.assert:
that:
- ansible_distribution == "Ubuntu"
- ansible_distribution_major_version | int >= 22
fail_msg: "Requires Ubuntu 22.04+"
quiet: true
roles:
- role: webserver
vars:
server_name: example.com
document_root: /var/www/example
tags: [web, nginx]
- role: database
vars:
db_name: myapp_production
tags: [db, postgres]
- role: application
tags: [app, deploy]
post_tasks:
- name: Verify deployment
ansible.builtin.uri:
url: "http://localhost"
status_code: 200
register: result
until: result.status == 200
retries: 5
delay: 10| Source | Scope |
|---|---|
| role/defaults/main.yml | Default values (safe) |
| inventory group_vars | Group-level vars |
| inventory host_vars | Host-level vars |
| playbook vars | Playbook-level vars |
| role/vars/main.yml | Role variables (override defaults) |
| task vars | Task-level vars |
| extra vars (-e) | Command line vars (highest) |
| Filter | Example | Output |
|---|---|---|
| default | var | default("fallback") | Default if undefined |
| upper / lower | name | upper | CASE CHANGE |
| replace | s | replace("a","b") | String replace |
| split | "a,b,c" | split(",") | List ["a","b","c"] |
| join | list | join(", ") | String from list |
| int / float | val | int | Type conversion |
| length | list | length | Count items |
| ternary | x if condition else y | Ternary operator |
# ── Ansible Vault: Encrypt Secrets ──
# Create encrypted file
ansible-vault create secrets.yml
# Encrypt existing file
ansible-vault encrypt group_vars/vault.yml
# Edit encrypted file
ansible-vault edit group_vars/vault.yml
# View encrypted file
ansible-vault view group_vars/vault.yml
# Change vault password
ansible-vault rekey group_vars/vault.yml
# Run playbook with vault
ansible-playbook site.yml --ask-vault-pass
ansible-playbook site.yml --vault-password-file ~/.vault_pass
# Encrypt string for inline use
ansible-vault encrypt_string 'my_secret_password' --name 'db_password'
# Returns:
# db_password: !vault |
# $ANSIBLE_VAULT;1.1;AES256;...
# ── Ansible Galaxy ──
# Search roles
ansible-galaxy search nginx --author geerlingguy
# Install role
ansible-galaxy install geerlingguy.nginx
# Install from requirements
ansible-galaxy install -r requirements.yml
# Create role skeleton
ansible-galaxy role init my_custom_role---
# ── Galaxy Requirements ──
roles:
- name: geerlingguy.nginx
version: 3.1.0
- name: geerlingguy.postgresql
version: 3.4.0
- name: geerlingguy.nodejs
version: 2.0.0
- name: community.docker
version: 3.0.0
collections:
- name: ansible.posix
version: ">=1.5.0"
- name: community.docker
version: ">=3.0.0"
- name: community.crypto
version: ">=2.0.0"ansible-vault for all sensitive data. In CI/CD, use --vault-password-file with a secret from your CI vault (GitHub Actions Secrets, Jenkins Credentials, etc.).ansible.builtin.command executes commands without a shell (no pipes, redirects, environment variables). It is more secure and predictable. ansible.builtin.shell executes through /bin/sh, supporting pipes (|), redirects (>), and shell features like &&. Use command for simple commands; use shell only when you need shell features.
Idempotency means running the same playbook multiple times produces the same result. Ansible modules check current state before making changes — if the desired state is already met, no changes are made. For example, apt: name=nginx state=present checks if nginx is installed and skips if already present. This makes Ansible safe to run repeatedly without side effects. Always prefer modules over shell commands for idempotency.