Preparation Set 3


Instructions

    1. You will be provided with the root password.
    1. You need to use the hostname , if told in the instructions.
    1. You need to use the IP address, if told in the instructions.
    1. You need to ensure that you can SSH into every machine in your inventory without being prompted for a password.

Q1: Install and configure ansible in the control node

  • a. Install the required packages
  • b. Create a static inventory file /home/devops/ansible/hosts so that:
    • i. Node1 is the member of developer host group
    • ii. Node2 is the member of testing host group
    • iii. Node3 is the member of production host group
  • c. The production group is a member of the webservers host group
  • d. Create a configuration file called /home/devops/ansible/ansible.cfg so that:
    • i. The host inventory file is /home/devops/ansible/hosts
    • ii. The default content collection directory is /home/devops/ansible/collections
    • iii. The default role directory is /home/devops/ansible/roles
# Check all the required packages
[devops@ansible-server tasks]$ rpm -q epel-release 
epel-release-9-9.el9.noarch
[devops@ansible-server tasks]$ rpm -q ansible-core
ansible-core-2.14.18-1.el9.aarch64
[devops@ansible-server tasks]$ rpm -q ansible
ansible-7.7.0-1.el9.noarch
 
# Configure indentation for ansible playbooks (yaml)
[devops@ansible-server ~]$ cat .vimrc
autocmd	FileType yaml setlocal ai ts=2 sw=2 et
 
# Make sure ssh and ssh-copy-id is working in all the machines from the control node
 
[devops@ansible-server tasks]$ ssh devops@192.168.208.181
devops@192.168.208.181's password: 
Last login: Mon Feb 24 14:29:40 2025 from 192.168.208.1
[devops@node1 ~]$ exit
logout
Connection to 192.168.208.181 closed.
[devops@ansible-server tasks]$ ssh-copy-id devops@192.168.208.181
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/devops/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
devops@192.168.208.181's password: 
 
Number of key(s) added: 1
 
Now try logging into the machine, with:   "ssh 'devops@192.168.208.181'"
and check to make sure that only the key(s) you wanted were added.
 
# Configure ansible.cfg file and hosts file 
[devops@ansible-server tasks]$ ls
ansible.cfg  collections  hosts  roles
[devops@ansible-server tasks]$ cat ansible.cfg 
[defaults]
inventory=/home/devops/tasks/hosts
roles_path=/home/devops/tasks/roles
collection_path=/home/devops/tasks/collections
remote_user=devops
 
[privilege_escalation]
become=true
[devops@ansible-server tasks]$ cat hosts 
[developer]
node1
 
[testing]
node2
 
[production]
node3
 
[webservers:children]
production
 
# Ping all the machines
[devops@ansible-server tasks]$ ansible -m ping all 
node1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
node2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
node3 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
 
# Add ANSIBLE_CONFIG Path 
[devops@ansible-server ~]$ tail -1 .bashrc 
export ANSIBLE_CONFIG=/home/devops/tasks/ansible.cfg
 
