Ansible Exam (RHCE 8) full solution with explanation

Comments Off on Ansible Exam (RHCE 8) full solution with explanation

 

The RHCE 8 exam is totally based on Ansible. Generally, 15-17 questions are asked to solve in the exam. In this article, I will try to solve all questions that you might face in the exam. Please ensure that you have already completed the RHCSA exam and have basic knowledge of YAML.

Question 1: Install and configure Ansible

User ismat has been created on your control node with the appropriate permissions already, do not change or modify ssh keys. Install the necessary packages to run ansible on the control node. Configure ansible.cfg to be in folder /home/ismat/ansible/ansible.cfg and configure to access remote machines via the ismat user.

All roles should be in the path /home/ismat/ansible/roles.
The inventory path should be in /home/ismat/ansible/inventory.

You will have access to 5 nodes:
node1.example.com
node2.example.com
node3.example.com
node4.example.com
node5.example.com

Configure these nodes to be in an inventory file where node1 is a member of group dev, node2 is a member of group test, node3 is a member of group proxy, node4 and node5 are members of group prod, Also, prod is a member of group webservers.

Solution:

The environment will remain ready. You just need to install the ansible in the control node by the command. But at home for practice, you need to enable an ansible repository.

sudo yum install -y ansible

For writing code easily you can install vim package also. In a real exam, it helps you a lot as you have to write everything in a text file and vim editor will show you an error if you do a mistake in yml syntax.

sudo yum -y install vim

create a file /home/ismat/ansible/inventory and a directory
/home/ismat/ansible/roles. Navigate to /home/ismat/ansible directory.

$ mkdir /home/ismat/ansible
$ touch /home/ismat/ansible/inventory
$ mkdir  /home/ismat/ansible/roles
$ touch /home/ismat/ansible.cfg
$ cd /home/ismat/ansible   

 

Now edit the inventory file ( vim inventory)

[dev]
node1.example.com
[test]
node2.example.com
[proxy]
node3.example.com
[prod]
node4.example.com
node5.example.com
[webservers:children]
prod

Edit the ansible.cfg file with the following information

[defaults]
inventory=/home/ismat/ansible/inventory
roles_path= /etc/ansible/roles:/usr/share/ansible/roles:/home/ismat/ansible/roles
remote_user= ismat
host_key_checking=false
[privilege_escalation]
become=true
become_user=root
become_method=sudo
become_ask_pass=false

Verify your installation

ansible --version

The above command’s output will shows ansible version installed and also configuration file location : config file = /home/ismat/ansible/ansible.cfg . If it comes in this way then your installation is done perfectly. Please run a command always from /home/ismat/ansible directory.

Tips: You can copy the /etc/ansible.cfg file to /home/ismat/ansible/ansible.cfg and uncomment and edit the above configuration parameters .
For practice at home install Ansible on CentOS 8 which is very straightforward. Click Install Ansible on CentOS 8 to see details.

cp /etc/ansible.cfg /home/ismat/ansible/ansible.cfg 

Question2: Repository setup

Create a file called adhoc.sh in /home/ismat/ansible which will use adhoc commands to set up a new repository. The name of the repo will be ‘EPEL’ the description ‘RHEL8’ the baseurl is ‘https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rmp’ , set gpgcheck and gpgkey is http://example.key and enable the repo

Solution:

vim adhoc.sh

#!/bin/bash
ansible all -m yum_repository -a 'name=EPEL description=RHEL8 baseurl=https://dl.fedoraproject.org/pub/epel/epelrelease-
latest-8.noarch.rmp gpgcheck=yes gpgkey=http://example.key enabled=yes'
$ chmod 777 adhoc.sh
$ ./adhoc.sh

Tips: In the real exam if you forget syntax run the following command and you will get all options and can make sure your syntax is correct.

$ ansible-doc yum_repository

Question 3: Install packages

