63 AWX und Ansible Tower

63.1 Überblick: Was sind AWX und Ansible Tower?

63.1.1 Zweck und Abgrenzung zu CLI-Ansible

AWX und Ansible Tower stellen webbasierte Benutzeroberflächen für Ansible-Automatisierung bereit. Sie erweitern CLI-Ansible um zentrale Jobverwaltung, Rollenkontrolle, Auditierung und Skalierbarkeit für Teamumgebungen. Die Plattformen ermöglichen die Kapselung von Ansible-Expertise in benutzerfreundliche Schnittstellen für Nicht-Ansible-Experten.

63.1.2 Unterschied zwischen AWX (Upstream) und Ansible Tower (Red Hat)

AWX ist das Open-Source-Upstream-Projekt mit kontinuierlichen Releases und experimentellen Features. Ansible Tower basiert auf AWX-Snapshots mit Long-Term-Support, kommerziellem Support und zusätzlichen Enterprise-Features wie Smart Inventory und erweiterten Analytics.

Versionierungsunterschiede:

Feature-Divergenz:

63.2 Komponenten und Architektur

63.2.1 Web UI, REST API, Scheduler, Task-Worker

Core-Komponenten:

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Web UI        │    │   REST API      │    │   Scheduler     │
│   (Django)      │    │   (DRF)         │    │   (Celery)      │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                        │                        │
         └────────────────────────┼────────────────────────┘
                                  │
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Task Workers  │    │   Message Queue │    │   Database      │
│   (ansible-     │◄───┤   (Redis/       │    │   (PostgreSQL)  │
│   playbook)     │    │   RabbitMQ)     │    │                 │
└─────────────────┘    └─────────────────┘    └─────────────────┘

63.2.2 Datenbank, RabbitMQ, Redis (je nach Version)

Storage-Layer: - PostgreSQL: Primäre Datenbank für Konfiguration, Benutzer, Job-Historie - Redis: Session-Store, Caching, Message-Broker (AWX 19+) - RabbitMQ: Message-Broker für Job-Queuing (Tower/ältere AWX-Versionen)

Execution-Layer: - Execution Nodes: Ansible-Playbook-Ausführung in isolierten Containern - Control Nodes: Job-Scheduling und UI-Services - Hybrid Nodes: Kombinierte Control/Execution-Funktionalität

63.2.3 Übersicht als Blockdiagramm

Internet/Users
      │
      ▼
┌─────────────────────────────────────────────────────────┐
│                    Load Balancer                       │
│                   (nginx/HAProxy)                      │
└─────────────────────────────────────────────────────────┘
      │
      ▼
┌─────────────────────────────────────────────────────────┐
│                   AWX Web Tier                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐    │
│  │   Web UI    │  │  REST API   │  │  WebSocket  │    │
│  │  (Django)   │  │    (DRF)    │  │   Events    │    │
│  └─────────────┘  └─────────────┘  └─────────────┘    │
└─────────────────────────────────────────────────────────┘
      │                    │                    │
      ▼                    ▼                    ▼
┌─────────────────────────────────────────────────────────┐
│                  Message Queue Layer                   │
│           Redis Cluster / RabbitMQ Cluster             │
└─────────────────────────────────────────────────────────┘
      │
      ▼
┌─────────────────────────────────────────────────────────┐
│                 Execution Tier                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐    │
│  │ Task Worker │  │ Task Worker │  │ Task Worker │    │
│  │   Node 1    │  │   Node 2    │  │   Node N    │    │
│  └─────────────┘  └─────────────┘  └─────────────┘    │
└─────────────────────────────────────────────────────────┘
      │
      ▼
┌─────────────────────────────────────────────────────────┐
│                   Data Layer                           │
│               PostgreSQL Cluster                       │
│            (Primary + Read Replicas)                   │
└─────────────────────────────────────────────────────────┘

63.3 Installation und Betrieb von AWX

63.3.1 Deployment mit AWX Operator oder Docker Compose

AWX Operator auf Kubernetes:

# awx-operator-deployment.yml
apiVersion: v1
kind: Namespace
metadata:
  name: awx
---
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
  name: awx-operator
  namespace: awx
