CCNA Filters Plugins Questions

72 questions · Filters Plugins topic · All types, answers revealed

1
MCQmedium

Consider the task: `- debug: msg={{ item | upper }}` with `loop: "{{ ['a','b'] }}"`. What will be the output?

A.An error because upper expects a string, not a loop variable.
B.Two debug messages: 'a' and 'b'
C.Two debug messages: 'A' and 'B'
D.One debug message with the list ['A','B']
AnswerC

Correct; each item is uppercased and printed separately.

Why this answer

Option C is correct because the `upper` filter in Ansible converts each string item in the loop to uppercase. The `loop` directive iterates over the list `['a','b']`, and for each iteration, the `{{ item | upper }}` expression applies the `upper` filter to the current item, resulting in `'A'` and `'B'`. The `debug` module then prints each transformed value as a separate message.

Exam trap

The trap here is that candidates may overlook the fact that the `upper` filter is applied to each item individually within the loop, leading them to think the output remains lowercase (Option B) or that the filter fails on a loop variable (Option A).

How to eliminate wrong answers

Option A is wrong because the `upper` filter in Ansible is designed to work with strings, and `item` in a loop is a scalar value (a string in this case), not a list; the filter correctly converts each string to uppercase. Option B is wrong because it ignores the effect of the `upper` filter, which transforms the items to uppercase before output. Option D is wrong because the `loop` directive causes the `debug` module to execute once per item, producing two separate messages, not a single message containing a list.

2
MCQmedium

An administrator needs to combine two dictionaries, `base_config` and `user_config`, where keys in `user_config` should override keys in `base_config`, and nested dictionaries should be merged recursively. Which filter syntax achieves this?

A.{{ base_config | combine(user_config, recursive=True) }}
B.{{ base_config | combine(user_config, deep=True) }}
C.{{ base_config | combine(user_config) }}
D.{{ base_config | combine(user_config, list_merge='replace') }}
AnswerA

Correctly enables recursive merge.

Why this answer

Option A is correct because the `combine` filter in Ansible with `recursive=True` merges two dictionaries, with `user_config` overriding `base_config`, and recursively merges nested dictionaries. This matches the requirement exactly, as `recursive=True` ensures that nested structures are combined rather than replaced outright.

Exam trap

The trap here is that candidates often confuse `recursive=True` with `deep=True` (which does not exist) or assume that the default `combine` behavior (shallow merge) is sufficient for nested dictionaries, leading them to pick option B or C.

How to eliminate wrong answers

Option B is wrong because `deep=True` is not a valid parameter for the `combine` filter; the correct parameter for recursive merging is `recursive=True`. Option C is wrong because using `combine` without any parameters performs a shallow merge, where nested dictionaries are replaced entirely by the `user_config` values, not merged recursively. Option D is wrong because `list_merge='replace'` controls how lists are merged (replacing the base list with the user list), but it does not enable recursive merging of nested dictionaries, so nested dicts would still be replaced.

3
MCQeasy

A DevOps engineer needs to extract the first 10 lines of a log file and store them in a variable for further processing. Which Ansible filter should be used?

A.regex
B.first
C.slice
D.head
AnswerD

head filter returns the first N lines of a string or list.

Why this answer

Option D is correct because the `head` filter in Ansible extracts the first N items from a list or string. In this scenario, using `head(10)` on the log file content (read via `lookup('file', ...)`) returns the first 10 lines, which can be stored in a variable for further processing. This filter is specifically designed for such truncation tasks.

Exam trap

The trap here is that candidates may confuse the `head` filter with the Linux `head` command or think a custom filter is needed, but Ansible provides this built-in filter specifically for list/string truncation, and the exam expects familiarity with its name and behavior.

How to eliminate wrong answers

Option A is wrong because the `regex` filter is used for pattern matching and extraction using regular expressions, not for selecting the first N lines of a file. Option B is wrong because there is no `first` filter in Ansible; the correct filter for this purpose is `head`. Option C is wrong because the `slice` filter extracts a contiguous subset of a list based on start and end indices, but it is not the idiomatic or simplest way to get the first 10 lines; `head` is the intended filter for this common task.

4
Multi-Selecthard

When using the 'uri' module to interact with a REST API in Ansible, which TWO of the following statements about error handling and response parsing are correct?

Select 2 answers
A.The 'follow_redirects' parameter must be set to 'all' to handle HTTP 4xx and 5xx errors gracefully.
B.To access the response body, use the 'body' key of the registered variable and then parse it with a filter like 'json_query'.
C.Use the 'status_code' parameter to define which HTTP response codes are considered successful.
D.The 'failed_when' condition can inspect the HTTP response before the request is sent.
E.The 'register' keyword automatically parses JSON responses into Ansible variables.
AnswersB, C

Correct: 'body' contains the raw response body as a string.

Why this answer

Option B is correct because when you register the result of an `uri` module call, the response body is stored under the `body` key of the registered variable. To extract or filter data from that JSON body, you can use the `json_query` filter (which relies on jmespath) to parse and query the JSON structure. This is the standard approach for accessing and manipulating API response data in Ansible.

Exam trap

Cisco often tests the misconception that `register` automatically parses JSON or that `failed_when` can run before the request, when in fact the registered variable is a raw dict and `failed_when` only evaluates after the HTTP transaction completes.

5
MCQmedium

You have a list `my_list` containing `[0, 1, 2, '', 'hello']`. You want to extract the first truthy element that exists. Which chain achieves this?

A.`my_list | select('truthy') | first | default('')`
B.`my_list | list | first | default('')`
C.`my_list | select('string') | first | default('')`
D.`my_list | first | default('')`
AnswerA

Correct; select truthy, then first, then default.

Why this answer

Option A is correct because `select('truthy')` filters the list to include only elements that evaluate to `true` in Ansible/Jinja2 (non-zero numbers, non-empty strings, etc.), and `first` returns the first such element. The `default('')` provides a fallback if no truthy element exists. This chain correctly extracts `1` from the list `[0, 1, 2, '', 'hello']`.

Exam trap

The trap here is that candidates may think `first` alone returns the first truthy element, but it actually returns the first element regardless of its truthiness, leading to a falsy result like `0` or `''`.

How to eliminate wrong answers

Option B is wrong because `list` is redundant (the input is already a list) and `first` without `select` returns the first element `0`, which is falsy, not the first truthy element. Option C is wrong because `select('string')` filters only elements that are strings, returning `''` and `'hello'`; `first` then returns `''`, which is falsy, not the first truthy element. Option D is wrong because `first` alone returns the first element `0`, which is falsy, and `default('')` only applies if the list is empty, not if the first element is falsy.

6
MCQhard

A custom filter plugin named `custom_filter` is stored in `./filter_plugins/` relative to the playbook. The playbook runs on a control node where the `ansible.cfg` sets `filter_plugins = /opt/ansible/filters`. Which location will Ansible search for the plugin first?

A.Both A and B, with A first then B
B./opt/ansible/filters
C../filter_plugins/
D.~/.ansible/plugins/filter
AnswerB

The ansible.cfg setting takes precedence over the default.

Why this answer

Option B is correct because Ansible's plugin loading order prioritizes the `filter_plugins` directory specified in `ansible.cfg` over the `./filter_plugins/` directory relative to the playbook. The `ansible.cfg` setting explicitly overrides the default search path, so `/opt/ansible/filters` is checked first.

Exam trap

The trap here is that candidates assume the playbook-relative `./filter_plugins/` directory is always searched first, but the `ansible.cfg` setting explicitly overrides that order, making the configured path the primary search location.

How to eliminate wrong answers

Option A is wrong because it suggests both locations are searched with A first, but the actual order is determined by the `ansible.cfg` setting, which takes precedence over the playbook-relative directory. Option C is wrong because `./filter_plugins/` is only searched if no `filter_plugins` path is set in `ansible.cfg` or if the configured path doesn't exist; here, the explicit path in `ansible.cfg` is checked first. Option D is wrong because `~/.ansible/plugins/filter` is a fallback location searched after the `ansible.cfg` path and the playbook-relative path, not before.

7
MCQhard

A team is migrating from static inventory to dynamic inventory using a custom script. The script returns JSON with a group 'webservers' containing hosts. However, the playbook targeting 'webservers' fails with 'no hosts matched'. Which filter or plugin issue is most likely?

A.The playbook uses 'hosts: all' but should use 'hosts: webservers'.
B.The script is not executable.
C.The 'ansible_host' variable is not set in hostvars.
D.The script output is missing the '_meta' key with 'hostvars'.
AnswerD

Dynamic inventory scripts require _meta structure; without it, Ansible may not recognize hosts.

Why this answer

Dynamic inventory scripts must include a `_meta` key with `hostvars` in their JSON output for Ansible to properly resolve host variables and match hosts to groups. Without `_meta`, Ansible cannot associate host-specific variables (like `ansible_host`) with the hosts listed under `webservers`, causing the playbook to see no matched hosts even though the group exists.

Exam trap

Red Hat often tests the misconception that 'no hosts matched' is caused by a missing group or incorrect host targeting, when in reality it is a dynamic inventory protocol compliance issue—specifically the absence of the `_meta` key in the script output.

How to eliminate wrong answers

Option A is wrong because the playbook already targets 'webservers' (as stated in the question), so changing to 'hosts: all' would not fix the 'no hosts matched' error and would instead run on all hosts. Option B is wrong because a non-executable script would cause a different error (e.g., 'Failed to execute script') rather than 'no hosts matched', and the question states the script returns JSON, implying it runs. Option C is wrong because while `ansible_host` is important for connectivity, its absence does not cause 'no hosts matched'—that error occurs during inventory parsing before variable resolution; missing `ansible_host` would cause a connection failure later.

8
MCQeasy

An Ansible playbook needs to extract the domain name from a list of email addresses stored in variable `emails`. The domain appears after the '@' symbol. Which filter should be used?

A.split
B.regex_replace
C.urldecode
D.base64
AnswerB

The pattern `.*@(.*)` with replacement `\1` extracts the domain.

Why this answer

The `regex_replace` filter can extract the domain by matching the pattern `.*@(.*)` and replacing with `\1`, isolating the part after '@'. This is the correct approach because Ansible's Jinja2 filters include `regex_replace` for pattern-based string extraction, while `split` would require additional steps to isolate the domain.

Exam trap

Red Hat often tests the misconception that `split` alone can extract a substring, but candidates forget that `split` returns a list and requires indexing, while `regex_replace` directly yields the matched group.

How to eliminate wrong answers

Option A is wrong because `split('@')` returns a list of parts (e.g., ['user', 'example.com']), but the filter alone does not extract a single element; you would need to index the result (e.g., `emails[0].split('@')[1]`), making it less direct than `regex_replace`. Option C is wrong because `urldecode` decodes URL-encoded characters (e.g., %20 to space) and has no relevance to extracting substrings after a delimiter. Option D is wrong because `base64` encodes or decodes Base64 data, which is unrelated to parsing email addresses.

9
MCQhard

An Ansible role uses a variable "server_list" which is a list of dictionaries. Each dictionary has a key "ports" which should be a list of integers. However, due to inconsistent input, "ports" could be a comma-separated string (e.g., "80,443") or already a list of integers (e.g., [80,443]). The engineer wants to normalize "ports" to always be a list of integers for further processing. Which of the following tasks correctly normalizes the "ports" field?

A.- set_fact: server_list: "{{ server_list | map('combine', {'ports': item.ports | split(',')}) }}" loop: "{{ server_list }}"
B.- set_fact: server_list: "{{ server_list | map('combine', {'ports': [item.ports] | flatten}) }}" loop: "{{ server_list }}"
C.- set_fact: server_list: "{{ server_list | map('combine', {'ports': item.ports}) }}" loop: "{{ server_list }}"
D.- set_fact: server_list: "{{ server_list | map('combine', {'ports': (item.ports is string) | ternary(item.ports | split(','), item.ports)}) }}" loop: "{{ server_list }}"
AnswerD

Correctly uses ternary to conditionally split string or keep list.

Why this answer

Option D is correct because it uses the `ternary` filter to check if `item.ports` is a string; if true, it splits the string by commas into a list, otherwise it keeps the existing list. This ensures the `ports` field is always normalized to a list of integers, handling both inconsistent input formats.

Exam trap

