Bookmark this page

Lab: Managing Linux Hosts and Using System Roles

In this review, on managed hosts running Red Hat Enterprise Linux, you write playbooks that use two system roles to set up new storage with a logical volume and configure network interfaces, and use modules to set up a Cron job and a user with Sudo privileges.

Outcomes

  • Use the redhat.rhel_system_roles.storage role to create, format, and persistently mount an LVM volume on a managed host.

  • Create a user with sudo access.

  • Configure network settings on managed hosts, and collect network-related Ansible facts.

  • Schedule a Cron job.

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

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

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

Specifications

  • Use the /home/student/review-cr3 project directory to perform this activity.

  • Install the redhat.rhel_system_roles Ansible Content Collection in the collections subdirectory of your project directory.

  • Write a playbook named storage.yml that uses the redhat.rhel_system_roles.storage system role to configure logical volumes for the managed hosts in the webservers group specified by the inventory file in your project directory. The playbook must set up the logical volumes as follows:

    • Create a volume group named vg_web on the /dev/vdb storage device.

    • Create a logical volume named lv_content, 128 MB in size, from the vg_web volume group, format it with an XFS file system, and mount it on the /var/www/html/content directory.

    • Create a logical volume named lv_uploads, 256 MB in size, from the vg_web volume group, format it with an XFS file system, and mount it on the /var/www/html/uploads directory.

  • Run the storage.yml playbook to configure the storage.

  • Write a playbook named dev-users.yml that creates the webdev user on managed hosts in the webservers inventory group. It must do so as follows:

    • The webdev password must be set by using the pwhash variable provided in the pass-vault.yml file, which is encrypted with Ansible Vault. The Ansible Vault password for the pass-vault.yml file is redhat.

    • The webdev user must also be a member of the webdev group.

    • Members of the webdev group must be able to run sudo commands without a password prompt. Create or modify the sudoers file in /etc/sudoers.d/webdev by using the ansible.builtin.lineinfile module. Any edits to the sudoers file should be validated before changes are applied.

  • Run the dev-users.yml playbook. Verify that the webdev user can log in to a managed host in the webservers group, and can execute commands as root on that host by using sudo without a password.

  • Write a playbook named network.yml that uses the redhat.rhel_system_roles.network system role to configure the eth1 network interface on the managed hosts in the webservers inventory group with the 172.25.250.45/24 IP address.

  • Run the network.yml playbook.

  • Write a playbook named log-rotate.yml to set up a system Cron job as follows:

    • Use the ansible.builtin.cron module to create the /etc/cron.d/rotate_web system Cron job on managed hosts in the webservers inventory group.

    • The job must run as the devops user every night at midnight.

    • The job must run the logrotate -f /etc/logrotate.d/httpd command to rotate the logs in the /var/log/httpd/ directory.

  • Run the log-rotate.yml playbook.

  • Write a playbook named site.yml that imports the four playbooks that you wrote in this activity, in the following order:

    • storage.yml

    • dev-users.yml

    • network.yml

    • log-rotate.yml

  • Run the site.yml playbook, and ensure that there are no errors.

  1. Install the rhel_system_roles collection from the redhat-rhel_system_roles-1.19.3.tar.gz file into the collections directory in the project directory.

    1. Change into the review-cr3 directory.

      [student@workstation ~]$ cd ~/review-cr3
      [student@workstation review-cr3]$
    2. Use the ansible-galaxy command to install the rhel_system_roles collection from the redhat-rhel_system_roles-1.19.3.tar.gz file into the collections directory.

      [student@workstation review-cr3]$ ansible-galaxy collection install \
      > ./redhat-rhel_system_roles-1.19.3.tar.gz -p collections
      ...output omitted...
      redhat.rhel_system_roles:1.19.3 was installed successfully
  2. Write a playbook named storage.yml that uses the redhat.rhel_system_roles.storage system role to configure logical volumes for the managed hosts in the webservers group specified by the inventory file in your project directory. The playbook must set up the logical volumes as follows:

    • Create a volume group named vg_web on the /dev/vdb storage device.

    • Create a logical volume named lv_content, 128 MB in size, from the vg_web volume group, format it with an XFS file system, and mount it on the /var/www/html/content directory.

    • Create a logical volume named lv_uploads, 256 MB in size, from the vg_web volume group, format it with an XFS file system, and mount it on the /var/www/html/uploads directory.

    1. Create the storage.yml playbook that targets the webservers group and applies the redhat.rhel_system_roles.storage role.

      ---
      - name: Configure storage on webservers
        hosts: webservers
      
        roles:
          - name: redhat.rhel_system_roles.storage
    2. Define the storage_pools variable. Set the volume group name to vg_web, the type to lvm, and the disks to use the /dev/vdb device.

            storage_pools:
              - name: vg_web
                type: lvm
                disks:
                  - /dev/vdb
    3. Define the volumes variable within storage_pools.

                volumes:
    4. Create a logical volume within volumes with the name lv_content, a size of 128 MB, a file system type of xfs, and a mount point of /var/www/html/content.

                  - name: lv_content
                    size: 128m
                    mount_point: "/var/www/html/content"
                    fs_type: xfs
                    state: present
    5. Create another logical volume within volumes with the name lv_uploads, a size of 256 MB, a file system type of xfs, and a mount point of /var/www/html/uploads.

                  - name: lv_uploads
                    size: 256m
                    mount_point: "/var/www/html/uploads"
                    fs_type: xfs
                    state: present

      The final playbook should consist of the following content:

      ---
      - name: Configure storage on webservers
        hosts: webservers
      
        roles:
          - name: redhat.rhel_system_roles.storage
            storage_pools:
              - name: vg_web
                type: lvm
                disks:
                  - /dev/vdb
                volumes:
                  - name: lv_content
                    size: 128m
                    mount_point: "/var/www/html/content"
                    fs_type: xfs
                    state: present
                  - name: lv_uploads
                    size: 256m
                    mount_point: "/var/www/html/uploads"
                    fs_type: xfs
                    state: present
    6. Run the storage.yml playbook to configure the storage.

      [student@workstation review-cr3]$ ansible-navigator run \
      > -m stdout storage.yml
      
      PLAY [Configure storage on webservers] ************************************************
      
      TASK [Gathering Facts] ****************************************************************
      ok: [servera.lab.example.com]
      ...output omitted...
      TASK [redhat.rhel_system_roles.storage : make sure blivet is available] ***************
      changed: [servera.lab.example.com]
      ...output omitted...
      TASK [redhat.rhel_system_roles.storage : set up new/current mounts] *******************
      changed: [servera.lab.example.com] => (item={'src': '/dev/mapper/vg_web-lv_content', 'path': '/var/www/html/content', 'fstype': 'xfs', 'opts': 'defaults', 'dump': 0, 'passno': 0, 'state': 'mounted'})
      changed: [servera.lab.example.com] => (item={'src': '/dev/mapper/vg_web-lv_uploads', 'path': '/var/www/html/uploads', 'fstype': 'xfs', 'opts': 'defaults', 'dump': 0, 'passno': 0, 'state': 'mounted'})
      ...output omitted...
      PLAY RECAP *********************************************************************
      servera.lab.example.com    : ok=21   changed=3    unreachable=0    failed=0    skipped=12   rescued=0    ignored=0
  3. Write a playbook named dev-users.yml that creates the user webdev on managed hosts in the webservers inventory group. It must do so as follows:

    • The webdev password must be set by using the pwhash variable provided in the pass-vault.yml file, which is encrypted with Ansible Vault. The Ansible Vault password for the pass-vault.yml file is redhat.

    • The webdev user must be a member of the webdev group.

    • Members of the webdev group must be able to run sudo commands without a password prompt. Create or modify the sudoers file in /etc/sudoers.d/webdev by using the ansible.builtin.lineinfile module. Any edits to the sudoers file should be validated before changes are applied.

    1. Start writing the dev-users.yml playbook. Define a single play in the playbook that targets the webservers host group.

      Add a vars_files key to access the pass-vault.yml file.

      Add the tasks key to the playbook.

      ---
      - name: Create local users
        hosts: webservers
        vars_files:
          - pass-vault.yml
        tasks:
    2. Add the first task to the playbook. Use the ansible.builtin.group module to create the webdev group on the managed host.

      The task should consist of the following content:

          - name: Add webdev group
            ansible.builtin.group:
              name: webdev
              state: present
    3. Add a second task to the playbook that uses the ansible.builtin.user module. Use the ansible.builtin.user module to create the webdev user on the managed host.

      The task should consist of the following content:

          - name: Create user accounts
            ansible.builtin.user:
              name: webdev
              groups: webdev
              password: "{{ pwhash }}"
    4. Add a third task to the play that uses the ansible.builtin.lineinfile module to modify the sudo configuration file and allow the webdev group members to use sudo without a password on the managed host. Use the validate parameter to validate the new sudoers entry.

      The task should consist of the following content:

          - name: Modify sudo config to allow webdev members sudo without a password
            ansible.builtin.lineinfile:
              path: /etc/sudoers.d/webdev
              state: present
              create: yes
              mode: 0440
              line: "%webdev ALL=(ALL) NOPASSWD: ALL"
              validate: /usr/sbin/visudo -cf %s

      The completed playbook should consist of the following content:

      ---
      - name: Create local users
        hosts: webservers
        vars_files:
          - pass-vault.yml
        tasks:
          - name: Add webdev group
            ansible.builtin.group:
              name: webdev
              state: present
      
          - name: Create user accounts
            ansible.builtin.user:
              name: webdev
              groups: webdev
              password: "{{ pwhash }}"
      
          - name: Modify sudo config to allow webdev members sudo without a password
            ansible.builtin.lineinfile:
              path: /etc/sudoers.d/webdev
              state: present
              create: yes
              mode: 0440
              line: "%webdev ALL=(ALL) NOPASSWD: ALL"
              validate: /usr/sbin/visudo -cf %s
  4. Run the dev-users.yml playbook. Verify that the webdev user can log in to a managed host, and execute commands using sudo without a password.

    1. Run the dev-users.yml playbook:

      [student@workstation review-cr3]$ ansible-navigator run \
      > -m stdout --playbook-artifact-enable false dev-users.yml --vault-id @prompt
      Vault password (default): redhat
      
      PLAY [Create local users] ****************************************************
      
      TASK [Gathering Facts] *******************************************************
      ok: [servera.lab.example.com]
      
      TASK [Add webdev group] ******************************************************
      changed: [servera.lab.example.com]
      
      TASK [Create user accounts] **************************************************
      changed: [servera.lab.example.com]
      
      TASK [Modify sudo config to allow webdev members sudo without a password] ****
      changed: [servera.lab.example.com]
      
      PLAY RECAP *******************************************************************
      servera.lab.example.com    : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    2. Use SSH as the webdev user and log in to the servera.lab.example.com server.

      [student@workstation review-cr3]$ ssh webdev@servera
      ...output omitted...
      [webdev@servera ~]$
    3. Change to the root user and confirm that no password is required.

      [webdev@servera ~]$ sudo -i
      [root@servera ~]#
    4. Log out from servera.lab.example.com.

      [root@servera ~]# exit
      logout
      [webdev@servera ~]$ exit
      logout
      Connection to servera closed.
      [student@workstation review-cr3]$
  5. Write a playbook named network.yml that uses the redhat.rhel_system_roles.network system role to configure the eth1 network interface on managed hosts in the webservers inventory group with the 172.25.250.45/24 IP address.

    1. Create the network.yml playbook with one play that targets the webservers inventory group. Include the redhat.rhel_system_roles.network role in the roles section of the play.

      ---
      - name: NIC Configuration
        hosts: webservers
      
        roles:
          - redhat.rhel_system_roles.network
    2. The /usr/share/doc/rhel-system-roles/network/README.md file lists all available variables and options for the rhel-system-roles.network role.

      Review the Setting the IP configuration: section in the README.md file and determine which variable is required to configure the eth1 network interface with the 172.25.250.45 IP address.

      [student@workstation review-cr3]$ cat \
      > collections/ansible_collections/redhat/rhel_system_roles/roles/network/README.md
      ...output omitted...
      Setting the IP configuration:
      
      ```yaml
      network_connections:
        - name: eth0
          type: ethernet
          ip:
            route_metric4: 100
            dhcp4: no
            #dhcp4_send_hostname: no
            gateway4: 192.0.2.1
      
            dns:
              - 192.0.2.2
              - 198.51.100.5
            dns_search:
              - example.com
              - subdomain.example.com
            dns_options:
              - rotate
              - timeout:1
      
            route_metric6: -1
            auto6: no
            gateway6: 2001:db8::1
      
            address:
              - 192.0.2.3/24
              - 198.51.100.3/26
              - 2001:db8::80/7
      
      ...output omitted...
    3. Create the group_vars/webservers subdirectory.

      [student@workstation review-cr3]$ mkdir -pv group_vars/webservers
      mkdir: created directory 'group_vars'
      mkdir: created directory 'group_vars/webservers'
    4. Create a network.yml file to define the required role variable.

      Because the variable value applies to the hosts on the webservers host group, you need to create the file in the group_vars/webservers directory.

      Add the variable definition to support the configuration of the eth1 network interface.

      When completed, the file contains the following content:

      ---
      network_connections:
        - name: eth1
          type: ethernet
          ip:
            address:
              - 172.25.250.45/24
    5. Run the network.yml playbook to configure the eth1 network interface on managed hosts in the webservers inventory group.

      [student@workstation review-cr3]$ ansible-navigator run \
      > -m stdout network.yml
      
      PLAY [NIC Configuration] *******************************************************
      
      TASK [Gathering Facts] *********************************************************
      ok: [servera.lab.example.com]
      
      TASK [redhat.rhel_system_roles.network : Check which services are running] *****
      ok: [servera.lab.example.com]
      
      TASK [redhat.rhel_system_roles.network : Check which packages are installed] ***
      ok: [servera.lab.example.com]
      
      TASK [redhat.rhel_system_roles.network : Print network provider] ***************
      ok: [servera.lab.example.com] => {
          "msg": "Using network provider: nm"
      }
      
      TASK [redhat.rhel_system_roles.network : Install packages] *********************
      skipping: [servera.lab.example.com]
      
      TASK [redhat.rhel_system_roles.network : Restart NetworkManager due to wireless or team interfaces] ***
      skipping: [servera.lab.example.com]
      
      TASK [redhat.rhel_system_roles.network : Enable and start NetworkManager] ******
      ok: [servera.lab.example.com]
      
      TASK [redhat.rhel_system_roles.network : Enable and start wpa_supplicant] ******
      skipping: [servera.lab.example.com]
      
      TASK [redhat.rhel_system_roles.network : Enable network service] ***************
      skipping: [servera.lab.example.com]
      
      TASK [redhat.rhel_system_roles.network : Ensure initscripts network file dependency is present] ***
      skipping: [servera.lab.example.com]
      
      TASK [redhat.rhel_system_roles.network : Configure networking connection profiles] ***
      changed: [servera.lab.example.com]
      
      TASK [redhat.rhel_system_roles.network : Show stderr messages] *****************
      ok: [servera.lab.example.com] => {
          "__network_connections_result.stderr_lines": [
              "[002] <info>  #0, state:None persistent_state:present, 'eth1': add connection eth1, 3ddd5197-7a96-4d05-abec-c586089145ff"
          ]
      }
      
      TASK [redhat.rhel_system_roles.network : Show debug messages] ******************
      skipping: [servera.lab.example.com]
      
      TASK [redhat.rhel_system_roles.network : Re-test connectivity] *****************
      ok: [servera.lab.example.com]
      
      PLAY RECAP *********************************************************************
      servera.lab.example.com    : ok=10   changed=1    unreachable=0    failed=0    skipped=12   rescued=0    ignored=0
  6. Write a playbook named log-rotate.yml to set up a system Cron job as follows:

    • Use the ansible.builtin.cron module to create the /etc/cron.d/rotate_web system Cron job on managed hosts in the webservers inventory group.

    • The job must run as the devops user every night at midnight.

    • The job must run the logrotate -f /etc/logrotate.d/httpd command to rotate the logs in the /var/log/httpd/ directory.

    Run the log-rotate.yml playbook.

    1. Create the log-rotate.yml playbook and add the lines needed to start the play. It must target the managed hosts in the webservers group and enable privilege escalation.

      ---
      - name: Recurring cron job
        hosts: webservers
        become: true
    2. Define a task that uses the ansible.builtin.cron module to schedule a recurring Cron job.

        tasks:
          - name: Crontab file exists
            ansible.builtin.cron:
              name: Rotate HTTPD logs
    3. Configure the job to run every night at midnight.

              minute: "0"
              hour: "0"
              weekday: "*"
    4. Use the cron_file parameter to create the job in the /etc/cron.d/rotate_web system Cron job file instead of an individual user's crontab in /var/spool/cron/.

      Using a relative path places the file in the /etc/cron.d directory.

      If the cron_file parameter is used, you must also specify the user parameter to fill in the field that specifies which user runs the system Cron job.

              user: devops
              job: "logrotate -f /etc/logrotate.d/httpd"
              cron_file: rotate_web
              state: present
    5. When completed, the playbook must contain the following content. Review the playbook for accuracy.

      ---
      - name: Recurring cron job
        hosts: webservers
        become: true
      
        tasks:
          - name: Crontab file exists
            ansible.builtin.cron:
              name: Rotate HTTPD logs
              minute: "0"
              hour: "0"
              weekday: "*"
              user: devops
              job: "logrotate -f /etc/logrotate.d/httpd"
              cron_file: rotate_web
              state: present
    6. Run the ansible-navigator run --syntax-check command to verify the playbook syntax. Correct any errors before moving to the next step.

      [student@workstation review-cr3]$ ansible-navigator run \
      > -m stdout log-rotate.yml --syntax-check
      playbook: /home/student/review-cr3/log-rotate.yml
    7. Run the log-rotate.yml playbook.

      [student@workstation review-cr3]$ ansible-navigator run \
      > -m stdout log-rotate.yml
      
      PLAY [Recurring cron job] *************************************************
      
      TASK [Gathering Facts] ****************************************************
      ok: [servera.lab.example.com]
      
      TASK [Crontab file exists] ************************************************
      changed: [servera.lab.example.com]
      
      PLAY RECAP ****************************************************************
      servera.lab.example.com    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    8. Run the following command to verify that the /etc/cron.d/rotate_web Cron file exists, and its content is correct. Exit servera.lab.example.com when done.

      [student@workstation review-cr3]$ ssh devops@servera \
      > "cat /etc/cron.d/rotate_web"
      #Ansible: Rotate HTTPD logs
      0 0 * * * devops logrotate -f /etc/logrotate.d/httpd
      [student@workstation review-cr3]$
  7. Write a playbook named site.yml that imports the four playbooks that you wrote in the preceding steps, in the order in which they were created:

    • storage.yml

    • dev-users.yml

    • network.yml

    • log-rotate.yml

    Run the site.yml playbook, and ensure that there are no errors.

    1. Start writing the site.yml playbook. Add an import_playbook statement for each playbook created in the previous steps.

      ---
      - import_playbook: storage.yml
      - import_playbook: dev-users.yml
      - import_playbook: network.yml
      - import_playbook: log-rotate.yml
    2. Run the site.yml playbook.

      [student@workstation review-cr3]$ ansible-navigator run \
      > -m stdout --playbook-artifact-enable false site.yml --vault-id @prompt
      Vault password (default): redhat
      
      ...output omitted...
      
      TASK [Crontab file exists] ************************************************
      ok: [servera.lab.example.com]
      
      PLAY RECAP ****************************************************************
      servera.lab.example.com    : ok=37   changed=0    unreachable=0    failed=0    skipped=24   rescued=0    ignored=0

Evaluation

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

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

Finish

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

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

This concludes the section.

Revision: rh294-9.0-c95c7de