Courseiva

CCNA Transform data with filters and plugins Questions

50 questions · Transform data with filters and plugins · 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

The `upper` filter transforms each string to uppercase, and `loop` iterates the list, producing one debug message per item. This satisfies the stem's requirement that both list elements be processed individually, yielding 'A' then 'B' as separate task iterations rather than a single combined output.

Why this answer

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

The recursive=True parameter merges nested dictionaries key-by-key rather than replacing the whole sub-dictionary, while user_config values take precedence over base_config at each level. Without it, combine would overwrite entire nested blocks, losing base_config keys the stem requires to be preserved.

Why this answer

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
Multi-Selectmedium

A playbook must normalize a list of dictionaries read from a YAML file. Each dictionary has keys 'name' and 'ports', where 'ports' is a comma-separated string such as '80,443'. You need to produce a new list where each dictionary has the same 'name' but 'ports' is a list of integers. Which TWO filter usages are required to accomplish this transformation? (Choose two.)

Select 2 answers
A.select('match', '^[0-9]+$') applied to the tokens to filter valid ports.
B.json_query('ports') applied to each dictionary to extract the ports field as a list.
C.map('int') applied to the resulting token list to convert each string to an integer.
D.combine() applied to the dictionaries to merge the new ports list into each entry.
E.split(',') applied to the ports string to produce a list of string tokens.
AnswersC, E

After splitting, the tokens are still strings. The map filter applies the int filter to every element, converting each token into an integer. This produces the required list of numeric ports. Combining map with int is the idiomatic way to transform all elements of a list without writing an explicit loop in the playbook.

Why this answer

Converting a comma-separated string into a list of integers requires two distinct operations. Splitting on the comma produces string tokens, and mapping the int filter over those tokens converts each to a number. Together they yield the desired list of integer ports, which can then be assembled into each dictionary entry, satisfying the normalization requirement without manual iteration.

Exam trap

The trap here is thinking a single filter such as json_query or combine can both split and convert the port string.

4
MCQeasy

A template must emit a comma-separated string of hostnames from a list variable `web_nodes`, sorted alphabetically, for use in a configuration file. Which expression produces that string?

A.{{ web_nodes | unique | join(',') }}
B.{{ web_nodes | flatten | join(',') }}
C.{{ web_nodes | sort | join(',') }}
D.{{ web_nodes | join(',') | sort }}
AnswerC

The `sort` filter returns a new list ordered alphabetically, and `join` concatenates its elements using the comma delimiter. Chaining them yields a single string with hostnames in sorted order separated by commas, which is exactly the format the configuration file expects. Both filters are standard Ansible/Jinja2 filters available in templates.

Why this answer

The order of filters matters: sorting must happen while the data is still a list, then joining converts it to a string. Applying join before sort would sort characters, and filters like unique or flatten do not impose alphabetical order. Only sorting the list first and then joining with a comma delimiter meets the requirement.

Exam trap

The trap here is applying filters in the wrong sequence, so the join collapses the list before sorting can order its elements.

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

