Below are the answers for Exam Scenario 2. You can also find all the files that would need to be created for this scenario on this folder.
Bash
1useradd ansible
Bash
1su - ansible
Bash
1mkdir exam-files
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ansible/.ssh/id_rsa): id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa.
Your public key has been saved in id_rsa.pub.
The key fingerprint is:
SHA256:rI3irvdNYBw1mUz/heLTAVloIiPfoR1KzJMwU/IC2N4 ansible@rhel8.localdomain
The key's randomart image is:
+---[RSA 3072]----+
| o. ==.++o.+. |
|. ...=O.Bo+. . |
| . ..+oO =o o . |
| . Eo+oo. + o |
| + S o o |
| . = . |
| . o o |
| ... o |
| .+o.. . |
+----[SHA256]-----+
Bash
1mkdir {roles,vars,playbooks,scripts,files}
inventory
Ini
1node12node23node34node4
ansible.cfg
Ini
1[defaults]23inventory = ./inventory4roles_path = ./roles5remote_user = ansible6private_key_file = ./id_rsa7host_key_checking = false8nocows = 19retry_files_enabled = false
Bash
1useradd ansible2passwd ansible
Bash
1visudo -f /etc/sudoers.d/ansible
Sudoers
1ansible ALL=(ALL) NOPASSWD: ALL
# su - ansible
$ sudo -ln
Matching Defaults entries for ansible on node2:
!visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin, env_reset,
env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS", env_keep+="MAIL PS1 PS2 QTDIR USERNAME
LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES",
env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL LANGUAGE
LINGUAS _XKB_CHARSET XAUTHORITY", secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin
User ansible may run the following commands on node2:
(ALL) NOPASSWD: ALL
Bash
1for i in 1 2 3 4 ; do2sshpass -p ansible ssh-copy-id -i ./id_rsa.pub ansible@node${i}3done
Bash
1ssh ansible@node1 -i id_rsa
Add to /etc/ssh/sshd_config
Config
1Match User ansible2PasswordAuthentication no
scripts/check-connection.sh
Bash
1#!/bin/bash23ansible all -m ping
scripts/get-server-info.sh
1#!/bin/bash2
3tuned_profile="$(tuned-adm active | grep 'Current active profile')"4
5if [ ! "$tuned_profile" ] ; then6 tuned_status="inactive"7 tuned_profile=" disabled"8else9 tuned_status="active"10 tuned_profile="$(echo "$tuned_profile" | awk -F':' '{print $2}')"11fi12
13echo "Hostname: $(hostname)14Name: $(grep -E '^NAME=' /etc/os-release | awk -F"=" '{print $2}')15Version: $(grep -E '^VERSION=' /etc/os-release | awk -F"=" '{print $2}')16Tuned status: $tuned_status17Current active profile:${tuned_profile}"
scripts/task2.sh
Bash
1#!/bin/bash23ansible all -m copy -a 'src=/home/ansible/exam-files/scripts/get-server-info.sh dest=/usr/local/bin/get-server-info.sh mode=0755 owner=root group=root' -b45ansible all -a '/usr/local/bin/get-server-info.sh' -b
Ini
1[webservers]2node13node245[databases]6node37node489[mysql]10node31112[postgresql]13node41415[version1]16node11718[version2]19node2
playbooks/task4.yml
YAML
1---2- hosts: all3become: true45tasks:67- name: Creates /data/backup8file:9path: /data/backup10state: directory11mode: g+x,o+x12when: '"webservers" in group_names'1314- name: Create /etc/server_role15copy:16dest: /etc/server_role17content: "{{ group_names | string | regex_search('webservers|databases')}}"1819- name: Cheks if httpd is installed20command: rpm -qa | grep -qE '^httpd-[0-9]'21args:22warn: false23register: httpd_install_status24changed_when: false25when: '"webservers" in group_names or "databases" in group_names'2627- name: Shows httpd package as installed28debug:29msg: "HTTPD is installed"30when:31- 'httpd_install_status.rc == 0 and32("webservers" in group_names or "databases" in group_names)'3334- name: Shows httpd package as not installed35debug:36msg: "HTTPD is not installed"37when:38- 'httpd_install_status.rc != 0 and39("webservers" in group_names or "databases" in group_names)'4041- name: Makes sure default target is multi-user.target42shell: |43if ! systemctl get-default | grep -q multi-user.target ; then44systemctl set-default multi-user.target45/bin/false46else47exit 048fi49register: targetlevel_output50changed_when: targetlevel_output.rc == 151ignore_errors: true52
playbooks/task5.yml
YAML
1---2- hosts: all3become: true45handlers:6- name: Restat HTTPD7systemd:8name: httpd9state: restarted10listen: "Restart HTTPD"11when: '"webservers" in group_names'1213- name: Backup httpd.conf14archive:15path: /etc/httpd/conf/httpd.conf16dest: "/data/backup/http.conf-{{ ansible_date_time.date | replace('-', '') }}_{{ ansible_date_time.time | replace(':', '') }}.zip"17format: zip18listen: "Backup httpd.conf"19when: '"webservers" in group_names'2021tasks:2223- name: Uploads root_space_check.sh24copy:25src: /home/ansible/exam-files/files/root_space_check.sh26dest: /usr/local/bin/root_space_check.sh27mode: ugo+x2829- name: Adds root_space_check.sh to con30cron:31name: Runs root_space_check.sh every hour32special_time: hourly33job: /usr/local/bin/root_space_check.sh3435# Block starts36- name: Block for webservers37block:3839- name: Installs httpd40dnf:41name: httpd4243- name: Enables the httpd service44systemd:45name: httpd46enabled: yes47state: started4849- name: Opens ports for httpd50firewalld:51service: "{{ item }}"52permanent: yes53state: enabled54loop:55- http56- https5758- name: Changes the Listen option in /etc/httpd/conf/httpd.conf59lineinfile:60path: /etc/httpd/conf/httpd.conf61line: "Listen {{ ansible_eth1.ipv4.address }}:80"62regexp: "^Listen .*"63notify:64- "Restart HTTPD"65- "Backup httpd.conf"6667when: '"webservers" in group_names'68# Block end6970# Block starts71- name: Start block for databases72block:7374- name: Creates PV and VG75lvg:76vg: databases_vg77pvs: /dev/sdb7879- name: Create LV80lvol:81vg: databases_vg82lv: databases_lv83size: 100%FREE84shrink: false8586- name: Formats to ext487filesystem:88fstype: ext489dev: /dev/mapper/databases_vg-databases_lv90opts: '-L DATABASES'9192- name: Create the mountpoint for DATABASES93mount:94path: /data/databases95src: LABEL=DATABASES96fstype: ext497state: present9899when: '"mysql" in group_names'100# Block end101102- name: Enables SELinux for databases103selinux:104state: enforcing105policy: targeted106when: '"databases" in group_names'107
Bash
1cd roles2ansible-galaxy init start-page
roles/start-page/templates/index.html.j2
Django/Jinja2
1<!DOCTYPE html>2<html lang="en">3<head>4<meta charset="UTF-8">5<meta name="viewport" content="width=device-width, initial-scale=1.0">6<title>Server Information</title>7<style>8body {9font-family: Arial, sans-serif;10margin: 20px;11}12div {13margin-bottom: 10px;14}15</style>16</head>17<body>18<h1>Server Information</h1>1920<div>21<strong>Hostname:</strong> <span id="hostname">{{ ansible_fqdn }}</span>22</div>2324<div>25<strong>Node Group:</strong> <span id="group">{{ group_names | string | regex_search('version.') }}</span>26</div>2728<div>29<strong>IP Address:</strong> <span id="ip">{{ ansible_eth1.ipv4.address }}</span>30</div>3132<div>33<strong>Timezone:</strong> <span id="timezone">{{ ansible_date_time.tz }}</span>34</div>3536</body>37</html>
roles/start-page/tasks/main.yml
YAML
1---2# tasks file for start-page34- name: Pushes index.html5template:6src: index.html.j27dest: /var/www/html/index.html
Bash
1cd roles2ansible-galaxy init journald-persistent
roles/journald-persistent/tasks/main.yml
YAML
1---2# tasks file for journald-persistent34- name: Create /var/log/journal5file:6path: /var/log/journal7state: directory8owner: root9group: root1011- name: Configures /etc/systemd/journald.conf12lineinfile:13path: /etc/systemd/journald.conf14line: "{{ item.line }}"15regexp: "{{ item.regexp }}"16loop:17- { line: 'SystemMaxUse=100M', regexp: '^SystemMaxUse=.*' }18- { line: 'Storage=persistent', regexp: '^Storage=.*' }19notify: "Restart Journald"
roles/journald-persistent/handlers/main.yml
YAML
1---2# handlers file for journald-persistent34- name: Restarts5systemd:6name: systemd-journald.service7state: restarted8listen: "Restart Journald"
playbooks/task6.yml
Django/Jinja2
1---2- hosts: all3become: true45roles:6- name: start-page7when: '"webservers" in group_names'89- name: journald-persistent
node1:
sudo mkdir -p /etc/ansible/facts.d
/etc/ansible/facts.d/exam.fact
Ini
1[server_info]2group=webservers3app_version=1
node2:
sudo mkdir -p /etc/ansible/facts.d
/etc/ansible/facts.d/exam.fact
Ini
1[server_info]2group=webservers3app_version=2
ansible-console
ansible@webservers (2)[f:5]$ ls
node1 | CHANGED | rc=0 >>
node2 | CHANGED | rc=0 >>
ansible@webservers (2)[f:5]$ setup
ansible@webservers (2)[f:5]$ debug var=ansible_local
node1 | SUCCESS => {
"ansible_local": {
"exam": {
"server_info": {
"app_version": "1",
"group": "webservers"
}
}
}
}
node2 | SUCCESS => {
"ansible_local": {
"exam": {
"server_info": {
"app_version": "2",
"group": "webservers"
}
}
}
}
roles/postgresql/tasks/main.yml
YAML
1---2# tasks file for postgresql34- name: Installs the VDO package5dnf:6name:7- vdo8- kmod-kvdo910- name: Starts the VDO service11systemd:12name: vdo.service13state: started14enabled: true1516# The force option in the vdo module is not present on Ansible 2.917- name: Checks if VDO volume already exists18command: vdostats databases_vdo19register: vgostats_output20changed_when: false21ignore_errors: true2223- name: Creates the VDO partition24vdo:25name: databases_vdo26device: /dev/sdb27logicalsize: 20G28writepolicy: auto29deduplication: disabled30#force: false31when: vgostats_output == 13233- name: Create the volume group34lvg:35pvs: /dev/mapper/databases_vdo36vg: databases_vg3738- name: Create the logical volume39lvol:40lv: databases_lv41vg: databases_vg42size: 100%FREE43force: false44shrink: false4546- name: Creates the ext4 filesystem47filesystem:48dev: /dev/mapper/databases_vg-databases_lv49fstype: ext450opts: -E nodiscard5152- name: Mounts the filesystem53mount:54fstype: ext455opts: defaults,_netdev,discard,x-systemd.requires=vdo.service,x-systemd.device-timeout=056src: /dev/mapper/databases_vg-databases_lv57path: /data/databases58state: mounted5960- name: Installs the postgresql module61dnf:62name: '@postgresql'63register: postgresql_install6465- name: Configures the data folder for postgresql service66lineinfile:67path: /usr/lib/systemd/system/postgresql.service68line: 'Environment=PGDATA=/data/databases/postgresql_data'69regexp: '^Environment=PGDATA=.*'70notify: "Reload daemon"7172- name: Creates /data/databases/postgresql_data73file:74path: /data/databases/postgresql_data75state: directory76owner: postgres77group: postgres78mode: '0700'79register: create_postgresql_data8081- name: Initializes the DB82command: postgresql-setup --initdb83args:84creates: /data/databases/postgresql_data/PG_VERSION85when:86- postgresql_install.changed == true87- postgresql_data.changed == true8889- name: Installs semanage90dnf:91name: setroubleshoot-server9293- name: Enables the selinuxuser_postgresql_connect_enabled boolean94seboolean:95name: selinuxuser_postgresql_connect_enabled96state: yes97persistent: yes9899- name: Fixes the SELinux context for the postgresql data files100sefcontext:101target: '/data/databases(/.*)?'102setype: postgresql_db_t103state: present104notify: "Restore SELinux context"
roles/postgresql/handlers/main.yml
YAML
1---2# handlers file for postgresql34- name: Reload daemon5systemd:6name: postgresql.service7daemon_reload: true8enabled: true9listen: "Reload daemon"1011- name: Restore SELinux context12command: restorecon -irv /data/databases13when: change_selinux_context.changed == true14listen: "Restore SELinux context"15notify: "Restart postgresql service"1617- name: Restart postgresql.service18systemd:19name: postgresql.service20state: restarted21listen: "Restart postgresql service"
playbooks/deploy-postgresql.yml
YAML
1---2- hosts: postgresql3become: true45roles:6- postgresql78tasks:9- name: Checks if /data/db_troubleshoot exists10stat:11path: /data/db_troubleshoot12register: stat_db_troubleshoot1314- name: Creates /data/db_troubleshoot15file:16path: /data/db_troubleshoot17state: directory18owner: postgres19group: postgres20mode: '0700'21force: false22when: stat_db_troubleshoot.stat.exists == false2324- name: Creates the group pgsqladmin25group:26name: pgsqladmin2728- name: Creates the user dbadmin29user:30name: dbadmin31group: pgsqladmin3233- name: Sets default ACL for /data/db_troubleshoot34acl:35path: /data/db_troubleshoot36default: true37etype: group38entity: pgsqladmin39permissions: rwx40state: present4142- name: Sets ACL for /data/db_troubleshoot43acl:44path: /data/db_troubleshoot45etype: group46entity: pgsqladmin47permissions: rwx48state: present
$ ansible-galaxy role install geerlingguy.mysql
- downloading role 'mysql', owned by geerlingguy
- downloading role from https://github.com/geerlingguy/ansible-role-mysql/archive/4.3.3.tar.gz
- extracting geerlingguy.mysql to /home/ansible/exam-files/roles/geerlingguy.mysql
- geerlingguy.mysql (4.3.3) was installed successfully
Bash
1$ tr -dc A-Za-z0-9*_$^! < /dev/urandom | head -c 24 > .vault_passwd
Ini
1[defaults]23inventory = ./inventory4roles_path = ./roles5remote_user = ansible6private_key_file = ./id_rsa7host_key_checking = false8nocows = 19retry_files_enabled = false10vault_password_file = ./.vault_passwd
vars/mysql.yml
YAML
1---2mysql_root_username: root3mysql_root_password: sqlrootpassword
$ ansible-vault encrypt vars/mysql.yml
Encryption successful
Change the line below in roles/geerlingguy.mysql/defaults/main.yml
YAML
1mysql_root_password_update: true
playbooks/deploy-mysql.yml
YAML
1---2- hosts: mysql3become: true45vars_files:6- /home/ansible/exam-files/vars/mysql.yml78roles:9- geerlingguy.mysql
ansible-config list > ansible.cfg.template
ansible-config dump > ansible.cfg.dump
ansible-doc -l > ansible-modules.txt
dnf install -y python3-jinja2.noarch
Documentation files can be found with:
rpm -ql python3-jinja2.noarch | grep index.html
/usr/share/doc/python3-jinja2/examples/rwbench/django/index.html
/usr/share/doc/python3-jinja2/examples/rwbench/genshi/index.html
/usr/share/doc/python3-jinja2/examples/rwbench/jinja/index.html
/usr/share/doc/python3-jinja2/examples/rwbench/mako/index.html
/usr/share/doc/python3-jinja2/ext/django2jinja/templates/index.html
/usr/share/doc/python3-jinja2/html/genindex.html
/usr/share/doc/python3-jinja2/html/index.html
/usr/share/doc/python3-jinja2/html/latexindex.html
/usr/share/doc/python3-jinja2/html/py-modindex.html