Bootstrap

Chapter 7 RH294 RHEL Automation with Ansible

ONLY FOR SELF STUDY, NO COMMERCIAL USAGE!!!


Contents

Chapter 7. Simplifying Playbooks with Roles and Ansible Content Collections

Describing Role Structure

Ansible roles make it easier to reuse Ansible code generically. You can package all the tasks, variables, files, templates, and other resources needed to provision infrastructure or deploy applications in a standardized directory structure. Copy a role from project to project by copying the directory, then call the role within a play.

Ansible roles have the following benefits:

  • Roles group content together, enabling easy sharing of code with others.
  • Roles can define the essential elements of a system type, such as a web server, database server, or Git repository.
  • Roles make larger projects more manageable.
  • Roles can be developed in parallel by different users.

In addition to writing, using, reusing, and sharing your own roles, you can obtain roles from other sources. You can find roles by using distribution packages, such as Ansible Content Collections. Or, you can download roles from the Red Hat automation hub, a private automation hub, and from the community’s Ansible Galaxy website.

Red Hat Enterprise Linux includes some roles in the rhel-system-roles package. You learn more about rhel-system-roles later in this chapter.

Examining the Ansible Role Structure

An Ansible role is defined by a standardized structure of subdirectories and files.

The top-level directory defines the name of the role itself. Files are organized into subdirectories that are named according to each file’s purpose in the role, such as tasks and handlers.

The files and templates subdirectories contain files referenced by tasks in other playbooks and task files.

The following tree command displays the directory structure of the user.example role.

[user@host roles]$ tree user.example
user.example/
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

Table 7.1. Ansible Role Subdirectories

SubdirectoryFunction
defaultsThe main.yml file in this directory contains the default values of role variables that can be overwritten when the role is used. These variables have low precedence and are intended to be changed and customized in plays.
filesThis directory contains static files that are referenced by role tasks.
handlersThe main.yml file in this directory contains the role’s handler definitions.
metaThe main.yml file in this directory contains information about the role, including author, license, platforms, and optional role dependencies.
tasksThe main.yml file in this directory contains the role’s task definitions.
templatesThis directory contains Jinja2 templates that are referenced by role tasks.
testsThis directory can contain an inventory and test.yml playbook that can be used to test the role.
varsThe main.yml file in this directory defines the role’s variable values. Often these variables are used for internal purposes within the role. These variables have high precedence and are not intended to be changed when used in a playbook.

Not every role has all of these directories.

Defining Variables and Defaults

Role variables are defined by creating a vars/main.yml file with key-value pairs in the role directory hierarchy. These variables are referenced in role task files like any other variable: {{ VAR_NAME }}. These variables have a high precedence and can not be overridden by inventory variables. These variables are used by the internal functioning of the role.

Default variables enable you to set default values for variables that can be used in a play to configure the role or customize its behavior. These variables are defined by creating a defaults/main.yml file with key-value pairs in the role directory hierarchy. Default variables have the lowest precedence of any available variables.

Default variable values can be overridden by any other variable, including inventory variables. These variables are intended to provide the person writing a play that uses the role with a way to customize or control exactly what it is going to do. You can use default variables to provide information to the role that it needs to configure or deploy something properly.

Define a specific variable in either vars/main.yml or defaults/main.yml, but not in both places. Use default variables when you intend that the variable values might be overridden.

Using Ansible Roles in a Play

There are several ways to call roles in a play. The two primary methods are:

  • You can include or import them like a task in your tasks list.
  • You can create a roles list that runs specific roles before your play’s tasks.

The first method is the most flexible, but the second method is also commonly used and was invented before the first method.

Including and Importing Roles as Tasks
Import Roles

Importing roles: static reuse

Roles can be added to a play by using an ordinary task. Use the ansible.builtin.import_role module to statically import a role, and the ansible.builtin.include_role module to dynamically include a role.

The following play demonstrates how you can import a role by using a task with the ansible.builtin.import_role module. The example play runs the task A normal task first, then imports the role2 role.

- name: Run a role as a task
  hosts: remote.example.com
  tasks:
    - name: A normal task
      ansible.builtin.debug:
        msg: 'first task'
    - name: A task to import role2 here
      ansible.builtin.import_role:
        name: role2

With the ansible.builtin.import_role module, Ansible treats the role as a static import and parses it during initial playbook processing.

In the preceding example, when the playbook is parsed:

  • If roles/role2/tasks/main.yml exists, Ansible adds the tasks in that file to the play.
  • If roles/role2/handlers/main.yml exists, Ansible adds the handlers in that file to the play.
  • If roles/role2/defaults/main.yml exists, Ansible adds the default variables in that file to the play.
  • If roles/role2/vars/main.yml exists, Ansible adds the variables in that file to the play (possibly overriding values from role default variables due to precedence).

Important

Because ansible.builtin.import_role is processed when the playbook is parsed, the role’s handlers, default variables, and role variables are all exposed to all the tasks and roles in the play, and can be accessed by tasks and roles that precede it in the play (even though the role has not run yet).

You can also set variables for the role when you call the task, in the same way that you can set task variables:

- name: Run a role as a task
  hosts: remote.example.com
  tasks:
    - name: A task to include role2 here
      ansible.builtin.import_role:
        name: role2
      vars:
        var1: val1
        var2: val2
Include Roles

Including roles: dynamic reuse

The ansible.builtin.include_role module works in a similar way, but it dynamically includes the role when the playbook is running instead of statically importing it when the playbook is initially parsed.

---
- hosts: webservers
  tasks:
    - name: Print a message
      ansible.builtin.debug:
        msg: "this task runs before the example role"

    - name: Include the example role
      include_role:
        name: example

    - name: Print a message
      ansible.builtin.debug:
        msg: "this task runs after the example role"

One key difference between the two modules is how they handle task-level keywords, conditionals, and loops:

  • ansible.builtin.import_role applies the task’s conditionals and loops to each of the tasks being imported.
  • ansible.builtin.include_role applies the task’s conditionals and loops to the statement that determines whether the role is included or not.

In addition, when you include a role, its role variables and default variables are not exposed to the rest of the play, unlike ansible.builtin.import_role.

---
- hosts: webservers
  tasks:
    - name: Include the some_role role
      include_role:
        name: some_role
      when: "ansible_facts['os_family'] == 'RedHat'"
Using a Roles Section in a Play

Another way you can call roles in a play is to list them in a roles section. The roles section is very similar to the tasks section, except instead of consisting of a list of tasks, it consists of a list of roles.

In the following example play, the role1 role runs, then the role2 role runs.

---
- name: A play that only has roles
  hosts: remote.example.com
  roles:
    - role: role1
    - role: role2

For each role specified, the role’s tasks, handlers, variables, and dependencies are imported into the play in the order in which they are listed.

When you use a roles section to import roles into a play, the roles run first, before any tasks that you define for that play. Whether the roles section is listed before or after the tasks section in the play does not matter.

---
- name: Roles run before tasks
  hosts: remote.example.com
  tasks:
    - name: A task
      ansible.builtin.debug:
        msg: "This task runs after the role."
  roles:
    - role: role1

Because roles run first, it generally makes sense to list the roles section before the tasks section, if you must have both. The preceding play can be rewritten as follows without changing how it runs:

---
- name: Roles run before tasks
  hosts: remote.example.com
  roles:
    - role: role1
  tasks:
    - name: A task.
      ansible.builtin.debug:
        msg: "This task runs after the role."

Important

A tasks section in a play is not required. In fact, it is generally a good practice to avoid both roles and tasks sections in a play to avoid confusion about the order in which roles and tasks run.

If you must have a tasks section and roles, it is better to create tasks that use ansible.builtin.import_role and ansible.builtin.include_role to run at the correct points in the play’s execution.

The following example sets values for two role variables of role2, var1 and var2. Any defaults and vars variables are overridden when role2 is used.

---
- name: A play that runs the second role with variables
  hosts: remote.example.com
  roles:
    - role: role1
    - role: role2
      var1: val1
      var2: val2

Another equivalent playbook syntax that you might see in this case is:

---
- name: A play that runs the second role with variables
  hosts: remote.example.com
  roles:
    - role: role1
    - { role: role2, var1: val1, var2: val2 }

There are situations in which this can be harder to read, even though it is more compact.

Important

Ansible looks for duplicate role lines in the roles section. If two roles are listed with exactly the same parameters, the role only runs once.

For example, the following roles section only runs role1 one time:

roles:
  - { role: role1, service: "httpd" }
  - { role: role2, var1: true }
  - { role: role1, service: "httpd" }

To run the same role a second time, it must have different parameters defined:

roles:
  - { role: role1, service: "httpd" }
  - { role: role2, var1: true }
  - { role: role1, service: "postfix" }
Special Tasks Sections

There are two special task sections, pre_tasks and post_tasks, that are occasionally used with roles sections. The pre_tasks section is a list of tasks, similar to tasks, but these tasks run before any of the roles in the roles section. If any task in the pre-tasks section notify a handler, then those handler tasks run before the roles or normal tasks.

Plays also support a post_tasks keyword. These tasks run after the play’s tasks and any handlers notified by the play’s tasks.

The following play shows an example with pre_tasks, roles, tasks, post_tasks and handlers. It is unusual that a play would contain all of these sections.

- name: Play to illustrate order of execution
  hosts: remote.example.com
  pre_tasks:
    - name: This task runs first
      ansible.builtin.debug:
        msg: This task is in pre_tasks
      notify: my handler
      changed_when: true
  roles:
    - role: role1
  tasks:
    - name: This task runs after the roles
      ansible.builtin.debug:
        msg: This task is in tasks
      notify: my handler
      changed_when: true
  post_tasks:
    - name: This task runs last
      ansible.builtin.debug:
        msg: This task is in post_tasks
      notify: my handler
      changed_when: true
  handlers:
    - name: my handler
      ansible.builtin.debug:
        msg: Running my handler