`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
MCQmedium

A playbook reads a YAML file containing a list of dictionaries named `packages`, where each dictionary has keys `name`, `version`, and `enabled`. You need to produce a comma-separated string of only the `name` values for all entries where `enabled` is true. Which Jinja2 expression accomplishes this in a single task variable assignment?

A.{{ packages | json_query('[?enabled].name') | join(',') }}
B.{{ packages | select('enabled') | map(attribute='name') | join(',') }}
C.{{ packages | selectattr('enabled') | map(attribute='name') | join(',') }}
D.{{ packages | map(attribute='name') | selectattr('enabled') | join(',') }}
AnswerC

The `selectattr` filter keeps only items whose `enabled` attribute is truthy, then `map(attribute='name')` extracts the name from each remaining dictionary, and `join(',')` collapses the resulting list into a comma-separated string. This matches the requirement exactly without needing a loop or extra variable.

Why this answer

Filtering a list of dictionaries by a boolean attribute and then extracting a specific key is a common data transformation. The `selectattr` filter is designed to filter by attribute, `map(attribute=...)` extracts the desired key, and `join` combines the results into a string. The order of operations is critical: filter first, then map, then join.

Exam trap

The trap here is assuming that `map` and `selectattr` can be used in any order, when in fact `map` transforms the data structure and must come after attribute-based filtering.

7
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

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.

8
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

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.

9
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 `default` filter with `required=true` raises an undefined-variable error when `my_var` is absent, halting the task immediately. This satisfies the stem's constraint that an undefined variable must not silently fall back to a substitute value, unlike a plain `default('x')` which would return the fallback instead.

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.

10
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

The join filter concatenates list elements into a single string using the supplied separator, so join(',') converts server_list into the comma-separated value the API expects. It operates directly on the list without requiring a loop.

Why this answer

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.

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

12
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

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

13
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.
AnswerD

The filter_plugins directive appends directories to Ansible's default search path for filter plugins, letting custom Jinja2 filters be located alongside built-ins. It does not enable or disable plugins; it only extends where the controller looks when resolving filter names.

Why this answer

The `filter_plugins` setting in `ansible.cfg` adds the specified directory to the default search path for filter plugins, not replacing it. Ansible searches default locations like `~/.ansible/plugins/filter` and the `filter_plugins` directory relative to the playbook, in addition to any directories specified by this setting. Therefore, option D is correct.

Exam trap

The trap is that candidates often assume `filter_plugins` replaces the default search path (option C), but it actually adds to it. This misunderstanding arises because some Ansible configuration settings override defaults, but `filter_plugins` is additive.

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.

14
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

The expression chains selectattr to filter dictionaries whose 'role' equals 'admin', then map extracts the 'name' attribute, and list materialises the result. This satisfies the requirement to produce a list of names, since selectattr alone would return whole dictionaries rather than the names.

Why this answer

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.

15
MCQeasy

What is the output of the following playbook task? ```yaml - name: Example task debug: msg: "{{ ['apple', 'banana'] | map('upper') | first }}" ```

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

The `map('upper')` filter applies the upper filter to every element, yielding `['APPLE', 'BANANA']`, then `first` returns the initial element, `'APPLE'`. This satisfies the stem's requirement to evaluate the chained filters in sequence and report the resulting string output.

Why this answer

The playbook task likely uses a filter that extracts only the first element after applying `map('upper')`. For example, `{{ ['apple', 'banana'] | map('upper') | first }}` would output 'APPLE'. The `map` filter applies the `upper` filter to each element, but the `first` filter selects only the first item, resulting in the string 'APPLE'.

Candidates may mistakenly think the entire list is returned, but this task explicitly retrieves a single element.

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'`.

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

17
MCQhard

You maintain a variable inventory_hostnames that is a list of FQDN strings such as 'web01.example.com'. A template must produce a new list containing only the hostname portion before the first dot for each entry, preserving order. Which Jinja2 expression using Ansible filters achieves this?

A.{{ inventory_hostnames | join('.') | regex_replace('\\..*$', '') }}
B.{{ inventory_hostnames | select('match', '^[^.]*') | list }}
C.{{ inventory_hostnames | map(attribute='split') | list }}
D.{{ inventory_hostnames | map('regex_replace', '^(.*?)\..*$', '\\1') | list }}
AnswerD

The map filter applies regex_replace to every element, and the pattern captures everything before the first literal dot while discarding the remainder. Wrapping with list materializes the generator so the template renders a proper list. This preserves input order and returns only the hostname portion, which matches the requirement without needing an explicit loop in the template.

Why this answer

Transforming each element of a list while preserving order is best done with map combined with a transformation filter. regex_replace with a capture group extracts the portion before the first dot for every FQDN, and list materializes the generator so the template emits an actual list. Selecting or joining would either leave the strings unchanged or collapse the collection, so mapping is the appropriate technique.

Exam trap

The trap here is confusing select, which filters elements by a test, with map, which transforms each element.

18
MCQeasy

A template must display the value of a variable named app_port. If app_port is undefined, the template should render the number 8080 instead of failing. Which expression accomplishes this using an Ansible filter?

A.{{ app_port | ternary(8080) }}
B.{{ app_port | default(omit) }}
C.{{ app_port | mandatory(8080) }}
D.{{ app_port | default(8080) }}
AnswerD

The default filter returns the supplied fallback value when the preceding variable is undefined. Here, if app_port has not been set, the template renders 8080; if it is defined, its actual value is used. This is the standard Ansible approach for providing safe fallbacks in templates and avoids undefined variable errors during rendering.

Why this answer

Providing a fallback for an undefined variable in a template is exactly what the default filter handles. When app_port is absent from the variable namespace, default(8080) supplies the specified value, allowing the template to render successfully. Other filters either raise errors, produce non-numeric placeholders, or require conditional arguments, so they do not meet the requirement.

