Add custom AWX Execution Environment with NetBox support and related scripts

This commit is contained in:
alex-local
2025-09-12 09:44:55 +02:00
parent 585fa54336
commit e423305242
10 changed files with 500 additions and 84 deletions

View File

@@ -1,28 +0,0 @@
---
- name: APT Update Playbook
hosts: all
become: yes
gather_facts: yes
tasks:
- name: Update apt package cache
ansible.builtin.apt:
update_cache: yes
cache_valid_time: 3600
register: apt_update_result
when: ansible_os_family == "Debian"
- name: Display update result for Debian/Ubuntu systems
ansible.builtin.debug:
msg: "APT cache updated successfully on {{ inventory_hostname }}"
when: ansible_os_family == "Debian" and apt_update_result is succeeded
- name: Skip non-Debian systems
ansible.builtin.debug:
msg: "Skipping {{ inventory_hostname }} - not a Debian/Ubuntu system ({{ ansible_os_family }})"
when: ansible_os_family != "Debian"
- name: Display failure message
ansible.builtin.debug:
msg: "APT update failed on {{ inventory_hostname }}"
when: ansible_os_family == "Debian" and apt_update_result is failed

View File

@@ -1,56 +0,0 @@
---
- name: APT Update and Upgrade Playbook
hosts: all
become: yes
gather_facts: yes
tasks:
- name: Update apt package cache
ansible.builtin.apt:
update_cache: yes
cache_valid_time: 3600
register: apt_update_result
when: ansible_os_family == "Debian"
- name: Upgrade all packages to latest version
ansible.builtin.apt:
upgrade: dist
autoremove: yes
autoclean: yes
register: apt_upgrade_result
when: ansible_os_family == "Debian" and apt_update_result is succeeded
- name: Check if reboot is required
ansible.builtin.stat:
path: /var/run/reboot-required
register: reboot_required_file
when: ansible_os_family == "Debian"
- name: Display upgrade summary
ansible.builtin.debug:
msg: |
APT operations completed on {{ inventory_hostname }}:
- Cache updated: {{ apt_update_result.changed | default(false) }}
- Packages upgraded: {{ apt_upgrade_result.changed | default(false) }}
- Reboot required: {{ reboot_required_file.stat.exists | default(false) }}
when: ansible_os_family == "Debian"
- name: Reboot system if required
ansible.builtin.reboot:
msg: "Reboot initiated by Ansible for package updates"
connect_timeout: 5
reboot_timeout: 300
pre_reboot_delay: 0
post_reboot_delay: 30
test_command: uptime
when: ansible_os_family == "Debian" and reboot_required_file.stat.exists | default(false)
- name: Skip non-Debian systems
ansible.builtin.debug:
msg: "Skipping {{ inventory_hostname }} - not a Debian/Ubuntu system ({{ ansible_os_family }})"
when: ansible_os_family != "Debian"
- name: Display failure message
ansible.builtin.debug:
msg: "APT operations failed on {{ inventory_hostname }}"
when: ansible_os_family == "Debian" and (apt_update_result is failed or apt_upgrade_result is failed)

11
ee/Dockerfile Normal file
View File

@@ -0,0 +1,11 @@
# Custom AWX Execution Environment with NetBox support
# Extends the official AWX EE and installs pynetbox and the netbox collection
FROM quay.io/ansible/awx-ee:latest
USER root
# Install python dependency and the NetBox Ansible collection
RUN pip install --no-cache-dir pynetbox \
&& ansible-galaxy collection install netbox.netbox || true
# Drop back to non-root user used by the base image
USER 1000

31
ee/README.md Normal file
View File

