Ansible Troubleshooting Lab

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.

Goals

Lab Setup

  1. Ensure you are in the ciq-basics directory

  2. Create a folder for this lab, let’s call it lab06

You are now ready to start the lab.

Troubleshooting Guidelines

When debugging these playbooks, look for common error patterns:

Syntax Errors:

Module Parameter Errors:

Logic Errors:

Template Errors:

File and Permission Issues:

Template Configuration Playbook

This playbook demonstrates template usage for web server configuration. Templates allow dynamic configuration files based on variables and facts.

  1. First, create a template file nginx.conf.j2:
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;
}
  1. Create the main playbook template_config.yml:
- 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: restarted

Expected 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]

Service Management Playbook

This playbook demonstrates complete service lifecycle management - installation, configuration, startup, shutdown, and removal.

  1. Create the playbook service_lifecycle.yml:
- 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]

User Management Playbook

This playbook demonstrates comprehensive user and group management including creation, configuration, and cleanup.

  1. Create the playbook user_management.yml:
- 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'})

Real-World Troubleshooting Tips