AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible map Filter: Extract Attributes from Lists (Complete Guide)

By Luca Berton · Published 2024-01-01 · Category: installation

Complete guide to the Ansible map filter. Extract attributes from lists of objects, apply filters to list items, chain with select/reject, and transform data.

The Ansible map filter is one of the most powerful Jinja2 filters for transforming lists. It lets you extract a single attribute from a list of objects, apply a filter to every item, or chain complex data transformations — all in a single expression.

Basic Syntax

# Extract an attribute from each item in a list
{{ list_of_dicts | map(attribute='name') | list }}

# Apply a filter to each item {{ list_of_strings | map('upper') | list }}

# Apply a filter with arguments {{ list_of_strings | map('regex_replace', '^(.*)$', 'prefix_\\1') | list }}

> Important: Always add | list at the end. The map filter returns a generator, not a list. Without | list, you'll get a generator object instead of usable data.

See also: Ansible default() Filter: Set Fallback Values for Undefined Variables

Extract a Single Attribute

The most common use case — pull one field from a list of dictionaries:

---
- name: Extract attributes with map
  hosts: localhost
  gather_facts: false
  vars:
    users:
      - name: alice
        email: alice@example.com
        role: admin
      - name: bob
        email: bob@example.com
        role: developer
      - name: charlie
        email: charlie@example.com
        role: viewer
  tasks:
    - name: Get all usernames
      ansible.builtin.debug:
        msg: "{{ users | map(attribute='name') | list }}"
      # Output: ['alice', 'bob', 'charlie']

- name: Get all emails ansible.builtin.debug: msg: "{{ users | map(attribute='email') | list }}" # Output: ['alice@example.com', 'bob@example.com', 'charlie@example.com']

Extract Nested Attributes

Access nested dictionary keys using dot notation:

vars:
  servers:
    - name: web1
      network:
        ip: 192.168.1.10
        subnet: 255.255.255.0
    - name: web2
      network:
        ip: 192.168.1.11
        subnet: 255.255.255.0

tasks: - name: Get all IP addresses ansible.builtin.debug: msg: "{{ servers | map(attribute='network.ip') | list }}" # Output: ['192.168.1.10', '192.168.1.11']

See also: Ansible regex_replace Filter: Find & Replace with Regex (Complete Guide)

Apply a Filter to Every Item

Use map to apply any Jinja2 filter to each element:

vars:
  names: ['alice', 'bob', 'charlie']

tasks: - name: Uppercase all names ansible.builtin.debug: msg: "{{ names | map('upper') | list }}" # Output: ['ALICE', 'BOB', 'CHARLIE']

- name: Wrap each name in quotes ansible.builtin.debug: msg: "{{ names | map('regex_replace', '^(.*)$', '\"\\1\"') | list }}"

- name: Add prefix to each item ansible.builtin.debug: msg: "{{ names | map('regex_replace', '^', 'user_') | list }}" # Output: ['user_alice', 'user_bob', 'user_charlie']

- name: Get string lengths ansible.builtin.debug: msg: "{{ names | map('length') | list }}" # Output: [5, 3, 7]

Chain map with selectattr

Combine selectattr to filter, then map to extract:

vars:
  users:
    - name: alice
      active: true
      role: admin
    - name: bob
      active: false
      role: developer
    - name: charlie
      active: true
      role: developer

tasks: - name: Get names of active users only ansible.builtin.debug: msg: "{{ users | selectattr('active', 'equalto', true) | map(attribute='name') | list }}" # Output: ['alice', 'charlie']

- name: Get names of developers ansible.builtin.debug: msg: "{{ users | selectattr('role', 'equalto', 'developer') | map(attribute='name') | list }}" # Output: ['bob', 'charlie']

- name: Get active developer names ansible.builtin.debug: msg: >- {{ users | selectattr('active', 'equalto', true) | selectattr('role', 'equalto', 'developer') | map(attribute='name') | list }} # Output: ['charlie']

See also: Ansible Jinja2 Filters: Transform Data in Playbooks (Complete Reference)

Extract Multiple Attributes

The map filter extracts only one attribute at a time. For multiple attributes, use a Jinja2 for-loop or combine with json_query:

Method 1: Multiple map Calls

- name: Get names and emails separately
  ansible.builtin.debug:
    msg:
      names: "{{ users | map(attribute='name') | list }}"
      emails: "{{ users | map(attribute='email') | list }}"

Method 2: json_query (Multiple Fields)

