How To

Ansible AWX Integration: Automation Ops with AWX's Native Tools

A hands-on Ansible AWX integration guide to running automation ops with AWX's own features — no third-party tools. Part two of our CI/CD reliability series: dynamic inventories with sync-on-launch, job template design (surveys, extra-vars discipline, check mode as a dry-run gate), workflows with convergence and approval nodes, failure webhooks, and the AWX API and awx CLI for launching templates, reading job stdout, and pulling per-host summaries (ok/changed/failures/dark). Exact curl calls and CLI commands you can run today, plus schedule hygiene. Closes honestly with the ceiling: workflows route decisions to humans and approval nodes pause for a person, but reading the failed stdout and deciding what to do is still manual toil.

·
ansibleawxcicdautomationansibledevopsinfrastructureascode
Cover Image for Ansible AWX Integration: Automation Ops with AWX's Native Tools

Ansible AWX Integration: Automation Ops with AWX's Native Tools

There's a node type in AWX workflows that quietly admits the whole platform has a ceiling: the approval node. You drop it into a workflow, a job pauses, and a human gets a notification asking "proceed or deny?" It exists because AWX knows that some steps in an automation run should not fire without a person looking at them first. That is honest engineering. It is also where the DIY story ends — because the person who clicks "approve" still had to open the failed job from last night, scroll the stdout, decide whether the failure was a bad host or a bad play, and carry that context in their head. AWX routes the decision to a human; it never makes the decision easier.

This is part two of a three-part series on running Ansible AWX well. Part one mapped the failure and waste patterns — failed jobs nobody reads, inventories drifting from cloud reality, snowflake extra-vars, schedules running jobs that stopped mattering. This article is the hands-on Ansible AWX integration work: how to tighten each of those with AWX's own features — dynamic inventories, job template design, workflows, notifications, the AWX API, and schedule hygiene. Every command below is real and runnable with the awx CLI or plain curl. We close, as an honest DIY guide should, with what these tools still cannot do for you.

We'll work feature by feature, because that's how you actually operate AWX.

Feature 1: Dynamic inventories with sync-on-launch

A static inventory rots the moment someone spins up an instance. The fix is a dynamic inventory backed by a source plugin (AWS EC2, Azure, GCP, OpenStack, VMware) that AWX syncs from the source of truth.

Console path: Resources → Inventories → Add inventory, then add a Source (Sources → Add) pointing at your cloud credential and inventory plugin. The setting that matters most is Update on Launch — enable it so every job that targets this inventory triggers a fresh sync first, with a configurable Cache Timeout so back-to-back jobs don't hammer the API (AWX inventories docs).

Trigger a sync from the CLI and watch it:

# Kick an inventory source sync and follow output
awx inventory_source update 42 --monitor

# List hosts AWX currently believes exist
awx hosts list --inventory 7 -f human --filter 'name,enabled'

How to read it. After a sync, compare the host count AWX reports against what the cloud provider reports. A gap in either direction is a finding: hosts in AWX that no longer exist (stale, will produce unreachable failures), or hosts in the cloud that AWX never learned about (unmanaged drift). Use constructed or smart inventories with a host_filter to slice the synced hosts into groups by tag instead of hand-maintaining group membership.

Feature 2: Job template design — surveys, extra-vars discipline, check mode

A job template is where operational discipline lives or dies. Three habits keep templates from becoming snowflakes.

Surveys instead of free-form extra-vars. A survey (Templates → your template → Survey) turns "whatever the operator remembered to type" into a typed, validated form with defaults and required fields. It's self-documenting; the next person doesn't have to guess what target_version means.

Extra-vars discipline. Keep template-level extra_vars minimal and in version control alongside the playbook. Reserve Prompt on launch for the one or two values that genuinely vary per run, and enable it deliberately — every promptable field is a place a bad value can enter unseen.

Check mode as a dry-run gate. Ansible's --check runs plays without changing anything and reports what would change. In AWX, set the job type to Check (or enable Ask job type on launch) to gate risky templates behind a dry run first.

# Launch in check mode with explicit vars, then monitor
awx job_templates launch 15 \
  --job_type check \
  --extra_vars 'target_version: 2.4.1' \
  --monitor

How to read it. A clean check-mode run that reports zero changed tasks on hosts you expected to change is itself a warning — your play may not be matching the state you think it is. Read the changed-task count the way you'd read a terraform plan: it's the diff you're about to apply (Ansible check mode docs).

Feature 3: Workflows — convergence and approval nodes

Workflows chain templates into a graph with success/failure/always edges. Two node types do the heavy lifting.

Convergence nodes let multiple parent branches fan into a single downstream node with an All or Any condition — so a "deploy" node only runs after both "build" and "smoke-test" branches succeed, not after either one.

Approval nodes pause the workflow until a human approves or denies (Workflow Visualizer → add node → Approval). You can set a timeout so an unanswered approval auto-fails rather than hanging forever (AWX workflows docs).

