Ansible is a top open source project which, on the surface, looks to provide a simple way to standardize your existing automation and allow it to run in parallel across multiple hosts, and it does this very successfully. Yet, in reality, Ansible has the capabilities to extend what your existing automation does to incorporate other systems and really simplify tasks across all aspects of your daily routine.
This capability starts with the collections and roles that are included with Ansible and all the third-party utilities distributed through Ansible Galaxy. You may have queried APIs with a web browser or curl, but one of the overlooked capabilities of Ansible is how well it can leverage APIs as part of any playbook. This is extremely useful because the number of REST APIs being built and deployed both internally and across the global internet is increasing exponentially. There's even a public-apis GitHub repo listing hundreds of free APIs across over a dozen categories just for a sense of scale.
A basic API playbook
Well, it really comes down to a few key core capabilities within Ansible, which are exposed nicely with one specific built-in task, uri. In this post, I'll go through a fairly simple example of how to call a REST API and use the data from that call to decide what to do next. This works with Ansible 2.9 and higher. In later versions (specifically v4), the modules we use need to be prepended with ansible.builtin like ansible.builtin.set_fact instead of just set_fact.
To get started, you need a basic playbook to build on. In this case, you're only using local calls, so you don't need to be a superuser.
First, create this YAML file to establish a working baseline:
---
- name: Using a REST API
become: false
hosts: localhost
gather_facts: false
tasks:
- debug:
msg: “Let’s call an API”
Here's the output after running it:
% ansible-playbook using-a-rest-api.yml
PLAY [Using a REST API] *********************************************************************************************
TASK [debug] ********************************************************************************************************
ok: [localhost] => {
"msg": "“Let’s call an API”"
}
PLAY RECAP **********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Calling an API
To call an actual API, you can use the uri module. Here are two examples. The first is just a GET and the second is a POST with parameters to show the different available options.
---
- name: Everyone loves a good Chuck Norris joke
uri:
url: https://api.chucknorris.io/jokes/random
method: GET
- name: Login to an API
uri:
url: https://auth.example.com/oauth/access_token
method: POST
body_format: json
body:
name: your_username
password: your_password
client_id: YOUR_CLIENT_ID
access_token: ACCESS_TOKEN
connection: CONNECTION
scope: SCOPE
I use the first API for the rest of this article to show how the returned data can be used. The question is, how do you collect the data being returned, and what does it look like?
[ Advance your automation expertise. Get the Ansible checklist: 5 reasons to migrate to Red Hat Ansible Automation Platform 2 ]
To collect the output from any task running in Ansible, you use the register attribute, and then you can use the debug task to display the raw data. In the case of APIs called using uri, all the output is put under the .json. Subsection of the result. The uri commands and other its output are also at that top level. These can be useful to make sure the API call works by looking at other data fields like status.
These are the two tasks you must add to the original playbook to add the API call to the mix to later do something with.
- name: Getting the definition of awesome
uri:
url: https://api.chucknorris.io/jokes/random
method: GET
register: results
- debug:
var: results
Run it to see the output generated by debug:
TASK [debug] ********************************************************************************************************
ok: [localhost] => {
"results": {
"alt_svc": "h3=\":443\"; ma=86400, h3-29=\":443\"; ma=86400, h3-28=\":443\"; ma=86400, h3-27=\":443\"; ma=86400",
"cf_cache_status": "DYNAMIC",
"cf_ray": "694f7d791aeb19e7-EWR",
"changed": false,
"connection": "close",
"content_type": "application/json;charset=UTF-8",
"cookies": {},
"cookies_string": "",
"date": "Sun, 26 Sep 2021 21:12:23 GMT",
"elapsed": 0,
"expect_ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"",
"failed": false,
"json": {
"categories": [],
"created_at": "2020-01-05 13:42:26.991637",
"icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
"id": "IjqNNWKvSDeVKaI82PaT1g",
"updated_at": "2020-01-05 13:42:26.991637",
"url": "https://api.chucknorris.io/jokes/IjqNNWKvSDeVKaI82PaT1g",
"value": "One person stated that Chuck Norris has forgotten more about killing than anyone will ever know. That is not true -- Chuck Norris never forgets. Ever."
},
"msg": "OK (unknown bytes)",
"nel": "{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}",
"redirected": false,
"report_to": "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=HVPJYMVr%2B3wB1HSlgxv6GThBMjkBJgfdu0DPw%2BunjQzQ9YfXZqifggIJ%2FxOIKgOu6JP1SrPsx1jCCp3GQ9hZAp7NO0pmlTZ0y3ufbASGwLmCOV1zyaecUkSwQD%2Fv3RYYgZTkaSQ%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}",
"server": "cloudflare",
"status": 200,
"transfer_encoding": "chunked",
"url": "https://api.chucknorris.io/jokes/random",
"via": "1.1 vegur"
}
}
Now that you can see all the output make a custom message listing the value returned by the API. Here is the completed playbook:
---
- name: Using a REST API
become: false
hosts: localhost
gather_facts: false
tasks:
- debug:
msg: “Let’s call an API”
- name: Everyone loves a good Chuck Norris joke
uri:
url: https://api.chucknorris.io/jokes/random
method: GET
register: results
- debug:
var: results.json.value
And now the complete output:
PLAY [Using a REST API] *********************************************************************************************
TASK [debug] ********************************************************************************************************
ok: [localhost] => {
"msg": "“Let’s call an API”"
}
TASK [Everyone loves a good Chuck Norris joke] **********************************************************************
ok: [localhost]
TASK [debug] ********************************************************************************************************
ok: [localhost] => {
"results.json.value": "Chuck Norris is the only computer system that beats a Mac or a PC. Too bad all it does is round house kicks the user."
}
PLAY RECAP **********************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Next steps
Things can get much more complicated than I've shown here. To get more details, head over to Ansible's documentation.
Comments are closed.