I have a list of user dictionaries that each contain username, gecos, etc, as well as a list of group dictionaries that each contain a list of group members. I need an Ansible task to loop over only the entries of the user list that match the member list of a specific group from the group list.
I suspect my problem has to do with proper use of quotations and Jinja delimiters so that everything gets evaluated at the proper level in the Ansible -> Jinja -> JMESPath hierarchy, but I haven't been able to determine exactly what I'm doing incorrectly. I've tried dozens of variants below, including breaking out the subqueries into their own variables, but without meaningful feedback I don't have much information to make improvements.
users.json:
{
"aws_users": [
{
"name": "userA",
"gecos": "User A"
},
{
"name": "userB",
"gecos": "User B"
},
{
"name": "userC",
"gecos": "User C"
}
]
}
groups.json:
{
"aws_groups": [
{
"name": "groupA",
"members": [
"userA",
"userC"
]
}
]
}
ansible.yml:
---
- hosts: 127.0.0.1
become: true
connection: local
gather_facts: no
vars_files:
- ../../config/groups.json
- ../../config/users.json
vars:
groupARoster: "{{ aws_groups | json_query('[?name==`groupA`].members | [0]') }}"
query: "aws_users[?contains(`{{ groupARoster }}`, name)]"
groupAUsers: "{{ aws_users | json_query(query) }}"
tasks:
- debug:
var: groupARoster
- debug:
var: query
- debug:
var: groupAUsers
# - name: Some looping thing
# command: ...
# loop: "{{ groupAusers }}"
Output:
PLAY [127.0.0.1] ***********************************************************************************************************************************************************************************************
TASK [debug] ***************************************************************************************************************************************************************************************************
ok: [127.0.0.1] => {
"groupARoster": [
"userA",
"userC"
]
}
TASK [debug] ***************************************************************************************************************************************************************************************************
ok: [127.0.0.1] => {
"query": "aws_users[?contains(`[u'userA', u'userC']`, name)]"
}
TASK [debug] ***************************************************************************************************************************************************************************************************
ok: [127.0.0.1] => {
"groupAUsers": ""
}
PLAY RECAP *****************************************************************************************************************************************************************************************************
127.0.0.1 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Desired Output (for passing into an Ansible loop parameter):
"groupAUsers": [
{
"name": "userA",
"gecos": "User A"
},
{
"name": "userC",
"gecos": "User C"
}
]
Unless you really want to use jmespath for this, there is an easier way IMO using selectattr
:
groupARoster: "{{ (aws_groups | selectattr('name', 'equalto', 'groupA') | list)[0].members }}"
groupAUsers: "{{ aws_users | selectattr('name', 'in', groupARoster) | list }}"
Meanwhile this is still possible with json_query
. You were really close. The problem is your intermediate query. You are passing the variable aws_user
to json_query
. Hence there is no first level "aws_user"
key in the value you pass. You have to drop it from the query:
groupARoster: "{{ aws_groups | json_query('[?name==`groupA`].members | [0]') }}"
query: "[?contains(`{{ groupARoster }}`, name)]"
groupAUsers: "{{ aws_users | json_query(query) }}"