AWX in Kubernetes: Part 2: Automating Setup

Published: Aug 23, 2022 by Isaac Johnson

Several times recently I have looked to automate the configuration of AWX, the OSS version of Ansible Tower. As a followup to my last post on AWX in Kubernetes, we will now configure the AWX setup in Kubernetes using curl and Kubernetes Jobs.

Everything I show could be done within the context of a Job, however, I’ll demonstrate with a mix of Kubernetes Jobs and directly commandline with curl.

Creating Organizations, SCM Credentials and Projects

First, let us show any organizations we have already created. We can do this with the REST API of AWX.

Since the ADWerx helm chart created secrets with our login, password and host we can use them in a Job.

$ cat awx_job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: awxtest
  namespace: adwerx
spec:
  backoffLimit: 4
  template:
    spec:
      containers:
      - name: test
        image: alpine
        envFrom:
        - secretRef:
            name: adwerxawx
        command:
        - bin/sh
        - -c
        - |
          apk --no-cache add curl
          apk --no-cache add jq
          curl --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X GET -H "Content-Type: application/json" http://$ADWERXAWX_SERVICE_HOST:$ADWERXAWX_SERVICE_PORT_HTTP/api/v2/organizations/ | jq
      restartPolicy: Never

$ kubectl apply -f awx_job.yaml
job.batch/awxtest created

We can then get the pod name from the event log and then use that to get logs

$ kubectl logs `kubectl describe job awxtest -n adwerx | grep "Created pod" | sed 's/^.*: //' | tr -d '\n
'` -n adwerx | grep url
(4/5) Installing libcurl (7.83.1-r2)
(5/5) Installing curl (7.83.1-r2)
      "url": "/api/v2/organizations/1/",

I can also do these steps on the command line to see details, like the Organization name

$ curl --silent --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X GET -H "Content-Type: application/json" htt
ps://awx.freshbrewed.science/api/v2/organizations/ | jq '.results[] | .name'
"onprem"

And I can see a project created already

$ curl --silent --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X GET -H "Content-Type: application/json" https://awx.freshbrewed.science/api/v2/projects/ | jq '.results[] | .name'
"My New Proejct"

Adding a Project

To add a project, we first need to create a credential. I’ll get the OrgID then use to create a GH PAT SCM Token.

$ export ORGID=`curl --silent --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X GET -H "Content-Type: application/json" https://awx.freshbrewed.science/api/v2/organizations/ | jq '.results[] | .id' | tr -d '\n'`

$ curl --silent --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X POST -H "Content-Type: application/json" https://awx.freshbrewed.science/api/v2/credentials/ --data "{\"credential_type\": 2, \"inputs\": { \"username\": \"idjohnson\", \"password\": \"ghp_************************************\" }, \"name\": \"ij_gh_pat\", \"organization\": $ORGID}"{"id":4,"type":"credential","url":"/api/v2/credentials/4/","related":{"named_url":"/api/v2/credentials/ij_gh_pat++Source Control+scm++onprem/","created_by":"/api/v2/users/1/","modified_by":"/api/v2/users/1/","organization":"/api/v2/organizations/1/","activity_stream":"/api/v2/credentials/4/activity_stream/","access_list":"/api/v2/credentials/4/access_list/","object_roles":"/api/v2/credentials/4/object_roles/","owner_users":"/api/v2/credentials/4/owner_users/","owner_teams":"/api/v2/credentials/4/owner_teams/","copy":"/api/v2/credentials/4/copy/","input_sources":"/api/v2/credentials/4/input_sources/","credential_type":"/api/v2/credential_types/2/"},"summary_fields":{"organization":{"id":1,"name":"onprem","description":"onprem datacenter"},"credential_type":{"id":2,"name":"Source Control","description":""},"created_by":{"id":1,"username":"admin","first_name":"","last_name":""},"modified_by":{"id":1,"username":"admin","first_name":"","last_name":""},"object_roles":{"admin_role":{"description":"Can manage all aspects of the credential","name":"Admin","id":60},"use_role":{"description":"Can use the credential in a job template","name":"Use","id":61},"read_role":{"description":"May view settings for the credential","name":"Read","id":62}},"user_capabilities":{"edit":true,"delete":true,"copy":true,"use":true},"owners":[{"id":1,"type":"organization","name":"onprem","description":"onprem datacenter","url":"/api/v2/organizations/1/"}]},"created":"2022-08-18T00:29:22.429042Z","modified":"2022-08-18T00:29:22.429088Z","name":"ij_gh_pat","description":"","organization":1,"credential_type":2,"managed_by_tower":false,"inputs":{"password":"$encrypted$","username":"idjohnson"},"kind":"scm","cloud":false,"kubernetes":false}