Exam trap

The trap here is confusing default, which supplies a fallback, with mandatory, which forces an error when a variable is missing.

19
MCQeasy

A playbook registers the output of a shell command that returns a JSON string. You need to parse this JSON string into a usable dictionary within the same play. Which filter should you apply to the registered variable?

A.json_query
B.from_yaml
C.to_json
D.from_json
AnswerD

The `from_json` filter parses a JSON-formatted string and returns the corresponding Python data structure, such as a dictionary or list. This is exactly what is needed to convert the registered stdout string into an accessible dictionary for subsequent tasks.

Why this answer

When a command returns JSON as a string, the registered variable stores that string, not a parsed object. The `from_json` filter is specifically designed to deserialize JSON strings into Ansible data structures, enabling direct access to nested keys. Using the wrong direction or a query filter would not achieve the required parsing.

Exam trap

The trap here is confusing `from_json` with `to_json`; the former parses a string into data, while the latter serializes data into a string.

20
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

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

21
MCQmedium

A playbook reads a JSON inventory file with `lookup('file', 'hosts.json') | from_json` and registers it as `hostdata`. The structure is a top-level dictionary where each key is a hostname and each value is a dictionary of attributes, for example `{"web1": {"env": "prod", "cpu": 4}, "db1": {"env": "dev", "cpu": 8}}`. You must build a list containing only the hostnames whose `env` attribute equals `prod`. Which task correctly produces that list?

A.- set_fact: prod_hosts: "{{ hostdata | dict2items | selectattr('value.env', 'equalto', 'prod') | map(attribute='value') | list }}"
B.- set_fact: prod_hosts: "{{ hostdata | dict2items | selectattr('env', 'equalto', 'prod') | map(attribute='key') | list }}"
C.- set_fact: prod_hosts: "{{ hostdata | dict2items | selectattr('value.env', 'equalto', 'prod') | map(attribute='key') | list }}"
D.- set_fact: prod_hosts: "{{ hostdata | selectattr('env', 'equalto', 'prod') | map(attribute='name') | list }}"
AnswerC

`dict2items` converts the mapping into a list of `{key, value}` pairs, so each item exposes the hostname under `key` and its attributes under `value`. `selectattr('value.env', 'equalto', 'prod')` keeps only prod entries, and `map(attribute='key')` extracts the hostnames into a list. This chain matches the nested data shape exactly.

Why this answer

Because the source is a dictionary keyed by hostname, it must first be turned into an iterable list of pairs with `dict2items`. Each pair then exposes the hostname as `key` and the attribute dictionary as `value`, so the environment test must reference `value.env`. Extracting `key` afterwards yields exactly the list of prod hostnames the task requires.

Exam trap

The trap here is assuming `selectattr` can filter a plain dictionary directly and that nested attributes are reachable without first converting the mapping with `dict2items`.

22
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

The select filter with match applies the regex '.*prod.*' to each node, keeping only hostnames containing prod, and sort(reverse=True) orders the survivors descending. This single chained expression satisfies both the filtering and reverse-sorting constraints in one evaluation.

Why this answer

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.

23
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

The ternary filter tests whether item.dport is 80 or 443 and returns the matching string, so chain receives 'INPUT' or 'FORWARD'. This satisfies the module's string requirement, unlike the map filter which returned a list.

Why this answer

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.

24
MCQmedium

A playbook reads a JSON file on the control node with the `file` lookup and registers the content as a string named `raw_config`. A task must convert that string into a native Python dictionary so later tasks can reference keys such as `raw_config.database.port`. Which filter should be applied to the registered content?

A.from_json
B.combine
C.from_yaml
D.to_json
AnswerA

The `from_json` filter deserializes a JSON-formatted string into the native data structure Ansible uses, so a string containing an object becomes a dictionary whose keys can be addressed with dotted notation. Because the lookup returned text rather than structured data, this conversion is exactly what allows `raw_config.database.port` to resolve in later tasks.

Why this answer

The registered content is a string because the lookup returns file contents verbatim. To use nested keys, that string must be parsed into Ansible's native data structures. The `from_json` filter performs exactly this deserialization, turning a JSON object string into a dictionary that supports dotted access.

The reverse filter or YAML-specific parsers would not produce the required dictionary from a JSON string.

Exam trap

