Ansible selectattr Example - Filter dictionary and select matching item

Ansible selectattr filter is to select matching objects  from the dictionary by applying a test across all the objects in a dictionary/sequence.

Ansible selectattr filter is basically an inherited version of Jinja selectattr filter.  As Ansible Quotes in their documentation besides the built-in Ansible filters, all JINJA2 filters can be used in Ansible playbooks.

Be informed that, Only Ansible 2.7+ with Jinja2 supports the selectattr for those who are not there yet, you can use ansible json_query. Here is our article that can help

To see what version of ansible that you are using and what version of JINJA does it support you can use the ansible – version command

⇒ ansible – version | grep "jinja"
  jinja version = 3.0.2

In the preceding output you can see that my ansible supports Jinja 3.0.2

Having said that. Let's go ahead in to our today topic.

ansible select_attr

List of Objects / Dictionary we are going to use

Consider the following dataset

[
    {
      "name": "Shanmugam",
      "gender": "male",
      "mobile": "9875643210",
      "dose1completed" : "yes",
      "dose2completed" : "yes",
      "age": "25",
      "city": "pudukottai",
      "state": "Tamilnadu"
    },
    {
      "name": "Lakshmi",
      "gender": "female",
      "mobile": "9875623410",
      "dose1completed" : "yes",
      "dose2completed" : "no",
      "age": "32",
      "city": "Chennai",
      "state": "Tamilnadu"
    },
    {
      "name": "Albert",
      "gender": "male",
      "mobile": "9875634510",
      "dose1completed" : "yes",
      "dose2completed" : "yes",
      "age": "65",
      "city": "Coimbatore",
      "state": "Tamilnadu"
    },
    {
      "name": "Abdul",
      "gender": "male",
      "mobile": "9875632341",
      "dose1completed" : "no",
      "dose2completed" : "no",
      "age": "45",
      "city": "Hosur",
      "state": "Tamilnadu"
    }
]

 

It is a list of objects/dictionary with few fields about the vaccination status and personal details of individuals.

On a side note, If you have not vaccinated yet. Please do it. Vaccination is our only hope now.

Back to the subject.

this is a sample dataset I have taken with 4 objects/persons but you can add more if you would like to.

From this dataset, Let us create some statistics of

  • individuals with in age group of 18 to 45
  • individuals with age group 45 and above
  • who are yet to put their second dose of vaccination
  • people who are not vaccinated at all
  • who are living in red zone ( based on the active cases)
  • who are living in green zone
  • People who are living in cities

you can create N number of stats based on this dataset. but how it is related to our ansible selectattr filter.

we are going to use ansible selectattr function to create these assertions/stats.

 

Ansible Playbook with selectattr filter

Here is a playbook with ansible selectattr filter which creates these various stats for us.

we have used some nice formatting in the playbook using debug module and msg

The complete playbook runs on localhost and has only one task with some variable declaration and printing on the same place.

---
 - name: selectattr example - Vaccination Report
   hosts: localhost
   tasks:
    -  name: debug
       vars:
          - userdata: [
                        {
                          "name": "Shanmugam",
                          "gender": "male",
                          "mobile": "9875643210",
                          "dose1completed" : "yes",
                          "dose2completed" : "yes",
                          "age": "25",
                          "city": "pudukottai",
                          "state": "Tamilnadu"
                        },
                        {
                          "name": "Lakshmi",
                          "gender": "female",
                          "mobile": "9875623410",
                          "dose1completed" : "yes",
                          "dose2completed" : "no",
                          "age": "32",
                          "city": "Chennai",
                          "state": "Tamilnadu"
                        },
                        {
                          "name": "Albert",
                          "gender": "male",
                          "mobile": "9875634510",
                          "dose1completed" : "yes",
                          "dose2completed" : "yes",
                          "age": "65",
                          "city": "Coimbatore",
                          "state": "Tamilnadu"
                        },
                        {
                          "name": "Abdul",
                          "gender": "male",
                          "mobile": "9875632341",
                          "dose1completed" : "no",
                          "dose2completed" : "no",
                          "age": "45",
                          "city": "Hosur",
                          "state": "Tamilnadu"
                        },
                      ]
          - agegroup18to45: "{{ userdata | selectattr('age','>=','18') | selectattr('age','<=','45'  ) }}"
          - agegroup45above: "{{ userdata | selectattr('age','gt','45')  }}"
          - dose2pending: "{{ userdata | selectattr('dose2completed','==','no') | selectattr('dose1completed','eq','yes') }}"
          - nonvaccinated: "{{ userdata | selectattr('dose1completed','equalto','no')}}"
          - redzoneresidents: "{{ userdata | selectattr('city','in','Coimbatore,Chennai')}}"
          - greenzoneresidents: "{{ userdata | rejectattr('city','in','Coimbatore,Chennai')}}"
          - cityresidents: "{{ userdata | selectattr('city','in','Coimbatore,Chennai')}}"
       debug: 
          msg:
          - "-------------------" 
          - "Age Group 18 to 45"
          - "-------------------"
          - "{{agegroup18to45}}"
          - "-------------------"
          - "-------------------" 
          - "Age Group 45 Above"
          - "-------------------"
          - "{{agegroup45above}}"
          - "-------------------"
          - "-------------------" 
          - "Second dose pending"
          - "-------------------"
          - "{{dose2pending}}"
          - "-------------------" 
          - "Yet to Vaccinate"
          - "-------------------"
          - "{{nonvaccinated}}"
          - "-------------------" 
          - "Containment Zone"
          - "-------------------"
          - "{{redzoneresidents}}"
          - "-------------------" 
          - "Non Containment Zone"
          - "-------------------"
          - "{{greenzoneresidents}}"
          - "-------------------" 
          - "Urbanites"
          - "-------------------"
          - "{{greenzoneresidents}}"



              

