AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible Tower API: Automate Jobs, Inventories & Workflows via REST (Guide)

By Luca Berton · Published 2024-01-01 · Category: troubleshooting

Complete guide to Ansible Tower API (AWX/AAP). Launch jobs, manage inventories, create workflows, authenticate with tokens, query job status, and integrate.

The AWX/Ansible Tower/AAP REST API lets you automate everything the web UI can do — launch jobs, manage inventories, create workflows, and integrate Ansible into CI/CD pipelines programmatically.

API Basics

Base URL

# AWX / Ansible Tower
https://tower.example.com/api/v2/

# AAP 2.x (Controller) https://controller.example.com/api/v2/

# API root — lists all available endpoints curl -s https://tower.example.com/api/v2/ | python3 -m json.tool

Authentication

Personal Access Token (Recommended)

# Create a token
curl -s -X POST \
  -H "Content-Type: application/json" \
  -u admin:password \
  https://tower.example.com/api/v2/tokens/ \
  -d '{"description": "CI/CD Token", "scope": "write"}'

# Use token in requests curl -s -H "Authorization: Bearer <token>" \ https://tower.example.com/api/v2/me/

Basic Auth

curl -s -u admin:password \
  https://tower.example.com/api/v2/me/

OAuth2 Token

# Create application
curl -s -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  https://tower.example.com/api/v2/applications/ \
  -d '{
    "name": "CI Pipeline",
    "organization": 1,
    "authorization_grant_type": "password",
    "client_type": "confidential"
  }'

See also: AWX vs Ansible Tower vs AAP: Key Differences Explained (2026)

Launch Jobs

Launch a Job Template

# Launch by ID
curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  https://tower.example.com/api/v2/job_templates/42/launch/ \
  -d '{
    "extra_vars": {
      "target_env": "production",
      "app_version": "2.1.0"
    },
    "limit": "webservers",
    "tags": "deploy"
  }'

Launch and Wait for Completion

#!/bin/bash
TOKEN="your-token"
TOWER="https://tower.example.com"

# Launch job RESPONSE=$(curl -s -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ "$TOWER/api/v2/job_templates/42/launch/" \ -d '{"extra_vars": {"version": "2.1.0"}}')

JOB_ID=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])") echo "Job launched: $JOB_ID"

# Poll until complete while true; do STATUS=$(curl -s \ -H "Authorization: Bearer $TOKEN" \ "$TOWER/api/v2/jobs/$JOB_ID/" | \ python3 -c "import sys,json; print(json.load(sys.stdin)['status'])") echo "Status: $STATUS" case "$STATUS" in successful) echo "Job succeeded!"; exit 0;; failed|error|canceled) echo "Job failed!"; exit 1;; *) sleep 10;; esac done

Launch with Ansible uri Module

- name: Launch Tower job template
  ansible.builtin.uri:
    url: "https://tower.example.com/api/v2/job_templates/42/launch/"
    method: POST
    headers:
      Authorization: "Bearer {{ tower_token }}"
    body_format: json
    body:
      extra_vars:
        target_env: production
    status_code: 201
  register: job_launch

- name: Wait for job completion ansible.builtin.uri: url: "https://tower.example.com/api/v2/jobs/{{ job_launch.json.id }}/" headers: Authorization: "Bearer {{ tower_token }}" register: job_status until: job_status.json.status in ['successful', 'failed', 'error', 'canceled'] retries: 60 delay: 30

- name: Fail if job failed ansible.builtin.fail: msg: "Tower job {{ job_launch.json.id }} failed: {{ job_status.json.status }}" when: job_status.json.status != 'successful'

Manage Inventories

List Inventories

curl -s -H "Authorization: Bearer $TOKEN" \
  "$TOWER/api/v2/inventories/" | python3 -m json.tool

Create Inventory

curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  "$TOWER/api/v2/inventories/" \
  -d '{
    "name": "Production",
    "organization": 1,
    "description": "Production servers"
  }'

Add Host to Inventory

curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  "$TOWER/api/v2/inventories/5/hosts/" \
  -d '{
    "name": "web-prod-01",
    "variables": "ansible_host: 10.0.1.10\nhttp_port: 8080"
  }'

Sync Inventory Source

curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  "$TOWER/api/v2/inventory_sources/3/update/"

See also: Ansible Automation: Complete Guide to IT Automation with Playbook Examples

Manage Job Templates

List Job Templates

curl -s -H "Authorization: Bearer $TOKEN" \
  "$TOWER/api/v2/job_templates/" | \
  python3 -c "