Now to use to create a project, I’ll update the Job and relaunch it

$ cat awx_job.yaml 
apiVersion: batch/v1
kind: Job
metadata:
  name: awxtest
  namespace: adwerx
spec:
  backoffLimit: 4
  template:
    spec:
      containers:
      - name: test
        image: alpine
        envFrom:
        - secretRef:
            name: adwerxawx
        command:
        - bin/sh
        - -c
        - |
          apk --no-cache add curl
          apk --no-cache add jq

          # Org ID for final curl
          curl --silent --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X GET -H "Content-Type: application/json" https://awx.freshbrewed.science/api/v2/organizations/ | jq '.results[] | select(.name=="onprem") | .id' > ./ORGID
          export ORGID=`cat ./ORGID | tr -d '\n'`

          # Easier to use org name in JQ. Tail in case we have multiple vars
          curl --silent --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X GET -H "Content-Type: application/json" http://$ADWERXAWX_SERVICE_HOST:$ADWERXAWX_SERVICE_PORT_HTTP/api/v2/credentials/ |  jq '.results[] | select((.kind == "scm") and (.summary_fields.organization.name == "onprem")) | .id' | tail -n1 > GHTOKENID
          export GHID=`cat ./GHTOKENID | tr -d '\n'`

          curl --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X POST -H "Content-Type: application/json" http://$ADWERXAWX_SERVICE_HOST:$ADWERXAWX_SERVICE_PORT_HTTP/api/v2/projects/ --data "{ \"name\": \"FBDevOps\", \"description\": \"FB SRE Project\", \"scm_type\": \"git\", \"scm_url\": \"https://github.com/idjohnson/ansible-playbooks.git\", \"scm_branch\": \"main\", \"scm_clean\": false, \"credential\": $GHID, \"timeout\": 0, \"organization\": $ORGID, \"scm_update_on_launch\": true, \"scm_update_cache_timeout\": 0, \"allow_override\": false }"
      restartPolicy: Never

Now delete and relaunch

$ kubectl delete -f awx_job.yaml
job.batch "awxtest" deleted

$ kubectl apply -f awx_job.yaml
job.batch/awxtest created

We can see it completed:

$ kubectl get job -n adwerx
NAME       COMPLETIONS   DURATION   AGE
awxtest    1/1           6s         8s

We can see the new project and verify it synced content

/content/images/2022/08/awxautomation-11.png

At this point we have an Organization, a proper GH Credential, and a Project that uses it.

I should note we can create a “Github PAT”, but best I can tell, it isn’t usable with projects.

curl --user admin:********-X POST -H "Content-Type: application/json" "https://awx.freshbrewed.science/api/v2/credentials/" --data '{"credential_type": 11, "inputs": { "token": "myOtherToken" }, "name": "anotherTestToken", "organization": 1}'

Adding a user

Perhaps we do not wish to do everything as the built in Admin user.
We can create a new user with admin rights using the REST API:

curl --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X POST -H "Content-Type: application/json" "https://awx.freshbrewed.science/api/v2/users/" --data '{"email":"isaac@freshbrewed.science","first_name": "Isaac","is_superuser": true, "last_name": "Johnson", "password": "MyUsersPassword", "username":"isaac"}'