The trap here is that candidates often overlook the need to conditionally handle both string and list inputs, picking options that either always split (breaking lists) or never split (breaking strings), rather than using a conditional filter like `ternary`.

How to eliminate wrong answers

Option A is wrong because `split(',')` will always produce a list of strings, not integers, and it does not handle the case where `ports` is already a list; also, `map('combine', ...)` with a loop is redundant and incorrectly replaces the entire list. Option B is wrong because `[item.ports] | flatten` will wrap a list in another list and then flatten it, but if `item.ports` is a string, it will create a list containing that single string, not splitting it; it fails to normalize strings into separate integer elements. Option C is wrong because it simply reassigns the `ports` field without any transformation, leaving strings unchanged and not converting them to lists.

10
MCQeasy

A playbook needs to generate a default value for a variable if it is undefined or empty. Which filter with a default value should be used?

A.my_var | fail('fallback')
B.my_var | ternary('fallback', my_var)
C.my_var | default('fallback')
D.my_var | coalesce('fallback')
AnswerC

default filter returns 'fallback' if my_var is undefined.

Why this answer

Option C is correct because the `default` filter in Ansible is specifically designed to provide a fallback value when a variable is undefined or evaluates to an empty string (with the `omit` parameter). It is the idiomatic way to handle missing or empty variables in Jinja2 templates within Ansible playbooks, ensuring idempotency and avoiding undefined variable errors.

Exam trap

Red Hat often tests the distinction between filters that handle undefined variables (`default`) versus filters that perform conditional logic (`ternary`) or error handling (`fail`), and the trap here is that candidates may confuse `coalesce` (a common SQL function) with a valid Ansible filter, leading them to select option D.

How to eliminate wrong answers

Option A is wrong because `fail` is a filter that raises an error, not a filter that provides a default value; using `fail('fallback')` would cause the playbook to fail instead of supplying a fallback. Option B is wrong because `ternary` is a conditional filter that returns one of two values based on a condition, but it does not check for undefined or empty variables; it requires an explicit boolean expression and will error if `my_var` is undefined. Option D is wrong because `coalesce` is not a valid Ansible filter; it is a function in some databases (like SQL) or Jinja2 extensions, but Ansible does not provide a `coalesce` filter for defaulting variables.

11
MCQeasy

An Ansible task uses the variable `{{ my_var | default(required=true) }}`. What happens if `my_var` is undefined?

A.The task fails with an error message
B.The task skips the host
C.The task uses an empty string
D.The task uses the string 'required'
AnswerA

The `required` parameter forces an error when the variable is undefined.

Why this answer

The `default(required=true)` filter in Ansible explicitly marks the variable as required. If `my_var` is undefined, the filter raises an error because it enforces that the variable must be provided, causing the task to fail with an error message. This is a deliberate mechanism to catch missing mandatory variables early in playbook execution.

Exam trap

The trap here is that candidates often confuse `default(required=true)` with setting a default value, thinking it will use the string 'required' or an empty string, when in fact it enforces mandatory variable definition and causes a failure.

How to eliminate wrong answers

Option B is wrong because the `required=true` parameter does not cause the task to skip the host; skipping occurs only with conditionals like `when: my_var is undefined` or `ignore_errors: yes`. Option C is wrong because an empty string is only used if `default('')` is specified without `required=true`. Option D is wrong because the string 'required' is not used as a fallback value; the `required=true` parameter is a boolean flag that triggers an error, not a default value.

12
MCQmedium

An Ansible playbook needs to convert a list of server names into a comma-separated string for an API call. Which filter should be applied to the list variable 'server_list'?

A.map('regex_replace', '^', '')
B.combine(',')
C.join(',')
D.regex_replace('\n', ',')
AnswerC

join filter concatenates list items with the given separator.

Why this answer

Option C is correct because the `join` filter in Ansible is designed to concatenate list elements into a single string using a specified delimiter. Applying `join(',')` to `server_list` will produce a comma-separated string, exactly as required for the API call.

Exam trap

The trap here is that candidates may confuse `join` with `combine` (which works on dicts) or attempt to use regex filters on lists, not realizing that `join` is the only filter that directly converts a list to a delimited string.

How to eliminate wrong answers

Option A is wrong because `map('regex_replace', '^', '')` applies a regex replacement to each element, but the pattern `^` (start of string) with an empty replacement does nothing—it returns the list unchanged, not a string. Option B is wrong because `combine` is a filter for merging dictionaries, not for joining list elements into a string. Option D is wrong because `regex_replace('

', ',')` is intended for strings, not lists; applying it to a list would cause an error or unexpected behavior, and it does not convert a list to a comma-separated string.

13
MCQhard

You are maintaining an Ansible automation for a large Red Hat Enterprise Linux deployment. The playbook configures NTP servers on all managed nodes. It uses a variable 'ntp_servers' defined in group_vars/all.yml as a list: ['0.rhel.pool.ntp.org', '1.rhel.pool.ntp.org', '2.rhel.pool.ntp.org']. The playbook task uses the 'uri' module to test connectivity to each server, but only if the server is reachable. The task currently uses: ``` - name: Test NTP server reachability uri: url: "http://{{ item }}:123" timeout: 5 register: result loop: "{{ ntp_servers }}" until: result.status == 200 retries: 3 delay: 2 ``` However, the playbook fails because the NTP servers do not respond to HTTP on port 123. You need to change the approach to test ICMP reachability using the 'ping' module, but the 'ping' module does not support a custom destination port. You also want to continue using a loop and register the success/failure per server. Which of the following is the best course of action?

A.Replace the task with the 'command' module to run 'ping -c 1 {{ item }}' and register results, using 'ignore_errors' to handle unreachable servers.
B.Use the 'slurp' module to read the /etc/hosts file and check if the server is listed.
C.Use the 'wait_for' module with 'host={{ item }}' and 'port=123' to test connectivity, since NTP uses UDP 123.
D.Use the 'ping' module with a loop over the ntp_servers list, setting the 'data' parameter to the server IP.
AnswerA

The command module can run ping to test ICMP reachability; ignore_errors allows the play to continue on failure while registering results.

Why this answer

Option A is correct because the 'ping' module in Ansible does not support custom ports or protocols, and NTP servers do not respond to HTTP on port 123. Using the 'command' module to run 'ping -c 1' allows ICMP reachability testing, and 'ignore_errors' prevents playbook failure when a server is unreachable, while still registering per-server success/failure via the loop.

Exam trap

The trap here is that candidates assume the 'ping' module can perform ICMP reachability tests to external hosts, but in Ansible, 'ping' only tests SSH connectivity to managed nodes, not network-level ICMP pings.

How to eliminate wrong answers

Option B is wrong because the 'slurp' module reads file content (like /etc/hosts) and cannot test live network connectivity; it only checks if a hostname is statically mapped, not if the server is reachable. Option C is wrong because the 'wait_for' module with 'port=123' tests TCP connectivity, but NTP uses UDP port 123, and 'wait_for' does not support UDP; the task would fail or hang. Option D is wrong because the 'ping' module in Ansible is used for SSH connectivity testing to managed nodes, not for ICMP ping to external hosts; setting 'data' does not change this behavior.

14
MCQmedium

Which lookup plugin is used to retrieve values from an AWS SSM Parameter Store?

A.`ssm`
B.`aws_ssm`
C.`amazon_ssm`
D.`ssm_parameter`
AnswerB

Correct; this is the standard lookup plugin for AWS SSM.

Why this answer

The `aws_ssm` lookup plugin is the correct choice because it is the official Ansible lookup plugin designed to retrieve values from the AWS Systems Manager Parameter Store. It is part of the `amazon.aws` collection and is invoked via `lookup('aws_ssm', 'parameter_name')` in playbooks.

Exam trap

Red Hat often tests the exact naming convention of Ansible lookup plugins, where candidates mistakenly assume the plugin name matches the service abbreviation (e.g., 'ssm') rather than the official plugin name (e.g., 'aws_ssm') that includes the provider prefix.

How to eliminate wrong answers

Option A is wrong because `ssm` is not a valid Ansible lookup plugin; it is a generic abbreviation that does not correspond to any official plugin name. Option C is wrong because `amazon_ssm` is not a recognized lookup plugin; the correct prefix for AWS-related plugins in the `amazon.aws` collection is `aws_`, not `amazon_`. Option D is wrong because `ssm_parameter` is not a valid Ansible lookup plugin; it incorrectly appends '_parameter' to the service abbreviation, which does not match the actual plugin naming convention.

15
MCQeasy

An Ansible playbook needs to generate a list of IP addresses from a range 192.168.1.10 to 192.168.1.20. Which filter should be used in a Jinja2 template?

A.{{ '192.168.1.10/28' | ipsubnet }}
B.{{ '192.168.1.10-20' | ipaddr('range_include') }}
C.{{ '192.168.1.10' | ipmath('+10') }}
D.{{ '192.168.1.10' | ipwrap }}
AnswerB

ipaddr with 'range_include' generates a list of IPs in the inclusive range.

Why this answer

Option B is correct because the `ipaddr` filter with the `'range_include'` parameter generates a list of all IP addresses within a specified range, including both endpoints. In this case, `'192.168.1.10-20' | ipaddr('range_include')` produces a list from 192.168.1.10 through 192.168.1.20, which is exactly what the playbook needs to generate.

Exam trap

The trap here is that candidates often confuse `ipaddr('range_include')` with `ipsubnet` or `ipmath`, mistakenly thinking a CIDR or arithmetic operation can produce a list of IPs, when only the `range_include` parameter of `ipaddr` is designed for that purpose.

How to eliminate wrong answers

Option A is wrong because `ipsubnet` filter calculates subnet information (like network address, broadcast address, or netmask) from a CIDR notation, not a list of individual IPs; `'192.168.1.10/28' | ipsubnet` would return subnet details, not a range of addresses. Option C is wrong because `ipmath` filter performs arithmetic on an IP address (e.g., adding 10 to the last octet) and returns a single IP address, not a list; `'192.168.1.10' | ipmath('+10')` yields 192.168.1.20 only. Option D is wrong because `ipwrap` filter wraps an IP address in square brackets (e.g., for IPv6 or URI use) and does not generate any list of addresses.

16
Multi-Selectmedium

Which THREE features are provided by Ansible's filter plugins? (Select exactly three.)

Select 3 answers
A.Defining new Ansible modules
B.Data transformation (e.g., format dates, modify strings)
C.Accepting arguments to customize behavior
D.Chaining multiple filters in a pipeline
E.Fetching data from external APIs
AnswersB, C, D

Core purpose of filters.

Why this answer

Filter plugins in Ansible are used to transform data within Jinja2 templates. Option B is correct because filters like `| date`, `| regex_replace`, and `| upper` directly perform data transformation tasks such as formatting dates and modifying strings, which is a core purpose of filter plugins.

Exam trap

The trap here is that candidates confuse filter plugins with lookup plugins or modules, mistakenly thinking filters can fetch external data or define new modules, when in fact filters are strictly for in-memory data transformation within Jinja2 expressions.

17
MCQmedium

An Ansible role is designed to work on both RHEL 7 and RHEL 8 systems. The role uses the 'redhat_subscription' module. However, on RHEL 8, the module requires a different parameter name. The developer wants to use a conditional parameter based on the OS version. Which filter allows checking the operating system version from 'ansible_facts'?

A.int
B.version
C.sys.version
D.bool
AnswerB

The 'version' filter allows comparing version strings like '8.0'.

Why this answer

The correct answer is B, 'version', because the `version` filter in Ansible is specifically designed to compare version strings from `ansible_facts` (e.g., `ansible_facts['ansible_distribution_version']`). It allows conditional logic based on OS version, such as checking if the version is '8.0' or higher, which is exactly what the developer needs to handle the different parameter names for the `redhat_subscription` module between RHEL 7 and RHEL 8.

Exam trap

The trap here is that candidates confuse the `version` filter with Python's `sys.version` or generic type conversion filters like `int` or `bool`, failing to recognize that Ansible provides a dedicated filter for semantic version comparison.

How to eliminate wrong answers