@@ -0,0 +1,31 @@
Custom AWX Execution Environment: NetBox support
This folder contains a minimal Dockerfile and helper to create an AWX Execution
Environment that includes the NetBox Ansible collection and pynetbox.
Files:
- Dockerfile - derived from quay.io/ansible/awx-ee:latest, installs pynetbox and netbox.netbox
- build-and-load-ee.sh - builds the image and loads it into Minikube
Usage:
1. Build and load into minikube:
IMAGE_NAME=local/awx-ee-netbox:latest ./awx-local-setup/ee/build-and-load-ee.sh
2. Register this Execution Environment in AWX (UI or awx-cli / API). Use the image
name you built (local/awx-ee-netbox:latest) as the Execution Environment image.
3. Commit an inventory plugin file (example: inventory/netbox_inventory.yml) to
your project repository that uses the netbox.netbox.inventory plugin and
references the NetBox token and URL.
4. Create an AWX inventory source of type `scm` pointing to the project and set
`source_path` to the plugin file created in step 3. Ensure the inventory
source uses the custom Execution Environment.
Notes:
- If your Minikube cluster uses a remote container runtime you may need to push
the built image to a registry reachable by the cluster and set IMAGE_NAME
accordingly.
- For production, build and push to a private registry and configure AWX to
pull the image using pull secrets.

21
ee/build-and-load-ee.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail
# build-and-load-ee.sh
# Build the custom AWX Execution Environment and load it into Minikube
IMAGE_NAME=${IMAGE_NAME:-local/awx-ee-netbox:latest}
MINIKUBE_PROFILE=${MINIKUBE_PROFILE:-minikube}
echo "Building EE image: $IMAGE_NAME"
docker build -t "$IMAGE_NAME" -f "$(dirname "$0")/ee/Dockerfile" .
# If minikube is running with docker driver we can use 'docker' directly, else load into minikube
if minikube status -p "$MINIKUBE_PROFILE" >/dev/null 2>&1; then
DRIVER=$(minikube status -p "$MINIKUBE_PROFILE" --format='{{.Host}}' || true)
fi
if minikube image load "$IMAGE_NAME" -p "$MINIKUBE_PROFILE"; then
echo "Loaded $IMAGE_NAME into minikube"
else
echo "Failed to use minikube image load; try docker push to a registry and update AWX EE settings"
fi

158
integrate_github_netbox.sh Executable file
View File

