Ansible URI module Examples - How to use Ansible URI | Devops Junction

Ansible has become a widely accepted automation platform not just for Infrastructure Automation but for various other use cases

Some of them are Web and API automation, Monitoring Automation, Cloud Automation and Infrastructure as a code like Terraform

In this article, we are going to see how to use Ansible for Web and API automation.

Ansible has two powerful modules for WebService and API automation. they are

  • get_url / win_get_url - to download files from HTTP/HTTPS and FTP sites
  • uri / win_uri - to interact with web service ( we are going to discuss in this article)

While the get_url module is simply to interact with web and FTP sites and download files.

The URI module is much more powerful to interact with API and Webservices, You can use the URI module for Complete API monitoring and Automation.

ansible uri

Ansible URI module Quick Examples

Ansible URI module can be used for simple use cases like checking the status of the web pages and validating the status code as well as complex use cases such as API automation with different HTTP methods and payloads.

Before we start to go deep into the URI module, Let me give two quick examples of Ansible URI to show you what is ansible URI module capable of.

Ansible URI used for Simple Status code check

---
- name: Playbook for Web automation 
  hosts: localhost
  tasks:
  - name: Check if devopsjunction.com is available and returning status 200
    uri:
      url: https://devopsjunction.com
    register: result

 

Ansible URI module used for API or WebService testing

---
- name: Playbook for Web automation 
  hosts: localhost
  tasks:
  - name: Ansible URI used for user registration
    uri:
      url: https://reqres.in/api/users
      method: POST
      body: |-
        {
          "name": "Sarav",
          "job": "leader"
        }
      return_content: yes
      status_code: 201

 

Both these playbooks can be tested from your end as we have used publically available URLs/ Webservices.

We will decode these playbooks in detail shortly.

Ansible URI parameters

These are the list of parameters supported by the Ansible URI module. I have compiled all the parameters of Ansible URI and their usage, in the following image

Please go through the list, we would be using most of these parameters in our ansible URI module example playbooks shortly.

Ansible URI examples

Let us explore and try a few examples of this Ansible URI module.  we will start with the ones from the previous Quick examples section.

 

Beginner to Ansible? No Problem

Unless you are new to Ansible, you must already be aware of the basic skeleton of the Ansible playbook.

If you are completely new to the Ansible playbook, I would refer you to read our Beginner Articles on Ansible by following the below links

We are presuming that you know the basics of the Ansible playbook, therefore we are going to decode only the tasks.

Let us move on to the examples of the Ansible URI module.

 

Ansible URI Examples

Let us go through various examples and learn and understand the Ansible URI module better

 

Ansible URI module to connect and validate status code

This was our first playbook from the Quick example section, it is to connect to URL and to validate if it is alive by validating the returning status code 200

---
- name: Playbook for Web automation 
  hosts: localhost
  tasks:
  - name: Check if devopsjunction.com is available and returning status 200
    uri:
      url: https://devopsjunction.com
    register: result

we have one task in the playbook, with a URI module.

- name: Check if devopsjunction.com is available and returning status 200
    uri:
      url: https://devopsjunction.com
    register: result

In this task, we are connecting to the remote URL https://devopsjunction.com and validating whether it's alive and responding with HTTP status code 200

If you look at the playbook, nowhere we are defining the status code to look for.

If status_code directive is not specified.

Ansible, by default, would consider status code 200 as a success, anything else would fail the playbook

In cases, where the remote URL is supposed to return a NON 200 status code, like 301 or 302 we must explicitly define what is the expected status code with the status_code  parameter

Here is the execution output of this playbook

ansible uri

You can see the playbook execution is successful and passed.  This is a simple ansible playbook to check if a website or webservice is up and running and returning a specific status code.

Let's suppose we want the URI module to succeed only when the remote URL is returning the status HTTP 301

As we have already discussed, we need to explicitly specify it with the status_code parameter, as shown in the following playbook

---
- name: Ansible URI module examples
  hosts: localhost
  tasks:
  - name: Check if devopsjunction.com is available and returning status 301
    uri:
      url: https://devopsjunction.com
      status_code: 301
    register: result

Here is the execution output of this new playbook

ansible uri status code

Let us move on.

 

The Ansible URI module follow redirection

Our previous playbook from example 1, should have ideally failed but it got succeeded. Let me tell you why.

If you try the https://devopsjunction.com directly through CURL, you would see it returning 301 status code

