Table of Contents
- 2.1 AWX Installation
- 2.2 Creation of projects and templates
- 2.3 Adding credentials and inventories
- 2.4 Creation and execution of the first playbook
- 2.5 Configuring Role-Based Access Control (RBAC)
- 3.1 Error handling with Block and Rescue
- 3.2 Ansible loops and complex variables
- 3.3 Task execution strategies
- 3.4 Creating a dynamic inventory with Azure Resource Manager
- 4.1 Creating a namespace and a Kubernetes pod from AWX
- 4.2 Creating a Docker container from AWX
- 4.3 Configuring a network interface on a Juniper vSRX firewall
- 4.4 Calling a public REST API from AWX
- 4.5 Creating an S3 bucket from AWX
- 5.1 Ansible configuration optimization for performance
- 5.2 Implementation of asynchronous actions and polling
- 5.3 Scaling automation with AWX clusters
1. Introduction to the course
Welcome to Pluralsight. In this course, Efficient and Scalable Ansible Automation Strategies, you will dive into the world of automation with Ansible AWX through an intensive course focused on hands-on demonstrations. The teaching approach remains simple, concise and concrete so that you can gain confidence step by step.
The course begins by laying out the foundations: creating projects, templates and basic automation tasks. Gradually, we are moving to more advanced workflows, real-world integrations and powerful optimization techniques. This course is very demo-oriented — you can follow along and build your own AWX infrastructure from scratch.
Prerequisites
To get the most out of this course, it is recommended to have knowledge of:
- Infrastructure provisioning
- The YAML language
- Git
- Linux basics
- Basics of Kubernetes
2. Launch your first automation with AWX
2.1 Installing AWX
Context and environment
AWX is the upstream open source version of Ansible Automation Platform (AAP). The latest stable version at the time of this course is 24.6.1. AWX requires containers and Kubernetes to work. The environment used in this course is:
- Server: Ubuntu 24 LTS with Docker and Ansible preinstalled
- Client: Windows 11 from which we connect to the Ubuntu server via the Remote SSH extension of VS Code
- Kubernetes Distribution: minikube
All commands, scripts, and playbooks used in this course are available in the Exercise Files section.
Checking Docker permissions
Before installing anything, you must ensure that your user account has the necessary permissions to run Docker containers without root access:
groups $USER
If you see your user in the docker group, you are ready. Otherwise, run the following commands and restart your system:
sudo groupadd docker
sudo usermod -aG docker $USER
Script 1: Installing minikube and kubectl
The first script installs minikube and kubectl, configures permissions, displays minikube status and lists all running pods.
#!/bin/bash
echo "Installing kubectl..."
sudo snap install kubectl --classic
echo -e "\n\n\nDownloading Minikube binary..."
wget https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 -O minikube
echo -e "\n\n\nSetting executable permissions for Minikube..."
chmod 755 minikube
echo -e "\n\n\nMoving Minikube to /usr/local/bin..."
sudo mv minikube /usr/local/bin/
echo -e "\n\n\nChecking Minikube version..."
minikube version
echo -e "\e[96m\n\n\nDo you want to start Minikube now? (y/n): \e[0m"
read -r proceed
if [ "$proceed" != "y" ] && [ "$proceed" != "Y" ]; then
echo "Minikube start skipped. Exiting script."
exit 0
fi
echo -e "\n\n\nStarting Minikube with Docker driver..."
minikube start --vm-driver=docker
sleep 2
echo -e "\n\n\nChecking Minikube status..."
minikube status
sleep 2
echo -e "\n\n\nListing all Kubernetes pods across namespaces..."
kubectl get pods -A
Script 2: Installing the AWX Operator
The second script installs the AWX Operator. The AWX Operator is a tool that automates the deployment and management of AWX in the Kubernetes environment. It supports the complex tasks of deploying, scaling, and managing AWX instances in Kubernetes.
#!/bin/bash
echo "Step 1: Cloning AWX Operator GitHub repo..."
git clone https://github.com/ansible/awx-operator.git
cd awx-operator/
echo -e "\n\n\nStep 2: Checking out version 2.19.0..."
git checkout 2.19.0
echo -e "\n\n\nStep 3: Setting namespace and deploying AWX Operator..."
export NAMESPACE=ansible-awx
make deploy
sleep 2
echo -e "\n\n\nStep 4: Getting AWX pods in namespace 'ansible-awx'..."
kubectl get pods -n ansible-awx
echo -e "\n\n\nStep 5: Cloning awx-demo.yml for customization..."
cp awx-demo.yml awx-ubuntu.yml
echo -e "\n\n\nStep 5: Created awx-ubuntu.yml for awx installation..."
echo -e "\n\n\nEditing 'awx-ubuntu.yml' for awx installation:"
cat <<EOF > awx-ubuntu.yml
---
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: awx-ubuntu
spec:
service_type: NodePort
EOF
echo -e "\e[96m\n\n\nVerify the awx-ubuntu.yml file before beggining awx installation.\e[0m"
AWX Deployment
After executing the second script, the process proceeds as follows:
- The script clones the AWX Operator repository.
- The operator is installed and a custom resource file (
awx-ubuntu.yml) is created. - The
kubectlcommand is used to install AWX from this custom resource file:
kubectl apply -f awx-ubuntu.yml
- We can verify that the pods start in the
ansible-awxnamespace:
kubectl get pods -n ansible-awx
The AWX Operator controller manager pod is created first, followed by the Postgres pod and other required pods. This process takes approximately 10 minutes. It is important to ensure that no task has failed at the end.
- To obtain the internal URL of the AWX service:
minikube service awx-ubuntu-service -n ansible-awx --url
- To access AWX from external systems, you must perform port forwarding:
kubectl port-forward service/awx-ubuntu-service -n ansible-awx --address 0.0.0.0 10445:80
In this example, port 10445 is used. You can choose any available port.
AWX autostart script
To avoid having to manually restart the AWX pods after each system reboot, an autostart-awx.sh script is provided in the exercise files. This script can be scheduled via a cron job to run at startup:
#!/bin/bash
# Start Kubernetes
minikube start --vm-driver=docker
## Wait for Kubernetes to be ready
sleep 60
# Optional: export KUBECONFIG if needed
# export KUBECONFIG=/home/your-user/.kube/config
# Start port forwarding in background and redirect output
kubectl port-forward service/awx-ubuntu-service -n ansible-awx --address 0.0.0.0 10445:80 > /tmp/awx-port-forward.log 2>&1 &
Connecting to AWX
With the pods up and running, open a browser and navigate to http://<IP_Ubuntu>:10445. The AWX home page appears. To recover the default administrator password:
kubectl get secret awx-ubuntu-admin-password -n ansible-awx -o jsonpath="{.data.password}" | base64 --decode
Use this password with the admin account to log in. The first thing to do after logging in is to change the password: go to Users, click Edit and set a new password.
Installation Summary
The steps taken to install AWX:
- Installing minikube and kubectl
- Installing the AWX Operator
- Installing AWX via a custom resource file (
awx-ubuntu.yml)
2.2 Creating projects and templates
What is a project in AWX?
A project in AWX is simply a way to organize your playbooks. It links AWX to the Git repository (or other control source) where your playbooks are hosted.
Creating a project
- In the left menu, under Resources, click on Projects.
- Click on the blue Add button.
- Complete the fields:
- Name:
server_info_project - Organization:
Default - Source Control Type:
Git - Source Control URL: URL of the Git repository containing the playbooks
- Commit/Branch: last commit (this field can be updated throughout the course because the repository is updated)
- Source Code Credentials are not necessary if the repository is public.
- Click on Save.
As soon as the project is created, AWX performs a test synchronization with the Git repository. The result of this synchronization is visible under Jobs and should display a success status.
What is a job template?
A job template tells AWX what to run, where to run it and how to run it. It is the central object that orchestrates the execution of playbooks.
Creating a job template
- In the left menu, under Resources, click on Templates.
- Click Add and select Job Template.
- Complete the fields:
- Name:
gather_server_details - Job Type:
Run - Inventory: select an inventory (to be created later, for now use the default inventory)
- Project:
server_info_project - Playbook: select a playbook available in the Git repository
- Credentials: optional at this stage
- Variables: allows you to define additional variables passed to the playbook at runtime
- Advanced options allow fine control of execution (performance, target hosts, privilege escalation, external integrations, etc.). For now, click Save.
Then click on Launch to run the template. The job appears under Jobs and should appear as successful, confirming connectivity with Git and the ability to retrieve and run playbooks from the repository.
2.3 Adding credentials and inventories
Credentials
Credentials allow AWX to authenticate to target hosts or services. AWX already provides a default credential upon installation.
Creating a credential
- Under Resources, click on Credentials.
- Click on Add.
- Enter the Name:
windows_server_credential. - Choose the Credential Type according to your needs:
Amazon Web Services→ stores Access Key and Secret KeyGitHub Personal Access Token→ stores a tokenMachine→ stores username, password or private SSH key (this type is used here)
- Enter the username and password of the Windows administrator account to manage (used to connect via WinRM).
- There is also an option to store an SSH private key.
- At the bottom of the page, advanced options for privilege escalation:
- Linux: use
sudo - Windows: use
runas - These fields can be configured to request running information.
- Click on Save.
Inventories
An inventory is the list of servers on which you want to run tasks.
Creating an inventory
- Under Resources, click on Inventories then Add Inventory.
- Enter the Name:
windows_inventory. - The Instance Groups field is important: it allows you to control on which execution node the jobs run. This allows, for example, to assign certain inventories (production, staging) to different groups of instances for security or performance reasons.
- Click on Save.
- Go to the Hosts tab and click on Add.
- In the Name field, enter the hostname or IP address of the server to manage.
- Click on Save.
Job template update
Once the credentials and inventory have been created, update the template:
- Go to the template created previously.
- Under Inventory, select
windows_inventory. - Under Credentials, select the created credential.
- Save changes.
2.4 Creation and execution of the first playbook
Playbook hello_world.yml
The simplest possible playbook to verify that AWX is working correctly:
- name: Hello World Sample
hosts: localhost
gather_facts: false
tasks:
- name: Hello Message
debug:
msg: "Hello World!"
Playbook server_info.yml — First real playbook
This playbook retrieves and displays the hostname and IP address of the targeted Windows server.
- name: Display server hostname and IP address
hosts: all
gather_facts: no
tasks:
- name: Get hostname
win_shell: |
hostname
register: hostname_output
- name: Show hostname
debug:
var: hostname_output.stdout_lines
- name: Get IP Address
win_shell: |
(Get-NetIPAddress -AddressFamily IPv4).IPAddress
register: ip_address_output
- name: Show IP Address
debug:
var: ip_address_output.stdout_lines
Key points from this playbook:
hosts: all— The playbook runs on all hosts in the selected inventory.gather_facts: no— Additional server details will not be captured automatically by AWX.- Module
win_shellruns PowerShell scripts on Windows hosts. - The keyword
registersaves the result of the task in a variable. - The
debugmodule displays the results stored in variables.
WinRM configuration on Windows host
Before running the playbook, you must configure AWX to connect to the DC01 server via WinRM. In the inventory, go to host DC01 and edit the Variables field:
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore
Important Note: In a production environment, it is recommended to use certificate-based authentication, especially when a certificate authority infrastructure is in place. Refer to official Ansible documentation for detailed steps.
Your Windows server must also be configured to accept WinRM connections.
Using gather_facts
Playbook server_info_gather_facts.yml
This playbook demonstrates how to use Ansible’s gather_facts feature to automatically collect detailed server information:
- name: Display server hostname and IP address
hosts: all
gather_facts: yes
tasks:
- name: Show gathered facts
debug:
var: ansible_facts
With gather_facts: yes, AWX automatically collects a large amount of information: IP addresses, operating system information, CPU architecture, and much more — without writing additional commands.
Playbook using_gather_facts_data.yml — Conditionals
Data from gather_facts can be used in conditionals to perform more complex operations:
- name: Display server hostname and IP address
hosts: all
gather_facts: yes
tasks:
- name: Display message if running on VMware
debug:
msg: "This is a virtual server running on VMware"
when: ansible_facts['virtualization_type'] == "VMware"
This playbook uses the ansible_facts['virtualization_type'] fact to display a message only if the server is running on VMware. In the demonstration example, the message is displayed because server DC01 is actually running on a VMware platform.
2.5 Configuring Role-Based Access Control (RBAC)
RBAC Concept
In real-world environments, it is not desirable for everyone to have complete control over everything. This is where Role-Based Access Control (RBAC) comes in. RBAC provides fine control over who can do what in AWX.
Configuring RBAC in AWX
Steps to assign permissions to a user:
- Go to the resource to which you wish to give access (project, inventory, job template, etc.).
- In the demonstrated case, select the
gather_server_detailstemplate. - Go to the Access tab.
- Click on Add.
- You can assign roles:
- Direct to a user (approach used in demo)
- To a team (to organize users into groups)
- Select the user (
TestUser). - Assign the Execute role — this allows the user to only execute the template, without being able to modify or delete it.
Checking RBAC
When connecting with TestUser:
- The user can launch the template successfully.
- When opening the template properties, the Edit and Delete options are not available.
This perfectly demonstrates how RBAC works in AWX: each user only sees and can do what they have permissions for.
3. Building Complex Workflows with AWX
This module addresses more advanced workflows: management of blocks, loops, and intelligent execution strategies. You’ll see how AWX handles errors, dynamic data, and scaling on different hosts.
3.1 Error handling with Block and Rescue
Key concepts
Error handling in Ansible uses three keywords to define structured exception handling:
| Keyword | Behavior |
|---|---|
block | Groups several tasks logically; allows sharing rescue and always behaviors |
rescue | Runs when a task inside the block fails — useful for fallback or recovery actions |
always | Executes regardless of success or failure of block or rescue — similar to finally in traditional programming |
Playbook with intentional error — 2-block_rescue.yml
This playbook demonstrates error handling. The PowerShell command to get the IP address is intentionally incorrect (IPv instead of IPv4) to generate an error:
---
- name: Manage Windows Server Info
hosts: dc01
gather_facts: no
tasks:
- name: Main task block with error handling
block:
- name: Get Hostname
win_shell: hostname
register: hostname_output
- name: Display hostname
debug:
var: hostname_output.stdout_lines
- name: Get IP Address
win_shell: (Get-NetIPAddress -AddressFamily IPv).IPAddress
register: ip_address_output
- name: Display IP Address
debug:
var: ip_address_output.stdout_lines
rescue:
- name: Handle errors if hostname or IP retrieval fails
debug:
msg: "An error occurred while fetching server information."
always:
- name: Always run this task
debug:
msg: "Block execution completed, whether success or failure."
Playbook fixed — 2-block_rescue_OK.yml
The version corrected with the correct PowerShell command (IPv4):
---
- name: Manage Windows Server Info
hosts: dc01
gather_facts: no
tasks:
- name: Main task block with error handling
block:
- name: Get Hostname
win_shell: hostname
register: hostname_output
- name: Display hostname
debug:
var: hostname_output.stdout_lines
- name: Get IP Address
win_shell: (Get-NetIPAddress -AddressFamily IPv4).IPAddress
register: ip_address_output
- name: Display IP Address
debug:
var: ip_address_output.stdout_lines
rescue:
- name: Handle errors if hostname or IP retrieval fails
debug:
msg: "An error occurred while fetching server information."
always:
- name: Always run this task
debug:
msg: "Block execution completed, whether success or failure."
Behavior during execution
When running the playbook with the intentional error:
- The first task (retrieving the hostname) succeeds.
- The second task (IP retrieval) fails because of the incorrect command.
- The
rescuesection runs and displays the error message. - The
alwayssection runs in all cases and displays the completion message.
Note: If no errors occur, the
rescuesection does not execute. You can perform any valid action in therescuesection (sending notification, rollback, logging, etc.).
3.2 Ansible loops and complex variables
Loop concept
Ansible supports loops to run a task multiple times with different items, using the loop keyword (as well as other keywords like with_items). Loops simplify repetitive operations like creating users or installing packages.
Playbook 2-loops.yml
This playbook uses a list variable to monitor the status of several Windows services:
---
- name: Manage Windows Server Info
hosts: dc01
gather_facts: no
vars:
services_to_check:
- w32time
- bits
- spooler
tasks:
- name: Main task block with error handling
block:
- name: Check status of important services
win_service:
name: "{{ item }}"
loop: "{{ services_to_check }}"
rescue:
- name: Handle errors if Retrieving service status fails
debug:
msg: "An error occurred while fetching service information."
Key points:
- The
services_to_checkvariable contains a list of Windows service names to monitor. - The keyword
looploops through each service in the list. - The special variable
{{ item }}represents the current item in the loop. - The
win_servicemodule retrieves the status of each service. - This structure avoids writing repetitive tasks for each service.
At runtime, Ansible retrieves the status of services one by one. By clicking on the name of a service in the AWX interface, you can see its complete status.
3.3 Task execution strategies
Ansible execution policies define how tasks are distributed and executed across multiple hosts in your inventory. Each policy has distinct behavior and a specific use case.
Linear Strategy
Behavior: Runs one task at a time on all hosts, in the same order. Each task completes on all hosts before moving on to the next one.
Example: With 3 tasks and 3 hosts (A, B, C):
- Task 1 running on A, B and C → Task 1 completed on all
- Task 2 is running on A, B and C → Task 2 completed on all
- Task 3 runs on A, B and C
Use case:
- When you want a predictable execution flow.
- When tasks depend on each other between hosts (for example, configuration steps must complete everywhere before continuing).
Free Strategy
Behavior: Tasks run in parallel and independently of other hosts. Each host executes its tasks as quickly as possible without waiting for others. Tasks run in order per host, but not in sync with other hosts.
Example: It is possible for host A to reach Task 3 while host B is still on Task 1.
Use case:
- When speed takes priority over synchronization.
- Ideal for large environments and parallel workloads.
Host Pinned Strategy
Behavior: Similar to the linear strategy, but each host is assigned a dedicated worker thread throughout the play. This strategy ensures that output and execution remain isolated and consistent per host.
Use case:
- Mainly used for troubleshooting, testing or debugging.
- Less common in everyday automations.
Policy Summary
| Strategy | Recommended use |
|---|---|
linear | Consistent scheduling, dependencies between hosts |
free | Performance, wide environments |
host_pinned | Host control, debugging |
3.4 Creating a dynamic inventory with Azure Resource Manager
Dynamic Inventory Concept
In Ansible, creating a dynamic inventory means configuring AWX to pull host data from an external source. This external source can be:
- A custom script
- A cloud provider like Amazon EC2, Azure, VMware vCenter, etc.
Prerequisites: Create an Azure Core Service
The first step is to create an Azure backend service to allow AWX to authenticate with Azure. Run the following commands from your Azure terminal:
az ad sp create-for-rbac --name ansible-awx-sp --role Contributor \
--scopes /subscriptions/{Subscription_ID} --sdk-auth
The output contains the subscriptionId, clientId, clientSecret and tenantId.
Creating the Azure credential in AWX
From the output of the previous command, create a credential of type Microsoft Azure Resource Manager with:
- Subscription ID
- Client ID
- Secret Client
- Tenant ID
Creating dynamic inventory
- Under Inventories, create a new inventory named
Azure Dynamic Inventory. - Go to the Sources tab and add a source.
- Give the source a name and select the type Microsoft Azure Resource Manager.
- In the Variables section, you can apply filters by resource groups or tags. For this demo, we only retrieve the VMs from the
ansible-managedresource group:
resource_groups:
- ansible-managed
- Click on Sync.
Result
The synchronization appears in Jobs and should show a success status. Navigating through the created inventory, the Hosts tab displays the list of VMs imported from the ansible-managed resource group in Azure. Clicking on a VM provides detailed information about it.
4. AWX Integration with External Platforms
This module explores how to integrate Ansible AWX with external systems like Docker, Kubernetes, networking equipment and cloud providers. AWX can extend automation beyond servers and interact with real platforms and APIs.
4.1 Creating a Kubernetes namespace and pod from AWX
Objective
This playbook uses Ansible to create a Kubernetes namespace and deploy an Nginx pod to it, all orchestrated from AWX.
Playbook 3-integrate_docker_k8s.yml
---
- name: Deploy nginx pod
hosts: 192.168.5.8
gather_facts: no
vars:
namespace: demo-space2
tasks:
- name: Create namespace
kubernetes.core.k8s:
api_version: v1
kind: Namespace
name: "{{ namespace }}"
state: present
- name: Deploy nginx pod
kubernetes.core.k8s:
api_version: v1
kind: Pod
namespace: "{{ namespace }}"
name: nginx-pod2
state: present
definition:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
- name: Get list of pods
kubernetes.core.k8s_info:
kind: Pod
namespace: "{{ namespace }}"
register: pod_info
- name: Display pod list
debug:
var: pod_info.resources
Explanation of tasks:
-
Create namespace: Creates a new namespace
demo-space2. Namespaces allow resources to be logically grouped in a Kubernetes cluster, making management and isolation easier. -
Deploy nginx pod: Deploys a single pod running an Nginx container in the created namespace. The
kubernetes.core.k8smodule allows you to define the pod configuration via a simple YAML structure. The container image isnginxand port 80 is exposed. -
Get list of pods: Uses the
kubernetes.core.k8s_infomodule to retrieve the list of all running pods in thedemo-space2namespace. The output is saved in a variable. -
Display pod list: Displays pod information via the
debugmodule.
Verification
After execution, you can check the namespace and pod created from the terminal with the kubectl commands:
kubectl get namespaces
kubectl get pods -n demo-space2
4.2 Creating a Docker container from AWX
Objective
This playbook uses Ansible to deploy an Nginx web server inside a Docker container and serve a custom HTML page.
Playbook 3-docker_nginx_webapp.yml
---
- name: Deploy nginx container with custom webpage (module-based)
hosts: 192.168.5.8
gather_facts: no
vars:
html_path: /home/uzair/nginx-content
tasks:
- name: Ensure nginx-content directory exists
file:
path: "{{ html_path }}"
state: directory
- name: Create sample HTML page
copy:
dest: "{{ html_path }}/index.html"
content: |
<html>
<head><title>Welcome</title></head>
<body><h1>Hello from Ansible & NGINX (Docker module)!</h1></body>
</html>
- name: Pull nginx image
community.docker.docker_image:
name: nginx
source: pull
- name: Start nginx container with volume
community.docker.docker_container:
name: webapp-nginx
image: nginx
state: started
ports:
- "8080:80"
volumes:
- "{{ html_path }}:/usr/share/nginx/html:ro"
- name: Set directory permission to be world-readable
file:
path: "{{ html_path }}"
mode: '0755'
recurse: yes
- name: Set index.html file permission to be world-readable
file:
path: "{{ html_path }}/index.html"
mode: '0644'
Explanation of tasks:
-
Ensure nginx-content directory exists: Creates a local directory to store website content.
-
Create sample HTML page: Places a simple HTML file in this directory. This will be the home page served by the container.
-
Pull nginx image: Uses the
community.docker.docker_imagemodule to download the latest Nginx image from Docker Hub. -
Start nginx container with volume: Launches a new container via the
community.docker.docker_containermodule:
- Maps host port 8080 to container port 80
- Mount the local HTML directory as a read-only volume (
ro) at the default Nginx web root path:/usr/share/nginx/html
- Permissions: The last two tasks adjust the file and directory permissions to be readable by everyone — necessary for Nginx to be able to access and serve the files without permissions issues.
Verification
After execution, we can check that the container works via the curl command:
curl http://192.168.5.8:8080
You can also access the server IP address on port 8080 from a browser to view the web page.
4.3 Configuring a network interface on a Juniper vSRX firewall
Objective
This playbook uses Ansible to assign an IP address to the ge-0/0/0 network interface of a Juniper vSRX firewall.
Preparation
Before running the playbook, you can check the current state of the interfaces on the vSRX from your terminal:
show version
show interfaces terse | match ge
These commands allow you to display the device version and the list of physical interfaces starting with ge, to visually confirm the state before changes.
Playbook 3-vSRX_Configure_interface.yml
- name: Configure ge-0/0/0 interface on Juniper vSRX
hosts: 192.168.5.20
gather_facts: no
tasks:
- name: Get interfaces
junipernetworks.junos.junos_command:
commands:
- show interfaces terse
register: all_ifaces
- name: Filter ge-* interfaces
debug:
msg: "{{ item }}"
loop: "{{ all_ifaces.stdout_lines[0] | select('search', 'ge-') | list }}"
- name: Configure ge-0/0/0
junipernetworks.junos.junos_config:
lines:
- set interfaces ge-0/0/0 unit 0 family inet address 192.168.10.10/24
- name: Show config for ge-0/0/0
junipernetworks.junos.junos_command:
commands:
- show configuration interfaces ge-0/0/0
register: config_output
- name: Display config
debug:
var: config_output.stdout_lines
- name: Verify interfaces
junipernetworks.junos.junos_command:
commands:
- show interfaces terse
register: all_ifaces
- name: Filter ge-* interfaces
debug:
msg: "{{ item }}"
loop: "{{ all_ifaces.stdout_lines[0] | select('search', 'ge-') | list }}"
Explanation of tasks:
-
Get interfaces: Execute the
show interfaces tersecommand to obtain a snapshot of the current state of the interfaces. The result is stored in a variable. -
Filter ge- interfaces*: Uses a loop with a
select('search', 'ge-')filter to display only interfaces starting withge-. -
Configure ge-0/0/0: The main task — configures interface
ge-0/0/0with IP address192.168.10.10/24via modulejunipernetworks.junos.junos_config. This task applies the configuration directly to the device. -
Show config for ge-0/0/0: Shows the interface configuration to immediately confirm that the changes have been applied correctly.
-
Verify interfaces (repeat): Repeats the verification of interfaces after configuration to ensure that the interface is not only configured, but also appears correctly in the list of active interfaces.
Result
In the command output, we can see that the interface ge-0/0/0 appears with the configured IP address. By checking directly from the vSRX terminal, we confirm that the IP address has been assigned to the interface.
4.4 Calling a public REST API from AWX
Objective
This playbook demonstrates how to use Ansible not only for infrastructure tasks, but also to integrate external APIs and work with their JSON responses.
Playbook 3-call_public_api.yml
---
- name: Get list of cat breeds
hosts: localhost
gather_facts: no
tasks:
- name: Call the Cat Facts API for breeds
uri:
url: https://catfact.ninja/breeds?limit=10
method: GET
return_content: yes
headers:
Accept: application/json
register: cat_breeds
- name: Display first cat breed name
debug:
msg: "{{ cat_breeds.json.data }}"
Key points:
hosts: localhost— The API call is made from the control node itself.- Module
urisends an HTTP GET request to the Cat Facts API. - An
Accept: application/jsonheader is included to request a JSON response. - The response is stored in the
cat_breedsvariable. - The
debugmodule displays thedatafield of the JSON response, which contains the list of the first 10 cat breeds.
Note: In real-world scenarios, such API responses are typically used in tracking tasks rather than just being displayed in the console. For example, one could use the data to provision resources or make automation decisions.
4.5 Creating an S3 bucket from AWX
Objective
This playbook uses Ansible to create an S3 bucket and upload a file to it using AWS modules.
Playbook 3-s3_create_and_upload.yml
- name: Create S3 bucket and upload a file
hosts: localhost
connection: local
gather_facts: false
vars:
bucket_name: uzair-test-bucket-003
file_path: /tmp/sample.txt
s3_object_name: sample.txt
tasks:
- name: Create an S3 bucket
amazon.aws.s3_bucket:
name: "{{ bucket_name }}"
state: present
region: us-east-1
- name: Create a sample file to upload
shell: echo "Hello from Ansible!" > "{{ file_path }}"
args:
creates: "{{ file_path }}"
- name: Upload the file from /tmp/sample.txt to the S3 bucket
amazon.aws.s3_object:
bucket: "{{ bucket_name }}"
object: "{{ s3_object_name }}"
src: "{{ file_path }}"
mode: put
region: us-east-1
Key points:
connection: local— AWS tasks run locally on the control node (not on a remote host).amazon.aws.s3_bucket: Creates the S3 bucket. The bucket name is defined in a variable and must be globally unique on AWS.shellwithcreates: Generates a simple text file to upload. Thecreatesargument ensures that the file is only created if it does not already exist.amazon.aws.s3_object: Upload the file to the S3 bucket. The file is placed with the same name as the original file.
Result
After execution, we can verify in the AWS console that the new bucket has been created and that the file has been correctly uploaded to it.
Note on managing Azure with AWX
If you want to manage Azure with Ansible, additional steps are required. By default, AWX does not include the Python libraries or Azure CLI needed to manage Azure resources. To fix this, you need to create a custom runtime environment containing all the required dependencies.
Step Summary:
- Create a
requirements.txtfile listing all the Python packages needed to interact with Azure (eg:MSAL,azure-mgmt-resource, etc.). - Create a
requirements.ymlfile specifying the Ansible collections needed to manage Azure. - Write a
Dockerfilethat builds a custom image including these dependencies. - Build the image and push it to a container registry (Docker Hub or private registry).
- In AWX, create a new Execution Environment and configure it in the template by referencing the custom image from your registry.
Once configured, any job template using this runtime will download the image and run with all necessary Azure support.
Important note: This same approach can be applied to any other third-party platform not natively supported in AWX. Simply create a custom runtime environment with the required Ansible libraries and collections.
5. Scaling and Optimizing AWX for Performance
This module explores ways to optimize AWX for better performance and scale it to handle larger environments. Topics covered include key Ansible configuration file settings, asynchronous actions, fact caching, and clustering.
5.1 Optimizing Ansible configuration for performance
The ansible.cfg file in AWX
AWX automatically reads the file ansible.cfg from the root of the Git project. This allows you to customize Ansible’s behavior directly in AWX.
Reference ansible.cfg file
[defaults]
forks = 20
pipelining = True
timeout = 30
# Enable fact caching for faster subsequent runs
fact_caching = jsonfile
fact_caching_connection = /runner/facts
gathering = smart
fact_caching_timeout = 3600
Explanation of each parameter:
| Parameter | Value | Description |
|---|---|---|
forks | 20 | Allows running up to 20 parallel tasks on hosts, accelerating execution in multi-host environments |
pipelining | True | Reduces SSH overhead by minimizing the number of connections per task — speeds up execution by reducing SSH connection setup time |
timeout | 30 | 30-second connection timeout for non-responsive hosts — prevents delays by quickly switching to slow or unreachable hosts |
fact_caching | jsonfile | Enables the facts cache with a JSON file backend |
fact_caching_connection | /runner/facts | Cache location on disk |
gathering | smart | The gather_facts only runs when needed, instead of every run — speeds up execution by avoiding unnecessary fact gathering |
fact_caching_timeout | 3600 | Keeps data cached for 1 hour (3600 seconds) |
Fact caching demo playbook — 4-optimize_performance.yml
---
- name: Gather facts and validate caching
hosts: all
gather_facts: yes
tasks:
- name: Show remote hostname
debug:
msg: "Remote hostname is {{ ansible_hostname }}"
- name: Verify fact cache exists in /runner/facts
command: ls -l /runner/facts
delegate_to: localhost
run_once: true
- name: Show cached file content
command: cat /runner/facts/{{ inventory_hostname }}
delegate_to: localhost
run_once: true
ignore_errors: yes
Explanation of tasks:
-
Show remote hostname: Collects and displays the hostname of the managed host (in the demo, it is an Ubuntu server named Ubuntu 24).
-
Verify fact cache exists: Lists the contents of the
/runner/factsdirectory to confirm that the cache file has been created. Thedelegate_to:localhostparameter performs this task on the AWX control node.run_once: trueensures that the task runs only once, no matter how many hosts are in the inventory. -
Show cached file content: Shows the contents of the cache JSON file to confirm that the facts data is correctly saved.
Performance gain demonstration
- First run: The facts are not yet hidden → the job takes longer.
- Second run: AWX reuses the hidden facts instead of recollecting them → the job completes much faster.
This behavior clearly illustrates how caching can significantly improve the speed of playbook execution, especially in environments with many hosts.
5.2 Implementing asynchronous actions and polling
Problem to resolve
By default, Ansible waits for a task to complete before moving on to the next one. For long operations (installations, system updates, etc.), this can unnecessarily block the execution of the entire playbook.
Solution: async and poll parameters
async: Tells Ansible that the task is allowed to run for up to X seconds in the background.poll: When set to0, Ansible does not wait for the task to complete and immediately moves on to the next one.
Playbook 4-Async_poll.yml
---
- name: Run long task asynchronously
hosts: all
gather_facts: no
tasks:
- name: Simulate a long-running task
ansible.builtin.command: /bin/sleep 60
async: 120
poll: 0
- name: Display message while background task runs
ansible.builtin.debug:
msg: "The sleep command has been started in the background."
Explanation:
- The first task simulates a long operation by executing the
sleep 60command. async: 120— The task can run for up to 120 seconds.poll:0— Ansible does not wait for this task to complete and immediately moves on to the next task.- The second task immediately displays a confirmation message while the background task continues to run in parallel.
Demonstration result
When running in the AWX portal, the debug message is displayed instantly, while the background task continues to run. This is how async and poll improve the performance and responsiveness of playbooks in AWX.
Real use cases:
- Long Software Installations
- System updates
- Backup operations
- Any operation where you don’t want to block the entire playbook for a long running task
5.3 Scaling automation with AWX clusters
The problem of scaling up
In large-scale environments, running many concurrent jobs can saturate a single AWX instance. AWX supports horizontal scaling via clusters, allowing jobs to be run on multiple nodes to distribute load and improve performance.
AWX Distributed Architecture
AWX supports a distributed architecture composed of several types of nodes:
1. Control Plane
This is the main AWX instance — the one used in this course. It orchestrates and distributes jobs to execution nodes.
2. Execution Nodes
These are additional worker nodes running AWX runtime environment containers that handle the actual execution of the playbooks.
Features:
- Each execution node is assigned to an Instance Group.
- Instance Groups can be mapped to specific inventories, job templates or organizations.
- Jobs are dispatched from the control node and run on the execution nodes.
Advantages:
- Load balancing
- High availability
- Job segregation (job segregation according to nodes)
3. Hop Nodes
These are optional nodes that transfer jobs to other execution nodes. Useful for complex networks where the execution nodes are not directly accessible from the control node.
Concrete use cases
- Assign an execution node for all Windows automation playbooks.
- Another node for cloud provisioning tasks.
- This gives granular control over job distribution in large environments.
Configuring clusters in AWX
To add instances in the AWX portal:
- Go to Instances and click on Add.
- Enter the name, for example
Win-Inst-1. - In the Instance Type drop-down, select:
Execution Nodefor an execution nodeHop Nodefor a bounce node
- Repeat to add
Win-Inst-2.
Creating an Instance Group
- Under Instance Groups, create a new group named
Win-IG. - Go to the Instances tab and click on Associate to link the two Windows instances created previously to this group.
Association of a template to an Instance Group
- Choose a template and click Edit.
- In the Instance Groups option, select the
Win-IGgroup. - Click on Save.
Result: Each job associated with this template will run on any execution node that is part of this instance group. In this way, we can segregate and distribute jobs according to the operating system, the environment (production, staging) or the business units.
6. Summary and conclusion
This course covered the entire automation lifecycle with Ansible AWX, from installation to advanced optimization and scaling techniques:
Module Summary
| Module | Main theme | Duration |
|---|---|---|
| Module 1 | AWX installation, projects, templates, credentials, inventories, RBAC | 20m 56s |
| Module 2 | Block/Rescue, loops, execution strategies, dynamic inventories | 9m 26s |
| Module 3 | Kubernetes, Docker, Juniper, REST API, AWS S3 integration | 11m 6s |
| Module 4 | Ansible.cfg optimization, async/poll, clustering | 8m 44s |
Key concepts mastered
- AWX: Open source web interface for Ansible, based on Kubernetes via minikube
- Projects: Organization of playbooks via Git
- Job Templates: Execution orchestration (what, where, how)
- Credentials: Secure management of authentications (Machine, AWS, Azure, GitHub)
- Inventories: Static (manual hosts) and dynamic (Azure Resource Manager, AWS EC2, etc.)
- RBAC: Granular access control by user or team
- Block/Rescue/Always: Structured error management
- Loops: Processing lists with
loopand{{ item }} - Execution strategies:
linear,free,host_pinned - Integration modules:
kubernetes.core.k8s,community.docker,junipernetworks.junos,uri,amazon.aws - Fact caching: Acceleration of runs thanks to the JSON cache
- Async/Poll: Running long-running tasks in the background
- Clustering: Horizontal scaling with execution nodes, hop nodes and instance groups
Search Terms
efficient · scalable · ansible · automation · strategies · infrastructure · ci/cd · devops · awx · playbook · objective · azure · configuring · execution · inventory · rbac · template · concept · dynamic · installing · job · performance · scaling · script