Ansible Sample Exam for EX407/EX294

This is a sample Ansible exam that I’ve created to prepare for EX407. I have not taken the EX407 exam yet.

You can also use it for the new RHCE 8 exam EX294.

As with the real exam, no answers to the sample exam questions will be provided.

Requirements

There are 18 questions in total.

You will need five RHEL 7 (or CentOS 7) virtual machines to be able to successfully complete all questions.

One VM will be configured as an Ansible control node. Other four VMs will be used to apply playbooks to solve the sample exam questions. The following FQDNs will be used throughout the sample exam.

  1. ansible-control.hl.local – Ansible control node
  2. ansible2.hl.local – managed host
  3. ansible3.hl.local – managed host
  4. ansible4.hl.local – managed host
  5. ansible5.hl.local – managed host

There are a couple of requiremens that should be met before proceeding further:

  1. ansible-control.hl.local server has passwordless SSH access to all managed servers (using the root user).
  2. ansible5.hl.local server has a 1GB secondary /dev/sdb disk attached.
  3. There are no regular users created on any of the servers.

Tips and Suggestions

I tried to cover as many exam objectives as possible, however, note that there will be no questions related to dynamic inventory.

Some questions may depend on the outcome of others. Please read all questions before proceeding.

Note that the purpose of the sample exam is to test your skills. Please don’t post your playbooks in the comments section.

Sample Exam Questions

Note: you have root access to all five servers.

Task 1: Ansible Installation and Configuration

Install ansible package on the control node (including any dependencies) and configure the following:

  1. Create a regular user automation with the password of devops. Use this user for all sample exam tasks and playbooks, unless you are working on the task #2 that requires creating the automation user on inventory hosts. You have root access to all five servers.
  2. All playbooks and other Ansible configuration that you create for this sample exam should be stored in /home/automation/plays.

Create a configuration file /home/automation/plays/ansible.cfg to meet the following requirements:

  1. The roles path should include /home/automation/plays/roles, as well as any other path that may be required for the course of the sample exam.
  2. The inventory file path is /home/automation/plays/inventory.
  3. Privilege escallation is disabled by default.
  4. Ansible should be able to manage 10 hosts at a single time.
  5. Ansible should connect to all managed nodes using the automation user.

Create an inventory file /home/automation/plays/inventory with the following:

  1. ansible2.hl.local is a member of the proxy host group.
  2. ansible3.hl.local is a member of the webservers host group.
  3. ansible4.hl.local is a member of the webservers host group.
  4. ansible5.hl.local is a member of the database host group.

Task 2: Ad-Hoc Commands

Generate an SSH keypair on the control node. You can perform this step manually.

Write a script /home/automation/plays/adhoc that uses Ansible ad-hoc commands to achieve the following:

  1. User automation is created on all inventory hosts (not the control node).
  2. SSH key (that you generated) is copied to all inventory hosts for the automation user and stored in /home/automation/.ssh/authorized_keys.
  3. The automation user is allowed to elevate privileges on all inventory hosts without having to provide a password.

After running the adhoc script, you should be able to SSH into all inventory hosts using the automation user without password, as well as a run all privileged commands.

Task 3: File Content

Create a playbook /home/automation/plays/motd.yml that runs on all inventory hosts and does the following:

  1. The playbook should replace any existing content of /etc/motd with text. Text depends on the host group.
  2. On hosts in the proxy host group the line should be “Welcome to HAProxy server”.
  3. On hosts in the webservers host group the line should be “Welcome to Apache server”.
  4. On hosts in the database host group the line should be “Welcome to MySQL server”.

Task 4: Configure SSH Server

Create a playbook /home/automation/plays/sshd.yml that runs on all inventory hosts and configures SSHD daemon as follows:

  1. banner is set to /etc/motd
  2. X11Forwarding is disabled
  3. MaxAuthTries is set to 3

Task 5: Ansible Vault

Create Ansible vault file /home/automation/plays/secret.yml. Encryption/decryption password is devops.

Add the following variables to the vault:

  1. user_password with value of devops
  2. database_password with value of devops

Store Ansible vault password in the file /home/automation/plays/vault_key.

Task 6: Users and Groups

You have been provided with the list of users below.

Use /home/automation/plays/vars/user_list.yml file to save this content.

---
users:
  - username: alice
    uid: 1201
  - username: vincent
    uid: 1202
  - username: sandy
    uid: 2201
  - username: patrick
    uid: 2202

