Manage Variables and Facts in RHEL with Ansible

AnsibleBeginner
Practice Now

Introduction

In this lab, you will learn fundamental techniques for managing variables, facts, and secrets within Ansible playbooks on a Red Hat Enterprise Linux (RHEL) system. You will explore how to make your automation more flexible and powerful by using playbook variables, gathering system information with both built-in and custom Ansible facts, and securing sensitive data like passwords using Ansible Vault.

Through a series of hands-on steps, you will build a playbook to deploy and configure an Apache web server. You will start by defining simple variables for the package name and web content, then leverage custom facts to dynamically update the web server's configuration. Finally, you will use Ansible Vault to securely create a new system user with an encrypted password, run the complete playbook, and verify that all configurations have been successfully applied.

Define and Use Playbook Variables to Deploy an Apache Web Server

In this step, you will learn how to use variables in an Ansible playbook. Variables are essential for making your automation flexible, reusable, and easier to read and maintain. Instead of hardcoding values like package names or file paths directly into your tasks, you can define them as variables and reference them throughout the playbook. We will create a simple playbook that uses variables to install the Apache web server (httpd) and deploy a basic web page.

  1. Navigate to the Project Directory

    First, ensure you are in the correct working directory. All your work for this lab will be done inside the ~/project directory, which has been created for you.

    cd ~/project
    

    Install the ansible-core package.

    sudo dnf install -y ansible-core
    
  2. Create the Ansible Playbook

    Now, let's create our playbook file. We will name it playbook.yml. You can use a command-line text editor like nano to create and edit the file.

    nano playbook.yml
    

    This command opens an empty file in the nano editor. Now, add the initial part of the playbook. This section defines the play's name, the target host (localhost, since we are running it on the same machine), and a vars section where we will define our variables.

    ---
    - name: Deploy Apache using variables
      hosts: localhost
      become: true
      vars:
        web_pkg: httpd
        web_content: "Hello from Ansible Variables"
    

    Here's a breakdown of the playbook structure:

    • hosts: localhost: Specifies that the playbook should run on the local machine.
    • become: true: Tells Ansible to use privilege escalation (equivalent to sudo) for the tasks, which is necessary for installing software.
    • vars: This is a dictionary where we define our key-value pairs for variables. We've defined web_pkg for the package name and web_content for the content of our test web page.
  3. Add Tasks to the Playbook

    Next, below the vars section, add the tasks that will use these variables. The first task will install the Apache package, and the second will create an index.html file. Add the following tasks block to your playbook.yml file while still in the nano editor.

    tasks:
      - name: Install the latest version of Apache
        ansible.builtin.dnf:
          name: "{{ web_pkg }}"
          state: latest
    
      - name: Create a basic index.html file
        ansible.builtin.copy:
          content: "{{ web_content }}"
          dest: /var/www/html/index.html
    

    Notice how we use {{ variable_name }} to reference the variables we defined earlier. This is Jinja2 templating, which Ansible uses for variables. This makes the task definitions generic; if you wanted to install Nginx instead, you would only need to change the web_pkg variable, not the task itself.

  4. Review and Save the Playbook

    Your complete playbook.yml file should now look like this. Double-check the content and indentation, as YAML is very sensitive to spacing.

    ---
    - name: Deploy Apache using variables
      hosts: localhost
      become: true
      vars:
        web_pkg: httpd
        web_content: "Hello from Ansible Variables"
      tasks:
        - name: Install the latest version of Apache
          ansible.builtin.dnf:
            name: "{{ web_pkg }}"
            state: latest
    
        - name: Create a basic index.html file
          ansible.builtin.copy:
            content: "{{ web_content }}"
            dest: /var/www/html/index.html
    

    To save the file in nano, press Ctrl+X, then Y to confirm the changes, and finally Enter to write the file with the name playbook.yml.

  5. Check the Playbook Syntax

    Before running a playbook, it's always a good practice to check its syntax for any errors.

    ansible-playbook --syntax-check playbook.yml
    

    If the syntax is correct, you will see the playbook's file path as output, confirming it's valid:

    playbook: playbook.yml
    

    If you see any errors, reopen the file with nano playbook.yml and fix them. Pay close attention to correct indentation (usually two spaces).

  6. Run the Playbook

    Now, execute the playbook. Ansible will connect to localhost, read the variables, and run the tasks.

    ansible-playbook playbook.yml
    

    You should see output indicating the successful execution of each task. The changed status means that Ansible made a modification to the system, such as installing a package or creating a file.

    PLAY [Deploy Apache using variables] *******************************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [localhost]
    
    TASK [Install the latest version of Apache] ************************************
    changed: [localhost]
    
    TASK [Create a basic index.html file] ******************************************
    changed: [localhost]
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    If you run the playbook a second time, the tasks should report ok instead of changed, because the package is already installed and the file already has the correct content. This demonstrates Ansible's idempotency.

  7. Verify the Configuration Manually

    Although the playbook has completed, you can manually verify that the tasks worked as expected. First, check if the httpd package was installed:

    rpm -q httpd
    

    The output should show the package name and version:

    httpd-2.4.57-7.el9.x86_64
    

    Next, check the content of the index.html file:

    cat /var/www/html/index.html
    

    The output should match the value of your web_content variable:

    Hello from Ansible Variables
    

    You have successfully used variables in an Ansible playbook to configure a system.