Create a file called packages.yml in /home/ismat/ansible to install some packages for the following hosts. On dev, prod and webservers install packages httpd, mod_ssl, and mariadb. On dev only install the development tools package. Also, on dev host update all the packages to the latest.

Solution:

---
- name: Install Common Packages
  hosts: dev,prod,webservers
  tasks:
      - name: Install httpd,mod_ssl,mariadb
        yum:
         name:
            - httpd
            - mod_ssl
            - mariadb
         state: present
      - name: Install development tools package on dev host
        yum:
         name: '@Development tools'
         state: latest
        when: "'dev' in group_names"
      - name: Update all the packages on dev hosts
        yum:
         name: '*'
         state: latest
        when: "'dev' in group_names"      
...

Tips: To know how to write code for the yum module. Run command ansible-doc yum , scrolling to the bottom, and copy example code.

Question 4: Create Role including template

Create a role called sample-apache in /home/ismat/ansible/roles that enables and starts httpd, enables and starts the firewall; and allows the webserver service. Create a template called index.html.j2 which creates and serves a message from /var/www/html/index.html. Whenever the content of the file changes, restart the webserver service.
Welcome to [FQDN] on [IP]
Replace the FQDN with the fully qualified domain name and IP with the ip address of the node using ansible facts. Lastly, create a playbook in /home/ismat/ansible named apache.yml and use the role to serve the index file on webserver hosts.

Solution:

Navigate to /home/ismat/ansible/roles directory and run command

$ ansible-galaxy init sample-apache

vim sample-apache/tasks/main.yml

---
# tasks file for sample-apache
- name: Enable httpd
  service:
   name: httpd
   state: started
   enabled: yes
- name: Enable firewalld
  service: 
   name: firewalld
   state: started
   enabled: yes
- name: Allow webserver service
  firewalld:
   service: http
   state: enabled
   permanent: yes
   immediate: yes
- name: Create index file from index.html.j2
  template:
   src: index.html.j2
   dest: /var/www/html/index.html
  notify:
   - restart_webservers

vim sample-apache/templates/index.html.j2

Welcome to {{ ansible_fqdn }} on {{ ansible_default_ipv4.address }}

Tips: There is a chance you can’t remember ansible_fqdn or ansible_default_ipv4.address these kinds of facts. You can run adhoc command of the setup module to get the whole remote machine facts and save them to a file. ansible node2.example.com -m setup > facts.json. Then vim facts.json file and find the facts variables. It is extremely helpful if you are afraid of a spelling mistake.

vim sample-apache/handlers/main.yml

---
# handlers file for sample-apache
- name: restart_webservers
  service:
   name: httpd
   state: restarted

Navigate back to your ansible working directory /home/ismat/ansible and write in apache.yml ( vim apache.yml)

---
- name: Install apachec from apache-role 
  hosts: webservers
  roles: 
   - name: sample-apache
...    

Question 5: Install Role from ansible-galaxy repository

Create a file called requirements.yml in /home/ismat/ansible/roles to install two roles. The source for the first role is geerlingguy.haproxy and geerlingguy.php. Name the first haproxy-role and the second php-role. The roles should be installed in /home/ismat/ansible/roles.

Solution:

Create a file requirements.yml in /home/ismat/ansible/roles and write the following code

- name: haproxy-role
  src: geerlingguy.haproxy

- name: php-role
  src: geerlingguy.php

Navigate to the roles directory and run the following command

$ ansible-galaxy install -r requirements.yml -p /home/sandy/ansible/roles/

Check the roles were installed

$ ls *
haproxy-role php-role sample-apache

Question 6: Call Role from playbook

Use the roles from Task 5 in a file called role.yml in /home/ismat/ansible/. The haproxy-role should be used on the proxy host. And when you curl http://node3.example.com. it should display “Welcome to node4.example.com” and when you curl again “Welcome to node5.example.com” The php-role should be used on the prod host.

Solution:

vim /home/ismat/ansible/role.yml