In the preceding example, an ansible.builtin.debug task runs in each tasks section and in the role in the roles section. Each of those tasks notifies the my handler handler, which means the my handler task runs three times:

  • After all the pre_tasks tasks run
  • After all the roles tasks and tasks tasks run
  • After all the post_tasks run

Note

In general, if you think you need pre_tasks and post_tasks sections in your play because you are using roles, consider importing the roles as tasks and including only a tasks section. Alternatively, it might be simpler to have multiple plays in your playbook.

References

Roles — Ansible Documentation

Creating Roles

The Role Creation Process

Creating roles in Ansible does not require any special development tools.

Creating and using a role is a three-step process:

  1. Create the role directory structure.
  2. Define the role content.
  3. Use the role in a playbook.
Creating the Role Directory Structure

Ansible looks for roles in a subdirectory called roles in the directory containing your Ansible Playbook. Each role has its own directory with a standardized directory structure. This structure allows you to store roles with the playbook and other supporting files.

For example, the following directory structure contains the files that define the motd role.

[user@host ~]$ tree roles/
roles/
└── motd
    ├── defaults
    │   └── main.yml
    ├── files
    ├── handlers
    ├── meta
    │   └── main.yml
    ├── README.md
    ├── tasks
    │   └── main.yml
    └── templates
        └── motd.j2

The README.md provides a basic human-readable description of the role, documentation, examples of how to use it, and any non-Ansible role requirements. The meta subdirectory contains a main.yml file that specifies information about the author, license, compatibility, and dependencies for the module.

The files subdirectory contains fixed content files and the templates subdirectory contains templates that the role can deploy.

The other subdirectories can contain main.yml files that define default variable values, handlers, tasks, role metadata, or variables, depending on their subdirectory.

If a subdirectory exists but is empty, such as handlers in this example, it is ignored. You can omit the subdirectory altogether if the role does not use a feature. This example omits the vars subdirectory.

Run ansible-galaxy role init to create the directory structure for a new role.

Specify the role’s name as an argument to the command, which creates a subdirectory for the new role in the current working directory.

[user@host playbook-project]$ cd roles
[user@host roles]$ ansible-galaxy role init my_new_role
- Role my_new_role was created successfully
[user@host roles]$ ls my_new_role/
defaults  files  handlers  meta  README.md  tasks  templates  tests  vars
Defining the Role Content

After creating the directory structure, you must write the content of the role. A good place to start is the *ROLENAME*/tasks/main.yml task file, the main list of tasks that the role runs.

The following tasks/main.yml file manages the /etc/motd file on managed hosts. It uses the template module to deploy the template named motd.j2 to the managed host. Because the template module is configured within a role task, instead of a playbook task, the motd.j2 template is retrieved from the role’s templates subdirectory.

[user@host ~]$ cat roles/motd/tasks/main.yml
---
# tasks file for motd

- name: deliver motd file
  ansible.builtin.template:
    src: motd.j2
    dest: /etc/motd
    owner: root
    group: root
    mode: 0444

The following command displays the contents of the motd.j2 template of the motd role. It references Ansible facts and a system_owner variable.

[user@host ~]$ cat roles/motd/templates/motd.j2
This is the system {{ ansible_facts['hostname'] }}.

Today's date is: {{ ansible_facts['date_time']['date'] }}.

Only use this system with permission.
You can ask {{ system_owner }} for access.

The role defines a default value for the system_owner variable. The defaults/main.yml file in the role’s directory structure is where this value is set.

The following defaults/main.yml file sets the system_owner variable to [email protected]. This email address is written in the /etc/motd file of managed hosts when this role is applied.

[user@host ~]$ cat roles/motd/defaults/main.yml
---
system_owner: [email protected]
Recommended Practices for Role Content Development

Roles allow you to break down playbooks into multiple files, resulting in reusable code. To maximize the effectiveness of newly developed roles, consider implementing the following recommended practices into your role development:

  • Maintain each role in its own version control repository. Ansible works well with Git-based repositories.
  • Use variables to configure roles so that you can reuse the role to perform similar tasks in similar circumstances.
  • Avoid storing sensitive information in a role, such as passwords or SSH keys. Configure role variables that are used to contain sensitive values when called in a play with default values that are not sensitive. Playbooks that use the role are responsible for defining sensitive variables through Ansible Vault variable files or other methods.
  • Use the ansible-galaxy role init command to start your role, and then remove any unnecessary files and directories.
  • Create and maintain README.md and meta/main.yml files to document the role’s purpose, author, and usage.
  • Keep your role focused on a specific purpose or function. Instead of making one role do many things, write more than one role.
  • Reuse roles often.

Resist creating new roles for edge configurations. If an existing role accomplishes most of the required configuration, refactor the existing role to integrate the new configuration scenario.

Use integration and regression testing techniques to ensure that the role provides the required new functionality and does not cause problems for existing playbooks.

A longer unofficial list of good practices to follow when you write a role is available from https://redhat-cop.github.io/automation-good-practices/#_roles_good_practices_for_ansible.

Changing a Role’s Behavior with Variables

A well-written role uses default variables to alter the role’s behavior to match a related configuration scenario. Roles that use variables are more generic and reusable in a variety of contexts.

The value of any variable defined in a role’s defaults directory is overwritten if that same variable is defined:

  • In an inventory file, either as a host variable or a group variable.
  • In a YAML file under the group_vars or host_vars directories of a playbook project.
  • As a variable nested in the vars keyword of a play.
  • As a variable when including the role in roles keyword of a play.

The following example shows how to use the motd role with a different value for the system_owner role variable. The value specified, [email protected], replaces the variable reference when the role is applied to a managed host.

[user@host ~]$ cat use-motd-role.yml
---
- name: use motd role playbook
  hosts: remote.example.com
  remote_user: devops
  become: true
  vars:
    system_owner: [email protected]
  roles:
    - role: motd

When defined in this way, the system_owner variable replaces the value of the default variable of the same name. Any variable definitions nested within the vars keyword do NOT replace the value of the same variable if defined in a role’s vars directory.

The following example also shows how to use the motd role with a different value for the system_owner role variable. The value specified, [email protected], replaces the variable reference regardless of being defined in the role’s vars or defaults directory.

[user@host ~]$ cat use-motd-role.yml
---
- name: use motd role playbook
  hosts: remote.example.com
  remote_user: devops
  become: true
  roles:
    - role: motd
      system_owner: [email protected]

Important NOTE

Variable precedence can be confusing when working with role variables in a play.

  • Most other variables override a role’s default variables: inventory variables, play vars, inline role parameters, and so on.
  • Fewer variables can override variables defined in a role’s vars directory. Facts, variables loaded with include_vars, registered variables, and role parameters can override these variables. Inventory variables and play vars cannot. This behavior is important because it helps keep your play from accidentally changing the internal functioning of the role.
  • Variables declared inline as role parameters have very high precedence; they can also override variables defined in a role’s vars directory.

If a role parameter has the same name as a variable set in play vars, a role’s vars, or an inventory or playbook variable, the role parameter overrides the other variable.

Defining Role Dependencies

Role dependencies allow a role to include other roles as dependencies.

For example, a role that defines a documentation server might depend upon another role that installs and configures a web server.

Dependencies are defined in the meta/main.yml file in the role directory hierarchy.

The following is a sample meta/main.yml file.

---
dependencies:
  - role: apache
    port: 8080
  - role: postgres
    dbname: serverlist
    admin_user: felix

A meta/main.yml file might also have a top-level galaxy_info key that has a dictionary of other attributes that specify the author, purpose, license, and the versions of Ansible Core and operating systems that the role supports.

By default, if multiple roles have a dependency on a role, and that role is called by different roles in the play multiple times with the same attributes, then the role only runs the first time it appears. This behavior can be overridden by setting the allow_duplicates variable to yes in your role’s meta/main.yml file.

Important:

Limit your role’s dependencies on other roles. Dependencies make it harder to maintain your role, especially if it has many complex dependencies.

References

Using Roles — Ansible Documentation

Using Variables — Ansible Documentation

Roles Good Practices for Ansible

Example
[student@workstation role-create]$ mkdir -v roles; cd roles
mkdir: created directory 'roles'

# create role 'myvhost'
[student@workstation roles]$ ansible-galaxy role init myvhost
- Role myvhost was created successfully

# delete unused 3 folders
[student@workstation roles]$ rm -rvf myvhost/{defaults,vars,tests}

[student@workstation role-create]$ tree
.
├── ansible.cfg
├── inventory
├── roles
│   └── myvhost
│       ├── files
│       ├── handlers
│       │   └── main.yml
│       ├── meta
│       │   └── main.yml
│       ├── README.md
│       ├── tasks
│       │   └── main.yml
│       └── templates
├── verify-config.yml
├── verify-content.yml
├── verify-httpd.yml
└── vhost.conf.j2

# file contents
[student@workstation role-create]$ cat ansible.cfg 
[defaults]
inventory=inventory
remote_user=devops

#Try me...
#callback_whitelist=timer