Now to test it, we could use the new user to fetch our existing tokens:

$ curl --silent --user isaac:MyUsersPassword -X GET -H "Content-Type: application/json" "https://awx.freshbrewed.science/api/v2/credentials/" | jq '.results[] | .name'
"anotherTestToken"
"ansibleuser"
"builder"
"builderByKey"
"builderNetwork"
"hpTest"
"idj_gh_token"
"ij_gh_pat"
"myFakeGithub"

Adding a host

We first need an inventory to contain the host.

We’ll create it in our first organization (1):

$ curl --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X POST -H "Content-Type: application/json" "https://awx.freshbrewed.science/api/v2/inventories/" --data '{"name": "MyFirstInventory","description": "My First Inventory", "organization": 1, "variables": "---" }'
{"id":2,"type":"inventory","url":"/api/v2/inventories/2/","related":{"named_url":"/api/v2/inventories/MyFirstInventory++onprem/","created_by":"/api/v2/users/35/","modified_by":"/api/v2/users/35/","hosts":"/api/v2/inventories/2/hosts/","groups":"/api/v2/inventories/2/groups/","root_groups":"/api/v2/inventories/2/root_groups/","variable_data":"/api/v2/inventories/2/variable_data/","script":"/api/v2/inventories/2/script/","tree":"/api/v2/inventories/2/tree/","inventory_sources":"/api/v2/inventories/2/inventory_sources/","update_inventory_sources":"/api/v2/inventories/2/update_inventory_sources/","activity_stream":"/api/v2/inventories/2/activity_stream/","job_templates":"/api/v2/inventories/2/job_templates/","ad_hoc_commands":"/api/v2/inventories/2/ad_hoc_commands/","access_list":"/api/v2/inventories/2/access_list/","object_roles":"/api/v2/inventories/2/object_roles/","instance_groups":"/api/v2/inventories/2/instance_groups/","copy":"/api/v2/inventories/2/copy/","organization":"/api/v2/organizations/1/"},"summary_fields":{"organization":{"id":1,"name":"onprem","description":"onprem datacenter"},"created_by":{"id":35,"username":"isaac","first_name":"Isaac","last_name":"Johnson"},"modified_by":{"id":35,"username":"isaac","first_name":"Isaac","last_name":"Johnson"},"object_roles":{"admin_role":{"description":"Can manage all aspects of the inventory","name":"Admin","id":83},"update_role":{"description":"May update the inventory","name":"Update","id":84},"adhoc_role":{"description":"May run ad hoc commands on the inventory","name":"Ad Hoc","id":85},"use_role":{"description":"Can use the inventory in a job template","name":"Use","id":86},"read_role":{"description":"May view settings for the inventory","name":"Read","id":87}},"user_capabilities":{"edit":true,"delete":true,"copy":true,"adhoc":true}},"created":"2022-08-23T11:14:19.740912Z","modified":"2022-08-23T11:14:19.740974Z","name":"MyFirstInventory","description":"My First Inventory","organization":1,"kind":"","host_filter":null,"variables":"---","has_active_failures":false,"total_hosts":0,"hosts_with_active_failures":0,"total_groups":0,"has_inventory_sources":false,"total_inventory_sources":0,"inventory_sources_with_failures":0,"insights_credential":null,"pending_deletion":false}

And we can see it reflected

/content/images/2022/08/awxautomation-01.png

Next, we can add a host to it.

Note: we will circle back on the credentials for the host, but at this moment, we just need to add the host into the Inventory.

I’ll want to use a human readable name, but be specific on the local IP. If your network has proper name resolution, the IP variable is not required.

We can also pipe the output to jq to see a much easier to read output.

