Using Ansible for executing API calls with URI Module

Ansible is one of powerful tool, which have use cases ranging from remote execution of health check scripts, system life cycle managements and DevOps pipeline via webhooks with Git.

All these can be done by accessing remote nodes on simple ssh protocol. However, in this particular blog I will be using Ansible URI module to execute API calls on HTTP protocol, including one of latest use cases added only after ansible 2.10 onwards, File upload via ansible.

For testing our playbook, we need a http based application running as webserver either via nginx or apache etc. While test system may allow API access via username and password, production grade application use more secured “Token” based authentication before allowing advanced PUT, POST or DELETE calls.

Authentication Token Based Access

While some of parameters may vary for different end application, below are fields which I used for requesting token from application. You may refer your own application’s API documentation to understand what is required.

---
- name: Get Auth Token
  hosts: localhost

  tasks:
  - name: Get Token
    uri:
      validate_certs: no
      url:   https://<IP>/**TOKEN REQUEST**/ 
      user: <USERNAME>
      password: <PASSWORD>
      method: POST
      body_format: form-urlencoded
      return_content: true
      headers:
        Content-Type: application/x-www-form-urlencoded
      body:
        grant_type: password
        username: <USERNAME>
        password: <PASSWORD>
        scope: offline_access
        client_id: <CLIENTID>
    register: data

Running above playbook will result in an auth token. This token may be reused from now onwards for each subsequant call, however copy pasting same in new playbook is cumbersome and defy purpose of automation. Hence, I will use set_fact and debug modules in ansible to extract token from result and assign name to it. Its similar to using alias in linux.


  - set_fact: access_token="{{ (data.content|from_json).token_type }} {{ (data.content|from_json).access_token }}"
  - debug:
      msg: "{{ access_token }}"

Basically, I am asking ansible to fetch access_token and token_type from json output of “Get Token” task. Because I am using bearer token for my auth, I need token type as well.

Uploading File Using URI Module

Now we already have bearer token with us, I will add another task in my playbook to upload file on my remote webserver using real time auth token generated in previous task.

For file upload to work, we need to use form-multipart body format in API call, which enables file to upload on remote server.

  - name: Upload Package
    uri:
      validate_certs: no
      url: https://<IP>/**FILEUPLOAD**/
      method: PUT
      return_content: true
      headers:
        Authorization: "{{ access_token }}"
        Accept: application/json
        Content-Type: multipart/form-data
      body_format: "form-multipart"
      body:
        archive:
          filename: /home/dmagnate/myfile.zip
          mime_type: application/octet-stream
        text_form_field: value
    register: upload

You can see I called access_token in authorization which basically have my token_type and token_value from previous task. I had to provide mime_type else you may get below error,

"developerMessage": "java.io.IOException: RESTEASY007550: Unable to get boundary for multipart",

Another common error is when we put incorrect body_format. You may encounter below error for it.

    "msg": "Status code was -1 and not [200]: An unknown error occurred: can't concat str to bytes",

Post execution of playbook, file is successfully uploaded on webserver via API call.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s