Create a playbook /home/automation/plays/users.yml that uses the vault file /home/automation/plays/secret.yml to achieve the following:

  1. Users whose user ID starts with 1 should be created on servers in the webservers host group. User password should be used from the user_password variable.
  2. Users whose user ID starts with 2 should be created on servers in the database host group. User password should be used from the user_password variable.
  3. All users should be members of a supplementary group wheel.
  4. Shell should be set to /bin/bash for all users.
  5. Account passwords should use the SHA512 hash format.
  6. Each user should have an SSH key uploaded (use the SSH key that you created previously, see task #2).

After running the playbook, users should be able to SSH into their respective servers without passwords.

Task 7: Scheduled Tasks

Create a playbook /home/automation/plays/regular_tasks.yml that runs on servers in the proxy host group and does the following:

  1. A root crontab record is created that runs every hour.
  2. The cron job appends the file /var/log/time.log with the output from the date command.

Task 8: Software Repositories

Create a playbook /home/automation/plays/repository.yml that runs on servers in the database host group and does the following:

  1. A YUM repository file is created.
  2. The name of the repository is mysql56-community.
  3. The description of the repository is “MySQL 5.6 YUM Repo”.
  4. Repository baseurl is http://repo.mysql.com/yum/mysql-5.6-community/el/7/x86_64/.
  5. Repository GPG key is at http://repo.mysql.com/RPM-GPG-KEY-mysql.
  6. Repository GPG check is enabled.
  7. Repository is enabled.

Task 9: Create and Work with Roles

Create a role called sample-mysql and store it in /home/automation/plays/roles. The role should satisfy the following requirements:

  1. A primary partition number 1 of size 800MB on device /dev/sdb is created.
  2. An LVM volume group called vg_database is created that uses the primary partition created above.
  3. An LVM logical volume called lv_mysql is created of size 512MB in the volume group vg_database.
  4. An XFS filesystem on the logical volume lv_mysql is created.
  5. Logical volume lv_mysql is permanently mounted on /mnt/mysql_backups.
  6. mysql-community-server package is installed.
  7. Firewall is configured to allow all incoming traffic on MySQL port TCP 3306.
  8. MySQL root user password should be set from the variable database_password (see task #5).
  9. MySQL server should be started and enabled on boot.
  10. MySQL server configuration file is generated from the my.cnf.j2 Jinja2 template with the following content:
[mysqld]
bind_address = {{ ansible_default_ipv4.address }}
skip_name_resolve
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock

symbolic-links=0
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES 

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

Create a playbook /home/automation/plays/mysql.yml that uses the role and runs on hosts in the database host group.

Task 10: Create and Work with Roles (Some More)

Create a role called sample-apache and store it in /home/automation/plays/roles. The role should satisfy the following requirements:

  1. The httpd, mod_ssl and php packages are installed. Apache service is running and enabled on boot.
  2. Firewall is configured to allow all incoming traffic on HTTP port TCP 80 and HTTPS port TCP 443.
  3. Apache service should be restarted every time the file /var/www/html/index.html is modified.
  4. A Jinja2 template file index.html.j2 is used to create the file /var/www/html/index.html with the following content:
The address of the server is: IPV4ADDRESS

IPV4ADDRESS is the IP address of the managed node.

Create a playbook /home/automation/plays/apache.yml that uses the role and runs on hosts in the webservers host group.

Task 11: Download Roles From Ansible Galaxy and Use Them

Use Ansible Galaxy to download and install geerlingguy.haproxy role in /home/automation/plays/roles.

Create a playbook /home/automation/plays/haproxy.yml that runs on servers in the proxy host group and does the following:

  1. Use geerlingguy.haproxy role to load balance request between hosts in the webservers host group.
  2. Use roundrobin load balancing method.
  3. HAProxy backend servers should be configured for HTTP only (port 80).
  4. Firewall is configured to allow all incoming traffic on port TCP 80.

If your playbook works, then doing “curl http://ansible2.hl.local/” should return output from the web server (see task #10). Running the command again should return output from the other web server.

Task 12: Security

Create a playbook /home/automation/plays/selinux.yml that runs on hosts in the webservers host group and does the following:

  1. Uses the selinux RHEL system role.
  2. Enables httpd_can_network_connect SELinux boolean.
  3. The change must survive system reboot.

Task 13: Use Conditionals to Control Play Execution

Create a playbook /home/automation/plays/sysctl.yml that runs on all inventory hosts and does the following:

  1. If a server has more than 2048MB of RAM, then parameter vm.swappiness is set to 10.
  2. If a server has less than 2048MB of RAM, then the following error message is displayed:

Server memory less than 2048MB

Task 14: Use Archiving

Create a playbook /home/automation/plays/archive.yml that runs on hosts in the database host group and does the following:

  1. A file /mnt/mysql_backups/database_list.txt is created that contains the following line: dev,test,qa,prod.
  2. A gzip archive of the file /mnt/mysql_backups/database_list.txt is created and stored in /mnt/mysql_backups/archive.gz.

Task 15: Work with Ansible Facts

Create a playbook /home/automation/plays/facts.yml that runs on hosts in the database host group and does the following:

  1. A custom Ansible fact server_role=mysql is created that can be retrieved from ansible_local.custom.sample_exam when using Ansible setup module.

Task 16: Software Packages

Create a playbook /home/automation/plays/packages.yml that runs on all inventory hosts and does the following:

  1. Installs tcpdump and mailx packages on hosts in the proxy host groups.
  2. Installs lsof and mailx and packages on hosts in the database host groups.

Task 17: Services

Create a playbook /home/automation/plays/target.yml that runs on hosts in the webservers host group and does the following:

  1. Sets the default boot target to multi-user.

Task 18. Create and Use Templates to Create Customised Configuration Files

Create a playbook /home/automation/plays/server_list.yml that does the following:

  1. Playbook uses a Jinja2 template server_list.j2 to create a file /etc/server_list.txt on hosts in the database host group.
  2. The file /etc/server_list.txt is owned by the automation user.
  3. File permissions are set to 0600.
  4. SELinux file label should be set to net_conf_t.
  5. The content of the file is a list of FQDNs of all inventory hosts.

After running the playbook, the content of the file /etc/server_list.txt should be the following:

ansible2.hl.local
ansible3.hl.local
ansible4.hl.local
ansible5.hl.local

Note: if the FQDN of any inventory host changes, re-running the playbook should update the file with the new values.

301 thoughts on “Ansible Sample Exam for EX407/EX294

  1. Hi @lisenet,

    Thanks for sharing this lab. I’ve used your RHCE lab for my studies and to be honest, in my humble option, your lab was harder than the proper exam itself. So I will use your lab again but this time for my EX407 studies and hopefully when I take the exam I will feel the same, and with a new cert on my belt.

    Thanks once again! :)

    • I adhere to Jhonatan comments, i used your guide on RHCE, and got my certification. So now onto Ansible with this guide. if it helps on something i did the prep-course on Linux Academy, and this guide is way harder and has more content.
      i just wanted to put my comment to thank you for your amazing work with this site which became my default “go to” when i’m studying any new subject.
      My most sincere thanks to you Tomas.

  2. Hello, Congratulations and thanks for this lab.
    First, I have a question about Task 5.
    How I can use condition with first caracter of userid?
    Thanks very much

    Fred

    • Hi, thanks very much! The goal of the sample exam is to test your Ansible skills. If you don’t know how to solve a task, then you need to work with Ansible documentation to find a solution. Good luck!

    • I had a hard time with this one but here is what I used.

      – name: create user for uid for webservers
      user:
      name: “{{item.username}}”
      password: “{{pass_works | password_hash(‘sha512’)}}”
      groups: wheel
      shell: /bin/bash
      ssh_key_file: .ssh/id_rsa
      generate_ssh_key: yes
      ssh_key_bits: 2048
      uid: “{{item.uid}}”
      when: ‘”webservers” in group_names and “12” in item.uid|string’
      loop: “{{users}}”
      – name: create user for uid for database
      user:
      name: “{{item.username}}”
      password: “{{pass_works | password_hash(‘sha512’)}}”
      groups: wheel
      shell: /bin/bash
      ssh_key_file: .ssh/id_rsa
      generate_ssh_key: yes
      ssh_key_bits: 2048
      uid: “{{item.uid}}”
      when: ‘”database” in group_names and “22” in item.uid|string’
      loop: “{{users}}”

    • Without posting a solution, I believe a helpful hint to solve this problem is regex.

    • you can also solve this by using multiple tasks using different hostgroup. so no conditions is needed and no regex is needed.

    • – name: create users WEBSERVERS
      user:
      name: “{{ item.username }}”
      uid: “{{ item.uid }}”
      groups: wheel
      shell: /bin/bash
      password: “{{ user_password|password_hash(‘sha512’, (256*256)|random(seed=inventory_hostname)|string) }}”
      when: “‘webservers’ in group_names and (item.uid|string)[0] == ‘1’”
      with_items:
      – “{{ users }}”

    • Nice Sample Exam Tomas well done!
      Solved all.

      If someone need a tip for solve some question I can give a hand too! :)

    • Hi Adam, my advise would be to check Ansible documentation. You will find the answer on docs.ansible.com.

    • Tomas
      Solved it with creating role, delegate_to and post_tasks :-)
      I hope that this is one of the proper solutions.

    • Hello Adam,
      Create role is not necessar for the Task18, just create template with magic variables.
      Thanks

    • For task 18, do we use vim editor and manually create the template file (server_list.j2) beforehand and then create the playbook (server_list.yml) to call the template file (server_list.j2) to create the /etc/server_list.txt on the managed nodes ? or do we create a playbook (server_list.yml) to dynamically create the the template file (server_list.j2) and then call the template file (server_list.j2) to create the /etc/server_list.txt on the managed nodes?

      Thanks.

  3. Hello Tomas,
    I have a question ?

    What are differences between two conditions:

    when: “‘proxy’ in group_names”
    and
    when: ” {{ inventory_hostname in groups[‘proxy’] }}

    thanks you very much

    • The first one looks for a group membership in the array of all the groups the host is in.

      The second one checks if the hostname as configured in Ansible’s inventory host file belongs to a specific group in the inventory.

  4. Hello Tomas,

    How much focus I need to give for HA Load Balancing configurations for ansible exam.. i.e. Can I expect the configuration of HA using ansible or NOT ??

    • It’s usefull to know, so I’d suggest you to learn how to do it. Anything that falls under the exam objectives can be expected during the exam.

    • Hey,

      Just to clarify, task 12 that utilises RHEL System Roles would be only on the newer v2.7 exam.
      RHEL System Roles was available from RHEL 7.4 only and the Ansible v2.3 (407k exam) was based on RHEL 7.3 afaik

      Also, in the official RH learning guide for this course in Ansible v2.3 2017, System Roles are not covered at all but they are covered in the latest v2.7 2019 learning guide.

      In other words, if you are doing the 407kv27 exam, definitely learn it, if you are doing the older exam, I guess you are OK to skim it, BUT learn it anyway as it seems Red Hat is going to be pushing more and more official system roles to be released so you will encounter them in the real world.

      PS. Instead of using system roles, for v2.3 play around with modules like wait_for and async/polling as there is an overlap there in terms of end results.

      Love your work by the way Tomas.

    • Yea, you’re right. Ansible v2.3 exam was based on RHEL 7.3, therefore you can skip the system roles question if you want to.

    • for “Enables httpd_can_network_connect SELinux boolean”, we must use variables or use module?


    • – name: First Plays
      hosts: webservers
      tasks:
      – name: block
      block:
      – include_role:
      name: rhel-system-roles.selinux

      rescue:
      – name: debug
      fail:
      when: not selinux_reboot_required

      – name: reboot
      reboot:

      – include_role:
      name: rhel-system-roles.selinux

      AND

      group_vars/webservers.yml:

      selinux_booleans:
      – name: httpd_can_network_connect
      sate: on
      persistent: ‘yes’

    • Sorry,

      AND

      group_vars/webservers/webservers.yml:

      selinux_booleans:
      – name: httpd_can_network_connect
      sate: on
      persistent: ‘yes’

    • “Sometimes, the SELinux role must ensure managed hosts are rebooted in order to completely apply its changes. However, it does not ever reboot hosts itselfs. This is so that you can control how the reboot is handled. But it means that it is a little more complicated than usual to properly use this role in a play.
      The way this works is that the role will set a Boolean variable, selinux_reboot_required to true and fail if a reboot is needed’ RedHat

  5. Hi, Tomas

    great work, for question 17, I can only think of running the command modile with systemctl set-default multi-user

    but that’s not what you intended to test right? service module seem unable to set the target right?

    • Try to avoid the command module in playbooks, because it’s easy to write non-idempotent playbooks this way.

      What happens when you set the default target with a systemctl command? Have a think about it.

    • Hi, Tomas

      Many thanks for the prompt replies, really appreciate it.

      I am aware that the command modules should be avoided. I tried to use the systemd module, surprisingly it didn’t throw any error, but also it didn’t change the default target, have you verify that your target has changed?

      “`
      tasks:
      – name: change target with systemd
      systemd:
      name: multi-user.target
      state: started
      enabled: True
      “`

    • Hi, Tomas,

      Managed to think about a more idempotent solution, but still wouldn’t be super excited about it, as this is not really a solution for service.

      “`
      file:
      path: /etc/systemd/system/default.target
      src: /lib/systemd/system/multi-user.target
      state: link
      “`

    • Hi Tomas,
      “What happens when you set the default target with a systemctl command? Have a think about it”:

      systemctl create a symlink?
      maybe it’s preferable to use file module
      Thanks you very much

  6. Hi Tomas,
    I have question for Task 10:
    “Apache service should be restarted every time the file /var/www/html/index.html is modified.”
    We must use “handlers” after template module?
    Thanks you very much

  7. thanks you

    I have other question:
    My service will restart only when my template is copied.
    But if my file index.html is modify by others, my handler don’t works.

    • That’s expected, because the handler only runs when it’s triggered. If you have a process that modifies the file but does not trigger the handler, then the handler will not run.

  8. Curious about questions to relate to updating config files.

    Not about the answer, more about best practices. The simplest solution for updating configs seems to be using replace with regex and lineinfile modules. Is the best practice to use such modules against config files or to use templates that get copied over?

    • If you want to update a single line, then using a regex with lineinfile makes sense. If you have multiple lines that need changing, then using a template might be a better option.

  9. Hi Tomas,

    For “Task 2: Ad-Hoc Commands”, with whitch user we must run script?
    root ?
    Thanks you

  10. Thanks Tomas for the sample test. I have a question regarding the exam if you can help me out here.

    Will we get access to docs.ansible.com or some form of it during the RedHat exam or only ansible-doc is available?

  11. Hello Tomas,

    In task 6 , “Each user should have an SSH key uploaded (use the SSH key that you create previously)”,
    I don’t understant; we must use key file of automation user?
    Thanks a lot

    • good, i understood, when you say “previously”, we must use ssh keys that we have created during creation of users ( alice…etc)
      thanks

  12. Task 2 , is achieved using adhoc command or playbook or running a script on the destination server ?
    This statement is confusing ”
    Create an SSH keypair. Write a script /home/automation/plays/adhoc that uses Ansible ad-hoc commands to achieve the following:”

    If we have to use adhoc commands then we dont need to create a file.
    I know during the exam we will not be getting any explanation to the question.. it up to the students to analyze and sort out the problem. ( KIOSK Exams )

    • You have to write a script that uses ad-hoc commands. I’m sorry, but I don’t see anything confusing about this question.

  13. Task 6. Are we supposed to be parsing the first character of uid ? For the life of me I can’t parse it unless I force uid to be a string in the user_list.yml by surrounding the uid’s with double quotes. I could use , etc on the number but that is not what we are looking for either I suppose. Maybe i’m reading into your question too much ?

    Thank you for putting this together it is really making be dig into it and prep for the exam.

    Thanks,
    Phil

  14. Thank you for this post again, Tomas. I got my certification yesterday on ver. 2.7 and this exercise was extremely helpful. I am glad I found your site :)

  15. 2. SSH key (that you generated) is copied to all inventory hosts for the automation user and stored in /home/automation/.ssh/authorized_keys.

    Hi Tomas.
    in this question I must create ssh-key with script adhoc or I can create ssh-keygen before this script?
    Can you show ad-hoc comand for this question please.

  16. I tried for few hours, i could not find the substring or taking out first charecter from a string in the documentation, either i am dumb or its too much for a non genius to get certified. Giving up :(

  17. For task 6, I do that:


    – hosts: all
    become: yes

    vars_files:
    – secret.yml
    – user_list.yml

    tasks:
    – name: Creating users
    user:
    name: “{{ item.username }}”
    uid: “{{ item.uid }}”
    password: “{{ user_password | password_hash(‘sha512’) }}”
    shell: /bin/bash
    groups: wheel
    with_items: “{{ users }}”
    when: (( inventory_hostname in groups[‘webservers’] ) and ( “{{ item.uid }}” >= “1000” and “{{ item.uid }}” = “2000” and “{{ item.uid }}” = “1000” and “{{ item.uid }}” = “2000” and “{{ item.uid }}” < "3000" ))

  18. For task 6, I do that:

    – hosts: all
    become: yes

    vars_files:
    – secret.yml
    – user_list.yml

    tasks:
    – name: Creating users
    user:
    name: “{{ item.username }}”
    uid: “{{ item.uid }}”
    password: “{{ user_password | password_hash(‘sha512’) }}”
    shell: /bin/bash
    groups: wheel
    with_items: “{{ users }}”
    when: (( inventory_hostname in groups[‘webservers’] ) and ( “{{ item.uid }}” >= “1000” and “{{ item.uid }}” = “2000” and “{{ item.uid }}” < "3000" ))

    • this when statement also works for me:

      when: inventory_hostname in groups[‘webservers’] and item.uid|string|first == “1”

  19. @lisenet for question 18 . I Got some solution but I’m unable o get the full inventory, I’m able to take the inventory of webserver group only if I mention ‘all’, it throws an error.

    this is my templates– and can you tell me how to achieve all server inventory details.

    127.0.0.1 localhost
    ::1 locahost

    {% for host in groups[‘webserver’] %}
    {{ hostvars[host].ansible_default_ipv4.address }} {{ hostvars[host].ansible_facts.hostname }} {{ hostvars[host].ansible_facts.fqdn }}
    {% endfor %}

    • $ cat server_list.j2
      {% for host in groups[‘all’] %}
      {{hostvars[host][‘ansible_facts’][‘fqdn’]}}
      {% endfor %}

      $ cat server_list.yml

      – hosts: all
      become: yes
      tasks:
      – name: copy template
      template:
      src: server_list.j2
      dest: /etc/server_list.txt
      mode: ‘0600’
      setype: net_conf_t
      when: inventory_hostname in groups[‘database’]

    • the question says only apply to the database group not all. That’s what i am thinking too. If i set to all, i can easily get the results. Scratching my head for a solution.

  20. Thank you for those awesome questions! lisenet.

    for task 6. I am confused with the following two statements because my understanding is they are not same.

    *Each user should have an SSH key uploaded (use the SSH key that you create previously –if the ssh key is the same one specified in the Task 2, then the user, automation, can ssh into users’s respective servers as their account without passwords.)

    *After running the playbook, users should be able to SSH into their respective servers without passwords. — does it means users can ssh into their respective servers from the control node? then it needs these users exist and have their ssh key generated on the control node.

    • Thanks for your reply. Tomas :-)
      In task 2, it only asks to generate the ssh key for the user, automation. It does not mention to generate the ssh keys for other users on the control node.

      for the task 6, I use two plays in the playbook. the first play to create users and their ssh public key on control node, update known_hosts key for them. the second play to upload their ssh public key to their respective server. so the users can ssh their servers without password from the control node.

    • It does not have to. You can reuse the same SSH keys as many times and on as many users as you want.

      The goal of this sample example is to allow people to test theirs Ansible skills. Whether you write one play or two plays is irrelevant, as long as you solve the task. How you solve it is really up to you, because there is no one right answer.

  21. Thank you for the nice information, Tomas
    It is good to know. I never thought different accounts can use the same SSH keys(paired private/public SSH keys) for the SSH passwordless login before. it makes sense. I did a quick test and it works. Thanks again :-)

    • You’re welcome! It’s just a keypair, similar to TLS certificates, you can deploy them on multiple servers.

  22. First of all, i wanted to thank you for doing an awesome job, i’m planning to take the ansible exam in 5 weeks, i have noticed on the second task, when i try to modify the /usr/sbin/visudo file, it looks like its taking the changes but when you look inside the file, there’s nothing in there. So i ended up creating a file in the file in the /etc/sudoers.d

    Thank!

    • Thanks Tomas, awesome job.

      I use to change escalation privileges using /sbin/visudo (CentOS7). Can I keep using it or is there any module to change the escalation privileges?

  23. Thanks for the work you do for linux,
    Please point me to a reliable study guide for ex407 exam.
    I also have a situation where our remote servers do not permit remote root login via ssh, we use su to elevate from a normal user. My question is how do I su to root when deploying playbook as the ssh key if the support user not root without compromising security.
    thanks

    • I used DO407, if you have RHLS, that’s all you need.

      If you want to use su, then take a look at become_method: su.

  24. I’ve been using DO407 to study, that also includes a bit of Tower and Vagrant, however, I don’t see these mentioned as an objective on the EX407 exam. Would it be safe to assume they are not covered on the day?

  25. Hi, thank you for the tips, i have some questions:
    1- will this questions help me to pass EX294(RHEL8) exam. cause i heard both exams are basically the same.
    2- in Task10, you said “Apache service should be restarted every time the file /var/www/html/index.html is modified.”
    does this mean whenever i change index.html via the palybook or any change will trigger httpd to be restarted.
    3- how to note this questions to be near the actual exam questions.
    4- The services that appears in the tasks, are limited (for example only httpd and mysql), or we can face other complicated services such as (iscsi…ldap….)

    Thank you in advance.

    • The sample questions should help you to get an idea of what the exam might look like. If you can solve them all in less than 3 hours, then you are in a position to book the exam.

      With regards to the #10, the service should be restarted every time the playbook modifies the index file.

      In theory you can expect any service that is covered by official Red Hat training to appear on the exam.

  26. I don’t see how it’ meant to create ad-hoc script for generating SSH-KEYS on the nodes when you haven’t provisioned the controller’s key?

    • As the tasks says, you need to generate an SSH keypair on the control node and then copy the files to all inventory hosts for the automation user.

  27. Hi,
    Just wanted to say thank you for putting together this excellent practice exam. Unfortunately for me I discovered it too late (literally a day before my EX294 exam). I did not make it but plan a retake after practicing with this exam and being able to complete it within 3 hours. I literally ran out of time…

  28. Hi Everyone,

    In my practice lab I am working on below mention scenario,

    1. Create a logical volume (“mylvol1”) of size 1024M on volume group (“myvg1”)
    With Given conditions:
    a. If volume group doesn’t exist, message “Volume Group does not exist” should be displayed.
    b. If there is not enough free space in volume group (“myvg1”), message “Not enough Free Space” should be displayed. And logical volume (“mylvol1”) should be created with 700M size instead of 1024M.

    I am unable to create logic for condition “b”, please guide me.

    Thanks.

    • The logic for part b should be something like this:

      Check the disk space of the volume group, if it’s less that the required volume size, then display the message, if it’s greater that the required volume size, then create the volume.

  29. Tomas, I am not exaggerating or making up stories here. but the grading in RHCE is really weird. The script that they are running to check your config and output is unimaginable. :) But I am an RHCE now.
    My serious question to you.

    What do you think Ansible exam? you got 300/300. Are they just focus on the result? they dont care about your playbook etc?

    Is this just a result based?

    • I think that it’s an interesting yet challenging exam that requires you to know Ansible really well. As with all Red Hat exams, you need to solve problems, and the outcome is what matters the most from my experience.

      There are different ways you can write a playbook that achieves a specific goal (e.g. creates a new partition). The content of such playbook will depend on your experience and familiarity with the tool. As long as your playbook creates a partition, you should receive points for the task.

      It helps if you approach Red Hat exams from a customer’s point of view (pretty much what happens in reality). If a customer asks you to deploy a new website, they expect the end result to be a running website, they don’t care that much whether you use Apache, Nginx or Lighttpd to deliver the solution.

  30. Hi Tomas

    I wrote my ex294 and passed thanks to you. Honestly when I first found this page , I found many of the questions challenging, but after some practice it was a breeze . Thanks again

  31. Hi Tomas,

    For task 5, does it matter if we modify the user_list.yml file? Like so:


    users:
    – username: alice
    uid: 1201
    – username: vincent
    uid: 1202
    – username: sandy
    uid: 2201
    – username: patrick
    uid: 2202

  32. question no.2 “/home/automation/plays/adhoc” a directory?
    do files containing ad hoc commands need to be in a directory?

  33. Hi,

    For task 11 “Use geerlingguy.haproxy role to load balance request between hosts in the webservers host group”, the value of backend should be set automatically based on the inventory ?

    The var haproxy_backend_servers can be dynamically created ?

    • Can someone had a response of this ?
      I tried multiple solution but won’t be able to construct this variable with this structure
      haproxy_backend_servers:
      – name: foo-app
      address: foo-address
      – name: bar-app
      address: bar-address

    • You can do something like this:

          haproxy_backend_servers:
            - name: web1
              address: ansible3.hl.local:80
            - name: web2
              address: ansible4.hl.local:80
  34. I would to share my solution for task 6 , thats so hard but I do it :D ! I hope to schedule the exam in january in 2020.

    – hosts: all
    become: true
    vars_files:
    – user_list.yml
    – secret.yml
    tasks:
    – user:
    name: “{{ item.username }}”
    uid: “{{ item.uid }}”
    password: “{{ user_password | password_hash(‘sha512’) }}”
    shell: /bin/bash
    groups: wheel
    with_items: “{{ users }}”
    when:
    – “‘webservers’ in group_names and item.uid|string|first == ‘1’ ”
    – user:
    name: “{{ item.username }}”
    uid: “{{ item.uid }}”
    password: “{{ user_password | password_hash(‘sha512’) }}”
    shell: /bin/bash
    groups: wheel
    with_items: “{{ users }}”
    when:
    – “‘database’ in group_names and item.uid|string|first == ‘2’ “

    • Hi,
      Nice one on the item.uid|string|first == ‘1’ !
      I see you’re using ‘with_items: “{{ users }}”’. I’ve seen this in a lot of other playbooks as well and I was wondering what the preferred way is?
      The official RedHat course materials for EX294-8.0 state that since Ansible 2.5, the recommended way to write loops is to use the loop keyword. However, you do need to know the with_items keyword because you can encounter it in older playbooks…
      I’m guessing you should still get a pass if you use the older style, seeing as that it still works.

    • Hi Serge thanks for your tip!

      I’ve revisited the ansible documentation for loop scenarions and yes they recommend use loop than with_items.

      I will force me to write with loop .

      Thanks!

    • you can simplify your playbook by using one task

      – name: ansible playbook task 06
      hosts: all
      vars_files:
      – user_list.yml
      – secret.yml
      become: yes
      tasks:
      – user:
      name: “{{item.username}}”
      uid: “{{item.uid}}”
      password: “{{user_password|password_hash(‘sha512’)}}”
      groups: wheel
      shell: /bin/bash
      loop: “{{users}}”
      when: (ansible_fqdn in groups[‘webservers’] and item.uid|string|first == “1”) or (ansible_fqdn in groups[‘database’] and item.uid|string|first == “2”)

  35. So for task 6 is there any reason why my playbook would not be idempotent? Not sure how to ask this without giving the answer away. I’m simply user the user module to loop through the users list provided with appropriate conditionals targeting the UID and the server’s group membership. It works as expected but I always get a changed result when I run it.

  36. Hi,

    I passed the ex407 and now i want to take the EX294. Is there a difference? Do I need to study extra for the EX294? or can I directly book my exam?

    • I didn’t take EX294 so don’t take my word for it, but looking at the exam objectives as well as Red Hat training material they are almost identical. If you studied and passed EX407, you should be able to pass EX294 because the objectives are 95% the same.

  37. Task 16 asks us to run the playbook against all hosts but then says to only define a variable for groups proxy and database. This means the playbook would fail against the webservers group as the variable wouldn’t be defined. I’m assuming that the solution here is to implement a conditional on the task to only run when the packages variable is defined? Seems like the obvious fix but wanted to confirm that there’s nothing else I’m missing.

    Thanks!

    • Task 16 asks you to run the playbook against all hosts and install packages based on host groups. Whether you decide to use variables or not is completelly up to you.

    • Makes sense. Thanks

      BTW I just passed the ex294 with a 276 in large part due to this site. So thanks for the quality material as usual!

  38. Hi Tomas,
    Just did my EX294 re-take and passed it with a 279 score. So just wanted to say a big THANK YOU for putting this sample exam together, it has been a great help! In fact more so, in my opinion, than the lab materials offered with the official RH294 course on RHLS….

    If anyone is interested, I will post a terraform plan, an ansible playbook and a few ancillary scripts to easily build, configure and start, stop a lab, on github that you could use to practice this sample exam.

    Requirements are:
    – KVM installed (works on CentOS 8 and Fedora 30)
    – RHEL8 qcow2 image (you can download this if you have a free Red Hat developer account, it’s only 713MB).
    – about 6GB of memory available on your system (I’ve given all VMs 1024MB of memory but you can easily change this)
    – Approx 14GB of free space on root

  39. Fabiano, first of all congratulations for the fact, that you’ve managed to pass the EX407 exam. I would like to ask you, if the questions given by Tomas ( Tomas, your sample exam, appears to cover almost 100% of the topics, great job!!! ) were more difficult than the “real” ones and if there was a mandatory bash script creation on the exam?
    Thank you in advance

  40. Hi
    I prepare for EX294 and i have following question:
    I configure partition on disk with condition, when size is too big for disk, fail, display message.
    I do something like that:
    tasks:
    – name: block for disk
    block:
    – name: check size
    parted:
    device: /dev/vdb
    number: 1
    state: present
    part_end: 1GiB
    register: disk_data
    ignore_errors: yes
    – name: fail size
    fail:
    msg: “{{ disk_data.failed }} ERROR”
    when: disk_rozmiar.failed == 1

    It’s work, but I wonder about when condition.
    In my registered variable key failed it’s only I think for check that size is ok or not.
    This is output from registered variable:
    “msg”: {
    “changed”: true,
    “disk”: {
    “dev”: “/dev/vdb”,
    “logical_block”: 512,
    “model”: “Virtio Block Device”,
    “physical_block”: 512,
    “size”: 2097152.0,
    “table”: “msdos”,
    “unit”: “kib”
    },
    “failed”: false,
    “partitions”: [],
    “script”: “rm 1”
    }

    It’s this condition is properly set, or maybe i’m doing something wrong.
    Regards

    • Hi
      i want achieve, that when a partition size is a bigger than free disk space, then I have error msg in play, and from rescue block tasks are run
      Regards
      Tom

    • OK, what you want to do is to gather disk size info and register it so that you can use it later. Then implement a logical operation where an error message is displayed if the partition size is bigger that the disk size.

      There are several ways you can get disk size info, you can use stat, you can use parted, or you can use Ansible facts.

      Take a look at one of my Ansible playbooks on GitHub.

  41. Hello Tomas,

    Quick Question. For the task 12.
    “Uses the selinux RHEL system role”

    Should I install system role using galaxy and use them?

  42. Hi Tom,

    Task 2: Can you please clarify how can automation user on control node use ansible adhoc commands to setup itself (automation) on managed hosts when the user itself doesn’t exist there?

    Thank you

  43. Thank you Tom!

    Task 1: requires privilege escalation disabled by default. With this in place, is the expectation for automation user to be able to run privileged commands on managed hosts without manually using -b | –become ? If this is true then I don’t understand how could that be, please help me understand.

    • It means that you need to disable by default. It doesn’t say that you can’t use privilege escallation.

  44. Thank you

    I am preparing for EX294 towards RHCE, anyone know if there are more sample exams and any other practice, preparation for EX294?

    thanks for help

  45. Hi Tom,

    Task 6: Instruction is not clear to me how the users would be able to ssh to inventory hosts without passwd using the key already created for automation user from (task 2)?

    How would the key from one user allow another user to ssh to a remote server ?

    Thank you

  46. For the sample exam if that’s what needed ….. great. But for my own understanding I still don’t get it.

    Thank you

    • When you use ssh-keygen to generate a public/private RSA key pair it does just that – creates a key pair. The key pair can be used with any user on any system. You would normally have a key pair per user account, however, this is not set in stone.

      You can use the same key pair for multiple users if you want to. It’s the same thing as if you were to use identical passwords for different user accounts. I hope this clarifies things for you.

  47. Hello Guys,

    While working on my lab, I’m not getting correct idea.

    1. While Creating a logical volume (“mylv1”) of size 1200M on volume group (“myvg1”)

    2. If volume group doesn’t exist with enough space then it should display message “Volume Group does not have enough space” should be displayed.

    3. If there is not enough free space in volume group (“myvg1”), message “Not enough Free Space” should be displayed. And logical volume (“mylv1”) should be created with 600M size instead of 1200M.

    I’m unable to write logic

    Thanks in advance.

    • The logic could be something like this:

      1. Check if the volume group exists.
      2. If it does not exist, print an error message.
      3. If it does exist, check the size of the volume group.
      4. If the size is sufficient, proceed with volume creation.
      5. If the size is insufficient, display an error message.

  48. Task 8: In the thread above I see rpm_key being suggested. If the read task 8 instructions it does not ask to install the gpg key. My understanding the key will be applied automatically for any package installs from the repo.

    Is it really required to install the key?

    Thanks

  49. Doubt on Task1 and Task2
    Task1 – says that the user that the adhoc scripts and playbooks should run as automation user
    Task2 – says to create the user automation on all the managed nodes using adhoc script.

    Doesn’t it contradict or am I missing something.
    First task says to run ansible as automation user and second says to make users on managed nodes.

    • Task 1 says that you need to configure ansible.cfg to use the automation user. It says nothing about ad-hoc commands. Note that you have root access to all five servers.

      Task 2 asks you to use Ansible ad-hoc commands to create the automation user. Ansible will default to running from your user account. Note that you can use the -u switch to connect as a different user.

    • So Tomas, as I understood TASK2 has to be run as root user and then the subsequent tasks with automation user?

  50. Hi Tomas, you help me with my RHCE, now I think you can help me with EX407.
    My question to you, is this sample 100%? Whats the reason why you did not cover Tower and Galaxy?
    Thank you

    • Ansible Tower is not part of the exam objectives (at least not for Ansible 2.7 which I took). Galaxy is included in the sample exam.

  51. Hi Tomas,
    Task two, when I tried to use when condition against uid it won’t recognise it.
    I tired to do it like this when: “{{ users[‘uid’] > 1200 }}”
    i can get the user name using item.username along with with_items
    any clue or advice

    • Your goal is to get two users created in the webservers group, and two users in the database group according to the requirements. If you can solve it by using item.username, then it’s totally fine.

  52. HI everyone! Happy new year! for task 4 would you use a template with a configured sshd_config file per requirements or would you use lineinfile and execute the playbook in 3 tasks for the sshd_config file modification following with a handler to restart sshd service?

  53. Hello everyone, I have a couple of questions regarding the EX294 exam – are all the machines ( the “master” node and all its hosts ) already configured ( I mean IP-s are set, there’s a root user already created ), or we need as in the RHCSA exam, configure the network, default gateway etc? And regarding the scripting task – are we allowed to install “sshpass” in order to facilitate the script execution, or it’s forbidden ( if so, could you guys share a bit of your knowledge, how exactly could we skip the initial “ask password” moment )? Because, I’m struggling to find the way how to fulfill the criteria “use Ansible ad hoc commands to first create the user, then copy the ssh-key and finally put a specific content into the sudoers file / or we can just append the newly created “automation” user to the “wheel” group —> the way that I see the things is : run the script as a root ( hence one have ssh key pair to send to the managed hosts ), then using the -u switch to the Ansible ad hoc commands execute them as the “automation” user / obviously his own ssh-key needs to be created separately before sending it to the hosts).

    Thank you in advance

    • I can’t tell you what’s on the exam, but all the info that you’ve been given is sufficient to solve the questions.

  54. Hello Tomas,
    First thing,your test questions are challenging and great for studying and practicing!

    For Task 3: Can I use 3 different copy modules and change the content: using variables and use different when: inventory_hostname in groups[‘proxy’], groups[‘webserver’], groups[‘database’] to do this task or am I supposed to use loop to do this?

    • Thanks! You can do it either way. There is no one right solution, you can solve the question however you see fit.

  55. For Task 18: Is the assumption that the Jinga2 template (server_list.j2) is created ahead of time for the playbook use?

    • Please disregard this question. The template file needs to be created on the control node ahead of time and gets pushed out to the managed node when the playbook is run.

  56. Hi Tomas,
    For Task 2, any clue what is wrong with this?
    ansible all -m user -a ‘name=automation password={{ ‘devops’ | password_hash(‘sha512’, ‘mysecretsalt’) }}’ -b -u root

    I get error saying:
    “msg”: “The tasks includes an option with an undefined variable. The error was ‘sha512’ is undefined”

    I tried to used the FAQ encrypted password for the user module and it is not working. :(

    Thanks.

  57. Hi Tomas,

    I have given EX294 exam thrice but got failed all the times. I have got all the outputs in exam but I got 177 in first and 2nd attempt and 145 in 3rd attempt. Whereas I have verified output for all the questions and got the correct output. I am going to write exam on 23rd Feb,2020 again. Can you please suggest me tips to clear this exam, it’s very important for me to clear the exam. Hope I will get response from you ASAP. Thank you!!

    • My advise would be to ensure that you don’t run your playbooks with root privileges unless it is required.

  58. Hi Tomas!
    Yesterday I have failed my ex407 v2.7 with 96 scores:( I have an official learning subscription and complete do407 course, but the practical part is very poor and not enough. I have seen at one of your posts where you are advised to practice such a lot as you can. Can you please advise what practical lab/materials/tasks do you use to prepare your practical part for this exam?
    Also, I am interested in what differences between v2.3 and 2.7 because both exams are available.
    Thanks for your posts.

    • I spent a lot of time writing playbooks for various services (Apache, MySQL, Postfix, HAproxy, FTP, SSH etc) using my homelab. I also converted my Puppet manifests to Ansible playbooks, that helped a lot. Try automating services that you use at work.

      I am aware of the two exam versions, but I don’t really know the differences between v2.3 and v2.7. I took the latter one.

  59. Hi Tomas,

    First of all thanks for the effort you made.

    I planning for Ansible exam, next week and I have one question regarding the Task2(creation of “automation” user)
    I am fine with creation of user and pushing the ssh_keys (both with adhoc and playbook).

    My question is does this user by default has privilege to do sudo without password. Or we need to configure them on the managed nodes in the exam.

    I know it is not advisable to edit the visudo file… I am aware alternatively to edit the /etc/sudoers.d and make entry for that user accordingly.

    Just I want to how to tackle this situation . Any idea will be really helpful for the exam.

    Thanks,

    • In the sample exam case the automation user does not exist by default. It is therefore up to you how you want to handle it. You can create a user and then configure the system to allow sudo access.

  60. Hi, Tomas. Thank you for sharing this sample exam.
    I have a question about it. Is necessary to complete this sample exam in how long to be prepared for the ex407?

    • The Ansible exam is 4 hours, therefore you probably want to spend less than that solving the sample exam. I’d aim for 3 hours.

  61. Hi Tomas,

    I can’t find the ansible ad-hoc command to copy the key to the servers
    On examination is it possible to use this directly ” ssh-copy-id @” ?

  62. Hi Tomas. Congratulations on passing ex407. Can you tell me which exam you think is harder, your exam example or the ex407?

  63. Hi Tomas, I have failed 3 times. I am planning to give exam again on Feb 25. Though I got all the output still did not through the exam. In configuration file I gave become_method=su under privilege escalation. Please share some tips to pass the exam.

    • I’m sorry to hear that, from my experience, the key to success is practice. Can you solve all sample exam questions in 3 hours?

    • Yes, I am have completed the paper within 3 hrs. Also verified all the Output. I see repos were installed on all the nodes but but got result in that objective as 0. For each and every question I got exact output.

  64. Hi Tomas,
    Please any updates on EX294? I am planning to sit the exams but have no sample exams or real materials for preparation apart from Sander Van Vugt classes on oreilly.

    Thank you

    • What update are you talking about exactly? The sample example for EX294 is the same as for EX407. They share almost identical exam objectives.

  65. Hi Tomas,

    I have just finished TASH 2 (Ad-Hoc Commands). I wanted to send it to you for your feedback. Where can I send it to you?

    • Well done. The goal of the sample exam is to allow you to test your skills, since there are many different ways you can solve Ansible tasks, providing feedback on an individual basis isn’t something that I offer I’m afraid.

  66. Hi Tomas,

    First of all thanks for your effort with sample exam preparation. Good job !

    I have a question regarding Task 2 (ad hoc ). Please correct me if I’m wrong.

    You mentioned that all tasks should be executed with “automation” user. But executing ad hoc command “ansible all -m user -a ‘name=automation state=present’ -u root” under automation user always failed because the user has no access to root ssh keys !

    To workaround this we can add to the addhoc command –ask-pass and provide root password but I suspect that root password is not known in real exam.

    Thanks for explanation

    • Task 2 asks you to use Ansible ad-hoc commands to create the automation user (this user does not exist at this point). Ansible will default to running from your user account. Note that you can use the -u switch to connect as a different user, e.g. root. You know the root password in the sample exam scenario (because it’s your VMs), this is sufficient to solve the task.

  67. Hello, Can anyone help me with a playbook to check if the managed servers are in timesync with the control node, I have used the timesync role to achieve the sync, however I want to write a task that checks the same conditionally.

  68. Thank you so much Tomas,

    I have passed the ansible exam and i was practicing your sample paper. It was really helpful.

  69. Wanna ask about #task 2. So I should create script named adhoc and inside this script I write ad-hoc command. Then I just run something like ‘./adhoc’ ??

  70. Hi Tomas,

    For # task 9, how do I check the Volume group exists using stat (what should be provided in path).

  71. Hello Tomas ,
    I can’t perform this task 6: “After running the adhoc script, you should be able to SSH into all inventory hosts using the automation user without password, as well as a run all privileged commands.”
    For example, do I have to copy alice’s public key to the vincent and vincent user to Alice?
    Is there a specific form to do this?
    Or is it simpler than expected?
    I’m wasting a lot of time figuring this out. Could you help me?

  72. Hi, Tomas. I would like to say thank you for the ansible sample exam that you have created. It really helps me to pass on the exam.
    Now, I will take the OpenShift Administrator EX280 :)
    Thank you again, man.

  73. Hi Tomas,
    Could you please clarify something for me?
    The control-node has passwordless access to the inventory hosts(using root). Ansible will run on “automation” user on the control-node. So in order to run adhoc commands with “-u root” ( to create the ‘automation’ user on the inv hosts) , user “automation” needs to connect on inventory hosts on user root. What is the proper way to achieve this?
    1.I reset the root passwords on all inv hosts and use ‘–ask-pass’ on the adhoc command
    2. Or I MANUALLY copy the public key of ‘automation’ user from control-node to the ‘root’ user of each inv hosts? This way the control-node will be able to connect in the end to both users root and automation on every inv host with the same key.

    I am interested in the right way that RedHat would point for this.

    Thanks!

    • Yes, the control node has passwordless access to the inventory hosts using the root account. You need to configure that part manually when you set your homelab environment up.

    • Well, both solutions work. Is there a prefered way considering the way RedHat validates this step?
      Thanks for this site and for replying.

  74. Hi Thomas,

    The last part of task 6 (After running the playbook, users should be able to SSH into their respective servers without passwords.”) , I can be done by logging into the machines and running the “ssh-copy-id” command from one user to another or I have to do it in the users.yml playbook ?

    Thanks

  75. Hi,

    First off, thanks for this practice exam! Second, i’m doing task 6, i have it working in regards to creating the users on the servers, what i’m struggling to understand is the SSH key, should it be another play using authorized key module to put the key from /home/automation/.ssh/id_rsa.pub on each server in the users home directory? Or am i looking to much into it?

    • Hi Pmtaa,
      In order to achieve Task 2 requirements you need to connect on the “root” user on each inventory host. So you need to use the “-u root” option. Option “-b” is used when you need to elevate privileges – and it is not the case here.

    • Hi, you will want to use the -u option with root as the user as this will run the ansible command as the root user. If you have set your environment up correctly you shouldn’t need the -ask-pass flag, don’t forget these commands are going to be run by a script. The -b flag option will elevate the user that is executing the ansible command to root but the automation user doesn’t have root permissions at this stage.

    • How to setup the environment correctly and not have to use the –ask-pass for root?

      Thanks.

    • Hello,
      How to setup the environment to correctly so the –ask-pass flag is not needed?

      Is this the correct way to setup the environement and not be prompted for the root password when running the adhoc command?
      ansible all -e “ansible_user=root ansible_password=rootpassword” -u root -m user -a “name=ansible password={{ ‘ansible’ | password_hash(‘sha512’, ‘mysecretsalt’) }}”

      Thanks in advance for your help.

  76. Hi Tomas,

    For Task 3, I have the below playbook that works fine.

    – name: Replace the existing content of /etc/motd
    hosts: all
    become: true
    become_user: root
    tasks:
    – name: Replace /etc/motd on PROXY host group
    content: ‘Welcome to PROXY’
    dest: /etc/motd
    when: inventory_hostname in groups[‘proxy’]

    – name: Replace /etc/motd on webservers host group
    content: ‘Welcome to webservers’
    dest: /etc/motd
    when: inventory_hostname in groups[‘webservers’]

    – name: Replace /etc/motd on database host group
    content: ‘Welcome to MYSQL server’
    dest: /etc/motd
    when: inventory_hostname in groups[‘database’]

    In the above playbook if I use “when: inventory_hostname = ‘ansible2’ ” instead of “when: inventory_hostname in groups[‘proxy’] it is not working. What is wrong with that?

  77. Hi Pmtaa,
    In order to achieve Task 2 requirements you need to connect on the “root” user on each inventory host. So you need to use the “-u root” option. Option “-b” is used when you need to elevate privileges – and it is not the case here.

  78. Hi Tomas,

    For Task 9, I am getting the below error message.

    TASK [sample-mysql : Change mysql root password] *******************************************************************************************************************************************************************************************
    fatal: [serverc]: FAILED! => {“changed”: false, “msg”: “The PyMySQL (Python 2.7 and Python 3.X) or MySQL-python (Python 2.X) module is required.”}

    I have both python 2.7 and 3.6 installed.
    [[email protected] ~]# yum module list|grep python
    libselinux-python 2.8 common Python 2 bindings for libselinux
    python27 2.7 [d] common [d] Python programming language, version 2.7
    python36 3.6 [d][e] common [d], build Python programming language, version 3.6
    [[email protected] ~]#

    My tasks/main.yml:

    # tasks file for sample-mysql
    – name: Create primary partition
    parted:
    device: /dev/sdb
    number: 1
    label: gpt
    flags: lvm
    part_start: 1MB
    part_end: 800MB
    name: primary
    state: present

    – name: Create volume group
    lvg:
    vg: vg_database
    pvs: /dev/sdb1

    – name: Create logical volume
    lvol:
    vg: vg_database
    lv: lv_mysql
    size: 512m
    state: present

    – name: Create file system
    filesystem:
    fstype: xfs
    dev: /dev/vg_database/lv_mysql

    – name: Mount the file system
    mount:
    path: /mnt/mysql_backups
    src: /dev/vg_database/lv_mysql
    fstype: xfs
    state: present

    – name: Install mysql package
    yum:
    name: mysql-community-server
    state: present

    – name: Configure Firewall
    firewalld:
    port: 3306/tcp
    permanent: yes
    immediate: yes
    state: enabled

    – name: Change mysql root password
    mysql_user:
    name: root
    check_implicit_admin: yes
    login_user: root
    login_password: “{{ database_password }}”
    priv: ‘*.*:ALL’
    state: present

    – name: Start mysql
    service:
    name: mysql
    enabled: yes
    state: started

    – name: Copy my.cnf.j2 template
    template:
    src: my.cnf.j2
    dest: /etc/.my.cnf
    notify:
    – restart mysql

    • Thanks a lot that fixed the issue. How do I know if mysql is working fine? Below is the output I get when I run the playbook.

      [[email protected] plays]$ ansible-playbook mysql.yml –vault-password-file=vault_key

      PLAY [Main playbook for mysql] *************************************************************************************************************************************************************************************************************

      TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************************
      ok: [serverc]

      TASK [command] *****************************************************************************************************************************************************************************************************************************
      [WARNING]: Consider using the yum module rather than running ‘yum’. If you need to use command because yum is insufficient you can add ‘warn: false’ to this command task or set ‘command_warnings=False’ in ansible.cfg to get rid of
      this message.

      changed: [serverc]

      TASK [sample-mysql : Create primary partition] *********************************************************************************************************************************************************************************************
      ok: [serverc]

      TASK [sample-mysql : Create volume group] **************************************************************************************************************************************************************************************************
      ok: [serverc]

      TASK [sample-mysql : Create logical volume] ************************************************************************************************************************************************************************************************
      ok: [serverc]

      TASK [sample-mysql : Create file system] ***************************************************************************************************************************************************************************************************
      ok: [serverc]

      TASK [sample-mysql : Mount the file system] ************************************************************************************************************************************************************************************************
      ok: [serverc]

      TASK [sample-mysql : Install python3-PyMySQL] **********************************************************************************************************************************************************************************************
      ok: [serverc]

      TASK [sample-mysql : Install mysql package] ************************************************************************************************************************************************************************************************
      ok: [serverc]

      TASK [sample-mysql : Configure Firewall] ***************************************************************************************************************************************************************************************************
      ok: [serverc]

      TASK [sample-mysql : Start mysql] **********************************************************************************************************************************************************************************************************
      ok: [serverc]

      TASK [sample-mysql : Change mysql root password] *******************************************************************************************************************************************************************************************
      ok: [serverc]

      TASK [sample-mysql : Copy my.cnf.j2 template] **********************************************************************************************************************************************************************************************
      changed: [serverc]

      RUNNING HANDLER [sample-mysql : restart mysql] *********************************************************************************************************************************************************************************************
      changed: [serverc]

      PLAY RECAP *********************************************************************************************************************************************************************************************************************************
      serverc : ok=14 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

      [[email protected] plays]$

    • systemctl status confirms mysql is running. Anything else I can check if mysql is OK?

      [email protected] ~]# systemctl status mysql
      ● mysqld.service – MySQL Community Server
      Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled)
      Active: active (running) since Tue 2020-03-10 07:53:55 EDT; 9min ago
      Process: 12589 ExecStartPost=/usr/bin/mysql-systemd-start post (code=exited, status=0/SUCCESS)
      Process: 12573 ExecStartPre=/usr/bin/mysql-systemd-start pre (code=exited, status=0/SUCCESS)
      Main PID: 12588 (mysqld_safe)
      Tasks: 23 (limit: 17484)
      Memory: 136.8M
      CGroup: /system.slice/mysqld.service
      ├─12588 /bin/sh /usr/bin/mysqld_safe –basedir=/usr
      └─12763 /usr/sbin/mysqld –basedir=/usr –datadir=/var/lib/mysql –plugin-dir=/usr/lib64/mysql/plugin –log-error=/var/log/mysqld.log –pid-file=/var/run/mysqld/mysqld.pid –socket=/var/lib/mysql/mysql.sock

      Mar 10 07:53:52 serverc systemd[1]: Stopped MySQL Community Server.
      Mar 10 07:53:52 serverc systemd[1]: Starting MySQL Community Server…
      Mar 10 07:53:54 serverc mysqld_safe[12588]: 200310 07:53:54 mysqld_safe Logging to ‘/var/log/mysqld.log’.
      Mar 10 07:53:54 serverc mysqld_safe[12588]: 200310 07:53:54 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
      Mar 10 07:53:55 serverc systemd[1]: Started MySQL Community Server.
      [[email protected]serverc ~]#

  79. Hi,

    For TASK 11, my playbook worked fine. HAPROXY is running on both the servers. But I am getting the below error when I run “curl” command. Has anyone experienced this issue?

    [[email protected] plays]$ curl http://serverb/
    503 Service Unavailable
    No server is available to handle this request.

    [[email protected] plays]$ curl http://serverd/
    503 Service Unavailable
    No server is available to handle this request.

    [[email protected] plays]$

    [[email protected] ~]# systemctl status haproxy.service
    ● haproxy.service – HAProxy Load Balancer
    Loaded: loaded (/usr/lib/systemd/system/haproxy.service; enabled; vendor preset: disabled)
    Active: active (running) since Sun 2020-03-08 22:06:03 AEDT; 11h ago
    Process: 6019 ExecStartPre=/usr/sbin/haproxy -f $CONFIG -c -q (code=exited, status=0/SUCCESS)
    Main PID: 6020 (haproxy)
    Tasks: 2 (limit: 11491)
    Memory: 2.8M
    CGroup: /system.slice/haproxy.service
    ├─6020 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
    └─6022 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid

    Mar 08 22:06:03 serverb systemd[1]: Stopped HAProxy Load Balancer.
    Mar 08 22:06:03 serverb systemd[1]: Starting HAProxy Load Balancer…
    Mar 08 22:06:03 serverb haproxy[6020]: Proxy hafrontend started.
    Mar 08 22:06:03 serverb haproxy[6020]: [WARNING] 067/220603 (6020) : parsing [/etc/haproxy/haproxy.cfg:15] : the ‘contimeout’ directive is now deprecated in favor of ‘timeout connect’, and will not be supported in future versions.
    Mar 08 22:06:03 serverb haproxy[6020]: [WARNING] 067/220603 (6020) : parsing [/etc/haproxy/haproxy.cfg:16] : the ‘clitimeout’ directive is now deprecated in favor of ‘timeout client’, and will not be supported in future versions.
    Mar 08 22:06:03 serverb haproxy[6020]: [WARNING] 067/220603 (6020) : parsing [/etc/haproxy/haproxy.cfg:17] : the ‘srvtimeout’ directive is now deprecated in favor of ‘timeout server’, and will not be supported in future versions.
    Mar 08 22:06:03 serverb haproxy[6020]: Proxy hafrontend started.
    Mar 08 22:06:03 serverb haproxy[6020]: Proxy habackend started.
    Mar 08 22:06:03 serverb haproxy[6020]: Proxy habackend started.
    Mar 08 22:06:03 serverb systemd[1]: Started HAProxy Load Balancer.

    [[email protected] ~]# systemctl status haproxy.service
    ● haproxy.service – HAProxy Load Balancer
    Loaded: loaded (/usr/lib/systemd/system/haproxy.service; enabled; vendor preset: disabled)
    Active: active (running) since Tue 2020-03-10 05:47:51 EDT; 12h ago
    Process: 7339 ExecStartPre=/usr/sbin/haproxy -f $CONFIG -c -q (code=exited, status=0/SUCCESS)
    Main PID: 7341 (haproxy)
    Tasks: 2 (limit: 5040)
    Memory: 4.5M
    CGroup: /system.slice/haproxy.service
    ├─7341 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
    └─7343 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid

    Mar 10 05:47:51 serverd systemd[1]: Stopped HAProxy Load Balancer.
    Mar 10 05:47:51 serverd systemd[1]: Starting HAProxy Load Balancer…
    Mar 10 05:47:51 serverd haproxy[7341]: [WARNING] 069/054751 (7341) : parsing [/etc/haproxy/haproxy.cfg:15] : the ‘contimeout’ directive is now deprecated in favor of ‘timeout connect’, and will not be supported in future versions.
    Mar 10 05:47:51 serverd haproxy[7341]: [WARNING] 069/054751 (7341) : parsing [/etc/haproxy/haproxy.cfg:16] : the ‘clitimeout’ directive is now deprecated in favor of ‘timeout client’, and will not be supported in future versions.
    Mar 10 05:47:51 serverd haproxy[7341]: [WARNING] 069/054751 (7341) : parsing [/etc/haproxy/haproxy.cfg:17] : the ‘srvtimeout’ directive is now deprecated in favor of ‘timeout server’, and will not be supported in future versions.
    Mar 10 05:47:51 serverd haproxy[7341]: Proxy hafrontend started.
    Mar 10 05:47:51 serverd haproxy[7341]: Proxy hafrontend started.
    Mar 10 05:47:51 serverd haproxy[7341]: Proxy habackend started.
    Mar 10 05:47:51 serverd haproxy[7341]: Proxy habackend started.
    Mar 10 05:47:51 serverd systemd[1]: Started HAProxy Load Balancer.

  80. Hei Tomas , everybody,

    Thanks Tomas for this sample exam. You have no idea how much it helped …or..you do have :).
    I have just passed EX294 today with 240/300.

    As an advice for those who will take this exam in the future: you really need to solve the exercicess fast. you need to have very very very good practice in order to touch all tasks. I didn t have time to touch two exercices and a third one I didn t finish.
    Also I didn t like that I couldn’t configure my .vimrc (or idk it didn t work) and I lost precious minutes with indentation – thing that does not happen when I work or practice with ansible.

    Goodluck 2 all!

  81. Hi Everyone!
    Hope you are keeping safe! qq: Any idea if there is access to Ansible Galaxy roles on EX294?

    Thanks,
    jn

  82. Hi! Thank you for your shared experience! Could you tell, did you have questions on exam about vagrant and dynamic inventory?
    Thank you a lot!

  83. Hi Tomas,

    Thanks for the sample exam, it is a great exercise. I am having trouble understanding Task 15. Setting the fact is easy, but I cannot understand how it can be retrieved from ansible_local.custom.sample_exam. Can you please give me hint?

    A custom Ansible fact server_role=mysql is created that can be retrieved from ansible_local.custom.sample_exam when using Ansible setup module.

    • You can create your own custom facts that can be gathered by Ansible. Facts live in /etc/ansible/facts.d/.

  84. Hi Tomas,
    I cannot understand what the role in exercise 12 (Security) is for ?
    Can it work this way?

    – name: Secuity
    hosts: webserver
    become: true
    tashs:
    – name: “Set httpd_can_network_connect flag on and keep it persistent across reboots”
    seboolean:
    name: httpd_can_network_connect
    state: yes
    persistent: yes

  85. Hi,
    Did anyone here take the EX447 exam and could offer me some tips on what to study, especially for git and Tower (API)?
    Thanks!

  86. I don’t see how I can use the templates module to complete task 18. I did it this way however:


    – hosts: all
    become: yes
    tasks:
    – name: create server list
    lineinfile:
    path: /home/automation/plays/server_list
    line: ‘{{ ansible_fqdn }}’
    create: yes
    delegate_to: 127.0.0.1
    – name: copy server list
    copy:
    src: /home/automation/plays/server_list
    dest: /etc/server_list.txt
    owner: automation
    mode: 0600
    setype: net_conf_t
    – name: delete server list
    file:
    path: /home/automation/plays/server_list
    state: absent
    delegate_to: 127.0.0.1

    The result is not a ordered list, but gets the job done:
    [[email protected] ~]$ cat /etc/server_list.txt
    ansible2.hl.local
    ansible5.hl.local
    ansible3.hl.local
    ansible4.hl.local

    Any hints on a better way of doing this?

    • As per requirements – the file server_list.txt should update in case the inventory changes/updates – so the best way would be to use ‘template’ module. This particular task is very easy to achieve using the “template”.
      Hint – look at the “magic variables” ->
      To get a list of what they contain, run:
      ansible localhost -m debug -a “var=hostvars”

      Hope this helps. Good luck.

  87. Hi Tomas, thanks you for sharing this.
    I have a question when i run the authorized_key in the adhoc command for task number2 ansible all –ask-pass -m authorized_key -a ‘user=automation state=present key=”{{lookup(‘file’, ‘/home/automation/.ssh/id_rsa.pub}}”‘
    I always get this error:
    template error : unexpected ‘/’.string: key=”{{lookup(‘file’, ‘/home/automation/.ssh/id_rsa.pub}}”
    Why am i getting this error ?
    Thank you.

  88. Hey Tomas, i have a “general” question (not necessarily about this test, but i couldn’t find any example on the internet)
    when working with ansible-vault is it possible to use the protected data, but withouth the need of using the “–ask-vault-pass”, “–vault-id” or any of the call methods from the CLI.
    to put it simple, is there any way to ONLY run “ansible-playbook playbook.yml” when the playbook uses protected data?

    (ps: i believe the only way is to set the vault_key file location in ansible.cfg?)

    • Hi Nick, if you don’t provide the vault password on the CLI, then you have to store it somewhere in a file.

      You can do something like this:

      ansible-playbook --vault-password-file=vault_secret.txt playbook.yml
    • Hey Nick,
      The answer to Your question is Yes – You can run a playbook containing Vault encrypted data without providing the command line arguments for vault pass or id-> look in the /etc/ansible/ansible.cfg and You will figure out the way.
      Good luck.

  89. Hello Thomas,
    Would you please advise with task 11 “Use geerlingguy.haproxy

    The proxy server installed and haproxy service active, and firewall port 80 added however when am curl://proxy_server
    i have error
    503 Service Unavailable
    No server is available to handle this request.

    also via web: 503 Service Unavailable
    Please see output from files below:

    # vim haproxy.yml

    – name: task_11
    hosts: proxy
    become: yes
    roles:
    – geerlingguy.haproxy
    vars:
    haproxy_frontend_mode: ‘http’
    haproxy_backend_port: 80
    haproxy_backend_balance_method: ’roundrobin’
    haproxy_backend_servers:
    – name: node1
    address: 192.168.1.80:80
    – name: node2
    address: 192.168.55.63:80

    tasks:
    – name: install firewalld
    yum:
    name: firewalld
    state: present
    – name: satrt service
    service:
    name: firewalld
    state: started
    enabled: yes
    – name: configure firewall 80
    firewalld:
    port: 80/tcp
    state: enabled
    permanent: yes
    immediate: yes
    and
    # vim/etc/haproxy/haproxy.cfg
    global
    log /dev/log local0
    log /dev/log local1 notice
    stats socket /var/lib/haproxy/stats level admin
    chroot /var/lib/haproxy
    user haproxy
    group haproxy
    daemon

    defaults
    log global
    mode http
    option httplog
    option dontlognull
    timeout connect 5000
    timeout client 50000
    timeout server 50000

    frontend hafrontend
    bind *:80
    mode http
    default_backend habackend

    backend habackend
    mode http
    balance roundrobin
    option forwardfor
    option httpchk HEAD / HTTP/1.1\r\nHost:localhost
    cookie SERVERID insert indirect
    server node1 192.168.1.80:80 cookie node1 check
    server node2 192.168.55.63:80 cookie node2 check

    Please advice

    Many thanks,
    Przemo

  90. Hi Tomas,

    Task 2 questions were sounding bit confusing, it did not clearly mention if the ssh keys to be generated on root or automation user on controller, since it was mentioned that we need to use automation user for all plays/tasks I believe keys to be generated will be for automation user – just wondering if the questions will be similarly phrased in exam as well?

    • It doesn’t matter, you can use any user to generate a keypair. The outcome of the task is clearly stated: as long as the SSH key that you generated gets copied to all inventory hosts for the automation user and you can SSH into all inventory hosts using the automation user without a password, you have completed the task.

  91. Hi, Tomas,

    I have two questions for you:
    1. Is the resolution of machine hostnames resolved by a DNS or do I need to add entries in the /etc/hosts file?

    2 . In Task 2: Ad-Hoc Commands, the command to create the automation user and deploy the public key, must be done so that the root password is given in input (stdin) or just run the script and each time type the root password for each machine?

    • Hi Riccardo, you can use the hosts file if you don’t have a working DNS server, either way is fine.

      With regards to the second question, again, either way is fine, as long as the ad-hoc command creates the user.

  92. Hello,

    The sixth task is giving me headache since yesterday, and I feel lost:
    “Each user should have an SSH key uploaded (use the SSH key that you created previously, see task #2).”

    And in an anwser you said:
    You can reuse the same SSH keys as many times and on as many users as you want.

    and when someone asks this:
    How would the key from one user allow another user to ssh to a remote server ?

    You said: It’s the same key. It’s like using the same password on multiple accounts.

    Can you please give an example (outside of the exam question), where you have two users a and b on two servers server1 and server2, let’s say you logged as user a and created private/public ssh keys for this user, did ssh-copy-id and now this user can ssh to server 2 without password.
    Now how I can reuse the same public key of user a for user b and then ssh to server2 as user b without password ?

    From what I see we can’t reuse the same public key.

    • Generate an SSH keypair using ssh-keygen. If you don’t change the filename, you’ll normally get two files created, id_rsa and id_rsa.pub. The first one is your private key, and the second one is your public key. If you have two (or more) users on different servers, you can use those keys for both/all of them. What that means is that the users will use the same key. Simply copy the private key and save it as ${HOME}/.ssh/id_rsa, then copy the public key and save it as ${HOME}/.ssh/authorized_keys. Regardless of the actual username, you can repeat this process for as many Linux accounts as you need.

    • Thank for the details !
      After testing this yesterday, I have to say that you were right, I want just to add in case we didn’t copy the private key of user.a to the home directory of user.b ,we need to login as user.a and then do ssh [email protected] or do this:
      sudo -u user.a ssh [email protected]

  93. Hi Tomas.
    Thank you for this, it is very helpful.
    when I arrived to task 6 … the ssh part it worked if i used that one key that I generated in the earlier however for outside of this sample exam i tried to generate a different ssh for every user and after i ran the playbook the files are copied correctly to every host but every time I try to connect it asks me for a password !?
    I’ve had a really hard time figuring it out ? is it because of the private key ? and if so how can I overcome this problem?
    Thank you very much.

    • When you try to connect, what private key do you use? Your private key has to match the user it has been deployed to.

  94. Hi Tomas ,
    for the conduct of exercise n° 6.
    – Each user should have an SSH key uploaded (use the SSH key that you created previously, see task #2).
    After running the playbook, users should be able to SSH into their respective servers without passwords.

    I tried to solve it this way in the playbook:
    – name: “Private Key for alice”
    copy:
    src: /home/automation/.ssh/id_rsa
    dest: /home/{{ item.username }}/.ssh/id_rsa
    owner: “{{ item.username }}”
    group: “{{ item.username }}”
    mode: ‘0600’
    with_items: “{{ users }}”
    when:
    – item.username == ‘alice’
    – “‘webservers’ in group_names”

    – name: Remove public key
    file:
    path: /home/{{ item.username }}/.ssh/id_rsa.pub
    state: absent
    with_items: “{{ users }}”
    when:
    – item.username == ‘alice’
    – “‘webservers’ in group_names”

    – name: “Authorized key for alice”
    copy:
    src: /home/automation/.ssh/id_rsa.pub
    dest: /home/{{ item.username }}/.ssh/authorized_keys
    owner: “{{ item.username }}”
    group: “{{ item.username }}”
    mode: ‘0600’
    with_items: “{{ users }}”
    when:
    – item.uid|string|first == ‘1’
    – “‘webservers’ in group_names”

    Can it be a good solution?
    Thanks

    • Hi Patrick, I didn’t use any to be honest with you, therefore can’t recommend much, sorry.

      Ansible documentation website was one of my main study resources if that helps.

  95. Hi Tomas,
    I saw a files directory in your homelab3, with each of the users unique SSH public key file.
    Please if could explain how to create such a files directory.

    Thanks Jules!

    • Hi Jules, it’s actually the opposite, if you open the files, you will notice that all users share the same SSH key. These files are stored on the control node, you can create them manually.

  96. Hi. Tomas, thank you for cool sample of EX-294. I’m preparing for this exam and your sample is very useful.
    I want to ask about Mysql task (Task 9). As I know in CentOS we should use mariaDB. I installed mariaDB instead mysql-community-server and mysql_user module don’t work well. What is preferred way to manage user passwords on mysql?

  97. Hi Tomas,

    When I create a repo as per Task 8, mysql-community-server installation on Centos 8 is not working. Have you tried on Centos 8?

    Thanks,
    Rajesh

Leave a Reply to Serge Meeuwsen Cancel reply

Your email address will not be published. Required fields are marked *