# Check the ansible config working
[devops@ansible-server ~]$ ansible --version 
ansible [core 2.14.18]
  config file = /home/devops/tasks/ansible.cfg
  configured module search path = ['/home/devops/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.9/site-packages/ansible
  ansible collection location = /home/devops/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.9.18 (main, Sep  7 2023, 00:00:00) [GCC 11.4.1 20230605 (Red Hat 11.4.1-2)] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True
 
# Check the hosts 
[devops@ansible-server ~]$ ansible all --list-hosts
  hosts (3):
    node1
    node2
    node3

Q2: Create and run a ansible playbook. As a system adminstrator you need to install software on managed hosts

  • a. Create a playbook called playbook2.yml to create yum repositories on each of the managed nodes as per the following details:
  • b. Note: You need to create 2 repos (BaseOS and AppStream) in the managed nodes.
  • c. The file should be external_repos.repo file

BaseOS:

AppStream:

[devops@ansible-server tasks]$ vim playbook2.yml 
[devops@ansible-server tasks]$ cat playbook2.yml 
- name: Playbook2 
  hosts: all 
  tasks:
  - name: Import a key from a url
    ansible.builtin.rpm_key:
      state: present
      key: http://192.168.208.100/softwares/RPM-GPG-KEY-centosofficial
  - name: Add baseos repository
    ansible.builtin.yum_repository:
      name: BaseOS
      description: BaseOS Repo
      baseurl: http://192.168.208.100/softwares/BaseOS
      gpgcheck: yes
      enabled: yes 
      file: external_repos
      gpgkey: http://192.168.208.100/softwares/RPM-GPG-KEY-centosofficial
  - name: Add appstream repository
    ansible.builtin.yum_repository:
      name: AppStream
      description: AppStream Repo
      baseurl: http://192.168.208.100/softwares/AppStream
      gpgcheck: yes
      enabled: yes
      file: external_repos
      gpgkey: http://192.168.208.100/softwares/RPM-GPG-KEY-centosofficial
[devops@ansible-server tasks]$ 
[devops@ansible-server tasks]$ 
[devops@ansible-server tasks]$ ansible-playbook --syntax-check playbook2.yml 
 
playbook: playbook2.yml
[devops@ansible-server tasks]$ ansible-playbook playbook2.yml 
 
[devops@ansible-server tasks]$ ansible all -m command -a 'cat /etc/yum.repos.d/external_repos.repo'

Q3: Create a playbook called /home/devops/tasks/playbook3.yml that:

  • Install the php and samba packages in the host in the developer, testing and production host groups only.
  • Install the RPM development tools package group on hosts in the developer host group only
  • Update all package to the latest version on hosts in the dev developer group only.
[devops@ansible-server tasks]$ vim playbook3.yml
[devops@ansible-server tasks]$ cat playbook3.yml 
- name: Playbook3 
  hosts: all 
  tasks: 
  - name: Install the latest version of php and samba
    ansible.builtin.yum:
      name: "{{ item }}"
      state: latest
    loop: 
    - php
    - samba
    when: inventory_hostname in groups['developer']
  
  - name: Install the 'RPM Development Tools' package group
    ansible.builtin.yum:
      name: "@RPM Development Tools"
      state: present
    when: inventory_hostname in groups['developer']
 
  - name: Upgrade all packages
    ansible.builtin.yum:
      name: '*'
      state: latest
 
[devops@ansible-server tasks]$ ansible-playbook --syntax-check playbook3.yml 
[devops@ansible-server tasks]$ ansible-playbook playbook3.yml 

Q4: Install the RHEL system roles package and create a playbook called /home/devops/tasks/playbook4.yml that:

  • Runs on all managed host
  • Use the timesync role
  • Configure the role to use the timeserver
  • Configure the role to set the iburst parameter as enabled
[devops@ansible-server tasks]$ sudo yum install rhel-system-roles
 
[devops@ansible-server tasks]$ cat playbook4.yml 
- name: Manage timesync
  hosts: all
  vars:
    timesync_ntp_servers:
      - hostname: time.google.com
        iburst: true
  roles:
    - /usr/share/ansible/roles/rhel-system-roles.timesync

Q5: Create a role in apache in /home/devops/ansible/roles with the following requirement

  • The httpd package should be installed, httpd service should be enabled on boot, and started.
  • The firewall is enabled and running with a rule to allow access to the webserver.
  • A template file index.html.j2 exists(you have to create this file) and is used to create the file /var/www/html/index.html with the following output: Welcome to hostname on ipaddress, where hostname is the fully qualified domain name of the managed node and the ipaddress is the ipaddress of the managed node.
# Create index.html.j2 file
[devops@ansible-server templates]$ vim index.html.j2
[devops@ansible-server templates]$ cat index.html.j2 
welcome to {{ ansible_facts['fqdn'] }} on {{ ansible_facts['default_ipv4']['address'] }} 
 
# Create vars
[devops@ansible-server vars]$ cat main.yml 
pkgs:
- httpd
- firewalld
 
firewall_svcs:
- http
- https
 
# Create tasks
[devops@ansible-server tasks]$ cat main.yml 
- name: Install the latest version of Packages
  ansible.builtin.yum:
    name: "{{ item }}"
    state: latest
  loop: "{{ pkgs }}"
  
- name: Start service if not started
  ansible.builtin.service:
    name: "{{ item }}"
    state: started
    enabled: yes
  loop: "{{ pkgs }}"
 
- name: permit traffic in default zone for https service
  ansible.posix.firewalld:
    service: "{{ item }}"
    state: enabled
    permanent: true
    immediate: true
  loop: "{{ firewall_svcs }}"
 
- name: Template a file to /var/www/html
  ansible.builtin.template:
    src: index.html.j2
    dest: /var/www/html/index.html
 
# Create playbook file and execute
[devops@ansible-server tasks]$ cat playbook5.yml 
- name: Playbook5
  hosts: developer
  roles:
  - /home/devops/tasks/roles/apache
 
[devops@ansible-server tasks]$ ansible-playbook --syntax-check playbook5.yml 
playbook: playbook5.yml
[devops@ansible-server tasks]$ ansible-playbook playbook5.yml 
 
# Check
[devops@ansible-server tasks]$ ansible developer -m command -a 'cat /var/www/html/index.html'
node1 | CHANGED | rc=0 >>
welcome to node1 on 192.168.208.136 
[devops@ansible-server tasks]$ curl http://192.168.208.181
welcome to node1 on 192.168.208.136 

Q6: Use Ansible galaxy with the requirement file /home/devops/tasks/roles/requirements.yml to download and install roles to /home/admin/ansible/roles from the following URLs:

[devops@ansible-server tasks]$ cd roles/
[devops@ansible-server roles]$ cat requirements.yml 
- src: http://192.168.208.100/downloads/role1.tar.gz
  name: role1
- src: http://192.168.208.100/downloads/role2.tar.gz
  name: role2
 
[devops@ansible-server roles]$ ansible-galaxy role install -r requirements.yml 

Q7: Create a playbook called role1.yml as per the following details.

  • The playbook contains a play that runs on hosts in the developer group and uses the role1 role present in your machine.
[devops@ansible-server tasks]$ cat playbook6.yml 
- name: Playbook6
  hosts: developer
  roles:
  - /home/devops/tasks/roles/role1
 
[devops@ansible-server tasks]$ curl http://192.168.208.181
Role1 Tasks !!!
welcome to node1 on 192.168.208.136 

Q8: Create a playbook called test.yml as per the following details:

  • The playbook runs on the managed nodes in the developer host group.
  • Create directory /webtest with the group ownership webgroup and having the regular permission rwx for the owner and group, and rx for the others.
  • Apply the special group permission: set group ID
  • Symbollically link /var/www/html/webtest to /webtest directory.
  • Create the file /webtest/index.html with a single line of text that reads :Testing.
[devops@ansible-server tasks]$ cat playbook8.yml 
- name: Playbook8
  hosts: developer
  tasks:
  - name: Ensure group "webgroup" exists
    ansible.builtin.group:
      name: webgroup
      state: present
  - name: Create directory
    ansible.builtin.file:
      path: /webtest
      state: directory
      group: webgroup
      owner: root
      mode: '2775'
  - name: Create a symbolic link
    ansible.builtin.file:
      src: /webtest
      dest: /var/www/html/webtest
      state: link
  - name: Copy using inline content
    ansible.builtin.copy:
      content: "Testing\n"
      dest: /webtest/index.html
      group: webgroup
      owner: root
      mode: '0664'
  - name: Appending the group 'webgroup' and 'apache'
    ansible.builtin.user:
      name: apache
      groups: webgroup
      append: yes

If curl is returning forbidden request, then set selinux to permissive

  - name: Set SELinux to permissive 
    ansible.posix.selinux:
      policy: targeted
      state: permissive

Q9: Create an ansible vault to store user password with the following conditions:

  • The name of the vault is vault.yml
  • The vault contains two variables dev_pass with value as redhat and mgr_pass with value as linux respectively.
  • The password to encrypt and decrypt the vault is devops
  • The password is stored in the file /home/devops/ansible/password.txt file.
[devops@ansible-server tasks]$ cat password.txt 
devops
[devops@ansible-server tasks]$ ansible-vault create --vault-password-file password.txt vault.yml 
[devops@ansible-server tasks]$ ansible-vault view vault.yml 
Vault password: 
dev_pass: redhat
mgr_pass: linux

Q10: Generate host files:

  • Download an initial template file called hosts.j2 from the below URL: http://192.168.208.100/content/hosts.j2 to /home/devops/tasks/directory. Complete the template so that it can be used to generate a file with a line for each inventory host in the same format as /etc/hosts.
  • Create a playbook called playbook10.yml that uses this template to generate the file /etc/myhosts on hosts in the all host group
  • When completed, the file /etc/myhosts on hosts in the all host group should have a line for each managed host:
[devops@ansible-server tasks]$ vim playbook10.yml 
[devops@ansible-server tasks]$ cat playbook10.yml 
- name: Playbook10
  hosts: all
  tasks: 
  - name: Template a file
    ansible.builtin.template:
      src: /home/devops/tasks/hosts.j2
      dest: /etc/myhosts
[devops@ansible-server tasks]$ cat hosts.j2 
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
 
{% for x in groups['all'] %}
{{ hostvars[x]['ansible_facts']['default_ipv4']['address'] }} {{ hostvars[x]['ansible_facts']['hostname'] }} {{ hostvars[x]['ansible_facts']['fqdn'] }}
{% endfor %} 
 
[devops@ansible-server tasks]$ ansible all -m command -a 'cat /etc/myhosts' 
node3 | CHANGED | rc=0 >>
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
 
192.168.208.136 node1 node1
192.168.208.151 node2 node2
192.168.208.152 node3 node3
 
node1 | CHANGED | rc=0 >>
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
 
192.168.208.136 node1 node1
192.168.208.151 node2 node2
192.168.208.152 node3 node3
 
node2 | CHANGED | rc=0 >>
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
 
192.168.208.136 node1 node1
192.168.208.151 node2 node2
192.168.208.152 node3 node3

Q11: Create a playbook called playbook11.yml that produces an output file called /root/hwreport.txt on all the managed nodes with following information

  • Inventory Hostname :
  • Total Memory in MB :
  • BIOS Version :
  • nvme0n1 Size :
  • nvme0n2 Size : Each line of the output file contains a single key-value pair. If no device then write NONE
[devops@ansible-server tasks]$ cat playbook11.yml 
- name: Playbook11
  hosts: all 
  tasks:
  - name: Template a file
    ansible.builtin.template:
      src: /home/devops/tasks/hwreport.j2
      dest: /root/hwreport.txt
[devops@ansible-server tasks]$ cat hwreport.j2 
* Inventory Hostname : {{ ansible_facts['hostname'] }}
* Total Memory in MB : {{ ansible_facts['memtotal_mb'] }}
* BIOS Version : {{ ansible_facts['bios_version'] }}
* nvme0n1 Size : {% if ansible_facts['devices']['nvme0n1'] is defined %} {{ ansible_facts['devices']['nvme0n1']['size'] }} {% else %} NONE {% endif %}
* nvme0n2 Size : {% if ansible_facts['devices']['nvme0n2'] is defined %} {{ ansible_facts['devices']['nvme0n2']['size'] }} {% else %} NO
NE {% endif %}

Q12: Create a playbook called /home/devops/tasks/playbook12.yml as per the following requirements

  • The playbook runs on all inventory hosts
  • The playbook replaces the contents of /etc/issue with a single line of text as:
    • On host in the developer host group, the line reads: Development
    • On host in the testing host group, the line reads: Testing
    • On host in the production host group, the line reads: Production
[devops@ansible-server tasks]$ vim playbook12.yml 
[devops@ansible-server tasks]$ cat playbook12.yml 
- name: Playbook12
  hosts: all 
  tasks:
  - name: Copy using inline content for developer
    ansible.builtin.copy:
      content: "Development\n"
      dest: /etc/issue
    when: inventory_hostname in groups['developer']
  - name: Copy using inline content for testing
    ansible.builtin.copy:
      content: "Testing\n"
      dest: /etc/issue
    when: inventory_hostname in groups['testing']
  - name: Copy using inline content for production
    ansible.builtin.copy:
      content: "Production\n"
      dest: /etc/issue
    when: inventory_hostname in groups['production']
 
[devops@ansible-server tasks]$ ansible-playbook playbook12.yml 
[devops@ansible-server tasks]$ ansible all -m command -a 'cat /etc/issue' 
node3 | CHANGED | rc=0 >>
Production
node2 | CHANGED | rc=0 >>
Testing
node1 | CHANGED | rc=0 >>
Development

Q13: Rekey an existing ansible vault as per the following condition:

  • Use the vault.yml file that you have created earlier
  • Set the new vault password as ansible
  • The vault remains in an encrypted state with the new password
[devops@ansible-server tasks]$ vim password.txt 
[devops@ansible-server tasks]$ cat password.txt 
ansible
[devops@ansible-server tasks]$ ansible-vault rekey --new-vault-password-file=password.txt vault.yml 
Vault password: 
Rekey successful
# Using new vault password - ansible
[devops@ansible-server tasks]$ ansible-vault view vault.yml 
Vault password: 
dev_pass: redhat
mgr_pass: linux

Q14: 14. Create user accounts. A list of users to be created can be found in the file called user_list.yml which you should download from "http://192.168.208.100/content/user_list.yml" and save to /home/devops/tasks/ directory. Using the password vault created elsewhere in this exam, create a playbook called playbook14.yml that creates user accounts as follows:

  • Users with a job description of developer should be created on managed nodes in the developer and testing host groups assigned the password from the dev_pass variable and is a member of supplementary group hit_developer.
  • Users with a job description of manager should be created on managed nodes in the production host group assigned the password from the mgr_pass variable and is a member of supplementary group hit_manager.
  • Passwords should use the SHA512 hash format. Your playbook should work using the vault password file created elsewhere in this exam.
[devops@ansible-server tasks]$ cat user_list.yml 
users:
- name: dilane
  job: developer
  uid: 3300
- name: fahim
  job: manager
  uid: 3301
- name: safayet 
  job: developer
  uid: 3302
 
[devops@ansible-server tasks]$ cat playbook14.yml 
- name: Playbook14 developer
  hosts: developer,testing
  vars_files:
  - /home/devops/tasks/vault.yml
  - /home/devops/tasks/user_list.yml
  tasks: 
  - name: Ensure group "hit_developer" exists
    ansible.builtin.group:
      name: hit_developer
      state: present
  - name: Add the user
    ansible.builtin.user:
      name: "{{ item.name }}"
      uid: "{{ item.uid }}"
      group: hit_developer
      password: "{{ dev_pass | password_hash('sha512') }}"
    loop: "{{ users }}"
    when: item.job == "developer"
 
 
- name: Playbook14 manager
  hosts: production
  vars_files:
  - /home/devops/tasks/vault.yml
  - /home/devops/tasks/user_list.yml
  tasks: 
  - name: Ensure group "hit_manager" exists
    ansible.builtin.group:
      name: hit_manager
      state: present
  - name: Add the user 
    ansible.builtin.user:
      name: "{{ item.name }}"
      uid: "{{ item.uid }}"
      group: hit_manager
      password: "{{ dev_pass | password_hash('sha512') }}"
    loop: "{{ users }}"
    when: item.job == "manager" 

Q15: Configure Cron Jobs: Create /home/devops/tasks/playbook15.yml playbook as per the following requirement

  • This playbook runs on all managed nodes in the hostgroup
  • Configure cronjob, which runs every 2 minutes and executes the following command: logger "EX294 exam in progress" and runs as user natasha
[devops@ansible-server tasks]$ cat playbook15.yml 
- name: Playbook15
  hosts: all 
  tasks: 
  - name: Add the user
    ansible.builtin.user:
      name: mohit
  - name: Ensure a job
    ansible.builtin.cron:
      name: "cron log"
      minute: "*/2"
      user: mohit
      job: 'logger "EX294 exam in progress"'
[devops@ansible-server tasks]$ ansible all -m command -a 'crontab -l -u mohit'
node3 | CHANGED | rc=0 >>
#Ansible: cron log
*/2 * * * * logger "EX294 exam in progress"
node1 | CHANGED | rc=0 >>
#Ansible: cron log
*/2 * * * * logger "EX294 exam in progress"
node2 | CHANGED | rc=0 >>
#Ansible: cron log
*/2 * * * * logger "EX294 exam in progress"

Q16: Create & use a logical volume: Create a playbook called /home/devops/tasks/playbook16.yml that runs on all the managed nodes and does the following:

  • Creates a logical volume with the following requirements:
    • The logical volume is created in the developer volume group.
    • The logical volume name is data.
    • The logical volume size is 1200 Mib.
    • Format the logical volume with the ext file-system.
    • If the requested logical volume size cannot be created, the error message "could not create logical volume of that size" should be displayed and size 800 MiB should be used instead.
    • If the volume research does not exist, the error message "volume group does not exist" should be displayed.
    • Don't mount the logical volume in any way.
[devops@ansible-server tasks]$ vim playbook16.yml 
[devops@ansible-server tasks]$ cat playbook16.yml 
- name: Playbook16
  hosts: all
  tasks:
  - name: block,rescue,always
    block:
    - name: If VG not present
      ansible.builtin.debug:
        msg: "volume group does not exist"
      when: ansible_facts['lvm']['vgs']['research'] is not defined
    - name: Create a logical volume of 1200m
      community.general.lvol:
        vg: research
        lv: data
        size: 1200
      when: ansible_facts['lvm']['vgs']['research'] is defined
    rescue:
    - name: If VG size is not sufficient
      ansible.builtin.debug:
        msg: "could not create logical volume of that size"
      when: ansible_facts['lvm']['vgs']['research'] is defined
    - name: Create a logical volume of 800m
      community.general.lvol:
        vg: research
        lv: data
        size: 800
      when: ansible_facts['lvm']['vgs']['research'] is defined
    always:
    - name: Create a ext4 
      community.general.filesystem:
        fstype: ext4
        dev: /dev/research/data
      when: ansible_facts['lvm']['vgs']['research'] is defined
 
[devops@ansible-server tasks]$ ansible-playbook playbook16.yml 
All systems normal

© 2025 2023 Sanjeeb KC. All rights reserved.