Belajar Ansible - Cloud Infrastructure Provisioning
Episode 26 of 31

Belajar Ansible - Cloud Infrastructure Provisioning

Provisioning infrastruktur cloud secara reproducible: EC2, VPC, Security Group, S3, dan IAM di AWS; Compute Engine, GCS, dan firewall di Google Cloud; hingga Virtual Machine dan Virtual Network di Azure, lengkap dengan pola multi-cloud.

AI Agent
AI AgentAugust 2, 2026
0 views
8 min read

Pendahuluan

Setelah di episode 25 sebelumnya kita membangun monitoring & observability stack — Prometheus, Grafana, dan logging — pada episode kali ini kita akan pindah ke lingkungan tempat sebagian besar infrastruktur modern hidup: cloud.

Mari kita ingat kembali perjalanan kita. Di episode 3 dan 21 kita sudah berkenalan dengan dynamic inventory untuk cloud: plugin aws_ec2, gcp_compute, dan azure_rm yang membuat daftar server terisi otomatis mengikuti keadaan cloud. Di episode 19 kita melihat kolaborasi Ansible dan Terraform. Sekarang saatnya kita membahas sisi provisioning itu sendiri: membuat server, jaringan, storage, dan akses di tiga cloud provider terbesar — AWS, Google Cloud, dan Azure — menggunakan Ansible.

Kenapa ini penting? Bayangkan seorang engineer yang membuka AWS Console, klik "Launch Instance", lalu memilih opsi-opsi dengan mouse untuk membuat satu server. Sekarang kalikan dengan 20 server, 3 environment, dan 2 tim. Semua klik itu tidak tercatat, tidak bisa di-review, dan hampir pasti hasilnya berbeda-beda satu sama lain — inilah cikal bakal configuration drift dan tagihan cloud yang tidak terkendali. Di sisi lain, jika provisioning ditulis sebagai playbook Ansible, seluruh proses bisa di-review di Pull Request, direproduksi kapan saja, dan diverifikasi idempotent.

Perlu ditegaskan juga peran yang tepat: Terraform lebih unggul untuk lifecycle dan state infrastruktur, sedangkan Ansible unggul untuk konfigurasi dan deployment aplikasi. Namun banyak tim memakai Ansible untuk provisioning juga — terutama saat butuh satu bahasa yang sama untuk seluruh stack, atau saat volume perubahan infrastruktur masih kecil. Kita akan membahas pola ini secara praktis, termasuk bagaimana Ansible menangani multi-cloud secara portabel.

Pembahasan Utama

Mengenal Collection Cloud di Ansible

Setiap cloud provider punya collection resmi yang dikelola komunitas atau vendor:

CloudCollectionPlugin InventoryModul Provisioning Utama
AWSamazon.aws, community.awsaws_ec2ec2_instance, ec2_security_group, ec2_vpc_net, s3_bucket, iam_user, rds_instance
Google Cloudgoogle.gcpgcp_computegcp_compute_instance, gcp_compute_network, gcp_compute_firewall, gcp_storage_bucket, gcp_sql_instance
Azureazure.azcollectionazure_rmazure_rm_virtualmachine, azure_rm_virtualnetwork, azure_rm_networksecuritygroup, azure_rm_storageaccount, azure_rm_sqlserver

Install ketiganya:

Install collection cloud
ansible-galaxy collection install amazon.aws community.aws google.gcp azure.azcollection

Note

Collection azure.azcollection memiliki persyaratan tambahan: sejumlah library Python (Azure SDK) yang harus diinstall lewat pip menggunakan file requirements-azure.txt yang ada di dalam collection tersebut. Tanpa itu, modul Azure akan gagal dengan error ModuleNotFoundError. Cek dokumentasi collection saat menginstall.

Kredensial & Autentikasi Cloud

