
Talin to a REST API requires to provide some information, usually in the form of JSON payload. Ansible offers various ways to do that in the URI module in playbooks.
In modern applications REST APIs are often the main API to integrate the given APP with the existing infrastructure. REST often requires posting JSON structures as payload.
Ansible offers the URI module to talk to REST APIs, and there are multiple ways add JSON payload to a playbook task that are shown below.
For example, given that the following arbitrary JSON payload needs to be provided to a REST API via POST:
{
"mainlevel": {
"subkey": "finalvalue"
}
}
The first and for me preferred way to provide JSON payloads is to write down the structure in plain YAML (if possible) and afterwards tell the module to format it as JSON:
HEADER_Content-Type: application/json
status_code: 202
body:
mainlevel:
subkey: finalvalue
body_format: json
Among various reasons this works well because variables can be easily used.
Another way is to define a variable and then use jinja to format it:
vars:
mainlevel:
"subkey": finalvalue
...
body: ' {{mainlevel|to_json}}'
Caution: not the empty space here in the body line. It avoids type detection which tries to check if a string begins with {
or [
.
Sometimes the payload is the content of a file generated somewhere else. In these cases the best way is to use the lookup plugin to read the file:
body: "{{ lookup('file','myvalues.json') }}"
Of course the lookup plugin can access data from other places as well – for example from a database or a config store, which is a nice way of integrating existing infrastructure with each other via Ansible.
A quicker, shorter way is to use folded style:
body: >
{"mainlevel":{"subkey":"finalvalue"}}
Note that folded style pastes things as they are – but ignores single new lines. So it might be difficult to add variables here.
Therefore, a little bit better suited is the literal style, indicated by the pipe:
body: |
{
"mainlevel": {
"subkey": "finalvalue"
}
}
This is probably the easiest way to deal with in many debugging situations where you need to be able to quickly change thins in your code.
Last, and honestly something I would try to avoid is the plain one-liner:
body: "{\"mainlevel\":{\"subkey\":\"finalvalue\"}}
Note, all quotation marks need to be escaped which makes it hard to read, hard to maintain and easy to introduce errors.
As shown Ansible is powerful and simple. Thus there are always multiple different ways to reach the goal you are aiming for – and it depends on the requirements what solution is the best one.
For more details I can only recommend Understanding multi line strings in YAML and Ansible (Part I & Part II) from adminswerk.de.
Update:
Added how to add body payload from existing files.
2. Update:
Added details about literal style.
Thank you!!! Out of all Google searched I did, yours nailed down my issue. I neglected the leading space in my template string. The URI module doesn’t provide any type of useful feedback when this happens, and we were pulling our hair out troubleshooting a playbook. Great article!
Hej Daniel, thanks for the feedback, great that it was of use to you!
great tip to post a JSON body without having to put a file somewhere or escape every quote
I’m glad that the post helped =)
thanks! useful stuff – and a pretty blog.
How can I specify the payload in file and provide the file name for uri module like the curl command?
That’s a very good question: use the lookup plugin:
body: "{{ lookup('file','issue.json') }}"
I’ll add that to the blog post.
lookup will work only with text files, if it is binary like zip file, upload files.
*fails
Hm, then I must admit that I have no easy solution at hand. How about you fill a bug report at Github against the lookup plugin to also cover zip files? Or how about to write your own lookup plugin?
how about the cookies jar can we have some similar to this command?
curl -v -k -b “${COOKIE_STORE_PATH}” -c “${COOKIE_STORE_PATH}” “${BASE_URL}login?action=lt
I don’t see why it should not work. After all, cookie operations are just headers, right?
I have this working but need to introduce a new key/value pair in the body that is breaking this.
YAML (not working)
accounts: “domain\\username”
JSON in CURL (working)
“accounts”:”domain\\username”,
I am getting errors because this needs to arrive in the submission with double-backslash. I assume this is because the receiving API I send this to needs it escaped. I have tried everything I can think of including: triple backslash, quad backslash, single quotes inside AND outside the double quotes. I cannot find a combination that works and I don’t see an option in Ansible URI module to ask it to show me exactly what it is sending against the API. I just keep getting an error from the receiving API indicating that it was not received in the proper format. Any ideas?
To Debug: Run your playbook with -vvv you should see wat uri sends
ansible-playbook test_uri.yml -vvv
….
“invocation”: {
“module_args”: {
“body”:
…