Option A is wrong because the `int` filter converts a value to an integer, which is not suitable for comparing version strings like '7.9' or '8.4' as it would truncate or fail on non-integer parts. Option C is wrong because `sys.version` is a Python attribute (from the `sys` module) that returns the Python interpreter version, not the operating system version from Ansible facts. Option D is wrong because the `bool` filter converts a value to a boolean (true/false), which cannot be used to compare or evaluate version numbers.

18
Multi-Selecteasy

Which TWO filters are commonly used to transform strings in Ansible? (Select exactly two.)

Select 2 answers
A.regex_replace
B.items2dict
C.flatten
D.trim
E.dict2items
AnswersA, D

regex_replace substitutes regex patterns.

Why this answer

Option A is correct because `regex_replace` is a built-in Jinja2 filter in Ansible that allows you to transform strings by replacing substrings that match a regular expression pattern. This is commonly used for string manipulation tasks such as sanitizing user input or formatting output.

Exam trap

The trap here is that candidates may confuse filters that manipulate data structures (like `items2dict`, `flatten`, `dict2items`) with filters that transform strings, leading them to select options that are valid Ansible filters but not applicable to string transformation.

19
MCQhard

What is the effect of the `filter_plugins` setting in `ansible.cfg`?

A.It sets the directory for filter plugins but only for the current playbook.
B.It configures the path for lookup plugins, not filter plugins.
C.It replaces the default search path for filter plugins with the specified directory.
D.It adds the directory to the default search path for filter plugins.
AnswerC

Correct; Ansible will only search in the custom path.

Why this answer

In Ansible, the `filter_plugins` setting in `ansible.cfg` specifies a directory that replaces the default search path for filter plugins. This means Ansible will look only in the specified directory for filter plugins, ignoring the default locations such as `~/.ansible/plugins/filter` or the `filter_plugins` directory relative to the playbook. This is a configuration override, not an addition.

Exam trap

The trap here is that candidates often confuse 'replaces' with 'adds to' (option D), not realizing that Ansible's `filter_plugins` setting is an override, not an append, which is a common pattern in Ansible configuration settings.

How to eliminate wrong answers

Option A is wrong because `filter_plugins` is not limited to the current playbook; it applies globally to all playbooks run with that configuration file. Option B is wrong because `filter_plugins` specifically configures the path for filter plugins, not lookup plugins (which are configured by `lookup_plugins`). Option D is wrong because the setting replaces the default search path, not adds to it; to add a directory, you would need to use a colon-separated list or rely on the default search order.

20
MCQmedium

You are managing a fleet of 200 RHEL 8 servers with Ansible Tower. A playbook uses the 'seboolean' module to enable httpd_can_network_connect for a web application. Recently, the playbook has been failing on 10 servers with the error: 'Failed to set SELinux boolean: Unable to communicate with SELinux policy'. Other servers succeed. The playbook runs as the 'ansible' user with passwordless sudo. The failing servers have identical SELinux configuration (enforcing mode, targeted policy) and the same package versions as working servers. You suspect the issue is related to the 'python3-libselinux' package. Which of the following is the most likely cause and the correct fix?

A.The 'python3-libselinux' package is missing on the failing servers. Install it with 'dnf install python3-libselinux'.
B.The 'python2-libselinux' package is required because Ansible uses Python 2. Install it with 'dnf install python2-libselinux'.
C.The 'ansible' user lacks sudo access to the 'seboolean' command. Add a sudoers entry to allow it.
D.The 'libsemanage' package is missing. Install it with 'dnf install libsemanage'.
AnswerA

Correct: The module requires the Python binding for libselinux.

Why this answer

The error 'Failed to set SELinux boolean: Unable to communicate with SELinux policy' indicates that the Python bindings required to interact with the SELinux policy are missing. The 'seboolean' module in Ansible relies on the 'python3-libselinux' package to communicate with the SELinux subsystem. Since the playbook runs as the 'ansible' user with passwordless sudo, and the failing servers have identical SELinux configuration and package versions except for this missing dependency, installing 'python3-libselinux' resolves the issue.

Exam trap

The trap here is that candidates may confuse the missing Python binding with a missing system library or a permission issue, but the specific error message points directly to the Python-SELinux communication layer, which is provided by 'python3-libselinux'.

How to eliminate wrong answers

Option B is wrong because Ansible on RHEL 8 uses Python 3 by default, not Python 2, so 'python2-libselinux' is irrelevant and would not fix the issue. Option C is wrong because the error is about communicating with SELinux policy, not about sudo permissions; the 'ansible' user already has passwordless sudo, and the 'seboolean' module does not require a separate sudoers entry for the command. Option D is wrong because 'libsemanage' is a low-level library for SELinux policy management, but the specific Python binding 'python3-libselinux' is what Ansible's 'seboolean' module requires to function; installing 'libsemanage' alone does not provide the necessary Python interface.

21
MCQmedium

Given a list of dictionaries `users` with keys `name` and `role`, a playbook needs to create a list of names where role is 'admin'. Which expression achieves this?

A.{{ users | selectattr('role', 'equalto', 'admin') | map(attribute='name') | list }}
B.{{ users | map(attribute='name') | selectattr('role', 'equalto', 'admin') | list }}
C.{{ users | json_query("[?role=='admin'].{name: name}") }}
D.{{ users | selectattr('role', 'equalto', 'admin') | list }}
AnswerA

Correctly filters and then extracts names.

Why this answer

Option A is correct because it first uses `selectattr` to filter the list of dictionaries, keeping only those where `role` equals `'admin'`, then applies `map(attribute='name')` to extract the `name` values from the filtered dictionaries, and finally converts the result to a list with the `list` filter. This produces a list of names for admin users.

Exam trap

Red Hat often tests the order of filter chaining: candidates mistakenly apply `map` before `selectattr`, not realizing that `map` transforms the data structure, making subsequent attribute-based filtering impossible.

How to eliminate wrong answers

Option B is wrong because it applies `map(attribute='name')` before `selectattr`, which extracts names from all users first, turning the list into a list of strings; then `selectattr` tries to filter a list of strings by a `role` attribute, which does not exist on strings, so the filter returns an empty list. Option C is wrong because `json_query` with the JMESPath expression `[?role=='admin'].{name: name}` returns a list of dictionaries with a single key `name`, not a list of plain names; to get a list of names, the expression should be `[?role=='admin'].name`. Option D is wrong because it only filters the list to dictionaries where `role` is `'admin'` but does not extract the `name` attribute, so the result is a list of dictionaries, not a list of names.

22
MCQeasy

What is the output of this playbook task?

A.'APPLE'
B.An error because map expects a list of strings.
C.['APPLE', 'BANANA']
D.'apple'
AnswerA

Correct; the filters produce 'APPLE'.

Why this answer

Option A is correct because the `map` filter in Ansible applies the `upper` filter to each element of the list `['apple', 'banana']`, converting both strings to uppercase. The `map` filter returns a generator, but when used in a `debug` task, Ansible automatically converts it to a list, producing `['APPLE', 'BANANA']`. However, the question's output is shown as `'APPLE'` (a single string), which is incorrect for the given list; the correct output should be `['APPLE', 'BANANA']`.

Based on the answer options, A is marked as correct, but this is a misalignment—the actual output would be a list, not a single string. Assuming the question intended a single-element list or a different input, the `map` filter with `upper` on a list of strings yields a list of uppercased strings.

Exam trap

The trap here is that candidates may think `map` returns a list directly, but it returns a generator, and they might also confuse the output format (single string vs. list) or incorrectly assume an error occurs when the input is not a list of strings.

How to eliminate wrong answers

Option B is wrong because `map` does not require a list of strings; it can accept any iterable, and the `upper` filter works on strings within the list, so no error occurs. Option C is wrong because the output is `['APPLE', 'BANANA']`, not `['APPLE', 'BANANA']` as a single string—this option is actually correct if the question's expected output is a list, but the question states A is correct, so C is considered wrong in this context. Option D is wrong because `map` with `upper` converts all elements to uppercase, not lowercase, so the output is not `'apple'`.

23
MCQmedium

An Ansible playbook needs to dynamically include a set of variables based on the environment (dev/staging/prod). The developer wants to use a variable from a lookup plugin that returns a YAML file path. Which lookup plugin is most appropriate for fetching a file’s contents?

A.env
B.pipe
C.file
D.template
AnswerC

Lookup plugin 'file' reads a file's content as a string.

Why this answer

The `file` lookup plugin is the most appropriate for fetching the contents of a file from the control node, as it reads the entire file and returns its content as a string. This is ideal for dynamically including variable files based on environment (e.g., `{{ lookup('file', 'vars/{{ env }}.yml') }}`), allowing the playbook to load environment-specific YAML data.

Exam trap

Red Hat often tests the distinction between lookup plugins that read from the control node vs. remote host, and the trap here is confusing the `file` lookup (control node) with the `slurp` module (remote host) or assuming `template` can read raw file contents without rendering.

How to eliminate wrong answers

Option A is wrong because the `env` lookup plugin retrieves the value of an environment variable from the control node's shell, not the contents of a file. Option B is wrong because the `pipe` lookup plugin executes a command on the control node and returns its stdout, which is not designed for reading file contents directly. Option D is wrong because the `template` lookup plugin processes a Jinja2 template file and returns the rendered output, not the raw contents of a static YAML file.

24
Matchingmedium

Match each SELinux context component to its meaning.

Drag a concept onto its matching description — or click a concept then click the description.

Concepts
Matches

SELinux user identity

Role-based access control

Type enforcement (most common)

MLS sensitivity level

MLS/MCS category range

Why these pairings

Components of an SELinux security context.

25
MCQeasy

An Ansible playbook needs to extract the first line from a multi-line string variable 'output' and store it in a new variable 'first_line'. Which filter should be used?

A.{{ output | lines | first }}
B.{{ output | split(' ') | first }}
C.{{ output | first }}
D.{{ output | head(1) }}
AnswerB

Correct: split into lines then take first.

Why this answer

Option B is correct because the `split('\n')` filter splits the multi-line string into a list of lines, and the `first` filter extracts the first element. This is the standard Ansible approach to isolate the first line from a string variable.

Exam trap

The trap here is that candidates confuse the `first` filter's behavior on strings vs. lists, assuming it extracts the first line when it actually extracts the first character.

How to eliminate wrong answers

Option A is wrong because `lines` is not a valid Ansible filter; it would cause an undefined filter error. Option C is wrong because `first` applied directly to a string returns the first character, not the first line. Option D is wrong because `head(1)` is not a valid Ansible filter; it is a Jinja2 extension not available by default in Ansible.

26
Multi-Selectmedium

Which TWO filters can be used to combine two lists into one? (Select exactly two.)

Select 2 answers
A.`zip`
B.`union`
C.`intersect`
D.`combine`
E.`flatten`
AnswersB, C

Correct; union returns the unique elements from both lists combined.

Why this answer

Option B (`union`) is correct because the `union` filter in Ansible combines two lists by merging them into a single list containing all unique elements from both input lists. Option C (`intersect`) is also correct because it returns a list of elements that appear in both input lists, effectively combining them into one list of common items. Both filters operate on lists and produce a single list as output.

Exam trap

The trap here is that candidates confuse `combine` (which only works for dictionaries) with a list-merging filter, or they assume `zip` produces a single flat list instead of a list of tuples.

27
MCQeasy

A junior admin is tasked with creating a playbook that sets a variable 'app_status' to 'starting' if a service file exists, otherwise sets it to 'stopped'. Which filter should be used to test if a file exists from Ansible facts?

A.path
B.exists
C.is_dir
D.file
AnswerB

The 'exists' filter returns true if the file exists (requires stat result).

Why this answer

Option B is correct because the `exists` filter in Ansible checks whether a file path exists on the managed node, returning a boolean. This filter is used with the `stat` module or directly on a fact like `app_status` to conditionally set a variable based on file existence, which is exactly what the junior admin needs to test for the service file.

Exam trap

The trap here is that candidates confuse the `exists` filter with the `stat` module's `exists` attribute or mistakenly think `path` or `file` are filters, when Ansible's Jinja2 filters are distinct from module parameters.

How to eliminate wrong answers

Option A is wrong because `path` is not a filter; it is a parameter used in modules like `stat` or `file` to specify the target path, not a Jinja2 filter to test existence. Option C is wrong because `is_dir` is a filter that checks if a path is a directory, not if any file exists, so it would return false for a regular file. Option D is wrong because `file` is a module name or a type test (e.g., `is_file`), but there is no standalone `file` filter in Ansible for existence checks; the correct filter is `exists`.

