Ansible Map Examples - Filter List and Dictionaries | Devops Junction

In this article, we are going to see the various use cases of Ansible Map Function or Filter.

As Ansible Official documentation claims, All Jinja2 Filters can be used within Ansible.

One such function or filter is map

It helps us to filter and iterate complex datasets and a list of objects. Helps in filtering the attributes or in applying other available filters onto the list like replace etc

In this article, we will cover various examples of Ansible Map and how to use them in real-time.

Let's go ahead.

Ansible Map

What is Ansible Map Filter and How it works?

Ansible Map Filter does two things

  • It applies a filter on a sequence of objects
  • It looks up an attribute.

This is useful when dealing with lists of objects but you are really only interested in a certain value of it.

The basic usage of map is to look up an attribute.  Let's cover it first

Imagine you have a list of users but you are only interested in a list of usernames:

for example consider the following employee data with lot of values like first name, lastname, address, mobile etc.  But what I want is only their city

names: [ {
        "first": "Paul",
        "last": "Thompson",
        "mobile": "+1-234-31245543",
        "ctc": "100000",
        "address": {
          "city": "LasVegas",
          "country": "USA"
        }
      },
      {
        "first": "Sarav",
        "last": "AK",
        "mobile": "+919876543210",
        "ctc": "200000",
        "address": {
          "city": "Chennai",
          "country": "India"
        }

      }]

So this is how,  ansible map can help to list only the cities from the employee data.

{{ names | map(attribute='address') | map(attribute='city')}}

Or to get only the First Names

{{ names | map(attribute='first') | join(',') }}

Ansible Map Filter Examples - Looking up an Attribute

Here is the playbook with multiple Ansible Map filter examples with attribute selection

Each task has self-explanatory comments for you to understand what exactly they are doing.

---
- name: filter test
  hosts: localhost
  vars: 
    
    names: [ {
        "first": "Paul",
        "last": "Thompson",
        "mobile": "+1-234-31245543",
        "ctc": "100000",
        "address": {
          "city": "LasVegas",
          "country": "USA"
        }
      },
      {
        "first": "Rod",
        "last": "Johnson",
        "mobile": "+1-584-31551209",
        "ctc": "300000",
        "address": {
          "city": "Boston",
          "country": "USA"
        }
      },
      {
        "first": "Sarav",
        "last": "AK",
        "mobile": "+919876543210",
        "ctc": "200000",
        "address": {
          "city": "Chennai",
          "country": "India"
        }

      }]
  tasks:
  
  # Map Filter only selective attributes from list of objects [{},{}]
  - name: Select and Extract only the cities 
    debug: 
      msg="{{ names | map(attribute='address') | map(attribute='city')}}"

  # using attirubtes with list of objects [{},{}] - Selecting only mobile numbers
  - name: Select and Extract only mobile numbers
    debug:
      msg: "{{ names | map(attribute='mobile') }}"

  # Select Attributes Joined with Comma in Singleline ( By Default it returns a List)
  - debug: msg={{ names | map(attribute='first') | join(',') }} 
  - debug: msg={{ names | map(attribute='last') | join(',') }} 

  # Convert the lastname to uppercase
  - debug: msg={{ names | map(attribute='last') | map('upper') }} 

  # Convert the CTC attriute to float value
  - debug: msg={{ names | map(attribute='ctc') | map('float') }}

  # Appending USD to each CTC value and print
  - debug: msg={{ names | map(attribute='ctc') | product(['USD']) | map('join',' ')}}

The Primary use of map filter is with attribute selection from the sequence of elements or objects.

It helps us to select only the values we want from the huge dataset.

In the preceding playbook, you can see we have various examples of map attribute selection.

The last three examples have additional map another filter like upper and float etc.

The join is used to format the output to single-line string which is otherwise a list.  As map returns a list by default.

Here is the output of this playbook

PLAY [filter test] ***************************************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************************************
ok: [localhost]

TASK [Select and Extract only the cities] ***************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        "LasVegas",
        "Boston",
        "Chennai"
    ]
}

TASK [Select only Attributes from List of Objects - Selecting only mobile numbers] ***********************************************************************************************
ok: [localhost] => {
    "msg": [
        "+1-234-31245543",
        "+1-584-31551209",
        "+919876543210"
    ]
}

TASK [debug] *********************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": "Paul,Rod,Sarav"
}

TASK [debug] *********************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": "Thompson,Johnson,AK"
}

TASK [debug] *********************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        "THOMPSON",
        "JOHNSON",
        "AK"
    ]
}

TASK [debug] *********************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        100000.0,
        300000.0,
        200000.0
    ]
}

TASK [debug] *********************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        "100000 USD",
        "300000 USD",
        "200000 USD"
    ]
}

 

Ansible Map Examples - using other Filters

The Second but major role of Ansible map filter is to apply an another filter on a sequence of objects/lists.

Let's suppose you have a list of names you want to convert all of them to an upper case strings.

