Ansible AWX Tower Installation and Configuration

Ansible AWX is the open-source upstream project for Ansible Tower, providing a web-based user interface, REST API, and multi-playbook orchestration engine for Ansible. AWX enables centralized management of Ansible deployments, provides role-based access control, credential management, and workflow capabilities for complex orchestrations. This guide covers installation on Kubernetes and Docker, configuring projects and inventories, managing credentials, setting up job templates, implementing RBAC, and building workflows.

Table of Contents

  1. AWX Overview and Architecture
  2. Kubernetes Installation
  3. Docker Compose Installation
  4. Configuring Projects
  5. Managing Inventories
  6. Credential Management
  7. Creating Job Templates
  8. Role-Based Access Control
  9. Building Workflows
  10. Conclusion

AWX Overview and Architecture

Ansible AWX provides enterprise-grade automation capabilities built on the Ansible engine. It separates the control plane (UI, API, database) from execution environments, allowing scalable automation across distributed infrastructure.

Key components:

Web UI: Browser-based interface for managing automation, accessible on port 80/443 REST API: Complete programmatic access to all AWX functionality PostgreSQL Database: Stores configuration, credentials, job history, and metadata Redis Cache: Provides task queue management and caching Execution Environments: Containerized environments where Ansible playbooks execute

The architecture supports multiple execution nodes, allowing you to scale playbook execution across distributed systems while maintaining centralized control and visibility.

Kubernetes Installation

Installing AWX on Kubernetes requires the AWX Operator. This approach is recommended for production deployments.

Prerequisites:

# Verify Kubernetes cluster access
kubectl cluster-info
kubectl get nodes

# Ensure you have sufficient resources
# Recommended: 4 CPU, 4GB RAM minimum for AWX pod

Install the AWX Operator:

# Create namespace
kubectl create namespace awx

# Clone the AWX Operator repository
git clone https://github.com/ansible/awx-operator.git
cd awx-operator

# Check out a stable release
git checkout 2.8.0
# or latest: git checkout devel

# Install the operator
make deploy NAMESPACE=awx

Verify operator installation:

kubectl get deployments -n awx
kubectl get pods -n awx

# Watch operator logs
kubectl logs -n awx deployment/awx-operator-controller-manager -f

Create an AWX instance using a custom resource:

cat > awx-instance.yml << 'EOF'
---
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
  name: awx
  namespace: awx
spec:
  # Basic configuration
  admin_user: admin
  admin_password_secret: awx-admin-password
  
  # Database configuration (use external PostgreSQL in production)
  postgres_init_storage: 10Gi
  postgres_storage_class: standard
  
  # Image configuration
  image: quay.io/ansible/awx
  image_version: 24.2.0
  
  # Resource limits
  web_resource_limits:
    cpu: 1000m
    memory: 2Gi
  postgres_resource_limits:
    cpu: 500m
    memory: 1Gi
  redis_resource_limits:
    cpu: 500m
    memory: 1Gi
  
  # Network configuration
  service_type: LoadBalancer
  ingress_type: ingress
  ingress_tls_secret: awx-tls
  
  # Execution environment
  execution_environments:
    - name: default
      image: quay.io/ansible/creator-base:latest
EOF

# Create AWX admin secret
kubectl create secret generic awx-admin-password \
  --from-literal=password=YourSecurePassword123 \
  -n awx

# Create TLS certificate (optional, for HTTPS)
kubectl create secret tls awx-tls \
  --cert=path/to/cert.pem \
  --key=path/to/key.pem \
  -n awx

# Deploy AWX instance
kubectl apply -f awx-instance.yml

Wait for AWX deployment to complete:

# Monitor pod creation
kubectl get pods -n awx -w

# Check AWX status
kubectl get awx -n awx
kubectl describe awx awx -n awx

# Wait until status shows 'Successful'

Access AWX:

# Get service details
kubectl get svc -n awx

# Port forward for local access
kubectl port-forward -n awx svc/awx-service 8080:80

# Then access http://localhost:8080
# Default username: admin
# Password: from awx-admin-password secret

For production, set up proper ingress:

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: awx-ingress
  namespace: awx
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - awx.example.com
      secretName: awx-tls
  rules:
    - host: awx.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: awx-service
                port:
                  number: 80

Docker Compose Installation

For development and testing, Docker Compose provides a simpler installation method.

Prerequisites:

# Install Docker and Docker Compose
docker --version
docker-compose --version

# System requirements
# Minimum: 2 CPU, 2GB RAM
# Recommended: 4+ CPU, 4+ GB RAM

Clone the AWX repository:

git clone https://github.com/ansible/awx.git
cd awx

# Check out a release
git checkout 24.2.0

Configure Docker Compose environment:

# Navigate to installer directory
cd installer

# Edit inventory file
cat > inventory << 'EOF'
localhost ansible_connection=local

[all:vars]
# Admin user configuration
admin_user=admin
admin_password=your_secure_password