Display System Information using Ansible Facts

In this step, you will explore Ansible facts. Facts are pieces of information that Ansible gathers about the systems it manages (in this case, localhost). This information includes details like the operating system, network interfaces, memory, and much more. By default, Ansible collects facts at the beginning of every play, making them available in a special variable called ansible_facts. Using facts allows you to create dynamic playbooks that adapt to the environment they are running in.

  1. Navigate to the Project Directory

    First, ensure you are in the ~/project directory where you will create the new playbook.

    cd ~/project
    
  2. Create a Playbook to Display All Facts

    Let's start by creating a playbook that simply displays all the facts Ansible can gather about your system. This will give you an idea of the vast amount of information available. Use nano to create a new file named display_facts.yml.

    nano display_facts.yml
    

    Inside the nano editor, add the following content. This playbook targets localhost and uses the ansible.builtin.debug module to print the contents of the ansible_facts variable.

    ---
    - name: Display all Ansible facts
      hosts: localhost
      tasks:
        - name: Print all available facts
          ansible.builtin.debug:
            var: ansible_facts
    

    Save the file and exit nano by pressing Ctrl+X, then Y, and Enter.

  3. Run the Playbook

    Now, execute the playbook to see the result.

    ansible-playbook display_facts.yml
    

    The output will be very long, as Ansible collects a lot of data. It will be a large JSON structure containing all the system details. This is expected.

    PLAY [Display all Ansible facts] ***********************************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [localhost]
    
    TASK [Print all available facts] ***********************************************
    ok: [localhost] => {
        "ansible_facts": {
            "ansible_all_ipv4_addresses": [
                "172.17.0.2"
            ],
            "ansible_all_ipv6_addresses": [
                "fe80::42:acff:fe11:2"
            ],
            "ansible_apparmor": {
                "status": "disabled"
            },
            "ansible_architecture": "x86_64",
            "ansible_bios_date": "01/01/2011",
            "ansible_bios_version": "1.0",
            "ansible_cmdline": {
                "BOOT_IMAGE": "/boot/vmlinuz-5.14.0-427.16.1.el9_4.x86_64",
                "root": "UUID=...",
                "ro": true
            },
            "ansible_date_time": {
                "date": "2024-05-21",
                "day": "21",
                "epoch": "1716298855",
                ...
            },
            "ansible_distribution": "RedHat",
            "ansible_distribution_major_version": "9",
            "ansible_distribution_version": "9.4",
            ...
        }
    }
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
  4. Create a Playbook to Display Specific Facts

    Displaying all facts is useful for discovery, but in most cases, you only need specific pieces of information. Let's create another playbook, display_specific_facts.yml, to display a formatted message with just a few key facts.

    nano display_specific_facts.yml
    

    Add the following content. This playbook uses the msg parameter of the debug module to print a custom string. We access individual facts using bracket notation, like ansible_facts['distribution'].

    ---
    - name: Display specific Ansible facts
      hosts: localhost
      tasks:
        - name: Print a summary of system facts
          ansible.builtin.debug:
            msg: >
              The operating system is {{ ansible_facts['distribution'] }}
              version {{ ansible_facts['distribution_major_version'] }}.
              It has {{ ansible_facts['processor_cores'] }} processor cores and
              {{ ansible_facts['memtotal_mb'] }} MB of total memory.
    

    The > character in msg: > is a YAML feature that allows you to write a multi-line string more cleanly. Save the file and exit nano.

  5. Run the Playbook for Specific Facts

    Now, run this new playbook.

    ansible-playbook display_specific_facts.yml
    

    The output will be much cleaner and more readable, showing only the information you requested.

    PLAY [Display specific Ansible facts] ******************************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [localhost]
    
    TASK [Print a summary of system facts] *****************************************
    ok: [localhost] => {
        "msg": "The operating system is RedHat version 9. It has 2 processor cores and 3925 MB of total memory."
    }
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    This demonstrates how you can leverage Ansible facts to make your playbooks aware of the environment they are running in, allowing for more intelligent and conditional automation.