$ curl --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X POST -H "Content-Type: application/json" "https://awx.freshbrewed.science/api/v2/hosts/" --data '{ "name": "AnnaMacbook", "description": "Annas Macbook Air", "inventory": 2, "enabled": true, "variables": "---\nansible_host: 192.168.1.81\nansible_connection: ssh"}' | jq 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1997  100  1831  100   166   2495    226 --:--:-- --:--:-- --:--:--  2743
{
  "id": 3,
  "type": "host",
  "url": "/api/v2/hosts/3/",
  "related": {
    "named_url": "/api/v2/hosts/AnnaMacbook++MyFirstInventory++onprem/",
    "created_by": "/api/v2/users/35/",
    "modified_by": "/api/v2/users/35/",
    "variable_data": "/api/v2/hosts/3/variable_data/",
    "groups": "/api/v2/hosts/3/groups/",
    "all_groups": "/api/v2/hosts/3/all_groups/",
    "job_events": "/api/v2/hosts/3/job_events/",
    "job_host_summaries": "/api/v2/hosts/3/job_host_summaries/",
    "activity_stream": "/api/v2/hosts/3/activity_stream/",
    "inventory_sources": "/api/v2/hosts/3/inventory_sources/",
    "smart_inventories": "/api/v2/hosts/3/smart_inventories/",
    "ad_hoc_commands": "/api/v2/hosts/3/ad_hoc_commands/",
    "ad_hoc_command_events": "/api/v2/hosts/3/ad_hoc_command_events/",
    "insights": "/api/v2/hosts/3/insights/",
    "ansible_facts": "/api/v2/hosts/3/ansible_facts/",
    "inventory": "/api/v2/inventories/2/"
  },
  "summary_fields": {
    "inventory": {
      "id": 2,
      "name": "MyFirstInventory",
      "description": "My First Inventory",
      "has_active_failures": false,
      "total_hosts": 0,
      "hosts_with_active_failures": 0,
      "total_groups": 0,
      "has_inventory_sources": false,
      "total_inventory_sources": 0,
      "inventory_sources_with_failures": 0,
      "organization_id": 1,
      "kind": ""
    },
    "created_by": {
      "id": 35,
      "username": "isaac",
      "first_name": "Isaac",
      "last_name": "Johnson"
    },
    "modified_by": {
      "id": 35,
      "username": "isaac",
      "first_name": "Isaac",
      "last_name": "Johnson"
    },
    "user_capabilities": {
      "edit": true,
      "delete": true
    },
    "groups": {
      "count": 0,
      "results": []
    },
    "recent_jobs": []
  },
  "created": "2022-08-23T11:25:07.500148Z",
  "modified": "2022-08-23T11:25:07.500191Z",
  "name": "AnnaMacbook",
  "description": "Annas Macbook Air",
  "inventory": 2,
  "enabled": true,
  "instance_id": "",
  "variables": "---\nansible_host: 192.168.1.81\nansible_connection: ssh",
  "has_active_failures": false,
  "has_inventory_sources": false,
  "last_job": null,
  "last_job_host_summary": null,
  "insights_system_id": null,
  "ansible_facts_modified": null
}

We can see this new host now listed in the Inventory

/content/images/2022/08/awxautomation-02.png

And if we check the YAML in the Variables for the host, it confirms what IP ansible will use

/content/images/2022/08/awxautomation-03.png

Machine Credentials: What should work

We should be able to create a credential with a valid user and password for this host:

