For example, I have the following structure that ansible is pulling via an API url:
"records": [
{
"data0": "string",
"data1": {
"internal0": "string",
"internal1": {
"subthing0": "string",
"subthing1": {
"otherstuff": {
"evenmore stuff": "string"
}
}
}
}
}
]
To filter the json and get the key/value I need I am using the following:
"{{ set_var.content | from_json | json_query('records[*].data1.internal1.subthing0') }}"
The debugger returns empty json, however if I use:
{{ set_var.content | from_json | json_query('records[*].data1.internal1') }}
I can see all of the key/values of subthing1 but subthing0 no longer shows up at all and the string of subthing0 is what is a needed variable to continue the next task. Why is subthing0 not returning at all unless I only query records?
I am trying to use the ansible.builtin.lookup plugin in order to read a JSON file from the local directory and then pass it as the payload to the ansible.builtin.uri module to send a POST message to a URI endpoint.
Following are the contents of my JSON file (config.json):
{
"Configuration": {
"Components": [
{
"Name": "A",
"Attributes": [
{
"Name": "A1",
"Value": "1",
"Set On Import": "True",
"Comment": "Read and Write"
},
{
"Name": "A2",
"Value": "2",
"Set On Import": "True",
"Comment": "Read and Write"
}
]
}
]
}
}
I need to send the above JSON content as the below string in the payload to ansible.builtin.uri module:
"{\"Configuration\": {\"Components\": [{\"Name\": \"A\", \"Attributes\": [{\"Name\": \"A1\", \"Value\": \"1\", \"Set On Import\": \"True\", \"Comment\": \"Read and Write\"}, {\"Name\": \"A2\", \"Value\": \"2\", \"Set On Import\": \"True\", \"Comment\": \"Read and Write\"}]}]}}"
I am trying to use the lookup plugin with the to_json filter to read and format the JSON content. Following is my playbook:
- name: import scp
ansible.builtin.uri:
url: "https://{{ inventory_hostname }}/api/config/actions/import"
user: "{{ user }}"
password: "{{ password }}"
method: POST
headers:
Accept: "application/json"
Content-Type: "application/json"
body:
Parameters:
Type: "LOCAL_FILE"
Target: "ALL"
IgnoreCertificateWarning: "Enabled"
Buffer: "{{ lookup('file', 'config.json') | to_json }}"
body_format: json
status_code: 202
validate_certs: no
force_basic_auth: yes
However, the uri module double escapes all the new-line and tab characters. Following is the how the payload is sent when I run the playbook:
"invocation": {
"module_args": {
"attributes": null,
"body": {
"Buffer": "\"{\\n\\t\\\"Configuration\\\": {\\n\\t\\t\\\"Components\\\": [\\n\\t\\t\\t{\\n\\t\\t\\t\\t\\\"Name\\\": \\\"A\\\",\\n\\t\\t\\t\\t\\\"Attributes\\\": [\\n\\t\\t\\t\\t\\t{\\n\\t\\t\\t\\t\\t\\t\\\"Name\\\": \\\"A1\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Value\\\": \\\"1\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Set On Import\\\": \\\"True\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Comment\\\": \\\"Read and Write\\\"\\n\\t\\t\\t\\t\\t},\\n\\t\\t\\t\\t\\t{\\n\\t\\t\\t\\t\\t\\t\\\"Name\\\": \\\"A2\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Value\\\": \\\"2\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Set On Import\\\": \\\"True\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Comment\\\": \\\"Read and Write\\\"\\n\\t\\t\\t\\t\\t}\\n\\t\\t\\t\\t]\\n\\t\\t\\t}\\n\\t\\t]\\n\\t}\\n}\"",
"Parameters": {
"IgnoreCertificateWarning": "Enabled",
"Type": "LOCAL_FILE",
"Target": "ALL"
},
},
"body_format": "json",
...
},
Could you please let me know how I can format the payload with uri module? Appreciate any help.
Edited (5/11/2021):
I made the changes as suggested by #mdaniel in his response and used string filter instead of to_json. With the suggested change, I can see the JSON being formatted properly into a string with newline ('\n') and tab ('\t') characters. I tried to use the replace filter to remove the \n and \t characters. However, now the whole string is converted back into the JSON.
Following is the playbook and the output when using the string filter alone:
...
body:
Parameters:
Type: "LOCAL_FILE"
Target: "ALL"
IgnoreCertificateWarning: "Enabled"
Buffer: "{{ lookup('file', 'config.json') | string }}"
$ ansible-playbook import_file.yml -i hosts --tags
...
"body": {
"HostPowerState": "On",
"Buffer": "{\n\t\"Configuration\": {\n\t\t\"Components\": [\n\t\t\t{\n\t\t\t\t\"Name\": \"A\",\n\t\t\t\t\"Attributes\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Name\": \"A1\",\n\t\t\t\t\t\t\"Value\": \"1\",\n\t\t\t\t\t\t\"Set On Import\": \"True\",\n\t\t\t\t\t\t\"Comment\": \"Read and Write\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Name\": \"A2\",\n\t\t\t\t\t\t\"Value\": \"2\",\n\t\t\t\t\t\t\"Set On Import\": \"True\",\n\t\t\t\t\t\t\"Comment\": \"Read and Write\"\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t}\n}",
"Parameters": {
"IgnoreCertificateWarning": "Enabled",
"Type": "LOCAL_FILE",
"Target": "ALL"
},
},
Following is the playbook and the output when using replace filter in conjunction with string filter:
...
body:
Parameters:
Type: "LOCAL_FILE"
Target: "ALL"
IgnoreCertificateWarning: "Enabled"
Buffer: "{{ lookup('file', 'config.json') | string | replace('\n', '') | replace('\t', '') }}"
...
$ ansible-playbook import_file.yml -i hosts --tags
...
"body": {
"Buffer": {
"Configuration": {
"Components": [
{
"Attributes": [
{
"Comment": "Read and Write",
"Name": "A1",
"Set On Import": "True",
"Value": "1"
},
{
"Comment": "Read and Write",
"Name": "A2",
"Set On Import": "True",
"Value": "2"
}
],
"Name": "A"
}
]
}
},
"Parameters": {
"IgnoreCertificateWarning": "Enabled",
"Type": "LOCAL_FILE",
"Target": "ALL"
},
},
...
Any pointers on how I remove the \n and \t characters from the string?
You have used to_json on a dict value that is, itself, going to be to_json-ed; ansible cannot transmit a python dict over HTTP, so any yaml structure that is not already a string needs to be converted into one first
What you'll want is just that lookup result (which will return a str, not a dict) and then ansible will apply to_json to the whole body: value for the aforementioned reason
However, because ansible is trying to be "helpful", it will auto-coerce a yaml value that it finds starting with { back into a dict -- that's why you just need to send the result of lookup through the | string filter to reinforce to ansible that yes, you really do want it to remain a str in that context
...
body:
Parameters:
Type: "LOCAL_FILE"
Target: "ALL"
IgnoreCertificateWarning: "Enabled"
Buffer: "{{ lookup('file', 'config.json') | string }}"
updated answer approach
In light of the comment discussion that the dict coercion was continuing to be a problem, and the leading space concerned the OP, the alternative approach is to build up the actual payload structure completely, and only "JSON-ify" it before transmission, to keep ansible and jinja on the same page about the data types:
- name: import scp
vars:
body_dict:
Parameters:
Type: "LOCAL_FILE"
Target: "ALL"
IgnoreCertificateWarning: "Enabled"
# this will be filled in before submission
# Buffer:
whitespace_free_config_json: >-
{{ lookup('file', 'config.json')
| regex_replace('[\t\n]', '')
| string
}}
ansible.builtin.uri:
...
body: >-
{{ body_dict
| combine({"Buffer": whitespace_free_config_json})
| to_json }}
body_format: json
status_code: 202
Hello Developer Community!
I'm currently working on developing some Ansible playbooks to manage Citrix NetScaler configuration and would like to get some help about the following. I have the following data structure defined in a variable named "helpervar_get_nsvpx_nscapacity_fact":
ok: [127.0.0.1] => {
"msg": [
{
"nsadc_nscapacity": {
"actualbandwidth": "1000",
"bandwidth": "1000",
"edition": "Platinum",
"instancecount": "1",
"maxbandwidth": "100000",
"maxvcpucount": "2",
"minbandwidth": "10",
"unit": "Mbps"
},
"nsip": "10.162.237.136",
"vm_name": "NSVPX-1"
},
{
"nsadc_nscapacity": {
"actualbandwidth": "1000",
"bandwidth": "1000",
"edition": "Platinum",
"instancecount": "1",
"maxbandwidth": "100000",
"maxvcpucount": "2",
"minbandwidth": "10",
"unit": "Mbps"
},
"nsip": "10.162.237.137",
"vm_name": "NSVPX-2"
}
]
}
I have been trying to get the following expected result (I would like to get the "edition" attribute, related to a specific "nsip"):
[
"Platinum"
]
But I'm getting the following actual result:
""
I have been trying to achieve this using the following JSON query:
- debug:
msg: "{{ helpervar_get_nsvpx_nscapacity_fact | json_query(test_query) }}"
vars:
test_query: "[?(#.nsip == '10.162.237.136')].nsadc_nscapacity.edition"
I have also tried to test the above query with on-line JSON query testers and using the web-based testers, the query works, but running the query in an Ansible script shows empty result.
Could anyone help me, what could be wrong with the query?
Thank You very much in advance!
I have a json file as below
"data": [
{
"name": "first",
"foo": "123",
"item": "monday"
},
{
"name": "second",
"foo": "456",
"item": "tuesday"
},
{
"name": "third",
"foo": "789",
"item": "wednesday"}
]
}
Now I need the value of foo when item is wednesday in my ansible playbook
I used the below json query but failed to get desired output
- name: Get foo value.
set_fact:
foo_value: "{{ (json.stdout | from_json).data | map(attribute='foo') | list }}"
I need foo_value : 789 when I mention item : wednesday, please let me know what I am doing wrong
I need foo_value : 789 when I mention item : wednesday, please let me know what I am doing wrong
The first thing you are doing wrong is the failure to mention item anywhere in your expression; that said, what you are looking for is selectattr
set_fact:
foo_value: "{{ (json.stdout | from_json).data
| selectattr('item', 'eq', 'wednesday')
| map(attribute='foo') | list }}"
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') }}"