[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False


[student@workstation role-create]$ cat inventory 
workstation

[webservers]
servera.lab.example.com


[student@workstation role-create]$ cat verify-config.yml 
---
- name: Verify the httpd config
  hosts: servera.lab.example.com

  tasks:

  - name: Verify the httpd config file is in place
    ansible.builtin.command: cat /etc/httpd/conf.d/vhost.conf
    register: config

  - name: What does the httpd config file contain
    ansible.builtin.debug:
      msg: '{{ config }}.stdout_lines'


[student@workstation role-create]$ cat verify-content.yml 
---
- name: Verify the index.html file
  hosts: servera.lab.example.com

  tasks:

  - name: Verify the index.html file is in place
    ansible.builtin.command: cat /var/www/vhosts/servera/index.html
    register: content

  - name: What does the index.html config file contain
    ansible.builtin.debug:
      msg: '{{ content }}.stdout_lines'


[student@workstation role-create]$ cat verify-httpd.yml 
---
- name: Verify the httpd service
  hosts: servera.lab.example.com

  tasks:

  - name: Verify the httpd service is installed
    ansible.builtin.command: rpm -q httpd
    register: installed

  - name: Is the httpd service installed
    ansible.builtin.debug:
      msg: '{{ installed }}.stdout'

  - name: Verify the httpd service is started
    ansible.builtin.command: systemctl is-active httpd
    register: started

  - name: Is the httpd service started
    ansible.builtin.debug:
      msg: '{{ started }}.stdout'

  - name: Verify the httpd service is enabled
    ansible.builtin.command: systemctl is-enabled httpd
    register: enabled

  - name: Is the httpd service enabled
    ansible.builtin.debug:
      msg: '{{ enabled }}.stdout'


[student@workstation role-create]$ cat vhost.conf.j2 
# {{ ansible_managed }}

<VirtualHost *:80>
    ServerAdmin webmaster@{{ ansible_fqdn }}
    ServerName {{ ansible_fqdn }}
    ErrorLog logs/{{ ansible_hostname }}-error.log
    CustomLog logs/{{ ansible_hostname }}-common.log common
    DocumentRoot /var/www/vhosts/{{ ansible_hostname }}/

    <Directory /var/www/vhosts/{{ ansible_hostname }}/>
    Options +Indexes +FollowSymlinks +Includes
    Order allow,deny
    Allow from all
    </Directory>
</VirtualHost>

Edit the main.yml file in the tasks subdirectory of the role. The role should perform the following tasks:

  • Install the httpd package.
  • Enable and start the httpd service.
  • Install the web server configuration file using a template provided by the role.
[student@workstation role-create]$ cat roles/myvhost/tasks/main.yml 
---
# tasks file for myvhost

- name: installing httpd
  ansible.builtin.dnf:
    name: httpd
    state: present

- name: Starting httpd svc
  ansible.builtin.service:
    name: httpd
    state: started
    enabled: true

- name: Installing web server config file
  ansible.builtin.template:
    src: vhost.conf.j2
    dest: /etc/httpd/conf.d/vhost.conf
    owner: root
    group: root
    mode: 0644
  notify:
    - restart httpd
    
    
[student@workstation role-create]$ cat roles/myvhost/handlers/main.yml 
---
# handlers file for myvhost

- name: restart httpd
  ansible.builtin.service:
    name: httpd
    state: restarted

# copying vhost template
[student@workstation role-create]$ mv -v vhost.conf.j2 roles/myvhost/templates/
renamed 'vhost.conf.j2' -> 'roles/myvhost/templates/vhost.conf.j2'

# Creating html content
[student@workstation role-create]$ mkdir -pv files/html
mkdir: created directory 'files'
mkdir: created directory 'files/html'

[student@workstation role-create]$ echo 'simple index' > files/html/index.html

Creating the use-vhost-role.yml which we are using the role myvhost created before:

---
- name: Use myvhost role playbook
  hosts: webservers
  pre_tasks:
    - name: pre_tasks message
      ansible.builtin.debug:
        msg: 'Ensure web server configuration.'

  roles:
    - myvhost

  post_tasks:
    - name: HTML content is installed
      ansible.builtin.copy:
        src: files/html/
        dest: "/var/www/vhosts/{{ ansible_hostname }}"

    - name: post_tasks message
      ansible.builtin.debug:
        msg: 'Web server is configured.'

check the result with:

student@workstation role-create]$ ansible-navigator run -m stdout use-vhost-role.yml --syntax-check
playbook: /home/student/role-create/use-vhost-role.yml
[student@workstation role-create]$ ansible-navigator run -m stdout use-vhost-role.yml
[student@workstation role-create]$ ansible-navigator run -m stdout verify-config.yml 
[student@workstation role-create]$ ansible-navigator run -m stdout verify-content.yml 
[student@workstation role-create]$ ansible-navigator run -m stdout verify-httpd.yml 
[student@workstation role-create]$ curl http://servera.lab.example.com
simple index

Deploying Roles from External Content Sources

External Content Sources

If you are using roles in your Ansible Playbooks, then you should get those roles from some centrally managed source. This practice ensures that all your projects have the current version of the role and that each project can benefit from bug fixes discovered by other projects with which they share the role.

  • role that stored in a Git repository managed by your organization.
  • a distributed as a tar archive file from a website or through other means.
  • the open source community maintains some roles through the Ansible Galaxy website. Note: These roles are not reviewed or supported officially by Red Hat, but might contain code that your organization finds useful.

It is increasingly common for roles to be packaged as Ansible Content Collections and offered by their authors through various methods:

  • In Red Hat Certified Ansible Content Collections from the Red Hat hosted automation hub at https://console.redhat.com, or through a private automation hub
  • As private content packaged from a private automation hub
  • By the community from Ansible Galaxy at https://galaxy.ansible.com

This section only covers how to get roles that are not packaged into an Ansible Content Collections.

Introducing Ansible Galaxy

A public library of Ansible content written by a variety of Ansible administrators and users is available at Ansible Galaxy. This library contains thousands of Ansible roles, and it has a searchable database that helps you identify roles that might help you accomplish an administrative task.

The ansible-galaxy command that you use to download and manage roles from Ansible Galaxy can also be used to download and manage roles from your own Git repositories.

The Ansible Galaxy Command Line Tool

You can use the ansible-galaxy command-line tool with the role argument to search for, display information about, install, list, remove, or initialize roles.

[user@host project]$ ansible-galaxy role -h
usage: ansible-galaxy role [-h] ROLE_ACTION ...

positional arguments:
  ROLE_ACTION
    init      Initialize new role with the base structure of a role.
    remove    Delete roles from roles_path.
    delete    Removes the role from Galaxy. It does not remove or alter the actual GitHub repository.
    list      Show the name and version of each role installed in the roles_path.
    search    Search the Galaxy database by tags, platforms, author and multiple keywords.
    import    Import a role into a galaxy server
    setup     Manage the integration between Galaxy and the given source.
    info      View more details about a specific role.
    install   Install role(s) from file(s), URL(s) or Ansible Galaxy

optional arguments:
  -h, --help   show this help message and exit
Installing Roles Using a Requirements File

If you have a playbook that must have specific roles installed, then you can create a roles/requirements.yml file in the project directory that specifies which roles are needed. This file acts as a dependency manifest for the playbook project that enables playbooks to be developed and tested separately from any supporting roles.

Then, before you run ansible-navigator run, you can use the ansible-galaxy role command to install those roles in your project’s roles directory.

Note:

If you use automation controller, it automatically downloads roles specified in your roles/requirements.yml file when it runs your playbook.

For example, you could have a role in a public repository on a Git server at https://git.example.com/someuser/someuser.myrole. A simple requirements.yml to install someuser.myrole might contain the following content:

- src: https://git.example.com/someuser/someuser.myrole
  scm: git
  version: "1.5.0"

The src attribute specifies the source of the role, in this case the URL for the repository of the role on your Git server. You can also use SSH key-based authentication by specifying something like [email protected]:someuser/someuser.myrole as provided by your Git repository.

The scm attribute indicates that this role is from a Git repository.

The version attribute is optional, and specifies the version of the role to install, in this case 1.5.0. In the case of a role stored in Git, the version can be the name of a branch, a tag, or a Git commit hash. If you do not specify a version, the command uses the latest commit on the default branch.

Important:

Specify the version of the role in your requirements.yml file, especially for playbooks in production.

If you do not specify a version, you get the latest version of the role.

If the upstream author makes changes to the role that are incompatible with your playbook, it might cause an automation failure or other problems.

To install roles using a role requirements file, run the ansible-galaxy role install command from within your project directory. Include the following options:

  • -r roles/requirements.yml to specify the location of your requirements file
  • -p roles to install the role into a subdirectory of the roles directory
[user@host project]$ ansible-galaxy role install -r roles/requirements.yml \
> -p roles
Starting galaxy role install process
- downloading role from https://git.example.com/someuser/someuser.myrole
- extracting myrole to /home/user/project/roles/someuser.myrole
- someuser.myrole (1.5.0) was installed successfully

Important:

If you do not specify the -p roles option, then ansible-galaxy role install uses the first directory in the default roles_path setting to determine where to install the role. This defaults to the user’s ~/.ansible/roles directory, which is outside the project directory and unavailable to the execution environment if you use ansible-navigator to run your playbooks.

One way to avoid the need to specify -p roles is to apply the following setting in the defaults section of your project’s ansible.cfg file:

roles_path = roles

If you have a tar archive file that contains a role, you can use roles/requirements.yml to install that file from a URL:

# from a role tar ball, given a URL;
#   supports 'http', 'https', or 'file' protocols
- src: file:///opt/local/roles/tarrole.tar
  name: tarrole

- src: https://www.example.com/role-archive/someuser.otherrole.tar
  name: someuser.otherrole

Note:

Red Hat recommends that you use version control with roles, storing them in a version control system such as Git. If a recent change to a role causes problems, using version control allows you to roll back to a previous, stable version of the role.

Finding Community-managed Roles in Ansible Galaxy

The open source Ansible community operates a public server, https://galaxy.ansible.com, that contains roles and Ansible Content Collections shared by other Ansible users.