Configure the Web Server using Custom Facts from the Managed Host

In this step, you will learn how to use custom facts. While Ansible automatically gathers a wide range of standard facts, you can also define your own. These are called "local facts" or "custom facts". This is a powerful feature that allows you to provide specific information from a managed host to your playbooks, such as application settings or hardware-specific data that Ansible doesn't collect by default.

Ansible looks for custom facts in the /etc/ansible/facts.d directory on the managed host. Any file in this directory with a .fact extension will be processed. These files can be simple INI-style text files or JSON files.

  1. Create the Custom Facts Directory

    First, you need to create the directory where Ansible will look for custom fact files. Since this is a system directory, you must use sudo to create it.

    sudo mkdir -p /etc/ansible/facts.d
    

    The -p flag ensures that the command doesn't return an error if the directory already exists.

  2. Create a Custom Fact File

    Now, let's create a custom fact file to define a welcome message for our web server. We will create an INI-formatted file named web_config.fact inside the /etc/ansible/facts.d directory.

    sudo nano /etc/ansible/facts.d/web_config.fact
    

    Add the following content to the file. This defines a section [webserver] with a key welcome_message.

    [webserver]
    welcome_message = Welcome to the server configured by Custom Facts!
    

    Save the file and exit nano by pressing Ctrl+X, then Y, and Enter.

  3. Create a Playbook to Use the Custom Fact

    With the custom fact in place, we can now create a playbook that reads this fact and uses it to configure our web server's home page. In your ~/project directory, create a new playbook named configure_web.yml.

    cd ~/project
    nano configure_web.yml
    

    Add the following content to the playbook. This playbook will update the /var/www/html/index.html file with the message defined in our custom fact.

    ---
    - name: Configure web server using custom facts
      hosts: localhost
      become: true
      tasks:
        - name: Update index.html with custom message
          ansible.builtin.copy:
            content: "{{ ansible_facts.ansible_local.web_config.webserver.welcome_message }}"
            dest: /var/www/html/index.html
    

    Let's break down the variable {{ ansible_facts.ansible_local.web_config.webserver.welcome_message }}:

    • ansible_facts: The root dictionary for all facts.
    • ansible_local: The key where all custom facts are stored.
    • web_config: The name of our fact file (web_config.fact), without the extension.
    • webserver: The section name [webserver] from our INI file.
    • welcome_message: The key for the value we want to use.

    Save the file and exit nano.

  4. Run the Configuration Playbook

    Now, execute the playbook to apply the configuration.

    ansible-playbook configure_web.yml
    

    The output should show that the copy task has changed the index.html file.

    PLAY [Configure web server using custom facts] *********************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [localhost]
    
    TASK [Update index.html with custom message] ***********************************
    changed: [localhost]
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
  5. Verify the Result

    Finally, let's verify that the web page was updated correctly. Use the cat command to view the contents of the index.html file.

    cat /var/www/html/index.html
    

    The output should now display the message from your custom fact file:

    Welcome to the server configured by Custom Facts!
    

    You have successfully created a custom fact on the managed host and used it within a playbook to dynamically configure a service. This technique is incredibly useful for making your automation more flexible and data-driven.

Create a System User using Encrypted Variables with Ansible Vault