spec:
  channel: release-21.x
  name: awx-operator
  source: operatorhubio-catalog
  sourceNamespace: olm
---
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
  name: awx-production
  namespace: awx
spec:
  service_type: LoadBalancer
  ingress_type: ingress
  hostname: awx.company.com
  admin_user: admin
  admin_password_secret: awx-admin-password
  postgres_configuration_secret: awx-postgres-configuration
  secret_key_secret: awx-secret-key
  
  # Resource specifications
  web_replicas: 2
  task_replicas: 2
  web_resource_requirements:
    requests:
      cpu: 1000m
      memory: 2Gi
    limits:
      cpu: 2000m
      memory: 4Gi
  task_resource_requirements:
    requests:
      cpu: 500m
      memory: 1Gi
    limits:
      cpu: 1000m
      memory: 2Gi
  
  # Storage configuration
  postgres_storage_class: fast-ssd
  postgres_storage_requirements:
    requests:
      storage: 50Gi
  
  # Security context
  security_context_settings:
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000

Secrets für AWX-Operator:

# Admin-Passwort erstellen
kubectl create secret generic awx-admin-password \
  --from-literal=password='SecureAdminPassword2023!' \
  -n awx

# PostgreSQL-Konfiguration
kubectl create secret generic awx-postgres-configuration \
  --from-literal=host=postgres.awx.svc.cluster.local \
  --from-literal=port=5432 \
  --from-literal=database=awx \
  --from-literal=username=awx \
  --from-literal=password='PostgresPassword2023!' \
  --from-literal=sslmode=prefer \
  --from-literal=type=managed \
  -n awx

# Secret Key für Verschlüsselung
kubectl create secret generic awx-secret-key \
  --from-literal=secret_key="$(openssl rand -base64 32)" \
  -n awx

63.3.2 Kubernetes/Openshift vs. Standalone

Helm-Chart-Installation:

# AWX Helm Repository hinzufügen
helm repo add awx-operator https://ansible.github.io/awx-operator/
helm repo update

# Values-Datei erstellen
cat > awx-values.yml << 'EOF'
AWX:
  enabled: true
  name: awx-production
  spec:
    hostname: awx.company.com
    service_type: ClusterIP
    ingress_type: ingress
    ingress_annotations:
      kubernetes.io/ingress.class: nginx
      cert-manager.io/cluster-issuer: letsencrypt-prod
    ingress_tls_secret: awx-tls-secret
    
    # High Availability
    web_replicas: 3
    task_replicas: 2
    
    # External PostgreSQL
    postgres_configuration_secret: awx-postgres-external
    
    # Resource limits
    web_resource_requirements:
      requests:
        cpu: "1"
        memory: 2Gi
      limits:
        cpu: "2"
        memory: 4Gi
    
    # Backup configuration
    backup_pvc: awx-backup-claim
    backup_pvc_namespace: awx
EOF

# Installation
helm install awx-production awx-operator/awx-operator \
  --namespace awx \
  --create-namespace \
  --values awx-values.yml

63.3.3 Beispiel für Helm-Installation oder Operator-Bereitstellung

Docker Compose für Development:

# docker-compose.yml
version: '3.8'

services:
  postgres:
    image: postgres:13
    environment:
      POSTGRES_DB: awx
      POSTGRES_USER: awx
      POSTGRES_PASSWORD: awxpass
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

  redis:
    image: redis:7
    restart: unless-stopped

  awx_web:
    image: quay.io/ansible/awx:21.x.x
    depends_on:
      - postgres
      - redis
    environment:
      DATABASE_USER: awx
      DATABASE_PASSWORD: awxpass
      DATABASE_NAME: awx
      DATABASE_PORT: 5432
      DATABASE_HOST: postgres
      REDIS_HOST: redis
      SECRET_KEY: awxsecret
    volumes:
      - awx_projects:/var/lib/awx/projects
      - awx_rsyslog:/var/log/rsyslog
    ports:
      - "8080:8052"
    restart: unless-stopped

  awx_task:
    image: quay.io/ansible/awx:21.x.x
    depends_on:
      - postgres
      - redis
    environment:
      DATABASE_USER: awx
      DATABASE_PASSWORD: awxpass
      DATABASE_NAME: awx
      DATABASE_PORT: 5432
      DATABASE_HOST: postgres
      REDIS_HOST: redis
      SECRET_KEY: awxsecret
    volumes:
      - awx_projects:/var/lib/awx/projects
      - awx_rsyslog:/var/log/rsyslog
    restart: unless-stopped

