Praktik terbaik untuk menggunakan Terraform

Dokumen ini memberikan panduan dan rekomendasi untuk pengembangan yang efektif dengan Terraform di beberapa anggota tim dan alur kerja.

Panduan ini bukan merupakan pengantar untuk Terraform. Untuk pengantar penggunaan Terraform dengan Google Cloud, baca Mulai menggunakan Terraform.

Pedoman gaya dan struktur umum

Rekomendasi berikut mencakup gaya dan struktur dasar untuk konfigurasi Terraform Anda. Rekomendasi berlaku untuk modul Terraform yang dapat digunakan kembali dan untuk konfigurasi root.

Mengikuti struktur modul standar

  • Modul Terraform harus mengikuti struktur modul standar.
  • Mulai setiap modul dengan file main.tf, tempat resource berada secara default.
  • Dalam setiap modul, sertakan file README.md dalam format Markdown. Dalam file README.md, sertakan dokumentasi dasar tentang modul.
  • Tempatkan contoh dalam folder examples/, dengan subdirektori terpisah untuk setiap contoh. Untuk masing-masing contoh, sertakan file README.md mendetail.
  • Buat pengelompokan logis resource dengan file dan nama deskriptifnya sendiri, seperti network.tf, instances.tf, atau loadbalancer.tf.
    • Jangan memberikan file-nya sendiri kepada setiap resource. Kelompokkan resource berdasarkan tujuan bersama. Misalnya, gabungkan google_dns_managed_zone dan google_dns_record_set di dns.tf.
  • Dalam direktori root modul, hanya sertakan Terraform (*.tf) dan file metadata repositori (seperti README.md dan CHANGELOG.md).
  • Tempatkan dokumentasi tambahan dalam subdirektori docs/.

Menggunakan konvensi penamaan

  • Beri nama semua objek konfigurasi menggunakan garis bawah untuk membatasi beberapa kata. Praktik ini memastikan konsistensi dengan konvensi penamaan untuk jenis resource, jenis sumber data, dan nilai lainnya yang sudah ditetapkan. Konvensi ini tidak berlaku untuk argumen nama.

    Direkomendasikan:

    resource "google_compute_instance" "web_server" {
      name = "web-server"
    }
    

    Tidak direkomendasikan:

    resource "google_compute_instance" "web-server" {
      name = "web-server"
    }
    
  • Untuk menyederhanakan referensi ke resource yang merupakan satu-satunya dari jenisnya (misalnya, satu load balancer untuk seluruh modul), beri nama resource main.

    • Diperlukan upaya mental ekstra untuk mengingat some_google_resource.my_special_resource.id versus some_google_resource.main.id.
  • Untuk membedakan resource dari jenis yang sama satu sama lain (misalnya, primary dan secondary), berikan nama resource yang bermakna.

  • Buat nama resource dalam bentuk tunggal.

  • Dalam nama resource, jangan ulangi jenis resource. Contoh:

    Direkomendasikan:

    resource "google_compute_global_address" "main" { ... }
    

    Tidak direkomendasikan:

    resource "google_compute_global_address" "main_global_address" { … }
    

Menggunakan variabel dengan hati-hati

  • Deklarasikan semua variabel di variables.tf.
  • Berikan nama deskriptif untuk variabel yang relevan dengan penggunaan atau tujuannya:
    • Input, variabel lokal, dan output yang merepresentasikan nilai numerik—seperti ukuran disk atau ukuran RAM—harus diberi nama dengan unit (seperti ram_size_gb). Google Cloud API tidak memiliki unit standar, sehingga penamaan variabel dengan unit akan membuat unit input yang diharapkan jelas bagi pengelola konfigurasi.
    • Untuk unit penyimpanan, gunakan awalan satuan biner (pangkat 1024)—kibi, mebi, gibi. Untuk semua satuan pengukuran lainnya, gunakan awalan satuan desimal (pangkat 1000)—kilo, mega, giga. Penggunaan ini cocok dengan penggunaan dalam Google Cloud.
    • Untuk menyederhanakan logika bersyarat, berikan nama positif untuk variabel boolean—misalnya, enable_external_access.
  • Variabel harus memiliki deskripsi. Deskripsi otomatis disertakan dalam dokumentasi yang dibuat secara otomatis pada modul yang dipublikasikan. Deskripsi menambahkan konteks tambahan bagi developer baru yang tidak dapat diberikan oleh nama deskriptif.
  • Berikan jenis yang ditentukan untuk variabel.
  • Jika sesuai, berikan nilai default:
    • Untuk variabel dengan nilai yang tidak bergantung pada lingkungan (seperti ukuran disk), berikan nilai default.
    • Untuk variabel dengan nilai khusus lingkungan (seperti project_id), jangan berikan nilai default. Dengan cara ini, modul panggilan harus memberikan nilai yang bermakna.
  • Gunakan default kosong untuk variabel (seperti string atau daftar kosong) hanya jika membiarkan variabel tetap kosong adalah preferensi valid yang tidak ditolak API yang mendasarinya.
  • Gunakan variabel dengan bijak. Hanya buat parameter nilai yang harus bervariasi untuk setiap instance atau lingkungan. Saat memutuskan apakah akan mengekspos variabel, pastikan ada kasus penggunaan konkret untuk mengubah variabel tersebut. Jika hanya ada sedikit peluang bahwa variabel mungkin diperlukan, jangan mengekspos variabel tersebut.
    • Penambahan variabel dengan nilai default kompatibel dengan versi lama.
    • Penghapusan variabel tidak kompatibel dengan versi lama.
    • Jika literal digunakan kembali di beberapa tempat, Anda dapat menggunakan nilai lokal tanpa mengeksposnya sebagai variabel.