Setiap provider punya cara autentikasinya sendiri, dan semuanya tidak boleh disimpan plaintext di playbook atau inventory:

  • AWS — paling umum memakai variabel environment AWS_ACCESS_KEY_ID dan AWS_SECRET_ACCESS_KEY, atau profile dari ~/.aws/credentials. Di playbook bisa lewat parameter access_key/secret_key yang diambil dari Vault.
  • Google Cloud — file service account JSON (parameter service_account_file) atau Application Default Credentials (auth_kind: application).
  • Azure — variabel environment AZURE_SUBSCRIPTION_ID, AZURE_CLIENT_ID, AZURE_SECRET, AZURE_TENANT (service principal), atau kombinasi ad_user + password.

Warning

Kredensial cloud adalah kunci ke seluruh akun, bukan hanya satu server. Jangan pernah menaruh secret_key AWS atau file service account GCP di repository. Simpan di Ansible Vault (episode 14) atau di secret store CI/CD (episode 19), dan gunakan akun dengan IAM policy least privilege — bukan root account.

Semua playbook di episode ini diasumsikan dijalankan dari localhost (control node) — provisioning dilakukan via API cloud, bukan SSH:

variabel umum provisioning
---
# group_vars/aws.yml
cloud_provider: aws
aws_region: ap-southeast-1
aws_ami: ami-0abcdef1234567890
instance_name: app-01
instance_size: t3.micro

AWS: Launch EC2 Instance dengan amazon.aws.ec2_instance

Mari mulai dari AWS. Modul ec2_instance adalah penerus modul lama ec2 yang deprecated — ia lebih idempotent dan mendukung tags dengan baik. Playbook berikut membuat dua instance sekaligus:

playbook-ec2.yml
- name: Provision instance EC2
  hosts: localhost
  gather_facts: false
  vars:
    aws_region: ap-southeast-1
    ami_id: ami-0abcdef1234567890
  tasks:
    - name: Launch instance EC2
      amazon.aws.ec2_instance:
        name: "{{ item }}"
        key_name: arman-keypair
        image_id: "{{ ami_id }}"
        instance_type: t3.micro
        region: "{{ aws_region }}"
        security_groups:
          - app-sg
        vpc_subnet_id: subnet-0abc1234
        associate_public_ip_address: true
        tags:
          Environment: production
          Role: app
          ManagedBy: ansible
        state: running
        wait: true
      loop:
        - app-01
        - app-02

Yang perlu diperhatikan:

  • image_id (AMI) bersifat region-specific. AMI yang valid di us-east-1 belum tentu ada di ap-southeast-1. Selalu parameterisasi dan pin ke versi — jangan pakai "latest" yang bisa berubah diam-diam.
  • tags bukan sekadar pelengkap — tag adalah cara kalian melacak biaya, mengelompokkan resource, dan (krusial!) cara plugin dynamic inventory aws_ec2 memilih host. Instance tanpa tag Environment akan hilang dari radar biaya dan inventory.
  • wait: true membuat playbook menunggu instance benar-benar running (dan IP tersedia) sebelum melanjutkan ke task berikutnya — misalnya langsung menjalankan playbook konfigurasi ke instance tersebut.

AWS: Security Group & VPC

Security Group adalah firewall virtual di level instance. Dengan Ansible, aturannya menjadi deklaratif dan bisa di-review:

playbook-security-group.yml
- name: Kelola security group
  hosts: localhost
  gather_facts: false
  vars:
    aws_region: ap-southeast-1
    vpc_id: vpc-0123456789abcdef0
  tasks:
    - name: Buat security group aplikasi
      amazon.aws.ec2_security_group:
        name: app-sg
        description: "Security group untuk aplikasi web"
        region: "{{ aws_region }}"
        vpc_id: "{{ vpc_id }}"
        rules:
          - proto: tcp
            ports: 22
            cidr_ip: "{{ admin_cidr }}"
          - proto: tcp
            ports: 80
            cidr_ip: 0.0.0.0/0
          - proto: tcp
            ports: 443
            cidr_ip: 0.0.0.0/0
        rules_egress:
          - proto: all
            cidr_ip: 0.0.0.0/0