volumes:
  postgres_data:
  awx_projects:
  awx_rsyslog:

63.4 Zentrale Konzepte in AWX/Tower

63.4.1 Organisationen, Projekte, Inventories

Organisationen gruppieren Benutzer, Teams und Ressourcen für Mandantentrennung:

# CLI-basierte Konfiguration mit awx-cli
awx organizations create \
  --name "Production Infrastructure" \
  --description "Production environment management"

awx organizations create \
  --name "Development Team" \
  --description "Development and staging environments"

Projekte verknüpfen Git-Repositories mit AWX:

# Git-Projekt erstellen
awx projects create \
  --name "Infrastructure Playbooks" \
  --organization "Production Infrastructure" \
  --scm_type git \
  --scm_url "https://github.com/company/ansible-infrastructure.git" \
  --scm_branch main \
  --scm_credential "GitHub Deploy Key" \
  --scm_update_on_launch true \
  --scm_update_cache_timeout 300

Inventories definieren Host-Gruppen und Variablen:

# Statisches Inventory
awx inventory create \
  --name "Production Servers" \
  --organization "Production Infrastructure" \
  --variables @- << 'EOF'
{
  "env": "production",
  "backup_enabled": true,
  "monitoring_enabled": true
}
EOF

# Inventory-Hosts hinzufügen
awx hosts create \
  --name "web-01.company.com" \
  --inventory "Production Servers" \
  --variables '{"server_role": "web", "datacenter": "us-east-1"}'

awx hosts create \
  --name "db-01.company.com" \
  --inventory "Production Servers" \
  --variables '{"server_role": "database", "datacenter": "us-east-1"}'

63.4.2 Credentials, Job Templates, Workflows

Credential-Management:

# SSH-Credential für Server-Zugriff
awx credentials create \
  --name "Production SSH Key" \
  --organization "Production Infrastructure" \
  --credential_type "Machine" \
  --inputs @- << 'EOF'
{
  "username": "ansible",
  "ssh_key_data": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----",
  "become_method": "sudo",
  "become_username": "root"
}
EOF

# Git-Credential für private Repositories
awx credentials create \
  --name "GitHub Deploy Key" \
  --organization "Production Infrastructure" \
  --credential_type "Source Control" \
  --inputs @- << 'EOF'
{
  "ssh_key_data": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----"
}
EOF

# Vault-Credential für verschlüsselte Daten
awx credentials create \
  --name "Ansible Vault Production" \
  --organization "Production Infrastructure" \
  --credential_type "Vault" \
  --inputs '{"vault_password": "ProductionVaultPassword2023!"}'

Job Templates konfigurieren:

# Basis Job Template
awx job_templates create \
  --name "Deploy Web Application" \
  --organization "Production Infrastructure" \
  --project "Infrastructure Playbooks" \
  --playbook "playbooks/deploy_webapp.yml" \
  --inventory "Production Servers" \
  --credential "Production SSH Key" \
  --vault_credential "Ansible Vault Production" \
  --job_type run \
  --verbosity 1 \
  --limit "web_servers" \
  --job_tags "deploy,config" \
  --ask_variables_on_launch true \
  --ask_limit_on_launch true

# Survey für Benutzerinteraktion
awx job_templates modify "Deploy Web Application" \
  --survey_spec @- << 'EOF'
{
  "name": "Deployment Survey",
  "description": "Configure deployment parameters",
  "spec": [
    {
      "question_name": "Application Version",
      "question_description": "Version to deploy",
      "variable": "app_version",
      "type": "text",
      "required": true,
      "default": "latest"
    },
    {
      "question_name": "Environment",
      "question_description": "Target environment",
      "variable": "target_env",
      "type": "multiplechoice",
      "choices": ["staging", "production"],
      "required": true,
      "default": "staging"
    },
    {
      "question_name": "Backup Before Deploy",
      "question_description": "Create backup before deployment",
      "variable": "create_backup",
      "type": "boolean",
      "required": false,
      "default": true
    }
  ]
}
EOF