Mengekspos output

  • Atur semua output dalam satu file outputs.tf.
  • Berikan deskripsi yang bermakna untuk semua output.
  • Dokumentasikan deskripsi output di file README.md. Buat deskripsi secara otomatis di commit dengan alat seperti terraform-docs.
  • Tampilkan semua nilai berguna yang mungkin perlu dirujuk atau dibagikan oleh modul root. Terutama untuk modul open source atau modul yang sangat sering digunakan, ekspos semua output yang berpotensi untuk konsumsi.
  • Jangan meneruskan output secara langsung melalui variabel input, karena hal itu akan mencegahnya ditambahkan dengan benar ke grafik dependensi. Untuk memastikan bahwa dependensi implisit dibuat, pastikan referensi output diatribusikan dari resource. Dibandingkan mereferensikan variabel input untuk instance secara langsung, teruskan atribut seperti yang ditunjukkan di sini:

    Direkomendasikan:

    output "name" {
      description = "Name of instance"
      value       = google_compute_instance.main.name
    }
    

    Tidak direkomendasikan:

    output "name" {
      description = "Name of instance"
      value       = var.name
    }
    

Menggunakan sumber data

  • Tempatkan sumber data di samping resource yang mereferensikannya. Misalnya, jika Anda mengambil gambar yang akan digunakan dalam meluncurkan instance, tempatkan gambar tersebut di samping instance, bukan mengumpulkan resource data dalam filenya sendiri.
  • Jika jumlah sumber data menjadi besar, pertimbangkan untuk memindahkannya ke file data.tf khusus.
  • Untuk mengambil data yang relatif terhadap lingkungan saat ini, gunakan jenis interpolasi variabel atau resource.

Membatasi penggunaan skrip kustom

  • Gunakan skrip hanya jika diperlukan. Status resource yang dibuat melalui skrip tidak diperhitungkan atau dikelola oleh Terraform.
    • Hindari skrip kustom, jika memungkinkan. Hanya gunakan resource ini jika resource Terraform tidak mendukung perilaku yang diinginkan.
    • Setiap skrip kustom yang digunakan harus memiliki alasan yang terdokumentasi dengan jelas untuk rencana penghentian penggunaan yang ada dan ideal.
  • Terraform dapat memanggil skrip kustom melalui penyedia, termasuk penyedia lokal eksekutif.
  • Tempatkan skrip kustom yang dipanggil oleh Terraform di direktori scripts/.

Menyertakan skrip helper di direktori terpisah

  • Atur skrip helper yang tidak dipanggil oleh Terraform di direktori helpers/.
  • Dokumentasikan skrip helper di file README.md dengan penjelasan dan contoh pemanggilan.
  • Jika skrip helper menerima argumen, sediakan pemeriksaan argumen dan output --help.

Meletakkan file statis di direktori terpisah

  • File statis yang direferensikan Terraform tetapi tidak dijalankan (seperti skrip startup yang dimuat ke instance Compute Engine) harus diatur ke dalam direktori files/.
  • Letakkan HereDocs yang panjang dalam file eksternal, terpisah dari HCL-nya. Referensikan dengan fungsi file().
  • Untuk file yang dibaca menggunakan fungsi templatefile Terraform, gunakan ekstensi file .tftpl.
    • Template harus ditempatkan di direktori templates/.

Melindungi resource stateful

Untuk resource stateful, seperti database, pastikan perlindungan penghapusan diaktifkan. Contoh:

resource "google_sql_database_instance" "main" {
  name = "primary-instance"
  settings {
    tier = "D0"
  }

  lifecycle {
    prevent_destroy = true
  }
}

Menggunakan pemformatan bawaan

Semua file Terraform harus sesuai dengan standar terraform fmt.

Membatasi kompleksitas ekspresi

  • Batasi kompleksitas setiap ekspresi interpolasi individual. Jika banyak fungsi diperlukan dalam satu ekspresi, pertimbangkan untuk membaginya menjadi beberapa ekspresi menggunakan nilai lokal.
  • Jangan pernah ada lebih dari satu operasi ternary dalam satu baris. Sebagai gantinya, gunakan beberapa nilai lokal untuk membuat logika.

Menggunakan count untuk nilai bersyarat

Untuk membuat instance resource secara bersyarat, gunakan argumen meta count. Contoh:

variable "readers" {
  description = "..."
  type        = list
  default     = []
}

resource "resource_type" "reference_name" {
  // Do not create this resource if the list of readers is empty.
  count = length(var.readers) == 0 ? 0 : 1
  ...
}

Berhati-hatilah saat menggunakan variabel yang ditentukan pengguna ketika menetapkan variabel count untuk resource. Jika atribut resource disediakan untuk variabel tersebut (seperti project_id) dan resource tersebut belum ada, Terraform tidak dapat membuat rencana. Sebagai gantinya, Terraform melaporkan error value of count cannot be computed. Dalam kasus tersebut, gunakan variabel enable_x yang terpisah untuk melakukan komputasi logika bersyarat.

Menggunakan for_each untuk resource iterasi

Jika Anda ingin membuat beberapa salinan resource berdasarkan resource input, gunakan argumen meta for_each.