The trap here is assuming a lookup returns structured data rather than a string, so candidates reach for serialization filters instead of a parser.

25
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

The debug task prints the value held in the variable, which resolves to 192.168.1.10 as shown in the exhibit's host facts. Other addresses appearing in the inventory belong to different hosts or interfaces, so they are not the registered output.

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.

26
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

The map filter applies a function to every element of a list, returning a transformed list of equal length. It is a core Jinja2 list-manipulation filter in Ansible, satisfying the stem by enabling element-wise transformation within playbooks and templates.

Why this answer

The `map` filter is specifically designed for list manipulation in Ansible, allowing you to apply a transformation (such as extracting a specific attribute) to every element in a list. Option D is correct because the `select` filter is used to filter list items based on a condition, returning only those elements that match. Both are core Jinja2 filters commonly used in Ansible playbooks for processing lists.

Exam trap

The trap here is that candidates often confuse filters that operate on strings (like `regex_replace`) or single values (like `default` and `ternary`) with those designed for list iteration, leading them to select incorrect options that are not list-specific.

27
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

The difference filter returns elements in the first list absent from the second, so hosts in webservers but not drain are extracted. Both groups resolve via the groups magic variable, giving the exact set difference the playbook requires.

Why this answer

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.

28
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

The reject filter removes list elements matching the supplied test, returning the remaining items. It performs conditional exclusion rather than selection, which is why it pairs with select as one of the two test-based list filters in Ansible.

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.

29
MCQmedium

A playbook registers the output of the `package_facts` module and must build a list containing only the version strings of installed packages whose name begins with 'httpd'. Which filter pipeline extracts those versions?

A.ansible_facts.packages | dict2items | selectattr('key', 'match', '^httpd') | map(attribute='value') | flatten | map(attribute='version') | list
B.ansible_facts.packages | dict2items | selectattr('key', 'match', '^httpd') | map(attribute='value.version') | list
C.ansible_facts.packages | selectattr('name', 'match', '^httpd') | map(attribute='version') | list
D.ansible_facts.packages | dict2items | selectattr('key', 'match', '^httpd') | map(attribute='value') | list
AnswerA

This pipeline converts the package mapping to key/value pairs, keeps only keys matching the 'httpd' prefix with a regular expression, extracts each matching value (a list of records), flattens the nested lists, then maps to the `version` attribute of each record. The final list contains only version strings for the matching packages, which is precisely what the scenario demands.

Why this answer

Package facts are stored as a dictionary keyed by package name, with each value being a list of records. Extracting versions therefore requires converting the mapping to items, filtering keys, unwrapping the value lists with flatten, and then mapping the version attribute. Pipelines that skip the flatten step or that treat the dictionary as a list of objects cannot reach the nested version field.

Exam trap

The trap here is forgetting that each package entry is a list of records, so mapping directly to a version attribute fails without flattening first.

30
MCQhard

A task registers the output of a command that returns a string containing a JSON document in `result.stdout`. You must extract the numeric value at `metrics.cpu.load` and use it as an integer in a subsequent comparison. Which expression correctly parses the string and retrieves the integer value?

A.{{ result.stdout | from_json | json_query('metrics.cpu.load') | int }}
B.{{ result.stdout | json_query('metrics.cpu.load') | from_json | int }}
C.{{ result.stdout | to_json | json_query('metrics.cpu.load') | int }}
D.{{ result.stdout | from_json | map(attribute='metrics.cpu.load') | int }}
AnswerA

`from_json` parses the JSON string into a Python dictionary, `json_query` traverses the nested path `metrics.cpu.load`, and `int` coerces the resulting value to an integer for comparison. This chain matches the scenario where stdout is a JSON string and the target is a nested numeric field.

Why this answer

The correct order is to parse the raw JSON string first with `from_json`, then traverse the nested structure with `json_query`, and finally cast the extracted value with `int`. Reversing or substituting `to_json` breaks the pipeline because it serializes rather than parses. Using `map` is inappropriate for a single dictionary input, and querying before parsing cannot succeed on a raw string.

Exam trap

The trap here is assuming `to_json` and `from_json` are interchangeable, when only `from_json` parses a string into structured data.

31
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

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

32
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

A lookup plugin runs on the control node and returns data to Ansible, so it can query an external API for secrets at runtime. This satisfies the requirement to fetch vault passwords from a source separate from host variables, unlike vault password files or inventory variables.

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.