63.4.3 Rollen und Berechtigungen (RBAC)

Team-basierte Rechteverwaltung:

# Teams erstellen
awx teams create \
  --name "Infrastructure Team" \
  --organization "Production Infrastructure" \
  --description "Infrastructure administrators"

awx teams create \
  --name "Developers" \
  --organization "Production Infrastructure" \
  --description "Application developers"

# Benutzer zu Teams hinzufügen
awx teams associate \
  --team "Infrastructure Team" \
  --user "alice" \
  --user "bob"

awx teams associate \
  --team "Developers" \
  --user "charlie" \
  --user "diana"

# Rollenbasierte Berechtigungen
awx role grant \
  --team "Infrastructure Team" \
  --type "admin" \
  --target-team "Infrastructure Team"

awx role grant \
  --team "Infrastructure Team" \
  --type "execute" \
  --target-job-template "Deploy Web Application"

awx role grant \
  --team "Developers" \
  --type "read" \
  --target-inventory "Production Servers"

awx role grant \
  --team "Developers" \
  --type "execute" \
  --target-job-template "Deploy Web Application"

63.5 Erstellen und Ausführen von Jobs

63.5.1 Projekt mit Git verbinden

Git-Projekt mit Webhook-Integration:

# Erweiterte Git-Konfiguration
awx projects create \
  --name "Microservices Deployment" \
  --organization "Production Infrastructure" \
  --scm_type git \
  --scm_url "https://github.com/company/microservices-ansible.git" \
  --scm_branch main \
  --scm_credential "GitHub Deploy Key" \
  --scm_update_on_launch true \
  --scm_update_cache_timeout 0 \
  --scm_clean true \
  --scm_delete_on_update true \
  --custom_virtualenv "/var/lib/awx/venv/custom-python"

# Webhook für automatische Updates
PROJECT_ID=$(awx projects list --name "Microservices Deployment" --format json | jq -r '.results[0].id')
awx projects update $PROJECT_ID

63.5.2 Inventory verknüpfen

Smart Inventory mit dynamischen Gruppen:

# Smart Inventory basierend auf Host-Variablen
awx inventory create \
  --name "Production Web Servers" \
  --organization "Production Infrastructure" \
  --kind smart \
  --host_filter 'server_role="web" and datacenter="us-east-1"'

# Inventory-Quellen für Cloud-Provider
awx inventory_sources create \
  --name "AWS EC2 Source" \
  --inventory "Production Servers" \
  --source ec2 \
  --credential "AWS Production Account" \
  --source_regions "us-east-1,us-west-2" \
  --instance_filters 'tag:Environment=production' \
  --group_by 'tag_Name,tag_Environment,instance_type' \
  --update_on_launch true \
  --update_cache_timeout 1800

63.5.3 Job Template konfigurieren und starten

Komplexes Job Template mit Notifications:

# Job Template mit erweiterten Optionen
awx job_templates create \
  --name "Full Stack Deployment" \
  --organization "Production Infrastructure" \
  --project "Microservices Deployment" \
  --playbook "site.yml" \
  --inventory "Production Servers" \
  --credential "Production SSH Key" \
  --vault_credential "Ansible Vault Production" \
  --cloud_credential "AWS Production Account" \
  --job_type run \
  --verbosity 2 \
  --diff_mode true \
  --concurrent_jobs_enabled false \
  --ask_variables_on_launch true \
  --ask_limit_on_launch true \
  --ask_tags_on_launch true \
  --webhook_service github \
  --webhook_credential "GitHub Webhook Secret"

# Job ausführen mit Extra-Variablen
awx job_templates launch "Full Stack Deployment" \
  --extra_vars @- << 'EOF'
{
  "deployment_id": "deploy-2023-001",
  "rollback_enabled": true,
  "health_check_timeout": 300,
  "notification_channels": ["slack", "email"]
}
EOF \
  --limit "web_servers:&production" \
  --tags "deploy,config,verify"