Memublikasikan modul ke registry

  • Modul yang dapat digunakan kembali: Publikasikan modul yang dapat digunakan kembali ke registry modul.

  • Modul open source: Publikasikan modul open source ke Terraform Registry.

  • Modul pribadi: Publikasikan modul pribadi ke registry pribadi.

Modul yang dapat digunakan kembali

Untuk modul yang dimaksudkan untuk digunakan kembali, selain panduan sebelumnya, gunakan panduan berikut.

Mengaktifkan API yang diperlukan dalam modul

Modul Terraform dapat mengaktifkan layanan apa pun yang diperlukan menggunakan resource google_project_service atau modul project_services. Menyertakan aktivasi API akan mempermudah demonstrasi.

  • Jika disertakan dalam modul, aktivasi API harus dapat dinonaktifkan dengan mengekspos variabel enable_apis yang ditetapkan secara default ke true.
  • Jika disertakan dalam modul, aktivasi API harus menetapkan disable_services_on_destroy ke false, karena atribut ini dapat menyebabkan masalah saat bekerja dengan beberapa instance modul ini.

    Contoh:

    module "project-services" {
      source  = "terraform-google-modules/project-factory/google//modules/project_services"
      version = "~> 12.0"
    
      project_id  = var.project_id
      enable_apis = var.enable_apis
    
      activate_apis = [
        "compute.googleapis.com",
        "pubsub.googleapis.com",
      ]
      disable_services_on_destroy = false
    }
    

Menyertakan file pemilik

Untuk semua modul bersama, sertakan file OWNERS (atau CODEOWNERS di GitHub), yang mendokumentasikan penanggung jawab untuk modul ini. Sebelum permintaan pull apa pun digabungkan, pemilik harus menyetujuinya.

Merilis versi yang diberi tag

Terkadang modul memerlukan perubahan yang dapat menyebabkan gangguan dan Anda perlu menyampaikan efeknya kepada pengguna agar mereka dapat menyematkan konfigurasi mereka ke versi tertentu.

Pastikan modul bersama mengikuti SemVer v2.0.0 saat versi baru diberi tag atau dirilis.

Saat mereferensikan modul, gunakan batasan versi untuk disematkan ke versi utama. Contoh:

module "gke" {
  source  = "terraform-google-modules/kubernetes-engine/google"
  version = "~> 20.0"
}

Jangan mengonfigurasi penyedia atau backend

Modul bersama tidak boleh mengonfigurasi penyedia atau backend. Sebagai gantinya, konfigurasikan penyedia dan backend di modul root.

Untuk modul bersama, tentukan versi penyedia minimum yang diperlukan dalam blok required_providers, sebagai berikut:

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = ">= 4.0.0"
    }
  }
}

Kecuali terbukti sebaliknya, asumsikan bahwa versi penyedia baru akan berfungsi.

Mengekspos label sebagai variabel

Izinkan fleksibilitas dalam pelabelan resource melalui antarmuka modul. Pertimbangkan untuk menyediakan variabel labels dengan nilai default peta kosong, seperti berikut:

variable "labels" {
  description = "A map of labels to apply to contained resources."
  default     = {}
  type        = "map"
}

Mengekspos output untuk semua resource

Variabel dan output memungkinkan Anda melakukan inferensi dependensi antara modul dan resource. Tanpa output apa pun, pengguna tidak dapat mengurutkan modul dengan benar sehubungan dengan konfigurasi Terraform mereka.

Untuk setiap resource yang ditentukan dalam modul bersama, sertakan setidaknya satu output yang mereferensikan resource tersebut.

Menggunakan submodul inline untuk logika yang kompleks

  • Modul inline dapat Anda gunakan untuk mengatur modul Terraform yang kompleks ke dalam unit-unit yang lebih kecil dan menghapus duplikat resource umum.
  • Tempatkan modul inline di modules/$modulename.
  • Perlakukan modul inline sebagai modul pribadi, bukan untuk digunakan oleh modul luar, kecuali jika dokumentasi modul bersama secara khusus menyatakan sebaliknya.
  • Terraform tidak melacak resource yang difaktorkan ulang. Jika Anda memulai dengan beberapa resource di modul level atas lalu memasukkannya ke submodul, Terraform akan mencoba membuat ulang semua resource yang telah difaktorkan ulang. Untuk mengurangi perilaku ini, gunakan blok moved saat pemfaktoran ulang.
  • Output yang ditetapkan oleh modul internal tidak otomatis ditampilkan. Untuk membagikan output dari modul internal, ekspor ulang modul tersebut.

Modul root Terraform

Konfigurasi root (modul root) adalah direktori kerja tempat Anda menjalankan Terraform CLI. Pastikan konfigurasi root mematuhi standar berikut (dan pedoman Terraform sebelumnya, jika berlaku). Rekomendasi eksplisit untuk modul root menggantikan panduan umum.

Meminimalkan jumlah resource di setiap modul root

Penting untuk menjaga konfigurasi root tunggal agar tidak bertambah terlalu besar, dengan terlalu banyak resource yang disimpan dalam direktori dan status yang sama. Semua resource dalam konfigurasi root tertentu di-refresh setiap kali Terraform dijalankan. Hal ini dapat menyebabkan eksekusi yang lambat jika resource yang disertakan dalam satu status terlalu banyak. Aturan umum: Jangan sertakan lebih dari 100 resource (dan idealnya hanya beberapa lusin) dalam satu status.