# Docker configuration
awx_container_init_command=""
awx_image=quay.io/ansible/awx
awx_version=24.2.0
docker_registry_username=
docker_registry_password=

# Database
postgres_init_storage=10Gi
postgres_container=awx_postgres
postgres_db_name=awx
postgres_db_user=awx
postgres_db_password=awx_password

# Redis
redis_container=awx_redis
redis_image=redis:latest

# Port configuration
host_port=80

# Secret key
secret_key=aabbccdd1234567890abcdefghijklmnop

# Broadcast websocket configuration
broadcast_websocket_secret=broadcast_secret_key_123456789
EOF

# Run the installer
./docker-compose up -d

# Or use the Python installer
python3 ./tools/docker-compose/_docker-compose.py

Monitor startup:

# View logs
docker-compose logs -f

# Wait for all services to start
docker-compose ps

# Check database initialization
docker-compose logs postgres

Access AWX:

# AWX is available at http://localhost:80
# Default credentials:
# Username: admin
# Password: your_secure_password

# View running containers
docker-compose ps

# Execute commands in container
docker-compose exec awx_web awx-manage createsuperuser

For production Docker Compose deployments, use proper configuration:

version: '3'
services:
  postgres:
    image: postgres:13
    environment:
      POSTGRES_DB: awx
      POSTGRES_USER: awx
      POSTGRES_PASSWORD: securepass123
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  redis:
    image: redis:6
    ports:
      - "6379:6379"

  awx_web:
    image: quay.io/ansible/awx:24.2.0
    ports:
      - "80:8052"
      - "443:8053"
    depends_on:
      - postgres
      - redis
    environment:
      DATABASE_USER: awx
      DATABASE_PASSWORD: securepass123
      DATABASE_HOST: postgres
      DATABASE_PORT: 5432
      DATABASE_NAME: awx
      REDIS_HOST: redis
      REDIS_PORT: 6379
      SECRET_KEY: "your-secret-key-here"
      BROADCAST_WEBSOCKET_SECRET: "broadcast-secret"
    volumes:
      - ./settings.py:/etc/tower/settings.py

volumes:
  postgres_data:

Configuring Projects

Projects connect AWX to your Ansible playbook repositories.

Create a project via the API:

# Define project details
TOWER_HOST="https://awx.example.com"
TOWER_USER="admin"
TOWER_PASS="password"

# Create project
curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Playbooks",
    "description": "Main production automation",
    "scm_type": "git",
    "scm_url": "https://github.com/company/playbooks.git",
    "scm_branch": "main",
    "scm_clean": true,
    "scm_delete_on_update": false,
    "scm_update_on_launch": true,
    "credential": 1
  }' \
  $TOWER_HOST/api/v2/projects/

Or via the web UI:

  1. Navigate to Projects
  2. Click Create Project
  3. Enter name and description
  4. Select SCM type (Git, Subversion, etc.)
  5. Enter SCM URL and branch
  6. Optionally select credential for authentication
  7. Save

Project synchronization:

# Manual project sync via API
curl -u $TOWER_USER:$TOWER_PASS \
  -X POST \
  $TOWER_HOST/api/v2/projects/5/update/

# Check project sync status
curl -u $TOWER_USER:$TOWER_PASS \
  $TOWER_HOST/api/v2/projects/5/

Managing Inventories

Inventories define the hosts and groups that Ansible manages.

Create an inventory:

# Simple inventory creation
curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Servers",
    "description": "Production infrastructure",
    "organization": 1
  }' \
  $TOWER_HOST/api/v2/inventories/

Add hosts to inventory:

# Add host
curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "name": "web01.example.com",
    "inventory": 3,
    "variables": "ansible_host: 192.168.1.10"
  }' \
  $TOWER_HOST/api/v2/hosts/

# Add host group
curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "name": "webservers",
    "inventory": 3
  }' \
  $TOWER_HOST/api/v2/groups/

# Add host to group
curl -u $TOWER_USER:$TOWER_PASS \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "id": 5
  }' \
  $TOWER_HOST/api/v2/groups/7/hosts/

Dynamic inventory from external sources:

# Create custom inventory script
#!/usr/bin/env python3
import json
import sys

inventory = {
    "webservers": {
        "hosts": ["web01.example.com", "web02.example.com"],
        "vars": {"environment": "production"}
    },
    "databases": {
        "hosts": ["db01.example.com"],
        "vars": {"environment": "production"}
    },
    "_meta": {
        "hostvars": {
            "web01.example.com": {"http_port": 80},
            "web02.example.com": {"http_port": 80}
        }
    }
}

print(json.dumps(inventory))

Credential Management

Credentials store authentication information securely.

Create SSH credentials:

curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "name": "SSH Credentials",
    "credential_type": 1,
    "organization": 1,
    "inputs": {
      "username": "ansible",
      "password": "secure_password",
      "become_password": "sudo_password"
    }
  }' \
  $TOWER_HOST/api/v2/credentials/