For this example, you can consider the same dataset we have used in our first playbook.

In fact, we had one task to convert the last name to upper case like this

- debug: msg={{ names | map(attribute='last') | map('upper') }}

which resulted in the upper case converting last names like this.

TASK [debug] *********************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"THOMPSON",
"JOHNSON",
"AK"
]
}

This is just a simple example on Applying other filters using map.

There are more advanced uses of this scenario.

 

Using Replace filter with Ansible Map

---
- name: Ansible Map with Other Filters
  hosts: localhost
  vars: 
    servers: 
      - "AppServer01"
      - "AppServer02"
      - "WebServer01"
      - "WebServer02"
  tasks: 
    - name: Append the Domain Name to the end of the hosts
      debug: 
        msg={{servers|map('lower')|map('regex_replace','^(.+)$','\\1.gritfy.io')}}

In the preceding Playbook, you can see we have list of hostnames assigned to the variable named servers

they are in Camel case which needs to be converted to lower case and a valid domain name gritfy.io has to be added at the end of each host.

Here comes the map to the help

{{servers|map('lower')|map('regex_replace','^(.+)$','\\1.gritfy.io')}}

we use two map filters here, one is to convert them to lower case and another one is to apply regex_replace filter to replace each hostname with a proper domain suffix.

here is the execution output of this playbook look like

you can see that the hostnames are now in lower case with proper domain extensions.

 

 

Using Extract filter in Ansible Map -  with List/Sequence

Extract filter within Ansible map used to map list of indices to list of values. the list of values can be a simple list or sequence or complex hashtable aka dictionary.

let us first see how an ansible map can be used to iterate a simple list

Here is the example playbook where we have a list of values (names) and we want to take certain names based on their positional index.

---
- name: filter test
  hosts: localhost
  tasks: 
    - name: Extract Filter with Ansible Map - Select based on indices
      debug: 
        msg={{ [0,2] | map('extract', ['Sarav','Hanu','Gopi']) | list }}

Now we are taking only the 0th and 2nd element from the list.

The result would be Sarav, Gopi  as they are on 0 and 2nd place based on the indices value

Using Extract filter in Ansible Map -  with Dictionary/Hashtable

As we have already seen how Ansible map filter can be used to select or filter an element using numeric index values.

Let us see an example of how it can be used with dictionary also called as hash table

Unlike the simple list, the dictionary would contain string index as most often called as key

Consider the following dataset of employees at Gritfy

{ 
"sarav":{ "mobile":"985643210","email":"[email protected]", "city":"Coimbatore" }, 
"hanu":{ "mobile":"7865432109","email":"[email protected]","city":"Hyderabad" }, 
"gopi":{ "mobile":"9812990123","email":"[email protected]","city":"Chennai" } 
}

Now I want to perform a few iterations and select different values from this dataset using extract and map

Here is the playbook

---
- name:  Ansible Map Extract filter
  hosts: localhost
  vars: 
        employees:
          { 
            "sarav":{ "mobile":"985643210","email":"[email protected]", "city":"Coimbatore" }, 
            "hanu":{ "mobile":"7865432109","email":"[email protected]","city":"Hyderabad" }, 
            "gopi":{ "mobile":"9812990123","email":"[email protected]","city":"Chennai" } 
          }
  tasks: 
    - name: Task1 - Select only Sarav's records from the Dictionary or Hash table
      debug: 
        msg={{ ['sarav']| map('extract', employees ) }}

    - name: Task2 - Select Hanu's record and look for his email ID using Third argument
      debug: 
        msg=" Hanu`s Email ID is {{ ['hanu']| map('extract', employees, 'email') | last}} "
    
    - name: Task3 - Select Gopi's record and look for his City using Third argument
      debug: 
        msg=" Gopi is residing in {{ ['gopi']| map('extract', employees, 'city') | last }} "

 

we have three tasks here

  • Task1task is to lookup only Sarav's records from the employees dataset
  • Task2 is to lookup Hanu's records first and then to look up his email ID using the third argument
  • Task3  is to lookup Gopi's records first and then to select his city using third argument

Here is the execution output of this playbook

 

Conclusion

Ansible Map is an advanced topic and it's a little hard to understand at the beginning

I personally have spent a long time understanding the different usage of Ansible Map and I know I have more to learn.

In a nutshell, Ansible Map helps us in two ways

  • To look up an attribute
  • To apply other filters on list and dictionaries

we have learnt various examples on both these scenarios and use cases.

A highlight here is the map with extract filter. this is more helpful while parsing hostvars and their elements.

If you have any comments, feedback or best practices please use the comments section or write to us.

 

Cheers
Sarav

Follow me on Linkedin My Profile
Follow DevopsJunction onFacebook orTwitter
For more practical videos and tutorials. Subscribe to our channel

Buy Me a Coffee at ko-fi.com

Signup for Exclusive "Subscriber-only" Content

Loading