Menggunakan direktori terpisah untuk setiap aplikasi

Untuk mengelola aplikasi dan project secara independen, tempatkan resource untuk setiap aplikasi dan project di direktori Terraform-nya sendiri. Layanan dapat mewakili aplikasi tertentu atau layanan umum seperti jaringan bersama. Tempatkan semua kode Terraform untuk layanan tertentu dalam satu direktori (termasuk subdirektori).

Memisahkan aplikasi ke dalam subdirektori khusus lingkungan

Saat men-deploy layanan di Google Cloud, bagi konfigurasi Terraform untuk layanan tersebut menjadi dua direktori level teratas: direktori modules yang berisi konfigurasi aktual untuk layanan dan direktori environments yang berisi konfigurasi root untuk setiap lingkungan.

-- SERVICE-DIRECTORY/
   -- OWNERS
   -- modules/
      -- <service-name>/
         -- main.tf
         -- variables.tf
         -- outputs.tf
         -- provider.tf
         -- README
      -- ...other…
   -- environments/
      -- dev/
         -- backend.tf
         -- main.tf

      -- qa/
         -- backend.tf
         -- main.tf

      -- prod/
         -- backend.tf
         -- main.tf

Menggunakan direktori lingkungan

Untuk membagikan kode di seluruh lingkungan, referensikan modul. Biasanya dapat berupa modul layanan yang menyertakan konfigurasi Terraform bersama dasar untuk layanan tersebut. Dalam modul layanan, input umum hard code dan hanya memerlukan input spesifik lingkungan sebagai variabel.

Setiap direktori lingkungan harus berisi file berikut:

  • File backend.tf, mendeklarasikan lokasi status backend Terraform (biasanya Cloud Storage)
  • File main.tf yang membuat instance modul layanan

Setiap direktori lingkungan (dev, qa, prod) sesuai dengan ruang kerja Terraform default dan men-deploy versi layanan ke lingkungan tersebut. Ruang kerja ini mengisolasi resource khusus lingkungan ke dalam konteksnya sendiri. Hanya gunakan ruang kerja default.

Tidak direkomendasikan untuk memiliki beberapa ruang kerja CLI dalam suatu lingkungan karena alasan berikut:

  • Konfigurasi di setiap ruang kerja mungkin akan sulit diperiksa.
  • Memiliki satu backend bersama untuk beberapa ruang kerja tidak direkomendasikan karena backend bersama akan menjadi titik tunggal kegagalan jika digunakan untuk pemisahan lingkungan.
  • Meskipun penggunaan ulang kode dapat dilakukan, kode menjadi lebih sulit dibaca karena harus dialihkan berdasarkan variabel ruang kerja saat ini (misalnya, terraform.workspace == "foo" ? this : that).

Untuk informasi selengkapnya, lihat referensi berikut:

Mengekspos output melalui status jarak jauh

Pastikan Anda mengekspos output yang berguna dari instance modul dari modul root.

Misalnya, cuplikan kode berikut melewati output project ID dari instance modul factory project sebagai output dari modul root.

# Project root module
terraform {
  backend "gcs" {
    bucket  = "BUCKET"
  }
}

module "project" {
  source  = "terraform-google-modules/project-factory/google"
  ...
}

output "project_id" {
  value       = module.project.project_id
  description = "The ID of the created project"
}

Lingkungan dan aplikasi Terraform lainnya hanya dapat mereferensikan output level modul root.

Dengan menggunakan status jarak jauh, Anda dapat mereferensikan output modul root. Untuk mengizinkan penggunaan oleh aplikasi dependen lainnya untuk konfigurasi, pastikan Anda mengekspor informasi yang terkait dengan endpoint layanan, ke status jarak jauh.

# Networks root module
data "terraform_remote_state" "network_project" {
  backend = "gcs"

  config = {
    bucket = "BUCKET"
  }
}

module "vpc" {
  source  = "terraform-google-modules/network/google"
  version = "~> 9.0"

  project_id   = data.terraform_remote_state.network_project.outputs.project_id
  network_name = "vpc-1"
  ...
}

Terkadang, seperti saat memanggil modul layanan bersama dari direktori lingkungan, sebaiknya ekspor ulang seluruh modul turunan, seperti berikut:

output "service" {
  value       = module.service
  description = "The service module outputs"
}

Menyematkan ke versi penyedia minor

Di modul root, deklarasikan setiap penyedia dan sematkan ke versi minor. Hal ini memungkinkan upgrade otomatis ke rilis patch baru sekaligus tetap mempertahankan target yang solid. Agar konsisten, beri nama file versi dengan versions.tf.

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 4.0.0"
    }
  }
}

Menyimpan variabel dalam file tfvars

Untuk modul root, sediakan variabel menggunakan file variabel .tfvars. Agar konsisten, beri nama file variabel dengan terraform.tfvars.

Jangan tentukan variabel menggunakan opsi command line var-files atau var='key=val' alternatif. Opsi command line bersifat singkat dan mudah dilupakan. Menggunakan file variabel default lebih dapat diprediksi.

Memeriksa file .terraform.lock.hcl

Untuk modul root, file kunci dependensi .terraform.lock.hcl harus diperiksa dalam kontrol sumber. Hal ini memungkinkan pelacakan dan peninjauan perubahan dalam pilihan penyedia untuk konfigurasi tertentu.