---
- name: install haproxy and php roles
  hosts: all
  vars:
   haproxy_backend_servers:
    - name: web1
      address: node4.example.com:80
    - name: web2
      address: node5.example.com:80 
  tasks:
   - name: import hasproxy
     include_role: 
      name: haproxy-role
     when: "'proxy' in group_names"
   - name: import php
     include_role:
      name: php-role
     when: "'prod' in group_names"                 
...

Question 7: Create secret file

Create an ansible vault password file called lock.yml with the password reallysafepw in the /home/ismat/ansible directory. In the lock.yml file define two variables. One is pw_dev and the password is ‘dev’ and the other is pw_mgr and the password is ‘mgr’. Create a regular file called secret.txt which contains the password for lock.yml.

Solution:

Run the following command

$ ansible-vault create lock.yml
New Vault password:
Confirm New Vault password:
$ ansible-vault view lock.yml
Vault password:
pw_dev: dev
pw_mgr: mgr

Create a file /home/ismat/ansible/secret.txt and write reallysafepw

$ vim /home/ismat/ansible/secret.txt
$ cat /home/ismat/ansible/secret.txt
reallysafepw

Question 8: Read secret from vault

Create the users in the file users_list.yml file provided. Do this in a playbook called users.yml located at /home/ismat/ansible. The passwords for these users should be set using the lock.yml file from TASK7. When running the playbook, the lock.yml file should be unlocked with secret.txt file from Question 7.

All users with the job of ‘developer’ should be created on the dev hosts, add them to the group devops, their password should be set using the pw_dev variable. Likewise create users with the job of ‘manager’ on the proxy host and add the users to the group ‘managers’, their password should be set using the pw_mgr variable.

users_list.yml

users:
 - username: bill
   job: developer
 - username: chris
   job: manager
 - username: dave
   job: test
 - username: ethan
   job: developer  

Solution:

Write your code in /home/ismat/ansible/users.yml file

---
- name: user create
  hosts: dev,proxy
  vars_files:
   - lock.yml
   - users_list.yml
  tasks:
   - name: developer user create
     user:
      name: "{{ item.username }}"  
      group: devops
      state: present
      password: "{{ pw_dev | password_hash('sha512') }}" 
     when: item.job=="developer" and "dev" in group_names
     loop: "{{ users }}" 
   - name: manager user create
     user:
      name: "{{ item.username }}"
      group: managers
      state: present
      password: "{{ pw_mgr | password_hash('sha512') }}"
     when: item.job=="manager" and "proxy" in group_names
     loop: "{{ users }}"
...

Run the playbook as follows

$ ansible-playbook users.yml --vault-password-file secret.txt 

Question 9: Download and modify file

Create a file in /home/ismat/ansible/ called report.yml. Using this playbook, get a file called report.txt. Download the file from http://classroom.example.com to all remote hosts at /root/report.txt. Then edit the lines in the file to provide the real information of the hosts. If a disk does not exist then write NONE. The file content of report.txt is

HOST= inventory hostname
MEMORY=total memory in mb
BIOS=bios version
VDA_DISK_SIZE=disk size
VDB_DISK_SIZE=disk size

Solution:

---
- name: copy file with custom information
  hosts: all
  tasks:
   - name: get file
     get_url: 
      url: https://classroom.example.com/report.txt
      dest: /root/report.txt
   - name: chahge hostname
     lineinfile:
      path: /root/report.txt
      line: HOST={{ ansible_hostname }}
      regex: ^HOST 
      state: present 
   - name: chahge memory
     lineinfile:
      path: /root/report.txt
      line: MEMORY={{ ansible_memtotal_mb }}
      regex: ^MEMORY
      state: present 
   - name: chahge bios version
     lineinfile:
      path: /root/report.txt  
      line: BIOS={{ ansible_bios_version }}
      regex: ^BIOS
      state: present 
   - name: chahge vda
     lineinfile:
      path: /root/report.txt
      line: VDA_DISK_SIZE={% if ansible_devices.vda is defined %}{{ ansible_devices.vda.size }} {% else %} NONE {% endif %}
      regex: ^VDA_DISK_SIZE
      state: present 
   - name: chahge vdb
     lineinfile:
      path: /root/report.txt
      line: VDB_DISK_SIZE={% if ansible_devices.sda is defined %}{{ ansible_devices.sda.size }} {% else %} NONE {% endif %}
      regex: ^VDB_DISK_SIZE
      state: present 
        