import sys, json
data = json.load(sys.stdin)
for jt in data['results']:
    print(f\"{jt['id']:4d} | {jt['name']:40s} | {jt['playbook']}\")
"

Create Job Template

curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  "$TOWER/api/v2/job_templates/" \
  -d '{
    "name": "Deploy Web App",
    "job_type": "run",
    "inventory": 5,
    "project": 3,
    "playbook": "deploy.yml",
    "credential": 2,
    "ask_variables_on_launch": true,
    "ask_limit_on_launch": true,
    "extra_vars": "app_version: latest"
  }'

Workflows

Launch Workflow

curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  "$TOWER/api/v2/workflow_job_templates/10/launch/" \
  -d '{
    "extra_vars": {
      "release_version": "3.0.0",
      "environment": "staging"
    }
  }'

See also: Ansible Development: Write Custom Modules, Plugins & Collections

CI/CD Integration

GitHub Actions

# .github/workflows/deploy.yml
name: Deploy via Tower
on:
  push:
    branches: [main]

jobs: deploy: runs-on: ubuntu-latest steps: - name: Launch Ansible Tower job run: | RESPONSE=$(curl -s -X POST \ -H "Authorization: Bearer ${{ secrets.TOWER_TOKEN }}" \ -H "Content-Type: application/json" \ "${{ secrets.TOWER_URL }}/api/v2/job_templates/${{ vars.JOB_TEMPLATE_ID }}/launch/" \ -d '{"extra_vars": {"version": "${{ github.sha }}"}}') JOB_ID=$(echo "$RESPONSE" | jq -r '.id') echo "job_id=$JOB_ID" >> $GITHUB_OUTPUT # Wait for completion for i in $(seq 1 60); do STATUS=$(curl -s \ -H "Authorization: Bearer ${{ secrets.TOWER_TOKEN }}" \ "${{ secrets.TOWER_URL }}/api/v2/jobs/$JOB_ID/" | jq -r '.status') echo "Attempt $i: $STATUS" [ "$STATUS" = "successful" ] && exit 0 [ "$STATUS" = "failed" ] || [ "$STATUS" = "error" ] && exit 1 sleep 15 done echo "Timeout waiting for job" && exit 1

Python Client

import requests
import time

class TowerClient: def __init__(self, url, token): self.url = url.rstrip('/') self.session = requests.Session() self.session.headers['Authorization'] = f'Bearer {token}' self.session.headers['Content-Type'] = 'application/json' def launch_job(self, template_id, extra_vars=None, limit=None): payload = {} if extra_vars: payload['extra_vars'] = extra_vars if limit: payload['limit'] = limit resp = self.session.post( f'{self.url}/api/v2/job_templates/{template_id}/launch/', json=payload ) resp.raise_for_status() return resp.json()['id'] def wait_for_job(self, job_id, timeout=1800, interval=15): end_time = time.time() + timeout while time.time() < end_time: resp = self.session.get(f'{self.url}/api/v2/jobs/{job_id}/') status = resp.json()['status'] if status == 'successful': return True if status in ('failed', 'error', 'canceled'): raise Exception(f'Job {job_id} {status}') time.sleep(interval) raise TimeoutError(f'Job {job_id} timed out')

# Usage tower = TowerClient('https://tower.example.com', 'my-token') job_id = tower.launch_job(42, extra_vars={'version': '2.1.0'}) tower.wait_for_job(job_id)

FAQ

How do I authenticate with the Tower/AWX API?

Use a Personal Access Token (recommended): create one via the API or web UI, then pass Authorization: Bearer in headers. Basic auth (-u user:pass) also works but tokens are more secure for automation.

How do I launch a job template via the API?

Send a POST to /api/v2/job_templates//launch/. The response includes the job ID which you can poll at /api/v2/jobs// until status is successful, failed, or canceled.

What is the difference between Tower API and AWX API?

They're the same API — AWX is the upstream open-source project and Ansible Tower/AAP Controller is the Red Hat commercial product. The API endpoints, authentication, and behavior are identical.

How do I pass extra variables when launching a job?

Include extra_vars in the POST body: {"extra_vars": {"key": "value"}}. The job template must have ask_variables_on_launch: true or accept survey/extra vars.

How do I integrate Tower with CI/CD pipelines?

Use the REST API to launch jobs from GitHub Actions, GitLab CI, or Jenkins. POST to the launch endpoint, capture the job ID, then poll the job status endpoint in a loop until completion.

Conclusion

Authentication: Use Bearer tokens for automation, Basic auth for testing • Launch jobs: POST /api/v2/job_templates//launch/Check status: GET /api/v2/jobs// → poll until status is terminal • Manage resources: Full CRUD on inventories, templates, credentials, projects • CI/CD: Integrate with any pipeline that can make HTTP calls

Related Articles

AWX vs Ansible Tower vs AAP ComparisonAnsible uri Module: HTTP/REST API CallsAnsible CI/CD Pipeline Integration

Category: troubleshooting

Browse all Ansible tutorials · AnsiblePilot Home