$ curl --silent --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X POST -H "Content-Type: application/json" https://awx.freshbrewed.science/api/v2/credentials/ --data "{\"credential_type\": 1, \"inputs\": { \"username\": \"builder\", \"password\": \"RTheUsersPassword\" }, \"name\": \"AnnaMachineCredential\", \"organization\": 1}" | jq
{
  "id": 10,
  "type": "credential",
  "url": "/api/v2/credentials/10/",
  "related": {
    "named_url": "/api/v2/credentials/AnnaMachineCredential++Machine+ssh++onprem/",
    "created_by": "/api/v2/users/35/",
    "modified_by": "/api/v2/users/35/",
    "organization": "/api/v2/organizations/1/",
    "activity_stream": "/api/v2/credentials/10/activity_stream/",
    "access_list": "/api/v2/credentials/10/access_list/",
    "object_roles": "/api/v2/credentials/10/object_roles/",
    "owner_users": "/api/v2/credentials/10/owner_users/",
    "owner_teams": "/api/v2/credentials/10/owner_teams/",
    "copy": "/api/v2/credentials/10/copy/",
    "input_sources": "/api/v2/credentials/10/input_sources/",
    "credential_type": "/api/v2/credential_types/1/"
  },
  "summary_fields": {
    "organization": {
      "id": 1,
      "name": "onprem",
      "description": "onprem datacenter"
    },
    "credential_type": {
      "id": 1,
      "name": "Machine",
      "description": ""
    },
    "created_by": {
      "id": 35,
      "username": "isaac",
      "first_name": "Isaac",
      "last_name": "Johnson"
    },
    "modified_by": {
      "id": 35,
      "username": "isaac",
      "first_name": "Isaac",
      "last_name": "Johnson"
    },
    "object_roles": {
      "admin_role": {
        "description": "Can manage all aspects of the credential",
        "name": "Admin",
        "id": 88
      },
      "use_role": {
        "description": "Can use the credential in a job template",
        "name": "Use",
        "id": 89
      },
      "read_role": {
        "description": "May view settings for the credential",
        "name": "Read",
        "id": 90
      }
    },
    "user_capabilities": {
      "edit": true,
      "delete": true,
      "copy": true,
      "use": true
    },
    "owners": [
      {
        "id": 1,
        "type": "organization",
        "name": "onprem",
        "description": "onprem datacenter",
        "url": "/api/v2/organizations/1/"
      }
    ]
  },
  "created": "2022-08-23T11:33:44.926758Z",
  "modified": "2022-08-23T11:33:44.926802Z",
  "name": "AnnaMachineCredential",
  "description": "",
  "organization": 1,
  "credential_type": 1,
  "managed_by_tower": false,
  "inputs": {
    "password": "$encrypted$",
    "username": "builder"
  },
  "kind": "ssh",
  "cloud": false,
  "kubernetes": false
}

And we can see it created

/content/images/2022/08/awxautomation-04.png

However, if we test with a Job (as we see below) we will see an error (and perhaps this is just a “my environment” issue)

Testing

Go to the inventories where you can see hosts, and click the host and choose “Run Command”

/content/images/2022/08/awxautomation-05.png

Choose the command to run (Shell) and a Debug verbosity

/content/images/2022/08/awxautomation-06.png

Then check our machine credential

/content/images/2022/08/awxautomation-07.png

and click the “Launch” button at the bottom.

For me, I needed to refresh the page for the output

/content/images/2022/08/awxautomation-08.png

I can test this user manually and it works fine. Why I get the UNREACHABLE error still puzzles me

AnnaMacbook | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Permission denied, please try again.\r\nPermission denied, please try again.\r\nbuilder@192.168.1.81: Permission denied (publickey,password).",
    "unreachable": true
}

Using SSH Credential

However, it is possible for us to use a passwordless SSH credential instead which is a more common approach to using Ansible.

I’ll assume you know how to copy ~/.ssh/id_rsa.pub to the target host (e.g. builder@anna-MacBookAir:~$ vi ~/.ssh/authorized_keys).

We can how use that private key in an SSH credential

