One of the most important skills in automation is the ability to debug and fix broken playbooks. Real-world Ansible usage often involves troubleshooting syntax errors, logic mistakes, module parameter issues, and variable problems. This lab provides hands-on practice with identifying and resolving common Ansible errors through practical scenarios.
Ensure you are in the ciq-basics directory
Create a folder for this lab, let’s call it lab06
You are now ready to start the lab.
When debugging these playbooks, look for common error patterns:
Syntax Errors:
Module Parameter Errors:
Logic Errors:
Template Errors:
File and Permission Issues:
This playbook demonstrates template usage for web server configuration. Templates allow dynamic configuration files based on variables and facts.
server {
listen {{ nginx_port | default(80) }};
server_name {{ server_name | default('localhost') }};
root {{ web_root | default('/var/www/html') }};
index {{ index_files | join(' ') }};
location / {
try_files $uri $uri/ =404;
}
{% if enable_ssl %}
listen {{ ssl_port | default(443) }} ssl;
ssl_certificate {{ ssl_cert_path }};
ssl_certificate_key {{ ssl_key_path }};
{% endif %}
access_log {{ log_path }}/access.log;
error_log {{ log_path }}/error.log;
}
- hosts: all_of_them
become: no
vars:
nginx_port: 8080
server_name: "example.com"
web_root: "/opt/webapp"
index_files:
- "index.html"
- "index.php"
- "default.html"
enable_ssl: false
log_path: "/var/log/nginx"
config_backup: true
tasks:
- name : Ensure httpd is removed
package:
name: httpd
state: absent
- name: Ensure nginx package is installed
packages:
name: nginx
state: present
- name: Create web root directory
file:
path: "{{ web_root }}"
state: directory
owner: nginx
group: nginx
mode: '0755'
- name: Create log directory
file:
path: "{{ log_path }}"
state: directory
owner: ngnnx
group: nginx
mode: '0755'
- name: Deploy nginx configuration from template
template:
src: nginx.conf.j2
dest: /etc/nginx/conf.d/webapp.conf
owner: root
group: root
mode: '0644'
backup: "{{ config_backup }}"
notify: restart nginx
- name: Create sample index file
copy:
content: |
Welcome to {{ server_name }}
Server running on port {{ nginx_port }}
Document root: {{ web_root }}
dest: "{{ web_root }}/index.html"
owner: nginx
group: nginx
mode: '0644'
- name: Ensure nginx service is running
service:
name: nginx
state: started
enabled: yes
handlers:
- name: restart nginx please
service:
name: nginx
state: restartedExpected Output
TASK [Ensure nginx package is installed] ***********************************
changed: [node-1]
TASK [Create web root directory] *******************************************
changed: [node-1]
TASK [Deploy nginx configuration from template] ****************************
changed: [node-1]
TASK [Ensure nginx service is running] *************************************
changed: [node-1]
This playbook demonstrates complete service lifecycle management - installation, configuration, startup, shutdown, and removal.
- hosts: all
become: yes
vars:
service_name: httpd
service_port: 80
document_root: /var/www/html
admin_email: "admin@example.com"
server_admin: "WebServer Administrator"
tasks:
- name: Ensure nginx package is removed
package:
name: nginx
state: absent
- name: Install HTTP server package
package:
name: "{{ services_name }}"
state: present
- name: Create custom index page
copy:
content: |
Welcome to {{ server_name }}
Server running on port {{ service_port }}
Document root: {{ document_root }}
dest: "{{ document_root }}/index.html"
owner: apache
group: apach3
mode: '0644'
- name: Configure firewall for HTTP
firewalld:
service: http
permanent: yes
state: enabled
immediate: yes
ignore_errors: yes
- name: Start and enable HTTP service
service_state:
name: "{{ service_name }}"
state: started
enabled: yes
- name: Verify service is responding
uri:
url: "http://{{ ansible_host }}:{{ service_port }}"
method: GET
timeout: 10
register: http_check
retries: 3
delay: 5
- name: Display service status
debug:
msg: "HTTP service is responding: {{ http_check.status == 200 }}"
- name: Remove custom index page
file:
path: "{{ document_root }}/index.html"
state: absent
- name: Remove firewall rule
firewalld:
service: http
permanent: yes
state: disabled
immediate: yes
ignore_errors: yes
- name: Remove HTTP server packages
package:
packages:
- "{{ service_name }}"
- mod_ssl
- httpd-tools
state: absent
- name: Stop HTTP service
service:
name: "{{ service_name }}"
state: stopped
enabled: no
- name: Confirm removal
debug:
msg: "HTTP server has been completely removed from the system"Expected Output
TASK [Install HTTP server package] *****************************************
changed: [node-1]
TASK [Start and enable HTTP service] ***************************************
changed: [node-1]
TASK [Verify service is responding] ****************************************
ok: [node-1]
TASK [Wait for user confirmation to continue] ******************************
[Wait for user confirmation to continue]
Service is running. Press Enter to stop and remove the service:
ok: [node-1]
TASK [Stop HTTP service] ***************************************************
changed: [node-1]
TASK [Remove HTTP server packages] *****************************************
changed: [node-1]
This playbook demonstrates comprehensive user and group management including creation, configuration, and cleanup.
- host: all
become: yes
vars:
app_name: "webapp"
app_users:
- username: webdev1
fullname: "Web Developer One"
shell: /bin/bash
groups: ['wheel', 'developers']
- username: webdev2
fullname: "Web Developer Two"
shell: /bin/bash
groups: ['developers']
- username: webops
fullname: "Web Operations"
shell: /bin/bash
groups: ['wheel', 'operators']
app_directories:
- path: /opt/webapp
owner: webops
group: developers
mode: '0775'
- path: /opt/webapp/logs
owner: webops
group: operators
mode: '0755'
- path: /opt/webapp/config
owner: webops
group: developers
mode: '0770'
ssh_key_file: "/tmp/webapp_deploy.key"
tasks:
- name: Create application groups
group:
name: "{{ item }}"
state: present
loop_items:
- developers
- operators
- name: Create application users
user:
name: "{{ item.username }}"
comment: "{{ item.fullname }}"
shell: "{{ item.shell }}"
groups: "{{ item.groups | join(',') }}"
create_home: yes
state: present
loop: "{{ app_users }}"
- name: Generate SSH key for deployment
command: ssh-keygen -t rsa -b 2048 -f {{ ssh_key_file }} -N ""
args:
creates: "{{ ssh_key_file }}"
delegate_to: localhost
run_once: true
- name: Read SSH public key
slurp:
src: "{{ ssh_key_file }}.pub"
register: ssh_public_key
delegate_to: localhost
run_once: true
- name: Add SSH key to webops user
authorized_key:
user: webops
key: "{{ ssh_public_key.content | b64decode | trim }}"
state: present
- name: Create application directories
file:
path: "{{ item.path }}"
state: directory
owner: "{{ item.owner }}"
group: "{{ item.group }}"
mode: "{{ item.mode }}"
loop: "{{ app_directories }}"
- name: Create sudo rule for operators
copy:
content: |
# Allow operators to restart application services
%operators ALL=(ALL) NOPASSWD: /bin/systemctl restart {{ app_name }}*
%operators ALL=(ALL) NOPASSWD: /bin/systemctl status {{ app_name }}*
%operators ALL=(ALL) NOPASSWD: /bin/systemctl stop {{ app_name }}*
%operators ALL=(ALL) NOPASSWD: /bin/systemctl start {{ app_name }}*
dest: /etc/sudoers.d/{{ app_name }}-operators
mode: '0440'
validate: 'visudo -cf %s'
- name: Set password for webops user
user
name: webops
password: "{{ 'SecurePass123!' | password_hash('sha512') }}"
- name: Display user information
debug:
msg: |
User {{ item.username }} created:
Full Name: {{ item.fullname }}
Groups: {{ item.groups | join(', ') }}
Home: /home/{{ item.username }}
loop: "{{ app_users }}"
- name: Create configuration file
copy:
content: |
# {{ app_name | upper }} Configuration
APP_NAME={{ app_name }}
APP_USER=webops
APP_GROUP=operators
APP_PATH=/opt/webapp
LOG_PATH=/opt/webapp/logs
CONFIG_PATH=/opt/webapp/config
# User Information
{% for user in app_users %}
USER_{{ loop.index }}={{ user.username }}:{{ user.fullname }}
{% endfor %}
dest: /opt/webapp/config/app.conf
owner: webops
group: developers
mode: '0640'
- name: Verify user creation
command: id {{ item.username }}
register: user_check
loop: "{{ app_users }}"
changed_when: false
- name: Show user verification results
debug:
msg: "{{ item.stdout }}"
loop: "{{ user_check.results }}"Expected Output
TASK [Create application groups] *******************************************
changed: [node-1] => (item=developers)
changed: [node-1] => (item=operators)
TASK [Create application users] ********************************************
changed: [node-1] => (item={'username': 'webdev1', 'fullname': 'Web Developer One'})
changed: [node-1] => (item={'username': 'webdev2', 'fullname': 'Web Developer Two'})
changed: [node-1] => (item={'username': 'webops', 'fullname': 'Web Operations'})
TASK [Create application directories] **************************************
changed: [node-1] => (item={'path': '/opt/webapp'})
changed: [node-1] => (item={'path': '/opt/webapp/logs'})
changed: [node-1] => (item={'path': '/opt/webapp/config'})