63.6 Nutzer- und Rechteverwaltung

63.6.1 Rollen, Teams, Berechtigungsvergabe

Granulare RBAC-Konfiguration:

# Custom-Rollen für spezifische Aufgaben
awx role create \
  --name "Deployment Manager" \
  --description "Can execute deployment jobs but not modify templates"

# Objektspezifische Berechtigungen
awx role grant \
  --user "deployment_user" \
  --type "execute" \
  --target-job-template "Deploy Web Application"

awx role grant \
  --user "deployment_user" \
  --type "read" \
  --target-inventory "Production Servers"

# Team-hierarchien und Vererbung
awx teams create \
  --name "Senior DevOps" \
  --organization "Production Infrastructure"

awx teams associate \
  --team "Senior DevOps" \
  --parent-team "Infrastructure Team"

# Conditional-Access basierend auf Ressourcen
awx role grant \
  --team "Developers" \
  --type "execute" \
  --target-job-template "Deploy to Staging" \
  --condition "target_env != 'production'"

63.6.2 LDAP- und SSO-Integration (nur Überblick)

LDAP-Konfiguration über Settings-API:

# LDAP-Settings konfigurieren
awx settings modify \
  --setting AUTH_LDAP_SERVER_URI \
  --value "ldaps://ldap.company.com:636"

awx settings modify \
  --setting AUTH_LDAP_BIND_DN \
  --value "cn=ansible,ou=service,dc=company,dc=com"

awx settings modify \
  --setting AUTH_LDAP_USER_SEARCH \
  --value '[
    "ou=users,dc=company,dc=com",
    "(uid=%(user)s)",
    {
      "first_name": "givenName",
      "last_name": "sn",
      "email": "mail"
    }
  ]'

# LDAP-Gruppen-Mapping
awx settings modify \
  --setting AUTH_LDAP_GROUP_SEARCH \
  --value '[
    "ou=groups,dc=company,dc=com",
    "(objectClass=groupOfNames)",
    "cn"
  ]'

# Team-Mapping für LDAP-Gruppen
awx settings modify \
  --setting AUTH_LDAP_ORGANIZATION_MAP \
  --value '{
    "Production Infrastructure": {
      "users": ["cn=ansible-users,ou=groups,dc=company,dc=com"],
      "admins": ["cn=ansible-admins,ou=groups,dc=company,dc=com"]
    }
  }'

63.7 Logging, Auditing und Notifikationen

63.7.1 Ereignisprotokollierung

Activity Stream und Job-Ausgaben:

# Job-Logs abrufen
JOB_ID=$(awx jobs list --name "Deploy Web Application" --status successful --format json | jq -r '.results[0].id')
awx jobs stdout $JOB_ID

# Activity Stream für Audit-Trail
awx activity_stream list \
  --object_type job_template \
  --object_id 15 \
  --format json | jq '.results[] | {timestamp, operation, actor, changes}'

# Event-basierte Logs
awx job_events list \
  --job $JOB_ID \
  --event task \
  --format json | jq '.results[] | {task, host, status, created}'

63.7.2 Webhooks, E-Mail, Slack etc.

Notification Templates:

# Slack-Notification
awx notification_templates create \
  --name "Slack Deployment Alerts" \
  --organization "Production Infrastructure" \
  --notification_type slack \
  --notification_configuration @- << 'EOF'
{
  "token": "xoxb-slack-bot-token",
  "channels": ["#infrastructure", "#deployments"],
  "username": "AWX Bot",
  "icon_emoji": ":robot_face:",
  "hex_color": "#FF0000"
}
EOF

# E-Mail-Notification
awx notification_templates create \
  --name "Email Operations Team" \
  --organization "Production Infrastructure" \
  --notification_type email \
  --notification_configuration @- << 'EOF'
{
  "username": "awx@company.com",
  "password": "smtp_password",
  "sender": "awx@company.com",
  "recipients": ["ops@company.com", "oncall@company.com"],
  "host": "smtp.company.com",
  "port": 587,
  "use_tls": true,
  "use_ssl": false
}
EOF