$ SSHKEY=$(cat ~/.ssh/id_rsa) && echo ${SSHKEY2//$'\n'/\\n} > /tmp/key_escaped
$ curl --silent --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X POST -H "Content-Type: application/json" https://awx.freshbrewed.science/api/v2/credentials/ --data "{\"credential_type\": 1, \"inputs\": { \"username\": \"builder\", \"ssh_key_data\": \"`cat /tmp/key_escaped`\" }, \"name\": \"AnnaMachineCredentialSSH\", \"organization\": 1}" | jq
{
  "id": 11,
  "type": "credential",
  "url": "/api/v2/credentials/11/",
  "related": {
    "named_url": "/api/v2/credentials/AnnaMachineCredentialSSH++Machine+ssh++onprem/",
    "created_by": "/api/v2/users/35/",
    "modified_by": "/api/v2/users/35/",
    "organization": "/api/v2/organizations/1/",
    "activity_stream": "/api/v2/credentials/11/activity_stream/",
    "access_list": "/api/v2/credentials/11/access_list/",
    "object_roles": "/api/v2/credentials/11/object_roles/",
    "owner_users": "/api/v2/credentials/11/owner_users/",
    "owner_teams": "/api/v2/credentials/11/owner_teams/",
    "copy": "/api/v2/credentials/11/copy/",
    "input_sources": "/api/v2/credentials/11/input_sources/",
    "credential_type": "/api/v2/credential_types/1/"
  },
  "summary_fields": {
    "organization": {
      "id": 1,
      "name": "onprem",
      "description": "onprem datacenter"
    },
    "credential_type": {
      "id": 1,
      "name": "Machine",
      "description": ""
    },
    "created_by": {
      "id": 35,
      "username": "isaac",
      "first_name": "Isaac",
      "last_name": "Johnson"
    },
    "modified_by": {
      "id": 35,
      "username": "isaac",
      "first_name": "Isaac",
      "last_name": "Johnson"
    },
    "object_roles": {
      "admin_role": {
        "description": "Can manage all aspects of the credential",
        "name": "Admin",
        "id": 91
      },
      "use_role": {
        "description": "Can use the credential in a job template",
        "name": "Use",
        "id": 92
      },
      "read_role": {
        "description": "May view settings for the credential",
        "name": "Read",
        "id": 93
      }
    },
    "user_capabilities": {
      "edit": true,
      "delete": true,
      "copy": true,
      "use": true
    },
    "owners": [
      {
        "id": 1,
        "type": "organization",
        "name": "onprem",
        "description": "onprem datacenter",
        "url": "/api/v2/organizations/1/"
      }
    ]
  },
  "created": "2022-08-23T11:46:36.475303Z",
  "modified": "2022-08-23T11:46:36.475340Z",
  "name": "AnnaMachineCredentialSSH",
  "description": "",
  "organization": 1,
  "credential_type": 1,
  "managed_by_tower": false,
  "inputs": {
    "username": "builder",
    "ssh_key_data": "$encrypted$"
  },
  "kind": "ssh",
  "cloud": false,
  "kubernetes": false
}
$ rm /tmp/key_escaped 

We will run the same Job, but this time pick our new SSH credential

/content/images/2022/08/awxautomation-09.png

And we can see it ran

/content/images/2022/08/awxautomation-10.png

As an aside, I had to do that funny business with a temp file because the BASH interpreter on the Mac is a bit different than what you would get on other distros. Had we been on WSL or another Ubuntu host we could have used

... \"ssh_key_data\": \"`$( sed -z 's/\n/\\n/g' ~/.ssh/id_rsa )`\" }, ...

Adding a Job Template

The last step is to create a template to run

Here, we can use the cloudcustodian template we created a few weeks ago

$ curl --silent --user $AWX_ADMIN_USER:$AWX_ADMIN_PASSWORD -X POST -H "Content-Type: application/json" https://awx.freshbrewed.science/api/v2/job_templates/ --data '{"name": "Run Azure CloudCustodian", "description": "Run the Azure Cloud Custodian Playbook", "job_type": "run", "inventory": 2, "project": 35, "playbook": "cloudcustodian.yaml", "extra_vars": "---", "timeout": 0, "ask_credential_on_launch": true, "become_enabled": false, "allow_simultaneous": false }'
{"id":37,"type":"job_template","url":"/api/v2/job_templates/37/","related":{"named_url":"/api/v2/job_templates/Run Azure CloudCustodian++onprem/","created_by":"/api/v2/users/35/","modified_by":"/api/v2/users/35/","labels":"/api/v2/job_templates/37/labels/","inventory":"/api/v2/inventories/2/","project":"/api/v2/projects/35/","organization":"/api/v2/organizations/1/","credentials":"/api/v2/job_templates/37/credentials/","jobs":"/api/v2/job_templates/37/jobs/","schedules":"/api/v2/job_templates/37/schedules/","activity_stream":"/api/v2/job_templates/37/activity_stream/","launch":"/api/v2/job_templates/37/launch/","webhook_key":"/api/v2/job_templates/37/webhook_key/","webhook_receiver":"","notification_templates_started":"/api/v2/job_templates/37/notification_templates_started/","notification_templates_success":"/api/v2/job_templates/37/notification_templates_success/","notification_templates_error":"/api/v2/job_templates/37/notification_templates_error/","access_list":"/api/v2/job_templates/37/access_list/","survey_spec":"/api/v2/job_templates/37/survey_spec/","object_roles":"/api/v2/job_templates/37/object_roles/","instance_groups":"/api/v2/job_templates/37/instance_groups/","slice_workflow_jobs":"/api/v2/job_templates/37/slice_workflow_jobs/","copy":"/api/v2/job_templates/37/copy/"},"summary_fields":{"organization":{"id":1,"name":"onprem","description":"onprem datacenter"},"inventory":{"id":2,"name":"MyFirstInventory","description":"My First Inventory","has_active_failures":false,"total_hosts":1,"hosts_with_active_failures":0,"total_groups":0,"has_inventory_sources":false,"total_inventory_sources":0,"inventory_sources_with_failures":0,"organization_id":1,"kind":""},"project":{"id":35,"name":"FBDevOps","description":"FB SRE Project","status":"successful","scm_type":"git"},"created_by":{"id":35,"username":"isaac","first_name":"Isaac","last_name":"Johnson"},"modified_by":{"id":35,"username":"isaac","first_name":"Isaac","last_name":"Johnson"},"object_roles":{"admin_role":{"description":"Can manage all aspects of the job template","name":"Admin","id":113},"execute_role":{"description":"May run the job template","name":"Execute","id":114},"read_role":{"description":"May view settings for the job template","name":"Read","id":115}},"user_capabilities":{"edit":true,"delete":true,"start":true,"schedule":true,"copy":true},"labels":{"count":0,"results":[]},"recent_jobs":[],"credentials":[]},"created":"2022-08-23T12:52:47.385947Z","modified":"2022-08-23T12:52:47.385978Z","name":"Run Azure CloudCustodian","description":"Run the Azure Cloud Custodian Playbook","job_type":"run","inventory":2,"project":35,"playbook":"cloudcustodian.yaml","scm_branch":"","forks":0,"limit":"","verbosity":0,"extra_vars":"---","job_tags":"","force_handlers":false,"skip_tags":"","start_at_task":"","timeout":0,"use_fact_cache":false,"organization":1,"last_job_run":null,"last_job_failed":false,"next_job_run":null,"status":"never updated","host_config_key":"","ask_scm_branch_on_launch":false,"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":true,"survey_enabled":false,"become_enabled":false,"diff_mode":false,"allow_simultaneous":false,"custom_virtualenv":null,"job_slice_count":1,"webhook_service":"","webhook_credential":null}

We now see that Job Template listed and ready to use

/content/images/2022/08/awxautomation-12.png

Summary

Today we setup our Organization, credentials and projects in AWX. We setup an inventory, added a host, the SSH credential and lastly tested interactively with a bespoke “hello work” test job.

Using this pattern we could easily tie to a local-exec TF provisioner or wrap the configuration into a Helm chart using Kubernetes jobs or even use the Terraform batch/v1 provider

awx kubernetes ansible

Have something to add? Feedback? Try our new forums

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes