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.
Bash1useradd ansible
Bash1su - ansible
Bash1mkdir 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]-----+
Bash1mkdir {roles,vars,playbooks,scripts,files}
inventory
Ini1node12node23node34node4
ansible.cfg
Ini1[defaults]23inventory = ./inventory4roles_path = ./roles5remote_user = ansible6private_key_file = ./id_rsa7host_key_checking = false8nocows = 19retry_files_enabled = false
Bash1useradd ansible2passwd ansible
Bash1visudo -f /etc/sudoers.d/ansible
Sudoers1ansible 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
Bash1for i in 1 2 3 4 ; do2sshpass -p ansible ssh-copy-id -i ./id_rsa.pub ansible@node${i}3done
Bash1ssh ansible@node1 -i id_rsa
Add to /etc/ssh/sshd_config
Config1Match User ansible2PasswordAuthentication no
scripts/check-connection.sh
Bash1#!/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
Bash1#!/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
Ini1[webservers]2node13node245[databases]6node37node489[mysql]10node31112[postgresql]13node41415[version1]16node11718[version2]19node2
playbooks/task4.yml
YAML1---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
YAML1---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
Bash1cd roles2ansible-galaxy init start-page
roles/start-page/templates/index.html.j2
Django/Jinja21<!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
YAML1---2# tasks file for start-page34- name: Pushes index.html5template:6src: index.html.j27dest: /var/www/html/index.html
Bash1cd roles2ansible-galaxy init journald-persistent
roles/journald-persistent/tasks/main.yml
YAML1---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
YAML1---2# handlers file for journald-persistent34- name: Restarts5systemd:6name: systemd-journald.service7state: restarted8listen: "Restart Journald"
playbooks/task6.yml
Django/Jinja21---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
Ini1[server_info]2group=webservers3app_version=1
node2:
sudo mkdir -p /etc/ansible/facts.d
/etc/ansible/facts.d/exam.fact
Ini1[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
YAML1---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
YAML1---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
YAML1---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
Bash1$ tr -dc A-Za-z0-9*_$^! < /dev/urandom | head -c 24 > .vault_passwd
Ini1[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
YAML1---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
YAML1mysql_root_password_update: true
playbooks/deploy-mysql.yml
YAML1---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