...

Tips: At real exam you will not see the content of report.txt file. So first download it to your working directory by the command curl
http://dl.example.com/report.txt –output report.txt .
After the task is completed you can delete the file from your working directory

Question 10: Create file using jinja2 template

Download the jinja template from http://dl.example.com/hosts.j2 in /home/ismat/ansible/ and Edit this file so it looks like the one below. The order of the nodes doesn’t matter. Then create a playbook in /home/ismat/ansible called hosts.yml and install the template on dev node at /root/myhosts

Download the jinja template from http://dl.example.com/hosts.j2 in /home/ismat/ansible/ and edit it. Then create a playbook in /home/ismat/ansible called hosts.yml and install the template on dev node at /root/myhosts so that /root/myhosts content will be like below. The order of the nodes doesn’t matter.

127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
10.0.2.1 node1.example.com node1
10.0.2.2 node2.example.com node2
10.0.2.3 node3.example.com node3
10.0.2.4 node4.example.com node4
10.0.2.5 node5.example.com node5

Solution:

Download the template

$ curl http://dl.example.com/hosts.j2 --output hosts.j2

Edit the hosts.j2 file as follows

127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
{% for host in groups.all  %}
{{ hostvars[host].ansible_default_ipv4.address }} {{ hostvars[host].ansible_fqdn }} {{ hostvars[host].ansible_hostname }}
{% endfor %}

Write a playbook named hosts.yml

---
- name: use hosts.j2 template
  hosts: all
  tasks:
   - name: template a file to dev hosts
     template: 
      src: /home/ismat/ansible/hosts.j2
      dest: /root/myhosts
     when: "'dev' in group_names"
...

Question 11: Create LVM

In /home/ismat/ansible/ create a playbook called logvol.yml. In the play create a logical volume called lv0 and make it of size 12GiB on volume group vg0 If there is not enough space in the volume group print a message “Not enough space for logical volume” and then make a 6GiB Iv0 instead. If the volume group still doesn’t exist, create a message “Volume group doesn’t exist” Create an xfs filesystem on all lv0 logical volumes. Don’t mount the logical volume.

Write logvol.yml playbook

---
- name: create volume
  hosts: all
  tasks:
   - name: create partition
     parted: 
      device: /dev/sdb
      number: 1
      flags: [lvm]
      state: present
   - name: create vg
     lvg:
      vg: vg0
      pvs: /dev/sdb1
     when: ansible_devices.sdb.partitions.sdb1 is defined
   - name: create lv
     lvol:
      vg: vg0
      lv: lv0
      size: 12000m
     when: ansible_lvm.vgs.vg0 is defined and ((ansible_lvm.vgs.vg0.size_g | float) > 12)
   - name: send message if volume group is not large enough
     debug: 
      msg: Not enough space for logical volume
     when:  ansible_lvm.vgs.vg0 is defined and ((ansible_lvm.vgs.vg0.size_g | float) < 12)
   - name: create similiar volume
     lvol:
      vg: vg0
      lv: lv0
      size: 6000m
     when: ansible_lvm.vgs.vg0 is defined and ((ansible_lvm.vgs.vg0.size_g | float) < 12)  
   - name: create fs
     filesystem: 
      dev: /dev/vg0/lv0
      fstype: xfs
     when: ansible_lvm.vgs.vg0 is defined   
...