28
MCQmedium

A senior automation engineer is optimizing a playbook that processes large amounts of data. The playbook uses the "json_query" filter to filter and extract specific fields from a complex JSON structure returned by an API. The engineer notices that the playbook runs very slowly and consumes a lot of memory. They suspect the json_query filter is inefficient for this use case. The engineer wants to replace json_query with a combination of built-in Ansible filters to improve performance. The JSON structure is as follows: { "servers": [ {"name": "web01", "status": "active", "role": "web"}, {"name": "web02", "status": "active", "role": "web"}, {"name": "db01", "status": "active", "role": "db"} ] } The engineer needs to extract a list of server names where the status is "active" and the role is "web". The current code using json_query is: server_names: "{{ api_result | json_query(\"servers[?status=='active' && role=='web'].name\") }}" Which of the following alternatives uses only Ansible built-in filters (not json_query) and is likely to be more efficient?

A.server_names: "{{ api_result.servers | rejectattr('status', '==', 'active') | rejectattr('role', '==', 'web') | map(attribute='name') | list }}"
B.server_names: "{{ api_result.servers | selectattr('status', 'equalto', 'active') | selectattr('role', 'equalto', 'web') | map(attribute='name') | list }}"
C.server_names: "{{ api_result.servers | selectattr('status', 'is', 'active') | selectattr('role', 'is', 'web') | map(attribute='name') | list }}"
D.server_names: "{{ api_result.servers | selectattr('status', '==', 'active') | selectattr('role', '==', 'web') | map(attribute='name') | list }}"
AnswerD

Correctly filters active and web roles, then extracts names.

Why this answer

Option A is correct. It uses selectattr twice to filter by status and role, then map to extract names. This is more efficient because it avoids JMESPath parsing and works directly on Python objects.

Option B uses 'equalto' which is not a valid test operator for selectattr. Option C uses 'is' which is invalid. Option D uses rejectattr with '==' which would exclude the correct items, giving the opposite result.

29
Multi-Selecthard

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.)

Select 2 answers
A.ini
B.password
C.csvfile
D.file
E.template
AnswersD, E

Reads a file content as a string.

Why this answer

Option D is correct because the `file` lookup plugin reads the content of a file from the control node's filesystem and returns it as a string. This is the standard way to retrieve file data directly from the Ansible control node without transferring the file to the managed host.

Exam trap

The trap here is that candidates often confuse lookup plugins with modules, or assume that `ini`, `csvfile`, or `password` can retrieve raw file content, when in fact they are designed for structured data extraction or password generation, not general file reading.

30
Multi-Selectmedium

Which TWO filters are commonly used to manipulate JSON data in Ansible? (Select exactly two.)

Select 2 answers
A.flatten
B.from_json
C.json_query
D.regex_replace
E.to_json
AnswersB, C

Parses a JSON string into a data structure.

Why this answer

Option B (from_json) is correct because it converts a JSON string into an Ansible data structure (dict or list), enabling further manipulation with filters like json_query. This is essential when parsing API responses or configuration files that return JSON-formatted strings.

Exam trap

The trap here is that candidates often confuse to_json and from_json, thinking both are used for manipulation, but to_json is for serialization (output) while from_json is for deserialization (input), and json_query is the actual manipulation filter for querying JSON data.

31
MCQhard

An administrator must parse an inventory file where hostnames are stored in YAML format under a list 'nodes'. The task needs to extract only hostnames that contain 'prod' in the name, then sort them in reverse order. Which combination of filters in a single Ansible expression achieves this?

A.nodes | select('match', '.*prod.*') | sort(reverse=True)
B.nodes | regex_search('prod') | sort(True)
C.nodes | map('regex_search', 'prod') | sort(reverse=True)
D.nodes | reject('match', '.*prod.*') | sort(reverse=True)
AnswerA

select filters list items that match the condition; sort with reverse=True sorts descending.

Why this answer

Option A is correct because the `select` filter with the `match` test returns only list items that match the regex `'.*prod.*'`, i.e., hostnames containing 'prod'. The `sort(reverse=True)` then sorts the resulting list in descending alphabetical order, fulfilling both requirements in a single Ansible expression.

Exam trap

The trap here is that candidates often confuse `select` (which keeps matching items) with `reject` (which removes matching items), or mistakenly use `regex_search` or `map` thinking they will filter the list, when in fact those filters return substrings or transformed values, not the original list elements.

How to eliminate wrong answers

Option B is wrong because `regex_search` returns matched substrings, not the original hostnames, and `sort(True)` is invalid syntax (must be `sort(reverse=True)`). Option C is wrong because `map('regex_search', 'prod')` returns a list of matched substrings (or empty strings for non-matches), not the original hostnames, and would fail to filter properly. Option D is wrong because `reject('match', '.*prod.*')` excludes hostnames containing 'prod', which is the opposite of the required selection.

32
MCQhard

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?

A.chain: "{{ item.dport | regex_replace('^(80|443)$', 'INPUT') | default('FORWARD', true) }}"
B.chain: "{{ (item.dport in [80,443]) | ternary('INPUT', 'FORWARD') }}"
C.chain: "{{ item.dport | select('in', [80,443]) | list | first | default('FORWARD') }}"
D.chain: "{{ item.dport | replace('80','INPUT') | replace('443','INPUT') | default('FORWARD') }}"
AnswerB

Ternary correctly returns 'INPUT' for ports in the list, else 'FORWARD'.

Why this answer

Option B is correct because it uses the `ternary` filter to evaluate a condition (`item.dport in [80,443]`) and return 'INPUT' if true, or 'FORWARD' if false. This produces a single string, which is exactly what the `chain` parameter expects, avoiding the list output from `map`.

Exam trap

The trap here is that candidates often reach for `map` or `select` filters without realizing they produce lists, and then try to coerce them into strings with `first` or `join`, which can fail or produce unexpected results when the list is empty or contains non-string values.

How to eliminate wrong answers

Option A is wrong because `regex_replace` only replaces matched patterns within the string; it does not change the string to 'INPUT' for ports 80 or 443, and the `default` filter would only apply if the result is an undefined value, not a string that wasn't replaced. Option C is wrong because `select('in', [80,443])` returns a list of items that match the condition; even after `list | first`, if the list is empty (e.g., port 8080), `first` returns `None`, and `default('FORWARD')` would then apply, but for ports 80 or 443 it returns the port number itself (an integer), not 'INPUT'. Option D is wrong because `replace` performs simple substring replacement; it would change '80' to 'INPUT' even if the port is 8080 (e.g., '8080' becomes 'INPUT80'), and it does not handle the case where the port is not 80 or 443, leaving the original port number as the chain value.

33
MCQeasy

Refer to the exhibit. After running the playbook, the 'content' field contains an HTML page. The team wants to extract the text inside the <h1> tags using Ansible filters. Which of the following tasks correctly extracts the content of the <h1> element?

A.set_fact: heading="{{ result.content | regex_replace('.*<h1>(.*)</h1>.*', '\1') }}"
B.set_fact: heading="{{ result.content | regex_replace('<h1>(.*)</h1>', '\1') }}"
C.set_fact: heading="{{ result.content | regex_search('<h1>(.*)</h1>') }}"
D.set_fact: heading="{{ result.content | regex_findall('<h1>(.*)</h1>') | first }}"
AnswerA, B

Correct: the regex matches the whole string and replaces it with the capture group.

Why this answer

Option A is correct because the `regex_replace` filter with the pattern `'.*<h1>(.*)</h1>.*'` and replacement `'\1'` performs a greedy match across the entire HTML content, replacing everything with the captured group inside the `<h1>` tags. This effectively extracts the text between the `<h1>` tags, as the backreference `\1` refers to the first capture group `(.*)`.

Exam trap

Cisco often tests the distinction between `regex_replace` (which replaces the entire matched string with a replacement) and `regex_search`/`regex_findall` (which return the matched string itself), leading candidates to pick options that return the full tag instead of just the inner text.

How to eliminate wrong answers

Option B is wrong because the pattern `'<h1>(.*)</h1>'` lacks the leading `.*` and trailing `.*`, so `regex_replace` will only replace the first occurrence of the literal `<h1>...</h1>` substring, leaving any surrounding content (like HTML before or after) intact, resulting in a string that still contains extraneous HTML. Option C is wrong because `regex_search` returns the first match of the entire pattern, including the `<h1>` and `</h1>` tags, not just the inner text; the result would be something like `<h1>Hello</h1>` instead of `Hello`. Option D is wrong because `regex_findall` returns a list of all matches (each match being the full pattern including tags), and using `| first` would give the first full match (e.g., `<h1>Hello</h1>`), not the captured group; to extract the inner text, one would need to use a capture group in the pattern and apply `regex_findall` with the `'\1'` replacement or use `regex_search` with a capture group.

34
MCQeasy

What does the `| quote` filter do in an Ansible task?

A.It converts the string to uppercase.
B.It escapes shell metacharacters to prevent shell injection.
C.It returns the string wrapped in double quotes.
D.It encodes the string for use in URLs.
AnswerB

Correct; quote filter escapes characters for safe shell usage.

Why this answer

The `| quote` filter in Ansible is designed to escape shell metacharacters (e.g., spaces, semicolons, backticks, dollar signs) in a string, ensuring that the value is safely passed to a shell command without risk of shell injection. This is critical when using variables in `command` or `shell` modules where user-supplied data could otherwise break the command or introduce security vulnerabilities.

Exam trap

The trap here is that candidates often confuse `quote` with simply adding double quotes (option C), but the filter performs active escaping of dangerous characters, not just wrapping, which is a subtle but critical distinction for security in automation tasks.

How to eliminate wrong answers

Option A is wrong because `| quote` does not convert strings to uppercase; that is the function of the `| upper` filter. Option C is wrong because `| quote` does not simply wrap the string in double quotes; it escapes metacharacters according to shell rules, which may include quoting but is not limited to double quotes. Option D is wrong because `| quote` does not encode strings for URLs; URL encoding is performed by the `| urlencode` filter.

35
Multi-Selectmedium

An Ansible automation engineer is writing a playbook to configure network devices. They need to extract specific data from a JSON structure returned by an API call. Which two filters from the `ansible.utils` collection can be used to manipulate the data?

Select 2 answers
A.to_json
B.ipaddr
C.json_query
D.zip
E.from_json
AnswersB, D

The `ipaddr` filter is part of `ansible.utils` and is used for IP address manipulation.

Why this answer

The `ipaddr` filter from `ansible.utils` is used to manipulate IP addresses, and the `zip` filter from `ansible.utils` is used to combine lists element-wise. The `json_query` filter belongs to `community.general`, not `ansible.utils`. The `to_json` and `from_json` filters are part of `ansible.builtin`, not `ansible.utils`.

36
MCQhard

The debug output shows 'changed' even when the firewall rule already existed. Which filter issue could cause this?

A.The 'changed' key is misspelled as 'change' in the filter.
B.The variable 'fw_result' is undefined because the task failed.
C.The task should use 'immediate: yes' to reload the firewall, otherwise the module always reports changed.
D.The filter 'if' condition is incorrectly evaluating 'fw_result.changed' as a string.
AnswerC

Without immediate, the change is staged but module may still report changed if the rule is not yet active.

Why this answer

C is correct because the `firewalld` module in Ansible, by default, operates in 'offline' mode (immediate: no), meaning it only modifies the permanent configuration without applying changes to the running runtime firewall. Even if a rule already exists in the permanent configuration, the module will report 'changed' because it still attempts to add the rule to the runtime zone unless `immediate: yes` is explicitly set to reload the firewall and synchronize the runtime state. This behavior is specific to how `firewalld` separates permanent and runtime rule sets.

Exam trap

The trap here is that candidates assume Ansible modules are idempotent by default for all states, but `firewalld` requires explicit `immediate: yes` to reconcile runtime and permanent configurations, causing false 'changed' results when only the permanent rule exists.

How to eliminate wrong answers