Komunikasi lintas konfigurasi

Masalah umum yang muncul saat menggunakan Terraform adalah cara berbagi informasi di berbagai konfigurasi Terraform (mungkin dikelola oleh tim yang berbeda). Umumnya, informasi dapat dibagikan antarkonfigurasi tanpa harus disimpan dalam satu direktori konfigurasi (atau bahkan satu repositori).

Cara yang direkomendasikan untuk membagikan informasi di antara berbagai konfigurasi Terraform adalah dengan menggunakan status jarak jauh untuk mereferensikan modul root lainnya. Cloud Storage atau Terraform Enterprise adalah backend status pilihan.

Untuk membuat kueri resource yang tidak dikelola oleh Terraform, gunakan sumber data dari penyedia Google. Misalnya, akun layanan Compute Engine default dapat diambil menggunakan sumber data. Jangan gunakan sumber data untuk membuat kueri resource yang dikelola oleh konfigurasi Terraform lain. Cara ini dapat membuat dependensi implisit pada nama dan struktur resource yang mungkin dirusak secara tidak sengaja oleh operasi Terraform normal.

Bekerja dengan resource Google Cloud

Praktik terbaik untuk menyediakan resource Google Cloud dengan Terraform terintegrasi dalam modul Cloud Foundation Toolkit yang dikelola oleh Google. Bagian ini mengulang beberapa praktik terbaik tersebut.

Menyimulasikan image virtual machine

Secara umum, sebaiknya Anda membuat image virtual machine menggunakan alat seperti Packer. Terraform kemudian hanya perlu meluncurkan mesin menggunakan gambar yang telah dibuat sebelumnya.

Jika gambar yang dibuat sebelumnya tidak tersedia, Terraform dapat menyerahkan virtual machine baru ke alat manajemen konfigurasi dengan blok provisioner. Sebaiknya hindari metode ini dan hanya gunakan sebagai upaya terakhir. Untuk membersihkan status lama yang terkait dengan instance, penyedia yang memerlukan logika penghapusan harus menggunakan blok provisioner dengan when = destroy.

Terraform harus memberikan informasi konfigurasi VM ke manajemen konfigurasi dengan metadata instance.

Mengelola Identity and Access Management

Saat menyediakan asosiasi IAM dengan Terraform, tersedia beberapa resource yang berbeda:

  • google_*_iam_policy (misalnya, google_project_iam_policy)
  • google_*_iam_binding (misalnya, google_project_iam_binding)
  • google_*_iam_member (misalnya, google_project_iam_member)

google_*_iam_policy dan google_*_iam_binding membuat asosiasi IAM otoritatif, dengan resource Terraform berfungsi sebagai satu-satunya sumber tepercaya untuk izin yang dapat ditetapkan ke resource yang relevan.

Jika izin berubah di luar Terraform, Terraform pada eksekusi berikutnya akan menimpa semua izin untuk mewakili kebijakan seperti yang ditetapkan dalam konfigurasi Anda. Hal ini mungkin masuk akal untuk resource yang sepenuhnya dikelola oleh konfigurasi Terraform tertentu, tetapi hal ini berarti peran yang dikelola secara otomatis oleh Google Cloud akan dihapus, yang berpotensi mengganggu fungsionalitas beberapa layanan.

Untuk mencegah hal ini, sebaiknya gunakan resource google_*_iam_member secara langsung atau modul IAM dari Google.

Kontrol versi

Seperti bentuk kode lainnya, simpan kode infrastruktur di kontrol versi untuk mempertahankan histori dan memungkinkan rollback yang mudah.

Menggunakan strategi cabang default

Untuk semua repositori yang berisi kode Terraform, gunakan strategi berikut secara default:

  • Cabang main adalah cabang pengembangan utama dan merepresentasikan kode terbaru yang disetujui. Cabang main dilindungi.
  • Pengembangan terjadi pada cabang fitur dan perbaikan bug yang bercabang dari cabang main.
    • Beri nama cabang fitur dengan feature/$feature_name.
    • Beri nama cabang perbaikan bug dengan fix/$bugfix_name.
  • Setelah perbaikan bug atau fitur selesai, gabungkan kembali ke cabang main dengan permintaan pull.
  • Untuk mencegah konflik penggabungan, lakukan rebase cabang sebelum menggabungkannya.

Menggunakan cabang lingkungan untuk konfigurasi root

Untuk repositori yang menyertakan konfigurasi root yang di-deploy langsung ke Google Cloud, diperlukan strategi peluncuran yang aman. Sebaiknya buat cabang terpisah untuk setiap lingkungan. Dengan demikian, perubahan pada konfigurasi Terraform dapat dipromosikan dengan menggabungkan perubahan antara cabang yang berbeda.

Cabang terpisah untuk setiap lingkungan

Memungkinkan visibilitas yang luas

Buat kode sumber dan repositori Terraform dapat dilihat secara luas dan dapat diakses di seluruh organisasi engineering oleh pemilik infrastruktur (misalnya, SRE) dan pemangku kepentingan infrastruktur (misalnya, developer). Hal ini memastikan pemangku kepentingan infrastruktur dapat memiliki pemahaman yang lebih baik tentang infrastruktur yang mereka andalkan.

Dorong pemangku kepentingan infrastruktur untuk mengirimkan permintaan penggabungan sebagai bagian dari proses permintaan perubahan.

Jangan pernah melakukan commit secret