In this step, you will learn how to manage sensitive data, such as passwords or API keys, using Ansible Vault. Storing sensitive information in plain text within your playbooks is a major security risk. Ansible Vault provides a way to encrypt files or individual variables, keeping your secrets safe. You can then use these encrypted files in your playbooks, and Ansible will decrypt them at runtime when you provide the correct password.

We will create an encrypted file containing a username and a hashed password, and then use a playbook to create a new system user with these credentials.

  1. Navigate to the Project Directory

    Ensure you are in the ~/project directory for this task.

    cd ~/project
    
  2. Create an Encrypted Vault File

    We will use the ansible-vault create command to create a new, encrypted YAML file named secrets.yml. This command will prompt you to create a password for the vault. This password is required to open, edit, or use the file later.

    First, let's set the editor to nano to make it easier to work with:

    export EDITOR=nano
    

    Now create the vault file:

    ansible-vault create secrets.yml
    

    When prompted, enter a password for your vault. For this lab, let's use labex as the vault password to keep things simple. You will need to enter it twice.

    New Vault password:
    Confirm New Vault password:
    

    After you confirm the password, the command will open the secrets.yml file in the nano text editor.

  3. Add Secret Variables to the Vault File

    Inside the nano editor, which is now editing the encrypted secrets.yml file, add the following variables. We will define a username and a pre-hashed password for a new user. Using a hashed password is much more secure than storing a plain-text password.

    username: myappuser
    pwhash: $6$mysalt$QwMzWSEyCAGmz7tzVrAi5o.8k4d05i2QsfGGwmPtlJsWhGjSjCW6yFCH/OEqEsHk7GMSxqYNXu5sshxPmWyxo0
    
    • username: The name of the system user we want to create.
    • pwhash: A securely hashed password. This specific hash corresponds to the password AnsibleUserP@ssw0rd and is in a format that the ansible.builtin.user module understands.

    Save the file and exit nano (Ctrl+X, then Y, then Enter). The secrets.yml file in your ~/project directory is now encrypted. If you try to view it with cat secrets.yml, you will only see encrypted text.

  4. Create a Playbook to Use the Vault File

    Now, create a new playbook named create_user.yml that will use the variables from your encrypted secrets.yml file.

    nano create_user.yml
    

    Add the following content. The vars_files directive tells Ansible to load variables from the specified file.

    ---
    - name: Create a user from secret variables
      hosts: localhost
      become: true
      vars_files:
        - secrets.yml
      tasks:
        - name: Create the {{ username }} user
          ansible.builtin.user:
            name: "{{ username }}"
            password: "{{ pwhash }}"
            state: present
    

    This playbook will create a user with the name and password hash defined in secrets.yml. Save the file and exit nano.

  5. Run the Playbook with the Vault Password

    To run a playbook that uses a vaulted file, you must provide the vault password. You can do this interactively using the --ask-vault-pass flag.

    ansible-playbook --ask-vault-pass create_user.yml
    

    Ansible will prompt you for the vault password. Enter labex (the password you set in step 2).

    Vault password:
    

    After you provide the correct password, Ansible will decrypt the file in memory and run the playbook. You should see the following output, indicating the user was created.

    PLAY [Create a user from secret variables] *************************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [localhost]
    
    TASK [Create the myappuser user] ***********************************************
    changed: [localhost]
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
  6. Verify the User was Created

    You can confirm that the myappuser was successfully created on the system by using the id command.

    id myappuser
    

    If the user exists, you will see their user ID (uid) and group ID (gid) information.

    uid=1002(myappuser) gid=1002(myappuser) groups=1002(myappuser)
    

    This confirms that you have successfully used Ansible Vault to manage sensitive data for your automation tasks.

Run a Playbook with a Vault Password File to Apply Configurations

In this step, you will learn a more automated way to provide the vault password to Ansible. In the previous step, you used --ask-vault-pass to enter the password interactively. While this is secure, it's not suitable for automated environments like CI/CD pipelines where no user is present to type a password.