@@ -0,0 +1,158 @@
#!/usr/bin/env bash
set -euo pipefail
# integrate_github_netbox.sh
# Create AWX Project (Git) and NetBox inventory source via AWX API.
# Usage (dry-run by default):
# ./integrate_github_netbox.sh --github-repo https://github.com/owner/repo.git \
# --github-pat GH_PAT --netbox-url https://netbox.example/api/ --netbox-token NETBOX_TOKEN [--apply]
AWX_URL=${AWX_URL:-http://localhost:30081}
AWX_USER=${AWX_USER:-admin}
AWX_PW="${AWX_PW:-}"
GITHUB_REPO=""
GITHUB_PAT=""
NETBOX_URL=""
NETBOX_TOKEN=""
PROJECT_NAME="awx-github-project"
INV_NAME="netbox-inventory"
DRY_RUN=1
usage(){
cat <<EOF
Usage: $0 --github-repo REPO_URL --github-pat PAT --netbox-url URL --netbox-token TOKEN [--project-name NAME] [--inventory-name NAME] [--apply]
This script is dry-run by default. Add --apply to execute API POSTs.
Secrets can also be provided via env vars: GITHUB_PAT, NETBOX_TOKEN, AWX_PW.
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--github-repo) GITHUB_REPO="$2"; shift 2;;
--github-pat) GITHUB_PAT="$2"; shift 2;;
--netbox-url) NETBOX_URL="$2"; shift 2;;
--netbox-token) NETBOX_TOKEN="$2"; shift 2;;
--project-name) PROJECT_NAME="$2"; shift 2;;
--inventory-name) INV_NAME="$2"; shift 2;;
--apply) DRY_RUN=0; shift;;
-h|--help) usage; exit 0;;
*) echo "Unknown arg: $1"; usage; exit 1;;
esac
done
# pick up env secrets if not provided
: "${GITHUB_PAT:-}" && GITHUB_PAT=${GITHUB_PAT:-$GITHUB_PAT}
AWX_PW=${AWX_PW:-}
NETBOX_TOKEN=${NETBOX_TOKEN:-$NETBOX_TOKEN}
for cmd in curl jq; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "missing dependency: $cmd" >&2
exit 1
fi
done
if [ -z "$AWX_PW" ]; then
# try to read from k8s secret
if command -v kubectl >/dev/null 2>&1; then
if AWX_PW_RAW=$(kubectl -n awx get secret awx-admin-password -o jsonpath='{.data.password}' 2>/dev/null || true); then
if [ -n "$AWX_PW_RAW" ]; then
AWX_PW=$(echo "$AWX_PW_RAW" | base64 --decode)
fi
fi
fi
fi
# Require github repo, netbox url and token. Either GITHUB_PAT OR a local SSH key file must exist.
if [ -z "$GITHUB_REPO" ] || [ -z "$NETBOX_URL" ] || [ -z "${NETBOX_TOKEN:-}" ] || { [ -z "${GITHUB_PAT:-}" ] && [ ! -f "$HOME/.ssh/awx_deploy_key" ]; }; then
echo "Missing required inputs." >&2
echo "Provide --github-repo, --netbox-url, --netbox-token and either --github-pat or a private key at $HOME/.ssh/awx_deploy_key." >&2
usage
exit 1
fi
AUTH="${AWX_USER}:${AWX_PW}"
api_get(){
local path=$1
curl -sS -u "$AUTH" "$AWX_URL/api/v2/$path"
}
api_post(){
local path=$1
local data=$2
if [ "$DRY_RUN" -ne 0 ]; then
echo "[DRY-RUN] POST $AWX_URL/api/v2/$path -> payload:" >&2
echo "$data" | jq . >&2 || true
return 0
fi
curl -sS -u "$AUTH" -H 'Content-Type: application/json' -X POST "$AWX_URL/api/v2/$path" -d "$data" | jq .
}
echo "AWX: $AWX_URL (user: $AWX_USER)"
echo "Repo: $GITHUB_REPO"
echo "Project: $PROJECT_NAME"
echo "Inventory: $INV_NAME"
echo "Dry-run: $DRY_RUN"
echo "Listing credential types (to pick SCM type)..."
api_get credential_types/ | jq -r '.results[] | "id:\(.id) name:\(.name) kind:\(.kind)"'
# User-friendly heuristic to pick SCM credential_type id
SCM_CT_ID=$(api_get credential_types/ | jq -r '.results[] | select((.name|test("Source Control|Git|SCM";"i")) or (.kind == "scm")) | .id' | head -n1)
if [ -z "$SCM_CT_ID" ]; then
echo "Could not auto-detect SCM credential_type id. You must create credentials manually using AWX UI or list credential_types." >&2
else
echo "Detected SCM credential_type id: $SCM_CT_ID"
fi
echo "Creating Git credential (token or ssh key)"
if [ -n "${GITHUB_PAT:-}" ]; then
git_cred_payload=$(jq -n --arg name "github-pat" --argjson ct "$SCM_CT_ID" --arg inputs "{\"token\":\"$GITHUB_PAT\"}" '{name:$name, credential_type:$ct, inputs:( $inputs | fromjson ) }')
else
# fall back to SSH private key if available
if [ -f "$HOME/.ssh/awx_deploy_key" ]; then
SSH_KEY_DATA=$(sed -n '1,2000p' "$HOME/.ssh/awx_deploy_key")
# use --arg to safely inject SSH private key text into JSON
git_cred_payload=$(jq -n --arg name "github-ssh" --argjson ct "$SCM_CT_ID" --arg ssh "$SSH_KEY_DATA" '{name:$name, credential_type:$ct, inputs:{ssh_key_data:$ssh}}')
else
echo "No GITHUB_PAT and no $HOME/.ssh/awx_deploy_key - cannot create SCM credential" >&2
exit 1
fi
fi
CREDS_OUT=$(api_post credentials/ "$git_cred_payload" || true)
SCM_CRED_ID=$(echo "$CREDS_OUT" | jq -r '.id // empty' 2>/dev/null || true)
if [ -z "$SCM_CRED_ID" ]; then
SCM_CRED_ID=null
echo "SCM credential id: (none - dry-run or failed)"
else
echo "SCM credential id: $SCM_CRED_ID"
fi
echo "Creating Project pointing to $GITHUB_REPO"
proj_payload=$(jq -n --arg name "$PROJECT_NAME" --arg scm_type "git" --arg scm_url "$GITHUB_REPO" --argjson cred "$SCM_CRED_ID" '{name:$name, scm_type:$scm_type, scm_url:$scm_url, scm_update_on_launch:true, scm_branch:null, scm_clean:false, scm_ref:null, scm_credential:$cred }')
api_post projects/ "$proj_payload"
echo "Creating Inventory: $INV_NAME"
inv_payload=$(jq -n --arg name "$INV_NAME" '{name:$name, organization:1}')
INV_OUT=$(api_post inventories/ "$inv_payload" || true)
INV_ID=$(echo "$INV_OUT" | jq -r '.id // empty')
if [ -z "$INV_ID" ]; then
echo "Could not create inventory (check output)." >&2
else
echo "Inventory id: $INV_ID"
fi
echo "Creating NetBox inventory source for inventory id $INV_ID"
source_vars=$(cat <<EOF
token: "$NETBOX_TOKEN"
url: "$NETBOX_URL"
validate_certs: true
EOF
)
src_payload=$(jq -n --arg name "netbox-src" --arg source "netbox" --arg source_vars "$source_vars" --argjson update_on_launch true '{name:$name, source:$source, source_vars:$source_vars, update_on_launch:$update_on_launch}')
api_post "inventories/$INV_ID/inventory_sources/" "$src_payload"
echo "Done. If run in dry-run mode, re-run with --apply to execute the API calls."
exit 0

