Preparation Set 3
Instructions
-
- You will be provided with the root password.
-
- You need to use the hostname , if told in the instructions.
-
- You need to use the IP address, if told in the instructions.
-
- 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:
- name: BaseOs
- baseurl: http://192.168.208.100/softwares/BaseOS
- description: BaseOS Repo
- gpgcheck: yes
- enabled: yes
- key: http://192.168.208.100/softwares/RPM-GPG-KEY-centosofficial
AppStream:
- name: AppStream
- baseurl: http://192.168.208.100/softwares/AppStream
- description: AppStream Repo
- gpgcheck: yes
- enabled: yes
- key: http://192.168.208.100/softwares/RPM-GPG-KEY-centosofficial
[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:
- http://192.168.208.181/downloads/role1.tar.gz . The name of this role should be role1.
- http://192.168.208.181/downloads/role2.tar.gz . The name of this role should be role2.
[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 ownershipwebgroup
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