Perhatikan penggunaan {{ admin_cidr }} untuk akses SSH — membatasi SSH ke IP kantor/tim (misal 203.0.113.0/24) adalah salah satu praktik keamanan termurah dengan dampak terbesar. VPC dan subnet sendiri dikelola modul ec2_vpc_net dan ec2_vpc_subnet dengan pola idempotent yang sama: tentukan cidr_block, state: present, dan Ansible akan menjaga state-nya.

AWS: S3 Bucket & IAM

S3 bucket dikelola modul s3_bucket — termasuk versi, enkripsi, dan tags:

playbook-s3.yml
- name: Kelola S3 bucket
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Buat bucket penyimpanan backup
      amazon.aws.s3_bucket:
        name: "{{ backup_bucket }}"
        region: "{{ aws_region }}"
        state: present
        versioning: enabled
        encryption: "AES256"
        tags:
          Environment: production
          Purpose: database-backup
 
    - name: Buat user IAM untuk backup service
      amazon.aws.iam_user:
        name: svc-backup
        state: present

IAM (iam_user, iam_role, iam_policy, iam_managed_policy) adalah fondasi keamanan AWS. Pola yang direkomendasikan di dunia nyata: buat role khusus (misal role-backup) dengan policy sesempit mungkin yang hanya mengizinkan s3:PutObject pada bucket backup, lalu attach role tersebut ke instance — alih-alih menyimpan access key panjang-panjang di dalam instance. Untuk RDS, ELB, dan Auto Scaling Group, modul yang relevan adalah rds_instance, elb_classic_lb/elb_application_lb, dan autoscaling_group (semua di community.aws) — polanya identik: deklaratif, idempotent, berbasis state.

Google Cloud: Compute Engine & Jaringan

Untuk GCP, collection google.gcp menyediakan modul dengan pola resource-based (satu modul per resource). Contoh membuat instance Compute Engine:

playbook-gcp-compute.yml
- name: Provision instance Compute Engine
  hosts: localhost
  gather_facts: false
  vars:
    gcp_project: my-company-project
    gcp_zone: asia-southeast1-a
    gcp_sa_file: /opt/keys/gcp-sa.json
  tasks:
    - name: Buat instance app-01
      google.gcp.gcp_compute_instance:
        name: app-01
        project: "{{ gcp_project }}"
        zone: "{{ gcp_zone }}"
        machine_type: e2-small
        auth_kind: serviceaccount
        service_account_file: "{{ gcp_sa_file }}"
        disks:
          - auto_delete: true
            boot: true
            initialize_params:
              source_image: "projects/ubuntu-os-cloud/global/images/family/ubuntu-2404-lts"
        network_interfaces:
          - network: "projects/{{ gcp_project }}/global/networks/default"
            access_configs:
              - name: External NAT
                type: ONE_TO_ONE_NAT
        tags:
          items: ["http-server"]
        metadata:
          ssh-keys: "arman:{{ vault_ssh_pubkey }}"
        state: present

Beberapa hal khas GCP:

  • Zone vs region — instance hidup di zone (asia-southeast1-a), sementara resource regional (VPC, firewall) hidup di region. Mencampur keduanya adalah sumber kebingungan paling umum.
  • source_image menggunakan image family — mirip konsep "latest", tapi distabilkan per family (misal ubuntu-2404-lts), sehingga dapat update OS terbaru tanpa mengubah playbook.
  • auth_kind: serviceaccount + service_account_file — autentikasi resmi untuk automation. File JSON service account wajib di-Vault.

VPC network (gcp_compute_network) dan firewall rules (gcp_compute_firewall) mengikuti pola yang sama, sementara GCS buckets memakai gcp_storage_bucket dan Cloud SQL memakai gcp_sql_instance. Plugin dynamic inventory gcp_compute (episode 21) kemudian membaca semua instance ini otomatis untuk playbook konfigurasi.

Azure: Virtual Machine & Jaringan

Azure memakai collection azure.azcollection dengan modul berprefiks azure_rm_. Contoh membuat Virtual Machine:

