This is 2nd Exercise of Ansible and in this exercise we will explain real time example based on Ansible loop and Handlers.
Table of Contents
Outcomes:
You should be able to define conditionals in Ansible Playbooks, set up loops that iterate over elements, define handlers in playbooks, and handle task errors.
To SetUp Ansible control machine and hosts machine please refer the previous real time project article Ansible Real-Time Project for Beginners. Once you compete Lab setup please proceed further.
Vars File:
min_ram_mb: 256
ssl_cert_dir: “/etc/ssl/certs”
ssl_key_dir: “/etc/ssl/private”
domain_name: “vm2.example.com”
packages:
– apache2
– mariadb-server
– php
services:
– apache2
web_config_files:
– src: “/home/vagrant/ansible_lab/vm2.example.com.crt”
dest: “/etc/ssl/certs/”
– src: “/home/vagrant/ansible_lab/vm2.example.com.key”
dest: “/etc/ssl/private”
Steps to Complete This Ansible Real Time Project:
The Ansible Real Time Project directory contains a partially completed playbook, playbook.yml. Using a text editor, add a task that uses the fail module under the #Fail Fast Message comment. Be sure to provide an appropriate name for the task. This task should only be executed when the remote system does not meet the minimum requirements.
The minimum requirements for the remote host are listed below:
• Has at least the amount of RAM specified by the min_ram_mb variable. The min_ram_mb variable is defined in the vars.yml file and has a value of 256.
• Is running Ubuntu running on Remote machine.
The completed task matches:
tasks:
# Fail Fasts Message
– name: Show Failed System Requirements Message
fail:
msg: “The {{ inventory_hostname }} did not meet minimum reqs.”
when: >
ansible_memtotal_mb < min_ram_mb or
ansible_distribution != “Ubuntu”
Add a single task to the playbook of Ansible Real Time Project under the #Install all Packages comment to install the latest version of any missing packages. Required packages are specified by the packages variable, which is defined in the vars.yml file. The task name should be Ensure required packages are present.
The Complete task matches:
– name: Ensure required packages are present
apt:
name: “{{ packages }}”
state: latest
Add a single task to the playbook of Ansible Real Time Project under the #Enable and start services comment to start all services. All services specified by the services variable, which is defined in the vars.yml file, should be started and enabled. Be sure to provide an appropriate name for the task.
The Complete task matches:
– name: Ensure services are started and enabled
service:
name: “{{ item }}”
state: started
enabled: yes
loop: “{{ services }}
Add a task block to the playbook of Ansible Real Time Project under the #Block of config tasks comment. This block contains three tasks:
• A task to create a private key on Local Machine
• A task to Create a CSR on Local Machine
• A Tasks to Create Generate Self Sign Certificates on Local hosts
The Complete Task Matches#Block For Self sign CRT generation.
– name: Setting up the SSL cert directory and config files
delegate_to: localhost
block:
– name: Create Priavte Key
community.crypto.openssl_privatekey:
path: “{{ domain_name }}.key”
size: 2048
– name: Create CSR (Certificate Signing Request)
community.crypto.openssl_csr:
path: “{{ domain_name }}.csr”
privatekey_path: “{{ domain_name }}.key”
common_name: “{{ domain_name }}”
– name: Generate Self-Signed Certificate
community.crypto.x509_certificate:
path: “{{ domain_name }}.crt”
privatekey_path: “./{{ domain_name }}.key”
csr_path: “{{ domain_name }}.csr”
provider: selfsigned
Add another task block to the playbook of Ansible Real Time Project under the # Block To Deploy Self sign CRT on Remote host.
This block contains two tasks:
• A task to ensure the directory specified by the ssl_cert_dir variable exists on the remote host. This directory stores the web server’s certificates.
• A task to copy all files specified by the web_config_files variable to the remote host. Examine the structure of the web_config_files variable in the vars.yml file. Configure the task to copy each file to the correct destination on the remote host.
Additionally, a debug task is executed if either of the two tasks above fail. In this case, the task prints the message: One or more of the configuration changes failed, but the web service is still active
The complete task matches:
# Block to Deploy Self sign Certificate.
– name: Deploy to Remote Host (vm2)
block:
– name: Ensure remote directories exist
file:
path: “{{ item }}”
state: directory
mode: ‘0755’
loop:
– “{{ ssl_cert_dir }}”
– “{{ ssl_key_dir }}”
– name: Copy Web Configuration files to vm2
copy:
src: “{{ item.src }}”
dest: “{{ item.dest}}”
mode: ‘0644’
loop: “{{ web_config_files }}”
rescue:
– name: Configuration Error Message
debug:
msg: >
One or more of the configuration
changes failed, but the web service
is still active.
The playbook configures the remote host to listen for standard HTTP, HTTPS and SSH requests. Add a single task to the playbook under the #Configure the firewall comment to configure ufw. This task should ensure that the remote host allows standard HTTP, SSH and HTTPS connections. These configuration changes should be effective immediately and persist after a system reboot. Be sure to provide an appropriate name for the task.
The complete task matches:
#Configure the firewall to configure ufw.
– name: Ensure web server ports are open (UFW)
ufw:
rule: allow
port: “{{ item }}”
proto: tcp
loop:
– 22
– 80
– 443
– name: Enable Firewall
ufw:
state: enabled
- Add two tasks in playbook one is for copy index.php template to remote Machine and another is for Remove html.index file from Control node to Remote host and also add notify to add handlers in next task.
The Complet task matches:– name: copy index.php from local to remote machine
copy:
src: index.php
dest: /var/www/html/index.php
owner: www-data
group: www-data
mode: ‘0644’
notify: restart web service
– name: Remove html.index file from Control node to Remote host
file:
path: /var/www/html/index.html
state: absent
- Define the restart web service handler. When triggered, this task should restart the web service defined by the web_service variable, defined in the vars.yml file.
The complete task matches:
handlers:
– name: restart web service
service:
name: apache2
state: restarted
From the project directory, ~/control-review, run the playbook.yml playbook. The playbook should execute without errors, and trigger the execution of the handler task.
Verify that the web server now responds to HTTPS requests, using the self-signed custom certificate to encrypt the connection. The web server response should match the string Configured for both HTTP and HTTPS.
The complete Playbook Contains:
—
– name: Complete lab on loop handlers and error control
hosts: remote_hosts
vars_files:
– vars/vars.yml
tasks:
# Fail Fasts Message
– name: Show Failed System Requirements Message
fail:
msg: “The {{ inventory_hostname }} did not meet minimum reqs.”
when: >
ansible_memtotal_mb < min_ram_mb or
ansible_distribution != “Ubuntu”
– name: Ensure required packages are present
apt:
name: “{{ packages }}”
state: latest
– name: Ensure services are started and enabled
service:
name: “{{ item }}”
state: started
enabled: yes
loop: “{{ services }}”
#Block of config tasks
– name: Setting up the SSL cert directory and config files
delegate_to: localhost
block:
– name: Create Priavte Key
community.crypto.openssl_privatekey:
path: “{{ domain_name }}.key”
size: 2048
– name: Create CSR (Certificate Signing Request)
community.crypto.openssl_csr:
path: “{{ domain_name }}.csr”
privatekey_path: “{{ domain_name }}.key”
common_name: “{{ domain_name }}”
– name: Generate Self-Signed Certificate
community.crypto.x509_certificate:
path: “{{ domain_name }}.crt”
privatekey_path: “./{{ domain_name }}.key”
csr_path: “{{ domain_name }}.csr”
provider: selfsigned
– name: Deploy to Remote Host (vm2)
block:
– name: Ensure remote directories exist
file:
path: “{{ item }}”
state: directory
mode: ‘0755’
loop:
– “{{ ssl_cert_dir }}”
– “{{ ssl_key_dir }}”
– name: Copy Web Configuration files to vm2
copy:
src: “{{ item.src }}”
dest: “{{ item.dest}}”
mode: ‘0644’
loop: “{{ web_config_files }}”
rescue:
– name: Configuration Error Message
debug:
msg: >
One or more of the configuration
changes failed, but the web service
is still active.
– name: Ensure web server ports are open (UFW)
ufw:
rule: allow
port: “{{ item }}”
proto: tcp
loop:
– 22
– 80
– 443
– name: Enable Firewall
ufw:
state: enabled
– name: copy index.php from local to remote machine
copy:
src: index.php
dest: /var/www/html/index.php
owner: www-data
group: www-data
mode: ‘0644’
notify: restart web service
– name: Remove html.index file from Control node to Remote hostfile:
path: /var/www/html/index.html
state: absent
handlers:
– name: restart web service
service:
name: apache2
state: restarted
Conclusion: Mastering the Ansible Real Time Project
This Ansible Real Time project demonstrated how loops, handlers can be used together to build efficient and production-ready automation. By leveraging loops, we reduced repetitive code, while handlers ensured services were restarted only when configuration changes occurred. The use of blocks, rescue, and fail-fast checks helped make the playbook more reliable and easier to troubleshoot.
This project is an excellent starting point for beginners to understand real-world Ansible automation, and it lays a strong foundation for managing services, security, and configurations in larger infrastructure environments.
