RHCE (EX294) is now entirely about Ansible automation — and it is still a performance-based exam on a live system. RHCSA is a prerequisite, and the exam assumes you can administer RHEL fluently. Where RHCSA tests manual system administration, RHCE tests whether you can automate that administration at scale using Ansible playbooks, roles, and modules.
Practice this topic
Ansible control node (where you run playbooks), managed nodes (target systems, require Python and SSH access), inventory (list of managed hosts). Inventory formats: INI (simple, grouped with [groupname]), YAML (hierarchical, children/vars blocks). Dynamic inventory: scripts or plugins that query external sources (AWS EC2, GCP, Foreman) and return JSON. Inventory variables: host_vars/hostname.yml (per-host), group_vars/groupname.yml (per-group). Variable precedence (highest to lowest): extra vars (-e), task vars, block vars, role vars, include_vars, set_fact, registered vars, play vars, host_vars, group_vars, inventory vars, role defaults. Ansible configuration: ansible.cfg searched in: environment variable ANSIBLE_CONFIG, ./ansible.cfg (current directory), ~/.ansible.cfg, /etc/ansible/ansible.cfg. Key settings: inventory, remote_user, become, become_method, host_key_checking, roles_path.
Playbook structure: hosts (target inventory group), become (privilege escalation), vars, tasks (name + module + parameters), handlers (triggered by notify), pre_tasks, post_tasks, roles. YAML gotchas: indentation is significant (2 spaces standard), strings with colons need quotes, booleans are yes/no or true/false. Essential modules for RHCE: yum/dnf (package management), service (start/stop/enable), copy (files to managed nodes), template (Jinja2 templates), file (create/modify files and directories), user/group (user management), firewalld (firewall rules), nmcli (network configuration), lineinfile/blockinfile (edit config files), command/shell (run commands — avoid when a module exists). Jinja2 templating: {{ variable }} for variable substitution, {% if condition %} for conditionals, {% for item in list %} for loops. Filters: {{ var | default('fallback') }}, {{ list | join(',') }}, {{ string | upper }}. ansible_facts variables: ansible_hostname, ansible_os_family, ansible_default_ipv4.address.
Role directory structure: tasks/main.yml (task list), handlers/main.yml, templates/ (Jinja2 .j2 files), files/ (static files for copy), vars/main.yml (high-precedence vars), defaults/main.yml (low-precedence defaults), meta/main.yml (dependencies, metadata). Create role with ansible-galaxy init rolename. Use roles in playbooks with roles: key or include_role/import_role tasks. Role dependencies in meta/main.yml.galaxy_info: automatically include prerequisite roles. Ansible Galaxy: community role repository. ansible-galaxy install namespace.rolename. Ansible Collections: packaging format for roles, modules, plugins, and playbooks. Install with ansible-galaxy collection install namespace.collection. Use FQCN (Fully Qualified Collection Name): ansible.builtin.yum, community.general.ufw.
Conditionals: when: clause (evaluate Jinja2 expression — note: no {{ }} in when clauses). Loops: loop: (replaces with_items), with_items (legacy), with_dict, loop_control (label, index_var). Error handling: ignore_errors: yes, failed_when: (custom failure condition), block/rescue/always (try/catch/finally equivalent). Tags: tag tasks with tags: [configure, deploy] and run with --tags or skip with --skip-tags. Check mode (--check): dry run, shows what would change without making changes. Diff mode (--diff): shows file content differences. Useful combination: -CD for review before applying. Vault: ansible-vault encrypt file (encrypt at rest), ansible-vault create file (create encrypted), --ask-vault-pass or --vault-password-file at runtime. Encrypt individual variables with !vault | syntax. Troubleshooting: -v (verbose), -vvv (connection debug), -vvvv (full debug including SSH). Use ansible all -m ping to verify connectivity.
import_role and include_role behave identically in all playbook contexts
import_role is static (processed at playbook parse time); include_role is dynamic (processed at runtime) — this affects tag behaviour
The command module and shell module support the same shell features
command module does not support shell features like pipes or redirects — use shell module when you need those
Registered variables from a loop capture results in a single combined value
Registered variables (register:) are host-specific — in a loop, register captures results per-item in register.results list
Try free RHCE practice questions with explanations, topic links and progress tracking.