Browsing Ansible Galaxy for Roles

The Search tab on the left side of the Ansible Galaxy website home page gives you access to information about the roles published on Ansible Galaxy. You can search for an Ansible role by name or by using tags or other role attributes.

Results are presented in descending order of the Best Match score, which is a computed score based on role quality, role popularity, and search criteria. (Content Scoring in the Ansible Galaxy documentation has more information on how roles are scored by Ansible Galaxy.)

在这里插入图片描述

Figure 7.1: Ansible Galaxy search screen

Ansible Galaxy reports the number of times each role has been downloaded from Ansible Galaxy. In addition, Ansible Galaxy also reports the number of watchers, forks, and stars the role’s GitHub repository has. You can use this information to help determine how active development is for a role and how popular it is in the community.

The following figure shows the search results that Ansible Galaxy displayed after a keyword search for redis was performed.
在这里插入图片描述

Figure 7.2: Ansible Galaxy search results example

The Filters menu to the right of the search box allows searches by type, contributor type, contributor, cloud platform, deprecated, platform, and tags.

Possible platform values include EL for Red Hat Enterprise Linux (and closely related distributions such as CentOS) and Fedora, among others.

Tags are arbitrary single-word strings set by the role author that describe and categorize the role. You can use tags to find relevant roles. Possible tag values include system, development, web, monitoring, and others. In Ansible Galaxy, a role can have up to 20 tags.

Important:

In the Ansible Galaxy search interface, keyword searches match words or phrases in the README file, content name, or content description. Tag searches, by contrast, specifically match tag values that the author set for the role.

Searching for Roles from the Command Line

The ansible-galaxy role search command searches Ansible Galaxy for roles.

If you specify a string as an argument, it is used to search Ansible Galaxy for roles by keyword. You can use the --author, --platforms, and --galaxy-tags options to narrow the search results. You can also use those options as the main search key.

For example, the command ansible-galaxy role search --author geerlingguy displays all roles submitted by the user geerlingguy. Results are displayed in alphabetical order, not by descending Best Match score.

The following example displays the names of roles that include redis, and are available for the Enterprise Linux (EL) platform.

[user@host ~]$ ansible-galaxy role search 'redis' --platforms EL

Found 232 roles matching your search:

 Name                                Description
 ----                                -----------
 ...output omitted...
 aboveops.ct_redis                   Ansible role for creating Redis container
 adfinis-sygroup.redis               Ansible role for Redis
 adriano-di-giovanni.redis           Ansible role for Redis
 AerisCloud.redis                    Installs redis on a server
 alainvanhoof.alpine_redis           Redis for Alpine Linux
 ...output omitted...

The ansible-galaxy role info command displays more detailed information about a role. Ansible Galaxy gets this information from a number of places, including the role’s meta/main.yml file and its GitHub repository.

The following command displays information about the geerlingguy.redis role, available from Ansible Galaxy.

[user@host ~]$ ansible-galaxy role info geerlingguy.redis

Role: geerlingguy.redis
        description: Redis for Linux
...output omitted...
        created: 2023-05-08T20:50:00.204075Z
        download_count: 1306420
        github_branch: master
        github_repo: ansible-role-redis
        github_user: geerlingguy
        id: 10990
        imported: 2022-09-26T12:10:34.707632-04:00
        modified: 2023-10-29T18:44:43.771821Z
        path: ('/home/student/.ansible/roles', '/usr/share/ansible/roles', '/etc/ansible/roles')
        upstream_id: 468
        username: geerlingguy
Downloading Roles from Ansible Galaxy

The following example shows how to configure a requirements file that uses a variety of remote sources.

[user@host project]$ cat roles/requirements.yml
# from Ansible Galaxy, using the latest version
- src: geerlingguy.redis 

# from Ansible Galaxy, overriding the name and using a specific version
- src: geerlingguy.redis
  version: "1.5.0" 
  name: redis_prod

# from any Git based repository, using HTTPS
- src: https://github.com/geerlingguy/ansible-role-nginx.git
  scm: git 
  version: master
  name: nginx

# from a role tar ball, given a URL;
#   supports 'http', 'https', or 'file' protocols
- src: file:///opt/local/roles/myrole.tar 
  name: myrole 
  

Explanation:
# The `version` keyword is used to specify a role's version. The `version` keyword can be any value that corresponds to a branch, tag, or commit hash from the role's software repository.

# If the role is hosted in a source control repository, the `scm` attribute is required.
 
# If the role is hosted on Ansible Galaxy or as a tar archive, the `scm` keyword is omitted.
 
# The `name` keyword is used to override the local name of the role.
Managing Downloaded Roles

The ansible-galaxy role command can also manage local roles, such as those roles found in the roles directory of a playbook project. The ansible-galaxy role list command lists the local roles.

[user@host project]$ ansible-galaxy role list
# /home/user/project/roles
- geerlingguy.redis, 1.7.0
- redis_prod, 1.5.0
- nginx, master
- myrole, (unknown version)
...output omitted...

You can remove a role with the ansible-galaxy role remove command.

[user@host ~]$ ansible-galaxy role remove nginx
- successfully removed nginx
References

ansible-galaxy — Ansible Documentation

Red Hat Hybrid Cloud Console | Ansible Automation Platform Dashboard

Example

Create a role requirements file in your project directory that downloads the student.bash_env role. This role configures the default initialization files for the Bash shell used for newly created users, the default prompt for the accounts of these users, and the prompt color to use.

[student@workstation ~]$ cd role-galaxy/
[student@workstation role-galaxy]$ ll
total 68
-rw-r--r--. 1 student student   144 Sep  6 10:14 ansible.cfg
-rw-r--r--. 1 student student 61440 Sep  6 10:14 bash_env.tar
-rw-r--r--. 1 student student    67 Sep  6 10:14 inventory
drwxr-xr-x. 2 student student     6 Sep  6 10:15 roles

[student@workstation role-galaxy]$ cat ansible.cfg 
[defaults]
inventory=inventory
remote_user=devops

[privilege_escalation]
become=true
become_method=sudo
become_user=root
become_ask_pass=false

[student@workstation role-galaxy]$ cat inventory 
workstation.lab.example.com

[devservers]
servera.lab.example.com

[student@workstation role-galaxy]$ ll roles/
total 0
[student@workstation role-galaxy]$ cd roles/
[student@workstation roles]$ ll
total 0

Create a file called requirements.yml in the roles subdirectory. The URL of the role’s Git repository is: [email protected]:student/bash_env.

To see how the role affects the behavior of production hosts, use the main branch of the repository. Set the local name of the role to student.bash_env.

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

---
# requirements.yml

- src: [email protected]:student/bash_env
  scm: git
  version: main
  name: student.bash_env
  

[student@workstation role-galaxy]$ tree
.
├── ansible.cfg
├── bash_env.tar
├── inventory
└── roles
    └── requirements.yml

1 directory, 4 files

installing the role

[student@workstation role-galaxy]$ ansible-galaxy role install -r roles/requirements.yml -p roles/
Starting galaxy role install process
- extracting student.bash_env to /home/student/role-galaxy/roles/student.bash_env
- student.bash_env (main) was installed successfully

[student@workstation role-galaxy]$ tree
.
├── ansible.cfg
├── bash_env.tar
├── inventory
└── roles
    ├── requirements.yml
    └── student.bash_env
        ├── defaults
        │   └── main.yml
        ├── meta
        │   └── main.yml
        ├── README.md
        ├── tasks
        │   └── main.yml
        ├── templates
        │   ├── _bash_profile.j2
        │   ├── _bashrc.j2
        │   └── _vimrc.j2
        ├── tests
        │   ├── inventory
        │   └── test.yml
        └── vars
            └── main.yml

8 directories, 14 files

# look the roles folder
[student@workstation role-galaxy]$ ansible-galaxy role list
# /usr/share/ansible/roles
- linux-system-roles.certificate, (unknown version)
- linux-system-roles.cockpit, (unknown version)
- linux-system-roles.crypto_policies, (unknown version)
- linux-system-roles.firewall, (unknown version)
- linux-system-roles.ha_cluster, (unknown version)
...omited...
# /etc/ansible/roles
[WARNING]: - the configured path /home/student/.ansible/roles does not exist.

# use -p specific the roles folder, here we see the roles we created
[student@workstation role-galaxy]$ ansible-galaxy role list -p roles/
# /home/student/role-galaxy/roles
- student.bash_env, main
# /usr/share/ansible/roles
- linux-system-roles.certificate, (unknown version)
- linux-system-roles.cockpit, (unknown version)

Test1:

Create a playbook named use-bash_env-role.yml that uses the student.bash_env role. The playbook must have the following contents:

---
- name: Use student.bash_env role playbook
  hosts: devservers
  vars:
    default_prompt: '[\u on \h in \W dir]\$ '
  pre_tasks:
    - name: Ensure test user does not exist
      ansible.builtin.user:
        name: student2
        state: absent
        force: true
        remove: true

  roles:
    - student.bash_env

  post_tasks:
    - name: Create the test user
      ansible.builtin.user:
        name: student2
        state: present
        password: "{{ 'redhat' | password_hash }}"

You must create a user account to see the effects of the configuration change. The pre_tasks and post_tasks section of the playbook ensure that the student2 user account is deleted and created each time the playbook is run.

When you run the use-bash_env-role.yml playbook, the student2 account is created with a password of redhat.

Note:

The student2 password is generated using a filter. Filters take data and modify it; here, the redhat string is modified by passing it to the password_hash filter to convert the value into a protected password hash. By defaul, the hashing algorithm used is sha512.

Run the use-bash_env-role.yml playbook.

[student@workstation role-galaxy]$ ansible-navigator run -m stdout use-bash_env-role.yml 

