Skip to content

Ansible — Practical

ansible/
ansible.cfg
inventory/
prod.ini
staging.ini
group_vars/
all.yml
web.yml
host_vars/
web1.example.com.yml
playbooks/
site.yml
deploy.yml
roles/
common/
nginx/
app/
collections/
requirements.yml
[defaults]
inventory = inventory/prod.ini
roles_path = ./roles
collections_path = ./collections
host_key_checking = False
retry_files_enabled = False
forks = 50
strategy = mitogen_linear
stdout_callback = yaml
gathering = smart
fact_caching = jsonfile
fact_caching_connection = ./.ansible_facts
fact_caching_timeout = 86400
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
- import_playbook: common.yml
- import_playbook: web.yml
- import_playbook: db.yml
- hosts: web
serial: 5
max_fail_percentage: 0
pre_tasks:
- name: drain from LB
command: aws elbv2 deregister-targets ...
delegate_to: localhost
tasks:
- name: pull image
docker_image: { name: ghcr.io/org/api, tag: "{{ tag }}", source: pull }
- name: restart container
docker_container:
name: api
image: ghcr.io/org/api:{{ tag }}
state: started
restart: true
published_ports: ["8080:8080"]
env: { LOG_LEVEL: info }
post_tasks:
- name: wait healthy
uri: { url: "http://{{ inventory_hostname }}:8080/healthz", status_code: 200 }
retries: 30
delay: 2
- name: re-add to LB
command: aws elbv2 register-targets ...
delegate_to: localhost
roles/common/tasks/main.yml
- name: ensure timezone
timezone: { name: UTC }
- name: base packages
apt:
name:
- curl
- htop
- vim
- jq
- tcpdump
state: present
update_cache: true
- name: SSH hardening
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PermitRootLogin'
line: 'PermitRootLogin no'
notify: restart sshd
- name: deploy users
user:
name: "{{ item.name }}"
groups: sudo
shell: /bin/bash
state: present
loop: "{{ admin_users }}"
roles/nginx/tasks/main.yml
- name: install
apt: { name: nginx, state: present }
- name: site config
template: { src: site.conf.j2, dest: /etc/nginx/sites-available/default, mode: '0644' }
notify: reload nginx
- name: enable + start
service: { name: nginx, state: started, enabled: true }
roles/nginx/handlers/main.yml
- name: reload nginx
service: { name: nginx, state: reloaded }
Terminal window
# create
ansible-vault create group_vars/prod/secrets.yml
# edit
ansible-vault edit group_vars/prod/secrets.yml
# string
ansible-vault encrypt_string 'super' --name 'db_password' >> group_vars/prod/vars.yml
# run
ansible-playbook site.yml --vault-password-file ~/.vault_pass
inventory/aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions: [eu-west-1]
keyed_groups:
- key: tags.role
prefix: role
- key: tags.env
prefix: env
hostnames:
- tag:Name
filters:
tag:env: prod
Terminal window
ansible-inventory -i inventory/aws_ec2.yml --list --graph
roles/nginx/molecule/default/molecule.yml
driver: { name: docker }
platforms:
- name: ubuntu22
image: geerlingguy/docker-ubuntu2204-ansible
privileged: true
provisioner: { name: ansible }
verifier: { name: ansible }
molecule/default/converge.yml
- hosts: all
roles: [nginx]
molecule/default/verify.yml
- hosts: all
tasks:
- name: nginx serves
uri: { url: "http://localhost", status_code: 200 }
Terminal window
molecule test
- run: ansible-lint .
- run: ansible-playbook -i inventory/staging.ini site.yml --check --diff
- run: ansible-playbook -i inventory/staging.ini site.yml
Terminal window
ansible all -m ping
ansible web -m setup -a 'filter=ansible_distribution*' # facts
ansible-playbook play.yml --start-at-task "deploy site"
ansible-playbook play.yml -e tag=1.2.3 # extra vars
ansible-doc apt # module docs
ansible-config dump --only-changed
  • Ansible Lint — best practices.
  • Molecule — role testing.
  • AWX / Ansible Tower / AAP — UI + RBAC.
  • Galaxy / collections — share/reuse.
  • Mitogen — speedup plugin.