Ansible pretty print json - json

when I'm running my playbook I get the debug output in the correct json format I would want it
"ansible_facts": {
"routes": [
{
"subnet": "10.0.0.0/24"
},
{
"subnet": "10.0.1.0/24"
}
]
},
but when I export it to a file using the local_action directive it displays like this
[{"subnet": "10.0.0.0/24"}, {"subnet": "10.0.1.0/24"}
Is there any working pretty print module or in Ansible which would export my file in the same way as I see it in the debug messages?
Thanks!

Not exactly the output you get on screen, but you could use a template task to print the variable, after passing it through a to_nice_json filter. Example:
---
- hosts: localhost
gather_facts: false
vars:
my_ansible_facts:
routes:
- subnet: 10.0.0.0/24
- subnet: 10.0.1.0/24
tasks:
- template: src=nice_yaml_filter.j2 dest=/tmp/nice_yaml_filter.out
Please note that i am not using the same variable as you are, just a my_ansible_facts variable that i populated.
And the template file's contents, nice_yaml_filter.j2:
{{ my_ansible_facts | to_nice_json }}
Result:
[http_offline#greenhat-32 ANSIBLE_TESTS]$ cat /tmp/nice_yaml_filter.out
{
"routes": [
{
"subnet": "10.0.0.0/24"
},
{
"subnet": "10.0.1.0/24"
}
]
}[http_offline#greenhat-32 ANSIBLE_TESTS]$
cheers

Related

How to import JSON file from a remote host into Ansible variable?

I am using ansible.builtin.slurp to do this. Here is the relevant part of my Ansible role:
- name: Load config
slurp:
src: "/etc/mlnx_snap/mlnx_snap.json"
register: imported_config
- name: Debug
debug:
var: imported_config
I expect to have debug print my file, however, something different happens.
Here's the file content:
root#ratchet01-snic:~# cat /etc/mlnx_snap/mlnx_snap.json
{
"ctrl": {
"sqes": 0x6,
"cqes": 0x4,
"cq_period": 3,
"cq_max_count": 6,
"nr_io_queues": 32,
"mn": "Mellanox BlueField NVMe SNAP Controller",
"sn": "MNC12",
"mdts": 4,
"oncs": 0,
"offload": false,
"max_namespaces": 0,
"quirks": 0x0
},
"backends": [ {
"type": "spdk_bdev",
"paths": [{}]
} ]
}
And here is what the debug output looks like:
Debug...
retchet01-snic.mtr.labs.mlnx ok: {
"changed": false,
"imported_config": {
"changed": false,
"content": "LyoKICogcHJ...CiAgICB9IF0KfQo=",
"encoding": "base64",
"failed": false,
"source": "/etc/mlnx_snap/mlnx_snap.json"
}
}
Content is actually thousands of characters long, I just shortened it for readability.
Imagine I have a following task further in this role:
- name: Do x
module_name:
var_name: value
when: imported_config.ctrl.quirks >= 0
How do I correctly import the JSON file so that such a task could work? What am I doing wrong?
Use from_yaml. JSON is a subset of YAML. For example
- set_fact:
content: "{{ imported_config.content|b64decode|from_yaml }}"
- debug:
msg: "{{ content.ctrl.quirks >= 0 }}"
gives
msg: true
The following helped get the raw text of JSON file:
- name: Debug
debug:
var: imported_config.content|b64decode
To further decode it into a variable, I would need to use this:
- name: Debug
debug:
var: imported_config.content|b64decode|from_json
That, however, fails for me due to invalid JSON format (it contains comments for some weird reason despite them not being allowed in JSON). But that is out of the initial scope of this question.

Ansible fetching Stdout key value

I need some help regarding, how to fetch specific value using Ansible
My task:
- name: 'check Describe Information'
debug:
var: describeresult.stdout
I need IP address value from below stdout, what should I put in debug var to fetch IP address
TASK [check Describe Information] **********************************************
task path: /home/tom/Getipaddress.yml:28
ok: [127.0.0.1] => {
"describeresult.stdout": {
"failures": [],
"tasks": [
{
"attachments": [
{
"details": [
{
"name": "subnetId",
"value": "subnet-xxxxxxxxxxxxxx"
},
{
"name": "networkInterfaceId",
"value": "eni-xxxxxxxxxxxxxxxx"
},
{
"name": "macAddress",
"value": "xxxxxxxxxxxxxxxxxx"
},
{
"name": "privateIPv4Address",
"value": "xxxxxxxxxxxxxxxxxx"
}
Plus I am using AWS ECS command to generate the above output with --output as a JSON not sure how to use --query to filter or fetch above IP address only
Use json_query. For example
- set_fact:
my_privateIPv4Address: "{{ describeresult.stdout.tasks|
json_query(query) }}"
vars:
query: "[].attachments[].details[?name=='privateIPv4Address'].value"
json_query returns by default a list.
You can use something like this for getting private IP address only
"{{describeresult.stdout['tasks'][0]['attachments'][0]['details'][3]['value']}}"
The above might have slight syntax issues but should work.
I have done something like this earlier for ec2_group_info module in ansible
You can start off by generating output in pieces describeresult.stdout['tasks'] should return JSON for the task attribute.
[0] indicates the first element when there is a box bracket for a JSON implying more than one element/list.
Let me know how it goes.

How to sort with JMESPath in ansible playbook?

I have an array returned by curl from consul during playing ansible playbook:
[
{
"Node": {
"Node": "test-eu-west-2-staging-0"
},
"Node": {
"Node": "test-nyc1-staging-0"
},
"Node": {
"Node": "test-sfo1-staging-0"
}
}
]
I want to have it sorted by "Node": {"Node" : "hostname"} in ansible playbook
- name: Set TEST vars, servers info and number
set_fact:
TEST_SERVERS="{{ test.json | json_query('SOME SORTING QUERY') }}"
TEST_SRV_NUM="{{ test.json | length }}"
I have read JMESPath and ansible docs for whole day but still don't know how to implement it (btw it is easy in jq sort_by(.Node.Node) but this trick is not applicable to ansible)
As #techraf noted, your sample JSON is malformed. I can make a guess that you have a list of objects with Node.Node inside, in this case you can use sort Jinja2 filter:
---
- hosts: localhost
gather_facts: no
vars:
myvar: [
{ "Node": { "Node": "test-c" } },
{ "Node": { "Node": "test-b" } },
{ "Node": { "Node": "test-a" } }
]
tasks:
- debug:
msg: "{{ myvar | sort(attribute='Node.Node') }}"

Ansible | Process data which can be either JSON or YAML

I'm using Ansible to read a config, which can be either JSON or YAML and extract values from some of the nodes in the file.
I know I can use from_json or from_yaml to process it in Ansible, but since I don't know which format the config will be in, I'm having difficulty making it work.
The file is Kubernetes' Kubeconfig. Examples below:
in YAML
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: REDACTED
server: https://my-k8s-cluster.com
name: k8s-clstr-master
contexts:
- context:
cluster: k8s-clstr-master
namespace: kube-system
user: k8s-clstr-master-admin
name: k8s-clstr-master
current-context: k8s-clstr-master
kind: Config
preferences: {}
users:
- name: k8s-clstr-master-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
in JSON
{
"kind": "Config",
"apiVersion": "v1",
"preferences": {},
"clusters": [
{
"name": "k8s-clstr-master",
"cluster": {
"server": "https://my-k8s-cluster.com",
"certificate-authority-data": "REDACTED"
}
}
],
"users": [
{
"name": "k8s-clstr-master-admin",
"user": {
"client-certificate-data": "REDACTED",
"client-key-data": "REDACTED"
}
}
],
"contexts": [
{
"name": "k8s-clstr-master",
"context": {
"cluster": "k8s-clstr-master",
"user": "k8s-clstr-master-admin",
"namespace": "kube-system"
}
}
],
"current-context": "k8s-clstr-master"
}
Ansible I'm using:
vars:
kubeconfig: "{{ lookup('hashivault', '/kubeconfig/admin', 'config') }}"
tasks:
- name: Find cluster server name
shell: "echo {{ kubeconfig.clusters[0].cluster.server }}"
Above Ansible block will work okay if kubeconfig is retrieved in JSON format, but it will fail if it's retrieved as in YAML format.
I might be able to make a task with |from yaml and then add ignore_errors: true, but that just doesn't feel like right way of doing it.
Anyone has any tips for me on how I can approach this problem?
There are some built-in tests in Jinja2.
The way Ansible templator works if you have JSON string inside {{...}} expression, it is automatically converted to object. So if you fetch JSON from your vault, kubeconfig becomes object, otherwise it is a string.
Here's a recipe for you:
vars:
kubeconfig_raw: "{{ lookup('hashivault', '/kubeconfig/admin', 'config') }}"
kubeconfig: "{{ kubeconfig_raw if kubeconfig_raw is mapping else kubeconfig_raw | from_yaml }}"
tasks:
- name: Find cluster server name
shell: "echo {{ kubeconfig.clusters[0].cluster.server }}"
If you use the include_vars task, it does not matter which format you provide. The task accepts both.
---
- hosts: localhost
connection: local
tasks:
- include_vars:
file: config
name: kubeconfig
- debug: var=kubeconfig

Extracting data from json within ansible

I have an ansible playbook that creates some IAM users in AWS.
I'd like the playbook to return the username and access keys details for each account created.
Registering the output from the task is fairly straightforward:
- name: Create IAM users
iam:
iam_type: user
name: "{{ item }}"
state: present
access_key_state: create
with_items:
- "user1"
- "someotheruser"
- "user3"
register: users
I'm finding doing something with that output tricky.
The Json it produces is as follows: (slightly truncated to reduce the length here)
ok: [localhost] => {
"users": {
"changed": true,
"msg": "All items completed",
"results": [
"user_meta": {
"access_keys": [
{
"access_key_id": "key1",
"access_key_selector": "HMAC",
"create_date": "2016-05-19T08:37:11.007Z",
"secret_access_key": "secretkey1",
"status": "Active",
"user_name": "user1"
}
],
}
},
{
"user_meta": {
"access_keys": [
{
"access_key_id": "key2",
"access_key_selector": "HMAC",
"create_date": "2016-05-19T08:37:13.391Z",
"secret_access_key": "secretkey2",
"status": "Active",
"user_name": "someotheruser"
}
],
}
},
{
"user_meta": {
"access_keys": [
{
"access_key_id": "key3",
"access_key_selector": "HMAC",
"create_date": "2016-05-19T08:37:16.243Z",
"secret_access_key": "secretkey3",
"status": "Active",
"user_name": "user3"
}
],
}
}
]
}
}
I've want to display this neatly on completion of the playbook, the closest I've come to getting what I'm after is use debug as follows:
- debug: var="users.results[0].user_meta.access_keys"
- debug: var="users.results[1].user_meta.access_keys"
- debug: var="users.results[2].user_meta.access_keys"
However that feels lame. What if I add user 4 and 5?
I'd like to be able to refer to the entire results array so that it will work no matter how many users I add to the playbook.
Is there a way to achieve this?
As a side note, I've also tried to use from_json, e.g.
- debug: msg="{{ (users.stdout|from_json).results }}"
but I don't know if I'm hitting a bug with ansible 2.0.1.0 or if I'm using it wrong but I could only ever get "type error. expected string or buffer"
Any thoughts gladly received.
You can iterate through the registered variable as well.
Something like this should work:
- name : debug access keys
debug : var="{{ item.user_meta.access_keys }}"
with_items: users.results
In case someone else wants to do the same thing...
I found the best solution for me was to count the number of objects in the array using 'length' and the then set the range that debug should iterate over.
I subtract "1" from the value returned since the first object in the array is referenced by "0".
- name: Record Users Access Keys
debug: var=users.results[{{ item }}].user_meta.access_keys
with_sequence: start=0 end={{users.results|length -1}}
Hope that helps someone looking to do something similar.
Ansiballer's answer worked for me, but if anyone else is looking for something simpler you can use json_query:
- name: show access keys
debug:
var: item
loop: "{{ users | json_query('results[*].user_meta.access_keys') }}"
Ansible docs on json_query
You may have to install jmespath for this to work (apt-get install python-jmespath or similar)