# Webhook für externe Systeme
awx notification_templates create \
  --name "ServiceNow Integration" \
  --organization "Production Infrastructure" \
  --notification_type webhook \
  --notification_configuration @- << 'EOF'
{
  "url": "https://company.service-now.com/api/now/table/incident",
  "http_method": "POST",
  "headers": {
    "Authorization": "Bearer servicenow_token",
    "Content-Type": "application/json"
  },
  "username": "awx_integration",
  "password": "servicenow_password"
}
EOF

# Notifications an Job Templates binden
awx job_templates associate \
  --job_template "Deploy Web Application" \
  --notification_template_started "Slack Deployment Alerts"

awx job_templates associate \
  --job_template "Deploy Web Application" \
  --notification_template_success "Email Operations Team"

awx job_templates associate \
  --job_template "Deploy Web Application" \
  --notification_template_error "ServiceNow Integration"

63.8 Integration in CI/CD-Umgebungen

63.8.1 API-basierter Zugriff

REST-API-Integration mit curl:

# Token-basierte Authentifizierung
AWX_TOKEN="your_awx_api_token"
AWX_URL="https://awx.company.com"

# Job Template ausführen
curl -X POST \
  -H "Authorization: Bearer $AWX_TOKEN" \
  -H "Content-Type: application/json" \
  "$AWX_URL/api/v2/job_templates/15/launch/" \
  -d @- << 'EOF'
{
  "extra_vars": {
    "app_version": "v2.1.0",
    "deployment_strategy": "blue_green",
    "health_check_enabled": true
  },
  "limit": "web_servers",
  "job_tags": "deploy,verify",
  "inventory": 25
}
EOF

# Job-Status überwachen
JOB_ID=$(curl -s -H "Authorization: Bearer $AWX_TOKEN" \
  "$AWX_URL/api/v2/jobs/?order_by=-id&page_size=1" | \
  jq -r '.results[0].id')

# Polling für Job-Completion
while true; do
  STATUS=$(curl -s -H "Authorization: Bearer $AWX_TOKEN" \
    "$AWX_URL/api/v2/jobs/$JOB_ID/" | \
    jq -r '.status')
  
  echo "Job $JOB_ID status: $STATUS"
  
  if [[ "$STATUS" == "successful" || "$STATUS" == "failed" || "$STATUS" == "canceled" ]]; then
    break
  fi
  
  sleep 10
done

63.8.2 Beispiel für Remote-Jobauslösung via REST-API

GitLab CI Pipeline mit AWX-Integration:

# .gitlab-ci.yml
stages:
  - build
  - test
  - deploy

variables:
  AWX_URL: "https://awx.company.com"
  AWX_TOKEN: "$AWX_API_TOKEN"  # GitLab CI Variable

build:
  stage: build
  script:
    - docker build -t app:$CI_COMMIT_SHA .
    - docker push registry.company.com/app:$CI_COMMIT_SHA

test:
  stage: test
  script:
    - pytest tests/
    - coverage report

deploy:staging:
  stage: deploy
  script:
    - |
      # AWX Job Template für Staging-Deployment
      JOB_RESPONSE=$(curl -s -X POST \
        -H "Authorization: Bearer $AWX_TOKEN" \
        -H "Content-Type: application/json" \
        "$AWX_URL/api/v2/job_templates/staging_deploy/launch/" \
        -d "{
          \"extra_vars\": {
            \"app_version\": \"$CI_COMMIT_SHA\",
            \"git_commit\": \"$CI_COMMIT_SHA\",
            \"pipeline_id\": \"$CI_PIPELINE_ID\",
            \"deployed_by\": \"$GITLAB_USER_NAME\"
          }
        }")
      
      JOB_ID=$(echo $JOB_RESPONSE | jq -r '.id')
      echo "AWX Job ID: $JOB_ID"
      
      # Job-Status überwachen
      bash scripts/wait_for_awx_job.sh $JOB_ID
  environment:
    name: staging
    url: https://staging.company.com
  only:
    - develop