42
scripts/add_static_hosts.sh Executable file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -euo pipefail
# add_static_hosts.sh
# Creates or updates static hosts in an AWX inventory via the AWX REST API.
# Usage:
# AWX_HOST=... AWX_USER=... AWX_PASS=... INV_ID=2 ./add_static_hosts.sh
AWX_HOST="${AWX_HOST:-http://192.168.49.2:30081}"
AWX_USER="${AWX_USER:-admin}"
AWX_PASS="${AWX_PASS:-Aase#1234!}"
INV_ID="${INV_ID:-2}"
IPS=("192.168.1.144" "192.168.1.23" "192.168.1.24")
echo "Using AWX at $AWX_HOST (inventory id $INV_ID)"
for ip in "${IPS[@]}"; do
name="host-$(echo "$ip" | tr '.' '-')"
vars=$(printf "ansible_host: %s\n" "$ip")
# URL-encode the name for query
encoded_name=$(jq -nr --arg s "$name" '$s|@uri')
echo "Processing $name -> $ip"
host_id=$(curl -sS -u "$AWX_USER:$AWX_PASS" "$AWX_HOST/api/v2/hosts/?name=$encoded_name&inventory=$INV_ID" | jq -r '.results[0].id // empty')
if [ -n "$host_id" ]; then
echo "Host exists (id=$host_id) — updating variables"
payload=$(jq -n --arg vars "$vars" '{variables:$vars}')
curl -sS -u "$AWX_USER:$AWX_PASS" -H "Content-Type: application/json" -X PATCH "$AWX_HOST/api/v2/hosts/$host_id/" -d "$payload" | jq .
else
echo "Host not found — creating"
payload=$(jq -n --arg name "$name" --arg inv "$INV_ID" --arg vars "$vars" '{name:$name, inventory:($inv|tonumber), variables:$vars}')
curl -sS -u "$AWX_USER:$AWX_PASS" -H "Content-Type: application/json" -X POST "$AWX_HOST/api/v2/hosts/" -d "$payload" | jq .
fi
echo
done
echo "Listing hosts in inventory $INV_ID:"
curl -sS -u "$AWX_USER:$AWX_PASS" "$AWX_HOST/api/v2/inventories/$INV_ID/hosts/?page_size=100" | jq '.results[] | {id: .id, name: .name, variables: .variables}'

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
set -euo pipefail
# awx_trigger_inventory_update.sh
# Trigger an AWX inventory source update and print its stdout.
# Usage:
# AWX_HOST=http://... AWX_USER=admin AWX_PASS=secret SRC_ID=9 ./awx_trigger_inventory_update.sh
# Defaults are set to the local minikube/awx values used in this workspace.
AWX_HOST="${AWX_HOST:-http://192.168.49.2:30081}"
AWX_USER="${AWX_USER:-admin}"
AWX_PASS="${AWX_PASS:-Aase#1234!}"
SRC_ID="${SRC_ID:-9}"
TIMEOUT_SECONDS="${TIMEOUT_SECONDS:-60}"
AUTH=("-u" "${AWX_USER}:${AWX_PASS}")
tmpfile=$(mktemp /tmp/awx_inv_resp.XXXX.json)
cleanup() { rm -f "$tmpfile"; }
trap cleanup EXIT
echo "Triggering inventory source update for source id=$SRC_ID on $AWX_HOST"
if ! curl -sS "${AUTH[@]}" -H "Content-Type: application/json" -X POST "$AWX_HOST/api/v2/inventory_sources/$SRC_ID/update/" -o "$tmpfile"; then
echo "Warning: request failed (curl exit non-zero). See contents of $tmpfile:" >&2
cat "$tmpfile" >&2 || true
fi
# Try to read inventory_update id from the POST response
INV_UP_ID=$(jq -r '.inventory_update.id // .id // empty' "$tmpfile" 2>/dev/null || true)
# If not present, poll the inventory_source object to find the active update id
if [ -z "$INV_UP_ID" ]; then
echo "No inventory_update id in initial response, polling inventory_source for current/last update id..."
poll_end=$((SECONDS + 15))
while [ $SECONDS -lt $poll_end ]; do
src_json=$(curl -sS "${AUTH[@]}" "$AWX_HOST/api/v2/inventory_sources/$SRC_ID/" || true)
# prefer current_update -> summary_fields.current_update -> last_job -> summary_fields.last_job -> last_update
INV_UP_ID=$(echo "$src_json" | jq -r '.current_update.id // .summary_fields.current_update.id // .last_job.id // .summary_fields.last_job.id // .last_update.id // .summary_fields.last_update.id // empty' 2>/dev/null || true)
if [ -n "$INV_UP_ID" ]; then
break
fi
sleep 1
done
fi
# Final fallback: use newest inventory_updates list
if [ -z "$INV_UP_ID" ]; then
echo "Falling back to newest inventory_update from list..."
INV_UP_ID=$(curl -sS "${AUTH[@]}" "$AWX_HOST/api/v2/inventory_sources/$SRC_ID/inventory_updates/?page_size=1" | jq -r '.results[0].id // empty' || true)
fi
if [ -z "$INV_UP_ID" ]; then
echo "ERROR: could not determine inventory_update id. Response was:" >&2
jq -C '.' "$tmpfile" || cat "$tmpfile" || true
exit 1
fi
echo "Inventory update id: $INV_UP_ID"
end=$((SECONDS + TIMEOUT_SECONDS))
status=""
while [ $SECONDS -lt $end ]; do
status=$(curl -sS "${AUTH[@]}" "$AWX_HOST/api/v2/inventory_updates/$INV_UP_ID/" | jq -r '.status // empty' || true)
echo "Status: ${status:-unknown}"
case "$status" in
pending|running)
sleep 2
;;
*)
break
;;
esac
done
echo "Final status: ${status:-unknown}"
echo "\n--- STDOUT (raw text) for inventory_update $INV_UP_ID ---"
curl -sS "${AUTH[@]}" "$AWX_HOST/api/v2/inventory_updates/$INV_UP_ID/stdout/?format=txt" || true