~|⇒ curl https://devopsjunction.com -I
HTTP/2 301
location: https://middlewareinventory.com/devops-junction
cache-control: max-age=3600
expires: Sat, 12 Nov 2022 13:28:56 GMT
content-type: text/html; charset=iso-8859-1
date: Sat, 12 Nov 2022 12:28:56 GMT
server: Apache

in that case, our playbook should have not succeeded right? but it has. why?

The reason is follow_redirection . By default, Ansible URI follows if there is a Safe redirection and consider the last hop's status.

In our case, despite the first hop was returning 301 once it has completed the redirections. the end result was 200 that's what made our previous playbook succeed.

To understand this better. You can use the follow redirection option available on the CURL itself. using the -L flag

Here is the output of CURL with follow redirection for devopsjunction.com. You can see the final status code is eventually 200.

~|⇒ curl -L https://devopsjunction.com -I
HTTP/2 301
location: https://middlewareinventory.com/devops-junction
cache-control: max-age=3600
expires: Sat, 12 Nov 2022 16:55:36 GMT
content-type: text/html; charset=iso-8859-1
date: Sat, 12 Nov 2022 15:55:36 GMT
server: Apache

HTTP/2 301
date: Sat, 12 Nov 2022 15:55:37 GMT
content-type: text/html; charset=UTF-8
location: https://www.middlewareinventory.com/devops-junction
x-redirect-by: WordPress
vary: X-Forwarded-Proto,Accept-Encoding,User-Agent
......
......

HTTP/2 301
date: Sat, 12 Nov 2022 15:55:37 GMT
content-type: text/html; charset=UTF-8
location: https://www.middlewareinventory.com/devops-junction/
x-redirect-by: WordPress
vary: X-Forwarded-Proto,Accept-Encoding,User-Agent
cache-control: max-age=3600
......
......

HTTP/2 200
date: Sat, 12 Nov 2022 15:55:38 GMT
content-type: text/html; charset=UTF-8
vary: Accept-Encoding,Cookie
last-modified: Thu, 10 Nov 2022 19:39:14 GMT
cache-control: max-age=0, public
......
......

This is exactly what Ansible did. It followed the redirections.

You can control whether or not to follow the redirects with a parameter follow_redirects

If you set the follow_redirects to none, Ansible URI would not follow the redirects and our playbook would fail.

Look at the playbook and the execution output of both with and without follow_redirects

Here is the execution output of the playbook without follow_redirects parameter.

Ansible assumes to follow the redirects by default ( this is the same playbook from previous example)

The output of the playbook with follow_redirects parameter set to none 

we instruct ansible to not follow redirects this time and it fails.

ansible uri json

hope you have understood the crucial role of follow_redirects in the Ansible URI module.

 

Ansible URI - How to work with body of the response or page content

the last two examples we have seen on Ansible URI were around the status code. sometimes we might want to work with the content of the web page.

In this example, we are going to see how Ansible URI handles the content of the webpage or the API response

Let us see it with a practical example, Let's suppose we want to know the public IP address of the node where the Ansible task is being executed. ( controller or remote)

Based on that result, we might want to execute or skip a downward task

Look at the following playbook where we connect to the remote URL https://checkip.amazonaws.com  this returns ( response/content) the public IP of the origin ( from where the call has been made)

---
- name: Ansible URI module examples
  hosts: localhost
  tasks:
  - name: Check the outbound IP address
    uri:
      url: https://checkip.amazonaws.com 
      return_content: true
    register: result

  - name: Creating a new variable to store this IP
    set_fact:
      publicip: "{{ result.content | trim() }}"

  - debug: 
      var: publicip

 

Here is the execution output of this playbook

ansible uri return content

If you look closer, you would be able to see we are using a parameter return_content and it is set to yes

Why do we need return_content on Ansible?

Without the return_content parameter, Ansible would not collect the response content or page content.

To validate this you can try the same playbook without the return_content parameter.

ansible uri return_content

As shown in the preceding snapshot, we have removed the return_content and the task is failing.

If you look at the highlighted text on the failure message. you can see there is no attribute named content

By default, ansible does not collect the entire page response or the content it is more like using curl -I  where curl collects only the headers, not the content.

 

Ansible URI for API Automation - JSON payload

So far we have been using only web services with ansible URI.  Let us start to use API endpoints and different content-type and payloads.

Let us start with the famous JSON payload. JSON has become an indispensable element in API and Webservices.

In this example, we are going to use a publicly available API endpoint reqres.in