deploy:production:
  stage: deploy
  script:
    - |
      # Production Deployment mit Approval
      JOB_RESPONSE=$(curl -s -X POST \
        -H "Authorization: Bearer $AWX_TOKEN" \
        -H "Content-Type: application/json" \
        "$AWX_URL/api/v2/job_templates/production_deploy/launch/" \
        -d "{
          \"extra_vars\": {
            \"app_version\": \"$CI_COMMIT_TAG\",
            \"release_notes\": \"$CI_COMMIT_DESCRIPTION\",
            \"rollback_version\": \"$(git describe --tags --abbrev=0 HEAD~1)\"
          }
        }")
      
      JOB_ID=$(echo $JOB_RESPONSE | jq -r '.id')
      bash scripts/wait_for_awx_job.sh $JOB_ID
  environment:
    name: production
    url: https://production.company.com
  when: manual
  only:
    - tags

Job-Monitoring-Skript:

#!/bin/bash
# scripts/wait_for_awx_job.sh

JOB_ID=$1
MAX_WAIT=1800  # 30 Minuten
WAIT_INTERVAL=30

start_time=$(date +%s)

while true; do
  current_time=$(date +%s)
  elapsed=$((current_time - start_time))
  
  if [ $elapsed -gt $MAX_WAIT ]; then
    echo "Job $JOB_ID timed out after $MAX_WAIT seconds"
    exit 1
  fi
  
  JOB_STATUS=$(curl -s -H "Authorization: Bearer $AWX_TOKEN" \
    "$AWX_URL/api/v2/jobs/$JOB_ID/" | jq -r '.status')
  
  echo "$(date): Job $JOB_ID status: $JOB_STATUS"
  
  case "$JOB_STATUS" in
    "successful")
      echo "Job completed successfully"
      exit 0
      ;;
    "failed"|"error"|"canceled")
      echo "Job failed with status: $JOB_STATUS"
      # Job-Logs ausgeben
      curl -s -H "Authorization: Bearer $AWX_TOKEN" \
        "$AWX_URL/api/v2/jobs/$JOB_ID/stdout/?format=txt_download"
      exit 1
      ;;
    "pending"|"waiting"|"running")
      sleep $WAIT_INTERVAL
      ;;
    *)
      echo "Unknown job status: $JOB_STATUS"
      exit 1
      ;;
  esac
done

63.9 Best Practices für den produktiven Einsatz

63.9.1 Backup-Strategien

Vollständige AWX-Backup-Konfiguration:

# PostgreSQL-Backup mit pg_dump
#!/bin/bash
# backup_awx.sh

BACKUP_DIR="/backup/awx/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"

# Datenbank-Backup
pg_dump -h postgres.awx.svc.cluster.local \
  -U awx \
  -d awx \
  --format=custom \
  --file="$BACKUP_DIR/awx_database.backup"

# Projekt-Dateien sichern
kubectl exec -n awx deployment/awx-production-web \
  -- tar czf - /var/lib/awx/projects | \
  cat > "$BACKUP_DIR/awx_projects.tar.gz"

# Secret-Export für Disaster Recovery
kubectl get secrets -n awx -o yaml > "$BACKUP_DIR/awx_secrets.yaml"

# AWX-Konfiguration exportieren
awx export \
  --organization "Production Infrastructure" \
  --format json > "$BACKUP_DIR/awx_config.json"

# Backup-Verifikation
tar -tzf "$BACKUP_DIR/awx_projects.tar.gz" > /dev/null
pg_restore --list "$BACKUP_DIR/awx_database.backup" > /dev/null

echo "Backup completed: $BACKUP_DIR"

Automatisierte Backup-Pipeline:

# backup-cronjob.yml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: awx-backup
  namespace: awx
spec:
  schedule: "0 2 * * *"  # Täglich um 2:00 Uhr
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: postgres:13
            env:
            - name: PGPASSWORD
              valueFrom:
                secretKeyRef:
                  name: awx-postgres-configuration
                  key: password
            command:
            - /bin/bash
            - -c
            - |
              # Database backup
              pg_dump -h $DATABASE_HOST -U $DATABASE_USER -d $DATABASE_NAME \
                --format=custom \
                --file=/backup/awx_$(date +%Y%m%d_%H%M%S).backup
              
              # Upload to S3
              aws s3 cp /backup/awx_$(date +%Y%m%d_%H%M%S).backup \
                s3://company-awx-backups/database/
            volumeMounts:
            - name: backup-storage
              mountPath: /backup
          volumes:
          - name: backup-storage
            persistentVolumeClaim:
              claimName: awx-backup-pvc
          restartPolicy: OnFailure

