Bookmark this page

Lab: Simplifying Playbooks with Roles and Ansible Content Collections

In this lab, you create Ansible roles that use variables, files, templates, tasks, and handlers.

Outcomes

  • Create Ansible roles that use variables, files, templates, tasks, and handlers to configure a development web server.

  • In a playbook, use a role that is hosted in a remote repository.

  • Use a system role for Red Hat Enterprise Linux in a playbook.

As the student user on the workstation machine, use the lab command to prepare your system for this exercise.

This command prepares your environment and ensures that all required resources are available.

[student@workstation ~]$ lab start role-review

Procedure 7.5. Instructions

Your organization must provide a single web server to host development code for all its web developers. You are tasked with writing a playbook to configure this development web server.

The development web server must satisfy several requirements:

  • The development server configuration matches the production server configuration. The production server is configured by using an Ansible role, developed by the organization's infrastructure team.

  • Each developer is given a directory on the development server to host code and content. Each developer's content is accessed by using an assigned, nonstandard port.

  • SELinux is set to enforcing mode and is using the targeted policy.

Your playbook must:

  • Use a role to configure directories and ports for each developer on the web server. You must write this role.

    This role has a dependency on a role to configure Apache that was written by your organization. You should define the dependency, specifying version v1.4 of the role. The URL of the dependency's Git repository is: git@workstation.lab.example.com:infra/apache

  • Use the rhel-system-roles.selinux role to configure SELinux to allow external hosts to access the nonstandard HTTP ports used by your web server. You are provided with an selinux.yml variable file that you can use in the group_vars directory to pass the correct settings to the role.

  1. Change into the /home/student/role-review directory.

    [student@workstation ~]$ cd ~/role-review
    [student@workstation role-review]$
  2. Use the ansible-galaxy collection install command to install the redhat.rhel_system_roles collection from the provided tar archive file, then verify that it was installed correctly.

    1. Use the ansible-galaxy collection install command to install the redhat.rhel_system_roles collection from the provided tar archive file.

      [student@workstation role-review]$ ansible-galaxy collection \
      > install -p collections/ redhat-rhel_system_roles-1.19.3.tar.gz
      Starting galaxy collection install process
      Process install dependency map
      Starting collection install process
      Installing 'redhat.rhel_system_roles:1.19.3' to '/home/student/role-review/collections/ansible_collections/redhat/rhel_system_roles'
      redhat.rhel_system_roles:1.19.3 was installed successfully
    2. Use the ansible-galaxy collection list command to verify that the collection is installed correctly and the system roles are now available.

      [student@workstation role-review]$ ansible-galaxy collection list
      
      # /home/student/role-system/collections/ansible_collections
      Collection               Version
      ------------------------ -------
      redhat.rhel_system_roles 1.19.3
      
      ...output omitted...
  3. Create a playbook named web_dev_server.yml with a single play named Configure Dev Web Server. Configure the play to target the dev_webserver host group. Do not add any roles or tasks to the play yet.

    Ensure that the play forces handlers to execute, because you might encounter an error when developing the playbook.

    The completed /home/student/role-review/web_dev_server.yml playbook contains the following content:

    ---
    - name: Configure Dev Web Server
      hosts: dev_webserver
      force_handlers: yes
  4. Verify the syntax of the playbook, then run the playbook. The syntax check should pass and the playbook should run successfully. The play only gathers facts because it has no roles or tasks yet.

    [student@workstation role-review]$ ansible-navigator run \
    > -m stdout web_dev_server.yml --syntax-check
    playbook: /home/student/role-review/web_dev_server.yml
    [student@workstation role-review]$ ansible-navigator run \
    > -m stdout web_dev_server.yml
    
    PLAY [Configure Dev Web Server] ************************************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [servera.lab.example.com]
    
    PLAY RECAP *********************************************************************
    servera.lab.example.com    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
  5. Make sure to install any roles that are dependencies of roles in your playbook.

    For example, the apache.developer_configs role that you write later in this lab depends on the infra.apache role.

    Create a roles/requirements.yml file. This file installs the role from the Git repository at git@workstation.lab.example.com:infra/apache, use version v1.4, and name it infra.apache locally.

    You can assume that your SSH keys are configured to allow you to get roles from this repository automatically. Install the role with the ansible-galaxy command.

    1. Create a roles subdirectory for the playbook project.

      [student@workstation role-review]$ mkdir -v roles
      mkdir: created directory 'roles'
    2. Create a roles/requirements.yml file and add an entry for the infra.apache role. Use version v1.4 from the role's git repository.

      The completed roles/requirements.yml file contains the following content:

      - name: infra.apache
        src: git@workstation.lab.example.com:infra/apache
        scm: git
        version: v1.4
    3. Install the project dependencies.

      [student@workstation role-review]$ ansible-galaxy install \
      > -r roles/requirements.yml -p roles
      Starting galaxy role install process
      - extracting infra.apache to /home/student/role-review/roles/infra.apache
      - infra.apache (v1.4) was installed successfully
  6. Initialize a new role named apache.developer_configs in the roles subdirectory.

    Add the infra.apache role as a dependency for the new role, using the same information for name, source, version, and version control system as the roles/requirements.yml file.

    The developer_tasks.yml file in the project directory contains tasks for the role. Move this file to the correct location in the role directory hierarchy for a tasks file that is used by this role, replacing the existing file in that location.

    The developer.conf.j2 file in the project directory is a Jinja2 template that is used by the tasks file. Move this file to the correct location for template files that are used by this role.

    1. Use the ansible-galaxy init command to create a role skeleton for the apache.developer_configs role.

      [student@workstation role-review]$ cd roles
      [student@workstation roles]$ ansible-galaxy init apache.developer_configs
      - Role apache.developer_configs was created successfully
      [student@workstation roles]$ cd ..
      [student@workstation role-review]$
    2. Modify the roles/apache.developer_configs/meta/main.yml file of the apache.developer_configs role to reflect a dependency on the infra.apache role.

      After editing, the dependencies variable is defined as follows:

      dependencies:
        - name: infra.apache
          src: git@workstation.lab.example.com:infra/apache
          scm: git
          version: v1.4
    3. Overwrite the role's tasks/main.yml file with the developer_tasks.yml file.

      [student@workstation role-review]$ mv -v developer_tasks.yml \
      > roles/apache.developer_configs/tasks/main.yml
      renamed 'developer_tasks.yml' -> 'roles/apache.developer_configs/tasks/main.yml'
    4. Place the developer.conf.j2 file in the role's templates directory.

      [student@workstation role-review]$ mv -v developer.conf.j2 \
      > roles/apache.developer_configs/templates/
      renamed 'developer.conf.j2' -> 'roles/apache.developer_configs/templates/developer.conf.j2'
  7. The apache.developer_configs role creates a user account and configures a web server instance for the list of users defined in the variable named web_developers.

    The web_developers.yml file in the project directory defines the web_developers variable, which is the list of users for the role.

    Review this file and put it in the correct location to define the web_developers variable for the development web server host group from your inventory.

    1. Review the web_developers.yml file.

      ---
      web_developers:
        - username: jdoe
          name: John Doe
          user_port: 9081
        - username: jdoe2
          name: Jane Doe
          user_port: 9082

      A name, username, and user_port variable is defined for each web developer.

    2. Place the web_developers.yml file in the group_vars/dev_webserver subdirectory.

      [student@workstation role-review]$ mkdir -pv group_vars/dev_webserver
      mkdir: created directory 'group_vars'
      mkdir: created directory 'group_vars/dev_webserver'
      [student@workstation role-review]$ mv -v web_developers.yml \
      > group_vars/dev_webserver/
      renamed 'web_developers.yml' -> 'group_vars/dev_webserver/web_developers.yml'
  8. Add the apache.developer_configs role to the play in the web_dev_server.yml playbook.

    Ensure that the /home/student/role-review/web_dev_server.yml playbook contains the following content:

    ---
    - name: Configure Dev Web Server
      hosts: dev_webserver
      force_handlers: yes
      roles:
        - apache.developer_configs
  9. Verify the syntax of the playbook, and then run the playbook. The syntax check should pass, but the playbook should fail when the infra.apache role attempts to restart Apache HTTPD.

    [student@workstation role-review]$ ansible-navigator run \
    > -m stdout web_dev_server.yml --syntax-check
    playbook: /home/student/role-review/web_dev_server.yml
    [student@workstation role-review]$ ansible-navigator run \
    > -m stdout web_dev_server.yml
    
    PLAY [Configure Dev Web Server] ************************************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [servera.lab.example.com]
    
    ...output omitted...
    
    TASK [apache.developer_configs : Create user accounts] *************************
    changed: [servera.lab.example.com] => (item={'username': 'jdoe', 'name': 'John Doe', 'user_port': 9081})
    changed: [servera.lab.example.com] => (item={'username': 'jdoe2', 'name': 'Jane Doe', 'user_port': 9082})
    
    ...output omitted...
    
    RUNNING HANDLER [infra.apache : restart firewalld] *****************************
    changed: [servera.lab.example.com]
    
    RUNNING HANDLER [infra.apache : restart apache] ********************************
    fatal: [servera.lab.example.com]: FAILED! => {"changed": false, "msg": "Unable to restart service httpd: Job for httpd.service failed because the control process exited with error code.\nSee \"systemctl status httpd.service\" and \"journalctl -xeu httpd.service\" for details.\n"}
    
    NO MORE HOSTS LEFT *************************************************************
    
    PLAY RECAP *********************************************************************
    servera.lab.example.com    : ok=14   changed=12   unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
    Please review the log for errors.

    An error occurs when the httpd service is restarted. The httpd service daemon cannot bind to the non-standard HTTP ports, due to the SELinux context on those ports.

  10. Apache HTTPD failed to restart in the preceding step because the network ports it uses for your developers are labeled with the wrong SELinux contexts.

    You can use the provided variable file, selinux.yml, with the rhel-system-roles.selinux role to fix the issue.

    Create a pre_tasks section for your play in the web_dev_server.yml playbook. In that section, use a task to include the rhel-system-roles.selinux role in a block/rescue structure, so that it is properly applied.

    Move the selinux.yml file to the correct location so that its variables are set for the dev_webserver host group.

    1. Add the pre_tasks section to the end of the play in the web_dev_server.yml playbook.

      You can review the block in /usr/share/doc/rhel-system-roles/selinux/example-selinux-playbook.yml for a basic outline of how to apply the role.

      Ensure that the pre_tasks section contains the following content:

        pre_tasks:
          - name: Verify SELinux configuration
            block:
              - include_role:
                  name: redhat.rhel_system_roles.selinux
            rescue:
              # Fail if failed for a different reason than selinux_reboot_required.
              - name: Handle general failure
                ansible.builtin.fail:
                  msg: "SELinux role failed."
                when: not selinux_reboot_required
      
              - name: Restart managed host
                ansible.builtin.reboot:
                  msg: "Ansible rebooting system for updates."
      
              - name: Reapply SELinux role to complete changes
                include_role:
                  name: redhat.rhel_system_roles.selinux
    2. The selinux.yml file contains variable definitions for the rhel-system-roles.selinux role. Use the file to define variables for the play's host group.

      [student@workstation role-review]$ cat selinux.yml
      ---
      # variables used by rhel-system-roles.selinux
      
      selinux_policy: targeted
      selinux_state: enforcing
      
      selinux_ports:
        - ports:
            - "9081"
            - "9082"
          proto: 'tcp'
          setype: 'http_port_t'
          state: 'present'
      
      [student@workstation role-review]$ mv -v selinux.yml \
      > group_vars/dev_webserver/
      renamed 'selinux.yml' -> 'group_vars/dev_webserver/selinux.yml'
  11. Verify the final web_dev_server.yml playbook and run a syntax check. The syntax check should pass.

    Note

    The ansible-navigator run command runs the play's tasks in the correct order, regardles of whether pre_tasks is at the end of the play or in the "correct" position of execution order in the playbook file.

    It still runs the play's tasks in the correct order.

    Validate that the web_dev_server.yml playbook passes a syntax check.

    Ensure that the final web_dev_server.yml playbook contains the following content:

    ---
    - name: Configure Dev Web Server
      hosts: dev_webserver
      force_handlers: yes
      roles:
        - apache.developer_configs
      pre_tasks:
        - name: Verify SELinux configuration
          block:
            - include_role:
                name: redhat.rhel_system_roles.selinux
          rescue:
            # Fail if failed for a different reason than selinux_reboot_required.
            - name: Handle general failure
              ansible.builtin.fail:
                msg: "SELinux role failed."
              when: not selinux_reboot_required
    
            - name: Restart managed host
              ansible.builtin.reboot:
                msg: "Ansible rebooting system for updates."
    
            - name: Reapply SELinux role to complete changes
              include_role:
                name: redhat.rhel_system_roles.selinux
    [student@workstation role-review]$ ansible-navigator run \
    > -m stdout web_dev_server.yml --syntax-check
    playbook: /home/student/role-review/web_dev_server.yml
  12. Run the web_dev_server.yml playbook. It should succeed.

    [student@workstation role-review]$ ansible-navigator run \
    > -m stdout web_dev_server.yml
    
    PLAY [Configure Dev Web Server] ************************************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [servera.lab.example.com]
    
    TASK [include_role : redhat.rhel_system_roles.selinux] *************************
    
    ...output omitted...
    
    TASK [redhat.rhel_system_roles.selinux : Set an SELinux label on a port] *******
    changed: [servera.lab.example.com] => (item={'ports': ['9081', '9082'], 'proto': 'tcp', 'setype': 'http_port_t', 'state': 'present'})
    
    TASK [redhat.rhel_system_roles.selinux : Set linux user to SELinux user mapping] ***
    
    TASK [redhat.rhel_system_roles.selinux : Get SELinux modules facts] ************
    ok: [servera.lab.example.com]
    
    TASK [redhat.rhel_system_roles.selinux : include_tasks] ************************
    skipping: [servera.lab.example.com]
    
    TASK [infra.apache : Apache Package is installed] ******************************
    ok: [servera.lab.example.com]
    
    TASK [infra.apache : Apache Service is started] ********************************
    changed: [servera.lab.example.com]
    
    ...output omitted...
    
    TASK [apache.developer_configs : Create user accounts] *************************
    ok: [servera.lab.example.com] => (item={'username': 'jdoe', 'name': 'John Doe', 'user_port': 9081})
    ok: [servera.lab.example.com] => (item={'username': 'jdoe2', 'name': 'Jane Doe', 'user_port': 9082})
    
    TASK [apache.developer_configs : Give student access to all accounts] **********
    ok: [servera.lab.example.com] => (item={'username': 'jdoe', 'name': 'John Doe', 'user_port': 9081})
    ok: [servera.lab.example.com] => (item={'username': 'jdoe2', 'name': 'Jane Doe', 'user_port': 9082})
    
    TASK [apache.developer_configs : Create content directory] *********************
    ok: [servera.lab.example.com] => (item={'username': 'jdoe', 'name': 'John Doe', 'user_port': 9081})
    ok: [servera.lab.example.com] => (item={'username': 'jdoe2', 'name': 'Jane Doe', 'user_port': 9082})
    
    TASK [apache.developer_configs : Create skeleton index.html if needed] *********
    ok: [servera.lab.example.com] => (item={'username': 'jdoe', 'name': 'John Doe', 'user_port': 9081})
    ok: [servera.lab.example.com] => (item={'username': 'jdoe2', 'name': 'Jane Doe', 'user_port': 9082})
    
    TASK [apache.developer_configs : Set firewall port] ****************************
    ok: [servera.lab.example.com] => (item={'username': 'jdoe', 'name': 'John Doe', 'user_port': 9081})
    ok: [servera.lab.example.com] => (item={'username': 'jdoe2', 'name': 'Jane Doe', 'user_port': 9082})
    
    TASK [apache.developer_configs : Copy Per-Developer Config files] **************
    ok: [servera.lab.example.com] => (item={'username': 'jdoe', 'name': 'John Doe', 'user_port': 9081})
    ok: [servera.lab.example.com] => (item={'username': 'jdoe2', 'name': 'Jane Doe', 'user_port': 9082})
    
    PLAY RECAP *********************************************************************
    servera.lab.example.com    : ok=21   changed=2    unreachable=0    failed=0    skipped=16   rescued=0    ignored=0
  13. Test the configuration of the development web server. Verify that all endpoints are accessible and serving each developer's content.

    [student@workstation role-review]$ curl servera
    This is the production server on servera.lab.example.com
    [student@workstation role-review]$ curl servera:9081
    This is index.html for user: John Doe (jdoe)
    [student@workstation role-review]$ curl servera:9082
    This is index.html for user: Jane Doe (jdoe2)
    [student@workstation role-review]$

Evaluation

As the student user on the workstation machine, use the lab command to grade your work. Correct any reported failures and rerun the command until successful.

[student@workstation ~]$ lab grade role-review

Finish

On the workstation machine, change to the student user home directory and use the lab command to complete this exercise. This step is important to ensure that resources from previous exercises do not impact upcoming exercises.

[student@workstation ~]$ lab finish role-review

This concludes the section.

Revision: rh294-9.0-c95c7de