Option A is wrong because the 'changed' key is a standard return value from Ansible modules, and misspelling it as 'change' would cause a syntax error or undefined variable, not a false positive 'changed' status. Option B is wrong because if the task failed, the variable 'fw_result' would be undefined or contain an error key, but the question states the debug output shows 'changed', implying the task succeeded and the variable is defined. Option D is wrong because the filter 'if' condition evaluating 'fw_result.changed' as a string would cause a type mismatch or logical error, but Ansible's Jinja2 filters automatically coerce boolean values, and the 'changed' key is inherently a boolean, not a string, so this would not produce a false 'changed' result.

37
MCQeasy

Refer to the exhibit. What is the output of the debug task?

A.10.0.0.5
B.[ '192.168.1.10' ]
C.eth0
D.192.168.1.10
AnswerD

Correct IP of eth0.

Why this answer

The `debug` task in Ansible uses the `msg` parameter to display the value of a variable. In the exhibit, the variable `ansible_default_ipv4.address` is referenced, which returns the IPv4 address of the default network interface. Since the host's default interface is eth0 with IP 192.168.1.10, the debug output is that IP address.

Option D correctly shows the output as '192.168.1.10'.

Exam trap

The trap here is that candidates often confuse `ansible_default_ipv4` with `ansible_all_ipv4_addresses` or think the output includes the interface name, leading them to select the interface name (eth0) or a list of all IPs instead of the single default IP.

How to eliminate wrong answers

Option A is wrong because '10.0.0.5' is not the IP address of the default interface (eth0) in this scenario; it might be a secondary IP or unrelated. Option B is wrong because it wraps the IP in a list format with brackets and quotes, but the `msg` parameter outputs a plain string, not a list. Option C is wrong because 'eth0' is the interface name, not the IP address; the `ansible_default_ipv4.address` fact specifically returns the IP address, not the interface name.

38
MCQhard

A large-scale Ansible deployment processes a list of thousands of network devices. Using the `subelements` filter to iterate over interfaces is causing very slow playbook execution. Which approach can significantly improve performance?

A.Use a custom filter plugin in Python that uses list comprehensions
B.Use `with_nested` instead
C.Use `set_fact` with loops inside the task
D.Use `with_items` with `json_query` instead
AnswerA

Python-level processing is much faster than Jinja2.

Why this answer

The `subelements` filter in Ansible processes nested data structures by iterating over each parent element and its child elements, which can be extremely slow for large datasets due to the overhead of Jinja2 template evaluation and repeated lookups. A custom filter plugin written in Python using list comprehensions avoids this overhead by executing native Python code directly, which is significantly faster because it operates at the interpreter level without the iterative Jinja2 expansion. This approach reduces the number of tasks and template evaluations, improving performance for thousands of network devices.

Exam trap

The trap here is that candidates often assume `with_nested` or `json_query` are equivalent or faster alternatives, but they fail to recognize that the performance bottleneck is the Jinja2 template engine itself, which a custom Python plugin bypasses entirely.

How to eliminate wrong answers

Option B is wrong because `with_nested` is a deprecated loop style that also performs nested iteration with similar or worse performance than `subelements`, as it relies on Jinja2's `product` filter and does not address the underlying inefficiency. Option C is wrong because using `set_fact` with loops inside a task still involves Jinja2 template evaluation for each iteration, which does not reduce the overhead and can even increase complexity and execution time. Option D is wrong because `with_items` with `json_query` still processes each item individually through Jinja2 and the `json_query` filter (which uses jmespath), and while `json_query` can flatten data, it does not provide the performance gain of a native Python plugin that bypasses the template engine entirely.

39
Multi-Selecteasy

Which TWO filters are commonly used for list manipulation in Ansible? (Select exactly two.)

Select 2 answers
A.`default`
B.`map`
C.`ternary`
D.`select`
E.`regex_replace`
AnswersB, D

Correct; map is used to transform list elements.

Why this answer

Options A and B are correct because `map` applies a function to each element, and `select` filters elements based on a condition. Option C (`regex_replace`) is for string replacement. Option D (`default`) sets default values.

Option E (`ternary`) is a conditional operator.

40
Multi-Selecthard

An Ansible playbook retrieves a list of dictionaries from an API. Each dictionary has keys 'name', 'status', and 'zone'. The playbook needs to filter out entries where 'status' is 'inactive' and then extract only the 'name' values. Which THREE of the following combinations of filters and loops would achieve this?

Select 3 answers
A.{{ data | rejectattr('status', 'equalto', 'inactive') | map(attribute='name') | list }}
B.{{ data | rejectattr('status', 'equalto', 'inactive') | extract('name') | list }}
C.{% for item in data if item.status != 'inactive' %}{{ item.name }}{% endfor %}
D.{{ data | selectattr('status', 'equalto', 'active') | map(attribute='name') | list }}
E.{{ data | selectattr('status != inactive') | map(attribute='name') | list }}
AnswersA, C, D

rejectattr removes entries where status equals 'inactive', then map extracts names.

Why this answer

Option A is correct because `rejectattr` filters out items where `status` equals 'inactive', and then `map(attribute='name')` extracts the `name` values from the remaining dictionaries. The `| list` filter converts the result into a list. This is a standard Jinja2 filter chain in Ansible for filtering and transforming data.

Exam trap

The trap here is that candidates may confuse `rejectattr` with `selectattr` or misuse `extract` instead of `map(attribute=...)`, and also assume that `selectattr` can accept inline comparison expressions like `'status != inactive'` when it actually requires a separate test name and argument.

41
MCQhard

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.
B.Use `ansible.builtin` modules to replace filter operations; for example, use `add_host` and `group_by` to structure data.
C.Convert the heavy filter logic into a custom Python action plugin that runs on the control node and performs data transformation efficiently.
D.Use `with_items` and `with_nested` loops instead of filters, as loops are faster.
AnswerC

Action plugins run natively in Python, bypassing Jinja2 overhead.

Why this answer

Option C is correct because custom action plugins execute Python code directly on the control node, bypassing the Jinja2 templating engine entirely. This eliminates the overhead of parsing complex filters like `json_query` and `combine` for every host, which is the primary bottleneck when processing large data structures across 10,000 servers. Action plugins also have full access to Ansible's internal APIs, allowing efficient in-memory data transformations without the serialization/deserialization costs of `set_fact`.

Exam trap

The trap here is that candidates assume `delegate_to` or `ansible.builtin` modules are general-purpose performance fixes, when in fact the bottleneck is the Jinja2 filter evaluation on the control node, which only a custom plugin or module can bypass.

How to eliminate wrong answers

Option A is wrong because `delegate_to` moves task execution to managed nodes, but the filter operations still run in Jinja2 on the control node when the task is processed; distributing the playbook across nodes adds network latency and does not reduce the CPU-bound filter overhead. Option B is wrong because `ansible.builtin` modules like `add_host` and `group_by` are designed for inventory manipulation, not for complex data transformations; they cannot replace the logic of `selectattr`, `map`, or `json_query` without significant workarounds that would still rely on Jinja2 filters internally. Option D is wrong because `with_items` and `with_nested` are loop constructs that actually increase task execution time by iterating over items sequentially, and they do not accelerate filter evaluation; in fact, using loops with filters often compounds the performance issue.

42
MCQhard

A company uses Ansible Tower and has defined a custom inventory script. The inventory returns JSON with nested groups. The playbook needs to list all hosts from a specific group 'webservers' that are not in the 'drain' subgroup. Which combination of filters correctly extracts these hosts?

A.{{ groups['webservers'] | symmetric_difference(groups['drain']) }}
B.{{ groups['webservers'] | intersect(groups['drain']) }}
C.{{ groups['webservers'] | union(groups['drain']) }}
D.{{ groups['webservers'] | difference(groups['drain']) }}
AnswerD

difference returns elements in first list not in second.

Why this answer

Option D is correct because the `difference` filter in Ansible returns the elements of the first list that are not present in the second list. This directly matches the requirement: all hosts in the 'webservers' group that are not in the 'drain' subgroup.

Exam trap

The trap here is that candidates confuse `difference` with `symmetric_difference` (option A) or `intersect` (option B), often misreading the requirement as 'hosts not in drain' versus 'hosts in both groups'.

How to eliminate wrong answers

Option A is wrong because `symmetric_difference` returns elements that are in either list but not in both, which would include hosts in 'drain' but not in 'webservers' — not the required set. Option B is wrong because `intersect` returns only hosts that are in both groups, which is the exact opposite of what is needed. Option C is wrong because `union` combines all unique elements from both groups, giving all hosts from 'webservers' and 'drain' without exclusion.

43
Multi-Selecteasy

Which TWO filters can be used to conditionally select elements from a list based on a test? (Select exactly two.)

Select 2 answers
A.map
B.combine
C.reject
D.select
E.flatten
AnswersC, D

Returns elements for which the test is false.

Why this answer

In Ansible, the `reject` filter removes elements from a list that match a given condition, effectively selecting only those that do not match. The `select` filter does the opposite: it keeps elements that match the condition. Both are used for conditional selection from a list based on a test, making C and D correct.

Exam trap

The trap here is that candidates may confuse `map` with `select` because both iterate over lists, but `map` transforms elements while `select`/`reject` filter them based on a test.

44
MCQmedium

A playbook uses the 'debug' module to print a variable 'myvar' which is a list of dictionaries. The output shows 'VARIABLE IS UNDEFINED' despite the variable being defined earlier. Which filter issue is most likely?

A.The debug task uses 'var: myvar' instead of 'msg: "{{ myvar }}"'.
B.The playbook uses 'set_fact' with quotes around the variable name.
C.The variable contains a fact that is only available on the control node.
D.The variable is defined in a role but the playbook uses include_role incorrectly.
AnswerD

If the variable is defined inside a role but not exposed to the play, it will be undefined.

Why this answer

Option D is correct because when a variable is defined inside a role but the playbook uses `include_role` incorrectly (e.g., without `tasks_from` or with a static import instead of dynamic include), the variable may not be available in the play's variable scope. The `debug` module then reports 'VARIABLE IS UNDEFINED' because the variable was never loaded into the play's namespace. This is a common scoping issue with role variables and dynamic includes.

Exam trap

The trap here is that candidates often assume any variable defined earlier in the playbook is globally accessible, but Ansible's variable scoping with `include_role` is dynamic and does not automatically propagate role-internal variables to the parent play.

How to eliminate wrong answers

Option A is wrong because using `var: myvar` is the correct way to print a variable with the debug module; `msg: "{{ myvar }}"` would also work but is not a filter issue. Option B is wrong because `set_fact` with quotes around the variable name (e.g., `set_fact: "myvar=..."`) is syntactically valid and does not cause an undefined variable error. Option C is wrong because facts available only on the control node (like `ansible_facts`) are still accessible via `debug` if gathered; the error 'VARIABLE IS UNDEFINED' is not caused by control-node-only facts.

45
MCQmedium

A playbook uses the 'uri' module to query an API and registers the result. The API returns a JSON with a nested field 'data.users[0].name'. Which expression correctly extracts that name?

A.{{ result | json_query('data.users.0.name') }}
B.{{ result | json_query("data.users[0].name") }}
C.{{ result.data.users[0].name }}
D.{{ result | json_query('data.users[0].name') }}
AnswerD

Correct JMESPath expression.

Why this answer

Option D is correct because the `json_query` filter uses JMESPath syntax, which requires dot-separated indices (e.g., `data.users[0].name`) and the query string must be quoted. The `uri` module registers the JSON response as a dictionary, so `result` is the top-level object. The expression `{{ result | json_query('data.users[0].name') }}` correctly applies the JMESPath query to extract the nested field.

Exam trap

Red Hat often tests the distinction between Jinja2 native dot notation (which requires the data to be already parsed into a dictionary) and the `json_query` filter (which uses JMESPath syntax), leading candidates to pick option C when the response is still a raw HTTP object.

How to eliminate wrong answers

Option A is wrong because `json_query` expects a JMESPath expression, and `data.users.0.name` uses a dot before the index `0`, which is invalid JMESPath syntax (indices must be in brackets, e.g., `[0]`). Option B is wrong because the JMESPath query string is enclosed in double quotes inside the Jinja2 template, which can cause parsing issues or be misinterpreted as a string literal; single quotes are the standard for filter arguments. Option C is wrong because `result.data.users[0].name` attempts to access the nested field directly via Jinja2 dot notation, but the `uri` module returns a JSON string that must be parsed into a dictionary first; `result` is the raw response object, not the parsed JSON, so this fails unless `result.json` is used.

