I struggle on a regular basis with data manipulation in Ansible. I’m not very familiar with Python and dict objects. I found an example that sums up a lot of my misunderstandings.
I would like to verify a list of certificates. In found an example for a single domain in the documentation, I’m just trying to loop over several domain names.
Certs are stored in a folder:
certs/
├── domain.com
│ ├── domain.com.pem
│ └── domain.com.key
└── domain.org
├── domain.org.key
└── domain.org.pem
My playbook is as follow:
---
- name: "check certs"
hosts: localhost
gather_facts: no
vars:
domain_names:
- domain.com
- domain.org
certs_folder: certs
tasks:
- name: Get certificate information
community.crypto.x509_certificate_info:
path: "{{ certs_folder }}/{{ item }}/{{ item }}.pem"
# for valid_at, invalid_at and valid_in
register: result_certs
loop: "{{ domain_names }}"
failed_when: 0
- name: Get private key information
community.crypto.openssl_privatekey_info:
path: "{{ certs_folder }}/{{ item }}/{{ item }}.key"
register: result_privatekey
loop: "{{ domain_names }}"
failed_when: 0
- name: Check cert and key match << DOES NOT WORK >>>
assert:
that:
- result_certs[ {{ item }} ].public_key == result_privatekey[ {{ item }} ].public_key
# - ... other checks ...
- not result[ {{ item }} ].expired
loop: "{{ domain_names }}"
So I get two variables result_certs
and result_privatekey
, each has a element result
which is , if I understand correctly, an array of dicts:
"result_certs": {
"changed": false,
"msg": "All items completed",
"results": [
{
"expired": false,
"item": "domain.org",
"public_key": "<<PUBLIC KEY>>"
},
{
"expired": false,
"item": "domain.com",
"public_key": "<<PUBLIC KEY>>"
}
],
"skipped": false
}
"result_privatekey": {
"changed": false,
"msg": "All items completed",
"results": [
{
"item": "domain.org",
"public_key": "<< PUBLIC KEY >>"
},
{
"item": "domain.com",
"public_key": "<< PUBLIC KEY >>"
}
],
"skipped": false
}
How can I refer to each of the dicts elements like result_privatekey.results[the dict where item ='domain.org'].public_key
in the assert
task?
I feel like I’m missing something, or a documentation page to make it clear to me. I noticed that I particularly struggle with arrays of dicts, and I run into those objects quite often…
I found those resources useful, but not sufficient to get this job done:
EDIT:
map
and selectattr
are the filters required to solve this problem, although the documentation (including the official ansible doc) is not that clear to me… This is very useful to get many tutorial examples on those two filters if one is struggling as I do.
2
Answers
From what you are showing — but you might have more conditions that needs it — I wouldn’t loop on the
domain_names
in your assertion task, I would rather loop onresult_certs
.From there on, you can select the corresponding private key thanks to the
selectattr
filter.So, your assertion would become:
Given the simplified data for testing
Declare the list of the domains
gives
Q: "How can I refer to each dictionary element?"
A: select the item(s) and map the attribute
gives
In the same way, you can compare the keys in the loop
gives
Q: "Extract and manipulate dict data to check certificates."
A: There are many options:
gives
To find the redundant keys compare the lists pkeys and domains. Compare the lengths to briefly find out if there are any
To find the expired domains declare variables
give
Then the assert task should look like
Example of a complete playbook for testing
gives
gives
Use this structure to compare any attributes. For example, declare
gives
Then, use it in the conditions
Example of a complete playbook for testing
gives