Portal | Level: L2: Operations | Topics: RHCE (EX294) Exam, Ansible, Git, Linux Fundamentals | Domain: DevOps & Tooling
RHCE (EX294) Exam Preparation — Primer¶
Why This Matters¶
The RHCE (EX294) is a hands-on, performance-based exam that validates your ability to automate Linux system administration using Red Hat Ansible Automation Platform. It is the industry-standard certification for Ansible automation on RHEL. Everything is done on a live system — no multiple choice. You either automate it correctly or you don't.
Prerequisite: RHCSA (EX200) or equivalent Linux administration skills.
Exam Objectives Overview¶
The EX294 covers these domains:
- Understand core components of Ansible — inventories, modules, variables, facts, plays, playbooks, configuration files, roles, collections
- Install and configure an Ansible control node — install packages, create config, manage settings
- Configure Ansible managed nodes — SSH keys, privilege escalation, validate connectivity
- Script administration tasks — shell scripts calling Ansible, ad hoc commands
- Create and use static inventories — host groups, nested groups, inventory variables
- Create Ansible plays and playbooks — tasks, handlers, conditionals, loops, tags, error handling, templates
- Automate standard RHCSA tasks — users, groups, packages, services, firewall, storage, network, cron, SELinux, files
- Manage content — deploy files, templates, archives
- Create and use templates — Jinja2 templates for configuration files
- Work with Ansible variables and facts — variable precedence, registered variables, facts, magic variables
- Create and work with roles — role structure, defaults, handlers, tasks, templates, files, vars
- Download roles from Ansible Galaxy and Automation Hub — requirements.yml, collections
- Manage parallelism — forks, serial, async, throttle
- Use Ansible Vault — encrypt files, strings, use at runtime
- Use provided documentation — ansible-doc, module documentation
- Work with Ansible collections — install, use, FQCN
1. Core Components of Ansible¶
Architecture¶
Control Node (RHEL with ansible-core installed)
|
| SSH (key-based auth)
v
Managed Nodes (RHEL systems)
- Control node: Where Ansible runs. Needs Python 3 + ansible-core.
- Managed nodes: Target systems. Need Python 3 + SSH access.
- Inventory: Defines which hosts to manage and how to group them.
- Modules: Units of work (e.g., yum, copy, service, user).
- Plugins: Extend Ansible (connection, callback, lookup, filter).
- Playbooks: YAML files containing ordered lists of plays.
- Roles: Reusable bundles of tasks, handlers, files, templates, variables.
- Collections: Distribution format for roles, modules, and plugins (namespace.collection).
Key Files¶
| File | Purpose |
|---|---|
/etc/ansible/ansible.cfg |
System-wide config (lowest precedence) |
./ansible.cfg |
Project-level config (highest file precedence) |
~/.ansible.cfg |
User-level config |
ANSIBLE_CONFIG env var |
Overrides all file-based config |
inventory or /etc/ansible/hosts |
Default inventory location |
Config precedence (highest to lowest):
1. ANSIBLE_CONFIG environment variable
2. ./ansible.cfg (current directory)
3. ~/.ansible.cfg (home directory)
4. /etc/ansible/ansible.cfg (system)
Minimal ansible.cfg¶
[defaults]
inventory = ./inventory
remote_user = ansible
ask_pass = false
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
2. Install and Configure the Control Node¶
Installation¶
# RHEL 9 — enable the Ansible repo
sudo dnf install ansible-core
# Verify
ansible --version
ansible-config dump --changed
# Install collections
ansible-galaxy collection install ansible.posix
ansible-galaxy collection install community.general
ansible.cfg Deep Dive¶
[defaults]
inventory = ./inventory
remote_user = devops
roles_path = ./roles:/usr/share/ansible/roles
collections_path = ./collections
forks = 5
log_path = ./ansible.log
host_key_checking = false
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
Key settings for the exam:
- forks — controls parallelism (default 5)
- host_key_checking — set to false in lab environments
- roles_path — where Ansible searches for roles
- remote_user — SSH user for managed nodes
- log_path — enables logging (not on by default)
3. Configure Managed Nodes¶
SSH Key Setup¶
# On control node — generate key if needed
ssh-keygen -t ed25519 -C "ansible"
# Distribute to managed nodes
ssh-copy-id devops@managed1.example.com
ssh-copy-id devops@managed2.example.com
# Verify
ansible all -m ping
Privilege Escalation¶
On each managed node, the Ansible user needs passwordless sudo:
# On managed node (or automate with Ansible)
echo "devops ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/devops
chmod 0440 /etc/sudoers.d/devops
visudo -cf /etc/sudoers.d/devops # syntax check
Connectivity Validation¶
# Test all hosts
ansible all -m ping
# Test specific group
ansible webservers -m ping
# Test with verbose output
ansible all -m ping -v
4. Static Inventories¶
INI Format¶
[webservers]
web1.example.com
web2.example.com
[dbservers]
db1.example.com
db2.example.com
[datacenter:children]
webservers
dbservers
[webservers:vars]
http_port=80
YAML Format¶
all:
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
vars:
http_port: 80
dbservers:
hosts:
db1.example.com:
db2.example.com:
datacenter:
children:
webservers:
dbservers:
Host and Group Variables¶
inventory/
hosts # or hosts.yml
host_vars/
web1.example.com.yml
group_vars/
all.yml
webservers.yml
dbservers.yml
Variable precedence (for inventory):
- host_vars/ overrides group_vars/
- Child group vars override parent group vars
- all group has lowest group precedence
Useful Inventory Commands¶
# List all hosts
ansible-inventory --list
# Show host variables
ansible-inventory --host web1.example.com
# Graph view
ansible-inventory --graph
5. Ad Hoc Commands¶
# Syntax: ansible <pattern> -m <module> -a "<arguments>"
# Ping all hosts
ansible all -m ping
# Run a command
ansible webservers -m command -a "uptime"
# Use shell module (supports pipes, redirection)
ansible all -m shell -a "df -h | grep /dev/sda"
# Copy a file
ansible all -m copy -a "src=/tmp/file dest=/tmp/file mode=0644"
# Install a package
ansible webservers -m dnf -a "name=httpd state=present" --become
# Start and enable a service
ansible webservers -m service -a "name=httpd state=started enabled=yes" --become
# Manage users
ansible all -m user -a "name=testuser state=present" --become
# Gather facts
ansible web1.example.com -m setup
# Filter facts
ansible web1.example.com -m setup -a "filter=ansible_distribution*"
6. Playbook Fundamentals¶
Basic Playbook Structure¶
---
- name: Configure webservers
hosts: webservers
become: true
vars:
http_port: 80
doc_root: /var/www/html
tasks:
- name: Install httpd
ansible.builtin.dnf:
name: httpd
state: present
- name: Start and enable httpd
ansible.builtin.service:
name: httpd
state: started
enabled: true
- name: Deploy index page
ansible.builtin.template:
src: templates/index.html.j2
dest: "{{ doc_root }}/index.html"
owner: apache
group: apache
mode: '0644'
notify: Restart httpd
handlers:
- name: Restart httpd
ansible.builtin.service:
name: httpd
state: restarted
Multiple Plays¶
---
- name: Configure webservers
hosts: webservers
become: true
tasks:
- name: Install httpd
ansible.builtin.dnf:
name: httpd
state: present
- name: Configure dbservers
hosts: dbservers
become: true
tasks:
- name: Install mariadb
ansible.builtin.dnf:
name: mariadb-server
state: present
Running Playbooks¶
# Run a playbook
ansible-playbook site.yml
# Check mode (dry run)
ansible-playbook site.yml --check
# Diff mode (show changes)
ansible-playbook site.yml --diff
# Check + diff together
ansible-playbook site.yml --check --diff
# Limit to specific hosts
ansible-playbook site.yml --limit web1.example.com
# Start at a specific task
ansible-playbook site.yml --start-at-task "Install httpd"
# Step through tasks
ansible-playbook site.yml --step
# Syntax check
ansible-playbook site.yml --syntax-check
# List tasks
ansible-playbook site.yml --list-tasks
# List tags
ansible-playbook site.yml --list-tags
7. Modules for System Administration¶
Package Management¶
- name: Install packages
ansible.builtin.dnf:
name:
- httpd
- firewalld
- php
state: present
- name: Remove a package
ansible.builtin.dnf:
name: telnet
state: absent
- name: Install a specific version
ansible.builtin.dnf:
name: httpd-2.4.51
state: present
- name: Update all packages
ansible.builtin.dnf:
name: "*"
state: latest
- name: Install a package group
ansible.builtin.dnf:
name: "@Development Tools"
state: present
- name: Enable a DNF module stream
ansible.builtin.dnf:
name: "@postgresql:15/server"
state: present
Service Management¶
- name: Start and enable httpd
ansible.builtin.service:
name: httpd
state: started
enabled: true
- name: Restart a service
ansible.builtin.service:
name: httpd
state: restarted
- name: Reload a service
ansible.builtin.service:
name: httpd
state: reloaded
- name: Stop and disable a service
ansible.builtin.service:
name: cups
state: stopped
enabled: false
User and Group Management¶
- name: Create a group
ansible.builtin.group:
name: developers
gid: 2000
state: present
- name: Create a user
ansible.builtin.user:
name: jdoe
uid: 2001
group: developers
groups: wheel
append: true
shell: /bin/bash
home: /home/jdoe
password: "{{ 'P@ssw0rd' | password_hash('sha512') }}"
state: present
- name: Remove a user
ansible.builtin.user:
name: olduser
state: absent
remove: true
- name: Add SSH key for user
ansible.posix.authorized_key:
user: jdoe
key: "{{ lookup('file', 'files/jdoe_id_ed25519.pub') }}"
state: present
File Management¶
- name: Copy a file
ansible.builtin.copy:
src: files/httpd.conf
dest: /etc/httpd/conf/httpd.conf
owner: root
group: root
mode: '0644'
backup: true
- name: Create a directory
ansible.builtin.file:
path: /opt/myapp
state: directory
owner: appuser
group: appuser
mode: '0755'
- name: Create a symlink
ansible.builtin.file:
src: /opt/myapp/current
dest: /opt/myapp/latest
state: link
- name: Download a file
ansible.builtin.get_url:
url: https://example.com/file.tar.gz
dest: /tmp/file.tar.gz
checksum: sha256:abcdef1234567890
- name: Extract an archive
ansible.builtin.unarchive:
src: /tmp/file.tar.gz
dest: /opt/myapp/
remote_src: true
- name: Edit a line in a file
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PermitRootLogin'
line: 'PermitRootLogin no'
backup: true
- name: Add a block of text
ansible.builtin.blockinfile:
path: /etc/hosts
block: |
192.168.1.10 web1.example.com
192.168.1.11 web2.example.com
marker: "# {mark} ANSIBLE MANAGED BLOCK"
Firewall Management¶
- name: Enable firewalld
ansible.builtin.service:
name: firewalld
state: started
enabled: true
- name: Allow HTTP
ansible.posix.firewalld:
service: http
permanent: true
immediate: true
state: enabled
- name: Allow HTTPS
ansible.posix.firewalld:
service: https
permanent: true
immediate: true
state: enabled
- name: Allow custom port
ansible.posix.firewalld:
port: 8080/tcp
permanent: true
immediate: true
state: enabled
- name: Allow port range
ansible.posix.firewalld:
port: 5000-5100/tcp
permanent: true
immediate: true
state: enabled
Storage Management¶
- name: Create a partition
community.general.parted:
device: /dev/sdb
number: 1
state: present
part_end: 1GiB
- name: Create a filesystem
community.general.filesystem:
fstype: xfs
dev: /dev/sdb1
- name: Mount a filesystem
ansible.posix.mount:
path: /mnt/data
src: /dev/sdb1
fstype: xfs
state: mounted
- name: Create an LVM volume group
community.general.lvg:
vg: datavg
pvs: /dev/sdc
- name: Create an LVM logical volume
community.general.lvol:
vg: datavg
lv: datalv
size: 5g
Cron Jobs¶
- name: Create a cron job
ansible.builtin.cron:
name: "Backup database"
minute: "0"
hour: "2"
job: "/usr/local/bin/backup.sh >> /var/log/backup.log 2>&1"
user: root
- name: Create a cron job with special time
ansible.builtin.cron:
name: "Daily cleanup"
special_time: daily
job: "/usr/local/bin/cleanup.sh"
SELinux Management¶
- name: Set SELinux to enforcing
ansible.posix.selinux:
policy: targeted
state: enforcing
- name: Set SELinux boolean
ansible.posix.seboolean:
name: httpd_can_network_connect
state: true
persistent: true
- name: Set SELinux file context
community.general.sefcontext:
target: '/srv/myapp(/.*)?'
setype: httpd_sys_content_t
state: present
- name: Apply SELinux file context
ansible.builtin.command: restorecon -Rv /srv/myapp
changed_when: true
- name: Set SELinux port label
community.general.seport:
ports: 8443
proto: tcp
setype: http_port_t
state: present
Network Configuration¶
- name: Configure a static IP with nmcli
community.general.nmcli:
conn_name: eth0
ifname: eth0
type: ethernet
ip4: 192.168.1.100/24
gw4: 192.168.1.1
dns4:
- 8.8.8.8
- 8.8.4.4
state: present
- name: Configure hostname
ansible.builtin.hostname:
name: web1.example.com
- name: Manage /etc/hosts
ansible.builtin.lineinfile:
path: /etc/hosts
line: "192.168.1.100 web1.example.com web1"
state: present
8. Variables, Facts, and Precedence¶
Variable Definition Locations¶
# In playbook vars section
vars:
http_port: 80
# In external file
vars_files:
- vars/common.yml
- "vars/{{ ansible_os_family }}.yml"
# From command line
# ansible-playbook site.yml -e "http_port=8080"
# In inventory (host_vars/, group_vars/)
# In roles (defaults/main.yml, vars/main.yml)
# Registered from task output
Variable Precedence (lowest to highest)¶
- Role defaults (
roles/x/defaults/main.yml) - Inventory file or script group vars
group_vars/allgroup_vars/<group>- Inventory file or script host vars
host_vars/<host>- Play
vars_files - Play
vars - Play
vars_prompt - Task
vars include_varsset_fact/ registered vars- Role vars (
roles/x/vars/main.yml) - Block vars
- Task vars (only for the task)
- Extra vars (
-e) — always win
Facts¶
# Access facts
- name: Show OS distribution
ansible.builtin.debug:
msg: "OS is {{ ansible_facts['distribution'] }} {{ ansible_facts['distribution_version'] }}"
# Alternative dot notation
- name: Show IP
ansible.builtin.debug:
msg: "IP is {{ ansible_default_ipv4.address }}"
# Custom facts
# Place .fact files in /etc/ansible/facts.d/ on managed nodes
# Accessible via ansible_local.<factname>
# Disable fact gathering
- hosts: all
gather_facts: false
# Cache facts
# ansible.cfg:
# [defaults]
# fact_caching = jsonfile
# fact_caching_connection = /tmp/ansible_facts
# fact_caching_timeout = 3600
Registered Variables¶
- name: Check if file exists
ansible.builtin.stat:
path: /etc/myapp.conf
register: myapp_conf
- name: Create config if missing
ansible.builtin.template:
src: myapp.conf.j2
dest: /etc/myapp.conf
when: not myapp_conf.stat.exists
- name: Capture command output
ansible.builtin.command: cat /etc/hostname
register: hostname_output
changed_when: false
- name: Display hostname
ansible.builtin.debug:
var: hostname_output.stdout
Magic Variables¶
| Variable | Description |
|---|---|
hostvars |
All variables for all hosts |
groups |
All groups and their hosts |
group_names |
Groups the current host belongs to |
inventory_hostname |
Name of the current host from inventory |
ansible_play_hosts |
Active hosts in the current play |
ansible_check_mode |
True if running in check mode |
9. Conditionals, Loops, and Error Handling¶
Conditionals (when)¶
- name: Install on RHEL only
ansible.builtin.dnf:
name: httpd
state: present
when: ansible_facts['os_family'] == "RedHat"
- name: Multiple conditions (AND)
ansible.builtin.dnf:
name: httpd
state: present
when:
- ansible_facts['os_family'] == "RedHat"
- ansible_facts['distribution_major_version'] | int >= 8
- name: OR condition
ansible.builtin.debug:
msg: "This is a web server"
when: "'webservers' in group_names or 'proxy' in group_names"
- name: Check variable is defined
ansible.builtin.debug:
msg: "Port is {{ http_port }}"
when: http_port is defined
Loops¶
# Simple list
- name: Install packages
ansible.builtin.dnf:
name: "{{ item }}"
state: present
loop:
- httpd
- firewalld
- php
# Better — pass list to module directly
- name: Install packages (preferred)
ansible.builtin.dnf:
name:
- httpd
- firewalld
- php
state: present
# Dict loop
- name: Create users
ansible.builtin.user:
name: "{{ item.name }}"
group: "{{ item.group }}"
state: present
loop:
- { name: alice, group: developers }
- { name: bob, group: admins }
# Loop with index
- name: Show loop index
ansible.builtin.debug:
msg: "{{ index }}: {{ item }}"
loop:
- alpha
- bravo
loop_control:
index_var: index
# Loop with label (cleaner output)
- name: Create users with label
ansible.builtin.user:
name: "{{ item.name }}"
loop: "{{ users }}"
loop_control:
label: "{{ item.name }}"
Handlers¶
tasks:
- name: Update httpd config
ansible.builtin.template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
notify:
- Restart httpd
- Verify httpd
handlers:
- name: Restart httpd
ansible.builtin.service:
name: httpd
state: restarted
- name: Verify httpd
ansible.builtin.uri:
url: http://localhost/
status_code: 200
# Force handlers to run mid-play
- name: Flush handlers now
ansible.builtin.meta: flush_handlers
Tags¶
tasks:
- name: Install packages
ansible.builtin.dnf:
name: httpd
state: present
tags:
- install
- packages
- name: Configure httpd
ansible.builtin.template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
tags:
- configure
# Run only tagged tasks
# ansible-playbook site.yml --tags "install"
# Skip tagged tasks
# ansible-playbook site.yml --skip-tags "configure"
# Special tags: always, never
- name: Always run this
ansible.builtin.debug:
msg: "Always runs"
tags: always
Error Handling¶
# Ignore errors
- name: Try to stop a service that may not exist
ansible.builtin.service:
name: nonexistent
state: stopped
ignore_errors: true
# Block / rescue / always
- name: Handle errors gracefully
block:
- name: Attempt risky operation
ansible.builtin.command: /opt/risky-script.sh
- name: This only runs if above succeeds
ansible.builtin.debug:
msg: "Script succeeded"
rescue:
- name: This runs if block fails
ansible.builtin.debug:
msg: "Script failed, running recovery"
always:
- name: This always runs
ansible.builtin.debug:
msg: "Cleanup complete"
# Custom changed_when / failed_when
- name: Run a script
ansible.builtin.command: /opt/check.sh
register: check_result
changed_when: "'CHANGED' in check_result.stdout"
failed_when: "'CRITICAL' in check_result.stdout"
10. Jinja2 Templates¶
Template Basics¶
{# This is a comment #}
{# Variable substitution #}
ServerName {{ ansible_fqdn }}
Listen {{ http_port }}
{# Conditional #}
{% if enable_ssl %}
Listen 443
SSLEngine on
{% endif %}
{# Loop #}
{% for host in groups['webservers'] %}
server {{ host }} {{ hostvars[host]['ansible_default_ipv4']['address'] }}:80;
{% endfor %}
{# Filter #}
ServerAdmin {{ admin_email | default('admin@example.com') }}
DocumentRoot {{ doc_root | default('/var/www/html') }}
Common Filters¶
| Filter | Example | Description |
|---|---|---|
default |
{{ var \| default('fallback') }} |
Default value if undefined |
lower / upper |
{{ name \| upper }} |
Case conversion |
replace |
{{ path \| replace('/', '-') }} |
String replacement |
join |
{{ list \| join(', ') }} |
Join list to string |
int / float |
{{ port \| int }} |
Type casting |
bool |
{{ val \| bool }} |
Cast to boolean |
password_hash |
{{ pw \| password_hash('sha512') }} |
Password hashing |
to_json / to_yaml |
{{ dict \| to_json }} |
Format conversion |
regex_replace |
{{ s \| regex_replace('a', 'b') }} |
Regex replace |
ipaddr |
{{ ip \| ansible.utils.ipaddr }} |
IP address validation |
Template Task¶
- name: Deploy Nginx config
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
validate: nginx -t -c %s
notify: Restart nginx
11. Roles¶
Role Structure¶
roles/
webserver/
defaults/ # Default variables (lowest precedence)
main.yml
vars/ # Role variables (high precedence)
main.yml
tasks/ # Main task list
main.yml
handlers/ # Handler definitions
main.yml
templates/ # Jinja2 templates
files/ # Static files
meta/ # Role metadata and dependencies
main.yml
Creating a Role¶
# Scaffold a role
ansible-galaxy role init webserver
# Or create manually
mkdir -p roles/webserver/{tasks,handlers,templates,files,vars,defaults,meta}
Role tasks/main.yml¶
---
- name: Install httpd
ansible.builtin.dnf:
name: "{{ webserver_packages }}"
state: present
- name: Deploy config
ansible.builtin.template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
notify: Restart httpd
- name: Start httpd
ansible.builtin.service:
name: httpd
state: started
enabled: true
Role defaults/main.yml¶
Role meta/main.yml¶
Using Roles in Playbooks¶
---
- name: Configure webservers
hosts: webservers
become: true
roles:
- common
- webserver
- role: firewall
vars:
firewall_services:
- http
# Or with include_role (dynamic)
tasks:
- name: Include webserver role
ansible.builtin.include_role:
name: webserver
vars:
http_port: 8080
# Or with import_role (static)
- name: Import webserver role
ansible.builtin.import_role:
name: webserver
12. Ansible Galaxy and Collections¶
Installing Roles from Galaxy¶
# Install a single role
ansible-galaxy role install geerlingguy.apache
# Install from requirements file
ansible-galaxy role install -r requirements.yml
requirements.yml¶
---
roles:
- name: geerlingguy.apache
version: "3.2.0"
- name: geerlingguy.mysql
collections:
- name: ansible.posix
version: ">=1.5.0"
- name: community.general
- name: community.crypto
Installing Collections¶
# Install a collection
ansible-galaxy collection install ansible.posix
# Install from requirements.yml
ansible-galaxy collection install -r requirements.yml
# Install to a specific path
ansible-galaxy collection install ansible.posix -p ./collections
Using FQCN (Fully Qualified Collection Name)¶
# Always use FQCN in playbooks
- name: Set SELinux boolean
ansible.posix.seboolean:
name: httpd_can_network_connect
state: true
persistent: true
# Not just "seboolean" — use the full path
13. Parallelism¶
# In ansible.cfg
[defaults]
forks = 10 # How many hosts to manage in parallel
# Serial execution (rolling updates)
- hosts: webservers
serial: 2 # 2 hosts at a time
tasks: ...
# Percentage-based serial
- hosts: webservers
serial: "25%"
# Stepped serial
- hosts: webservers
serial:
- 1 # First batch: 1 host (canary)
- 5 # Second batch: 5 hosts
- "100%" # Remaining hosts
# Async tasks (fire and forget)
- name: Long-running task
ansible.builtin.command: /opt/long-job.sh
async: 3600 # Max runtime in seconds
poll: 0 # Don't wait (fire and forget)
register: long_job
- name: Check on long job
ansible.builtin.async_status:
jid: "{{ long_job.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 60
delay: 10
# Throttle (limit concurrent tasks across hosts)
- name: Rate-limited API call
ansible.builtin.uri:
url: "https://api.example.com/deploy"
throttle: 2 # Max 2 concurrent
14. Ansible Vault¶
Encrypting Files¶
# Create a new encrypted file
ansible-vault create secrets.yml
# Encrypt an existing file
ansible-vault encrypt vars/passwords.yml
# View encrypted file
ansible-vault view secrets.yml
# Edit encrypted file
ansible-vault edit secrets.yml
# Decrypt a file
ansible-vault decrypt secrets.yml
# Change password
ansible-vault rekey secrets.yml
Encrypting Strings¶
# Encrypt a single variable value
ansible-vault encrypt_string 'SuperSecret123' --name 'db_password'
# Output (paste into vars file):
# db_password: !vault |
# $ANSIBLE_VAULT;1.1;AES256
# ...encrypted data...
Using Vault at Runtime¶
# Prompt for password
ansible-playbook site.yml --ask-vault-pass
# Use password file
ansible-playbook site.yml --vault-password-file ~/.vault_pass
# Multiple vault IDs
ansible-vault encrypt --vault-id dev@prompt secrets-dev.yml
ansible-vault encrypt --vault-id prod@~/.vault_prod secrets-prod.yml
ansible-playbook site.yml --vault-id dev@prompt --vault-id prod@~/.vault_prod
Vault in Practice¶
# vars/secrets.yml (encrypted)
---
db_password: SuperSecret123
api_key: abcdef1234567890
# playbook references it normally
- hosts: dbservers
become: true
vars_files:
- vars/common.yml
- vars/secrets.yml
tasks:
- name: Configure database
ansible.builtin.template:
src: db.conf.j2
dest: /etc/myapp/db.conf
15. Using Documentation¶
ansible-doc¶
# List all modules
ansible-doc -l
# Search for modules
ansible-doc -l | grep firewall
# View module documentation
ansible-doc ansible.builtin.dnf
ansible-doc ansible.builtin.copy
ansible-doc ansible.posix.firewalld
# Show only examples
ansible-doc ansible.builtin.user -s
# List plugins by type
ansible-doc -t callback -l
ansible-doc -t lookup -l
ansible-doc -t filter -l
This is critical on the exam — you have access to ansible-doc and the local
docs, but NOT the internet.
16. Ansible Collections¶
Understanding Collections¶
Collections bundle: - Modules - Roles - Plugins (lookup, filter, callback, connection)
Namespace format: namespace.collection (e.g., ansible.posix, community.general)
Key Collections for RHCE¶
| Collection | Contents |
|---|---|
ansible.builtin |
Core modules (dnf, copy, service, template, file, etc.) |
ansible.posix |
SELinux, firewalld, mount, authorized_key, cron, sysctl |
community.general |
nmcli, parted, lvg, lvol, filesystem, sefcontext, timezone |
community.crypto |
Certificate management |
Installing and Using¶
# Install
ansible-galaxy collection install community.general
# List installed collections
ansible-galaxy collection list
# Use in playbooks with FQCN
- community.general.nmcli:
conn_name: eth0
type: ethernet
...
Exam Day Tips¶
- Read the objectives carefully. Each task maps to a specific skill.
- Use ansible-doc extensively. It's your only reference.
- Always use FQCN for module names (ansible.builtin.copy, not just copy).
- Test incrementally. Run
--syntax-checkthen--check --diffbefore full runs. - Manage your time. Don't get stuck — move on and come back.
- Idempotency matters. Your playbooks must be re-runnable without errors.
- Check your work. Run the playbook twice — second run should show no changes.
- Don't forget handlers. If config changes need a service restart, use handlers.
- Variable precedence. Extra vars (
-e) always win. Role defaults are lowest. - Vault password. They will tell you the vault password — note it immediately.
Wiki Navigation¶
Prerequisites¶
- Linux Ops (Topic Pack, L0)
- Ansible Automation (Topic Pack, L1)
Related Content¶
- LPIC / LFCS Exam Preparation (Topic Pack, L2) — Bash / Shell Scripting, Linux Fundamentals, systemd
- Linux Ops (Topic Pack, L0) — Bash / Shell Scripting, Linux Fundamentals, systemd
- Mental Models (Core Concepts) (Topic Pack, L0) — Ansible, Git, Linux Fundamentals
- Track: Foundations (Reference, L0) — Bash / Shell Scripting, Git, Linux Fundamentals
- Advanced Bash for Ops (Topic Pack, L1) — Bash / Shell Scripting, Linux Fundamentals
- Bash Exercises (Quest Ladder) (CLI) (Exercise Set, L0) — Bash / Shell Scripting, Linux Fundamentals
- Cron & Job Scheduling (Topic Pack, L1) — Bash / Shell Scripting, systemd
- Deep Dive: Linux Boot Sequence (deep_dive, L2) — Linux Fundamentals, systemd
- Deep Dive: Systemd Architecture (deep_dive, L2) — Linux Fundamentals, systemd
- Deep Dive: Systemd Timers Journald Cgroups and Resource Control (deep_dive, L2) — Linux Fundamentals, systemd