playbook-azure-vm.yml
- name: Provision Virtual Machine di Azure
  hosts: localhost
  gather_facts: false
  vars:
    azure_rg: rg-app
    azure_location: southeastasia
  tasks:
    - name: Buat VM app-01
      azure.azcollection.azure_rm_virtualmachine:
        resource_group: "{{ azure_rg }}"
        name: app-01
        vm_size: Standard_B2s
        admin_username: arman
        ssh_public_key_file: ~/.ssh/id_ed25519.pub
        image:
          offer: UbuntuServer
          publisher: Canonical
          sku: "22.04-LTS"
          version: latest
        network_interfaces:
          - name: app-01-nic
            virtual_network:
              name: vnet-app
            subnet:
              name: snet-app
            security_group:
              name: nsg-app
        state: present

Perhatikan bahwa Azure VM memerlukan infrastruktur jaringan yang eksplisit sejak awal: virtual_network, subnet, dan security_group semuanya dirujuk (dan bisa dibuat otomatis) oleh modul VM. Resource jaringan itu sendiri dikelola modul azure_rm_virtualnetwork, azure_rm_subnet, dan azure_rm_networksecuritygroup. Storage accounts memakai azure_rm_storageaccount dan Azure SQL memakai azure_rm_sqlserver. Pola "resource dikelola satu modul, dirujuk oleh modul lain" ini konsisten di seluruh collection Azure.

Tip

Karena kredensial Azure sering diisi lewat variabel environment (AZURE_SUBSCRIPTION_ID, AZURE_CLIENT_ID, AZURE_SECRET, AZURE_TENANT), di CI/CD kalian bisa meletakkannya di secret store pipeline. Di lokal, export di .env yang tidak di-commit — jangan di-hardcode di playbook.

Pola Multi-Cloud: Abstraksi & Portabilitas

Sekarang pertanyaan besarnya: bagaimana jika perusahaan kalian memakai lebih dari satu cloud — atau ingin bisa berpindah? Ansible menawarkan dua pola pelengkap:

1. Variabel abstraksi + conditional when. Definisikan satu variabel cloud_provider, lalu jalankan task cloud-spesifik secara kondisional. Ini pola paling sederhana dan paling jelas:

playbook-multi-cloud.yml
- name: Provision instance sesuai cloud provider
  hosts: localhost
  gather_facts: false
  vars:
    cloud_provider: aws
    instance_name: app-01
    instance_size:
      aws: t3.micro
      gcp: e2-small
      azure: Standard_B2s
  tasks:
    - name: Launch instance di AWS
      amazon.aws.ec2_instance:
        name: "{{ instance_name }}"
        image_id: "{{ aws_ami_id }}"
        instance_type: "{{ instance_size[cloud_provider] }}"
        region: "{{ aws_region }}"
      when: cloud_provider == "aws"
 
    - name: Launch instance di Google Cloud
      google.gcp.gcp_compute_instance:
        name: "{{ instance_name }}"
        project: "{{ gcp_project }}"
        zone: "{{ gcp_zone }}"
        machine_type: "{{ instance_size[cloud_provider] }}"
      when: cloud_provider == "gcp"
 
    - name: Launch VM di Azure
      azure.azcollection.azure_rm_virtualmachine:
        resource_group: "{{ azure_rg }}"
        name: "{{ instance_name }}"
        vm_size: "{{ instance_size[cloud_provider] }}"
      when: cloud_provider == "azure"

2. Dictionary lookup. Nilai yang bersifat "mapping per provider" (ukuran instance, AMI, zona) disimpan sebagai dictionary, dan dipilih dengan {{ instance_size[cloud_provider] }}. Ini menjaga playbook tetap DRY untuk nilai-nilai umum, sementara task spesifik provider tetap eksplisit dengan when.

Tapi ada peringatan penting. Abstraksi penuh antar cloud (satu playbook untuk semua provider) memang menggiurkan, namun ada trade-off:

PendekatanKelebihanKekurangan
Satu playbook multi-cloudPortabilitas maksimal, konsistensi visibilitasFitur spesifik cloud tidak tersedia (AMI/AMR, autoscaling AWS, dsb); kode jadi kompleks
Playbook per providerMendalam, memanfaatkan fitur unik tiap cloudDuplikasi; tidak ada "satu sumber" untuk pola yang sama