Jangan pernah melakukan commit secret ke kontrol sumber, termasuk dalam konfigurasi Terraform. Sebagai gantinya, upload file ke sistem seperti Secret Manager dan referensikan menggunakan sumber data.

Perlu diingat bahwa nilai sensitif tersebut mungkin masih berakhir di file status dan mungkin juga diekspos sebagai output.

Mengatur repositori berdasarkan batasan tim

Meskipun Anda dapat menggunakan direktori terpisah untuk mengelola batas logis antara resource, batas organisasi dan logistik menentukan struktur repositori. Secara umum, gunakan prinsip desain yang memisahkan konfigurasi dengan persyaratan persetujuan dan pengelolaan yang berbeda ke dalam repositori kontrol sumber yang berbeda. Untuk menggambarkan prinsip ini, berikut beberapa kemungkinan konfigurasi repositori:

  • Satu repositori pusat: Dalam model ini, semua kode Terraform dikelola secara terpusat oleh satu tim platform. Model ini berfungsi paling baik ketika ada tim infrastruktur khusus yang bertanggung jawab atas semua pengelolaan cloud dan menyetujui setiap perubahan yang diminta oleh tim lain.

  • Repositori tim: Dalam model ini, setiap tim bertanggung jawab atas repositori Terraform mereka sendiri, tempat mereka mengelola semua yang terkait dengan infrastruktur yang mereka miliki. Misalnya, tim keamanan mungkin memiliki repositori tempat semua kontrol keamanan dikelola, dan tim aplikasi masing-masing memiliki repositori Terraform sendiri untuk men-deploy dan mengelola aplikasinya.

    Mengatur repositori sepanjang batas tim adalah struktur terbaik untuk sebagian besar skenario perusahaan.

  • Repositori yang dipisahkan: Dalam model ini, setiap komponen Terraform logis dibagi dalam repositorinya sendiri. Misalnya, jaringan mungkin memiliki repositori khusus dan mungkin ada repositori factory project terpisah untuk pembuatan dan pengelolaan project. Cara ini paling efektif di lingkungan yang sangat terdesentralisasi dengan peralihan tanggung jawab yang sering dilakukan antar- tim.

Contoh struktur repositori

Anda dapat menggabungkan prinsip-prinsip ini untuk membagi konfigurasi Terraform di berbagai jenis repositori:

  • Dasar
  • Khusus aplikasi dan tim
Repositori dasar

Repositori dasar yang berisi komponen pusat utama, seperti IAM organisasi atau folder. Repositori ini dapat dikelola oleh tim cloud pusat.

  • Dalam repositori ini, sertakan direktori untuk setiap komponen utama (misalnya, folder, jaringan, dan sebagainya).
  • Dalam direktori komponen, sertakan folder terpisah untuk setiap lingkungan (dengan mempertimbangkan panduan struktur direktori yang disebutkan sebelumnya).

Struktur repositori dasar

Repositori khusus aplikasi dan tim

Deploy repositori khusus aplikasi dan tim secara terpisah untuk setiap tim guna mengelola konfigurasi Terraform khusus aplikasi yang unik.

Struktur repositori khusus aplikasi dan tim

Operasi

Menjaga keamanan infrastruktur Anda bergantung pada kepemilikan proses yang stabil dan aman untuk menerapkan update Terraform.

Selalu membuat rencana terlebih dahulu

Selalu buat rencana terlebih dahulu untuk eksekusi Terraform. Simpan rencana ke file output. Setelah pemilik infrastruktur menyetujuinya, jalankan rencana. Meskipun membuat perubahan prototipe secara lokal, developer harus membuat rencana dan meninjau resource yang akan ditambahkan, diubah, dan dihancurkan sebelum menerapkan rencana.

Menerapkan pipeline otomatis

Untuk memastikan konteks eksekusi yang konsisten, jalankan Terraform melalui alat otomatis. Jika sistem build (seperti Jenkins) sudah digunakan dan diadopsi secara luas, gunakan sistem tersebut untuk menjalankan perintah terraform plan dan terraform apply secara otomatis. Jika tidak ada sistem yang tersedia, gunakan Cloud Build atau Terraform Cloud.

Menggunakan kredensial akun layanan untuk continuous integration

Ketika dijalankan dari mesin di pipeline CI/CD, Terraform akan mewarisi kredensial akun layanan dari layanan yang menjalankan pipeline. Jika memungkinkan, jalankan pipeline CI di Google Cloud karena Cloud Build, Google Kubernetes Engine, atau Compute Engine memasukkan kredensial tanpa mendownload kunci akun layanan.

Untuk pipeline yang berjalan di luar Google Cloud, pilih workload identity federation untuk mendapatkan kredensial tanpa mendownload kunci akun layanan.

Menghindari impor resource yang sudah ada

Jika memungkinkan, hindari mengimpor resource yang sudah ada (menggunakan terraform import), karena hal ini dapat mempersulit pemahaman tentang asal dan konfigurasi resource yang dibuat secara manual. Sebagai gantinya, buat resource baru melalui Terraform dan hapus resource lama.

Jika menghapus resource lama akan menimbulkan toil yang signifikan, gunakan perintah terraform import dengan persetujuan eksplisit. Setelah resource diimpor ke Terraform, kelola resource secara eksklusif dengan Terraform.

Google menyediakan alat yang dapat Anda gunakan untuk mengimpor resource Google Cloud ke status Terraform. Untuk informasi selengkapnya, baca bagian Mengimpor resource Google Cloud ke status Terraform.