PLAY [use student.bash_env role in this playbook] ******************************

TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]

TASK [Ensure test user does not exist] *****************************************
changed: [servera.lab.example.com]

TASK [student.bash_env : put away .bashrc] *************************************
changed: [servera.lab.example.com]

TASK [student.bash_env : put away .bash_profile] *******************************
ok: [servera.lab.example.com]

TASK [student.bash_env : put away .vimrc] **************************************
ok: [servera.lab.example.com]

TASK [create the test user] ****************************************************
[WARNING]: The input password appears not to have been hashed. The 'password'
argument must be encrypted for this module to work properly.
changed: [servera.lab.example.com]

PLAY RECAP *********************************************************************
servera.lab.example.com    : ok=6    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[student@workstation role-galaxy]$ ssh student2@servera
Register this system with Red Hat Insights: insights-client --register
Create an account or view all your systems at https://red.ht/insights-dashboard

Test2:

Run the playbook using the development version of the student.bash_env role.

The development version of the role is located in the dev branch of the Git repository. The development version of the role uses a new variable, prompt_color.

Before running the playbook, add the prompt_color variable to the vars section of the playbook and set its value to blue.

  1. Update the roles/requirements.yml file, and set the version value to dev. Ensure that the roles/requirements.yml file contains the following content:

    ---
    # requirements.yml
    
    - src: [email protected]:student/bash_env
      scm: git
      version: dev
      name: student.bash_env
    
  2. Modify the ~/role-galaxy/ansible.cfg and add the roles_path setting to the [defaults] section of the file. This sets the default roles path and enable you to omit the -p roles option when typing ansible-galaxy commands.

    When completed, the file contains then following content:

    [defaults]
    inventory=inventory
    remote_user=devops
    roles_path=roles
    
    [privilege_escalation]
    become=true
    become_method=sudo
    become_user=root
    become_ask_pass=false
    
  3. Remove the existing version of the student.bash_env role from the roles subdirectory.

    [student@workstation role-galaxy]$ ansible-galaxy role remove student.bash_env
    - successfully removed student.bash_env
    
  4. Use the ansible-galaxy role install command to install the role by using the updated requirements file.

    [student@workstation role-galaxy]$ ansible-galaxy role install \
    > -r roles/requirements.yml
    Starting galaxy role install process
    - extracting student.bash_env to /home/student/role-galaxy/roles/student.bash_env
    - student.bash_env (dev) was installed successfully
    
  5. Modify the use-bash_env-role.yml file. Add the prompt_color variable with a value of blue to the vars section of the playbook. Ensure that the file contains the following content:

    ---
    - name: Use student.bash_env role playbook
      hosts: devservers
      vars:
        prompt_color: blue
        default_prompt: '[\u on \h in \W dir]\$ '
      pre_tasks:
    ...output omitted...
    
  6. Run the use-bash_env-role.yml playbook again and see students2 became blue prompt.

在这里插入图片描述

Getting Roles and Modules from Content Collections

Ansible Content Collections

With Ansible Content Collections, Ansible code updates are separated from updates to modules and plug-ins. An Ansible Content Collection provides a set of related modules, roles, and other plug-ins that you can use in your playbooks. This approach enables vendors and developers to maintain and distribute their collections at their own pace, independently of Ansible releases.

For example:

  • The redhat.insights content collection provides modules and roles that you can use to register a system with Red Hat Insights for Red Hat Enterprise Linux.
  • The cisco.ios content collection, supported and maintained by Cisco, provides modules and plug-ins that manage Cisco IOS network appliances.

You can also select a specific version of a collection (possibly an earlier or later one) or choose between a version of a collection supported by Red Hat or vendors, or one provided by the community.

Ansible 2.9 and later support Ansible Content Collections. Upstream Ansible unbundled most modules from the core Ansible code in Ansible Base 2.10 and Ansible Core 2.11 and placed them in collections. Red Hat Ansible Automation Platform 2.2 provides automation execution environments based on Ansible Core 2.13 that inherit this feature.

You can develop your own collections to provide custom roles and modules to your teams. course: Developing Advanced Automation with Red Hat Ansible Automation Platform (DO374).

Namespaces for Ansible Content Collections

The namespace is the first part of a collection name. For example, all the collections that the Ansible community maintains are in the community namespace, and have names like community.crypto, community.postgresql, and community.rabbitmq.

Selecting Sources of Ansible Content Collections

Regardless of whether you are using ansible-navigator with the minimal automation execution environment or ansible-playbook on bare metal Ansible Core, you always have at least one Ansible Content Collection available to you: ansible.builtin.

In addition, your automation execution environment might have additional automation execution environments built into it, for example, the default execution environment used by Red Hat Ansible Automation Platform 2.2, ee-supported-rhel8.

If you need to have additional Ansible Content Collections, you can add them to the collections subdirectory of your Ansible project. You might obtain Ansible Content Collections from several sources:

  • Automation Hub

    Automation hub is a service provided by Red Hat to distribute Red Hat Certified Ansible Content Collections that are supported by Red Hat and ecosystem partners. You need a valid Red Hat Ansible Automation Platform subscription to access automation hub. Use the automation hub web UI at https://console.redhat.com/ansible/automation-hub/ to browse these collections.

  • Private Automation Hub

    Your organization might have its own on-site private automation hub, and might also use that hub to distribute its own Ansible Content Collections. Private automation hub is included with Red Hat Ansible Automation Platform.

  • Ansible Galaxy

    Ansible Galaxy is a community-supported website that hosts Ansible Content Collections that have been submitted by a variety of Ansible developers and users. Ansible Galaxy is a public library that provides no formal support guarantees and that is not curated by Red Hat. For example, the community.crypto, community.postgresql, and community.rabbitmq collections are all available from that platform.Use the Ansible Galaxy web UI at https://galaxy.ansible.com/ to search it for collections.

  • Third-Party Git Repository or Archive File

    You can also download Ansible Content Collections from a Git repository or a local or remote tar archive file, much like you can download roles.

Installing Ansible Content Collections

The Ansible configuration collections_paths setting specifies a colon separated list of paths on the system where Ansible looks for installed collections.

You can set this directive in the ansible.cfg configuration file.

The following default value references the collections_paths directive.

~/.ansible/collections:/usr/share/ansible/collections

The following example uses the ansible-galaxy collection install command to download and install the community.crypto Ansible Content Collection. The -p collections option installs the collection in the local collections subdirectory.

[user@controlnode ~]$ ansible-galaxy collection install community.crypto -p collections

Important:

You must specify the -p collections option or ansible-galaxy installs the collection based on your current collections_paths setting, or into your ~/.ansible/collections/ directory on the control node by default. The ansible-navigator command does not load this directory into the automation execution environment, although this directory is available to Ansible commands that use the control node as the execution environment, such as ansible-playbook.

When you install the community.crypto collection, you could see a warning that your Ansible project’s playbooks might not find the collection because the specified path is not part of the configured Ansible collections path.

You can safely ingore this warning because your Ansible project checks the local collections subdirectory before checking directories specified by the collections_paths setting and can use the collections stored there.

The command can also install a collection from a local or a remote tar archive, or a Git repository. A Git repository must have a valid galaxy.yml or MANIFEST.json file that provides metadata about the collection, such as its namespace and version number.

For example, see the community.general collection at https://github.com/ansible-collections/community.general.

[user@controlnode ~]$ ansible-galaxy collection install \
> /tmp/community-dns-1.2.0.tar.gz -p collections
...output omitted...
[user@controlnode ~]$ ansible-galaxy collection install \
> http://www.example.com/redhat-insights-1.0.5.tar.gz -p collections
...output omitted...
[user@controlnode ~]$ ansible-galaxy collection install \
> [email protected]:organization/repo_name.git -p collections
Installing Ansible Content Collections with a Requirements File

If your Ansible project needs additional Ansible Content Collections, you can create a collections/requirements.yml file in the project directory that lists all the collections that the project requires. Automation controller detects this file and automatically installs the specified collections before running your playbooks.

A requirements file for Ansible Content Collections is a YAML file that consists of a collections dictionary key that has the list of collections to install as its value. Each list item can also specify the particular version of the collection to install, as shown in the following example:

---
collections: 
  - name: community.crypto 

  - name: ansible.posix
    version: 1.2.0 

  - name: /tmp/community-dns-1.2.0.tar.gz 

  - name: http://www.example.com/redhat-insights-1.0.5.tar.gz 

  - name: git+https://github.com/ansible-collections/community.general.git 
    version: main

The ansible-galaxy command can then use the collections/requirements.yml file to install all those collections. Specify the requirements file with the --requirements-file (or -r) option, and use the -p collections option to install the Ansible Content Collection into the collections subdirectory.

[root@controlnode ~]# ansible-galaxy collection install \
> -r collections/requirements.yml -p collections
Configuring Ansible Content Collection Sources

By default, the ansible-galaxy command uses Ansible Galaxy at https://galaxy.ansible.com/ to download Ansible Content Collections. You might not want to use this command, preferring automation hub or your own private automation hub. Alternatively, you might want to try automation hub first, and then try Ansible Galaxy.

You can configure the sources that ansible-galaxy uses to get Ansible Content Collections in your Ansible project’s ansible.cfg file. The relevant parts of that file might look like the following example:

...output omitted...
[galaxy]
server_list = automation_hub, galaxy  

[galaxy_server.automation_hub]
url=https://console.redhat.com/api/automation-hub/  
auth_url=https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token 
token=eyJh...Jf0o 

[galaxy_server.galaxy]
url=https://galaxy.ansible.com/

Instead of a token, you can use the username and password parameters to provide your customer portal username and password.

