π οΈ Langkah Step-by-Step Setup Terraform
β Arsitektur yang Akan Dibuat:
+----------------+ +--------------------+
| Azure VM (Ubuntu) | <--> | Azure MySQL Database |
| Private IP | | Private Endpoint |
+----------------+ +--------------------+
|
v
+------------------------+
| Azure Storage Account |
| Private Endpoint |
+------------------------+
Bagus! Struktur Terraform yang dipi
β Step 1: Persiapan Install Terraform di WSL
- sudo apt update && sudo apt install -y software-properties-common gnupg curl
- curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
- echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com
- $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
- sudo apt update && sudo apt install -y terraform
- terraform -v
β Β Step 2: Mengatur Kredensial AZURE
Terraform menggunakan salah satu dari cara berikut untuk mengakses akun Azure kamu, berikut beberapa metode autentikasi yang umum digunakan di Azure untuk Terraform:
β Metode 1: Azure CLI Login (paling sederhana untuk personal use)
Langkah:
-
Login ke Azure CLI:
az login
-
Pastikan subscription aktif:
az account set --subscription "<SUBSCRIPTION_NAME_OR_ID>"
-
Setelah login berhasil, Terraform akan otomatis menggunakan session ini saat kamu menjalankan:
terraform init terraform apply
π’ Kelebihan: Simple, cocok untuk pengembangan lokal.
π΄ Kekurangan: Tidak cocok untuk otomatisasi di CI/CD (karena butuh login manual).
β Metode 2: Service Principal (disarankan untuk CI/CD atau automation)
Langkah:
-
Buat Service Principal:
az ad sp create-for-rbac --name "terraform-sp" --role="Contributor" --scopes="/subscriptions/<your-subscription-id>"
Output akan seperti ini:
{ "appId": "xxxxx", "displayName": "terraform-sp", "password": "yyyyy", "tenant": "zzzzz" }
-
Set environment variable (Linux/macOS):
export ARM_CLIENT_ID=xxxxx export ARM_CLIENT_SECRET=yyyyy export ARM_SUBSCRIPTION_ID=<your-subscription-id> export ARM_TENANT_ID=zzzzz
Atau di Windows (PowerShell):
$env:ARM_CLIENT_ID="xxxxx" $env:ARM_CLIENT_SECRET="yyyyy" $env:ARM_SUBSCRIPTION_ID="<your-subscription-id>" $env:ARM_TENANT_ID="zzzzz"
-
Jalankan Terraform seperti biasa:
terraform init terraform apply
π’ Kelebihan: Aman untuk automation, bisa disimpan di pipeline/secret CI/CD
π΄ Kekurangan: Perlu setup sedikit lebih panjang
π Metode Lain (Opsional)
-
Managed Identity (untuk VM di Azure yang menjalankan Terraform)
-
OIDC Workload Identity Federation (untuk GitHub Actions + Azure, lebih secure daripada menyimpan secret langsung)
Jika kamu hanya menggunakan Terraform dari laptop pribadi untuk deploy resource MPN, maka pakai metode az login
saja sudah cukup.
Tapi untuk pipeline atau tim DevOps, sebaiknya pakai Service Principal.
β Step 3: Koneksi Internet
Karena Terraform akan:
-
Menghubungi Azure API
-
Mendownload provider plugin
Pastikan WSL kamu punya akses internet (cek ping google.com
atau curl aws.amazon.com
).
β Β Step 4: Struktur Terraform sederhana
Struktur Terraform yang dipisah-pisah per file memang praktik terbaik (modular dan rapi). Berikut adalah struktur direktori dan isi file untuk:
-
VM Ubuntu (sebagai Web Server)
-
Azure Database for MySQL
-
Storage Account
-
Virtual Network + Subnet + NSG
-
Private Endpoints
π Struktur Folder
terraform/
βββ main.tf
βββ mysql.tf
βββ network.tf
βββ output.tf
βββ private_endpoints.tf
βββ provider.tf
βββ storage.tf
βββ variables.tf
βββ vm.tf
1. main.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
required_version = ">= 1.3.0"
}
2. variables.tf
variable "location" {
default = "southeastasia"
}
variable "resource_group_name" {
default = "rg-wid-research"
}
variable "vm_admin_username" {
default = "azureuser"
}
variable "vm_admin_password" {
default = "P@ssw0rd1234!" # Untuk testing, sebaiknya pakai `terraform.tfvars` dan jangan upload ke repo
}
3. network.tf
resource "azurerm_resource_group" "main" {
name = var.resource_group_name
location = var.location
}
resource "azurerm_virtual_network" "vnet" {
name = "vnet-webserver"
address_space = ["10.0.0.0/16"]
location = var.location
resource_group_name = azurerm_resource_group.main.name
}
# Subnet untuk VM/Webserver
resource "azurerm_subnet" "subnet" {
name = "subnet-main"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.1.0/24"]
}
# Subnet khusus untuk MySQL Flexible Server (dengan delegasi)
resource "azurerm_subnet" "subnet_mysql" {
name = "subnet-mysql"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.2.0/24"]
delegation {
name = "mysql-delegation"
service_delegation {
name = "Microsoft.DBforMySQL/flexibleServers"
actions = [
"Microsoft.Network/virtualNetworks/subnets/action"
]
}
}
}
#resource link antara vnet dan private DNS
#resource "azurerm_private_dns_zone_virtual_network_link" "mysql" {
# name = "mysql-vnet-link"
# resource_group_name = azurerm_resource_group.main.name
# private_dns_zone_name = azurerm_private_dns_zone.mysql.name
# virtual_network_id = azurerm_virtual_network.vnet.id
# registration_enabled = false
#}
resource "azurerm_network_security_group" "nsg" {
name = "nsg-webserver"
location = var.location
resource_group_name = azurerm_resource_group.main.name
security_rule {
name = "Allow-SSH"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "Allow-HTTP"
priority = 1002
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
resource "azurerm_subnet_network_security_group_association" "nsg_assoc" {
subnet_id = azurerm_subnet.subnet.id
network_security_group_id = azurerm_network_security_group.nsg.id
}
4. vm.tf
resource "azurerm_network_interface" "nic" {
name = "nic-webserver"
location = var.location
resource_group_name = azurerm_resource_group.main.name
ip_configuration {
name = "ipconfig1"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.vm_public.id
}
}
resource "azurerm_public_ip" "vm_public" {
name = "publicip-webserver"
location = var.location
resource_group_name = azurerm_resource_group.main.name
allocation_method = "Dynamic"
}
resource "azurerm_linux_virtual_machine" "vm" {
name = "vm-webserver"
location = var.location
resource_group_name = azurerm_resource_group.main.name
size = "Standard_B1s"
admin_username = var.vm_admin_username
admin_password = var.vm_admin_password
disable_password_authentication = false
network_interface_ids = [
azurerm_network_interface.nic.id,
]
os_disk {
name = "osdisk-webserver"
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts"
version = "latest"
}
tags = {
owner = "WID"
Team = "MS"
Description = "Resources For WID project"
Environment = "Research"
}
}
5. mysql.tf
resource "azurerm_mysql_flexible_server" "mysql" {
name = "wid-mysql-dbserver"
location = var.location
resource_group_name = azurerm_resource_group.main.name
administrator_login = "mysqladmin"
administrator_password = "MyS3cur3P@ssw0rd"
sku_name = "B_Standard_B1ms"
version = "8.0.21"
zone = "1"
storage {
size_gb = 20
}
# high_availability {
# mode = "Disabled"
# }
delegated_subnet_id = azurerm_subnet.subnet_mysql.id
private_dns_zone_id = azurerm_private_dns_zone.mysql.id
# depends_on = [azurerm_subnet_network_security_group_association.nsg_assoc]
depends_on = [
azurerm_subnet.subnet,
azurerm_subnet_network_security_group_association.nsg_assoc,
time_sleep.wait_for_dns_link
]
tags = {
owner = "WID"
Team = "MS"
Description = "Resources For WID project"
Environment = "Research"
}
}
6. storage.tf
resource "azurerm_storage_account" "storage" {
name = "widstoragewebapp01"
resource_group_name = azurerm_resource_group.main.name
location = var.location
account_tier = "Standard"
account_replication_type = "LRS"
# allow_blob_public_access = false
}
7. outputs.tf
output "vm_public_ip" {
value = azurerm_public_ip.vm_public.ip_address
}
output "mysql_fqdn" {
value = azurerm_mysql_flexible_server.mysql.fqdn
}
output "storage_account_name" {
value = azurerm_storage_account.storage.name
}
8.Β private_endpoints.tf
resource "azurerm_private_dns_zone" "mysql" {
name = "privatelink.mysql.database.azure.com"
resource_group_name = azurerm_resource_group.main.name
}
resource "azurerm_private_dns_zone_virtual_network_link" "mysql_link" {
name = "mysql-dnslink"
resource_group_name = azurerm_resource_group.main.name
private_dns_zone_name = azurerm_private_dns_zone.mysql.name
virtual_network_id = azurerm_virtual_network.vnet.id
}
#Tambahkan delay sampai proses tahap ini selesai agar tidak membuat error dalam pembuatan mysql flexibleServers
resource "time_sleep" "wait_for_dns_link" {
depends_on = [azurerm_private_dns_zone_virtual_network_link.mysql_link]
create_duration = "60s" # Atur sesuai kebutuhan, misal 60 detik
}
Atau kamu bisa nge clone/pull dari repository saya di https://github.com/iniwidi/terraform-azure
9.Β provider.tf
provider "azurerm" {
features {
resource_group {
prevent_deletion_if_contains_resources = false
}
}
}
π Cara Deploy
terraform init
terraform plan
terraform apply
Tidak semuanyaΒ gratis β beberapa resource yang dibuat dalam konfigurasi Terraform ini berbayar. Berikut penjelasan per resource dan apakah termasuk gratis (Free Tier) atau berbayar, berdasarkan Azure Free Tier dan pengalaman praktis:
π‘ Ringkasan Tipe Resource
Resource | Free? | Catatan |
---|---|---|
Resource Group | β Gratis | Hanya wadah/logis. |
Virtual Network + Subnet | β Gratis | Tidak dikenakan biaya. |
Network Security Group (NSG) | β Gratis | NSG itu sendiri gratis; trafiknya bisa dihitung ke bandwidth. |
Linux Virtual Machine (VM) | β οΈ Tergantung size | Free hanya untuk B1s selama 750 jam/bulan (di Free Tier). |
Azure MySQL Flexible Server | β Berbayar | Tidak termasuk dalam Free Tier. Paling murah mulai dari ~Rp50rb+/bln. |
Azure Storage Account | β /β οΈ Terbatas Gratis | 5 GB LRS blob storage masuk Free Tier; selebihnya berbayar. |
Private Endpoint (MySQL + Storage) | β Berbayar | Dikenakan biaya per jam per endpoint (~Rp20rb+/bln per endpoint). |
Public IP Address (optional) | β οΈ Terbatas Gratis | Dynamic Public IP tidak dikenai biaya saat tidak dipakai terus-menerus. |
π Rekomendasi Agar Hemat (Selama Uji Coba / Belajar)
-
Gunakan VM
Standard_B1s
di lokasi yang masuk Free Tier (contoh: East US, West Europe). -
Hapus Private Endpoint saat tidak diperlukan (karena ini yang biasanya diam-diam mahal).
-
Gunakan local MySQL di dalam VM jika tidak butuh Azure Database MySQL.
-
Monitor billing dari Azure Portal:
π Tips Tambahan
Kalau kamu ingin belajar full tanpa biaya, alternatif:
-
Gunakan 1 VM Ubuntu, dan install sendiri MySQL + Nginx/Apache + PHP di dalamnya.
-
Simulasi private communication via localhost atau VNet, tanpa Private Endpoint.
-
Storage bisa pakai disk tambahan VM (tanpa Storage Account).
No Comments