2
votes

Using ansible 2.5.3 on ubuntu 16.10.

I need to include a variable in a JSON string for the body tag of the Ansible uri module. I have tried various things. My latest try looks like this in the below task in a role:

- name: REST POST Example
  uri:
    url: "{{ webapp_url }}/api/orgs"
    method: POST
    return_content: yes
    body: "{ \"name\": \"{{ project_name }}\" }"
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes
    headers:
      Content-Type: "application/json"

In my playbook I define the project_name variable:

---

- hosts: all
  gather_facts: no
  vars:
      project_name: "SAMPLE_PROJECT"

But when I run the playbook the project_name variable does not seem to get expanded correctly:

An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TypeError: unhashable type
fatal: [localhost]: FAILED! => {"changed": false, "content": "", "msg": "Status code was -1 and not [200]: An unknown error occurred: unhashable type", "redirected": false, "status": -1, "url": "https://webapp/api/orgs"}

In the above role/task. If I hardcode the body like:

body: "{\"name\": \"SAMPLE_PROJECT\"}"

it works fine. But I cannot do that I need the variable in there. Any suggestions on how to fix:

body: "{ \"name\": \"{{ project_name }}\" }"

?

2

2 Answers

5
votes

Set json as the body type inside body_format parameter:

- name: REST POST Example
  uri:
    url: "{{ webapp_url }}/api/orgs"
    method: POST
    return_content: yes
    body: "{ \"name\": \"{{ project_name }}\" }"
    body_format: json
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes

When you use body: "{\"name\": \"SAMPLE_PROJECT\"}", the value is represented internally as a string (AnsibleUnicode).

When you use body: "{ \"name\": \"{{ project_name }}\" }", the value becomes a dictionary (dict), because it goes through the templating engine, after which the output is mapped into a Python object (it doesn't happen with plain strings).

Trivia: you can still trick Ansible to think it is a string by adding a space before first {. See the answers here.

uri module uses body_format: raw by default and it expects a string, if it finds an object it fails; refer to this issue.

1
votes

Ansible ca interpret YAML as json automatically, I recommand the method below :

- name: REST POST Example
  uri:
    url: "{{ webapp_url }}/api/orgs"
    method: POST
    return_content: yes
    body:
      name: "{{ projectname }}"
    body_format: json
    validate_certs: no
    user: "{{ user }}"
    password: "{{ password }}"
    force_basic_auth: yes