AWX & Tower #
Menjalankan Ansible dari command line bekerja baik untuk satu orang. Tapi saat tim tumbuh, muncul tantangan baru: siapa yang boleh menjalankan playbook apa ke lingkungan mana? Bagaimana history semua eksekusi dicatat? Bagaimana non-engineer bisa memicu deployment tanpa akses ke terminal? AWX (versi open source) dan Ansible Tower (sekarang bernama Red Hat Ansible Automation Platform / Automation Controller) menjawab semua ini — mereka adalah platform web untuk mengelola, menjadwalkan, dan mengontrol akses ke Ansible. Artikel ini membahas kapan kita membutuhkan platform ini, arsitekturnya, konsep intinya, dan pola yang harus kita hindari saat mengimplementasikannya.
AWX vs Ansible Tower — Perbandingan Fitur #
AWX adalah upstream open source dari Ansible Tower. Kode yang sama, dua distribusi yang berbeda — keputusan memilih salah satu biasanya ditentukan oleh kebutuhan support, model lisensi, dan skala organisasi. Tabel berikut merangkum perbedaan yang paling relevan untuk keputusan produksi:
| Aspek | AWX (Open Source) | Ansible Tower / Automation Controller (Red Hat) |
|---|---|---|
| Lisensi | Apache 2.0 (gratis) | Berbayar — subscription per node atau per Automation Platform |
| Dukungan resmi | Komunitas (GitHub, forum, Matrix) | Red Hat (SLA, patch keamanan, support 24/7) |
| Siklus rilis | Sering (tiap ~2-3 minggu); bisa breaking | Predictable (2-3 release mayor per tahun); backward compatible |
| Kompatibilitas OS | Kubernetes (Operator), Docker Compose (dev) | RHEL, OpenShift, Kubernetes via Operator |
| Update Otomatis | Bisa, tapi tidak otomatis | Subscription entitlement untuk stable channel |
| Integrasi ekosistem Red Hat | Tidak ada | Ansible Automation Hub (private Galaxy), Insights, Satellite |
| Cocok untuk | Lab, homelab, tim kecil-menengah, eksplorasi fitur terbaru | Perusahaan dengan tim besar, compliance requirement, kebutuhan support |
| Source code | github.com/ansible/awx | Proprietary (subset AWX + patches Red Hat) |
| Biaya tersembunyi | Waktu engineer untuk maintain, troubleshoot upgrade | Biaya subscription di awal, tapi predictable |
Poin penting: AWX dan Tower berbagi codebase inti. Fitur yang kita lihat di AWX biasanya muncul di Tower dalam 6-12 bulan berikutnya dengan hardening tambahan. Tapi stability dan support adalah apa yang kita bayar di Tower — bukan fitur.
Decision Tree Memilih AWX atau Tower #
flowchart TD
A{"Butuh support<br/>resmi Red Hat?"} -- "Ya" --> B{"Anggaran subscription<br/>tersedia?"}
A -- "Tidak" --> C{"Tim kita mampu<br/>maintain upgrade<br/>AWX sendiri?"}
B -- "Ya" --> D["Tower / Automation Controller"]
B -- "Tidak" --> E["Evaluasi trade-off:<br/>support vs budget"]
C -- "Ya" --> F{"Jumlah node<br/>> 100?"}
C -- "Tidak" --> G["Pertimbangkan managed<br/>service atau konsultan"]
F -- "Tidak" --> H["AWX"]
F -- "Ya" --> I{"Butuh private<br/>Automation Hub?"}
I -- "Ya" --> D
I -- "Tidak" --> J{"Compliance requirement<br/>ketat?"}
J -- "Ya" --> D
J -- "Tidak" --> H
Gunakan decision tree ini sebagai starting point, bukan aturan absolut. Banyak tim besar tetap memilih AWX karena mereka punya engineer yang maintenance-nya masuk ke rutinitas tim. Dan banyak tim kecil justru memilih Tower karena mereka tidak ingin pusing dengan upgrade compatibility.
AWX versi 24.x (per awal 2026) sudah stabil untuk kebutuhan production ringan-menengah. Versi mayor baru biasanya membawa peningkatan ke UI, performance, dan integrasi Kubernetes — tapi migrasi antar versi mayor kadang butuh perhatian khusus karena ada perubahan API. Selalu baca release notes sebelum upgrade.
Arsitektur AWX: Control Plane dan Execution Plane #
AWX dipisah jadi dua plane yang punya tanggung jawab berbeda. Memahami pemisahan ini penting saat kita melakukan scaling AWX ke ribuan node atau saat troubleshoot kenapa job tertentu lambat.
flowchart LR
subgraph CP["Control Plane — mengatur, tidak mengeksekusi"]
API["REST API<br/>:8043 / :443"]
WEB["Web UI<br/>nginx + Django"]
TASKD[("Task System<br/>awx-manage")]
SCHED[("Scheduler<br/>awx-manage schedule")]
PG[("PostgreSQL<br/>state + audit log")]
end
subgraph EP["Execution Plane — menjalankan playbook"]
EX1["Execution Node 1<br/>awx-execution-env"]
EX2["Execution Node 2<br/>awx-execution-env"]
EX3["Execution Node 3<br/>awx-execution-env"]
end
subgraph EXT["Sistem Eksternal"]
GIT[("Git Repository<br/>playbooks + roles")]
CMDB[("Inventory Source<br/>AWS / GCP / Azure")]
CRED[("Credential Store<br/>Vault / cloud KMS")]
SLACK["Slack / Email<br/>callback notifier"]
end
USER(["User / Engineer"]) --> WEB
CI(["CI/CD System"]) --> API
GIT -->|"scm update"| TASKD
CMDB -->|"sync inventory"| TASKD
CRED -->|"fetch secret saat runtime"| EX1
CRED -->|"fetch secret saat runtime"| EX2
WEB --> API
API --> TASKD
TASKD --> PG
SCHED --> TASKD
TASKD -->|"distribute job"| EX1
TASKD -->|"distribute job"| EX2
TASKD -->|"distribute job"| EX3
EX1 -->|"callback event"| TASKD
TASKD --> SLACK
Control plane berisi API server, web UI, task system yang menjadwalkan job, scheduler, dan database PostgreSQL. Semua state — inventory, project, job template, history eksekusi — disimpan di database. Control plane tidak pernah mengeksekusi playbook langsung; ia hanya mendistribusikan job ke execution plane.
Execution plane berisi execution node yang menjalankan awx-execution-env (container image berisi Ansible + collections + dependencies). Setiap job berjalan di execution node, lalu hasilnya (stdout, return code, artifact) dikembalikan ke control plane untuk dicatat di database.
Pemisahan ini punya konsekuensi penting. Pertama, scaling horizontal: tambah execution node saat job queue panjang, tanpa harus membesarkan control plane. Kedua, isolasi environment: execution plane bisa berada di jaringan yang punya akses ke managed node, sementara control plane cukup diakses user. Ketiga, observability lebih mudah: semua event tercatat di control plane, sehingga kita bisa membuat dashboard di observability/dashboard yang membaca dari API AWX.
Jangan pernah menjalankan execution plane di node yang sama dengan managed node production. Job AWX butuh privilege (root atau sudo ke managed node) dan akses ke credential — mengkonsolidasikan semuanya di satu host berarti satu compromised credential bisa langsung menyentuh seluruh fleet.
Konsep Inti AWX #
Sebelum menulis kode, kita perlu memahami hierarki resource di AWX. Semua objek di AWX berada di bawah Organization — sebuah container logis untuk mengelompokkan resource berdasarkan tim, bisnis unit, atau environment.
flowchart TD
ORG["Organization"]
ORG --> INV["Inventory"]
ORG --> PROJ["Project"]
ORG --> CRED["Credential"]
ORG --> JT["Job Template"]
ORG --> WF["Workflow Template"]
ORG --> ROLE["Role / Team"]
INV --> HOST["Hosts + Groups"]
PROJ --> SCMDET[("Git Repo<br/>playbooks")]
CRED --> CREDT["Credential Type<br/>SSH, Vault, AWS, dll"]
JT -. "kombinasi" .-> INV
JT -. "kombinasi" .-> PROJ
JT -. "kombinasi" .-> CRED
JT -. "kombinasi" .-> CREDT
JT --> SURVEY["Survey<br/>form input"]
WF --> JTA["Job Template A"]
WF --> JTB["Job Template B"]
WF --> JTC["Job Template C"]
WF -->|"success/failure link"| JTA
WF -->|"success/failure link"| JTB
WF -->|"success/failure link"| JTC
Inventory adalah kumpulan host yang menjadi target playbook. Bisa static (file YAML) atau dynamic (diambil dari AWS EC2, GCP, Azure, VMware, atau CMDB). Inventory bisa punya groups yang berfungsi seperti pattern hosts: webservers di playbook.
Project adalah pointer ke repository Git (GitHub, GitLab, Bitbucket) yang berisi playbook. AWX akan git pull repository ini setiap kali job dijalankan (jika scm_update_on_launch: true), sehingga kita selalu menggunakan versi playbook terbaru.
Credential adalah objek yang menyimpan informasi otentikasi — SSH key, vault password, AWS access key, dan lain-lain. Credential tidak pernah disimpan di database sebagai plaintext; AWX mengenkripsinya dengan key dari file SECRET_KEY, dan hanya di-decrypt saat job dijalankan.
Job Template adalah unit eksekusi utama. Ia adalah kombinasi: Inventory + Project + Playbook + Credential + (opsional) Survey. Setiap kali Job Template dijalankan, AWX membuat objek Job yang mencatat input, output, return code, dan durasi.
Workflow Template menghubungkan beberapa Job Template dengan logika percabangan. Workflow adalah cara AWX mengimplementasikan pipeline — kita bisa mendefinisikan “jalankan Job A; jika sukses jalankan Job B; jika gagal jalankan Job C” tanpa menulis satu baris kode pun.
ANTI-PATTERN: Menjalankan Playbook Langsung dari CLI untuk Production #
Pola yang sering muncul di tim yang baru migrasi ke AWX: developer tetap menjalankan ansible-playbook dari laptop untuk deploy ke production karena “lebih cepat” dan “sudah hafal”. Ini adalah anti-pattern serius yang harus kita hilangkan sejak hari pertama.
# ANTI-PATTERN: production deploy dari laptop engineer
# Tidak ada audit trail, tidak ada approval, tidak ada rollback otomatis
ssh ops@jump-host "cd /opt/ansible && ansible-playbook -i production deploy.yml --extra-vars 'version=2.1.0'"
# Masalah:
# 1. Siapa yang deploy? -- hanya "yang punya akses ssh"
# 2. Kapan di-deploy? -- tidak tercatat
# 3. Apa yang berubah? -- diff dihitung manual dari Git
# 4. Kalau gagal? -- engineer harus SSH manual untuk rollback
# 5. Compliance? -- auditor tidak bisa jawab "siapa deploy apa ke mana"
# BENAR: production deploy lewat AWX Job Template dengan approval
# playbooks/configure-awx.yml — definisikan Job Template dengan ask_scm_diff dan ask_limit_on_launch
---
- name: Konfigurasi Job Template production dengan approval
hosts: localhost
vars:
awx_host: "https://awx.company.internal"
awx_oauthtoken: "{{ vault_awx_token }}"
tasks:
- name: Buat Job Template production dengan approval workflow
awx.awx.job_template:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "Deploy — Production"
organization: "Platform Engineering"
job_type: run
inventory: "Production Inventory"
project: "Infrastructure Playbooks"
playbook: "playbooks/deploy.yml"
credentials:
- "Production SSH Key"
- "Ansible Vault Password"
# Fitur yang hilang kalau pakai CLI langsung:
ask_scm_diff_on_launch: true # Tampilkan diff Git sebelum run
ask_limit_on_launch: true # Bisa batasi ke subset host
ask_variables_on_launch: true # Bisa override variabel
ask_inventory_on_launch: false # Production inventory tetap fixed
survey_enabled: true
become_enabled: true
# Schedule untuk deployment window
verbosity: 1 # Default verbosity, bisa dinaikkan saat run
no_log: true
Dengan Job Template seperti di atas, setiap eksekusi ke production:
- Record di audit log AWX — siapa yang trigger, kapan, dengan parameter apa.
- Menampilkan Git diff sebelum run — engineer bisa review perubahan apa yang akan di-deploy.
- Bisa dibatasi ke subset host (
--limit webservers-01,webservers-02) — berguna untuk canary deploy. - Menjadi bagian dari Workflow yang punya approval gate — tidak ada deploy ke production tanpa reviewer menyetujui.
Lifecycle Job: Dari Klik ke Hasil #
Memahami state yang dilalui sebuah job membantu kita melakukan debug saat ada job yang “stuck” atau tidak pernah selesai. Diagram state berikut menunjukkan transisi yang mungkin:
stateDiagram-v2
[*] --> New: "launch"
New --> Pending: "masuk queue"
Pending --> Waiting: "butuh input<br/>(survey/approval)"
Waiting --> Pending: "input diberikan"
Pending --> Running: "execution node<br/>tersedia"
Running --> Successful: "exit code 0"
Running --> Failed: "exit code non-zero"
Running --> Error: "execution error<br/>(connection lost, dll)"
Running --> Canceled: "user cancel"
Waiting --> Canceled: "user cancel"
Pending --> Canceled: "user cancel"
Successful --> [*]
Failed --> [*]
Error --> [*]
Canceled --> [*]
State Pending adalah yang paling sering membuat bingung: job sudah di-trigger tapi belum jalan. Penyebab umumnya: tidak ada execution node yang available (semua sibuk), node dalam maintenance, atau control plane kehabisan resource. Cek Instances → Jobs di AWX UI untuk lihat job mana yang sedang berjalan dan execution node mana yang handle.
State Waiting berarti job butuh input lebih lanjut — biasanya karena Workflow Template punya approval node, atau karena Job Template punya survey yang belum diisi.
State Error berbeda dari Failed. Failed berarti playbook exit dengan kode non-zero (task ada yang gagal). Error berarti masalah di level eksekusi — connection ke managed node terputus, execution environment crash, atau OOM di execution node.
Konfigurasi AWX dengan Ansible: As Code untuk Platform Automation #
AWX sendiri bisa dikonfigurasi dengan Ansible menggunakan collection awx.awx dan module tower_* di belakangnya. Ini bukan recursive irony — justru ini best practice. Platform automation harus di-deklarasi di Git, di-review via PR, dan di-rollback jika ada masalah, sama persis seperti managed node yang dikonfigurasi AWX.
# Install dependencies
ansible-galaxy collection install awx.awx
pip install awxkit
# playbooks/configure-awx.yml
---
- name: Konfigurasi AWX untuk project infrastruktur
hosts: localhost
vars:
awx_host: "https://awx.company.internal"
awx_oauthtoken: "{{ vault_awx_token }}"
tasks:
# 1. Buat Organization
- name: Buat organization
awx.awx.organization:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "Platform Engineering"
description: "Team Platform Engineering — owns production infrastructure"
state: present
# 2. Buat Credential untuk SSH
- name: Buat credential SSH untuk production
awx.awx.credential:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "Production SSH Key"
organization: "Platform Engineering"
credential_type: "Machine"
inputs:
ssh_key_data: "{{ vault_production_ssh_key }}"
username: ansible-deploy
become_method: sudo
become_username: root
state: present
no_log: true
# no_log: true mencegah private key bocor ke log Ansible
# 3. Buat Credential untuk Vault
- name: Buat credential Ansible Vault
awx.awx.credential:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "Ansible Vault Password"
organization: "Platform Engineering"
credential_type: "Vault"
inputs:
vault_password: "{{ vault_ansible_vault_password }}"
state: present
no_log: true
# 4. Buat Credential untuk AWS (untuk dynamic inventory)
- name: Buat credential AWS untuk inventory sync
awx.awx.credential:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "AWS Read-Only for Inventory"
organization: "Platform Engineering"
credential_type: "Amazon Web Services"
inputs:
username: "{{ vault_aws_access_key }}"
password: "{{ vault_aws_secret_key }}"
state: present
no_log: true
# 5. Buat Project (link ke Git repository)
- name: Buat project dari Git repository
awx.awx.project:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "Infrastructure Playbooks"
organization: "Platform Engineering"
scm_type: git
scm_url: "https://github.com/company/ansible-infra.git"
scm_branch: main
scm_update_on_launch: true # Selalu pull terbaru sebelum run
scm_clean: true # Hapus perubahan lokal sebelum update
scm_delete_on_update: false # Jaga credential di working copy
timeout: 60
state: present
# 6. Buat Inventory source dinamis dari AWS EC2
- name: Buat inventory production
awx.awx.inventory:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "Production Inventory"
organization: "Platform Engineering"
state: present
- name: Buat inventory source dari AWS EC2
awx.awx.inventory_source:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "AWS EC2 Production"
inventory: "Production Inventory"
source: ec2
credential: "AWS Read-Only for Inventory"
region: "ap-southeast-1"
# Cache inventory 15 menit untuk mengurangi API call
cache_timeout: 900
# Group otomatis dari tag EC2
keyed_groups:
- prefix: role
key: tags.Role
- prefix: env
key: tags.Environment
update_on_launch: true
state: present
# 7. Buat Job Template untuk deployment
- name: Buat Job Template untuk deployment
awx.awx.job_template:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "Deploy — Production"
organization: "Platform Engineering"
job_type: run
inventory: "Production Inventory"
project: "Infrastructure Playbooks"
playbook: "playbooks/deploy.yml"
credentials:
- "Production SSH Key"
- "Ansible Vault Password"
# Survey: form input sebelum run
survey_enabled: true
survey_spec:
description: "Parameter deployment ke production"
name: "Deploy Parameters"
spec:
- variable: version
question_name: "Versi yang akan di-deploy"
question_description: "Contoh: 2.1.0 — pastikan tag Git sudah dibuat"
required: true
type: text
min: 1
max: 32
- variable: confirm
question_name: "Konfirmasi deploy ke production"
question_description: "Production deploy mempengaruhi user — pastikan on-call aware"
required: true
type: multiplechoice
choices: "yes\nno"
default: "no"
# Approval
ask_scm_diff_on_launch: true # Tampilkan Git diff
ask_limit_on_launch: true # Bisa batasi subset host
ask_variables_on_launch: true # Bisa override variabel
state: present
Perhatikan beberapa hal penting dari playbook di atas. Pertama, semua task yang menangani secret punya no_log: true — ini mencegah private key dan password bocor ke output Ansible (yang mungkin masuk ke log AWX dan jadi searchable). Kedua, scm_clean: true memastikan working copy di AWX bersih sebelum pull, tapi scm_delete_on_update: false mencegah credential di working copy terhapus. Ketiga, inventory source dikonfigurasi dengan cache_timeout: 900 (15 menit) — mencegah API throttling ke AWS dan mempercepat launch job.
ANTI-PATTERN: Menyimpan Credential sebagai Plaintext #
Salah satu kesalahan paling berbahaya yang sering muncul di deployment AWX: developer copy-paste private key atau AWS secret langsung ke field input di UI AWX, atau bahkan menulisnya di survey spec. AWX mengenkripsi credential di database, tetapi input dari user di survey atau extra variables tidak selalu dilindungi.
# ANTI-PATTERN: credential plaintext di Job Template survey
- name: Job Template dengan credential di survey
awx.awx.job_template:
name: "Deploy — Production"
survey_enabled: true
survey_spec:
spec:
- variable: ssh_private_key
question_name: "SSH Private Key"
type: textarea # ANTI-PATTERN: ini jadi tersimpan di launch history
- variable: db_password
question_name: "Database Password"
type: password # ANTI-PATTERN: tetap tersimpan di launch history
# BENAR: gunakan credential type eksternal (Vault, cloud secret manager)
---
- name: Buat credential type untuk HashiCorp Vault
awx.awx.credential_type:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "HashiCorp Vault SSH Key"
kind: cloud
inputs:
fields:
- id: vault_url
type: string
label: Vault URL
- id: vault_token
type: string
label: Vault Token
secret: true
- id: ssh_key_path
type: string
label: Path ke SSH Key di Vault
required:
- vault_url
- vault_token
- ssh_key_path
injectors:
ssh_private_key: "{{ lookup('hashi_vault', 'secret=' + ssh_key_path, token=vault_token, url=vault_url)['data']['private_key'] }}"
state: present
- name: Buat credential eksternal yang reference ke Vault
awx.awx.credential:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "Production SSH Key (from Vault)"
organization: "Platform Engineering"
credential_type: "HashiCorp Vault SSH Key"
inputs:
vault_url: "https://vault.company.internal:8200"
vault_token: "{{ vault_operator_token }}"
ssh_key_path: "secret/data/infrastructure/prod-ssh-key"
state: present
no_log: true
# Token di-decrypt saat job jalan, lalu di-inject sebagai ssh_private_key
# yang dipakai oleh module 'Machine' credential di runtime
Tabel Credential Type yang Tersedia #
AWX menyediakan banyak credential type bawaan, dan kita bisa membuat custom credential type untuk sistem internal:
| Credential Type | Use Case | Secret yang Disimpan |
|---|---|---|
| Machine | SSH ke Linux/Unix managed node | SSH private key, username, become password |
| Source Control | Pull playbook dari Git | Token PAT, SSH key, atau basic auth |
| Ansible Vault | Decrypt vault file di playbook | Vault password |
| Amazon Web Services | AWS API untuk inventory + module | Access key + secret key |
| Google Compute Platform | GCP API | Service account JSON |
| Microsoft Azure | Azure API | Client ID, secret, tenant, subscription |
| HashiCorp Vault | Ambil secret dari Vault saat runtime | Vault token + path (token bisa di-renew otomatis) |
| CyberArk AIM | Ambil secret dari CyberArk | Query string + AIM credentials |
| OpenStack | OpenStack API | Username, password, project, auth URL |
| Custom (kind: cloud) | Integrasi sistem internal | Sesuai definisi inputs.fields |
Prinsip yang harus kita pegang: credential tidak pernah ditulis di playbook, survey, atau extra variables. Selalu lewat credential type eksternal yang fetch secret saat runtime dari Vault, cloud KMS, atau sistem secret management lain. Audit log AWX mencatat siapa yang menggunakan credential apa, tetapi tidak pernah mencatat isi credential itu sendiri.
Jika kita melihat playbook AWX yang punyaextra_varsberisi string yang terlihat seperti-----BEGIN RSA PRIVATE KEY-----, berhenti. Itu private key, dan sekarang sudah bocor ke mana-mana: Git history, AWX launch history, log Ansible, dan mungkin log monitoring. Rotate key-nya, audit access-nya, dan refactor ke credential type eksternal.
ANTI-PATTERN: Inventory Hardcoded di Project Repository #
Pola yang sering muncul di tim yang baru adopsi AWX: simpan file inventory/production.yml di Git repository playbook, dan pakai inventory itu langsung dari Job Template. Ini bekerja, tetapi mengingkari salah satu kekuatan utama AWX — dynamic inventory.
# ANTI-PATTERN: inventory hardcoded di repository playbook
# inventory/production.yml — disimpan di Git, manual update saat ada server baru
---
all:
hosts:
web-01:
ansible_host: 10.0.1.10
web-02:
ansible_host: 10.0.1.11
db-01:
ansible_host: 10.0.2.10
children:
webservers:
hosts:
web-01:
web-02:
databases:
hosts:
db-01:
# BENAR: inventory source dinamis dari AWX
---
- name: Buat inventory source dari AWS EC2
awx.awx.inventory_source:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "AWS EC2 Production"
inventory: "Production Inventory"
source: ec2
credential: "AWS Read-Only for Inventory"
region: "ap-southeast-1"
# Filter: hanya instance yang running dan punya tag tertentu
instance_filters:
tag:Environment: production
instance-state-name: running
# Group otomatis dari tag EC2
keyed_groups:
- prefix: role
key: tags.Role
- prefix: env
key: tags.Environment
# Override variabel per host
hostvars:
ansible_user: ansible-deploy
ansible_python_interpreter: /usr/bin/python3
# Cache 15 menit, refresh saat launch job
cache_timeout: 900
update_on_launch: true
state: present
Dengan dynamic inventory, server baru yang di-tag Environment=production and Role=webserver di AWS otomatis masuk ke grup role_webserver dan env_production di AWX — tidak perlu commit apa-apa ke Git. Server yang di-terminate otomatis hilang dari inventory saat cache expired.
Tabel Sumber Inventory yang Didukung AWX #
| Source | Plugin / Script | Use Case | Cocok untuk |
|---|---|---|---|
| Static | File YAML/INI di Project | Server yang jarang berubah | On-premise legacy, lab |
| AWS EC2 | amazon.aws.aws_ec2 |
Instance EC2 | AWS-centric infrastructure |
| AWS RDS | Custom plugin | Database RDS | Inventory DB untuk konfigurasi |
| GCP Compute | google.cloud.gcp_compute |
Instance GCP | GCP-centric infrastructure |
| Azure VM | azure.azcollection.azure_rm |
VM di Azure | Azure-centric infrastructure |
| VMware vCenter | community.vmware.vmware_vm_inventory |
VM di vSphere | Hybrid cloud dengan on-prem |
| OpenStack | openstack.cloud.os_server_info |
Instance OpenStack | Private cloud OpenStack |
| Satellite | Custom script via Foreman | Server yang dimanage Red Hat Satellite | RHEL fleet management |
| Custom script | Script Python/Shell | CMDB internal, sumber custom | Organisasi dengan CMDB sendiri |
Cara paling sederhana bermigrasi dari static ke dynamic: buat inventory baru dengan source yang sama dengan cloud provider kita, lalu point Job Template ke inventory baru itu. Inventory lama bisa di-archive, dan dynamic inventory jadi sumber utama.
Workflow Template: Pipeline Deployment dengan Approval #
Workflow Template adalah cara AWX mengimplementasikan pipeline. Alih-alih menulis script shell atau Python yang memanggil beberapa ansible-playbook secara berurutan, kita definisikan graph Job Template di UI AWX — lebih visual, lebih mudah di-review, dan terintegrasi dengan RBAC.
flowchart TD
START(["Start"]) --> A["JT: Lint & Validate"]
A -->|"success"| B{"JT: Deploy Staging"}
A -->|"failure"| NOTIFY1["End: Notify Failure"]
B -->|"success"| C{"JT: Integration Test"}
B -->|"failure"| NOTIFY2["End: Notify Failure"]
C -->|"success"| APPROVAL{"Gate: Approval"}
C -->|"failure"| NOTIFY3["End: Notify Failure"]
APPROVAL -->|"approved"| D["JT: Deploy Production"]
APPROVAL -->|"rejected"| HOLD["End: Hold"]
D -->|"success"| E["JT: Verify Health"]
D -->|"failure"| ROLLBACK["JT: Rollback Production"]
ROLLBACK --> NOTIFY4["End: Notify Failure"]
E -->|"healthy"| END(["End: Success"])
E -->|"unhealthy"| ROLLBACK
Workflow ini mengimplementasikan pipeline deployment production yang lengkap: lint → deploy staging → integration test → approval gate → deploy production → verify health. Kalau ada step yang gagal, jalur failure mengarah ke notification atau rollback.
# playbooks/configure-workflow.yml
---
- name: Buat workflow deployment lengkap
hosts: localhost
vars:
awx_host: "https://awx.company.internal"
awx_oauthtoken: "{{ vault_awx_token }}"
tasks:
- name: Buat Workflow Job Template
awx.awx.workflow_job_template:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "Full Deployment Pipeline"
organization: "Platform Engineering"
description: "Pipeline deployment lengkap: validate, staging, integration, approval, production, verify"
# Survey di level workflow
survey_enabled: true
survey_spec:
description: "Parameter deployment pipeline"
name: "Pipeline Parameters"
spec:
- variable: version
question_name: "Versi yang akan di-deploy"
required: true
type: text
state: present
register: workflow
- name: Tambahkan node validate ke workflow
awx.awx.workflow_job_template_node:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
workflow_job_template: "Full Deployment Pipeline"
identifier: "validate"
unified_job_template: "Validate Playbook"
state: present
- name: Tambahkan approval node setelah integration test
awx.awx.workflow_job_template_node:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
workflow_job_template: "Full Deployment Pipeline"
identifier: "production_approval"
all_parents_must_converge: false
# Approval node di-pause sampai reviewer setujui
state: present
register: approval_node
- name: Set approval ke 'always' — workflow pause di node ini
awx.awx.workflow_job_template_node_approval:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
workflow_job_template: "Full Deployment Pipeline"
node_identifier: "production_approval"
timeout: 3600 # Auto-cancel setelah 1 jam jika tidak ada approval
state: present
- name: Hubungkan node dengan success/failure links
awx.awx.workflow_job_template_node:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
workflow_job_template: "Full Deployment Pipeline"
identifier: "validate"
related:
success_nodes:
- identifier: "deploy_staging"
failure_nodes:
- identifier: "notify_failure"
state: present
- name: Sambungkan semua node ke production deploy dan rollback path
awx.awx.workflow_job_template_node:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
workflow_job_template: "Full Deployment Pipeline"
identifier: "deploy_production"
related:
success_nodes:
- identifier: "verify_health"
failure_nodes:
- identifier: "rollback_production"
state: present
Approval node adalah fitur yang sering di-underestimate. Saat workflow sampai di node ini, ia berhenti dan menunggu — tidak ada eksekusi lebih lanjut sampai reviewer menyetujui. Reviewer menerima notifikasi (bisa dikonfigurasi per workflow) dan bisa approve atau reject dari UI AWX atau via REST API. Ini mengimplementasikan four-eyes principle untuk production deployment tanpa perlu script tambahan.
Workflow ini bisa di-trigger dari banyak sumber: manual launch dari UI, REST API dari CI/CD, webhook dari GitHub/GitLab, atau schedule. Untuk integrasi dengan pipeline CI/CD yang lebih kompleks, lihat cicd/pipeline-design — artikel itu membahas bagaimana CI menghasilkan artifact dan CD (termasuk AWX workflow) mendistribusikannya.
RBAC: Kontrol Akses Berbasis Peran #
AWX punya sistem RBAC yang detail. Kita bisa memberikan permission ke user atau team di level Organization, Project, Inventory, Job Template, atau Workflow Template. Pattern yang umum: beri akses yang diperlukan saja, jangan lebih.
# playbooks/configure-rbac.yml
---
- name: Konfigurasi RBAC untuk tim development dan SRE
hosts: localhost
vars:
awx_host: "https://awx.company.internal"
awx_oauthtoken: "{{ vault_awx_token }}"
tasks:
# Tim developer: bisa execute staging, tapi BUKAN production
- name: Beri role execute ke tim developer untuk staging
awx.awx.role:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
team: "Developers"
role: execute
job_templates:
- "Deploy — Staging"
- "Restart App — Staging"
state: present
- name: Beri role read-only ke tim developer untuk production inventory
awx.awx.role:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
team: "Developers"
role: read
inventories:
- "Production Inventory"
state: present
# Developer bisa lihat isi production inventory (untuk debugging)
# tapi TIDAK bisa execute Job Template yang target production
# Tim SRE: admin penuh
- name: Beri role admin ke tim SRE
awx.awx.role:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
team: "SRE"
role: admin
organizations:
- "Platform Engineering"
state: present
# SRE bisa modify Job Template, manage credential, dll.
# Auditor: read-only di semua level
- name: Beri role read-only ke auditor
awx.awx.role:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
team: "Auditors"
role: read
organizations:
- "Platform Engineering"
state: present
# Auditor bisa lihat history job, konfigurasi, log
# tapi tidak bisa trigger atau modify apapun
Pattern RBAC yang baik: least privilege. Developer tidak perlu akses ke production credential; mereka hanya perlu Job Template staging. SRE perlu akses penuh karena mereka yang on-call. Auditor perlu read-only untuk compliance check. Setiap role punya alasan yang jelas.
Webhook: Trigger Job dari Sistem Eksternal #
AWX mendukung webhook untuk GitHub, GitLab, dan generic HTTP POST. Ini memungkinkan job dipicu otomatis saat ada push ke repository, atau saat pipeline CI selesai:
# Aktifkan webhook di Job Template
- name: Aktifkan webhook di Job Template
awx.awx.job_template:
controller_host: "{{ awx_host }}"
controller_oauthtoken: "{{ awx_oauthtoken }}"
name: "Deploy — Staging"
webhook_service: github # atau gitlab, atau 'none' untuk generic
webhook_credential: "GitHub Webhook Token" # opsional, untuk validasi HMAC
state: present
# Di GitHub Actions — trigger AWX saat ada push ke main
- name: Trigger AWX deployment
if: github.ref == 'refs/heads/main'
run: |
curl -X POST \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: push" \
-H "X-GitHub-Delivery: ${{ github.event_id }}" \
"https://awx.company.internal/api/v2/job_templates/42/github/" \
-d '{"ref": "refs/heads/main", "version": "${{ github.sha }}"}'
# Di GitLab CI — trigger AWX via webhook
trigger-awx:
stage: deploy
script:
- |
curl -X POST \
-H "X-Gitlab-Event: Push Hook" \
-H "X-Gitlab-Token: $AWX_WEBHOOK_SECRET" \
"https://awx.company.internal/api/v2/job_templates/42/gitlab/" \
-d '{"ref": "refs/heads/main"}'
only:
- main
Saat menggunakan webhook dengan HMAC credential, AWX akan memverifikasi signature dari GitHub/GitLab sebelum menjalankan job. Ini mencegah orang yang tidak berwenang memicu job hanya dengan mengetahui URL webhook. Untuk generic webhook (tanpa GitHub/GitLab), kita bisa mengimplementasikan custom authentication via header token.
Saat mengintegrasikan AWX dengan CI/CD pipeline, pemisahan peran penting: CI (build, test, scan) tidak perlu akses ke AWX production credential. CI cukup trigger AWX workflow via webhook; AWX sendiri yang handle deployment dengan credential-nya sendiri. Kalau CI di-compromise, attacker tidak langsung punya akses ke production — mereka hanya bisa trigger job yang tetap melalui approval gate AWX.
Observabilitas: Dashboard dan Alert dari AWX #
AWX mengekspos REST API yang bisa di-scrape untuk monitoring. Endpoint yang paling berguna untuk observabilitas:
# Total job yang berjalan (untuk gauge di dashboard)
GET /api/v2/unified_job_templates/?page_size=1
# Job yang gagal dalam 1 jam terakhir
GET /api/v2/jobs/?status=failed&started__gte=2026-06-08T04:00:00Z
# Job yang masih pending (antrian panjang = masalah)
GET /api/v2/jobs/?status=pending
# Inventory source yang gagal sync
GET /api/v2/inventory_sources/?last_job_failed=true
Data ini bisa di-scrape ke Prometheus via json_exporter, lalu divisualisasikan di Grafana. Artikel observability/dashboard membahas pola membuat dashboard dari REST API seperti ini. Untuk alerting, kita bisa menetapkan alert Prometheus berdasarkan metric: awx_jobs_failed_total > 5 artinya ada 5 job gagal dalam 5 menit terakhir, kemungkinan ada masalah di environment.
Ringkasan #
- AWX adalah open source (Apache 2.0), cocok untuk lab, homelab, dan tim yang mampu maintain upgrade sendiri. Tower / Automation Controller berbayar dengan support Red Hat, cocok untuk perusahaan dengan compliance requirement. Keduanya berbagi codebase inti.
- AWX dipisah jadi control plane (API, UI, database, scheduler) dan execution plane (execution node yang jalankan playbook). Scaling horizontal dengan tambah execution node; control plane cukup untuk traffic UI dan API.
- Konsep inti: Organization → Project (Git repo) + Inventory (host target) + Credential (secret) + Job Template (kombinasi ketiganya) + Workflow Template (graph beberapa Job Template dengan approval).
- ANTI-PATTERN: jalankan playbook langsung dari CLI untuk production — tidak ada audit, approval, atau rollback. Gunakan Job Template dengan
ask_scm_diff_on_launchdan approval gate.- ANTI-PATTERN: credential plaintext di survey atau extra variables — selalu pakai credential type eksternal (Vault, cloud KMS). Audit log mencatat siapa yang akses credential, bukan isinya.
- ANTI-PATTERN: inventory hardcoded di repository — gunakan inventory source dinamis (AWS EC2, GCP, Azure) dengan
keyed_groupsuntuk auto-grouping dari tag/label cloud.- Workflow Template mengimplementasikan pipeline deployment (lint → staging → integration → approval → production → verify) tanpa kode. Approval node enforces four-eyes principle.
- RBAC harus pakai least privilege: developer punya execute di staging tapi tidak di production; SRE punya admin di production; auditor punya read-only.
- Webhook memungkinkan CI/CD trigger job AWX via HTTP POST. Gunakan HMAC credential untuk validasi signature, dan pisahkan secret CI dengan credential AWX.
- Observabilitas lewat REST API AWX — scrape ke Prometheus, visualisasikan di Grafana, set alert untuk job gagal, antrian panjang, dan inventory sync failure.
← Sebelumnya: Strategy & Serial Berikutnya: Pipeline Design →