...output omitted...
[galaxy_server.automation_hub]
url=https://cloud.redhat.com/api/automation-hub/
username=operator1
password=Sup3r53cR3t
...output omitted...

However, you might not want to expose your credentials in the ansible.cfg file because the file could potentially get committed when using version control.

It is preferable to remove the authentication parameters from the ansible.cfg file and define them in environment variables, as shown in the following example:

export ANSIBLE_GALAXY_SERVER_<server_id>_<key>=value
  • server_id

    Server identifier in uppercase. The server identifier is the name you used in the server_list parameter and in the name of the [galaxy_server.*server_id*] section.

  • key

    Name of the parameter in uppercase.

The following example provides the token parameter as an environment variable:

[user@controlnode ~]$ cat ansible.cfg
...output omitted...
[galaxy_server.automation_hub]
url=https://cloud.redhat.com/api/automation-hub/
auth_url=https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token
[user@controlnode ~]$ export \
> ANSIBLE_GALAXY_SERVER_AUTOMATION_HUB_TOKEN='eyJh...Jf0o'
[user@controlnode ~]$ ansible-galaxy collection install ansible.posix \
> -p collections
Using Resources from Ansible Content Collections

You can use the ansible-navigator collections command in your Ansible project directory to list all the collections that are installed in your automation execution environment. This includes the Ansible Content Collections in your project’s collections subdirectory.

You can select the line number of the collection in the interactive mode of ansible-navigator to view its contents. Then, you can select the line number of a module, role, or other plug-in to see its documentation. You can also use other tools, such as ansible-navigator doc, with the FQCN of a module to view that module’s documentation.

The following playbook invokes the mysql_user module from the community.mysql collection for a task.

---
- name: Create the operator1 user in the test database
  hosts: db.example.com

  tasks:
    - name: Ensure the operator1 database user is defined
      community.mysql.mysql_user:
        name: operator1
        password: Secret0451
        priv: '.:ALL'
        state: present

The following playbook uses the organizations role from the redhat.satellite collection.

---
- name: Add the test organizations to Red Hat Satellite
  hosts: localhost

  tasks:
    - name: Ensure the organizations exist
      include_role:
        name: redhat.satellite.organizations
      vars:
        satellite_server_url: https://sat.example.com
        satellite_username: admin
        satellite_password: Sup3r53cr3t
        satellite_organizations:
          - name: test1
            label: tst1
            state: present
            description: Test organization 1
          - name: test2
            label: tst2
            state: present
            description: Test organization 2
References

Ansible Automation Platform Certified Content

Ansible Certified Content FAQ

Using collections — Ansible Documentation

Galaxy User Guide — Ansible Documentation

Example

Install the collection from tar or requirement.yml

[student@workstation role-collections]$ ll
total 3040
-rw-r--r--. 1 student student     146 Sep  9 03:05 ansible.cfg
-rw-r--r--. 1 student student     419 Sep  9 03:05 bck.yml
-rw-r--r--. 1 student student 2235450 Sep  9 03:05 community-general-5.5.0.tar.gz
-rw-r--r--. 1 student student    6234 Sep  9 03:05 gls-utils-0.0.1.tar.gz
-rw-r--r--. 1 student student     199 Sep  9 03:05 inventory
-rw-r--r--. 1 student student     519 Sep  9 03:05 new_system.yml
-rw-r--r--. 1 student student   34218 Sep  9 03:05 redhat-insights-1.0.7.tar.gz
-rw-r--r--. 1 student student  808333 Sep  9 03:05 redhat-rhel_system_roles-1.19.3.tar.gz
-rw-r--r--. 1 student student     239 Sep  9 03:05 requirements.yml

[student@workstation role-collections]$ cat ansible.cfg 
[defaults]
inventory=./inventory
remote_user=devops

[privilege_escalation]
become=true
become_method=sudo
become_user=root
become_ask_pass=false


[student@workstation role-collections]$ cat bck.yml 
---
- name: Backup the system configuration
  hosts: servera.lab.example.com
  become: true
  gather_facts: false

  tasks:
    - name: Ensure the machine is up
      gls.utils.newping:
        data: pong

    - name: Ensure configuration files are saved
      ansible.builtin.include_role:
        name: gls.utils.backup
      vars:
        backup_id: backup_etc
        backup_files:
          - /etc/sysconfig
          - /etc/yum.repos.d
[student@workstation role-collections]$ cat inventory 
[controlnode]
workstation.lab.example.com


[na_datacenter]
servera.lab.example.com

[europe_datacenter]
serverb.lab.example.com


[database_servers]
servera.lab.example.com
serverb.lab.example.com

[student@workstation role-collections]$ cat new_system.yml 
---
- name: Configure the system
  hosts: servera.lab.example.com
  become: true
  gather_facts: true

  tasks:
    - name: Ensure the system is registered with Insights
      ansible.builtin.include_role:
        name: redhat.insights.insights_client
      vars:
        auto_config: false
        insights_proxy: http://proxy.example.com:8080

    - name: Ensure SELinux mode is Enforcing
      ansible.builtin.include_role:
        name: redhat.rhel_system_roles.selinux
      vars:
        selinux_state: enforcing
[student@workstation role-collections]$ cat requirements.yml 
---
collections:
  - name: /home/student/role-collections/redhat-insights-1.0.7.tar.gz
  - name: /home/student/role-collections/redhat-rhel_system_roles-1.19.3.tar.gz
  - name: /home/student/role-collections/community-general-5.5.0.tar.gz
[student@workstation role-collections]$ ansible-galaxy collection install gls-utils-0.0.1.tar.gz -p collections

# entering TUI to check collections
[student@workstation role-collections]$ ansible-navigator collections


[student@workstation role-collections]$ ansible-navigator run -m stdout bck.yml

[student@workstation role-collections]$ ansible-galaxy collection install -r requirements.yml -p collections


[student@workstation role-collections]$ ansible-galaxy collection list -p collections

# /usr/share/ansible/collections/ansible_collections
Collection               Version
------------------------ -------
redhat.rhel_system_roles 1.16.2 

# /home/student/role-collections/collections/ansible_collections
Collection               Version
------------------------ -------
community.general        5.5.0  
gls.utils                0.0.1  
redhat.insights          1.0.7  
redhat.rhel_system_roles 1.19.3 


[student@workstation role-collections]$ ansible-navigator run -m stdout new_system.yml --check

Reusing Content with System Roles

System Roles

System roles are a set of Ansible roles that you can use to configure and manage various components, subsystems, and software packages included with Red Hat Enterprise Linux. System roles provide automation for many common system configuration tasks, including time synchronization, networking, firewall, tuning, and logging.

These roles are intended to provide an automation API that is consistent across multiple major and minor releases of Red Hat Enterprise Linux. The Knowledgebase article at https://access.redhat.com/articles/3050101 documents the versions of Red Hat Enterprise Linux on your managed hosts that specific roles support.

Simplified Configuration Management

System roles can help you simplify automation across multiple versions of Red Hat Enterprise Linux. For example, the recommended time synchronization service is different in different versions of Red Hat Enterprise Linux.

  • The chronyd service is preferred in RHEL 9.
  • The ntpd service is preferred in RHEL 6.

You can use an Ansible Playbook that runs the redhat.rhel_system_roles.timesync role to configure time synchronization for managed hosts running either version of Red Hat Enterprise Linux.

Support for System Roles

System roles are provided as the Red Hat Certified Ansible Content Collection redhat.rhel_system_roles through automation hub for Red Hat Ansible Automation Platform customers, as part of their subscription.

In addition, the roles are provided as an RPM package (rhel-system-roles) with Red Hat Enterprise Linux 9 for use with the version of Ansible Core provided by that operating system.

Some system roles are in “Technology Preview”. These are tested and are stable, but might be subject to future changes that are incompatible with the current state of the role. Integration testing is recommended for playbooks that incorporate any “Technology Preview” role. Playbooks might require refactoring if role variables change in a future version of the role.

You can access documentation for the system roles in ansible-navigator, or by reviewing the documentation on the Red Hat Customer Portal at Administration and configuration tasks using System Roles in RHEL .

Installing the System Roles Ansible Content Collection

You can install the redhat.rhel_system_roles Ansible Content Collection with the ansible-galaxy collection install command. An Ansible project can specify this dependency by creating a collections/requirements.yml file. The following example assumes that your project is set up to pull Ansible Content Collections from automation hub.

---
collections:
  - name: redhat.rhel_system_roles

The following command installs any required collections for a given project into the project’s collections/ directory.

[user@demo demo-project]$ ansible-galaxy collection \
> install -p collections/ -r collections/requirements.yml
...output omitted...

Upstream development is done through the Linux System Roles project; their development versions of the system roles are provided through Ansible Galaxy as the fedora.linux_system_roles Ansible Content Collection. These roles are not supported by Red Hat.

Example: Time Synchronization Role

Suppose you need to configure NTP time synchronization on your web servers. You could write automation yourself to perform each of the necessary tasks. But the system roles collection includes a role that can perform the configuration, redhat.rhel_system_roles.timesync.

The documentation for this role describes all the variables that affect the role’s behavior and provides three playbook snippets that illustrate different time synchronization configurations.

To manually configure NTP servers, the role has a variable named timesync_ntp_servers. This variable defines a list of NTP servers to use. Each item in the list is made up of one or more attributes. The two key attributes are hostname and iburst.

AttributePurpose
hostnameThe hostname of an NTP server with which to synchronize.
iburstA Boolean that enables or disables fast initial synchronization. Defaults to no in the role, but you should normally set this to yes.

Given this information, the following example is a play that uses the redhat.rhel_system_roles.timesync role to configure managed hosts to get time from three NTP servers by using fast initial synchronization.