Create API token credentials:

curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "name": "API Token",
    "credential_type": 23,
    "organization": 1,
    "inputs": {
      "host": "api.example.com",
      "bearer_token": "token_value"
    }
  }' \
  $TOWER_HOST/api/v2/credentials/

Creating Job Templates

Job templates are reusable configurations for running Ansible playbooks.

Create a job template:

curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Deploy Web Application",
    "project": 5,
    "playbook": "deploy.yml",
    "inventory": 3,
    "credential": 7,
    "ask_limit_on_launch": true,
    "ask_tags_on_launch": true,
    "ask_variables_on_launch": true,
    "extra_vars": "environment: production\nversion: latest",
    "verbosity": 1,
    "job_type": "run"
  }' \
  $TOWER_HOST/api/v2/job_templates/

Launch a job:

# Simple launch
curl -u $TOWER_USER:$TOWER_PASS \
  -X POST \
  $TOWER_HOST/api/v2/job_templates/8/launch/

# Launch with extra variables
curl -u $TOWER_USER:$TOWER_PASS \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "extra_vars": "{\"target_hosts\": \"webservers\", \"action\": \"restart\"}"
  }' \
  $TOWER_HOST/api/v2/job_templates/8/launch/

# Check job status
curl -u $TOWER_USER:$TOWER_PASS \
  $TOWER_HOST/api/v2/jobs/15/

Role-Based Access Control

RBAC controls who can perform specific actions in AWX.

Create a custom role:

curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "name": "DevOps Team Lead",
    "description": "Can manage all automation resources",
    "permissions": [
      "add_project",
      "change_project",
      "delete_project",
      "view_project",
      "add_jobtemplate",
      "change_jobtemplate",
      "delete_jobtemplate",
      "execute_jobtemplate"
    ]
  }' \
  $TOWER_HOST/api/v2/roles/

Assign roles to users:

# Create team
curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "name": "DevOps",
    "description": "DevOps engineering team",
    "organization": 1
  }' \
  $TOWER_HOST/api/v2/teams/

# Add member to team
curl -u $TOWER_USER:$TOWER_PASS \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "id": 3
  }' \
  $TOWER_HOST/api/v2/teams/2/members/

# Assign role to team
curl -u $TOWER_USER:$TOWER_PASS \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "role_definition": 4,
    "content_type": "project",
    "object_id": 5
  }' \
  $TOWER_HOST/api/v2/role_bindings/

Building Workflows

Workflows chain multiple job templates together with decision logic.

Create a workflow template:

curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Application Deployment Pipeline",
    "description": "Deploy app with pre/post checks",
    "organization": 1,
    "webhook_service": "github",
    "webhook_credential": null
  }' \
  $TOWER_HOST/api/v2/workflow_job_templates/

Add workflow nodes:

# Syntax check node
curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "workflow_job_template": 3,
    "unified_job_template": 5,
    "identifier": "syntax_check"
  }' \
  $TOWER_HOST/api/v2/workflow_job_template_nodes/

# Deploy node
curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "workflow_job_template": 3,
    "unified_job_template": 8,
    "identifier": "deploy"
  }' \
  $TOWER_HOST/api/v2/workflow_job_template_nodes/

# Validation node
curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "workflow_job_template": 3,
    "unified_job_template": 12,
    "identifier": "validate"
  }' \
  $TOWER_HOST/api/v2/workflow_job_template_nodes/

Connect workflow nodes:

# Add edge from syntax_check to deploy
curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "workflow_job_template_node": 1,
    "child_node": 2,
    "edge_type": "success"
  }' \
  $TOWER_HOST/api/v2/workflow_job_template_node_edges/

# Add edge from deploy to validate
curl -u $TOWER_USER:$TOWER_PASS \
  -H "Content-Type: application/json" \
  -d '{
    "workflow_job_template_node": 2,
    "child_node": 3,
    "edge_type": "success"
  }' \
  $TOWER_HOST/api/v2/workflow_job_template_node_edges/

Launch workflow:

curl -u $TOWER_USER:$TOWER_PASS \
  -X POST \
  $TOWER_HOST/api/v2/workflow_job_templates/3/launch/

# Monitor workflow execution
curl -u $TOWER_USER:$TOWER_PASS \
  $TOWER_HOST/api/v2/workflow_jobs/7/

Conclusion

Ansible AWX transforms Ansible from a command-line tool into a comprehensive automation platform with web interface, API access, and enterprise features. Whether deploying on Kubernetes for production scale or Docker Compose for development, AWX provides the infrastructure for centralized automation management. By properly configuring projects, inventories, credentials, job templates, RBAC, and workflows, you create a robust automation platform that scales with your infrastructure needs while maintaining security and governance. Combine AWX with well-designed playbooks and you have a powerful system for managing complex infrastructure at scale.