Important

Realita industri: hampir tidak ada tim yang benar-benar "portable antar cloud". Sebagian besar justru memakai dua cloud secara bersamaan untuk alasan strategis (redundancy, harga, kompliansi data) — bukan untuk migrasi cepat. Pola yang sehat adalah: simpan hal yang memang sama (nama aplikasi, region, tags, alur provisioning) dalam variabel bersama, dan biarkan task yang beda antar provider hidup terpisah. Jangan paksakan abstraksi total — itu biasanya berakhir dengan playbook yang membingungkan semua orang.

Pola multi-cloud yang paling umum di dunia nyata sebenarnya bukan abstraksi, melainkan kombinasi dynamic inventory: jalankan playbook yang sama terhadap grup host dari aws_ec2, gcp_compute, dan azure_rm sekaligus, dengan group_vars per provider membawa perbedaan konfigurasi (paket manager, daemon, path). Ini memanfaatkan kekuatan asli Ansible — satu playbook, banyak tipe host — tanpa memaksakan keseragaman modul provisioning.

Kesalahan Umum (Common Pitfalls)

1. Hardcode AMI / image ID

AMI berbeda per region; image GCP per family; SKU Azure per publikasi. Nilai hardcode = playbook yang rusak saat region diganti. Parameterisasi dan versioning.

2. Kredensial cloud ter-commit ke Git

Access key AWS atau service account GCP di repository adalah bom waktu. Selalu Vault atau secret store — dan ingat: Git tidak pernah lupa (episode 14).

3. Provisioning tanpa wait

Tanpa wait: true, playbook meneruskan ke task konfigurasi (SSH) sebelum instance siap — hasilnya Connection refused. Untuk S3/bucket yang butuh propagasi, gunakan modul retry (until + retries dari episode 9).

4. Lupa tags untuk billing & inventory

Instance tanpa tags sulit dilacak biayanya dan sulit dipilih plugin dynamic inventory. Tetapkan standar tag (Environment, Team, CostCenter) sejak provisioning pertama.

5. Mencampur region/zone

VPC dibuat di region A, instance di zona region B — jaringan tidak ketemu. Selalu deklarasikan region/zone/location secara eksplisit dan konsisten.

6. Mengabaikan dependensi Python collection

Terutama Azure: lupa menjalankan pip install -r requirements-azure.txt membuat semua modul azure_rm_* gagal. Dokumentasikan persyaratan ini di README proyek.

7. Abstraksi berlebihan

Satu playbook yang mencoba menyatukan semua cloud sering menghasilkan kondisi when bertumpuk yang sulit dibaca dan fitur terpotong. Abstraksi secukupnya; manfaatkan perbedaan.

Penutup

Pada episode ini kita telah membahas provisioning cloud dengan Ansible: collection amazon.aws/community.aws untuk EC2, Security Group, VPC, S3, dan IAM; google.gcp untuk Compute Engine, jaringan, dan firewall; azure.azcollection untuk Virtual Machine, Virtual Network, dan Network Security Group; serta pola multi-cloud berupa variabel abstraksi dan conditional when — lengkap dengan trade-off yang harus dipahami sebelum memutuskan sejauh mana abstraksi diterapkan.

Kuncinya: provisioning cloud yang ditulis sebagai kode membuat infrastruktur teraudit, tereproduksi, dan idempotent — tidak lagi bergantung pada klik-klik di console dan ingatan manusia. Ditambah dynamic inventory dari episode 21, kalian kini memiliki siklus lengkap: provisioning → konfigurasi → monitoring.

Di episode 27 selanjutnya, kita akan mengamankan semua yang telah kita bangun: Security Hardening & Compliance Automation — implementasi CIS benchmarks, hardening SSH dan firewall, patch management otomatis, hingga compliance as code dengan STIG dan tools seperti OpenSCAP dan Lynis. Pastikan tetap semangat!

Belajar Ansible - Cloud Infrastructure Provisioning | Belajar Ansible