33
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

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

34
MCQhard

A playbook must merge a base dictionary of defaults with an override dictionary, where the override may contain nested dictionaries that should be merged recursively rather than replaced. Which filter expression performs a recursive merge?

A.{{ base | combine(override) }}
B.{{ base | combine(override, recursive=True) }}
C.{{ base | combine(override, list_merge='keep') }}
D.{{ base | union(override) }}
AnswerB

The combine filter accepts a recursive parameter. Setting recursive=True merges nested dictionaries level by level instead of replacing them wholesale, preserving base subkeys while applying overrides. This matches the scenario where override contains nested dictionaries that must merge into the base structure rather than overwrite it entirely.

Why this answer

The combine filter merges dictionaries, and its recursive parameter controls whether nested dictionaries are merged deeply or replaced. For nested overrides that must preserve base subkeys, recursive=True is required. Parameters like list_merge affect list handling only and do not enable recursive dictionary merging.

Exam trap

The trap here is assuming combine always merges deeply, when by default it replaces nested dictionaries.

35
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

The dict2items filter transforms a dictionary into a list of dictionaries, each containing key and value entries. Iterating that list exposes both service names and ports within the task, which is exactly what the playbook requires.

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.

36
MCQmedium

A playbook registers the output of a command that returns a JSON string inside stdout. The string contains a top-level key 'services' with a nested list of dictionaries. You need to convert that string into native data so you can select only entries where the 'state' key equals 'running'. Which expression accomplishes this?

A.{{ (command_result.stdout | from_json).services | selectattr('state', 'equalto', 'running') | list }}
B.{{ command_result.stdout | from_json | selectattr('state', 'equalto', 'running') | list }}
C.{{ command_result.stdout | from_json | map(attribute='services') | selectattr('state', 'equalto', 'running') | list }}
D.{{ command_result.stdout | to_json | selectattr('state', 'equalto', 'running') | list }}
AnswerA

The from_json filter parses the stdout string into native data, then the '.services' lookup reaches the nested list. Applying selectattr to that list with equalto 'running' returns only matching dictionaries. The final list filter materializes a real list instead of a generator, which is important for templating and iteration.

Why this answer

Parsing the JSON string with from_json is required before any attribute-based filtering can occur. Once parsed, the nested list must be dereferenced through the 'services' key. Only then can selectattr compare each dictionary's 'state' value to 'running' and return the matching subset as a list.

Exam trap

The trap here is assuming selectattr can filter dictionaries nested under a key without first dereferencing that key in the expression.

37
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

The `map` filter extracts the `memory_mb` attribute from each server dictionary, producing a list of integers, which `sum` then totals into `total_memory`. This satisfies the stem's requirement to aggregate values across a list without a loop, using Jinja2 filters natively supported in Ansible playbooks.

Why this answer

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

38
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

The map filter applies the int filter to each element of the list, converting the strings '1', '2' and '3' into integers. The subsequent list filter materialises the generator into an actual list, yielding [1, 2, 3].

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

39
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

The community.general.parse_csv filter converts CSV text into a list of dictionaries, using the header row as keys and each subsequent row as values. Fields remain strings, so age appears as '30' rather than an integer, matching the structure shown.

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.

40
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

The default filter substitutes 'fallback' when my_var is undefined, preventing the undefined-variable error. It resolves the stem's problem because group_vars/all.yml evidently is not being loaded for this host, so the variable never reaches the template.

Why this answer

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

41
MCQmedium

A playbook defines the variable `packages` as a list of dictionaries, each containing `name` and `version` keys. You need to build a new list of strings in the form `name-version` for use in a log message. Which filter combination produces this result?

A.{{ packages | map(attribute='name') | zip(packages | map(attribute='version')) | map('join', '-') | list }}
B.{{ packages | selectattr('name') | map('join', '-') | list }}
C.{{ packages | map('combine') | list }}
D.{{ packages | map('dict2items') | map('join', '-') | list }}
AnswerA

This expression extracts the `name` and `version` attributes from each dictionary, pairs them positionally with `zip`, then joins each pair with a hyphen using `map('join', '-')`. The final `list` materializes the result. It produces exactly the `name-version` strings required in the correct order.

Why this answer

Building `name-version` strings from a list of dictionaries requires extracting both attributes separately and then pairing them. The `zip` filter aligns the two extracted lists element-wise, and `map('join', '-')` converts each pair into a hyphenated string. Filters like `combine`, `dict2items`, and `selectattr` operate on different data shapes and do not produce the desired formatted strings.