---
- name: Ansible URI examples
  hosts: localhost
  tasks:
  - name: Ansible URI playbook for API with JSON Payload.
    uri:
      url: https://reqres.in/api/login
      method: POST
      body_format: json
      body: |-
        {
          "email": "[email protected]",
          "password": "cityslicka"
        }
      return_content: yes
    register: result
    changed_when: "'token' in result.content"

  - debug: var=result.content

As you can see, our preceding playbook is designed to send a payload using body parameter

body: |-
        {
          "email": "[email protected]",
          "password": "cityslicka"
        }

Unless defined, this payload would be considered as body raw not as JSON

so you need to define the right interpretation of our body

body_format: json

Since we care about the response content. we are using return_content: yes  which is something we have learnt in the previous examples.

We are also defining the method of the HTTP as POST by default Ansible would consider the request as GET  when you are making a wrong method of call, you might get HTTP 400 error.

Finally, the expected response from this API endpoint is a token. If our credentials are accepted the API should present us with a token.

We are ensuring that we get a token in our response

The reason for using changed_when is to ensure that the job is considered successful only when a certain condition is met.

You can alternatively use failed_when as well in this context with inverted condition.

Read my exclusive article on changed_when and failed_when here

execution output of our playbook is here

ansible uri json

 

Ansible playbook to log in to Form-based Authentication - Ansible URI

In the previous example, we have seen how to login to API and obtain a token by sending a payload over HTTP POST using Ansible URI

In this example, we will see how to log in to the form-based Authentication enabled Web Application.

As I could not find a simple Application to demonstrate I ended up creating one

It is a simple Form based Login App designed on Flask. you can find the source code in my GitHub repo

https://github.com/AKSarav/login-form-app-sample

You can download the source code and host it yourself on your locally.

Or use the readily available public Heroku link of this application

https://form-login-webapp.herokuapp.com/

Note*: This URL might change or expire this is hosted on HerokuApp free plan.

 

Before we automate this with Ansible, let us take a quick look at how this application works when tried from the browser.

ansible login form based webapp

As shown in the preceding GIF, we are entering the credentials admin/admin and clicking on the Login button.

After successful login, we can see a message you have successfully logged in

If you put in the wrong credentials, you would be redirected to the login form once again with an error message.

ansible uri form

Here is the Ansible playbook to Login to the form based Authentication enabled Web App

---
  - name: Ansible URI examples
    hosts: localhost
    tasks:
    - name: Login to WebApplication with Ansible
      uri:
        url: https://form-login-webapp.herokuapp.com
        method: POST
        body_format: form-urlencoded
        follow_redirects: all
        body:
        - [ username, admin ]
        - [ password, admin ]
        - [ enter, Login]
        return_content: yes
      register: login
      changed_when: "'you have successfully logged in' in login.content"

    - debug: var=login.content

Let us decode the important fields of this playbook

  • url - URL of our form-based login application
  • method - we are using HTTP POST to send our username and password
  • body_format - to send the credentials we should use form-urlencoded body format
  • follow_redirects - we are following through all the redirects until we get to the home screen
  • body - In this form-urlencoded body we need to define what is the key and the value for each form item
    • username - we are passing admin as our username
    • password -  we are passing admin as our password too.
    • enter - where to click. A button name to submit. in our case it is Login
  • return_content:  we are enabling return_content to see the response content
  • changed_when:  Just to let Ansible know when to consider this task successful. in our case, when we have the message you have successfully logged in on the response content

 

How to wait for the URL to be reachable or until it returns a Status code or Content

As we have seen various examples already on Ansible URI. we might want to add some conditional execution on it

Like making it wait until it returns a specific status code or message etc.

I have a dedicated article on the same. please refer the following link

Ansible Wait for URL to respond or Retry – WEB and API | Devops Junction

 

More examples are coming

I have already put a few days/weeks of effort already in compiling this article and creating necessary supporting artefacts like the cheat sheet, the flask application etc.

I still feel there is more to cover in Ansible URI such as

  • File upload with Ansible URI
  • Authenticating with already stored Cookie
  • More examples on API calls etc

While am working on these items with necessary examples and supporting images/videos. I am publishing this article to help those in need

if you have any more requirements or ideas that can or should be covered in this article. let me know in the comments.

Cheers
Sarav AK

Follow me on Linkedin My Profile
Follow DevopsJunction onFacebook orTwitter
For more practical videos and tutorials. Subscribe to our channel

Buy Me a Coffee at ko-fi.com

Signup for Exclusive "Subscriber-only" Content

Loading