The solution is to use a vault password file. This is a simple text file that contains the vault password. You can then reference this file when running your playbook, and Ansible will read the password from it automatically. For security, it is crucial to restrict the permissions of this password file so that only authorized users can read it.

  1. Navigate to the Project Directory

    Make sure you are in the ~/project directory where your playbook and vault file are located.

    cd ~/project
    
  2. Create the Vault Password File

    Let's create a file to store our vault password. We will name it vault_pass.txt. We can use the echo command to create the file and write the password (labex) into it in a single step.

    echo "labex" > vault_pass.txt
    

    You can verify the file's content with cat:

    cat vault_pass.txt
    

    The output should be:

    labex
    
  3. Secure the Password File

    Storing a password in a plain text file is risky. You must restrict its file permissions to protect it. The chmod command allows you to change file permissions. We will set the permissions to 600, which means only the file owner (in this case, the labex user) has read and write permissions. No other users on the system will be able to access it.

    chmod 600 vault_pass.txt
    

    You can verify the new permissions using the ls -l command:

    ls -l vault_pass.txt
    

    The output should start with -rw-------, confirming the restricted permissions.

    -rw-------. 1 labex labex 6 May 21 14:30 vault_pass.txt
    
  4. Modify the Playbook to Add a User to a Group

    Let's modify our create_user.yml playbook to perform an additional action. We will add the myappuser to the wheel group, which on many systems grants administrative (sudo) privileges. This will demonstrate running a playbook that makes a change to an existing configuration.

    First, open the create_user.yml playbook for editing.

    nano create_user.yml
    

    Modify the ansible.builtin.user task to include the groups and append parameters.

    ---
    - name: Create a user from secret variables
      hosts: localhost
      become: true
      vars_files:
        - secrets.yml
      tasks:
        - name: Create the {{ username }} user and add to wheel group
          ansible.builtin.user:
            name: "{{ username }}"
            password: "{{ pwhash }}"
            state: present
            groups: wheel
            append: true
    
    • groups: wheel: Specifies the group to add the user to.
    • append: true: Ensures that the user is added to this group without removing them from any other groups they might belong to.

    Save the file and exit nano.

  5. Run the Playbook with the Vault Password File

    Now, run the playbook again. This time, instead of --ask-vault-pass, use the --vault-password-file option (or its shorter alias --vault-pass-file) to specify the path to your password file.

    ansible-playbook --vault-password-file vault_pass.txt create_user.yml
    

    Ansible will now run without prompting for a password because it reads it directly from vault_pass.txt. You should see output indicating that the user's configuration was changed.

    PLAY [Create a user from secret variables] *************************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [localhost]
    
    TASK [Create the myappuser user and add to wheel group] ************************
    changed: [localhost]
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    The changed status confirms that Ansible modified the user by adding them to the wheel group.

  6. Verify the User's Group Membership

    Finally, verify that myappuser is now a member of the wheel group. You can do this with the groups command.

    groups myappuser
    

    The output should show both the user's primary group (myappuser) and the wheel group.

    myappuser : myappuser wheel
    

    You have successfully used a vault password file to run a playbook non-interactively, a key skill for automating secure workflows.

Verify the Web Server and User Configuration

In this final step, you will consolidate your learning by creating a dedicated verification playbook. So far, you have been manually checking the results of your playbooks using standard Linux commands like cat, id, and groups. A more powerful and repeatable approach is to use Ansible itself to audit and validate the state of your system.