99
scripts/create_cred_and_ping.sh Executable file
View File

@@ -0,0 +1,99 @@
#!/usr/bin/env bash
set -euo pipefail
# create_cred_and_ping.sh
# Creates an AWX Machine credential from a local private key and runs an ad-hoc ping
# against three static hosts. Configure AWX_HOST/AWX_USER/AWX_PASS via env if needed.
AWX_HOST="${AWX_HOST:-http://192.168.49.2:30081}"
AWX_USER="${AWX_USER:-admin}"
AWX_PASS="${AWX_PASS:-Aase#1234!}"
KEY_PATH="${KEY_PATH:-$HOME/.ssh/awx_deploy_key}"
CREDS_NAME="${CREDS_NAME:-auto-machine-awx_deploy_key}"
USERNAME="${USERNAME:-admin}"
ORG_ID="${ORG_ID:-1}"
if [ ! -f "$KEY_PATH" ]; then
echo "ERROR: private key not found at $KEY_PATH" >&2
exit 1
fi
TMP_JSON="/tmp/awx_new_cred.json"
TMP_BODY="/tmp/awx_new_cred_body.json"
TMP_CODE="/tmp/awx_new_cred_code.txt"
cat > /tmp/make_cred.py <<'PY'
import json,sys
keypath=sys.argv[1]
name=sys.argv[2]
username=sys.argv[3]
ct=int(sys.argv[4])
org=int(sys.argv[5])
with open(keypath,'r',encoding='utf-8') as f:
key=f.read()
payload={'name': name, 'credential_type': ct, 'organization': org, 'inputs': {'username': username, 'ssh_key_data': key}}
print(json.dumps(payload))
PY
python3 /tmp/make_cred.py "$KEY_PATH" "$CREDS_NAME" "$USERNAME" 1 "$ORG_ID" > "$TMP_JSON"
echo "Creating AWX credential from $KEY_PATH..."
curl -sS -u "$AWX_USER:$AWX_PASS" -H "Content-Type: application/json" -X POST "$AWX_HOST/api/v2/credentials/" -d "@$TMP_JSON" -o "$TMP_BODY" -w "%{http_code}" > "$TMP_CODE" || true
CODE=$(cat "$TMP_CODE" || true)
echo "HTTP status: $CODE"
cat "$TMP_BODY" || true
if [ "$CODE" -lt 200 ] || [ "$CODE" -ge 300 ]; then
echo "Failed to create credential (status=$CODE)." >&2
exit 1
fi
NEW_CRED_ID=$(jq -r '.id // empty' "$TMP_BODY")
if [ -z "$NEW_CRED_ID" ]; then
echo "No credential id returned; aborting" >&2
exit 1
fi
echo "Created credential id=$NEW_CRED_ID"
# Prepare ad-hoc payload
INV_ID="${INV_ID:-2}"
LIMIT="host-192-168-1-144,host-192-168-1-23,host-192-168-1-24"
ADHOC_JSON="/tmp/awx_adhoc.json"
cat > "$ADHOC_JSON" <<EOF
{
"inventory": $INV_ID,
"credential": $NEW_CRED_ID,
"module_name": "ping",
"module_args": "",
"limit": "$LIMIT"
}
EOF
echo "Launching ad-hoc ping..."
RESP_FILE="/tmp/awx_adhoc_resp.json"
curl -sS -u "$AWX_USER:$AWX_PASS" -H "Content-Type: application/json" -X POST "$AWX_HOST/api/v2/ad_hoc_commands/" -d "@$ADHOC_JSON" -o "$RESP_FILE" || true
cat "$RESP_FILE" || true
ADHOC_ID=$(jq -r '.id // empty' "$RESP_FILE" || true)
if [ -z "$ADHOC_ID" ]; then echo "Failed to launch ad-hoc command" >&2; exit 1; fi
echo "Launched ad-hoc id=$ADHOC_ID; polling until finished (180s max)"
END=$((SECONDS+180))
while [ $SECONDS -lt $END ]; do
STATUS=$(curl -sS -u "$AWX_USER:$AWX_PASS" "$AWX_HOST/api/v2/ad_hoc_commands/$ADHOC_ID/" | jq -r '.status // empty' || true)
echo "Status: ${STATUS:-unknown}"
if [ -n "$STATUS" ] && [ "$STATUS" != "running" ] && [ "$STATUS" != "pending" ]; then
break
fi
sleep 2
done
echo "Final status: ${STATUS:-unknown}"
echo "--- events (first 500) ---"
curl -sS -u "$AWX_USER:$AWX_PASS" "$AWX_HOST/api/v2/ad_hoc_commands/$ADHOC_ID/events/?page_size=500" | jq '.results[] | {counter: .counter, event: .event, host_name: .host_name, stdout: .stdout}' || true
echo "--- stdout (text) ---"
curl -sS -u "$AWX_USER:$AWX_PASS" "$AWX_HOST/api/v2/ad_hoc_commands/$ADHOC_ID/stdout/?format=txt" || true
echo "Done. If SSH auth still fails, ensure the private key has access to the target hosts (or use a key with passwordless access)."

