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.
Change into the /home/student/role-review directory.
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.
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.gzStarting 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
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...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.
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-checkplaybook: /home/student/role-review/web_dev_server.yml [student@workstation role-review]$ansible-navigator run \>-m stdout web_dev_server.ymlPLAY [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
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.
Create a roles subdirectory for the playbook project.
[student@workstation role-review]$ mkdir -v roles
mkdir: created directory 'roles'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
Install the project dependencies.
[student@workstation role-review]$ansible-galaxy install \>-r roles/requirements.yml -p rolesStarting galaxy role install process - extracting infra.apache to /home/student/role-review/roles/infra.apache - infra.apache (v1.4) was installed successfully
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.
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]$
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.4Overwrite 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.ymlrenamed 'developer_tasks.yml' -> 'roles/apache.developer_configs/tasks/main.yml'
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'
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.
Review the web_developers.yml file.
---
web_developers:
- username: jdoe
name: John Doe
user_port: 9081
- username: jdoe2
name: Jane Doe
user_port: 9082A name, username, and user_port variable is defined for each web developer.
Place the web_developers.yml file in the group_vars/dev_webserver subdirectory.
[student@workstation role-review]$mkdir -pv group_vars/dev_webservermkdir: 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'
Add the apache.developer_configs role to the play in the web_dev_server.yml playbook.
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-checkplaybook: /home/student/role-review/web_dev_server.yml [student@workstation role-review]$ansible-navigator run \>-m stdout web_dev_server.ymlPLAY [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.
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.
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.selinuxThe 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'
Verify the final web_dev_server.yml playbook and run a syntax check.
The syntax check should pass.
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-checkplaybook: /home/student/role-review/web_dev_server.yml
Run the web_dev_server.yml playbook.
It should succeed.
[student@workstation role-review]$ansible-navigator run \>-m stdout web_dev_server.ymlPLAY [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
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 serveraThis is the production server on servera.lab.example.com [student@workstation role-review]$curl servera:9081This is index.html for user: John Doe (jdoe) [student@workstation role-review]$curl servera:9082This is index.html for user: Jane Doe (jdoe2) [student@workstation role-review]$
This concludes the section.