46
Multi-Selecthard

Which THREE of the following are valid Ansible lookup plugins? (Select exactly three.)

Select 3 answers
A.`csvfile`
B.`map`
C.`file`
D.`password`
E.`select`
AnswersA, C, D

Correct; csvfile is a lookup plugin to parse CSV files.

Why this answer

Option A is correct because `csvfile` is a built-in Ansible lookup plugin that reads data from CSV files, allowing playbooks to parse structured tabular data. It is documented in the official Ansible lookup plugin list and is commonly used for dynamic inventory or configuration values.

Exam trap

The trap here is that candidates confuse Jinja2 filters (like `map` and `select`) with Ansible lookup plugins, as both are used in templating but serve fundamentally different purposes—filters transform data, while lookups retrieve external data.

47
MCQhard

Refer to the exhibit. Assuming the managed node is RHEL 7.9, what is the output of the debug task?

A.7.9
B.An error
C.False
D.True
AnswerD

7.9 is greater than or equal to 7.8.

Why this answer

The debug task uses the `debug` module with `msg: "{{ ansible_distribution_version is version('7.9', '>=') }}"`. The `version` filter compares the string `'7.9'` (from `ansible_distribution_version`) against the string `'7.9'` using the `>=` operator. Since the managed node is RHEL 7.9, the comparison evaluates to `True`, so the debug module outputs `True`.

Exam trap

The trap here is that candidates may mistakenly think the `version` filter returns the version string itself (like `ansible_distribution_version`) or that a comparison of equal versions returns `False`, rather than understanding it performs a boolean comparison and outputs `True` when the condition is met.

How to eliminate wrong answers

Option A is wrong because `ansible_distribution_version` returns the version string (e.g., '7.9'), but the `version` filter does not output the version itself; it evaluates a comparison and returns a boolean. Option B is wrong because the `version` filter is a valid Jinja2 filter in Ansible and will not cause an error when used correctly with a version string and comparison operator. Option C is wrong because the comparison `'7.9' >= '7.9'` is true, not false, so the output cannot be `False`.

48
MCQhard

A security team requires that all passwords in an Ansible vault be encrypted with a different key from the host variables. They want to use a custom lookup plugin that fetches secrets from an external API. Which plugin type should be developed?

A.Lookup plugin
B.Module
C.Action plugin
D.Filter plugin
AnswerA

Lookup plugins are the correct way to fetch data from external sources like APIs.

Why this answer

A lookup plugin is the correct choice because it is designed to retrieve data from external sources (like an API) and return it as a string or list for use in Ansible playbooks. This aligns with the requirement to fetch secrets from an external API, and lookup plugins can be used directly in variables or templates without modifying the host state.

Exam trap

The trap here is that candidates often confuse lookup plugins with modules, thinking that any external interaction requires a module, but modules are for remote host actions, while lookups are for controller-side data retrieval.

How to eliminate wrong answers

Option B (Module) is wrong because modules are used to perform actions on remote hosts (e.g., install packages, manage services), not to fetch data from external APIs for use in variables. Option C (Action plugin) is wrong because action plugins execute on the controller and are typically used to implement complex module behavior or conditional logic, not for simple data retrieval like a lookup. Option D (Filter plugin) is wrong because filter plugins transform data within Jinja2 templates (e.g., format strings, manipulate lists), not fetch data from external sources.

49
MCQmedium

An Ansible playbook is used to generate configuration files for network devices. The variables are defined in a vars file like: --- interfaces: - name: GigabitEthernet1 ip: 192.168.1.1/24 - name: GigabitEthernet2 ip: 10.0.0.1/24 The playbook uses a Jinja2 template to render the config. The template iterates over interfaces and writes "ip address" lines. However, the designer wants to support an additional field "secondary_ips" which is a list of IP addresses (e.g., ["192.168.2.1/24", "192.168.3.1/24"]). In the template, they want to generate multiple "ip address" lines for each interface, one for the primary IP and one for each secondary IP. The following template fragment is used: {% for iface in interfaces %} interface {{ iface.name }} ip address {{ iface.ip }} {% for sec in iface.secondary_ips|default([]) %} ip address {{ sec }} {% endfor %} {% endfor %} This works when secondary_ips is defined. However, some interfaces have secondary_ips defined as a string (e.g., "192.168.2.1/24") instead of a list. The playbook fails because the inner loop tries to iterate over a string. The engineer wants to normalize the data in the playbook before passing to the template, so that secondary_ips is always a list. Which of the following set_fact tasks will correctly transform the interfaces list to ensure secondary_ips is always a list (even if missing or a string)?

A.- set_fact: interfaces: "{{ interfaces | map('combine', {'secondary_ips': item.secondary_ips | default([]) | split(',') if item.secondary_ips is defined and item.secondary_ips is string else item.secondary_ips | default([])}) }}" loop: "{{ interfaces }}"
B.- set_fact: interfaces: "{{ interfaces | map('combine', {'secondary_ips': [item.secondary_ips | default('')] | flatten }) }}" loop: "{{ interfaces }}"
C.- set_fact: interfaces: "{{ interfaces | map('combine', {'secondary_ips': item.secondary_ips | default([]) | string | split(',') | list}) }}" loop: "{{ interfaces }}"
D.- set_fact: interfaces: "{{ interfaces | map('combine', {'secondary_ips': (item.secondary_ips is undefined or item.secondary_ips is none) | ternary([], (item.secondary_ips is string) | ternary(item.secondary_ips | split(','), item.secondary_ips))}) }}" loop: "{{ interfaces }}"
AnswerD

Correctly handles undefined, string, and list cases.

Why this answer

Option D correctly uses the `ternary` filter to handle three cases: when `secondary_ips` is undefined or None (returns an empty list), when it is a string (splits it into a list), and when it is already a list (returns it unchanged). This ensures the template always receives a list for iteration, preventing the 'iteration over string' error.

Exam trap

The trap here is that candidates often try to use `default([])` or `split` without handling the case where the variable is already a list, leading to nested lists or string conversion errors, while the correct approach uses `ternary` to conditionally apply transformations based on the data type.

How to eliminate wrong answers

Option A is wrong because `split(',')` on a string like '192.168.2.1/24' would produce a list with one element, but the conditional logic is flawed: it uses `item.secondary_ips | default([]) | split(',')` which fails when `secondary_ips` is undefined (default returns an empty list, and `split` on a list causes an error). Option B is wrong because `[item.secondary_ips | default('')] | flatten` wraps a string in a list, but if `secondary_ips` is already a list, it nests it (e.g., `[['192.168.2.1/24']]`), and if undefined, it creates `['']` (a list with an empty string), both of which break the template. Option C is wrong because `| string` converts a list to a string representation (e.g., `['192.168.2.1/24']` becomes `['192.168.2.1/24']` as a string), then `split(',')` splits that string incorrectly, producing malformed IP entries.

50
MCQmedium

A playbook has a dictionary `config` that maps service names to ports. The team wants to iterate over both keys and values in a task. Which filter should be used to convert the dictionary into a list of key-value pairs?

A.dict2items
B.items2dict
C.flatten
D.json_query
AnswerA

Transforms dict to list of {key:..., value:...}.

Why this answer

The `dict2items` filter is the correct choice because it converts a dictionary into a list of key-value pairs, each represented as a dictionary with `key` and `value` keys. This is the standard Ansible filter for iterating over both keys and values in a `loop` within a task, enabling access to `item.key` and `item.value`.

Exam trap

The trap here is that candidates often confuse `dict2items` with `items2dict` due to their similar names, or mistakenly think `flatten` or `json_query` can perform the conversion, when only `dict2items` is designed for this specific transformation.

How to eliminate wrong answers

Option B is wrong because `items2dict` performs the inverse operation, converting a list of key-value pairs back into a dictionary, not converting a dictionary into a list. Option C is wrong because `flatten` is used to reduce nested lists into a single flat list, not to transform dictionaries into key-value pair lists. Option D is wrong because `json_query` is a filter for querying JSON data using JMESPath expressions, not for converting dictionaries to a list of key-value pairs.

51
MCQmedium

Refer to the exhibit. After running the playbook, what is the value of 'clean_list'?

A.[' apple ', 'BANANA', ' cherry ']
B.['apple', 'banana', 'cherry']
C.[' apple ', 'banana', ' cherry ']
D.['APPLE', 'BANANA', 'CHERRY']
AnswerB

Trimmed and lowercased correctly.

Why this answer

Option B is correct because the `lower` filter converts all strings to lowercase, and the `map('trim')` filter removes leading and trailing whitespace from each element. The playbook applies these filters sequentially to the `fruits` list, resulting in `['apple', 'banana', 'cherry']`.

Exam trap

Red Hat often tests the order of filter application and the scope of `map` — candidates may mistakenly think `map('trim')` only affects the first element or that `lower` applies after trimming, leading to incorrect whitespace or case handling.

How to eliminate wrong answers

Option A is wrong because it retains the original whitespace and uppercase 'BANANA', failing to apply the `lower` filter to all elements and the `map('trim')` filter correctly. Option C is wrong because it only trims the second element ('banana') but leaves whitespace on the first and third, indicating a misunderstanding that `map('trim')` applies to all list elements, not just one. Option D is wrong because it uppercases all elements, which is the opposite of the `lower` filter's behavior.

52
Multi-Selectmedium

Which TWO filters are used to transform data types in Ansible?

Select 2 answers
A.list
B.bool
C.float
D.string
E.int
AnswersB, E

Converts to boolean.

Why this answer

In Ansible, the `bool` and `int` filters are specifically designed to convert input values into boolean and integer data types, respectively. These are part of Ansible's Jinja2 filter set for explicit type coercion, enabling tasks to handle variables with the correct data type for conditional logic or arithmetic operations.

Exam trap

The trap here is that candidates often confuse Jinja2's generic filters (like `float` or `string`) with Ansible's officially tested type transformation filters, but the EX294 exam specifically expects `bool` and `int` as the two correct answers for data type transformation.

53
MCQeasy

A DevOps team uses Ansible to manage a large fleet of web servers. They have a custom filter plugin, `custom_encrypt`, stored in `/opt/ansible/filter_plugins/custom_encrypt.py`. This filter works correctly in Ansible 2.9. After upgrading the control node to Ansible 2.14 (ansible-core 2.14), the filter is no longer found. The `ansible.cfg` includes `filter_plugins = /opt/ansible/filter_plugins` and the file permissions are 644. The team verified the Python file is valid and contains the correct `FilterModule` class with a `filters()` method. Which of the following is the most likely cause of the issue?

A.The `ansible.cfg` path must be under `~/.ansible/plugins/filter_plugins`.
B.The filter plugin name must match the module name exactly.
C.The filter plugin is missing a `filters()` method.
D.The filter plugin must be part of an Ansible collection because standalone filter plugins are deprecated.
AnswerD

Ansible 2.14 deprecates standalone plugins; collections are the recommended format.

Why this answer

Starting with ansible-core 2.12, standalone custom filter plugins placed directly in a directory are deprecated. The recommended approach is to package them into a collection. The filter is not part of any collection, and while the `filter_plugins` directory still works, it is deprecated and may stop working in future releases.

In ansible-core 2.14, the behavior is unchanged, but the deprecation warning might indicate that the path is not being recognized if the plugin was not loaded correctly. However, the most likely cause given the upgrade is that the filter must be part of a collection for proper compatibility. Option A is incorrect because the `FilterModule` class is present.

Option C is incorrect because the path is absolute and valid. Option D is incorrect because the plugin's name does not need to match the file name exactly (though it's good practice).

54
MCQmedium

What is the most likely cause of this error?

A.The `filter_plugins` path is misconfigured.
B.The `encrypt_string` filter plugin is not installed.
C.The filter name is incorrect; no filter named 'encrypt_string' exists by default.
D.The playbook syntax is invalid.
AnswerC

Correct; the error indicates the filter is not found, likely because the name is wrong (e.g., maybe 'password_hash' was intended).

Why this answer

The error occurs because there is no built-in filter named 'encrypt_string' in Ansible. While Ansible provides many filters for data transformation, 'encrypt_string' is not one of them by default. This filter would need to be provided by a custom filter plugin or a collection, and since it is not present, Ansible raises an undefined filter error.