Exam trap

The trap here is assuming `map('join', '-')` alone can combine two fields from the same dictionary, when it only joins elements within one list.

42
Multi-Selectmedium

You are writing a playbook that needs to transform a list of strings representing file paths. You want to extract only the base filenames (without directory paths) and ensure they are all lowercase. Which TWO filters should you use in your Jinja2 expression? (Choose two.)

Select 2 answers
A.lower
B.dirname
C.splitext
D.upper
E.basename
AnswersA, E

The `lower` filter converts a string to all lowercase characters. After extracting the base filename, applying `lower` ensures the result is consistently lowercase, which is useful for case-insensitive comparisons or standardizing output. It operates on strings and returns a new string.

Why this answer

To extract base filenames from full paths, the `basename` filter is the correct tool. To ensure the result is lowercase, the `lower` filter is applied afterward. Together they transform a list of paths into a list of lowercase filenames.

Other filters like `dirname`, `upper`, or `splitext` serve different purposes and do not meet the requirements.

Exam trap

The trap here is confusing `basename` with `dirname` or assuming that `splitext` can extract the filename from a path.

43
MCQeasy

A task sets a variable `raw_size` to the string `'2048'`. You need to pass this value to a module parameter that expects an integer. Which filter should be applied to `raw_size` to achieve the correct type?

A.{{ raw_size | float }}
B.{{ raw_size | bool }}
C.{{ raw_size | int }}
D.{{ raw_size | string }}
AnswerC

`int` converts the string `'2048'` into the integer 2048, satisfying the module parameter's numeric type requirement. It correctly handles numeric strings and produces a value usable in arithmetic or size comparisons. This is the appropriate filter for the stated scenario.

Why this answer

When a variable holds a numeric string and a module parameter requires an integer, the `int` filter performs the necessary conversion. Filters like `bool` and `string` do not change the value into an integer, and `float` produces a floating-point number that may not satisfy an integer parameter. Using `int` ensures the value is the correct type for the module.

Exam trap

The trap here is reaching for `float` because it is numeric, when the parameter specifically requires an integer type.

44
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

Ansible loads filter plugins only from Python modules exposing a FilterModule class whose filters method returns a dict mapping filter names to callables. Without that structure, the plugin is ignored, so no filter named my_custom_filter is registered, producing the error.

Why this answer

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.

45
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 or tap steps into the slots.

Steps
Order
1Step 1
2Step 2
3Step 3
4Step 4

Why this order

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

46
MCQmedium

A playbook reads a YAML configuration file with the `include_vars` module and registers the result. The variable `app_config` now holds a dictionary with keys `database`, `cache`, and `logging`. You need to produce a list containing only the top-level key names for a later loop. Which Jinja2 filter expression should you use in the task that builds this list?

A.{{ app_config | items2dict | map(attribute='key') | list }}
B.{{ app_config | dict2items | map(attribute='key') | list }}
C.{{ app_config | flatten | map(attribute='key') | list }}
D.{{ app_config | subelements('key') | map(attribute='key') | list }}
AnswerB

The `dict2items` filter converts a dictionary into a list of dictionaries, each with a `key` and `value` field. Applying `map(attribute='key')` extracts the original top-level dictionary keys, and `list` materializes the result. This produces exactly the list of top-level names required for the loop, without touching nested values.

Why this answer

To turn dictionary keys into a list, the canonical approach is to convert the dictionary to a list of `{key, value}` dictionaries with `dict2items`, then extract the `key` attribute using `map`. The resulting list can be looped over directly. Filters like `items2dict`, `flatten`, and `subelements` operate on different data shapes and do not produce top-level key names from a plain dictionary.

Exam trap

The trap here is confusing `dict2items` with `items2dict` and assuming either one works in both directions on a dictionary.

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

48
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

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.

49
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

selectattr filters the list to objects whose status equals active, then map extracts the name attribute from each match, and list materialises the result. This chains the exact predicates needed, unlike map alone or json_query alternatives.

Why this answer

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.

50
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

The Jinja2 default filter returns its argument only when the preceding variable is undefined; otherwise the variable's value passes through unchanged. So an undefined my_var yields 'fallback', while a defined my_var keeps its own value.

Why this answer

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 Transform data with filters and plugins questions.