I set a variable from a previous play that collects a value (integer), then call it in the following task using the uri module to pass it via API. But I always get a 422 error back saying "Value should be integer".
- name: Deploy Staging Blueprint
local_action:
module: uri
url: "https://{{ server_address }}/api/application/{{app_id}}/deploy"
method: PUT
status_code: 202
validate_certs: no
headers:
Content-Type: 'application/json'
Accept: 'application/json'
AUTHTOKEN: "{{ server_session.token }}"
body_format: json
body:
version: "{{ staging.json.version }}"
run_once: true
register: deploy
changed_when: deploy|succeeded
I first tried converting it to integer with "| int" but that didnt work. I then tried "| type_debug" and validated the variable is an integer.
But I still get this error. If I replace the variable with a raw integer it works just fine. Using verbose output it still appears the value is being passed as a string.
"invocation": {
"module_args": {
"attributes": null,
"backup": null,
"body": {
"version": "48"
},
"body_format": "json",
Any idea what Im missing here or how I can work around this? Im currently running Ansible 2.4.0 for this project.
The uri module can take a pre-formatted body in JSON format. Try:
- name: Deploy Staging Blueprint
local_action:
module: uri
url: "https://{{ server_address }}/api/application/{{app_id}}/deploy"
method: PUT
status_code: 202
validate_certs: no
headers:
Content-Type: 'application/json'
Accept: 'application/json'
AUTHTOKEN: "{{ server_session.token }}"
body_format: json
body: '{ "version": {{ staging.json.version }} }'
run_once: true
register: deploy
changed_when: deploy|succeeded
Yes, this is a really strange default behavior of Ansible. It renders every value as string regardless its original type. So you end up with staging.json.version being a string instead of a numeric type.
This behavior can be altered in recent Ansible releases using the jina2_native flag globally.
Example with
test_int: 50
Try using the Jinja variable without quotes in the body parameter i.e.
body: '{ "test": {{ test_int }} }'
It works for me in Ansible 2.9.13
Related
I am trying to make a post call to the AWX API in order to register the hostname IP into the inventory.
A normal curl request that works would look like this:
curl -k -s --user "awx-user:awx-pwd" -X POST -H "Content-Type: application/json" "https://awx-api-url/api/v2/inventories/host_group_id/hosts/" --data '{ "name": "172.10.100.10", "description": "test-vm" }'
The playbook part that I am trying to use looks like this:
- name: Register host in AWX Inventory
uri:
url: "https://awx-api-url/api/v2/inventories/{{ host_group_id }}/hosts/"
method: POST
validate_certs: false
user: "{{ api_awx_username }}"
password: "{{ awx_pwd_api }}"
return_content: true
force_basic_auth: true
status_code: [200, 202]
body: '{"name": {{ guest_custom_ip }}, "description": {{ vm_hostname }}; }'
headers:
Content-Type: application/json
body_format: raw
register: reg_awx_inventory
The error I get is:
"content": "{\"detail\":\"JSON parse error - Expecting value: line 1 column 1 (char 0)\\nPossible cause: trailing comma.\"}",
I double checked the message, but it doesn't seem to be an issue with the trailing comma, any ideas? Thx.
I'm using Ansible to get a list of user emails from an API and I want to loop over them.
This is the json response I get from the API:
"users": [
{
"email": "email1#email.com",
"id": 1,
"is_admin": true
},
{
"email": "email2#email.com",
"id": 2,
"is_admin": false
},
]
edit:
the task after that which I need the email for:
- name: Send emails
register: result
uri:
url: http://api
method: GET
body_format: json
return_content: yes
body:
email: "{{ item.email }}"
scope: SCOPE
loop: "{{ users.json['users'] }}"
- name: Print result
debug:
var: result
the error I get:
fatal: [localhost]: FAILED! => {"msg": "template error while templating string: unexpected char '#' at 16. String: {{ email#email.com }}"}
If I use email: item.email the json request body will be "body": {"email": "item.email"} instead of the email value
How Can I get the full email of each user?
You need to remove the {{ and }} markers from your var: directive. The value of var is implicitly evaluated in a Jinja template context so you don't need those markers:
- name: Print returned json dictionary
debug:
var: item.email
loop: "{{ users.json['users'] }}"
(Updated based on edits to your question)
The example you've shown doesn't generate the error you're asking about. Here's a complete reproducer with just the uri changed:
- hosts: localhost
gather_facts: false
vars:
users:
json:
users:
- email: email1#email.com
id: 1
is_admin: true
- email: email2#email.com
id: 2
is_admin: false
tasks:
- name: Send emails
register: result
uri:
url: https://eny1tdcqj6ghp.x.pipedream.net
method: GET
body_format: json
return_content: true
body:
email: "{{ item.email }}"
scope: SCOPE
loop: "{{ users.json['users'] }}"
This runs without errors (although note that I'm suspicious about the
use of a GET request with a JSON body; typically you would expect
that to be a POST request).
You can see the result of running this playbook here, which shows that the values being sent in the request are exactly what we expect from the data.
I have a config_file with values in the format: key-value pair
e.g. a: "{\"type\": \"some_type\"}"
Everything is ok. Util I tried to pass this JSON value as value inside uri utility:
- name: 'Push config values'
uri:
url: '{{ uri }}/api/{{ item.key }}'
method: POST
body: '{"alias":"{{ item.key }}","key":"{{ item.key }}","value":"{{ item.value }}"}'
body_format: json
validate_certs: no
status_code: 204
headers:
X-Token: "{{ token }}"
with_dict: '{{ config_overrides_file }}'
register: response
I tried different way of escaping, also filter combination | from_json | to_json. But nothing is working. I got 'failed to parse JSON input: invalid character ''t'' after object key:value pair' in case of escaping
Probably, exist some standard approach to handle it?
I found just one way to achieve what I want: instead of a: "{\"type\": \"some_type\"}" this value in config file it could be a: {"type": "some_type"} and in uri it will be body: {"alias":"{{ item.key }}","key":"{{ item.key }}", "value": {{ item.value | to_json }}}. It's working approach but I would like not create additional task for json and non-json properties for such type of request.
P.S. When I hardcode this value everything is ok
You're getting bitten by ansible's "helpful" behavior of coercing any string it finds that starts with { back into a dict; the usual fix for that is to send it explicitly through |string to indicate to ansible you do not wish for that to happen. For sure any loop-ish behavior on a task (with_dict: being one of them) makes that problem much worse, because ansible gets really, really weird about coercion and recursive jinja2 templates in that circumstance
- name: test data for SO
set_fact:
config_values_file:
a: '{"type": "some_type"}'
- name: 'Push config values'
uri:
url: '{{ uri }}/api/{{ item.key }}'
method: POST
body: >-
{{ {"alias": item.key,
"key": item.key,
"value": item.value|string
} }}
body_format: json
validate_certs: no
status_code: 204
headers:
X-Token: "{{ token }}"
with_dict: '{{ config_overrides_file }}'
register: response
POSTs:
{"alias": "a", "key": "a", "value": "{\"type\": \"some_type\"}"} which I believe is what you were asking for
I want to push ios_facts to gitlab using ansibles uri module.
- name: get ios facts
ios_facts:
gather_subset: all
register: ios_facts
- name: commit to gitlab
delegate_to: localhost
uri:
url: http://gitlab/api/v4/projects/2/repository/commits
method: POST
body_format: json
status_code: 201
headers:
PRIVATE-TOKEN: "xxxxxxxxxxxxxx"
Content-Type: "application/json"
body: |
{
"branch": "master",
"commit_message": "{{ ansible_net_hostname }} update",
"actions": [
{
"action": "update",
"file_path": "conf/{{ ansible_net_hostname }}",
"content": "{{ ansible_net_config }}"
}
]
}
The Playbook works fine if I am using any other variable than ansible_net_config, or if I am pasting the raw content of ansible_net_config instead of using the jinja2 reference. The ansible_net_config is a large string using \n as new line and contains some special characters. I guess the problem occurs because I don't get valid json when the playbook parses.
I then get the HTTP Error 400: Bad Request
Is there any filter I can apply or any other thing I might be missing out?
I managed to solve the issue:
The API Call failed when the variable contains "\n". I could make it work by replacing "\n" with escaped "\\n":
...
"content": {{ ansible_net_config | replace('\n','\\n') }}
...
I want to make a POST request to an API endpoint via Ansible where some of the items inside the post data are dynamic, here is what I try and fail:
My body_content.json:
{
apiKey: '{{ KEY_FROM_VARS }}',
data1: 'foo',
data2: 'bar'
}
And here is my Ansible task:
# Create an item via API
- uri: url="http://www.myapi.com/create"
method=POST return_content=yes HEADER_Content-Type="application/json"
body="{{ lookup('file','create_body.json') | to_json }}"
Sadly this doesn't work:
failed: [localhost] => {"failed": true}
msg: this module requires key=value arguments
....
FATAL: all hosts have already failed -- aborting
My ansible version is 1.9.1
You can't use newlines like this in yaml. Try this instead (the ">" indicates that the next lines are to be concatenated):
# Create an item via API
- uri: >
url="http://www.myapi.com/create"
method=POST return_content=yes HEADER_Content-Type="application/json"
body="{{ lookup('file','create_body.json') | to_json }}"
But I find this much better:
# Create an item via API
- uri:
url: "http://www.myapi.com/create"
method: POST
return_content: yes
HEADER_Content-Type: "application/json"
body: "{{ lookup('file','create_body.json') | to_json }}"
I'm posting below what I ended up using for my usecase (Ansible 2.0). This is useful if your json payload is stated inline (and not in a file).
this task expects 204 as its success return code.
And since the body_format is json, the header is inferred automatically
- name: add user to virtual host
uri:
url: http://0.0.0.0:15672/api/permissions/{{ rabbit_virtualhost }}/{{ rabbit_username }}
method: PUT
user: "{{ rabbit_username }}"
password: "{{ rabbit_password }}"
return_content: yes
body: {"configure":".*","write":".*","read":".*"}
body_format: json
status_code: 204
it is basically equivalent to:
curl -i -u user:pass -H "content-type:application/json" -XPUT http://0.0.0.0:15672/api/permissions/my_vhost/my_user -d '{"configure":".*","write":".*","read":".*"}'