- name: Get name and email pairs
  ansible.builtin.debug:
    msg: "{{ users | json_query('[].{name: name, email: email}') }}"

Method 3: Jinja2 List Comprehension

- name: Build custom list
  ansible.builtin.set_fact:
    user_summary: >-
      {{ users | map(attribute='name') | zip(users | map(attribute='email'))
         | map('join', ': ') | list }}
  # Output: ['alice: alice@example.com', 'bob: bob@example.com', ...]

Real-World Examples

Get All IP Addresses from Inventory

- name: Collect all IPs from group
  ansible.builtin.debug:
    msg: "{{ groups['webservers'] | map('extract', hostvars, 'ansible_default_ipv4') | map(attribute='address') | list }}"

Build Package List from Role Variables

vars:
  packages:
    - name: nginx
      version: "1.24"
      state: present
    - name: redis
      version: "7.0"
      state: present
    - name: mysql
      version: "8.0"
      state: absent

tasks: - name: Install only present packages ansible.builtin.package: name: "{{ item }}" state: present loop: "{{ packages | selectattr('state', 'equalto', 'present') | map(attribute='name') | list }}"

Generate Configuration from List

vars:
  dns_servers:
    - ip: 8.8.8.8
      provider: Google
    - ip: 1.1.1.1
      provider: Cloudflare

tasks: - name: Write DNS config ansible.builtin.copy: content: | # DNS Servers {% for ip in dns_servers | map(attribute='ip') | list %} nameserver {{ ip }} {% endfor %} dest: /etc/resolv.conf

Flatten and Map Nested Structures

vars:
  departments:
    - name: Engineering
      members: ['alice', 'bob']
    - name: Marketing
      members: ['charlie', 'diana']

tasks: - name: Get all members across departments ansible.builtin.debug: msg: "{{ departments | map(attribute='members') | flatten | list }}" # Output: ['alice', 'bob', 'charlie', 'diana']

map vs Other Filters

| Filter | Purpose | Example | |--------|---------|---------| | map(attribute='x') | Extract attribute from dicts | users \| map(attribute='name') | | map('filter') | Apply filter to each item | names \| map('upper') | | selectattr | Filter dicts by attribute value | users \| selectattr('active') | | select | Filter items by test | numbers \| select('gt', 5) | | json_query | Complex JMESPath queries | users \| json_query('[].name') | | items2dict | Convert list of k/v to dict | pairs \| items2dict |

Common Errors

"Generator object" in Output

# Wrong — missing | list
msg: "{{ users | map(attribute='name') }}"
# Output: <generator object...>

# Correct msg: "{{ users | map(attribute='name') | list }}"

"Undefined attribute" Error

# Handle missing attributes with default
msg: "{{ users | map(attribute='nickname', default='N/A') | list }}"

"Expected string or number" in Loop

# Wrong — map returns objects, not strings
loop: "{{ users | map(attribute='name') }}"

# Correct — convert to list first loop: "{{ users | map(attribute='name') | list }}"

FAQ

What does the Ansible map filter do?

The map filter iterates over a list and either extracts a specific attribute from each item (when used with attribute=) or applies a Jinja2 filter to each item. It returns a generator that you convert to a list with | list.

How do I extract multiple attributes with map?

You cannot extract multiple attributes in a single map call. Use separate map calls for each attribute, or use json_query with JMESPath: users | json_query('[].{name: name, email: email}').

What is the difference between map and selectattr?

selectattr filters a list of dictionaries by an attribute value (returns matching items). map transforms a list by extracting an attribute or applying a filter (returns the transformed values). They're often chained: selectattr first to filter, then map to extract.

Why do I need | list after map?

The map filter returns a lazy generator object, not a Python list. Most Ansible operations need a real list, so you must pipe through | list to materialize the results.

Can I use map with nested attributes?

Yes, use dot notation: servers | map(attribute='network.ip') | list. This works for dictionaries nested multiple levels deep.

Conclusion

The map filter is essential for working with lists of dictionaries in Ansible. Key patterns to remember: • Extract attributes: list | map(attribute='field') | listApply filters: list | map('filter_name') | listFilter then extract: list | selectattr(...) | map(attribute='field') | listAlways add | list at the end

Master map and you'll write cleaner, more concise Ansible playbooks with less boilerplate looping.

Related Articles

Filter a List by Its Attributes: Ansible selectattr FilterAnsible Combine for Dicts vs Product for ListsAnsible Flatten: Nested Lists in PlaybooksAnsible split Filter Complete GuidePrint Text or Variable: Ansible Module debug

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home