← RHCE in Ansible: automation and operations
10 / 15 · 45 MIN

Validated configuration and observable activation

Reject invalid candidates and measure the difference between prediction, convergence and activation.

Three separate decisions

A configuration may have valid syntax yet fail the business requirement. Separate candidate-file validation, byte installation and service acceptance. In this exercise the candidate has exactly worker and port; the worker must belong to the expected set and the port must lie between 1024 and 65535. These rules belong to the fictional scenario. They are not a universal banking application configuration. The Python validator reads the temporary path supplied through validate. If it rejects port 70000, the template must not replace the previous worker.conf.

Observe initial execution and repetition

Run converge.yml with service_port=8443. The template installs the file with mode 0600 and notifies the handler that appends one activation-log line. flush_handlers places that effect before final inspection. In the lab, a line means only that the handler executed; no application process is restarted. Repeat the same command and compare content, mode and line count. The recap should report zero changes and the log should retain one line per worker. If the template included a changing timestamp, the repeat would no longer represent the same input even if functional intent had not changed.

Inject an invalid proposal

The runner saves approved bytes before trying port 70000. Execution must fail and both destinations must retain identical bytes. It also confirms that the activation log did not grow. This combination is more useful than finding the word failed alone: it demonstrates candidate rejection and preservation of observed state. A validator that always examines the installed file while ignoring %s could approve a bad candidate because it read the old version. Deliberately test that case when reviewing a team validator.

Interpret check mode

The next rehearsal proposes 9443 with --check. The actual file remains at 8443. A changed prediction does not mean the service changed; the command handler is not activation evidence in this mode either. Inspect tasks with check_mode: false before treating a rehearsal as non-mutating. In production, add a functional probe after activation and define what happens if syntax passes but reconciliation fails. Record valid candidate, installed configuration, healthy process and accepted business outcome separately.

# File: converge.yml
- name: Validate and converge synthetic worker configuration
 hosts: workers
 gather_facts: false
 vars:
 node_dir: "{{ lab_root }}/{{ inventory_hostname }}"
 tasks:
 - name: Create isolated worker directory
 ansible.builtin.file:
 path: "{{ node_dir }}"
 state: directory
 mode: '0700'
 - name: Validate before replacing the destination
 ansible.builtin.template:
 src: config.j2
 dest: "{{ node_dir }}/worker.conf"
 mode: '0600'
 validate: "{{ ansible_playbook_python }} {{ playbook_dir }}/validate.py %s"
 notify: Record activation
 - name: Finish activation before acceptance
 ansible.builtin.meta: flush_handlers
 - name: Inspect persisted file content
 ansible.builtin.stat:
 path: "{{ node_dir }}/worker.conf"
 checksum_algorithm: sha256
 register: observed
 - name: Require a private regular file after normal execution
 ansible.builtin.assert:
 that:
 - observed.stat.isreg
 - observed.stat.mode == '0600'
 when: not ansible_check_mode
 handlers:
 - name: Record activation
 ansible.builtin.command:
 argv:
 - "{{ ansible_playbook_python }}"
 - -c
 - "import pathlib,sys; p=pathlib.Path(sys.argv[1]); p.open('a').write('activated' + chr(10))"
 - "{{ node_dir }}/activation.log"

# File: config.j2
worker={{ inventory_hostname }}
port={{ service_port }}

# File: validate.py
import pathlib,sys
lines=pathlib.Path(sys.argv[1]).read_text.splitlines
values=dict(line.split('=',1) for line in lines)
assert set(values)=={'worker','port'}
assert values['worker'] in {'worker_a','worker_b'}
assert 1024<=int(values['port'])<=65535
IN PRACTICE

Port 70000 fails before replacement; previous hashes and log remain unchanged.

Common pitfalls

Validating the old destination; activating before validation; counting a prediction as an applied change.

Related topics: Executable rehearsal: scope, state and limits · Archives, residual files and markers · Recovery and change outcome

Take this idea with you

Validate the candidate and observe installation, activation and acceptance with separate criteria.

Create account

Reference: Template module · EX294 current objectives inspected 2026-09-30; RHCE in Ansible framework effective 2026-05-11; booking product version not publicly pinned

Red Hat®, RHCE and Ansible are trademarks or registered trademarks of Red Hat, Inc. bigsavant.com is an independent preparation platform and is not affiliated with, associated with, sponsored, authorised or endorsed by Red Hat. Content and questions are original, are not official exam questions, and completing our tests does not award or guarantee any certification. Names are used only to identify the subject. All other trademarks belong to their respective owners.