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

FilterPurposeExample
map(attribute='x')Extract attribute from dictsusers \map(attribute='name')
map('filter')Apply filter to each itemnames \map('upper')
selectattrFilter dicts by attribute valueusers \selectattr('active')
selectFilter items by testnumbers \select('gt', 5)
json_queryComplex JMESPath queriesusers \json_query('[].name')
items2dictConvert list of k/v to dictpairs \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') | list
  • Apply filters: list | map('filter_name') | list
  • Filter then extract: list | selectattr(...) | map(attribute='field') | list
  • Always add | list at the end
Master map and you'll write cleaner, more concise Ansible playbooks with less boilerplate looping.

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home