63.9.2 Mandantenfähigkeit und Trennung von Umgebungen

Multi-Tenant-Architektur:

# Umgebungsspezifische Organisationen
awx organizations create \
  --name "Production" \
  --description "Production environment only"

awx organizations create \
  --name "Staging" \
  --description "Staging and testing environments"

awx organizations create \
  --name "Development" \
  --description "Development environments"

# Isolierte Execution Environments
awx execution_environments create \
  --name "Production EE" \
  --image "registry.company.com/awx-ee:production" \
  --organization "Production"

awx execution_environments create \
  --name "Development EE" \
  --image "registry.company.com/awx-ee:development" \
  --organization "Development"

# Umgebungsspezifische Instance Groups
awx instance_groups create \
  --name "production-workers" \
  --instances "prod-worker-01,prod-worker-02,prod-worker-03"

awx instance_groups create \
  --name "development-workers" \
  --instances "dev-worker-01,dev-worker-02"

# Resource-Isolation
awx job_templates modify "Production Deployment" \
  --instance_groups "production-workers" \
  --execution_environment "Production EE"

63.9.3 Versionsmanagement und GitOps-Prinzipien

GitOps-Workflow für AWX-Konfiguration:

# awx-config-repo/environments/production/job_templates.yml
job_templates:
  - name: "Deploy Web Application"
    project: "Infrastructure Playbooks"
    playbook: "playbooks/deploy_webapp.yml"
    inventory: "Production Servers"
    credentials:
      - "Production SSH Key"
      - "Ansible Vault Production"
    execution_environment: "Production EE"
    instance_groups:
      - "production-workers"
    extra_vars:
      environment: production
      backup_enabled: true
      health_checks: true
    survey_enabled: true
    survey_spec:
      name: "Deployment Parameters"
      spec:
        - question_name: "Application Version"
          variable: "app_version"
          type: "text"
          required: true
          default: "latest"
        - question_name: "Rolling Deployment"
          variable: "rolling_deployment"
          type: "boolean"
          required: false
          default: true

  - name: "Database Maintenance"
    project: "Infrastructure Playbooks"
    playbook: "playbooks/db_maintenance.yml"
    inventory: "Production Database Servers"
    credentials:
      - "Production SSH Key"
      - "Database Admin Credential"
    schedule:
      name: "Weekly DB Maintenance"
      rrule: "DTSTART:20231201T020000Z RRULE:FREQ=WEEKLY;BYDAY=SU"
      enabled: true

Automated Configuration Deployment:

#!/bin/bash
# deploy_awx_config.sh

CONFIG_REPO="https://github.com/company/awx-configuration.git"
ENVIRONMENT=${1:-production}

# Repository klonen
git clone $CONFIG_REPO /tmp/awx-config
cd /tmp/awx-config

# Environment-spezifische Konfiguration anwenden
for config_file in environments/$ENVIRONMENT/*.yml; do
  echo "Applying configuration from $config_file"
  
  case $(basename $config_file) in
    "organizations.yml")
      yq eval '.organizations[]' $config_file | \
        while IFS= read -r org; do
          awx organizations create --name "$org.name" --description "$org.description"
        done
      ;;
    "job_templates.yml")
      yq eval '.job_templates[]' $config_file | \
        while IFS= read -r template; do
          awx job_templates create \
            --name "$template.name" \
            --project "$template.project" \
            --playbook "$template.playbook" \
            --inventory "$template.inventory"
        done
      ;;
    "schedules.yml")
      yq eval '.schedules[]' $config_file | \
        while IFS= read -r schedule; do
          awx schedules create \
            --name "$schedule.name" \
            --rrule "$schedule.rrule" \
            --job-template "$schedule.job_template"
        done
      ;;
  esac
done

# Cleanup
rm -rf /tmp/awx-config