59
scripts/try_usernames_awx.sh Executable file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/env bash
set -euo pipefail
# try_usernames_awx.sh
# Patch an existing AWX Machine credential (id 5) with candidate usernames
# and run an ad-hoc ping against the three static hosts until one succeeds.
AWX_HOST="${AWX_HOST:-http://192.168.49.2:30081}"
AWX_USER="${AWX_USER:-admin}"
AWX_PASS="${AWX_PASS:-Aase#1234!}"
CRED_ID="${CRED_ID:-5}"
INV_ID="${INV_ID:-2}"
USERS=(admin alex alexpolo ubuntu core ec2-user root ansible)
for u in "${USERS[@]}"; do
echo
echo "===> Trying username=$u"
TMPPATCH=$(mktemp)
# build JSON with a literal "$encrypted$" for ssh_key_data so AWX keeps the stored key
printf '{"inputs":{"username":"%s","ssh_key_data":"$encrypted$"}}' "$u" > "$TMPPATCH"
curl -sS -u "$AWX_USER:$AWX_PASS" -H "Content-Type: application/json" -X PATCH "$AWX_HOST/api/v2/credentials/$CRED_ID/" -d "$TMPPATCH" -o /tmp/patch_resp.json || true
jq -r '.name, .inputs.username' /tmp/patch_resp.json || cat /tmp/patch_resp.json
TMPADHOC=$(mktemp)
cat > "$TMPADHOC" <<JSON
{"inventory": $INV_ID, "credential": $CRED_ID, "module_name": "ping", "limit": "host-192-168-1-144,host-192-168-1-23,host-192-168-1-24"}
JSON
resp=$(curl -sS -u "$AWX_USER:$AWX_PASS" -H "Content-Type: application/json" -X POST "$AWX_HOST/api/v2/ad_hoc_commands/" -d @$TMPADHOC)
id=$(echo "$resp" | jq -r .id)
echo "Launched ad-hoc id=$id"
if [ -z "$id" ] || [ "$id" = "null" ]; then
echo "launch failed: $resp"
continue
fi
# poll
for i in $(seq 1 45); do
status=$(curl -sS -u "$AWX_USER:$AWX_PASS" "$AWX_HOST/api/v2/ad_hoc_commands/$id/" | jq -r '.status // ""')
echo -n "."
if [ "$status" != "running" ] && [ "$status" != "pending" ] && [ -n "$status" ]; then
break
fi
sleep 2
done
echo
echo "Status: $status"
events=$(curl -sS -u "$AWX_USER:$AWX_PASS" "$AWX_HOST/api/v2/ad_hoc_commands/$id/events/?page_size=500")
echo "$events" | jq -r '.results[] | "EVENT: \(.event) HOST: \(.host_name)\n\(.stdout)\n---"'
ok=$(echo "$events" | jq -r '.results[] | select(.event=="runner_on_ok") | .host_name' | wc -l)
if [ "$ok" -gt 0 ]; then
echo "SUCCESS with username=$u"
exit 0
fi
done
echo "None of the usernames worked"