Jangan mengubah status Terraform secara manual

File status Terraform sangat penting untuk mempertahankan pemetaan antara konfigurasi Terraform dan resource Google Cloud. Kerusakan dapat menyebabkan masalah infrastruktur besar. Saat modifikasi pada status Terraform diperlukan, gunakan perintah terraform state.

Meninjau pin versi secara rutin

Penyematan versi akan memastikan stabilitas, tetapi mencegah perbaikan bug dan peningkatan lainnya disertakan ke dalam konfigurasi Anda. Oleh karena itu, tinjau pin versi untuk Terraform, penyedia Terraform, dan modul secara rutin.

Untuk mengotomatiskan proses ini, gunakan alat seperti Dependabot.

Menggunakan kredensial default aplikasi saat berjalan secara lokal

Saat melakukan iterasi secara lokal pada konfigurasi Terraform, developer harus melakukan autentikasi dengan menjalankan gcloud auth application-default login untuk membuat kredensial default aplikasi. Jangan mendownload kunci akun layanan, karena kunci yang didownload lebih sulit dikelola dan diamankan.

Menetapkan alias ke Terraform

Untuk mempermudah pengembangan lokal, Anda dapat menambahkan alias ke profil command shell:

  • alias tf="terraform"
  • alias terrafrom="terraform"

Keamanan

Terraform memerlukan akses sensitif ke infrastruktur cloud Anda agar dapat beroperasi. Mengikuti praktik terbaik keamanan ini dapat membantu meminimalkan risiko terkait dan meningkatkan keamanan cloud Anda secara keseluruhan.

Menggunakan status jarak jauh

Untuk pelanggan Google Cloud, sebaiknya gunakan backend status Cloud Storage. Pendekatan ini mengunci status untuk memungkinkan kolaborasi sebagai tim. Tindakan ini juga memisahkan status dan semua informasi yang mungkin bersifat sensitif dari kontrol versi.

Pastikan hanya sistem build dan administrator dengan hak istimewa tinggi yang dapat mengakses bucket yang digunakan untuk status jarak jauh.

Untuk mencegah pengubahan status pengembangan secara tidak sengaja ke kontrol sumber, gunakan gitignore untuk file status Terraform.

Mengenkripsi status

Meskipun bucket Google Cloud dienkripsi saat nonaktif, Anda dapat menggunakan kunci enkripsi yang disediakan pelanggan untuk memberikan lapisan perlindungan tambahan. Lakukan hal ini dengan menggunakan variabel lingkungan GOOGLE_ENCRYPTION_KEY. Meskipun tidak boleh ada secret dalam file status, selalu enkripsi status sebagai langkah pertahanan tambahan.

Jangan menyimpan secret dalam status

Ada banyak resource dan penyedia data di Terraform yang menyimpan nilai secret dalam teks biasa pada file status. Jika memungkinkan, hindari menyimpan secret dalam status. Berikut adalah beberapa contoh penyedia yang menyimpan secret dalam teks biasa:

Menandai output yang sensitif

Dibandingkan mencoba mengenkripsi nilai sensitif secara manual, Anda dapat mengandalkan dukungan bawaan Terraform untuk pengelolaan status sensitif. Saat mengekspor nilai sensitif ke output, pastikan nilai tersebut ditandai sebagai sensitif.

Memastikan pemisahan tugas

Jika Anda tidak dapat menjalankan Terraform dari sistem otomatis yang tidak dapat diakses pengguna, terapkan pemisahan tugas dengan memisahkan izin dan direktori. Misalnya, project jaringan akan sesuai dengan akun layanan Terraform jaringan atau pengguna yang aksesnya dibatasi ke project ini.

Menjalankan pemeriksaan sebelum penerapan

Saat menjalankan Terraform di pipeline otomatis, gunakan alat seperti gcloud terraform vet untuk memeriksa output rencana terhadap kebijakan sebelum diterapkan. Dengan melakukannya, Anda dapat mendeteksi regresi keamanan sebelum terjadi.

Menjalankan audit berkelanjutan

Setelah perintah terraform apply dijalankan, jalankan pemeriksaan keamanan otomatis. Pemeriksaan ini dapat membantu memastikan bahwa infrastruktur tidak berubah ke status yang tidak aman. Alat berikut adalah pilihan yang valid untuk jenis pemeriksaan ini:

Pengujian

Pengujian modul dan konfigurasi Terraform terkadang mengikuti pola dan konvensi yang berbeda dari pengujian kode aplikasi. Meskipun menguji kode aplikasi paling banyak melibatkan pengujian logika bisnis aplikasi itu sendiri, pengujian kode infrastruktur secara menyeluruh memerlukan deployment resource cloud nyata untuk meminimalkan risiko kegagalan produksi. Ada beberapa pertimbangan saat menjalankan pengujian Terraform:

  • Menjalankan pengujian Terraform akan menciptakan, mengubah, dan menghancurkan infrastruktur nyata, sehingga pengujian Anda berpotensi memerlukan waktu yang lama dan biaya yang mahal.
  • Anda tidak bisa hanya menguji unit arsitektur menyeluruh. Pendekatan terbaik adalah dengan membagi arsitektur Anda menjadi beberapa modul dan mengujinya satu per satu. Manfaat pendekatan ini mencakup pengembangan iteratif yang lebih cepat karena runtime pengujian yang lebih cepat, pengurangan biaya untuk setiap pengujian, dan penurunan peluang kegagalan uji dari faktor-faktor di luar kendali Anda.
  • Hindari menggunakan kembali status jika memungkinkan. Mungkin ada situasi saat Anda melakukan pengujian dengan konfigurasi yang memiliki data yang sama seperti konfigurasi lain, tetapi idealnya setiap pengujian harus dilakukan terpisah dan tidak boleh menggunakan kembali status di seluruh pengujian.