Question 12: Read content from custom web directory

Create a playbook called webdev.yml in /home/ismat/ansible. The playbook will create a directory /webdev on dev host. The permission of the directory is u=rwx,g=rw, other has no permission. Set group id for the folder and owner is webdev. Create a symbolic link from /webdev to /var/www/html/webdev. Serve a file from /webdev/index.html which displays the text “Development”. Curl http://node1.example.com/webdev/index.html to test

Write the playbook webdev.yml

---
- name: web development
  hosts: dev
  tasks:
   - name: create webdev user
     user:
      name: webdev
      state: present
   - name: create directory
     file: 
      path: /webdev
      state: directory
      owner: webdev
      mode: u=rwx,g=rw,0=---,g+s
   - name: create symbolic link
     file:
      src: /webdev
      path: /var/www/html/webdev  
      state: link 
   - name: create index.html
     copy:
      content: Development
      dest: /var/www/html/webdev/index.html
   - name: install selinux policy
     yum:
      name: python3-policycoreutils
      state: present
   - name: allow httpd from custom directory
     sefcontext:
      target: '/webdev(/.*)?'
      setype: httpd_sys_content_t
      state: present
   - name: restore the context
     shell: restorecon -vR /webdev         
...

Question 13: Set up NTP using Red Hat system roles

Create a playbook called timesync.yml in /home/ismat/ansible using rhel system role timesync. Set the time to use currently configured ntp with the server 0.uk.pool.ntp.org. Enable burst. Do this on all hosts.

Solution:

Install Redhat System Roles

$ sudo yum install rhel-system-roles

Check timesync role is installed properly

$ ls  -d /usr/share/ansible/roles/*.timesync
/usr/share/ansible/roles/rhel-system-roles.timesync

Write a playbook /home/ismat/ansible/timesync.yml

---
- name: use rhel system role to timesync
  hosts: all
  roles:
   - rhel-system-roles.timesync 
  vars:
   timesync_ntp_servers:
    - hostname: 0.uk.pool.ntp.org
      iburst: yes 
...

Question 14: Write content filtered by host

Create a playbook called issue.yml in /home/ismat/ansible which changes the file /etc/issue on all managed nodes: If the host is a member of dev then write “Development” If the host is a member of test then write “Test” If the host is a member of prod then write “Production”

Solution:

Write a playbook /home/ismat/ansible/issue.yml

---
- name: change issue file
  hosts: all
  tasks:
      - name: change dev hosts issue file
        copy:
           content: "Development"
           dest: /etc/issue
        when: '"dev" in group_names'
      - name: change test hosts issue file
        copy:
           content: "Test"
           dest: /etc/issue
        when: '"test" in group_names'
      - name: change prod hosts issue file
        copy:
           content: "Producation"
           dest: /etc/issue
        when: '"prod" in group_names'
...

Question 15: Schedule cron job

Create a playbook called regulartasks.yml which has the system that appends the date to /root/datefile every day at noon. Name is job ‘datejob’

Solution:

Write a playbook /home/sandy/ismat/regulartasks.yml

---
- name: cron
  hosts: all
  tasks:
   - name: ensure a job that runs at noon
     cron:
      name: "datejob"
      minute: "0"
      hour: "12"
      job: "date >> /root/datefile"
...

Question 16: Create and change password of vault file

Create an empty encrypted file called myvault.yml in /home/ismat/ansible and set the password to notsafepw. Rekey the password to iaiai202112.

Solution:

$ ansible-vault create myvault.yml
New Vault password:
Confirm New Vault password:

Check you set up the right password

$ ansible-vault view myvault.yml
Vault password:

Rekey the password

$ ansible-vault rekey myvault.yml
Vault password:
New Vault password:
Confirm New Vault password:
Rekey successful

Check rekey is successful

$ ansible-vault view myvault.yml
Vault password:

You can download all the playbooks from https://github.com/zillur00/ansible-exam