Search and Configure within the Ansible Tower API using the Ansible URI Module

How to configure and search within the Ansible Tower API using the Ansible URI Module

Dynamically search within the Ansible Tower API

Authors: Brennan Stride and George Nalen

Ansible Tower API Project Background:

During a project with a client, we were working on manipulating the Ansible Tower inventory using Ansible templates (plays). If you’ve ever used Ansible to automate the manipulation of Tower via the URI module, you probably have run into scenarios where you’ve needed the ID of an entry. For example, you know the name of a host but not the internal ID of that host. Usually, to find these ID numbers, you must manually browse through the Tower API (via a web browser). Then you manually enter that ID into the needed fields — which isn’t dynamic enough for some scenarios.

We have found a way to retrieve these ID’s without having to manually search for the ID and then enter the ID statically in a task; thus, saving time and allowing further automation in your Tower manipulation tasks.

Was this originally our intention? Well, sort of. We wanted to fetch the IDs for later retrieval, so Brennan (our brilliant intern) figured out a way to do this.

Steps we took:

 Before we go into the nitty-gritty details, there are a few main steps that we will outline. This includes:

  • Role setup
  • Project setup
  • Applying the patches by Group
  • Applying the patches by Patch ID
  • Removing the Patch ID
  • Success!

Getting started

For the sake of this blog, we will use a use case around configuration. To make this task easier, it is best to list the names of all created credentials, projects, and templates as “ansible variables”. For the following examples, we will be using the variables shown below. Keep in mind the variables in the xsearch lists are case-sensitive and must be unique for this approach, as they are used to filter the results in the search tasks. The name_x variables are used to refer to positions in the list and help facilitate indexing.

credentialsearch:

    - "SSH"

    - "Git"

# add credential search items

name_ssh: 0

name_git: 1

# extra name_x variables for additional credentials

projectsearch:

    - "Project_1"

# add project search items

name_project1: 0

# extra name_x variables for additional projects

jobtemplatesearch:

    - "Job_1"

# add template search items

name_job1: 0

# extra name_x variables for additional templates

With this approach, we use a URI task to retrieve the raw data of any created object, using the filter feature of Tower’s API to retrieve only those we require. This approach assumes the previous initialization of Tower objects, which can also be done manually through the browser or automated via the URI module. Using the previously mentioned variables, we can retrieve the desired objects using the following tasks:

## Task: Get Credentials

- name: get credentials

  uri:

    url: "https://localhost/api/v2/credentials/?name={{ item }}"

    method: GET

    user: admin

    password: "{{ towerpass }}"

    validate_certs: False

    force_basic_auth: yes

    status_code:

      - 200

  register: credentialresults

  with_items:

      - "{{ credentialsearch }}"

## Task: Get Project

- name: get projects

  uri:

    url: "https://localhost/api/v2/projects/?name={{ item }}"

    method: GET

    user: admin

    password: "{{ towerpass }}"

    validate_certs: False

    force_basic_auth: yes

    status_code:

      - 200

  register: projectresults

  with_items:

      - "{{ projectsearch }}"

## Task: Get Templates

- name: get job templates

  uri:

    url: "https://localhost/api/v2/job_templates/?name={{ item }}"

    method: GET

    user: admin

    password: "{{ towerpass }}"

    validate_certs: False

    force_basic_auth: yes

    status_code:

      - 200

  register: jobtemplateresults

  with_items:

      - "{{ jobtemplatesearch }}"

These tasks should be placed immediately after the tasks to initialize all objects of that type. For example, if one were creating a credential that needed to be referenced later, the following could be used.

# TASKS

- name: create git credential

  uri:

    url: https://localhost/api/v2/credentials/   

    method: POST

    user: admin

    password: "{{ towerpass }}"

    body: "{{ lookup('file','tower_git_credential.json') }}"

    body_format: json

    validate_certs: False

    force_basic_auth: yes

    status_code:

      - 200

      - 201

  register: response

  changed_when: response.status == 201

# INSERT GET CREDENTIALS TASK HERE

# TASKS

Once the retrieval tasks are complete, we can then refer to them via the registers specified in each. A simplified version of the register format is displayed below:

{

    "results": {

        "item": "ITEM_1"

        "json": {

            "results": {

                # corresponding json formatted output for one object

            }

        }

        "item": "ITEM_2"

        "json": {

            "results": {

                # corresponding json formatted output for one object

            }

        }

        ...

    }

}

Thus, when referencing an attribute, we use the following:

{{ registername.results[name_x].json.results[0].attribute }}

Where name_x corresponds to the name_ variable we specified earlier (such as name_ssh). Using with_items on the register results in the form “item.results[0].json…” rather than the specifier for name_x.

In practice, we can use this to reference variables within URI POST tasks to create job templates for a project or credential a template, as shown below.

- name: create git job template

  uri:

    url:  https://localhost/api/v2/job_templates/

    method: POST

    user: admin

    password: "{{ towerpass }}"

    body_format: json

    body: >

      {

        "name": "Job_1",

        "description": "",

        "job_type": "run",

        "inventory": 2,

        "project": {{ projectresults.results[name_project1].json.results[0].id }},

        "playbook": "site.yaml",

        "vault_credential": null,

        "forks": 0,

        "limit": "",

        "verbosity": 0,

        "extra_vars": "webapp_version: 91d7a895302744cfd3c5ad40cc261dec4b796de3",

        "job_tags": "",

        "force_handlers": false,

        "skip_tags": "",

        "start_at_task": "",

        "timeout": 0,

        "use_fact_cache": false,

        "host_config_key": "",

        "ask_diff_mode_on_launch": false,

        "ask_variables_on_launch": false,

        "ask_limit_on_launch": false,

        "ask_tags_on_launch": false,

        "ask_skip_tags_on_launch": false,

        "ask_job_type_on_launch": false,

        "ask_verbosity_on_launch": false,

        "ask_inventory_on_launch": false,

        "ask_credential_on_launch": false,

        "survey_enabled": false,

        "become_enabled": true,

        "diff_mode": true,

        "allow_simultaneous": false,

        "cloud_credential": null,

        "network_credential": null

      }

    validate_certs: False

    force_basic_auth: yes

    status_code:

      - 200

      - 201

      - 202

  register: response

  until: response.status == 201 or response.status == 202

  retries: 10

  delay: 30

  changed_when: response.status == 201 or response.status == 202

  when: responseproj.status == 201

- name: credential job templates

  uri:

    url: "https://localhost/api/v2/job_templates/{{ item.results[0].json.results[0].id }}/credentials/"

    method: POST

    user: admin

    password: "{{ towerpass }}"

    body_format: json

    body: >

      {

        "id": {{ credential.results[name_ssh].json.results[0].id | int }}

      }

    validate_certs: False

    force_basic_auth: yes

    status_code:

      - 200

      - 201

      - 202

      - 204

  register: response

  changed_when: response.status == 201 or response.status == 202 or response.status == 204

  with_items:

      - "{{ jobresults }}"

Your Automation Resource

Adopting automation can make a significant difference in your organization. That is why MindPoint Group created Ansible Counselor, which helps you accelerate Ansible implementation, preventing new automation-associated risks to your business.

Automation Counselor provides on-demand access to Ansible experts — including former Ansible employees — that guide your teams on their automation journey. Whether you need a Playbook to automate a task, best-practices answers, or strategic planning, our Ansible Counselor subscription is for you.

Contact us to learn more.

More from Our Cybersecurity Experts