Practise Red Hat Certified Engineer EX294 practice questions — original exam-style scenarios covering every exam domain, with detailed explanations, wrong-answer analysis, and common exam traps.
These are the questions most candidates get wrong. They require connecting multiple concepts, reading tricky output, or knowing edge-case behaviour that isn't on most study cards. Practising them trains you to operate under uncertainty — a necessary skill on the real exam.
Quick answer
Hard Difficulty Questions questions test whether you can apply the concept in context, not just recognise a definition.
How the topic appears in realistic exam-style scenarios.
Which detail in the question changes the correct answer.
How to eliminate plausible but wrong options.
How to connect the question back to the wider exam objective.
Related practice questions
Related EX294 topic practice pages
Scenario questions usually connect to one or more exam topics. Use these links to review the underlying concepts behind the scenario.
A senior engineer needs to debug an Ansible playbook that uses lookups. Which TWO plugins can be used to retrieve data from a file on the control node? (Select exactly two.)
A
ini
Why it fails: ini lookups parse INI files, but return specific keys, not whole file.
B
password
Why it fails: password generates random passwords, not retrieve file data.
C
csvfile
Why it fails: csvfile looks up a specific row/column, not entire file.
A large enterprise manages thousands of servers grouped by data center. They are designing a rolling update that must complete within a maintenance window. Which combination of Ansible strategies best minimizes total update time while maintaining safety?
A
Set serial: 0 to update all hosts simultaneously.
Why it fails: No parallelism control; could overload systems and cause downtime.
B
Set serial to 10% and max_fail_percentage to 25%.
Setting `serial` to 10% batches hosts into parallel groups, cutting total update time across thousands of servers while still limiting blast radius. `max_fail_percentage: 25` halts the rolling update once a quarter of the current batch fails, preserving safety within the maintenance window.
C
Set forks to 100 and max_fail_percentage to 50.
Why it fails: forks does not control batch order; max_fail_percentage alone without serial may update all at once.
D
Set serial to 1 to update one host at a time with max_fail_percentage: 0.
Why it fails: Too slow; updates all hosts sequentially.
An administrator has a requirements.yml file specifying roles from multiple sources: a public Galaxy server, a private Git repository, and a local path. They want to install all roles into the roles directory of the current project. Which command will achieve this?
Why it fails: The collection subcommand reads requirements.yml expecting a collections key, so a roles-only file yields nothing installed. It is tempting because requirements.yml serves both role and collection installs; it would be correct only if the file listed collections rather than roles from Galaxy, Git and a local path.
The `-r` flag reads every entry in requirements.yml, resolving Galaxy, Git and local sources in one pass, while `--roles-path ./roles` overrides the default install location so roles land in the project's own roles directory rather than the user-level path — satisfying the stem's requirement to install all roles locally.
C
ansible-galaxy install -r requirements.yml -p .
Why it fails: The -p flag sets the role installation path, but '.' resolves to the project root, not its roles subdirectory, so roles land in the wrong location. It is tempting because -p is the correct flag for customising install location, and it would work if roles were wanted directly in the project root.
D
ansible-galaxy role install --force -r requirements.yml
Why it fails: The --force flag reinstalls roles that already exist, but it does not change the source resolution or install path. It is tempting because --force is used when roles are present but stale; here the requirement is simply installing all roles from the mixed sources into the project's roles directory, which the plain install command already does.
An organization uses Ansible Tower (AWX) for rolling updates. They have a job template that runs a playbook with serial: 5. The inventory contains 50 hosts. The update fails after the first batch due to a syntax error in a playbook. After fixing the error, the administrator wants to resume updating from where it left off without updating already successful hosts. Which approach achieves this?
A
Use the job template survey to input a list of hosts to skip, and pass it as --limit.
Using `--limit` with an explicit host list lets the administrator target only the remaining 45 hosts, since the first batch of 5 already succeeded. This satisfies the requirement to resume without re-updating successful hosts, though the list must be supplied manually via the survey rather than tracked automatically.
B
Create a new job template with a dynamic inventory subset excluding the first batch hosts.
Why it fails: Dynamic subsets require inventory plugin logic, not straightforward for resuming.
C
Modify the playbook to check if a host has already been updated using a fact and skip it.
Why it fails: Complex and requires careful fact management; not built-in.
D
Rerun the entire playbook; Ansible will skip hosts that are already in the desired state.
Why it fails: Idempotency may not guarantee that tasks are skipped if the state is not perfectly checked.
A DevOps engineer is responsible for coordinating a rolling update of a Red Hat OpenShift Container Platform 4.12 cluster with 10 worker nodes. The cluster hosts a stateful application that uses persistent volumes with ReadWriteOnce access mode. The update involves a minor version upgrade of the cluster from 4.12.0 to 4.12.5. The engineer uses the recommended `oc adm upgrade` command. During the update, after the first worker node is updated, the engineer notices that the node's status shows 'NotReady' and the cluster version operator reports a degraded status. A check of the node logs reveals 'kubelet: Failed to run kubelet: Could not get kubelet config from cluster: could not get config from cluster: context deadline exceeded'. Which action should the engineer take first?
A
Rebuild the node from scratch using the machine config operator.
Why it fails: Rebuilding is a last resort; check network first.
B
Check the network connectivity between the updated node and the control plane nodes on port 6443.
The kubelet error "context deadline exceeded" while fetching its config from the cluster indicates the updated node cannot reach the control plane's API server. Verifying connectivity to port 6443 on the control plane nodes directly tests the transport path that the kubelet depends on, satisfying the stem's requirement to diagnose why the node reports NotReady after upgrade.
C
Roll back the entire cluster to version 4.12.0 using the `oc adm upgrade --to=4.12.0` command.
Why it fails: Rolling back the whole cluster is excessive; the issue may be limited to one node.
D
Increase the kubelet's `--node-status-update-frequency` parameter on the updated node.
Why it fails: This parameter affects status reporting, not initial configuration retrieval.
You manage an Ansible Tower instance that has multiple inventories synced from different sources (static, dynamic cloud, and satellite). Recently, a job template that uses an inventory synced from Red Hat Satellite fails with 'No hosts matched' even though hosts exist in Satellite. The inventory sync job runs successfully and shows hosts populated in Tower. The job template uses a limit field set to '*' and there are no tags or other filters. The playbook is simple: 'hosts: all'. What is the most likely cause?
A
The inventory sync job is not scheduled to run before the job template.
Why it fails: While scheduling is important, the sync runs successfully and hosts appear populated, so this is not the immediate cause.
B
The groups imported from Satellite are nested and the job template's limit does not include the parent group.
Why it fails: Nested groups do not prevent 'all' from matching hosts. The 'all' group includes all hosts regardless of nesting.
C
The inventory source is configured to use 'scraped from project' instead of 'satellite'.
Selecting 'scraped from project' makes Tower parse an inventory file from the project repository rather than querying the Satellite API. The sync still succeeds and lists hosts, but the job template's inventory lacks the Satellite-sourced hosts, producing 'No hosts matched'.
D
The credential used for Satellite sync is expired.
Why it fails: If the credential were expired, the sync would fail, not succeed.
A company manages a large infrastructure of 10,000 servers using Ansible. The Ansible control node runs on a powerful machine with 32 cores and 64GB RAM. Recently, a playbook that processes server facts and generates a compliance report has become extremely slow, taking over 6 hours to complete. The playbook uses several `set_fact` tasks with complex jinja2 filters including `selectattr`, `map`, `json_query`, and `combine`. The inventory is stored in a dynamic inventory script that returns JSON. The team suspects that the filter operations are causing performance bottlenecks, especially when creating large data structures. A junior engineer suggests splitting the playbook into multiple plays and using `delegate_to` to distribute processing across managed nodes. Another suggests using the `ansible.builtin` module instead of filters. The senior architect recommends converting the heavy filter logic into a custom action plugin. What is the most effective approach to significantly reduce the execution time while maintaining functionality?
A
Distribute the processing across managed nodes using `delegate_to` and loop over hosts.
Why it fails: Delegating filter work to managed nodes adds SSH round-trips and per-host task overhead, so the control node still serialises the 10,000-host loop; Jinja2 filters run in the controller's Python process regardless of where tasks execute. It tempts when offloading genuinely host-local work, such as running a script on each server.
B
Use `ansible.builtin` modules to replace filter operations; for example, use `add_host` and `group_by` to structure data.
Why it fails: `add_host` and `group_by` mutate inventory groupings rather than transforming fact dictionaries, so `selectattr`, `map` and `json_query` logic cannot be expressed; each task still incurs controller overhead. These modules suit dynamically building inventories from discovered hosts, not reshaping facts.
C
Convert the heavy filter logic into a custom Python action plugin that runs on the control node and performs data transformation efficiently.
A custom action plugin executes as Python on the control node, bypassing the per-host, per-task overhead that makes set_fact with heavy Jinja2 filters slow across 10,000 hosts. Delegation and module swaps still incur task iteration costs, so the plugin is the only approach that removes the bottleneck.
D
Use `with_items` and `with_nested` loops instead of filters, as loops are faster.
Why it fails: `with_items` and `with_nested` are controller-side lookup loops that execute sequentially in Python and cannot replicate `selectattr`, `map` or `json_query` semantics, so correctness breaks while runtime worsens. Loops suit small iteration sets, not transforming large fact dictionaries.
An Ansible playbook uses a rolling update strategy with serial: 1. After the first host is updated, the playbook stops and shows 'PLAY RECAP' with only one host. What is the most likely reason?
A
the playbook has a 'failed_when' condition that stops execution
Why it fails: A 'failed_when' condition would cause the play to stop on failure, but it would not normally result in PLAY RECAP showing only one host unless all hosts fail. The scenario describes a successful run on one host.
B
the inventory contains only one host
Correct. If the inventory contains only one host, the playbook will run on that host and then finish, producing a PLAY RECAP with exactly one host. This is the most likely reason given the behavior described.
C
the play uses 'delegate_to' incorrectly
Why it fails: Incorrect delegate_to usage would misdirect a task to another host, not truncate the play after one host; serial: 1 runs the whole play per host, so a failure or unreachable host on the first batch halts the run. It is tempting because delegate_to genuinely causes unexpected host targeting in other scenarios.
D
the playbook does not have any task that triggers the next batch, and 'serial' only controls concurrency, not retry
Why it fails: While 'serial: 1' controls concurrency, it does not stop execution after one batch; Ansible automatically proceeds to the next host after the current batch completes. This option is incorrect because it suggests that 'serial' alone causes the playbook to stop.
An Ansible automation is used to manage firewall rules on a set of Linux servers. The playbook defines a variable "allow_rules" as:
allow_rules:
- proto: tcp
dport: 80
comment: HTTP
- proto: tcp
dport: 443
comment: HTTPS
The engineer needs to use the "iptables" module to create rules. The module expects "chain" to be specified, and the engineer wants to dynamically set the chain based on the port: ports 80 and 443 go to "INPUT" chain, while others go to "FORWARD". The engineer writes a loop:
- name: Add iptables rules
iptables:
chain: "{{ item.dport | map('some_filter') }}"
protocol: "{{ item.proto }}"
destination_port: "{{ item.dport }}"
comment: "{{ item.comment }}"
loop: "{{ allow_rules }}"
But this fails because the chain field expects a string, not a list. The engineer realizes the map filter returns a list. Which of the following modifications correctly sets the chain based on port number?
Why it fails: regex_replace returns the original string unchanged when the pattern does not match, so ports other than 80 and 443 yield the port number rather than FORWARD, and the default filter never triggers. It is tempting because regex substitution looks like conditional logic, and would be correct for rewriting matched substrings, not for branching between two chain values.
B
chain: "{{ (item.dport in [80,443]) | ternary('INPUT', 'FORWARD') }}"
The ternary filter tests whether item.dport is 80 or 443 and returns the matching string, so chain receives 'INPUT' or 'FORWARD'. This satisfies the module's string requirement, unlike the map filter which returned a list.
C
chain: "{{ item.dport | select('in', [80,443]) | list | first | default('FORWARD') }}"
Why it fails: select filters the list but returns a list, and first then yields the port number itself, not the chain name, so the chain field receives an invalid value. It is tempting because select and first resemble conditional extraction, and would be correct for picking matching list elements, not for mapping a port to a chain name.
Why it fails: The replace filter only rewrites the literal substrings '80' and '443'; every other port, such as 22 or 8080, passes through unchanged, so the chain becomes a port number rather than FORWARD. It is tempting because replace chains neatly for the two listed ports, but it cannot express the conditional fallback the scenario requires.
Refer to the exhibit. An administrator runs an Ansible playbook and gets an unreachable error. The administrator has set ansible.cfg as shown. Which configuration change would most likely resolve the issue?
Why it fails: The error is about SSH connection, not privilege escalation.
B
Set 'host_key_checking = False' in ansible.cfg.
Why it fails: Host key checking would cause a different error, not permission denied.
C
Set 'ask_pass = true' in ansible.cfg.
Setting `ask_pass = true` prompts interactively for the SSH password, supplying credentials that the current configuration lacks. The unreachable error stems from authentication failure, not connectivity, so enabling a prompt satisfies the missing-credential constraint without altering inventory or connection settings.
D
Add 'remote_user: root' to the playbook.
Why it fails: The remote user is already set to 'ansible', but the error is about authentication, not user.
Why it fails: The copy task does not require become; it's a local operation.
B
The remote_src parameter is not set in the copy task
Why it fails: remote_src is not a parameter of copy; it's used in unarchive.
C
The archive file does not exist on the control node
Ansible resolves the src path relative to the control node's filesystem, not the managed host. The error therefore means the archive is absent from the control node at that path, so the copy task cannot find its source file.
D
The unarchive task should use remote_src: no
Why it fails: The error occurs before the unarchive task.
A managed node is not responding to Ansible automation. The administrator verifies that the node is reachable via SSH and that the SSH key is correctly deployed. However, 'ansible all -m ping' fails with 'UNREACHABLE'. The automation controller uses a custom execution environment. What is the most likely cause?
A
The SSH private key has incorrect permissions on the controller.
Correct. If the SSH private key has incorrect permissions (e.g., group/world readable), SSH will refuse to use it, causing an 'UNREACHABLE' error. The administrator may have verified the key's presence but not its permissions within the execution environment.
B
The remote user specified in the credential does not have sudo access.
Why it fails: Incorrect. Missing sudo access would cause a privilege escalation failure (e.g., 'Permission denied' or 'Missing sudo password'), not an 'UNREACHABLE' status. The host would still be reachable.
C
The custom execution environment is missing the 'python3' or 'python' package.
Why it fails: Incorrect. Missing 'python3' or 'python' on the managed node would cause a module execution failure (e.g., 'FAILED! => {"msg": "...python in path..."}'), not an 'UNREACHABLE' status. The ping module itself does not require Python on the remote host; it uses raw SSH.
D
The automation controller is behind a firewall that blocks SSH.
Why it fails: Incorrect. If a firewall blocked SSH, the administrator would not have verified SSH connectivity. Since SSH is reachable, this is not the cause.
Refer to the exhibit. An administrator runs a playbook in check mode and receives the shown output. What should be done to fix the failure while maintaining idempotency?
Network Topology
A
Modify the deploy role to use the force parameter when copying files.
Why it fails: The force parameter on copy overwrites the destination every run, so the task always reports changed and idempotency is lost. The failure stems from a missing directory, which a file task with state: directory should create. It is tempting to silence the copy error, but forcing masks rather than fixes the cause.
B
Add a task in the apache role to create the /var/www/html directory using the file module.
Creating the directory with the file module declares the desired state, so repeated runs report no change and idempotency holds. The failure stems from httpd's document root being absent before the copy task runs; the file module with state: directory satisfies that prerequisite declaratively rather than through an imperative shell command.
C
Run the playbook without check mode to force the deployment.
Why it fails: Running without check mode forces changes and hides the underlying non-idempotent task, so a second run still reports changed. Check mode exists to expose exactly this drift. It is tempting to bypass the error, but the requirement is a playbook that converges without forcing.
D
Add a pre_task in the playbook to create /var/www/html before roles execute.
Why it fails: A pre_task creating /var/www/html runs before roles, but check mode still reports the role's copy as changed because the directory is absent at check time; the fix belongs in the role's own tasks using a file task with state: directory. It is tempting as a quick ordering fix, yet it does not make the role idempotent.
An operations team is designing a rolling update for a stateful application that requires quorum (minimum 3 out of 5 nodes online). They plan to use Ansible's serial keyword. Which serial value ensures the update proceeds without breaking quorum while still being efficient?
A
serial: 2
Serial 2 updates two nodes at a time, leaving three of five online, which preserves the quorum minimum throughout the rolling update. Larger values risk dropping below three; serial 1 is safe but slower, so 2 balances safety with efficiency.
B
serial: 1
Why it fails: serial: 1 updates nodes one at a time, keeping four online and preserving quorum, but it is the least efficient choice here. It is tempting as the safest possible setting, and would be correct for clusters with no spare capacity, yet the question asks for efficiency alongside quorum safety.
C
serial: 3
Why it fails: serial: 3 updates three nodes at once, leaving only two online, which falls below the three-node quorum minimum. It is tempting because it matches the quorum size, and would work if the requirement were updating exactly a quorum's worth, but the remaining nodes must still satisfy quorum during each batch.
D
serial: 5
Why it fails: serial: 5 updates all five nodes in one batch, taking the whole cluster offline simultaneously and breaking the three-node quorum. It is tempting as the fastest single-pass option, and would suit a stateless fleet where no node interdependence exists, but quorum-dependent stateful clusters cannot tolerate it.
You are managing a large infrastructure of 500 Linux servers. The servers are divided into groups: 'web', 'app', and 'db'. Each group has specific configuration requirements. You have developed a set of Ansible roles to manage these configurations. Recently, you noticed that when you run the playbook against all servers, the 'web' role is applied to 'app' servers due to a variable misconfiguration. The playbook uses include_role with a variable that determines which role to apply. The variable is defined in group_vars/all.yml as 'server_role: web'. However, each group should have its own role: 'web' for web servers, 'app' for app servers, 'db' for db servers. The playbook includes the role based on '{{ server_role }}'. What is the best course of action to fix this issue without modifying the playbook structure?
A
Change the variable in group_vars/all.yml to a list and use 'include_role' with loop.
Why it fails: Looping include_role over a list still resolves server_role from group_vars/all.yml, so every host receives the same role; the variable's precedence, not its type, causes the fault. Lists suit applying several roles to one host, which is correct when a host genuinely needs multiple roles.
B
Add a 'when' condition to the include_role task to check the group name.
Why it fails: A when condition tests group membership but server_role still resolves to web from group_vars/all.yml, so app and db hosts skip the task entirely rather than loading their own role. Conditional includes suit optional role execution, which is correct when some hosts should legitimately run no role.
C
Define the server_role variable in group_vars/web.yml, group_vars/app.yml, and group_vars/db.yml with the appropriate values.
Defining `server_role` in each group's `group_vars` file exploits Ansible's variable precedence: group-specific variables override the `group_vars/all.yml` default, so each host resolves `{{ server_role }}` to its own group's role. This satisfies the constraint of fixing the misconfiguration without altering the playbook's `include_role` structure.
D
Define the server_role in host_vars for each server.
Why it fails: Host-level variables override group_vars but require editing 500 entries, and the stem's group-based requirement is already expressible through group_vars precedence. Host_vars is the right choice when individual hosts within a group need exceptions, not when whole groups share one role.
A developer wrote a custom filter plugin in a Python file `my_filters.py` and placed it in the directory `./filter_plugins/`. The playbook fails with 'ERROR! no filter named 'my_custom_filter''. The playbook is located in `/home/user/project/playbook.yml`. The `ansible.cfg` file in the same directory does not set `filter_plugins`. Which is the most likely cause?
A
The filter file must be named `__init__.py`
Why it fails: Ansible loads every .py file in filter_plugins/ as a module; __init__.py is only needed to make a directory a Python package, which Ansible does not require here. It is tempting because Python packages conventionally use __init__.py, and that would be correct if the plugins were imported as a package rather than loaded by path.
B
The plugin file must be a Python module with a class named `FilterModule` and the filter function must be listed in the `filters` method
Ansible loads filter plugins only from Python modules exposing a FilterModule class whose filters method returns a dict mapping filter names to callables. Without that structure, the plugin is ignored, so no filter named my_custom_filter is registered, producing the error.
C
The filter function must be imported in the playbook via `filter_plugins: my_filters`
Why it fails: filter_plugins is an ansible.cfg or play-level keyword pointing to a directory, not a playbook import statement for individual files. It is tempting because importing a module by name is standard Python practise, and that would be correct if the filter were a library consumed inside a module_utils or action plugin rather than a filter plugin.
D
The filter function name does not match the class name in the plugin
Why it fails: Filter plugins expose plain functions in a FilterModule mapping; there is no class-name matching requirement as there is for, say, lookup or callback plugins. It is tempting because many Ansible plugin types do tie the class name to the plugin name, and that rule would be correct for a callback plugin registered by its class.
The job template running against host db1 uses a machine credential with an SSH key. The key is correctly configured in Automation Controller. However, the job fails with the error shown. What is the most likely cause?
Exhibit
Refer to the exhibit.
Error message from a job run:
```
fatal: [db1]: UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).",
"unreachable": true
}
```
A
The SSH public key corresponding to the private key is not installed on the target host
Automation Controller authenticates over SSH using the private key from the machine credential. If the matching public key is absent from db1's authorised_keys, authentication fails, producing the connection error. Installing the public key on the target host resolves it.
B
The vault password is incorrect
Why it fails: A wrong vault password would surface as a decryption failure for encrypted variables, not an SSH authentication error against db1. It is tempting because credentials and secrets are both stored in Automation Controller, but the reported failure points to the machine credential's SSH key or remote user, not vault decryption.
C
The SSH port is blocked by a firewall
Why it fails: A blocked SSH port would prevent connection entirely, producing a timeout or refused-connection error rather than an authentication failure; the stem states the key is correctly configured and the job reaches the host. Firewall rules are the right fix when every job to a host times out before any credential is offered.
D
The host's SSH host key has changed and the known_hosts file is outdated
Why it fails: Automation Controller does not consult a local known_hosts file for machine credentials; host-key checking is disabled by default unless explicitly enabled, so a changed host key would not cause this failure. Updating known_hosts is correct when strict host-key checking is enabled and the error names the host key.
Refer to the exhibit. An administrator runs an Ansible playbook and receives the error shown. The playbook uses a variable 'vault_httpd_port' that should be stored in an encrypted vault file. Which step should the administrator take first to resolve the issue?
Exhibit
[root@controller ~]# ansible-playbook -i inventory.ini site.yml --ask-vault-pass
Vault password:
PLAY [all] ***************************************************************
TASK [Gathering Facts] ***************************************************
ok: [server1]
TASK [common : Install httpd] ********************************************
fatal: [server1]: FAILED! => {"changed": false, "msg": "The task includes an option with an undefined variable. The error was: 'vault_httpd_port' is undefined"}
PLAY RECAP ****************************************************************
server1 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
A
Re-encrypt the vault file with a different password.
Why it fails: Re-encrypting with a different password does not fix a playbook that cannot decrypt the existing vault; the error concerns the supplied password or vault ID, not the file's encryption key. Re-encryption suits rotating credentials after a compromise, not resolving a decryption error.
B
Add 'vault_httpd_port' to the group_vars/all.yml file without encryption.
Why it fails: Storing the variable unencrypted in group_vars/all.yml defeats the vault's purpose and does not address the decryption failure shown. Plain group_vars files are the right place for non-sensitive variables, so this would be correct only if the value were never meant to be secret.
C
Use the --ask-vault-pass option again with a different password.
Why it fails: Retrying with another password assumes the first was wrong, but the error typically indicates the vault password source was not supplied or the wrong vault ID was referenced. Re-prompting is correct when the administrator genuinely mistyped the password during an interactive run.
D
Ensure the vault file is referenced in the playbook using include_vars or vars_files, and that the vault password is correct.
Referencing the vault file via `vars_files` or `include_vars` is what actually loads `vault_httpd_port` into the play's variable scope; without that, Ansible cannot decrypt or resolve it, producing the undefined-variable error. Supplying the correct vault password then allows decryption to succeed, satisfying the stem's encrypted-vault constraint.
An organization uses a private Git repository to store Ansible content collections. They want to automate the building of execution environments that include these collections. Which approach is recommended?
A
Store the collection tarball in a Git LFS and use an ADD command in the base image.
Why it fails: This approach is not reproducible and mixes infrastructure with application code.
B
Add the Git repository as a source in the execution-environment.yml using the 'git' type.
Why it fails: ansible-builder does not support git sources directly; it expects collections from Galaxy or Automation Hub.
C
Use ansible-builder to clone the repository during build with a pre-build script.
Why it fails: Pre-build scripts are not recommended for including collections; they bypass dependency management.
D
Build the collection manually, publish it to a private Automation Hub, then reference it in the EE.
Publishing the built collection to a private Automation Hub gives the execution environment build a resolvable source, satisfying the constraint that collections live in a private Git repository rather than a public Galaxy server. The EE definition then references that Hub, so ansible-builder can fetch and install the collection during image creation.
These EX294 practice questions are part of Courseiva's free Red Hat certification practice question bank. Courseiva provides original exam-style EX294 questions with detailed explanations, topic-based practice, mock exams, readiness tracking, and study analytics.