How to acces dictionary variable from ansible jinja2 templates? - json

i want to access the size_available variable from Dict list
"ansible_mounts": [
{
"device": "/dev/sda1",
"fstype": "ext4",
"mount": "/",
"options": "rw,errors=remount-ro",
"size_available": 15032406016,
"size_total": 20079898624
}
]
- action: debug msg="mem= {{ ansible_mounts.size_available }}"
TASK [debug] *******************************************************************
fatal: [127.0.0.1]: FAILED! => {"failed": true, "msg": "'list' object has no attribute 'size_available'"}

Can you please check this, didn't test it:
- action: debug msg="mem= {{ ansible_mounts[0].size_available }}"

Related

JMESPath filter for elements with nested values starting with certain string

I have the following JSON structure:
[
{
"stack": [
"datasync"
],
"env": [
"dev",
"test"
],
"runOnBranch": [
"feature/",
"bugfix/",
"develop"
]
},
{
"stack": [
"datasync"
],
"env": [
"val",
"prod"
],
"runOnBranch": [
"main"
]
}
]
And I would like to filter the list based on if a given string starts with one of the strings defined in the runOnBranch attribute.
My best guess so far doesn't work:
[?runOnBranch.starts_with(#, `feature/`) == `true`]
The error I get is:
"Search parse error": TypeError: starts_with() expected argument 1 to be type 2 but received type 3 instead.
The result I would like to get is:
[
{
"stack": [
"datasync"
],
"env": [
"dev",
"test"
],
"runOnBranch": [
"feature/",
"bugfix/",
"develop"
]
}
]
What am I missing?
In order to assess that there is one element in the array that starts with something you will need to:
assess each string in the array, effectively creating a list of boolean, so, with starts_with but targeting each string, not the whole array:
runOnBranch[].starts_with(#, `feature/`),
assess that there is at least one true value contained in the resulting array, with the help of the contains function
contains(runOnBranch[].starts_with(#, `feature/`), `true`)
and finally put all this in your filter
So, we end with the query:
[?contains(runOnBranch[].starts_with(#, `feature/`), `true`)]
Which yields:
[
{
"stack": [
"datasync"
],
"env": [
"dev",
"test"
],
"runOnBranch": [
"feature/",
"bugfix/",
"develop"
]
}
]
And to be more coherent in notation, this can also be written as:
[?(runOnBranch[].starts_with(#, `feature/`)).contains(#, `true`)]
Side note: simplify those kind of filter:
[?runOnBranch.starts_with(#, `feature/`) == `true`]
to
[?runOnBranch.starts_with(#, `feature/`)]
as starts_with already returns a boolean, as documented in the function signature:
boolean starts_with(string $subject, string $prefix)
Source: https://jmespath.org/specification.html#starts-with
Q: "Filter the list on attribute's runOnBranch any item starts with a given string."
A: Put the below declarations into the vars
_query: '[?runOnBranch[?starts_with(#, `feature/`)]]'
result: "{{ data|json_query(_query) }}"
gives
result:
- env: [dev, test]
runOnBranch: [feature/, bugfix/, develop]
stack: [datasync]
Example of a complete playbook for testing
- hosts: localhost
vars:
data:
- env: [dev, test]
runOnBranch: [feature/, bugfix/, develop]
stack: [datasync]
- env: [val, prod]
runOnBranch: [main]
stack: [datasync]
_query: '[?runOnBranch[?starts_with(#, `feature/`)]]'
result: "{{ data|json_query(_query) }}"
tasks:
- debug:
var: result|to_yaml
You can put the string into a variable. For example,
pattern: feature/
_query: '[?runOnBranch[?starts_with(#, `{{ pattern }}`)]]'
Then, you can override the string on the command line. For example,
shell> ansible-playbook playbook.yml -e pattern=ma
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] =>
feature|to_yaml: |-
- env: [val, prod]
runOnBranch: [main]
stack: [datasync]
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

How to append JSON array with specific data

I am trying to run :
- name: Describe config aggregator
shell: >
aws configservice describe-configuration-aggregators --configuration-aggregator-name test-config
register: config_ouput
below is the data generated.
{
"ConfigurationAggregators": [
{
"ConfigurationAggregatorName": "test-config",
"ConfigurationAggregatorArn": "arn:aws:config:us-east-1:4567:config-aggregator/config-aggregator-uw2o9pzf",
"AccountAggregationSources": [
{
"AccountIds": [
"895677"
],
"AllAwsRegions": true
}
],
"CreationTime": 1624454176.124,
"LastUpdatedTime": 1626426755.504
}
]
}
Now I want to append the accountIds above with any new account say 1234567 which should give me result such as
{
"ConfigurationAggregators": [
{
"ConfigurationAggregatorName": "test-config",
"ConfigurationAggregatorArn": "arn:aws:config:us-east-1:8778:config-aggregator/test-config-pzf",
"AccountAggregationSources": [
{
"AccountIds": [
"895677,1234567"
],
"AllAwsRegions": true
}
],
"CreationTime": 1624454176.124,
"LastUpdatedTime": 1626426755.504
}
]
}
I am trying to do is:
- name: Export results to JSON
set_fact:
config_ouput_json: "{{ config_ouput + [{"AccountIds": "1234567","AllAwsRegions": true}]}}"
but this doesn't work, please let me know the right syntax.
Basically you require bit of JSON manipulation to achieve your task.
Steps :
Store output of first command in some json file. In your case you can keep that as registered variable of ansible.
Get existing account_ids in some variable.
Create a list of new accounts as variables in ansible.
Iterate over new account_ids and add to existing account_ids.
Update the aws config command.
Sample Code :
- name: initial validation
hosts: localhost
connection: local
vars:
newAccountIds:
- "123456"
- "566544"
- "555445"
tasks:
- name: register json file
include_vars:
file: 'abc.json'
name: bundle
- name: set value
set_fact:
values: "{{ bundle['ConfigurationAggregators'][0]['AccountAggregationSources'][0]['AccountIds'] }}"
- set_fact:
values: "{{ (values | default([])) + [item] }}"
with_items: "{{ newAccountIds }}"
- debug:
msg: "{{ values }}"
- debug:
msg: '"aws configservice put-configuration-aggregator --configuration-aggregator-name test-config --account-aggregation-sources "[{"AccountIds": {{ values | to_json }},"AwsRegions": ["us-east-1"]}]\""'
Sample Output :
PLAY [initial validation] ********************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************
ok: [localhost]
TASK [register json file] ********************************************************************************************
ok: [localhost]
TASK [set value] *****************************************************************************************************
ok: [localhost]
TASK [set_fact] ******************************************************************************************************
ok: [localhost] => (item=123456)
ok: [localhost] => (item=566544)
ok: [localhost] => (item=555445)
TASK [debug] *********************************************************************************************************
ok: [localhost] => {
"msg": [
"895677",
"123456",
"566544",
"555445"
]
}
TASK [debug] *********************************************************************************************************
ok: [localhost] => {
"msg": "\"aws configservice put-configuration-aggregator --configuration-aggregator-name test-config --account-aggregation-sources \"[{\"AccountIds\": [\"895677\", \"123456\", \"566544\", \"555445\"],\"AwsRegions\": [\"us-east-1\"]}]\\\"\""}
PLAY RECAP ***********************************************************************************************************
localhost : ok=6 changed=0 unreachable=0 failed=0

How to I access JSON value from ansible output?

I have this ansible playbook which will create and attach volumes to EC2 instances using ec2_vol module and I want to partition the same using parted module. Below is my ec2_vol module,
EC2_VOL Module:
ec2_vol:
aws_access_key: XXXXXXXX
aws_secret_key: XXXXXXXX
security_token: XXXXXXXX
instance: "{{ item.instance_id }}"
region: "{{ var_dict['REGION'] }}"
device_name: "{{ VOL_NAME }}"
volume_type: gp2
volume_size: '1000'
delete_on_termination: false
with_items: "{{ ec2_instances_list.instances}}"
register: ec2_volumes
- debug:
msg: "{{ ec2_volumes.results }}"
Result of ec2_volumes:
And the result that is printing from ec2_volumes.results. I want to loop through the device_name and partition the devices which is created
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": [
{
"_ansible_ignore_errors": null,
"_ansible_item_label": {
"ami_launch_index": 0,
"architecture": "x86_64",
"block_device_mappings": [
{
"device_name": "/dev/sda1",
"ebs": {
"attach_time": "2021-04-30T15:47:07+00:00",
"delete_on_termination": true,
"status": "attached",
"volume_id": "vol-XXXXXXXXXXXXXX"
}
},
{
"device_name": "/dev/sdb",
"ebs": {
"attach_time": "2021-04-30T15:47:07+00:00",
"delete_on_termination": false,
"status": "attached",
"volume_id": "vol-XXXXXXXXXXXXXXXX"
}
}
],
And here is my parted module code:
- name: Partition Additional volumes
parted:
device: "{{ item.block_device_mappings[0].device_name }}"
number: 1
part_type: 'primary'
state: present
when: ("{{ ec2_volumes.device_name }}" != "/dev/sda1")
with_items: "{{ ec2_volumes.results}}"
But still getting error that 'block_device_mappings' has no attribute.
fatal: [localhost]: FAILED! => {"msg": "'list object' has no attribute 'block_device_mappings'"}
What could be the cause? Can someone help me to resolve this issue?
Edit 1:
Facing another issue after updating the playbook in the parted module
- name: Partition Additional volumes
parted:
device: "{{ item._ansible_item_label.block_device_mappings[0].device_name }}"
number: 1
part_type: 'primary'
state: present
when: ("{{ ec2_volumes.device_name }}" != "/dev/sda1")
with_items: "{{ ec2_volumes.results}}"
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'device_name'
The error is clear:
FAILED! => {"msg": "'list object' has no attribute 'block_device_mappings'"}
When you take a look at the list you'll see that block_device_mappings is an attribute of _ansible_item_label
"msg": [
{
"_ansible_ignore_errors": null,
"_ansible_item_label": {
"ami_launch_index": 0,
"architecture": "x86_64",
"block_device_mappings": [
...
Fix the reference
device: "{{ item._ansible_item_label.block_device_mappings[0].device_name }}"

Ansible json key output

Im using ansible 2.9.2 and i have this playbook :
register: output
- debug: msg={{ output.instance }}
which gives this output:
TASK [debug] ***************************************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"annotation": "",
"current_snapshot": null,
"customvalues": {},
"guest_consolidation_needed": false,
"guest_question": null,
"guest_tools_version": "0",
"hw_cluster": null,
"hw_datastores": [
"V1",
],
"hw_esxi_host": "10.10.101.10",
"hw_eth0": {
"addresstype": "assigned",
"ipaddresses": null,
"label": "",
"macaddress": "00:00:00:00:00:51",
"portgroup_key": null,
"portgroup_portkey": null,
"summary": "Vlan1"
How can i get the output to give me onlythe "ipaddresses": null?
I tried this :
debug: msg={{ output.instance | json_query('hw_eth0{}.ipaddresses') }}
but got an error
FAILED! => {"msg": "JMESPathError in json_query filter plugin:\\ninvalid token: Parse error at column 7, token \\"{\\" (LBRACE), for expression:\\n\\"hw_eth0{}.ipaddresses
Your jmespath expression is wrong. You can check the doc for more details
The following should work
- debug:
msg: "{{ output.instance | json_query('hw_eth0.ipaddresses') }}"
Meanwhile, you really don't need json_query in this situation where you just have to read the value in the hash:
- debug:
var: output.instance.hw_eth0.ipaddresses
Note that in the output, ansible will automatically transform the json null value to an empty string.
From the name, I guess this parameter is supposed to return a list when not empty. If you ever need to check on that in your playbook, the best practice is to verify the parameter length, e.g:
- name: Do something only when there are configured IPs
debug:
msg: There is at least one IP configured
when: output.instance.hw_eth0.ipaddresses | length > 0

Check if variable is in a list within a dictionary in Ansible

I have the following tasks:
- name: Retrieve records from Route53
route53:
command: get
zone: "{{ stag_zone }}"
record: "{{ stag_record }}"
type: A
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
register: rec
- name: Print records
debug: var=rec
- name: Record contains IP
debug: msg="{{rec}} contains {{stag_ip}}"
when: "'{{stag_ip}}' in {{rec.set.values}}"
The Print records task outputs something like this:
ok: [XXX.XXX.XXX.XXX] => {
"var": {
"rec": {
"changed": false,
"invocation": {
"module_args": "",
"module_name": "route53"
},
"set": {
"alias": false,
"record": "YYY.XXX.ZZZ.",
"ttl": "300",
"type": "A",
"value": "AAA.AAA.AAA.AAA,BBB.BBB.BBB.BBB",
"values": [
"AAA.AAA.AAA.AAA",
"BBB.BBB.BBB.BBB"
],
"zone": "XXX.ZZZ."
}
}
}
}
And I want to execute "Record contains IP" task only when {{stag_ip}} is in {{rec.set.values}}. But my when clause is definitely broken. It outputs this:
fatal: [XXX.XXX.XXX.XXX] => Failed to template {% if 'QQQ.QQQ.QQQ.QQQ' in <built-in method values of dict object at 0x7fb66f54e6e0> %} True {% else %} False {% endif %}: template error while templating string: unexpected '<'
How can I "cast" rec.set.values to a list?
The problem here is because values is a dict method. So it has "precedence" over accessing keys. To fix this, one has to explicitly call get:
when: stag_ip in rec.set.get('values')