Since the variables are created in the sequential order, we are able to do our all our data processing right in the vars section itself

except the userdata variable all other variables are used to create our stats reports.

- agegroup18to45: "{{ userdata | selectattr('age','>=','18') | selectattr('age','<=','45'  ) }}"
- agegroup45above: "{{ userdata | selectattr('age','gt','45')  }}"
- dose2pending: "{{ userdata | selectattr('dose2completed','==','no') | selectattr('dose1completed','eq','yes') }}"
- nonvaccinated: "{{ userdata | selectattr('dose1completed','equalto','no')}}"
- redzoneresidents: "{{ userdata | selectattr('city','in','Coimbatore,Chennai')}}"
- greenzoneresidents: "{{ userdata | rejectattr('city','in','Coimbatore,Chennai')}}"
- cityresidents: "{{ userdata | selectattr('city','in','Coimbatore,Chennai')}}"

In every single variable declaration we are using selectattr except the second last greenzoneresidents there we are using rejectattr just to give you a perspective

The Syntax of Ansible selectattr filter consists of three elements

  • attribute in an object or operand
  • comparison operator or test
  • value to compare with
selectattr('variable/attribute in an object','comparison operator / tests','Value to compare with')

Read more about comparison operators in Jinja2 here like ==, !=, >, <  etc

Read more about tests in Jinja2 here like eq, ne, ge, gt, in, not etc

In most cases the test and comparison operator do the same operation i.e eq and ==

We are printing all the variables we have created dynamically just now within the vars section with msg block with nice formatting

 

Execute and Validate the output

Here is the execution output of the playbook for your reference.

Ansible|⇒ ansible-playbook selectattr-ex1.yml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [selectattr example] ***************************************************************************************************************************************************

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

TASK [debug] ****************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        "-------------------",
        "Age Group 18 to 45",
        "-------------------",
        [
            {
                "age": "25",
                "city": "pudukottai",
                "dose1completed": "yes",
                "dose2completed": "yes",
                "gender": "male",
                "mobile": "9875643210",
                "name": "Shanmugam",
                "state": "Tamilnadu"
            },
            {
                "age": "32",
                "city": "Chennai",
                "dose1completed": "yes",
                "dose2completed": "no",
                "gender": "female",
                "mobile": "9875623410",
                "name": "Lakshmi",
                "state": "Tamilnadu"
            },
            {
                "age": "45",
                "city": "Hosur",
                "dose1completed": "no",
                "dose2completed": "no",
                "gender": "male",
                "mobile": "9875632341",
                "name": "Abdul",
                "state": "Tamilnadu"
            }
        ],
        "-------------------",
        "-------------------",
        "Age Group 45 Above",
        "-------------------",
        [
            {
                "age": "65",
                "city": "Coimbatore",
                "dose1completed": "yes",
                "dose2completed": "yes",
                "gender": "male",
                "mobile": "9875634510",
                "name": "Albert",
                "state": "Tamilnadu"
            }
        ],
        "-------------------",
        "-------------------",
        "Second dose pending",
        "-------------------",
        [
            {
                "age": "32",
                "city": "Chennai",
                "dose1completed": "yes",
                "dose2completed": "no",
                "gender": "female",
                "mobile": "9875623410",
                "name": "Lakshmi",
                "state": "Tamilnadu"
            }
        ],
        "-------------------",
        "Yet to Vaccinate",
        "-------------------",
        [
            {
                "age": "45",
                "city": "Hosur",
                "dose1completed": "no",
                "dose2completed": "no",
                "gender": "male",
                "mobile": "9875632341",
                "name": "Abdul",
                "state": "Tamilnadu"
            }
        ],
        "-------------------",
        "Containment Zone",
        "-------------------",
        [
            {
                "age": "32",
                "city": "Chennai",
                "dose1completed": "yes",
                "dose2completed": "no",
                "gender": "female",
                "mobile": "9875623410",
                "name": "Lakshmi",
                "state": "Tamilnadu"
            },
            {
                "age": "65",
                "city": "Coimbatore",
                "dose1completed": "yes",
                "dose2completed": "yes",
                "gender": "male",
                "mobile": "9875634510",
                "name": "Albert",
                "state": "Tamilnadu"
            }
        ],
        "-------------------",
        "Non Containment Zone",
        "-------------------",
        [
            {
                "age": "25",
                "city": "pudukottai",
                "dose1completed": "yes",
                "dose2completed": "yes",
                "gender": "male",
                "mobile": "9875643210",
                "name": "Shanmugam",
                "state": "Tamilnadu"
            },
            {
                "age": "45",
                "city": "Hosur",
                "dose1completed": "no",
                "dose2completed": "no",
                "gender": "male",
                "mobile": "9875632341",
                "name": "Abdul",
                "state": "Tamilnadu"
            }
        ],
        "-------------------",
        "Urbanites",
        "-------------------",
        [
            {
                "age": "25",
                "city": "pudukottai",
                "dose1completed": "yes",
                "dose2completed": "yes",
                "gender": "male",
                "mobile": "9875643210",
                "name": "Shanmugam",
                "state": "Tamilnadu"
            },
            {
                "age": "45",
                "city": "Hosur",
                "dose1completed": "no",
                "dose2completed": "no",
                "gender": "male",
                "mobile": "9875632341",
                "name": "Abdul",
                "state": "Tamilnadu"
            }
        ]
    ]
}