This playbook will act as a test suite, programmatically checking that the web server is installed, the web page has the correct content, and the system user exists with the proper group membership. This demonstrates how Ansible can be used not just for configuration management, but also for compliance and state validation.

  1. Navigate to the Project Directory

    First, ensure you are in the ~/project directory.

    cd ~/project
    
  2. Create the Verification Playbook

    Let's create a new playbook named verify_config.yml. This playbook will contain a series of tasks that check the configurations you applied in the previous steps.

    nano verify_config.yml
    
  3. Add Tasks to Verify the Configuration

    Inside the nano editor, add the following content. We will build this playbook with several tasks, each one designed to assert a specific condition is true. If any assertion fails, the playbook will stop and report an error, immediately telling you what is wrong.

    ---
    - name: Verify system configuration
      hosts: localhost
      become: true
      tasks:
        - name: Check if httpd package is installed
          ansible.builtin.dnf:
            list: httpd
          register: httpd_pkg_info
    
        - name: Assert that httpd is installed
          ansible.builtin.assert:
            that:
              - httpd_pkg_info.results | length > 0
            fail_msg: "Apache (httpd) package is not installed."
            success_msg: "Apache (httpd) package is installed."
    
        - name: Read the content of the index.html file
          ansible.builtin.slurp:
            src: /var/www/html/index.html
          register: index_file
    
        - name: Assert that the web page content is correct
          ansible.builtin.assert:
            that:
              - "'Custom Facts' in (index_file.content | b64decode)"
            fail_msg: "Web page content is incorrect."
            success_msg: "Web page content is correct."
    
        - name: Check if myappuser exists
          ansible.builtin.getent:
            database: passwd
            key: myappuser
          register: user_info
    
        - name: Assert that myappuser exists
          ansible.builtin.assert:
            that:
              - user_info.ansible_facts.getent_passwd['myappuser'] is defined
            fail_msg: "User 'myappuser' does not exist."
            success_msg: "User 'myappuser' exists."
    
        - name: Query the wheel group members
          ansible.builtin.getent:
            database: group
            key: wheel
          register: wheel_group_info
    
        - name: Assert that myappuser is in the wheel group
          ansible.builtin.assert:
            that:
              - "'myappuser' in (wheel_group_info.ansible_facts.getent_group['wheel'][2] | default('') | split(','))"
            fail_msg: "User 'myappuser' is not in the wheel group."
            success_msg: "User 'myappuser' is in the wheel group."
    

    Let's review the key modules used here:

    • ansible.builtin.dnf with list: This checks for a package and registers the result.
    • ansible.builtin.slurp: This "slurps" up the entire content of a file from the remote host. The content is base64-encoded for safe transport.
    • ansible.builtin.getent: This is a safe way to query system databases like passwd and group. The results are stored under ansible_facts, so access the returned data through keys such as user_info.ansible_facts.getent_passwd.
    • ansible.builtin.assert: This is the core of our verification. It checks if a given condition is true. If not, it fails the play. We provide custom success and failure messages.
    • b64decode: This is a Jinja2 filter used to decode the base64 content we got from the slurp module.

    Notice that we query the passwd and group databases separately. This keeps the user existence check and the wheel group membership check aligned with the actual data returned by getent.

    Save the file and exit nano (Ctrl+X, Y, Enter).

  4. Run the Verification Playbook

    Now, execute your verification playbook. Since it doesn't use any vaulted files, you don't need to provide a password.

    ansible-playbook verify_config.yml
    

    If all your previous steps were completed correctly, the playbook will run successfully, and you will see the custom success message for each assertion.

    PLAY [Verify system configuration] *********************************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [localhost]
    
    TASK [Check if httpd package is installed] *************************************
    ok: [localhost]
    
    TASK [Assert that httpd is installed] ******************************************
    ok: [localhost] => {
        "changed": false,
        "msg": "Apache (httpd) package is installed."
    }
    
    TASK [Read the content of the index.html file] *********************************
    ok: [localhost]
    
    TASK [Assert that the web page content is correct] *****************************
    ok: [localhost] => {
        "changed": false,
        "msg": "Web page content is correct."
    }
    
    TASK [Check if myappuser exists] ***********************************************
    ok: [localhost]
    
    TASK [Assert that myappuser exists] ********************************************
    ok: [localhost] => {
        "changed": false,
        "msg": "User 'myappuser' exists."
    }
    
    TASK [Query the wheel group members] *******************************************
    ok: [localhost]
    
    TASK [Assert that myappuser is in the wheel group] *****************************
    ok: [localhost] => {
        "changed": false,
        "msg": "User 'myappuser' is in the wheel group."
    }
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=9    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    Congratulations! You have successfully used Ansible to define variables, gather system facts, manage secrets with Vault, and finally, to verify the state of your system in an automated way.

Summary

In this lab, you learned to manage different types of data within Ansible playbooks to configure a RHEL system. You began by defining and using standard playbook variables to flexibly install and configure an Apache web server. Following this, you explored how to leverage Ansible's built-in facts to display system information, providing a foundation for creating dynamic and host-aware automation tasks.

Building on this, you configured the web server further by creating and utilizing custom facts from the managed host. To handle sensitive information securely, you used Ansible Vault to encrypt a user password, created a new system user with this encrypted variable, and executed the playbook non-interactively with a vault password file. The lab concluded by verifying that both the web server and the new system user were configured correctly, confirming the successful application of all learned concepts.