- name: Time Synchronization Play
  hosts: webservers
  vars:
    timesync_ntp_servers:
      - hostname: 0.rhel.pool.ntp.org
        iburst: yes
      - hostname: 1.rhel.pool.ntp.org
        iburst: yes
      - hostname: 2.rhel.pool.ntp.org
        iburst: yes

  roles:
    - redhat.rhel_system_roles.timesync

This example sets the role variables in a vars section of the play, but a better practice might be to configure them as inventory variables for hosts or host groups.

Consider an example project with the following structure:

[user@demo demo-project]$ tree
.
├── ansible.cfg
├── group_vars
│   └── webservers
│       └── timesync.yml
├── inventory
└── timesync_playbook.yml
(1)Defines the time synchronization variables overriding the role defaults for hosts in group webservers in the inventory. This file would look something like:
timesync_ntp_servers:
- hostname: 0.rhel.pool.ntp.org
iburst: yes
- hostname: 1.rhel.pool.ntp.org
iburst: yes
- hostname: 2.rhel.pool.ntp.org
iburst: yes
(2)The content of the playbook simplifies to:
- name: Time Synchronization Play
  hosts: webservers
  roles:
    - redhat.rhel_system_roles.timesync

This structure cleanly separates the role, the playbook code, and configuration settings. The playbook code is simple, easy to read, and should not require complex refactoring.

This structure also supports a dynamic, heterogeneous environment. Hosts with new time synchronization requirements might be placed in a new host group. Appropriate variables are defined in a YAML file and placed in the appropriate group_vars (or host_vars) subdirectory.

Example: SELinux Role

As another example, the redhat.rhel_system_roles.selinux role simplifies management of SELinux configuration settings. This role is implemented using the SELinux-related Ansible modules. The advantage of using this role instead of writing your own tasks is that it relieves you from the responsibility of writing those tasks. Instead, you provide variables to the role to configure it, and the maintained code in the role ensures your desired SELinux configuration is applied.

This role can perform the following tasks:

  • Set enforcing or permissive mode
  • Run restorecon on parts of the file system hierarchy
  • Set SELinux Boolean values
  • Set SELinux file contexts persistently
  • Set SELinux user mappings
Calling the SELinux Role

Sometimes, the SELinux role must ensure that the managed hosts are rebooted in order to completely apply its changes. However, the role does not ever reboot hosts itself, enabling you to control how the reboot is handled. Therefore, it is a little more complicated than usual to properly use this role in a play.

The role sets a Boolean variable, selinux_reboot_required, to true and fails if a reboot is needed. You can use a block/rescue structure to recover from the failure by failing the play if that variable is not set to true, or rebooting the managed host and rerunning the role if it is true. The block in your play should look something like this:

    - name: Apply SELinux role
      block:
        - include_role:
            name: redhat.rhel_system_roles.selinux
      rescue:
        - name: Check for failure for other reasons than required reboot
          ansible.builtin.fail:
          when: not selinux_reboot_required

        - name: Restart managed host
          ansible.builtin.reboot:

        - name: Reapply SELinux role to complete changes
          include_role:
            name: redhat.rhel_system_roles.selinux
Configuring the SELinux Role

The variables used to configure the redhat.rhel_system_roles.selinux role are described in the role’s documentation. The following examples show some ways to use this role.

The selinux_state variable sets the mode that SELinux runs in. It can be set to enforcing, permissive, or disabled. If it is not set, the mode is not changed.

selinux_state: enforcing

The selinux_booleans variable takes a list of SELinux Boolean values to adjust. Each item in the list is a dictionary of variables: the name of the Boolean, the state (whether it should be on or off), and whether the setting should be persistent across reboots.

This example sets httpd_enable_homedirs to on persistently:

selinux_booleans:
  - name: 'httpd_enable_homedirs'
    state: 'on'
    persistent: 'yes'

The selinux_fcontexts variable takes a list of file contexts to persistently set (or remove), and works much like the selinux fcontext command.

The following example ensures the policy has a rule to set the default SELinux type for all files under /srv/www to httpd_sys_content_t.

selinux_fcontexts:
  - target: '/srv/www(/.*)?'
    setype: 'httpd_sys_content_t'
    state: 'present'

The selinux_restore_dirs variable specifies a list of directories on which to run the restorecon command:

selinux_restore_dirs:
  - /srv/www

The selinux_ports variable takes a list of ports that should have a specific SELinux type.

selinux_ports:
  - ports: '82'
    setype: 'http_port_t'
    proto: 'tcp'
    state: 'present'

There are other variables and options for this role. See the role documentation for more information.

Using System Roles with Ansible Core Only

This section is relevant if you plan to use system roles without a Red Hat Ansible Automation Platform subscription, by using the version of Ansible Core that is provided with Red Hat Enterprise Linux 9.

That version of Ansible Core is only supported for use with system roles and other automation code provided by Red Hat. In addition, it does not include ansible-navigator, so you have to use tools like ansible-playbook that treat your control node as the execution environment to run your automation.

Because ansible-playbook does not use execution environments, the only Ansible Content Collection that you have available by default is ansible.builtin collection. Other packages included with Red Hat Enterprise Linux might add additional Ansible Content Collections to the control node.

Installing the System Roles RPM Package

Make sure that your control node is registered with Red Hat Subscription Manager and has a Red Hat Enterprise Linux subscription. You should also install the ansible-core RPM package.

To install the rhel-system-roles RPM package, make sure that the AppStream package repository is enabled. For Red Hat Enterprise Linux 9 on the x86_64 processor architecture, this is the rhel-9-for-x86_64-appstream-rpms repository. Then, you can install the package.

[user@controlnode ~]$ sudo dnf install rhel-system-roles

If you use Ansible Core from a basic Red Hat Enterprise Linux installation for your control node, and do not have a Red Hat Ansible Automation Platform subscription on that node, then your control node should be a fully updated installation of the most recent version of Red Hat Enterprise Linux. You should also use the most recent version of the ansible-core and rhel-system-roles packages.

After installation, the collection is installed in the /usr/share/ansible/collections/ansible_collections/redhat/rhel_system_roles directory on your control node. The individual roles are also installed in the /usr/share/ansible/roles directory for backward compatibility:

[user@controlnode ~]$ ls -1F /usr/share/ansible/roles/
linux-system-roles.certificate@
linux-system-roles.cockpit@
linux-system-roles.crypto_policies@
linux-system-roles.firewall@
linux-system-roles.ha_cluster@
linux-system-roles.kdump@
linux-system-roles.kernel_settings@
linux-system-roles.logging@
linux-system-roles.metrics@
linux-system-roles.nbde_client@
linux-system-roles.nbde_server@
linux-system-roles.network@
linux-system-roles.postfix@
linux-system-roles.selinux@
linux-system-roles.ssh@
linux-system-roles.sshd@
linux-system-roles.storage@
linux-system-roles.timesync@
linux-system-roles.tlog@
linux-system-roles.vpn@
rhel-system-roles.certificate/
rhel-system-roles.cockpit/
rhel-system-roles.crypto_policies/
rhel-system-roles.firewall/
rhel-system-roles.ha_cluster/
rhel-system-roles.kdump/
rhel-system-roles.kernel_settings/
rhel-system-roles.logging/
rhel-system-roles.metrics/
rhel-system-roles.nbde_client/
rhel-system-roles.nbde_server/
rhel-system-roles.network/
rhel-system-roles.postfix/
rhel-system-roles.selinux/
rhel-system-roles.ssh/
rhel-system-roles.sshd/
rhel-system-roles.storage/
rhel-system-roles.timesync/
rhel-system-roles.tlog/
rhel-system-roles.vpn/

The corresponding upstream name of each role is linked to the system role. This enables individual roles to be referenced in a playbook by either name.

If you are using ansible-playbook to run your playbook, and your playbook refers to a system role that was installed using the RPM package’s FQCN, you must use the redhat.rhel_system_roles version of its name. For example, you could refer to the firewall role as:

  • redhat.rhel_system_roles.firewall (its FQCN in the collection)
  • rhel-system-roles.firewall (its name as an independent role)
  • linux-system-roles.firewall (its name as the upstream independent role)

You cannot use fedora.linux_system_roles.firewall because the fedora.linux_system_roles collection is not installed on the system.

In addition, the independent role names only work if /usr/share/ansible/roles is in your roles_path setting.

Accessing Documentation for System Roles

If you are working with system roles in Red Hat Enterprise Linux and do not have ansible-navigator available to you, there are other ways to get documentation about system roles.

The official documentation for system roles is located at Administration and configuration tasks using System Roles in RHEL .

However, if you installed the system roles from the RPM package, documentation is also available under the /usr/share/doc/rhel-system-roles/ directory.

[user@controlnode ~]$ ls -1 /usr/share/doc/rhel-system-roles/
certificate
cockpit
collection
crypto_policies
firewall
ha_cluster
kdump
kernel_settings
logging
metrics
nbde_client
nbde_server
network
postfix
selinux
ssh
sshd
storage
timesync
tlog
vpn

Each role’s documentation directory contains a README.md file. The README.md file contains a description of the role, along with information on how to use it.

The README.md file also describes role variables that affect the behavior of the role. Often the README.md file contains a playbook snippet that demonstrates variable settings for a common configuration scenario.

Running Playbooks Without Automation Content Navigator