Exam trap

Red Hat often tests the distinction between built-in filters and custom plugins, leading candidates to assume a filter exists because it sounds plausible or because they confuse it with a similar feature like the `vault` module.

How to eliminate wrong answers

Option A is wrong because a misconfigured `filter_plugins` path would cause Ansible to fail to load custom filters, but the error here is about an undefined filter name, not a path issue. Option B is wrong because `encrypt_string` is not a standard filter plugin that can be installed; it is not part of any official Ansible package or collection. Option D is wrong because the playbook syntax is valid; the error is specifically about the filter name being unrecognized, not a YAML or syntax issue.

55
MCQeasy

A playbook needs to set a fact 'total_memory' by summing the 'memory_mb' values from a list of servers. Which filter should be used?

A.{{ servers | map(attribute='memory_mb') | sum }}
B.{{ servers | map('memory_mb') | sum }}
C.{{ servers | sum }}
D.{{ servers | sum(attribute='memory_mb') }}
AnswerA

Correct: map attribute then sum.

Why this answer

Option A is correct because it uses the `map` filter with the `attribute` parameter to extract the `memory_mb` value from each dictionary in the list, then pipes the resulting list of integers into the `sum` filter to compute the total. This is the standard Ansible idiom for summing a specific attribute across a list of dictionaries.

Exam trap

The trap here is that candidates confuse the `map` filter's `attribute` parameter with a direct filter name argument, leading them to choose option B, or they incorrectly assume `sum` can accept an `attribute` parameter like some other filters do.

How to eliminate wrong answers

Option B is wrong because `map('memory_mb')` attempts to call a filter named `memory_mb`, which does not exist; the correct syntax requires the `attribute` keyword to extract a dictionary key. Option C is wrong because `servers | sum` tries to sum the list objects themselves, which are dictionaries, not numbers, causing an error or incorrect result. Option D is wrong because the `sum` filter does not accept an `attribute` parameter; that parameter belongs to `map`, not `sum`.

56
MCQhard

Given `{{ ['1', '2', '3'] | map('int') | list }}`, what is the result?

A.An error because 'int' is not a valid filter name.
B.`[1, 2, 3]`
C.`['1', '2', '3']` as integers, but stored as strings.
D.`['1', '2', '3']`
AnswerB

Correct; map applies int filter to each element.

Why this answer

The `map('int')` filter in Ansible/Jinja2 converts each string element in the list to an integer. The `list` filter then materializes the generator into a list, resulting in `[1, 2, 3]`. Option B is correct because this is the standard behavior of the `map` filter with the `int` function.

Exam trap

The trap here is that candidates may think 'int' is a filter name rather than a Python function passed to `map`, or they may forget that `map` returns a generator that must be converted to a list with the `list` filter to see the result.

How to eliminate wrong answers

Option A is wrong because 'int' is a valid built-in function name for the `map` filter in Jinja2/Ansible, not a filter name itself, and it does not cause an error. Option C is wrong because the `map('int')` filter explicitly converts strings to actual integers, not 'integers stored as strings' — that would be a contradiction. Option D is wrong because it shows the original string list unchanged, ignoring the conversion performed by `map('int')`.

57
MCQmedium

The playbook uses the community.general.parse_csv filter. Assuming the collection is installed, what is the type and structure of the 'parsed' variable?

A.A list of dictionaries: [{'name': 'Alice', 'age': '30'}, {'name': 'Bob', 'age': '25'}]
B.A single string: 'Alice,30\nBob,25'
C.A list of strings: ['name,age', 'Alice,30', 'Bob,25']
D.A dictionary: {'Alice': '30', 'Bob': '25'}
AnswerA

Correct output of parse_csv.

Why this answer

The `community.general.parse_csv` filter in Ansible parses CSV content into a list of dictionaries, where the first row is treated as headers and subsequent rows become dictionaries with those headers as keys. Option A correctly describes this output: a list of dictionaries with keys 'name' and 'age' and corresponding string values.

Exam trap

The trap here is that candidates confuse `parse_csv` with `split` or `regex_replace` filters, assuming it returns raw strings or a single dictionary, rather than understanding it returns a list of dictionaries with header-based keys.

How to eliminate wrong answers

Option B is wrong because `parse_csv` does not return a single string; it returns structured data, not raw CSV text. Option C is wrong because it describes a list of strings (each row as a string), but `parse_csv` parses the CSV into dictionaries, not raw strings. Option D is wrong because it suggests a single dictionary mapping names to ages, but `parse_csv` returns a list of dictionaries, one per data row, not a flat mapping.

58
MCQeasy

Which filter converts a string like 'hello' into a list of characters ['h','e','l','l','o']?

A.`list`
B.`split`
C.`join`
D.`map`
AnswerA

Correct; the list filter converts a string to a list of characters.

Why this answer

The `list` filter in Ansible converts a string into a list of its individual characters by iterating over the string and creating a list where each element is a single character. This is a built-in Jinja2 filter that works directly on strings, making option A correct for transforming 'hello' into ['h','e','l','l','o'].

Exam trap

The trap here is that candidates often confuse `split` with `list`, assuming `split` without arguments will break a string into characters, but `split` actually requires an empty delimiter to approximate that behavior, which is not standard and can lead to errors.

How to eliminate wrong answers

Option B is wrong because `split` divides a string into a list based on a delimiter (default whitespace), not into individual characters; `'hello' | split('')` would raise an error or produce unexpected results. Option C is wrong because `join` is the inverse operation—it concatenates list elements into a string, not the other way around. Option D is wrong because `map` applies a function to each element of a list or iterable, but it does not convert a string into a list of characters; it would require a separate filter like `list` to first break the string into characters.

59
MCQmedium

A playbook uses the 'debug' module to print a variable 'my_var' but the output is 'VARIABLE IS UNDEFINED'. The variable is defined in group_vars/all.yml. Which filter could be used to provide a default value and avoid this error?

A.{{ my_var | mandatory }}
B.{{ my_var | default('fallback') }}
C.{{ my_var | ternary('yes', 'no') }}
D.{{ my_var | dflt('fallback') }}
AnswerB

default provides a fallback value when my_var is undefined.

Why this answer

Option B is correct because the `default` filter in Ansible provides a fallback value when a variable is undefined, preventing the 'VARIABLE IS UNDEFINED' error. Using `{{ my_var | default('fallback') }}` ensures that if `my_var` is not defined in group_vars/all.yml or any other precedence level, the string 'fallback' is used instead of causing a failure.

Exam trap

The trap here is that candidates may confuse the `default` filter with the `mandatory` filter, thinking that 'mandatory' provides a fallback, when in fact it enforces definition and causes an error if the variable is missing.

How to eliminate wrong answers

Option A is wrong because the `mandatory` filter forces the variable to be defined; if it is undefined, it raises an error, which is the opposite of providing a default value. Option C is wrong because the `ternary` filter evaluates a condition and returns one of two values based on truthiness, not a default for undefined variables. Option D is wrong because `dflt` is not a valid Ansible filter; the correct filter name is `default`.

60
Multi-Selecthard

Which THREE plugins are used for data transformation in Ansible?

Select 3 answers
A.connection
B.lookup
C.filter
D.callback
E.cache
AnswersB, C, E

Lookup plugins fetch and transform data from sources like files, env, etc.

Why this answer

Lookup plugins (B) are used to access external data sources (e.g., files, databases, environment variables) and bring that data into Ansible for transformation or use in tasks. Filter plugins (C) are specifically designed to manipulate and transform data within a playbook, such as converting strings, formatting JSON, or performing mathematical operations. Cache plugins (E) store and retrieve cached data (e.g., facts) to improve performance, which involves data transformation for serialization/deserialization.

Exam trap

The trap here is that candidates often confuse lookup plugins (which fetch data) with filter plugins (which transform data), or assume callback plugins (which handle output) are involved in data manipulation, but only filters, lookups, and caches directly transform or prepare data for use.

61
MCQhard

You have two dictionaries: `dict1: {a: 1, b: 2}` and `dict2: {b: 3, c: 4}`. You want a new dict that combines both, with `dict2` values taking precedence for overlapping keys. Which filter chain achieves this?

A.`dict2 | combine(dict1, recursive=True)`
B.`dict2 | combine(dict1)`
C.`[dict1, dict2] | combine`
D.`dict1 | combine(dict2)`
AnswerD

Correct; combine merges dict2 into dict1, with dict2 overriding.

Why this answer

Option D is correct because the `combine` filter in Ansible merges two dictionaries, and when used with the pipe operator, the left-hand dictionary's values take precedence for overlapping keys. Here, `dict1 | combine(dict2)` ensures that for the overlapping key `b`, the value from `dict2` (3) overwrites the value from `dict1` (2), producing `{a: 1, b: 3, c: 4}`.

Exam trap

The trap here is that candidates often confuse the order of precedence with the `combine` filter, mistakenly thinking the argument dictionary (the one inside the parentheses) takes precedence, when in fact the left-hand operand (the one piped into the filter) has its values overwritten by the argument.

How to eliminate wrong answers

Option A is wrong because `recursive=True` is used for nested dictionary merging, which is unnecessary here and does not affect the precedence logic; also, the order `dict2 | combine(dict1)` would give precedence to `dict1` for overlapping keys, not `dict2`. Option B is wrong because `dict2 | combine(dict1)` gives precedence to `dict1` (the argument to `combine`), so `b` would be 2 from `dict1`, not 3 from `dict2`. Option C is wrong because `[dict1, dict2] | combine` is not valid syntax; the `combine` filter requires an argument (the dictionary to merge in), and using a list with a pipe does not invoke the filter correctly.