Launch a workflow and follow the whole graph:

awx workflow_job_templates launch 9 --monitor

How to read it. Build workflows so the dangerous node always sits behind an approval or a check-mode predecessor. The convergence node is your correctness guard (don't deploy on a partial success); the approval node is your judgment guard (a human owns the go/no-go). Both are structural — they don't tell the approver whether last night's failure was transient.

Feature 4: Notifications — webhooks on failed jobs

Silent failures are the whole problem from part one. Wire notifications so a failed job reaches you where you already look.

Console: Administration → Notifications → Add, choose a type (Slack, webhook, email, PagerDuty), then attach it to a template or organization on the Notifications tab with the On Failure trigger. A generic webhook posts a JSON body you can route however you like:

# Create a webhook notification template via the API
curl -sk -X POST https://awx.example.com/api/v2/notification_templates/ \
  -H "Authorization: Bearer $AWX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "failed-jobs-webhook",
    "notification_type": "webhook",
    "notification_configuration": {
      "url": "https://hooks.example.com/awx",
      "http_method": "POST",
      "headers": {}
    }
  }'

How to read it. On Failure is the trigger that matters; On Success notifications train people to ignore the channel. Route failure webhooks to a channel with an owner, and include the job ID in the payload so the responder can jump straight to the stdout instead of hunting for it (AWX notifications docs).

Feature 5: The AWX API and CLI — launching, stdout, host summaries

Everything the UI does is a REST endpoint, and the awx CLI is a thin wrapper over it. This is how you script audits and integrate AWX with the rest of your stack. Point the CLI at your instance first (AWX CLI docs):

export TOWER_HOST=https://awx.example.com
export TOWER_OAUTH_TOKEN=$AWX_TOKEN

Launch a template and capture the job ID:

job_id=$(awx job_templates launch 15 --extra_vars 'app=api' -f jq --filter '.id')
echo "launched job $job_id"

Read the stdout of a job — the archaeology from part one, scripted:

# Plain-text stdout of a completed job
curl -sk "https://awx.example.com/api/v2/jobs/${job_id}/stdout/?format=txt" \
  -H "Authorization: Bearer $AWX_TOKEN"

# Or via the CLI
awx jobs stdout $job_id

Pull per-host summaries to see what actually happened, not just pass/fail:

curl -sk "https://awx.example.com/api/v2/jobs/${job_id}/job_host_summaries/" \
  -H "Authorization: Bearer $AWX_TOKEN" \
  | jq '.results[] | {host: .host_name, ok, changed, failures, dark}'

How to read it. dark is the unreachable count — hosts AWX couldn't even connect to, which is a different class of problem from failures (tasks that ran and errored). A job with high dark and zero failures is almost always an inventory or credential problem, not a playbook regression. That distinction is the first thing you want from any failed run, and the job_host_summaries endpoint hands it to you without scrolling 400 lines of stdout.

Feature 6: Schedule hygiene

Schedules are where dead automation hides. AWX schedules use RRULE (iCal) recurrence, and nobody audits them.

# Every schedule, when it last ran, and when it fires next
awx schedules list -f human \
  --filter 'name,enabled,next_run,unified_job_template'

How to read it. Cross-reference each enabled schedule against whether anyone still cares about its job. A schedule firing a nightly play against an environment that was decommissioned two quarters ago is pure noise — and worse, it manufactures the failure alerts that desensitize the team. Disable rather than delete first (awx schedules modify <id> --enabled false) so you keep the definition while you confirm nobody screams.

The honest part: where DIY automation ops stops

Run all of this. Dynamic inventories, survey-driven templates, check-mode gates, convergence and approval nodes, failure webhooks, scripted stdout pulls, schedule audits — together they are the difference between AWX-as-a-mess and AWX-as-a-platform. But be clear about the ceiling:

The structure routes decisions; it doesn't make them. An approval node pauses for a human. A convergence node enforces "all branches passed." Neither reads the failed stdout for you or decides whether a dark host is a fluke or an outage.

Reading the failure is still manual. The job_host_summaries endpoint tells you that five hosts were unreachable. Deciding whether that's a transient network blip, an expired credential, or genuinely dead infrastructure — and what to do next — is you, at your desk, correlating against three other tabs.

It's point-in-time and it's toil. Every audit above is a snapshot you re-run by hand. The inventory drifts again next sprint; a new snowflake template lands; someone adds a schedule nobody will revisit. The work regenerates the moment you stop.

Detection isn't remediation. A failure webhook fires. Then it waits for a human. Nothing here reconciles inventory against cloud reality on its own or relaunches an approved remediation template when an incident elsewhere calls for it.

That's not a knock on AWX — it's the boundary of any DIY setup. The question it leads to: what if something read every failed job the way an operator would, reconciled your inventory continuously, and launched approved templates as remediation under real approval gates? That's part three, where CloudThinker agents connect to AWX read-only and take that next step. Or start now: Try CloudThinker free — 100 premium credits, no card required.