PLAY RECAP ******************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

Realtime Example for Ansible selectattr - AWS EFS report

The playbook we have designed earlier in this article, is designed to cover all the usage of selectattr  however we thought of giving one more realtime example.

This playbook we have written a while ago to create AWS EFS report can serve as a one best real time example of selectattr along with map filters. Do take a look.

Find EC2 instances using EFS – EFS Usage Report

 

Difference between Ansible Select and Selectattr (Additional)

Though this is not in the objective of this article, I wanted to go little deeper to give some clarity on this topic

As I have mentioned in the beginning, All Jinja2 filters can be used in Ansible.

Having said that, there are two filters named select and selectattr  in jinja2 so does in ansible.

So how they are different and where to use them. ?

Both these filters helps to filter an object from a sequence or list

One difference is that

select Filters a sequence of objects by applying a test to each object, and only selecting the objects with the test succeeding.

selectattr Filters a sequence of objects by applying a test to the specified attribute of each object, and only selecting the objects with the test succeeding.

the difference here is selectattr  runs a test on a specific attribute of the object while the select does it on the object itself.

The elements inside the list/sequence would have attributes only when they are dictionary  or proper object.

numbers: ['10','20','30']

 

the preceding list numbers  does not ( cannot) have any attributes so here you can use select to run any test on the elements directly

the following JINJA2 snippet would print numbers greater than or equal to 20 which is directly applied on the elements of list

"{{ numbers | select('ge','20') }}"

So the verdict here is that

  • select is to filter elements/objects of a simple list  [x,y,z,1,2,3]
  • selectattr is to filter elements/objects of list with nested objects [{},{},{}]

Here is the quick playbook for you to understand this practically

---
- name: filter test
  hosts: localhost
  vars: 
    numbers: ['10','20','30']
    names: [ 
      {
        "first": "Sarav",
        "last": "AK",
        "mobile": "+919876543210",
        "ctc": "200000",
        "address": {
          "city": "Chennai",
          "country": "India"
        }

      }]
  tasks:
    - name: Ansible Select vs SelectAttr
      debug: 
        msg: 
          - "==========================="
          - "With SelectAttr - Filter using Attribute name of Object"
          - "==========================="
          - '{{ names | selectattr("first", "eq", "Sarav")}}'
          - ""
          - "==========================="
          - "With Select - We are filtering using regex as the elements of list are treated as a normal string not as object/dict"
          - "==========================="
          - '{{ names | select("match", ".+first.+:.+Sarav.+")}}'
          - ""
          - "==========================="
          - "With Select - Rule is directly applied on the Object"
          - "==========================="
          - "{{ numbers | select('ge','20') }}"

Output of this playbook is given below as an image

Ansible Select vs Selectattr

Conclusion

Hope this article helps you understand the Ansible selectattr filter and how to use it  along with few other subtopics like

  • How to Pretty print or do formatting in Ansible Debug
  • How to do inversive action with rejectattr
  • How to use Jinja2 Comparison operators and tests in Ansible filters
  • What is Ansible Select and Selectattr and difference between them
  • Realtime example of Ansible Selectattr with EFS

If you have any more questions or feed back please free to update on the comments section

If you are looking for any Professional Support in Devops/SRE/Cloud Consider Gritfy

 

Cheers
Sarav AK

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