Menggunakan metode pengujian yang lebih murah terlebih dahulu

Ada beberapa metode yang dapat Anda gunakan untuk menguji Terraform. Dalam urutan biaya, waktu proses, dan kedalaman dari atas ke bawah, metrik tersebut meliputi:

  • Analisis statis: Menguji sintaksis dan struktur konfigurasi tanpa men-deploy resource apa pun menggunakan alat seperti compiler, linter, dan uji coba. Untuk melakukannya, gunakan terraform validate.
  • Pengujian integrasi modul: Untuk memastikan bahwa modul berfungsi dengan benar, uji setiap modul secara terpisah. Pengujian integrasi untuk modul melibatkan deployment modul ke dalam lingkungan pengujian dan memverifikasi bahwa resource yang diharapkan telah dibuat. Ada beberapa framework pengujian yang memudahkan penulisan pengujian sebagai berikut:
  • Pengujian menyeluruh: Dengan memperluas pendekatan pengujian integrasi ke seluruh lingkungan, Anda dapat mengonfirmasi bahwa beberapa modul dapat berfungsi bersama. Dalam pendekatan ini, deploy semua modul yang membentuk arsitektur di lingkungan pengujian baru. Idealnya, lingkungan pengujian harus semirip mungkin dengan lingkungan produksi Anda. Meskipun biayanya mahal, tetapi memberikan keyakinan besar bahwa perubahan tidak merusak lingkungan produksi Anda.

Memulai dari yang kecil

Pastikan pengujian Anda saling melengkapi satu sama lain secara iteratif. Sebaiknya jalankan pengujian yang lebih kecil terlebih dahulu, lalu lanjutkan ke pengujian yang lebih kompleks, menggunakan pendekatan fail fast.

Mengacak project ID dan nama resource

Untuk menghindari konflik penamaan, pastikan konfigurasi Anda memiliki project ID yang unik secara global dan nama resource yang tidak tumpang-tindih dalam setiap project. Untuk melakukannya, gunakan namespace untuk resource Anda. Terraform memiliki penyedia acak bawaan untuk ini.

Menggunakan lingkungan terpisah untuk pengujian

Selama pengujian, banyak resource yang dibuat dan dihapus. Pastikan lingkungan diisolasi dari project pengembangan atau produksi untuk menghindari penghapusan yang tidak disengaja selama pembersihan resource. Pendekatan terbaik adalah meminta setiap pengujian membuat project atau folder baru. Untuk menghindari kesalahan konfigurasi, pertimbangkan untuk membuat akun layanan khusus untuk setiap eksekusi uji.

Membersihkan semua resource

Menguji kode infrastruktur berarti Anda men-deploy resource yang sebenarnya. Agar tidak dikenai biaya, pertimbangkan untuk menerapkan langkah pembersihan.

Untuk menghancurkan semua objek jarak jauh yang dikelola oleh konfigurasi tertentu, gunakan perintah terraform destroy. Beberapa framework pengujian memiliki langkah pembersihan bawaan untuk Anda. Misalnya, jika Anda menggunakan Terratest, tambahkan defer terraform.Destroy(t, terraformOptions) ke pengujian. Jika menggunakan Kitchen-Terraform, hapus ruang kerja Anda menggunakan terraform kitchen delete WORKSPACE_NAME.

Setelah menjalankan perintah terraform destroy, jalankan juga prosedur pembersihan tambahan untuk menghapus resource yang gagal dihancurkan Terraform. Lakukan hal ini dengan menghapus project yang digunakan untuk eksekusi uji atau menggunakan alat seperti modul project_cleanup.

Mengoptimalkan waktu proses pengujian

Untuk mengoptimalkan waktu eksekusi uji, gunakan pendekatan berikut:

  • Jalankan pengujian secara paralel. Beberapa framework pengujian mendukung pengoperasian beberapa pengujian Terraform secara bersamaan.
    • Misalnya, dengan Terratest, Anda dapat melakukannya dengan menambahkan t.Parallel() setelah definisi fungsi pengujian.
  • Uji secara bertahap. Pisahkan pengujian Anda ke dalam konfigurasi independen yang dapat diuji secara terpisah. Dengan pendekatan ini, Anda tidak perlu melalui semua tahap saat menjalankan pengujian dan siklus pengembangan iteratif juga menjadi lebih cepat.
    • Misalnya, di Kitchen-Terraform, bagi pengujian dalam suite terpisah. Saat melakukan iterasi, jalankan setiap suite secara terpisah.
    • Demikian pula, dengan menggunakan Terratest, gabungkan setiap tahap pengujian Anda dengan stage(t, STAGE_NAME, CORRESPONDING_TESTFUNCTION). Menetapkan variabel lingkungan yang menunjukkan pengujian yang akan dijalankan. Misalnya, SKIPSTAGE_NAME="true".
    • Framework pengujian blueprint mendukung eksekusi yang dirilis bertahap untuk pengujian.

Langkah selanjutnya