You can use the ansible-playbook command to run a playbook that uses system roles in Red Hat Enterprise Linux when you do not have Red Hat Ansible Automation Platform or `ansible-navigator``.

The syntax of ansible-playbook is very similar to ansible-navigator run -m stdout, and takes many of the same options.

[user@controlnode ~]$ ansible-playbook playbook.yml
References

Red Hat Enterprise Linux (RHEL) System Roles

Red Hat Enterprise Linux System Roles Ansible Collection

Scope of support for the Ansible Core package included in the RHEL 9 and RHEL 8.6 and later AppStream repositories

Using Ansible in RHEL 9

Linux System Roles

Linux System Roles Ansible Collection

Example

Add the collections_paths key to the ansible.cfg file, so that the ./collections directory is searched.

[defaults]
inventory=./inventory
remote_user=devops
collections_paths=./collections:~/.ansible/collections:/usr/share/ansible/collections

install the redhat.rhel_system_roles collection from the tar

[student@workstation role-system]$ ansible-galaxy collection \
> install -p collections/ redhat-rhel_system_roles-1.19.3.tar.gz

[student@workstation role-system]$ ansible-galaxy collection list

# /home/student/role-system/collections/ansible_collections
Collection               Version
------------------------ -------
redhat.rhel_system_roles 1.19.3

Create the configure_time.yml playbook with one play that targets the database_servers host group and runs the redhat.rhel_system_roles.timesync role in its roles section.

Create the group_vars/all subdirectory.

[student@workstation role-system]$ mkdir -pv group_vars/all
mkdir: created directory 'group_vars'
mkdir: created directory 'group_vars/all'

Create a group_vars/all/timesync.yml file, adding variable definitions to satisfy the time synchronization requirements. The file now contains:

---
#redhat.rhel_system_roles.timesync variables for all hosts

timesync_ntp_provider: chrony

timesync_ntp_servers:
  - hostname: classroom.example.com
    iburst: yes

Add two tasks to the configure_time.yml file to get and conditionally set the time zone for each host. Ensure that both tasks run after the redhat.rhel_system_roles.timesync role.

configure_time.yml file is like:

---
- name: Time Synchronization
  hosts: database_servers

  roles:
    - redhat.rhel_system_roles.timesync

  post_tasks:
    - name: Get time zone
      ansible.builtin.command: timedatectl show
      register: current_timezone
      changed_when: false

    - name: Set time zone
      ansible.builtin.command: "timedatectl set-timezone {{ host_timezone }}"
      when: host_timezone not in current_timezone.stdout
      notify: reboot host

  handlers:
    - name: reboot host
      ansible.builtin.reboot:

For each data center, create a file named timezone.yml that contains an appropriate value for the host_timezone variable. Use the timedatectl list-timezones command to find the valid time zone string for each data center.

[student@workstation role-system]$ mkdir -pv \
> group_vars/{na_datacenter,europe_datacenter}

[student@workstation role-system]$ timedatectl list-timezones | grep Chicago
America/Chicago
[student@workstation role-system]$ timedatectl list-timezones | grep Helsinki
Europe/Helsinki

[student@workstation role-system]$ echo "host_timezone: America/Chicago" > \
> group_vars/na_datacenter/timezone.yml
[student@workstation role-system]$ echo "host_timezone: Europe/Helsinki" > \
> group_vars/europe_datacenter/timezone.yml

run the playbook

[student@workstation role-system]$ ansible-navigator run \
> -m stdout configure_time.yml

examine the result:

[student@workstation role-system]$ ssh servera date
Tue Sep 10 07:43:33 PM CDT 2024
[student@workstation role-system]$ ssh serverb date
Wed Sep 10 03:43:41 AM EEST 2024

Chapter 7 Example

[student@workstation role-review]$ ll
total 1540
-rw-r--r--.  1 student student    232 Sep 11 10:35 ansible.cfg
drwxr-xr-x. 10 student student    154 Sep 11 21:46 apache.developer_configs
-rw-r--r--.  1 student student 143360 Sep 11 10:35 apache.tar
drwxr-xr-x.  3 student student     33 Sep 11 10:42 collections
-rw-r--r--.  1 student student    595 Sep 11 10:35 developer.conf.j2
-rw-r--r--.  1 student student   1566 Sep 11 10:35 developer_tasks.yml
-rw-r--r--.  1 student student     83 Sep 11 10:35 inventory
-rw-r--r--.  1 student student    396 Sep 11 10:35 jdoe2.key.pub
-rw-r--r--.  1 student student    396 Sep 11 10:35 jdoe.key.pub
-rw-r--r--.  1 student student 808333 Sep 11 10:35 redhat-rhel_system_roles-1.19.3.tar.gz
-rw-r--r--.  1 student student    231 Sep 11 10:35 selinux.yml
-rw-r--r--.  1 student student    138 Sep 11 10:35 web_developers.yml
-rw-r--r--.  1 student student    715 Sep 11 22:49 web_dev_server.yml

[student@workstation role-review]$ cat ansible.cfg 
[defaults]
inventory=./inventory
remote_user=devops
collections_paths=./collections:~/.ansible/collections:/usr/share/ansible/collections

[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False
[student@workstation role-review]$ 
[student@workstation role-review]$ 
[student@workstation role-review]$ cat inventory 
[controlnode]
workstation.lab.example.com

[dev_webserver]
servera.lab.example.com
[student@workstation role-review]$ 
[student@workstation role-review]$ 
[student@workstation role-review]$ cat developer.conf.j2 
#This is an Ansible Template
# 'item' is a user from the web_developers user list,
#  and defines:
#    - user_port
#    - username
#    - name

Listen {{ item['user_port'] }}

<VirtualHost *:{{ item['user_port'] }}>
    ServerName {{ inventory_hostname }}

    ErrorLog "logs/{{ item['username'] }}_error_log"
    CustomLog "logs/{{ item['username'] }}_access_log" common

    DocumentRoot /srv/{{ item['username'] }}/www


    <Directory /srv/{{ item['username'] }}/www >
        Options FollowSymLinks

        AllowOverride All
        Require all granted

    </Directory>

</VirtualHost>

[student@workstation role-review]$ 
[student@workstation role-review]$ 
[student@workstation role-review]$ cat developer_tasks.yml 
---
# tasks file for apache.developer_configs

- name: Create user accounts
  ansible.builtin.user:
    name: "{{ item['username'] }}"
    state: present
  loop: "{{ web_developers }}"

#Allows 'student' user to login to
# any of the user accounts - for the lab only.
# Jinja syntax for 'key' keyword available from the documentation.
- name: Give student access to all accounts
  ansible.posix.authorized_key:
    user: "{{ item['username'] }}"
    state: present
    key: "{{ lookup('file', '/home/student/role-review/'+ item['username'] + '.key.pub') }}"
  loop: "{{ web_developers }}"

- name: Create content directory
  ansible.builtin.file:
    path: /srv/{{ item['username'] }}/www
    state: directory
    owner: "{{ item['username'] }}"
  loop: "{{ web_developers }}"

- name: Create skeleton index.html if needed
  ansible.builtin.copy:
    content: "This is index.html for user: {{ item['name'] }} ({{ item['username'] }})\n"
    dest: /srv/{{ item['username'] }}/www/index.html
    force: false
    owner: "{{ item['username'] }}"
    group: root
    mode: 0664
  loop: "{{ web_developers }}"


- name: Set firewall port
  ansible.posix.firewalld:
    port: "{{ item['user_port'] }}/tcp"
    permanent: true
    state: enabled
  loop: "{{ web_developers }}"
  notify: restart firewalld

- name: Copy Per-Developer Config files
  ansible.builtin.template:
    src: developer.conf.j2
    dest: "/etc/httpd/conf.d/developer_{{ item['username'] }}.conf"
    owner: root
    group: root
    mode: 0644
  loop: "{{ web_developers }}"
  notify: restart apache

[student@workstation role-review]$ 

# install rhel roles
[student@workstation role-review]$ ansible-galaxy collection \
> install -p collections/ redhat-rhel_system_roles-1.19.3.tar.gz

[student@workstation role-review]$ ansible-galaxy collection list

#Make sure to install any roles that are dependencies of roles in your playbook.
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 [email protected]:infra/apache, use version v1.4, and name it infra.apache locally.

# create roles dir
[student@workstation role-review]$ mkdir -v roles

roles/requirements.yml file contains the following content:

- name: infra.apache
  src: [email protected]:infra/apache
  scm: git
  version: v1.4
[student@workstation role-review]$ ansible-galaxy role install \
> -r roles/requirements.yml -p roles

Initialize a new role named apache.developer_configs in the roles subdirectory.

[student@workstation roles]$ ansible-galaxy role init apache.developer_configs

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.

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.

dependencies:
  - name: infra.apache
    src: [email protected]:infra/apache
    scm: git
    version: v1.4

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.

[student@workstation role-review]$ mv -v developer_tasks.yml \
> roles/apache.developer_configs/tasks/main.yml

[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'

Review the web_developers.yml file.

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

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

[student@workstation role-review]$ mkdir -pv group_vars/dev_webserver

[student@workstation role-review]$ mv -v web_developers.yml \
> group_vars/dev_webserver/

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

---
- name: Configure Dev Web Server
  hosts: dev_webserver
  force_handlers: true
  roles:
    - apache.developer_configs

run the playbook

[student@workstation role-review]$ ansible-navigator run \
> -m stdout web_dev_server.yml

...
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"}
...
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 redhat.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 redhat.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.

[student@workstation role-review]$ cat selinux.yml
---
# variables used by redhat.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'
---
- name: Configure Dev Web Server
  hosts: dev_webserver
  force_handlers: true
  roles:
    - apache.developer_configs
  pre_tasks:
    - name: Verify SELinux configuration
      block:
        - name: Apply SELinux role
          ansible.builtin.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
          ansible.builtin.include_role:
            name: redhat.rhel_system_roles.selinux

run again

[student@workstation role-review]$ ansible-navigator run \
> -m stdout web_dev_server.yml

...
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 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)

TO BE CONTINUED…

;