Master Ansible playbooks, variables, templates, handlers, and advanced features for robust configuration management and automation.
<p>
While ad-hoc commands are great for quick tasks, Ansible playbooks are where the real power
of automation shines. Playbooks are YAML files that define a series of tasks to be executed
in a specific order, allowing you to orchestrate complex deployments and configurations.
</p>
<p>
This lesson covers playbooks, variables, templates, and advanced features that enable you
to build sophisticated, reusable automation for configuration management at scale.
</p>
<h3>Understanding Playbooks</h3>
<p>
A playbook is a YAML file containing one or more "plays". Each play targets a group of
hosts and defines tasks to execute on those hosts. Tasks call Ansible modules to perform
actions.
</p>
<h4>Playbook Structure:</h4>
1---
2# Playbook structure
3- name: Play 1 - Web Server Setup # Play name (descriptive)
4 hosts: webservers # Target hosts/groups
5 become: yes # Privilege escalation
6 vars: # Play-level variables
7 http_port: 80
8
9 tasks: # List of tasks
10 - name: Install nginx # Task name (required)
11 apt: # Module name
12 name: nginx # Module parameters
13 state: present
14
15 - name: Start nginx service
16 service:
17 name: nginx
18 state: started
19 enabled: yes
20
21- name: Play 2 - Database Setup # Second play in same playbook
22 hosts: databases
23 become: yes
24
25 tasks:
26 - name: Install PostgreSQL
27 apt:
28 name: postgresql
29 state: present<h4>Basic Playbook Example:</h4>
1---
2# site.yml - Complete web server setup
3- name: Configure Web Servers
4 hosts: webservers
5 become: yes
6
7 tasks:
8 - name: Update apt cache
9 apt:
10 update_cache: yes
11 cache_valid_time: 3600
12
13 - name: Install nginx
14 apt:
15 name: nginx
16 state: present
17
18 - name: Copy custom nginx config
19 copy:
20 src: files/nginx.conf
21 dest: /etc/nginx/nginx.conf
22 owner: root
23 group: root
24 mode: '0644'
25
26 - name: Start and enable nginx
27 service:
28 name: nginx
29 state: started
30 enabled: yes
31
32 - name: Ensure nginx is running
33 service:
34 name: nginx
35 state: started<h4>Running Playbooks:</h4>
1# Execute playbook
2ansible-playbook site.yml
3
4# With specific inventory
5ansible-playbook -i production_inventory site.yml
6
7# Limit to specific hosts
8ansible-playbook site.yml --limit webservers
9
10# Check mode (dry-run)
11ansible-playbook site.yml --check
12
13# With extra variables
14ansible-playbook site.yml -e "http_port=8080"
15
16# Verbose output
17ansible-playbook site.yml -v # verbose
18ansible-playbook site.yml -vv # more verbose
19ansible-playbook site.yml -vvv # very verbose
20
21# Start at specific task
22ansible-playbook site.yml --start-at-task="Install nginx"
23
24# Run specific tags only
25ansible-playbook site.yml --tags "nginx,config"
26
27# Skip specific tags
28ansible-playbook site.yml --skip-tags "database"
29
30# Syntax check
31ansible-playbook site.yml --syntax-check
32
33# List all tasks
34ansible-playbook site.yml --list-tasks
35
36# List all hosts
37ansible-playbook site.yml --list-hosts<h4>Playbook Return Values:</h4>
1# Playbook execution results show:
2PLAY RECAP *********************************************************
3web1.example.com : ok=5 changed=2 unreachable=0 failed=0
4web2.example.com : ok=5 changed=2 unreachable=0 failed=0
5db1.example.com : ok=3 changed=1 unreachable=0 failed=0
6
7# ok: Tasks that ran successfully
8# changed: Tasks that made changes
9# unreachable: Hosts that couldn't be reached
10# failed: Tasks that failed
11# skipped: Tasks that were skipped (conditionals)
12# rescued: Tasks recovered from failure
13# ignored: Failed tasks with ignore_errors<h4>Multiple Plays:</h4>
1---
2# Multi-tier application deployment
3- name: Prepare all servers
4 hosts: all
5 become: yes
6 tasks:
7 - name: Update all packages
8 apt:
9 upgrade: dist
10 update_cache: yes
11
12- name: Configure database servers
13 hosts: databases
14 become: yes
15 tasks:
16 - name: Install PostgreSQL
17 apt:
18 name: postgresql
19 state: present
20
21- name: Configure web servers
22 hosts: webservers
23 become: yes
24 tasks:
25 - name: Install nginx
26 apt:
27 name: nginx
28 state: present
29
30- name: Configure load balancers
31 hosts: loadbalancers
32 become: yes
33 tasks:
34 - name: Install HAProxy
35 apt:
36 name: haproxy
37 state: present<h3>Working with Variables</h3>
<p>
Variables make playbooks flexible and reusable. You can define variables in multiple
places with different precedence levels.
</p>
<h4>Defining Variables:</h4>
1# 1. In playbook (vars section)
2---
3- name: Example with vars
4 hosts: webservers
5 vars:
6 http_port: 80
7 max_clients: 200
8 server_name: example.com
9 tasks:
10 - name: Show variable
11 debug:
12 msg: "Port is {{ http_port }}"
13
14# 2. In separate vars file
15---
16- name: Example with vars_files
17 hosts: webservers
18 vars_files:
19 - vars/common.yml
20 - vars/web.yml
21 tasks:
22 - name: Use variables
23 debug:
24 msg: "{{ http_port }}"
25
26# 3. In inventory
27[webservers]
28web1 http_port=80
29web2 http_port=8080
30
31[webservers:vars]
32max_clients=200
33
34# 4. In group_vars/host_vars
35# group_vars/webservers.yml
36---
37http_port: 80
38max_clients: 200
39
40# host_vars/web1.example.com.yml
41---
42http_port: 8080
43
44# 5. Command line (-e has highest precedence)
45ansible-playbook site.yml -e "http_port=3000"
46
47# 6. Registered variables (from task output)
48- name: Get disk usage
49 shell: df -h /
50 register: disk_usage
51
52- name: Show disk usage
53 debug:
54 msg: "{{ disk_usage.stdout }}"<h4>Variable Precedence (Lowest to Highest):</h4>
11. command line values (e.g. -u my_user)
22. role defaults
33. inventory file or script group vars
44. inventory group_vars/all
55. playbook group_vars/all
66. inventory group_vars/*
77. playbook group_vars/*
88. inventory file or script host vars
99. inventory host_vars/*
1010. playbook host_vars/*
1111. host facts / cached set_facts
1212. play vars
1313. play vars_prompt
1414. play vars_files
1515. role vars (defined in role/vars/main.yml)
1616. block vars (only for tasks in block)
1717. task vars (only for the task)
1818. include_vars
1919. set_facts / registered vars
2020. role (and include_role) params
2121. include params
2222. extra vars (-e in CLI) - ALWAYS WIN<h4>Variable Types:</h4>
1# Strings
2server_name: example.com
3another_string: "quoted string"
4multiline: |
5 This is a
6 multiline string
7
8# Numbers
9http_port: 80
10max_connections: 1000
11
12# Booleans
13enable_ssl: true
14debug_mode: false
15
16# Lists (arrays)
17packages:
18 - nginx
19 - postgresql
20 - redis
21
22# Alternative list syntax
23packages: ['nginx', 'postgresql', 'redis']
24
25# Dictionaries (hashes/maps)
26database:
27 host: db.example.com
28 port: 5432
29 name: myapp
30 user: dbuser
31
32# Alternative dict syntax
33database: {host: 'db.example.com', port: 5432}
34
35# Access list items
36{{ packages[0] }} # nginx
37{{ packages | first }} # nginx
38
39# Access dict items
40{{ database.host }} # db.example.com
41{{ database['host'] }} # db.example.com<h4>Special Variables:</h4>
1# Magic variables (provided by Ansible)
2{{ inventory_hostname }} # Current host name
3{{ ansible_hostname }} # System hostname (from facts)
4{{ groups }} # All groups and hosts
5{{ groups['webservers'] }} # Hosts in webservers group
6{{ group_names }} # Groups current host belongs to
7{{ hostvars }} # Variables for all hosts
8{{ play_hosts }} # Hosts in current play
9{{ ansible_play_hosts }} # List of active hosts
10{{ ansible_version }} # Ansible version
11
12# Environment variables
13{{ lookup('env', 'HOME') }} # Get HOME env var
14{{ ansible_env.HOME }} # From gathered facts
15
16# Facts (gathered automatically)
17{{ ansible_distribution }} # OS distribution
18{{ ansible_distribution_version }}
19{{ ansible_processor_cores }}
20{{ ansible_memtotal_mb }}
21{{ ansible_default_ipv4.address }}
22{{ ansible_hostname }}
23{{ ansible_fqdn }}
24
25# Example usage
26- name: Display facts
27 debug:
28 msg: |
29 Hostname: {{ ansible_hostname }}
30 IP: {{ ansible_default_ipv4.address }}
31 OS: {{ ansible_distribution }} {{ ansible_distribution_version }}
32 Memory: {{ ansible_memtotal_mb }} MB<h4>Variable Filters:</h4>
1# Jinja2 filters transform variables
2{{ some_variable | default('default_value') }}
3{{ path | basename }}
4{{ string | upper }}
5{{ string | lower }}
6{{ list | length }}
7{{ dict | to_json }}
8{{ dict | to_yaml }}
9{{ string | regex_replace('[0-9]', 'X') }}
10
11# Common filters
12- name: Example filters
13 debug:
14 msg: "{{ item }}"
15 loop:
16 - "{{ 'hello' | upper }}" # HELLO
17 - "{{ '/path/to/file.txt' | basename }}" # file.txt
18 - "{{ packages | length }}" # 3
19 - "{{ http_port | default(80) }}" # 80
20 - "{{ server_name | mandatory }}" # Fail if not defined
21 - "{{ some_bool | bool }}" # Convert to boolean
22 - "{{ some_string | int }}" # Convert to integer<h3>Dynamic Configuration with Templates</h3>
<p>
Templates allow you to create dynamic configuration files using Jinja2 syntax.
Template files (*.j2) contain variables, loops, and conditionals that are rendered
when deployed to target hosts.
</p>
<h4>Basic Template Usage:</h4>
1# nginx.conf.j2 template
2server {
3 listen {{ http_port }};
4 server_name {{ server_name }};
5
6 root {{ document_root }};
7 index index.html index.htm;
8
9 location / {
10 try_files $uri $uri/ =404;
11 }
12}
13
14# Playbook task to deploy template
15- name: Deploy nginx configuration
16 template:
17 src: templates/nginx.conf.j2
18 dest: /etc/nginx/sites-available/{{ server_name }}
19 owner: root
20 group: root
21 mode: '0644'
22 notify: Restart nginx<h4>Template with Conditionals:</h4>
1# nginx-vhost.conf.j2
2server {
3 listen {{ http_port }};
4
5 {% if enable_ssl %}
6 listen {{ https_port }} ssl;
7 ssl_certificate {{ ssl_cert }};
8 ssl_certificate_key {{ ssl_key }};
9 {% endif %}
10
11 server_name {{ server_name }};
12
13 {% if enable_logging %}
14 access_log /var/log/nginx/{{ server_name }}_access.log;
15 error_log /var/log/nginx/{{ server_name }}_error.log;
16 {% endif %}
17
18 location / {
19 root {{ document_root }};
20 {% if enable_php %}
21 location ~ \.php$ {
22 fastcgi_pass unix:/var/run/php/php-fpm.sock;
23 fastcgi_index index.php;
24 }
25 {% endif %}
26 }
27}<h4>Template with Loops:</h4>
1# hosts.j2 - Generate /etc/hosts file
2127.0.0.1 localhost
3
4# Application servers
5{% for host in groups['webservers'] %}
6{{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ host }}
7{% endfor %}
8
9# Database servers
10{% for host in groups['databases'] %}
11{{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ host }}
12{% endfor %}
13
14# haproxy.cfg.j2 - Load balancer config
15global
16 maxconn {{ max_connections }}
17
18defaults
19 mode http
20 timeout connect 5000ms
21
22frontend http_front
23 bind *:{{ frontend_port }}
24 default_backend http_back
25
26backend http_back
27 balance {{ lb_algorithm }}
28 {% for host in groups['webservers'] %}
29 server {{ host }} {{ hostvars[host]['ansible_default_ipv4']['address'] }}:{{ backend_port }} check
30 {% endfor %}<h4>Template with Filters:</h4>
1# config.j2
2# Server name in uppercase
3server_name={{ server_name | upper }}
4
5# Default values
6port={{ http_port | default(80) }}
7
8# Convert to JSON
9database_config={{ database | to_json }}
10
11# Convert to YAML
12app_config={{ application | to_yaml }}
13
14# File basename
15log_file=/var/log/{{ config_file | basename }}
16
17# Date/time
18generated_on={{ ansible_date_time.iso8601 }}
19
20# Custom formatting
21{% set timestamp = ansible_date_time.epoch | int %}
22build_number={{ timestamp }}<h4>Template with Comments:</h4>
1# app.conf.j2
2{# This is a Jinja2 comment - won't appear in output #}
3
4# Application Configuration
5# Generated by Ansible on {{ ansible_date_time.date }}
6
7[server]
8host = {{ server_host }}
9port = {{ server_port }}
10
11{# Environment-specific settings #}
12{% if environment == 'production' %}
13debug = false
14log_level = warning
15{% else %}
16debug = true
17log_level = debug
18{% endif %}
19
20[database]
21{% for db in databases %}
22# {{ db.name }} database
23{{ db.name }}_host = {{ db.host }}
24{{ db.name }}_port = {{ db.port }}
25{% endfor %}<h4>Advanced Template Features:</h4>
1# Set variables within template
2{% set my_var = "value" %}
3
4# Conditionals
5{% if condition %}
6 content
7{% elif other_condition %}
8 other content
9{% else %}
10 default content
11{% endif %}
12
13# For loops
14{% for item in list %}
15 {{ item }}
16{% endfor %}
17
18# For loop with index
19{% for item in list %}
20 {{ loop.index }}: {{ item }}
21{% endfor %}
22
23# Filter empty items
24{% for item in list if item %}
25 {{ item }}
26{% endfor %}
27
28# Include other templates
29{% include 'header.j2' %}
30
31# Macros (reusable blocks)
32{% macro print_server(name, ip) %}
33server {{ name }} {{ ip }}:80 check
34{% endmacro %}
35
36{% for host in groups['webservers'] %}
37{{ print_server(host, hostvars[host]['ansible_default_ipv4']['address']) }}
38{% endfor %}<h4>Using Templates in Playbooks:</h4>
1---
2- name: Deploy application configuration
3 hosts: appservers
4 vars:
5 environment: production
6 server_port: 8080
7
8 tasks:
9 - name: Deploy main config
10 template:
11 src: templates/app.conf.j2
12 dest: /etc/myapp/config.conf
13 owner: myapp
14 group: myapp
15 mode: '0640'
16 backup: yes # Backup existing file
17 validate: 'myapp --check-config %s' # Validate before replacing
18 notify: Restart application
19
20 - name: Deploy environment-specific config
21 template:
22 src: "templates/{{ environment }}.conf.j2"
23 dest: /etc/myapp/env.conf
24 when: environment is defined<h3>Event-Driven Actions with Handlers</h3>
<p>
Handlers are special tasks that run only when notified by other tasks. They're perfect
for restarting services after configuration changes, running once even if notified
multiple times.
</p>
<h4>Basic Handler Usage:</h4>
1---
2- name: Web server configuration
3 hosts: webservers
4 become: yes
5
6 tasks:
7 - name: Install nginx
8 apt:
9 name: nginx
10 state: present
11
12 - name: Deploy nginx config
13 copy:
14 src: files/nginx.conf
15 dest: /etc/nginx/nginx.conf
16 notify: Restart nginx # Notify handler
17
18 - name: Deploy site config
19 template:
20 src: templates/site.conf.j2
21 dest: /etc/nginx/sites-enabled/default
22 notify: Reload nginx # Notify different handler
23
24 handlers:
25 - name: Restart nginx
26 service:
27 name: nginx
28 state: restarted
29
30 - name: Reload nginx
31 service:
32 name: nginx
33 state: reloaded<h4>Multiple Notifications:</h4>
1---
2- name: Application deployment
3 hosts: appservers
4
5 tasks:
6 - name: Update application code
7 git:
8 repo: https://github.com/user/app.git
9 dest: /opt/app
10 notify:
11 - Restart application
12 - Clear cache
13 - Send notification
14
15 - name: Update configuration
16 template:
17 src: app.conf.j2
18 dest: /etc/app/config.conf
19 notify:
20 - Restart application
21
22 handlers:
23 - name: Restart application
24 systemd:
25 name: myapp
26 state: restarted
27
28 - name: Clear cache
29 file:
30 path: /var/cache/myapp
31 state: absent
32
33 - name: Send notification
34 slack:
35 token: "{{ slack_token }}"
36 msg: "Application deployed on {{ inventory_hostname }}"<h4>Handler Best Practices:</h4>
1# Handlers run in the order they're defined, not notified
2handlers:
3 - name: Stop application
4 service:
5 name: myapp
6 state: stopped
7
8 - name: Run database migrations
9 command: /opt/app/migrate.sh
10
11 - name: Start application
12 service:
13 name: myapp
14 state: started
15
16# Force handler execution with meta
17tasks:
18 - name: Update config
19 copy:
20 src: urgent.conf
21 dest: /etc/app/urgent.conf
22 notify: Restart application
23
24 - name: Force handlers to run now
25 meta: flush_handlers # Run all pending handlers
26
27 - name: Continue with other tasks
28 debug:
29 msg: "Handlers have run, continuing..."
30
31# Listen to multiple notifications
32handlers:
33 - name: Restart web services
34 service:
35 name: "{{ item }}"
36 state: restarted
37 loop:
38 - nginx
39 - php-fpm
40 listen: "restart web stack"
41
42tasks:
43 - name: Update nginx config
44 copy:
45 src: nginx.conf
46 dest: /etc/nginx/nginx.conf
47 notify: restart web stack
48
49 - name: Update PHP config
50 copy:
51 src: php.ini
52 dest: /etc/php/php.ini
53 notify: restart web stack<h4>Conditional Handlers:</h4>
1handlers:
2 - name: Restart nginx
3 service:
4 name: nginx
5 state: restarted
6 when: ansible_distribution == "Ubuntu"
7
8 - name: Reload nginx
9 service:
10 name: nginx
11 state: reloaded
12 when: not nginx_config_changed | default(false)
13
14# Handler with delegation
15handlers:
16 - name: Update load balancer
17 uri:
18 url: "http://{{ load_balancer }}/api/reload"
19 method: POST
20 delegate_to: localhost
21 run_once: true # Run only once for all hosts<h4>Handler Error Handling:</h4>
1handlers:
2 - name: Restart service
3 service:
4 name: myapp
5 state: restarted
6 register: restart_result
7 failed_when: false # Don't fail on error
8
9 - name: Check if restart succeeded
10 debug:
11 msg: "Restart failed"
12 when: restart_result.failed
13
14# Handler with retry
15handlers:
16 - name: Restart application
17 service:
18 name: myapp
19 state: restarted
20 retries: 3
21 delay: 5
22 register: result
23 until: result is succeeded<h3>Flow Control in Playbooks</h3>
<h4>Conditional Execution (when):</h4>
1# Simple condition
2- name: Install nginx on Ubuntu
3 apt:
4 name: nginx
5 state: present
6 when: ansible_distribution == "Ubuntu"
7
8# Multiple conditions (AND)
9- name: Install on Ubuntu 20.04
10 apt:
11 name: nginx
12 state: present
13 when:
14 - ansible_distribution == "Ubuntu"
15 - ansible_distribution_version == "20.04"
16
17# OR condition
18- name: Install on Ubuntu or Debian
19 apt:
20 name: nginx
21 state: present
22 when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"
23
24# Using in operator
25- name: Install on RedHat family
26 yum:
27 name: nginx
28 state: present
29 when: ansible_distribution in ['CentOS', 'RedHat', 'Rocky']
30
31# Variable defined check
32- name: Task only if variable is defined
33 debug:
34 msg: "Variable is {{ my_var }}"
35 when: my_var is defined
36
37# Variable not defined
38- name: Task if variable not defined
39 debug:
40 msg: "Variable not set"
41 when: my_var is not defined
42
43# Boolean checks
44- name: Task if enabled
45 debug:
46 msg: "Feature enabled"
47 when: feature_enabled | bool
48
49# Register variable conditions
50- name: Check if file exists
51 stat:
52 path: /etc/myapp/config.conf
53 register: config_file
54
55- name: Create config if missing
56 copy:
57 src: default.conf
58 dest: /etc/myapp/config.conf
59 when: not config_file.stat.exists
60
61# String comparisons
62- name: Conditional on string
63 debug:
64 msg: "Production environment"
65 when: environment == 'production'
66
67# Numeric comparisons
68- name: Task if port is high
69 debug:
70 msg: "Using high port"
71 when: http_port > 8000<h4>Loops (loop keyword):</h4>
1# Simple loop
2- name: Install multiple packages
3 apt:
4 name: "{{ item }}"
5 state: present
6 loop:
7 - nginx
8 - postgresql
9 - redis
10
11# Loop with dictionary
12- name: Create multiple users
13 user:
14 name: "{{ item.name }}"
15 state: present
16 groups: "{{ item.groups }}"
17 loop:
18 - {name: 'alice', groups: 'sudo,developers'}
19 - {name: 'bob', groups: 'developers'}
20 - {name: 'charlie', groups: 'sudo'}
21
22# Loop with variables
23- name: Install packages from variable
24 apt:
25 name: "{{ item }}"
26 state: present
27 loop: "{{ packages }}"
28
29# Loop with range
30- name: Create numbered directories
31 file:
32 path: "/opt/app/worker{{ item }}"
33 state: directory
34 loop: "{{ range(1, 6) | list }}" # 1,2,3,4,5
35
36# Loop with index
37- name: Show index and item
38 debug:
39 msg: "{{ loop.index }}: {{ item }}"
40 loop:
41 - apple
42 - banana
43 - orange
44
45# Loop control variables
46loop.index # 1-indexed
47loop.index0 # 0-indexed
48loop.first # True on first iteration
49loop.last # True on last iteration
50loop.length # Total items
51loop.revindex # Reverse index (1-indexed)
52loop.revindex0 # Reverse index (0-indexed)
53
54# Conditional loop
55- name: Install on specific hosts
56 apt:
57 name: "{{ item }}"
58 state: present
59 loop: "{{ packages }}"
60 when: inventory_hostname in groups['webservers']
61
62# Nested loops
63- name: Create user on all databases
64 postgresql_user:
65 name: "{{ item.0 }}"
66 db: "{{ item.1 }}"
67 loop: "{{ users | product(databases) | list }}"
68
69# Loop with register
70- name: Get status of multiple services
71 service_facts:
72 register: service_status
73
74- name: Show service status
75 debug:
76 msg: "{{ item.key }}: {{ item.value.state }}"
77 loop: "{{ service_status.ansible_facts.services | dict2items }}"
78 when: "'nginx' in item.key"<h4>Loop Control:</h4>
1# Change loop variable name
2- name: Custom loop var
3 debug:
4 msg: "{{ package }}"
5 loop:
6 - nginx
7 - postgresql
8 loop_control:
9 loop_var: package
10
11# Add labels for clarity
12- name: Install packages with labels
13 apt:
14 name: "{{ item }}"
15 state: present
16 loop:
17 - nginx
18 - postgresql
19 loop_control:
20 label: "Installing {{ item }}"
21
22# Add pause between iterations
23- name: Restart services slowly
24 service:
25 name: "{{ item }}"
26 state: restarted
27 loop:
28 - service1
29 - service2
30 loop_control:
31 pause: 5 # 5 second pause
32
33# Extended loop information
34- name: Show extended info
35 debug:
36 msg: "Item {{ loop.index }}/{{ loop.length }}: {{ item }}"
37 loop:
38 - first
39 - second
40 - third
41 loop_control:
42 extended: yes<h4>Until Loops (Retry):</h4>
1# Retry until condition met
2- name: Wait for service to be ready
3 uri:
4 url: "http://localhost:8080/health"
5 status_code: 200
6 register: result
7 until: result.status == 200
8 retries: 10
9 delay: 5 # Wait 5 seconds between retries
10
11# Check file existence
12- name: Wait for file
13 stat:
14 path: /tmp/deployment.complete
15 register: file_check
16 until: file_check.stat.exists
17 retries: 60
18 delay: 10
19
20# Wait for port
21- name: Wait for database port
22 wait_for:
23 host: "{{ db_host }}"
24 port: 5432
25 timeout: 300<h3>Grouping Tasks and Handling Errors</h3>
<h4>Blocks for Task Grouping:</h4>
1---
2- name: Example with blocks
3 hosts: webservers
4
5 tasks:
6 - name: Web server setup block
7 block:
8 - name: Install nginx
9 apt:
10 name: nginx
11 state: present
12
13 - name: Configure nginx
14 template:
15 src: nginx.conf.j2
16 dest: /etc/nginx/nginx.conf
17
18 - name: Start nginx
19 service:
20 name: nginx
21 state: started
22
23 when: ansible_distribution == "Ubuntu"
24 become: yes<h4>Error Handling with rescue and always:</h4>
1---
2- name: Error handling example
3 hosts: webservers
4
5 tasks:
6 - name: Application deployment with error handling
7 block:
8 - name: Stop application
9 service:
10 name: myapp
11 state: stopped
12
13 - name: Deploy new version
14 copy:
15 src: /builds/app-v2.0.tar.gz
16 dest: /opt/app/
17
18 - name: Extract application
19 unarchive:
20 src: /opt/app/app-v2.0.tar.gz
21 dest: /opt/app/
22 remote_src: yes
23
24 - name: Run database migrations
25 command: /opt/app/migrate.sh
26
27 - name: Start application
28 service:
29 name: myapp
30 state: started
31
32 rescue:
33 # These tasks run if any task in block fails
34 - name: Log deployment failure
35 lineinfile:
36 path: /var/log/deployments.log
37 line: "Deployment failed at {{ ansible_date_time.iso8601 }}"
38
39 - name: Rollback to previous version
40 command: /opt/app/rollback.sh
41
42 - name: Start old application version
43 service:
44 name: myapp
45 state: started
46
47 - name: Send alert
48 slack:
49 token: "{{ slack_token }}"
50 msg: "Deployment failed on {{ inventory_hostname }}"
51
52 always:
53 # These tasks ALWAYS run (success or failure)
54 - name: Cleanup temporary files
55 file:
56 path: /opt/app/app-v2.0.tar.gz
57 state: absent
58
59 - name: Log deployment attempt
60 lineinfile:
61 path: /var/log/deployments.log
62 line: "Deployment attempted at {{ ansible_date_time.iso8601 }}"<h4>Ignoring Errors:</h4>
1# Continue on failure
2- name: Try to install package
3 apt:
4 name: optional-package
5 state: present
6 ignore_errors: yes
7
8# Conditional failure
9- name: Run command
10 command: /usr/bin/some-command
11 register: result
12 failed_when: "'ERROR' in result.stderr"
13
14# Never fail
15- name: Command that might fail
16 command: /usr/bin/risky-command
17 failed_when: false
18
19# Custom failure condition
20- name: Check disk space
21 shell: df -h / | tail -1 | awk '{print $5}' | sed 's/%//'
22 register: disk_usage
23 failed_when: disk_usage.stdout | int > 90
24
25# Changed when condition
26- name: Run script
27 command: /usr/local/bin/script.sh
28 register: result
29 changed_when: "'Changes made' in result.stdout"<h4>Assertions:</h4>
1# Assert conditions
2- name: Check prerequisites
3 assert:
4 that:
5 - ansible_distribution == "Ubuntu"
6 - ansible_distribution_version == "20.04"
7 - ansible_memtotal_mb >= 2048
8 fail_msg: "System does not meet requirements"
9 success_msg: "All prerequisites met"
10
11# Multiple assertions
12- name: Validate configuration
13 assert:
14 that:
15 - http_port is defined
16 - http_port > 0
17 - http_port < 65536
18 - server_name is defined
19 - server_name | length > 0
20 quiet: yes # Don't show success message<h4>Abort on Failure:</h4>
1# Stop all hosts on any failure
2- name: Critical task
3 command: /usr/bin/critical-command
4 any_errors_fatal: yes
5
6# Force all hosts to stop
7- name: Abort on error
8 hosts: all
9 any_errors_fatal: true
10
11 tasks:
12 - name: Critical configuration
13 template:
14 src: critical.conf.j2
15 dest: /etc/app/critical.conf
16
17# Maximum failure percentage
18- name: Update with max failures
19 hosts: webservers
20 max_fail_percentage: 20 # Abort if >20% of hosts fail
21
22 tasks:
23 - name: Update application
24 command: /usr/bin/update.sh<h3>Tags, Includes, and Imports</h3>
<h4>Tags for Selective Execution:</h4>
1---
2- name: Complete server setup
3 hosts: all
4
5 tasks:
6 - name: Install base packages
7 apt:
8 name: "{{ item }}"
9 state: present
10 loop:
11 - vim
12 - git
13 - curl
14 tags:
15 - packages
16 - base
17
18 - name: Configure firewall
19 ufw:
20 rule: allow
21 port: "{{ item }}"
22 loop:
23 - 22
24 - 80
25 - 443
26 tags:
27 - security
28 - firewall
29
30 - name: Setup monitoring
31 include_tasks: monitoring.yml
32 tags:
33 - monitoring
34
35 - name: Always run this
36 debug:
37 msg: "This always runs"
38 tags:
39 - always
40
41# Run specific tags
42ansible-playbook site.yml --tags "packages,security"
43
44# Skip specific tags
45ansible-playbook site.yml --skip-tags "monitoring"
46
47# List available tags
48ansible-playbook site.yml --list-tags<h4>Include vs Import:</h4>
1# include_tasks: Dynamic (evaluated at runtime)
2- name: Include tasks dynamically
3 include_tasks: "{{ ansible_distribution }}.yml"
4 tags: install
5
6# import_tasks: Static (evaluated at parse time)
7- name: Import tasks statically
8 import_tasks: common.yml
9 tags: common
10
11# include_role: Dynamic role include
12- name: Include role dynamically
13 include_role:
14 name: nginx
15 when: install_nginx | bool
16
17# import_role: Static role import
18- name: Import role statically
19 import_role:
20 name: common
21
22# import_playbook: Include entire playbook
23- import_playbook: database.yml
24- import_playbook: webserver.yml<h4>Task Delegation:</h4>
1# Run task on different host
2- name: Update load balancer
3 command: /usr/local/bin/update-lb.sh
4 delegate_to: loadbalancer.example.com
5
6# Run locally
7- name: Run local command
8 command: /usr/local/bin/backup.sh
9 delegate_to: localhost
10
11# Run once for all hosts
12- name: Database migration
13 command: /opt/app/migrate.sh
14 run_once: true
15 delegate_to: "{{ groups['databases'][0] }}"
16
17# Delegate facts gathering
18- name: Gather facts from remote host
19 setup:
20 delegate_to: "{{ item }}"
21 delegate_facts: true
22 loop: "{{ groups['databases'] }}"<h4>Serial Execution:</h4>
1# Rolling update
2- name: Rolling deployment
3 hosts: webservers
4 serial: 2 # Update 2 hosts at a time
5
6 tasks:
7 - name: Remove from load balancer
8 command: /usr/local/bin/lb-remove.sh {{ inventory_hostname }}
9 delegate_to: loadbalancer
10
11 - name: Deploy new version
12 copy:
13 src: /builds/app-latest.tar.gz
14 dest: /opt/app/
15
16 - name: Restart application
17 service:
18 name: myapp
19 state: restarted
20
21 - name: Wait for application
22 wait_for:
23 port: 8080
24 timeout: 60
25
26 - name: Add back to load balancer
27 command: /usr/local/bin/lb-add.sh {{ inventory_hostname }}
28 delegate_to: loadbalancer
29
30# Percentage-based serial
31- name: Gradual rollout
32 hosts: webservers
33 serial:
34 - 1 # First 1 host
35 - 25% # Then 25% of remaining
36 - 100% # Then all remaining<h4>Pre and Post Tasks:</h4>
1---
2- name: Application deployment
3 hosts: appservers
4
5 pre_tasks:
6 - name: Check prerequisites
7 assert:
8 that:
9 - ansible_memtotal_mb >= 2048
10
11 - name: Take pre-deployment snapshot
12 command: /usr/local/bin/snapshot.sh
13
14 tasks:
15 - name: Deploy application
16 copy:
17 src: /builds/app.tar.gz
18 dest: /opt/app/
19
20 post_tasks:
21 - name: Verify deployment
22 uri:
23 url: http://localhost:8080/health
24 status_code: 200
25
26 - name: Send notification
27 slack:
28 msg: "Deployment complete on {{ inventory_hostname }}"<h4>Environment Variables:</h4>
1# Set environment variables for tasks
2- name: Run with custom environment
3 command: /usr/local/bin/script.sh
4 environment:
5 PATH: "/usr/local/bin:{{ ansible_env.PATH }}"
6 DATABASE_URL: "postgres://{{ db_host }}/{{ db_name }}"
7 API_KEY: "{{ api_key }}"
8
9# Play-level environment
10- name: Configure with environment
11 hosts: appservers
12 environment:
13 http_proxy: http://proxy.example.com:8080
14 https_proxy: http://proxy.example.com:8080
15
16 tasks:
17 - name: Download file (uses proxy)
18 get_url:
19 url: https://example.com/file.tar.gz
20 dest: /tmp/file.tar.gz<h3>Writing Maintainable Playbooks</h3>
<h4>Directory Structure:</h4>
1ansible-project/
2├── ansible.cfg # Ansible configuration
3├── inventory/
4│ ├── production/
5│ │ ├── hosts # Production inventory
6│ │ ├── group_vars/
7│ │ │ ├── all.yml
8│ │ │ ├── webservers.yml
9│ │ │ └── databases.yml
10│ │ └── host_vars/
11│ │ └── web1.example.com.yml
12│ └── staging/
13│ ├── hosts
14│ └── group_vars/
15├── playbooks/
16│ ├── site.yml # Master playbook
17│ ├── webservers.yml
18│ ├── databases.yml
19│ └── deploy.yml
20├── roles/
21│ ├── common/
22│ ├── nginx/
23│ └── postgresql/
24├── group_vars/
25│ └── all.yml
26├── host_vars/
27├── files/ # Static files
28├── templates/ # Jinja2 templates
29├── vars/ # Variable files
30│ ├── common.yml
31│ └── secrets.yml.enc # Encrypted with ansible-vault
32└── library/ # Custom modules<h4>Naming Conventions:</h4>
1# Use descriptive names
2# Bad
3- name: task1
4 apt:
5 name: nginx
6
7# Good
8- name: Install nginx web server
9 apt:
10 name: nginx
11 state: present
12
13# Playbook names
14site.yml # Master playbook
15webservers.yml # Web server configuration
16deploy-app.yml # Application deployment
17database-backup.yml # Database backup
18
19# Variable names
20http_port # Not port
21nginx_worker_processes # Not workers
22mysql_max_connections # Not max_conn<h4>Idempotency:</h4>
1# Always idempotent - can run multiple times safely
2# Good
3- name: Ensure nginx is installed
4 apt:
5 name: nginx
6 state: present # Installs if missing, skips if present
7
8- name: Ensure line is in file
9 lineinfile:
10 path: /etc/hosts
11 line: "192.168.1.10 server.local"
12 state: present # Adds if missing, skips if present
13
14# Not idempotent - avoid
15# Bad
16- name: Add line to file
17 shell: echo "192.168.1.10 server.local" >> /etc/hosts
18 # Adds line every time!
19
20# Make shell commands idempotent
21# Good
22- name: Add line to file idempotently
23 shell: grep -q "192.168.1.10" /etc/hosts || echo "192.168.1.10 server.local" >> /etc/hosts
24 args:
25 creates: /etc/hosts # Or use creates/removes
26 changed_when: false # Or define when it changes<h4>Security Best Practices:</h4>
1# Use Ansible Vault for secrets
2# Encrypt file
3ansible-vault encrypt secrets.yml
4
5# Encrypted vars file
6---
7database_password: !vault |
8 $ANSIBLE_VAULT;1.1;AES256
9 66386439653634...
10
11# Use in playbook
12- name: Configure database
13 hosts: databases
14 vars_files:
15 - secrets.yml
16 tasks:
17 - name: Create database user
18 postgresql_user:
19 name: appuser
20 password: "{{ database_password }}"
21
22# Run with vault password
23ansible-playbook site.yml --ask-vault-pass
24
25# Use password file
26ansible-playbook site.yml --vault-password-file ~/.vault_pass
27
28# No log for sensitive tasks
29- name: Set password
30 user:
31 name: john
32 password: "{{ user_password }}"
33 no_log: true # Don't log output
34
35# Use environment variables for secrets
36- name: Use API key from environment
37 uri:
38 url: https://api.example.com/data
39 headers:
40 Authorization: "Bearer {{ lookup('env', 'API_KEY') }}"
41 no_log: true<h4>Error Prevention:</h4>
1# Check mode support
2- name: Ensure config is valid
3 template:
4 src: nginx.conf.j2
5 dest: /etc/nginx/nginx.conf
6 validate: 'nginx -t -c %s' # Validate before deploying
7
8# Pre-deployment checks
9- name: Verify prerequisites
10 assert:
11 that:
12 - ansible_memtotal_mb >= 2048
13 - ansible_processor_vcpus >= 2
14 - disk_free_gb | int > 10
15 fail_msg: "System requirements not met"
16
17# Dry-run capability
18- name: Show what would change
19 template:
20 src: config.j2
21 dest: /etc/app/config.conf
22 check_mode: yes
23 diff: yes
24 register: config_changes
25
26- name: Ask for confirmation
27 pause:
28 prompt: "Continue with deployment?"
29 when: config_changes.changed<h4>Performance Optimization:</h4>
1# Disable fact gathering when not needed
2- name: Quick task
3 hosts: all
4 gather_facts: no # Skip fact gathering
5
6 tasks:
7 - name: Restart service
8 service:
9 name: nginx
10 state: restarted
11
12# Gather only required facts
13- name: Minimal facts
14 hosts: all
15 gather_facts: yes
16 gather_subset:
17 - '!all'
18 - '!min'
19 - network # Only network facts
20
21# Use async for long-running tasks
22- name: Long running task
23 command: /usr/bin/long-task
24 async: 3600 # Max time
25 poll: 0 # Fire and forget
26
27# Check status later
28- name: Check task status
29 async_status:
30 jid: "{{ long_task.ansible_job_id }}"
31 register: job_result
32 until: job_result.finished
33 retries: 30
34 delay: 10
35
36# Efficient loops
37# Bad - Multiple task executions
38- name: Install packages (slow)
39 apt:
40 name: "{{ item }}"
41 loop:
42 - vim
43 - git
44 - curl
45
46# Good - Single task execution
47- name: Install packages (fast)
48 apt:
49 name:
50 - vim
51 - git
52 - curl<h4>Documentation:</h4>
1---
2# webservers.yml - Configure nginx web servers
3#
4# This playbook installs and configures nginx on web servers
5# with SSL support and custom configurations.
6#
7# Requirements:
8# - Ubuntu 20.04+
9# - Sudo access
10# - SSL certificates in files/ssl/
11#
12# Variables:
13# http_port: HTTP port (default: 80)
14# https_port: HTTPS port (default: 443)
15# server_name: Server name for vhost
16# enable_ssl: Enable SSL (default: true)
17#
18# Usage:
19# ansible-playbook webservers.yml
20# ansible-playbook webservers.yml -e "enable_ssl=false"
21
22- name: Configure nginx web servers
23 hosts: webservers
24 become: yes
25
26 vars:
27 http_port: 80
28 https_port: 443
29 enable_ssl: true
30
31 tasks:
32 - name: Install nginx
33 apt:
34 name: nginx
35 state: present
36 tags: [packages, nginx]
37
38 # ... more tasks with commentsComplete Multi-Tier Application Deployment
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Ansible Configuration Management Demo</title>
7 <style>
8 * {
9 margin: 0;
10 padding: 0;
11 box-sizing: border-box;
12 }
13
14 body {
15 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17 min-height: 100vh;
18 padding: 20px;
19 }
20
21 .container {
22 max-width: 1400px;
23 margin: 0 auto;
24 }
25
26 h1 {
27 text-align: center;
28 color: white;
29 margin-bottom: 10px;
30 font-size: 2.5em;
31 text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
32 }
33
34 .subtitle {
35 text-align: center;
36 color: rgba(255,255,255,0.9);
37 margin-bottom: 30px;
38 font-size: 1.1em;
39 }
40
41 .playbook-viewer {
42 background: #1e1e1e;
43 border-radius: 10px;
44 box-shadow: 0 10px 30px rgba(0,0,0,0.3);
45 margin-bottom: 30px;
46 overflow: hidden;
47 }
48
49 .playbook-header {
50 background: #323232;
51 padding: 15px 20px;
52 color: #d4d4d4;
53 font-weight: bold;
54 display: flex;
55 align-items: center;
56 gap: 10px;
57 }
58
59 .playbook-icon {
60 width: 20px;
61 height: 20px;
62 background: #4ec9b0;
63 border-radius: 3px;
64 }
65
66 .playbook-content {
67 padding: 20px;
68 color: #d4d4d4;
69 font-family: 'Courier New', monospace;
70 font-size: 0.9em;
71 overflow-x: auto;
72 max-height: 500px;
73 overflow-y: auto;
74 }
75
76 .yaml-key {
77 color: #9cdcfe;
78 }
79
80 .yaml-value {
81 color: #ce9178;
82 }
83
84 .yaml-comment {
85 color: #6a9955;
86 font-style: italic;
87 }
88
89 .features-grid {
90 display: grid;
91 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
92 gap: 20px;
93 margin-bottom: 30px;
94 }
95
96 .feature-card {
97 background: white;
98 border-radius: 10px;
99 padding: 25px;
100 box-shadow: 0 10px 30px rgba(0,0,0,0.2);
101 transition: transform 0.3s ease;
102 }
103
104 .feature-card:hover {
105 transform: translateY(-5px);
106 }
107
108 .feature-icon {
109 font-size: 2.5em;
110 margin-bottom: 15px;
111 }
112
113 .feature-title {
114 color: #667eea;
115 font-size: 1.3em;
116 font-weight: bold;
117 margin-bottom: 10px;
118 }
119
120 .feature-description {
121 color: #666;
122 line-height: 1.6;
123 }
124
125 .example-code {
126 background: #2d2d2d;
127 border-radius: 8px;
128 padding: 20px;
129 margin: 10px 0;
130 overflow-x: auto;
131 }
132
133 .execution-flow {
134 background: white;
135 border-radius: 10px;
136 padding: 30px;
137 box-shadow: 0 10px 30px rgba(0,0,0,0.2);
138 margin-bottom: 30px;
139 }
140
141 .flow-step {
142 display: flex;
143 align-items: center;
144 margin: 20px 0;
145 padding: 15px;
146 background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
147 border-radius: 8px;
148 border-left: 4px solid #667eea;
149 }
150
151 .step-number {
152 background: #667eea;
153 color: white;
154 width: 40px;
155 height: 40px;
156 border-radius: 50%;
157 display: flex;
158 align-items: center;
159 justify-content: center;
160 font-weight: bold;
161 margin-right: 20px;
162 flex-shrink: 0;
163 }
164
165 .step-content {
166 flex: 1;
167 }
168
169 .step-title {
170 font-weight: bold;
171 color: #333;
172 margin-bottom: 5px;
173 }
174
175 .step-description {
176 color: #666;
177 font-size: 0.95em;
178 }
179
180 .comparison-table {
181 background: white;
182 border-radius: 10px;
183 padding: 30px;
184 box-shadow: 0 10px 30px rgba(0,0,0,0.2);
185 margin-bottom: 30px;
186 overflow-x: auto;
187 }
188
189 table {
190 width: 100%;
191 border-collapse: collapse;
192 }
193
194 th {
195 background: #667eea;
196 color: white;
197 padding: 15px;
198 text-align: left;
199 font-weight: bold;
200 }
201
202 td {
203 padding: 15px;
204 border-bottom: 1px solid #e0e0e0;
205 color: #333;
206 }
207
208 tr:hover {
209 background: #f5f7fa;
210 }
211
212 .check {
213 color: #27c93f;
214 font-weight: bold;
215 }
216
217 .cross {
218 color: #ff5f56;
219 font-weight: bold;
220 }
221
222 @media (max-width: 768px) {
223 h1 {
224 font-size: 1.8em;
225 }
226
227 .features-grid {
228 grid-template-columns: 1fr;
229 }
230 }
231 </style>
232</head>
233<body>
234 <div class="container">
235 <h1>📘 Ansible Configuration Management</h1>
236 <p class="subtitle">Playbooks, Variables, Templates & Advanced Automation</p>
237
238 <div class="features-grid">
239 <div class="feature-card">
240 <div class="feature-icon">📝</div>
241 <div class="feature-title">Playbooks</div>
242 <div class="feature-description">
243 YAML-based automation scripts that define tasks, plays, and workflows for
244 orchestrating complex deployments across multiple hosts.
245 </div>
246 </div>
247
248 <div class="feature-card">
249 <div class="feature-icon">🔧</div>
250 <div class="feature-title">Variables</div>
251 <div class="feature-description">
252 Flexible variable system with precedence rules, allowing dynamic configurations
253 across different environments and host groups.
254 </div>
255 </div>
256
257 <div class="feature-card">
258 <div class="feature-icon">📋</div>
259 <div class="feature-title">Templates</div>
260 <div class="feature-description">
261 Jinja2 templating engine for creating dynamic configuration files with
262 variables, loops, and conditionals.
263 </div>
264 </div>
265
266 <div class="feature-card">
267 <div class="feature-icon">🔔</div>
268 <div class="feature-title">Handlers</div>
269 <div class="feature-description">
270 Event-driven tasks that execute only when notified, perfect for restarting
271 services after configuration changes.
272 </div>
273 </div>
274
275 <div class="feature-card">
276 <div class="feature-icon">🔄</div>
277 <div class="feature-title">Loops & Conditionals</div>
278 <div class="feature-description">
279 Advanced flow control with loops, conditionals, and blocks for complex
280 automation logic.
281 </div>
282 </div>
283
284 <div class="feature-card">
285 <div class="feature-icon">🛡️</div>
286 <div class="feature-title">Error Handling</div>
287 <div class="feature-description">
288 Robust error handling with rescue blocks, always blocks, and custom
289 failure conditions.
290 </div>
291 </div>
292 </div>
293
294 <div class="playbook-viewer">
295 <div class="playbook-header">
296 <div class="playbook-icon"></div>
297 <span>Complete Application Deployment Playbook</span>
298 </div>
299 <div class="playbook-content">
300<pre><span class="yaml-comment">---
301# site.yml - Multi-tier application deployment</span>
302
303<span class="yaml-key">- name:</span> <span class="yaml-value">Configure Web Servers</span>
304 <span class="yaml-key">hosts:</span> webservers
305 <span class="yaml-key">become:</span> yes
306 <span class="yaml-key">vars:</span>
307 <span class="yaml-key">http_port:</span> 80
308 <span class="yaml-key">server_name:</span> <span class="yaml-value">example.com</span>
309
310 <span class="yaml-key">tasks:</span>
311 - <span class="yaml-key">name:</span> <span class="yaml-value">Install nginx</span>
312 <span class="yaml-key">apt:</span>
313 <span class="yaml-key">name:</span> nginx
314 <span class="yaml-key">state:</span> present
315 <span class="yaml-key">notify:</span> Restart nginx
316
317 - <span class="yaml-key">name:</span> <span class="yaml-value">Deploy nginx configuration</span>
318 <span class="yaml-key">template:</span>
319 <span class="yaml-key">src:</span> templates/nginx.conf.j2
320 <span class="yaml-key">dest:</span> /etc/nginx/sites-available/{{ server_name }}
321 <span class="yaml-key">notify:</span> Reload nginx
322
323 - <span class="yaml-key">name:</span> <span class="yaml-value">Start and enable nginx</span>
324 <span class="yaml-key">service:</span>
325 <span class="yaml-key">name:</span> nginx
326 <span class="yaml-key">state:</span> started
327 <span class="yaml-key">enabled:</span> yes
328
329 <span class="yaml-key">handlers:</span>
330 - <span class="yaml-key">name:</span> <span class="yaml-value">Restart nginx</span>
331 <span class="yaml-key">service:</span>
332 <span class="yaml-key">name:</span> nginx
333 <span class="yaml-key">state:</span> restarted
334
335 - <span class="yaml-key">name:</span> <span class="yaml-value">Reload nginx</span>
336 <span class="yaml-key">service:</span>
337 <span class="yaml-key">name:</span> nginx
338 <span class="yaml-key">state:</span> reloaded
339
340<span class="yaml-key">- name:</span> <span class="yaml-value">Configure Database Servers</span>
341 <span class="yaml-key">hosts:</span> databases
342 <span class="yaml-key">become:</span> yes
343
344 <span class="yaml-key">tasks:</span>
345 - <span class="yaml-key">name:</span> <span class="yaml-value">Install PostgreSQL</span>
346 <span class="yaml-key">apt:</span>
347 <span class="yaml-key">name:</span>
348 - postgresql
349 - postgresql-contrib
350 <span class="yaml-key">state:</span> present
351
352 - <span class="yaml-key">name:</span> <span class="yaml-value">Start PostgreSQL</span>
353 <span class="yaml-key">service:</span>
354 <span class="yaml-key">name:</span> postgresql
355 <span class="yaml-key">state:</span> started
356 <span class="yaml-key">enabled:</span> yes</pre>
357 </div>
358 </div>
359
360 <div class="execution-flow">
361 <h2 style="color: #667eea; margin-bottom: 20px;">Playbook Execution Flow</h2>
362
363 <div class="flow-step">
364 <div class="step-number">1</div>
365 <div class="step-content">
366 <div class="step-title">Parse Playbook</div>
367 <div class="step-description">Ansible reads and validates YAML syntax</div>
368 </div>
369 </div>
370
371 <div class="flow-step">
372 <div class="step-number">2</div>
373 <div class="step-content">
374 <div class="step-title">Gather Facts</div>
375 <div class="step-description">Collect system information from target hosts</div>
376 </div>
377 </div>
378
379 <div class="flow-step">
380 <div class="step-number">3</div>
381 <div class="step-content">
382 <div class="step-title">Execute Tasks</div>
383 <div class="step-description">Run each task in order, calling modules</div>
384 </div>
385 </div>
386
387 <div class="flow-step">
388 <div class="step-number">4</div>
389 <div class="step-content">
390 <div class="step-title">Notify Handlers</div>
391 <div class="step-description">Track which handlers need to run</div>
392 </div>
393 </div>
394
395 <div class="flow-step">
396 <div class="step-number">5</div>
397 <div class="step-content">
398 <div class="step-title">Run Handlers</div>
399 <div class="step-description">Execute notified handlers at end of play</div>
400 </div>
401 </div>
402
403 <div class="flow-step">
404 <div class="step-number">6</div>
405 <div class="step-content">
406 <div class="step-title">Report Results</div>
407 <div class="step-description">Display summary of ok, changed, failed tasks</div>
408 </div>
409 </div>
410 </div>
411
412 <div class="comparison-table">
413 <h2 style="color: #667eea; margin-bottom: 20px;">Ad-Hoc vs Playbooks</h2>
414 <table>
415 <thead>
416 <tr>
417 <th>Feature</th>
418 <th>Ad-Hoc Commands</th>
419 <th>Playbooks</th>
420 </tr>
421 </thead>
422 <tbody>
423 <tr>
424 <td><strong>Syntax</strong></td>
425 <td>Command line</td>
426 <td>YAML files</td>
427 </tr>
428 <tr>
429 <td><strong>Reusability</strong></td>
430 <td class="cross">Limited</td>
431 <td class="check">High</td>
432 </tr>
433 <tr>
434 <td><strong>Version Control</strong></td>
435 <td class="cross">Difficult</td>
436 <td class="check">Easy</td>
437 </tr>
438 <tr>
439 <td><strong>Complex Logic</strong></td>
440 <td class="cross">Not supported</td>
441 <td class="check">Full support</td>
442 </tr>
443 <tr>
444 <td><strong>Error Handling</strong></td>
445 <td class="cross">Basic</td>
446 <td class="check">Advanced</td>
447 </tr>
448 <tr>
449 <td><strong>Orchestration</strong></td>
450 <td class="cross">Single task</td>
451 <td class="check">Multi-step</td>
452 </tr>
453 <tr>
454 <td><strong>Best For</strong></td>
455 <td>Quick tasks, testing</td>
456 <td>Production deployments</td>
457 </tr>
458 </tbody>
459 </table>
460 </div>
461 </div>
462</body>
463</html>Your progress is saved in this browser, on this device only.