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.