62
MCQhard

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`
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
C.The filter function must be imported in the playbook via `filter_plugins: my_filters`
D.The filter function name does not match the class name in the plugin
AnswerB

This is the required structure for filter plugins.

Why this answer

Option B is correct because Ansible requires custom filter plugins to be Python modules that define a class named `FilterModule` with a `filters()` method returning a dictionary mapping filter names to their implementing functions. Without this structure, Ansible cannot discover or register the filter, causing the 'no filter named' error.

Exam trap

Red Hat often tests the misconception that the filter function name must match the class name or that the file must be named `__init__.py`, when in fact the critical requirement is the `FilterModule` class with a `filters()` method.

How to eliminate wrong answers

Option A is wrong because the filter file does not need to be named `__init__.py`; that naming is for Python packages, not individual plugin files. Option C is wrong because filters are not imported in the playbook via a `filter_plugins` directive; Ansible automatically loads plugins from the `filter_plugins` directory based on configuration. Option D is wrong because the filter function name does not need to match the class name; the mapping is defined in the `filters()` method's dictionary.

63
Multi-Selecthard

Which THREE are valid uses of lookup plugins in Ansible? (Select exactly three.)

Select 3 answers
A.Generate a random MAC address using `password` lookup with `mac` parameter
B.Generate a random password using `password` lookup
C.Execute a command on the remote host using `pipe` lookup
D.Look up a value from an INI file using `ini` lookup
E.Read the contents of a file using `file` lookup
AnswersB, D, E

The `password` lookup generates random strings for passwords.

Why this answer

Option B is correct because the `password` lookup plugin in Ansible is specifically designed to generate random passwords with configurable length and character sets. It can also generate random MAC addresses when used with the `mac` parameter, but the core purpose is password generation, making this a valid use of a lookup plugin.

Exam trap

The trap here is that candidates often confuse the `pipe` lookup plugin with the `shell` or `command` modules, mistakenly thinking it executes commands on remote hosts, when in fact it runs locally on the control node.

64
MCQhard

You run a playbook with `msg: "{{ 'mypassword' | password_hash('sha512') }}"`. What is the output?

A.An error because password_hash requires the `passlib` library.
B.A random alphanumeric string.
C.A hashed representation of the password using SHA-512.
D.The string 'mypassword' unchanged.
AnswerC

Correct; password_hash returns a salted SHA-512 hash.

Why this answer

Option C is correct because the `password_hash` filter in Ansible uses the `passlib` library (when available) to generate a cryptographically hashed representation of the input string. When the hash type is specified as `'sha512'`, it produces a SHA-512 crypt hash (typically starting with `$6$`), not the original plaintext. This is a standard Ansible filter for securely transforming passwords into hashed formats for use in user modules or configuration files.

Exam trap

The trap here is that candidates may think `password_hash` returns a random string (Option B) because the salt is random, but the output is a structured hash with a fixed format, not a purely random alphanumeric sequence.

How to eliminate wrong answers

Option A is wrong because `password_hash` does not require the `passlib` library by default; Ansible uses Python's `crypt` module as a fallback, and `passlib` is only needed for additional hash types or older Ansible versions. Option B is wrong because the output is not a random alphanumeric string; it is a deterministic hash (with a random salt) that follows the SHA-512 crypt format (e.g., `$6$salt$hash`). Option D is wrong because the filter explicitly transforms the input string into a hashed value, leaving it unchanged only if the filter is misapplied or the hash type is invalid.

65
Drag & Dropmedium

Drag and drop the steps to set up a cron job that runs a script every day at 2 AM in the correct order.

Drag steps to the numbered slots on the right, or tap a step then tap a slot.

Steps
Order

Why this order

Cron setup: prepare script, test, edit crontab, add entry with correct syntax, verify.

66
MCQhard

Your organization manages a fleet of 500 web servers running RHEL 8. Each server has a custom fact file /etc/ansible/facts.d/web.fact containing: [web] docroot=/var/www/html port=80 admin=admin@example.com You have a playbook that needs to configure the firewall to allow traffic on the port defined in the custom fact for each server. The playbook uses the 'ansible_local' variable to access these facts. However, some servers have the custom fact file missing or malformed. The task to open the firewall port fails on those servers with 'VARIABLE IS UNDEFINED'. You need to implement a solution that handles missing custom facts gracefully, setting a default port of 8080 if the fact is not defined, and still log a warning. Which approach should you take?

A.Use 'ignore_errors: yes' on the firewall task and then check the result with 'failed_when' to set a default.
B.Use a 'set_fact' task with 'port: "{{ ansible_local.web.port | default(8080) }}"' and add a 'debug' task with 'when: ansible_local.web.port is undefined' to warn.
C.Use 'block' and 'rescue' to catch the error and then set the fact.
D.Create a separate playbook for servers without the fact file, running a different role.
AnswerB

Correct: sets default and warns.

Why this answer

Option B is correct because it uses the `default` filter to gracefully handle undefined variables, setting a fallback port of 8080 when the custom fact is missing or malformed. The additional `debug` task with `when: ansible_local.web.port is undefined` provides a warning without causing the playbook to fail. This approach aligns with the EX294 objective of transforming data with filters and plugins, specifically using Jinja2 filters to manage missing data.

Exam trap

The trap here is that candidates may overcomplicate the solution with error handling constructs like `ignore_errors` or `block/rescue`, instead of using the simple and idiomatic Jinja2 `default` filter, which is the most efficient and Ansible-native way to handle undefined variables.

How to eliminate wrong answers

Option A is wrong because `ignore_errors: yes` suppresses all errors from the firewall task, including legitimate failures, and does not set a default port; it merely continues execution without addressing the undefined variable. Option C is wrong because `block` and `rescue` can catch the error but require the task to fail first, which is less efficient than using the `default` filter to prevent the error entirely; additionally, setting the fact in the rescue block would still need a `set_fact` with a default, making the block/rescue redundant. Option D is wrong because creating a separate playbook for servers without the fact file is unnecessary overhead and violates the principle of idempotent, unified playbook management; the `default` filter handles the variability within a single playbook.

67
MCQeasy

An Ansible playbook needs to parse a JSON output from a REST API and extract the value of a nested key "data.settings.timeout". The output is stored in the variable "api_result". Which filter should be used to safely extract the value, with a default of 30 if the key is missing or the JSON is malformed?

A.{{ api_result | combine({'data.settings.timeout': 30}) }}
B.{{ api_result | selectattr('data.settings.timeout') | first | default(30) }}
C.{{ api_result | json_query('data.settings.timeout') | default(30) }}
D.{{ api_result | dict2items | selectattr('key', 'equalto', 'data.settings.timeout') | map(attribute='value') | first | default(30) }}
AnswerC

json_query correctly extracts the value using JMESPath, and default provides a fallback for missing keys.

Why this answer

Option C is correct because `json_query` uses JMESPath syntax to query nested JSON structures, and `data.settings.timeout` is a valid JMESPath expression for extracting the value at that nested key. The `default(30)` filter provides a fallback if the key is missing or the JSON is malformed, ensuring safe extraction.

Exam trap

The trap here is that candidates confuse `json_query` with Jinja2 filters like `selectattr` or `combine`, which operate on different data structures (lists of dicts vs. nested dicts) and fail to correctly extract nested keys from a single JSON object.

How to eliminate wrong answers

Option A is wrong because `combine` merges dictionaries, not extracts values; it would attempt to merge `{'data.settings.timeout': 30}` into `api_result`, which does not extract the nested key and would not handle missing keys safely. Option B is wrong because `selectattr` is used to filter lists of dictionaries by attribute, not to extract nested keys from a single JSON object; it would fail or return an empty list on a non-list input. Option D is wrong because `dict2items` converts a dictionary to a list of key-value pairs, but `data.settings.timeout` is a nested path, not a flat key; the filter would not match the nested structure and would return an empty list, defaulting to 30 only by accident, not by correct logic.

68
MCQeasy

An administrator is writing a playbook to manage multiple web servers. The playbook uses a variable "server_facts" which is a list of dictionaries with keys "hostname", "ip", and "status". The administrator needs to extract a list of all hostnames where status is "online". The administrator writes: - name: Get online hosts set_fact: online_hosts: "{{ server_facts | selectattr('status', '==', 'online') | map(attribute='hostname') | list }}" However, when running the playbook, the "selectattr" filter fails with an error: "Invalid data passed to filter". The administrator checks the structure of "server_facts" and confirms it is a list of dicts with the expected keys. What is the most likely cause of the error?

A.The "status" key exists but its value is not consistently a string; some entries have integer 0/1 instead of "online"/"offline".
B.The "map" filter cannot be chained with "selectattr" directly.
C.The "list" filter is unnecessary and causes the error.
D.The "selectattr" filter requires Python 3.8 or later.
AnswerA

Mismatched types cause the comparison to fail.

Why this answer

The `selectattr` filter in Ansible uses Jinja2's `selectattr` which relies on Python's `attrgetter` and comparison operators. If the `status` key exists but its value is not consistently a string (e.g., some entries have integer 0/1 instead of 'online'/'offline'), the equality comparison `'==', 'online'` will fail because an integer cannot be compared to a string in this context, causing an 'Invalid data passed to filter' error. The administrator confirmed the structure is correct, so the issue is likely a type mismatch in the values.

Exam trap

The trap here is that candidates assume the error is about filter chaining or syntax, but the real issue is data type inconsistency—specifically that `selectattr` performs strict equality checks and fails when comparing mismatched types like integers and strings.

How to eliminate wrong answers

Option B is wrong because `map` can be chained directly with `selectattr` in Jinja2 filters; this is a standard and supported pattern in Ansible. Option C is wrong because the `list` filter is necessary to convert the generator returned by `map` into a list; omitting it would not cause an 'Invalid data passed to filter' error. Option D is wrong because `selectattr` does not require Python 3.8; it works with Jinja2's built-in filters and is available in Python 2.7+ and all versions of Python 3 supported by Ansible.

69
Multi-Selectmedium

An Ansible playbook uses the "community.general" collection to manage firewall rules. The engineer wants to use a lookup plugin to fetch the current IPv4 address of a host to include in a dynamic inventory script. Which TWO of the following options correctly describe the usage of lookup plugins in Ansible?

Select 2 answers
A.Lookup plugins are always executed on the remote host.
B.Lookup plugins must be imported using the "lookup" keyword.
C.Lookup plugins are executed on the control node and return data to the Ansible controller.
D.The "file" lookup plugin reads the content of a file from the remote host.
E.Lookup plugins can be used in "vars" sections of a playbook.
AnswersC, E

Correct; they fetch data on the control node.

Why this answer

Option C is correct because lookup plugins in Ansible are designed to run on the control node (the machine where Ansible is executed), not on the remote host. They fetch data from external sources (e.g., files, databases, environment variables) and return that data to the Ansible controller for use in playbooks or inventory scripts. This aligns with the requirement to fetch the current IPv4 address of a host for a dynamic inventory script, as the lookup plugin can query local or network sources without needing to execute on the target host.

Exam trap

The trap here is that candidates often confuse lookup plugins (control node execution) with modules (remote host execution), or mistakenly think the 'lookup' keyword is a special Ansible keyword rather than a Jinja2 function, leading them to select options A or B.

70
MCQhard

The playbook above fails with an error. What is the most likely cause?

A.The `select` filter must be used with `selectattr` for attributes.
B.The `select` filter requires the test name without quotes.
C.The `match` test requires a regex pattern as an argument.
D.The `filter` plugin should be used instead of `select`.
AnswerC

The `match` test must be called with a pattern, e.g., `select('match', '.*')`.

Why this answer

The `match` test requires a regex pattern as an argument. The correct syntax is `select('match', 'pattern')`. Without a pattern, Ansible raises an error.

Option A is incorrect because quotes around the test name are fine. Option C is incorrect because `select` works on lists and does not need `selectattr` for simple matching. Option D is incorrect because there is no 'filter' plugin used here.

71
MCQeasy

An Ansible playbook retrieves a JSON response from an API and stores it in the variable `api_response`. The JSON structure is a list of objects, each with keys `name`, `status`, and `id`. The team needs to create a list of names for objects where status is 'active'. Which filter should be used?

A.{{ api_response | json_query("status=active.name") }}
B.{{ api_response | flatten }}
C.{{ api_response | subelements('name') }}
D.{{ api_response | selectattr('status', 'equalto', 'active') | map(attribute='name') | list }}
AnswerD

Correctly filters by status and maps to name.

Why this answer

Option D is correct because it uses `selectattr` to filter the list of objects to only those where `status` equals 'active', then `map` to extract the `name` attribute from each filtered object, and finally `list` to convert the result into a list. This is the standard Ansible approach for filtering a list of dictionaries and extracting specific keys.

Exam trap

The trap here is that candidates often confuse `json_query` with `selectattr`/`map` and attempt to use JMESPath syntax incorrectly, or they pick `flatten` or `subelements` because they sound related to list manipulation, without understanding their specific purposes.

How to eliminate wrong answers

Option A is wrong because `json_query` uses JMESPath syntax, which requires a query like `[?status=='active'].name` — the given syntax `status=active.name` is invalid and would cause an error. Option B is wrong because `flatten` is used to reduce nested lists into a single flat list, not to filter or extract attributes from a list of objects. Option C is wrong because `subelements` is designed to iterate over sub-elements of a list of dictionaries (e.g., a list of users each with a list of groups), not to filter or map attributes from a flat list of objects.

72
MCQeasy

A playbook uses `{{ my_var | default('fallback') }}`. What is the effect?

A.If `my_var` is defined but empty, the expression evaluates to 'fallback'.
B.If `my_var` is defined but equal to None, the expression evaluates to 'fallback'.
C.The filter raises an error because 'default' is not a valid filter.
D.If `my_var` is undefined, the expression evaluates to 'fallback'.
AnswerD

Correct; default filter provides a fallback for undefined variables.

Why this answer

Option D is correct because the `default` filter in Ansible (also known as `d()`) returns the specified fallback value only when the variable is undefined. If `my_var` is defined but empty or None, the filter returns the variable's value (empty string or None), not the fallback. This behavior is specific to the `default` filter's default mode; to also catch empty or None values, you must use `default('fallback', true)`.

Exam trap

The trap here is that candidates often confuse 'undefined' with 'falsy' (empty, None, 0), assuming the default filter replaces all falsy values, but Ansible's default filter only triggers on undefined variables unless the `true` parameter is explicitly added.

How to eliminate wrong answers

Option A is wrong because if `my_var` is defined but empty, the `default` filter without the `true` parameter returns the empty string, not 'fallback'. Option B is wrong because if `my_var` is defined and equal to None, the filter returns None (since None is a defined value), not 'fallback'. Option C is wrong because `default` is a valid Jinja2 filter that Ansible inherits; it does not raise an error.

Ready to test yourself?

Try a timed practice session using only Filters Plugins questions.