Ringkasan Utama
OpenTofu 1.11, dirilis pada 9 Desember 2025, menjaga secret tetap di luar state dan plan file lewat ephemeral variable, output, dan resource, ditambah write-only attribute yang selalu tersimpan sebagai null. Plan tidak bisa melihat perubahan nilai write-only, jadi pasangkan dengan version attribute. Meta-argument enabled baru menggantikan count untuk nol atau satu instance.
Pertanyaan yang membawa saya ke dokumentasi OpenTofu 1.11 sederhana: kenapa password database tetap muncul di state file padahal konfigurasi tidak pernah mencetaknya? Menandainya sensitive hanya menyembunyikannya dari terminal. Nilainya tetap ada di file yang tersimpan di bucket Anda.
Tulisan ini membaca sumber primer dengan teliti: halaman what's new 1.11, pengumuman rilis 9 Desember 2025, serta halaman referensi bahasa untuk ephemerality, ephemeral resource, write-only attribute, variable, output, dan meta-argument enabled. Setiap aturan di bawah berasal dari halaman itu. Bila dokumentasi hanya memberi kerangka tanpa contoh lengkap, HCL di sini adalah ilustrasi saya sendiri dan saya sebutkan.
Sensitive menyembunyikan secret, tidak menghapusnya
Referensi variable menjelaskannya dengan lugas. Nilai variable sensitive disembunyikan di output plan dan apply, tetapi tetap dicatat di state dalam cleartext, sehingga siapa pun yang punya akses ke state bisa membacanya. Halaman ephemerality menyarankan memakai nilai ephemeral sebisa mungkin, dan bila data harus disimpan, tandai sensitive dan aktifkan enkripsi state dan plan.
Nilai ephemeral mengubah jaminan penyimpanannya. Pengumuman rilis menyebut nilai itu hanya ada di memori selama satu fase OpenTofu, sehingga tidak pernah masuk ke state snapshot maupun plan file. Write-only attribute berada di ujung lain alurnya: membawa nilai ephemeral ke managed resource tanpa meninggalkan salinan. Tabel berikut membandingkan ketiga mekanisme.
| Mekanisme | Di state | Di plan file | Output CLI |
|---|---|---|---|
| sensitive = true | Tersimpan dalam cleartext | Tersimpan (enkripsi bila harus disimpan) | Tampil sebagai sensitive value |
| ephemeral variable atau resource | Tidak pernah tersimpan | Tidak pernah tersimpan; hanya nama variable yang dicatat | Tidak berlaku: hanya bisa dipakai di konteks yang terdaftar |
| write-only attribute | Selalu null | Selalu null | Tampil sebagai write-only attribute |
Baca tabel ini seperti anak tangga. Sensitive adalah pengaturan tampilan. Ephemeral dan write-only adalah aturan penyimpanan yang ditegakkan OpenTofu, bukan sekadar kesopanan. Dokumentasi tetap menyarankan enkripsi state dan plan untuk apa pun yang tidak bisa dibuat ephemeral, karena kedua fitur ini mencakup nilai yang berbeda.
Ephemeral variable dan output dalam praktik
Input variable menjadi ephemeral dengan satu argumen. OpenTofu tidak menyimpan nilainya di state, dan plan hanya menyimpan nama variable. Apa pun yang dihitung darinya juga ephemeral: dokumentasi menunjukkan trimspace pada password ephemeral menghasilkan local ephemeral.
variable "db_admin_password" {
type = string
ephemeral = true # never written to state; the plan keeps only the variable name
}
locals {
# Anything derived from an ephemeral value is itself ephemeral.
sanitized = trimspace(var.db_admin_password)
}
# modules/secrets/outputs.tf -- a CHILD module. A root module output block
# cannot carry ephemeral = true.
output "db_password" {
value = local.sanitized
ephemeral = true
}Output punya aturan yang lebih ketat. Output yang merujuk nilai ephemeral harus ditandai ephemeral juga, dan referensi output menyatakan argumen itu hanya untuk child module: output block di root module tidak bisa memakainya. Detail ini penting karena ringkasan awal fitur ini menyebut output bisa ephemeral tanpa catatan tersebut. Dalam pohon module, secret mengalir ke atas lewat output child module, tidak pernah keluar dari root.
Untuk variable di root module, halaman variable hanya menyebut perlunya penanganan hati-hati pada opsi var dan var-file, lalu merujuk ke dokumentasi apply. Aturan saya sendiri mengikutinya: jangan taruh nilainya di file tfvars di repository, dan jangan ketik di command line yang masuk ke shell history. Baca ke environment variable, seperti pada blok kedua.
# Wrong: the secret is now in your shell history (and often in a tfvars file in git).
tofu apply -var="db_admin_password=hunter2"
# Right: read it silently into the environment, then apply.
# Environment variables are literal strings by default, so no escaping is needed.
read -rs TF_VAR_db_admin_password && export TF_VAR_db_admin_password
tofu applyEphemeral resource: baca secret, jangan simpan
Ephemeral block adalah saudara read-only dari data block, dengan satu perbedaan penting: hasilnya adalah nilai sementara yang tidak disimpan OpenTofu di state atau plan. Halaman bahasa menyebut tiga langkah siklusnya. OpenTofu memvalidasi konfigurasi dengan provider, membuka resource untuk mengambil nilai, lalu menutupnya begitu fase saat ini tidak lagi membutuhkannya. Ini hanya bekerja dengan provider yang menyediakan kemampuan tersebut, dan bukan pengganti langsung data atau resource block.
Referensinya mendukung depends_on, count, for_each, provider, dan lifecycle, dan lifecycle hanya menerima blok precondition dan postcondition. Halaman OpenTofu yang saya baca hanya memberi kerangka, jadi contoh AWS di bawah adalah ilustrasi saya. Saya mengonfirmasi sintaks ephemeral block dari dokumentasi, bukan nama type atau attribute AWS-nya; periksa di dokumentasi provider sebelum menyalin.
terraform {
required_version = ">= 1.11.0" # ephemeral blocks need OpenTofu 1.11 or later
}
# Skeleton confirmed from the OpenTofu docs. The AWS type and the secret_string
# attribute below are ILLUSTRATIVE: verify them in your provider's documentation.
ephemeral "aws_secretsmanager_secret_version" "db" {
secret_id = "prod/db/admin"
lifecycle {
postcondition {
condition = self.secret_string != ""
error_message = "The secret came back empty."
}
}
}
# Allowed: a provider configuration. Not allowed: count, for_each, lifecycle enabled.
provider "postgresql" {
host = "127.0.0.1"
username = "admin"
password = ephemeral.aws_secretsmanager_secret_version.db.secret_string
}Jika konfigurasi belum sepenuhnya diketahui saat planning, proses open ditunda ke apply, dan baru terjadi setelah semua dependency terpenuhi. Pengumuman 1.11 menyebut dua pemakaian nyata: kredensial AWS berbatas waktu dari OpenBao, dan SSH tunnel sementara untuk provider postgresql milik cyrilgdn.
Write-only attribute dan pasangan version-nya
Write-only attribute hanya ada di bagian konfigurasi resource. Ia ditulis ke state dan plan sebagai null, provider selalu mengembalikannya sebagai null, dan CLI menampilkannya sebagai write-only attribute. Berbeda dengan attribute biasa, ia boleh merujuk nilai reguler maupun ephemeral, dan itulah intinya: secret ephemeral akhirnya bisa mengalir ke managed resource.
resource "aws_secretsmanager_secret" "db" {
name = "app/db-password"
}
resource "aws_secretsmanager_secret_version" "db" {
secret_id = aws_secretsmanager_secret.db.id
# Write-only: state and plan hold null, the CLI prints (write-only attribute).
secret_string_wo = var.db_admin_password
# Regular companion: OpenTofu cannot see a change to the line above, so it
# watches this one. Bump it (1 -> 2) to make the next apply write the new value.
secret_string_wo_version = 1
}Konsekuensinya langsung terlihat. OpenTofu tidak bisa mendeteksi perubahan nilai write-only, karena state berisi null dan provider mengembalikan null. Karena itu provider diminta memasangkan setiap write-only attribute dengan attribute biasa. Mengubah pasangannya memberi tahu provider untuk menerapkan nilai baru. Dokumentasi memakai resource secret version AWS, dengan secret_string_wo dan secret_string_wo_version.
Akibat praktisnya, rotasi harus dilakukan dengan sengaja. Ubah secret di sistem eksternal, lalu naikkan nomor version di konfigurasi agar apply berikutnya menuliskannya. Jika lupa menaikkan, plan tidak menunjukkan perubahan dan resource diam-diam tetap memakai nilai lama.
Ke mana nilai ephemeral boleh mengalir
Nilai ephemeral ditandai sejak sumbernya dan dilacak OpenTofu. Memakainya di luar konteks yang diizinkan menghasilkan error, dan itulah mekanisme yang mencegah secret bocor ke state tanpa sengaja. Tabel ini merangkum aturan dari halaman ephemeral resource, ephemerality, dan enabled.
| Tempat nilai dipakai | Boleh | Kata dokumentasi |
|---|---|---|
| Ephemeral resource, variable, output, local | Ya | Konteks yang terdaftar; ekspresi turunannya tetap ephemeral |
| Konfigurasi provider | Ya | Inti dari kredensial berumur pendek |
| Provisioner dan connection block | Ya | Tetapi output provisioner tidak disembunyikan; tandai sensitive juga |
| Write-only attribute | Ya | Satu-satunya argumen managed resource yang menerimanya |
| Attribute resource biasa | Tidak | Memakainya di tempat lain menghasilkan error |
| count dan for_each | Tidak | Disebut eksplisit sebagai batasan |
| lifecycle enabled, termasuk tofu.applying | Tidak | Halaman enabled juga melarang nilai ephemeral |
Dua helper melonggarkan aturan ini. Fungsi ephemeralasnull menyaring nilai ephemeral untuk tempat yang tidak menerimanya, dan tofu.applying memungkinkan konfigurasi membedakan fase plan dan apply. Kondisi validation juga boleh merujuk nilai ephemeral.
Meta-argument enabled menggantikan count untuk satu instance
Fitur 1.11 lainnya dalam tulisan ini adalah enabled, alternatif yang lebih jelas dari count untuk resource atau module dengan nol atau satu instance. Letaknya di dalam lifecycle block, yang menurut halaman what's new menghindari konflik dengan variable atau argumen yang sudah bernama enabled. Jika true atau dihilangkan, resource berjalan normal; jika false, resource tidak dibuat dan bernilai null.
| Aspek | count | lifecycle enabled |
|---|---|---|
| Ekspresi umum | count = var.create ? 1 : 0 | lifecycle dengan enabled = var.create |
| Cara merujuk resource | aws_instance.example[0] | aws_instance.example |
| Saat nonaktif | List kosong | Bernilai null |
| Batasan nilai | Angka; tanpa nilai ephemeral | Hanya boolean: tidak unknown, sensitive, null, atau ephemeral |
| Kombinasi | Tidak bisa bersama enabled | Tidak bisa bersama count atau for_each |
# Before: count with an index on every reference.
resource "aws_instance" "example" {
count = var.create_instance ? 1 : 0
ami = "ami-12345678"
instance_type = "t3.micro"
}
output "instance_id" {
value = var.create_instance ? aws_instance.example[0].id : null
}
# After: enabled inside lifecycle. No moved block needed; state is moved for you.
resource "aws_instance" "example" {
ami = "ami-12345678"
instance_type = "t3.micro"
lifecycle {
enabled = var.create_instance # boolean, known at plan time
}
}
output "instance_id" {
# A disabled resource is null, so compare before reading an attribute.
value = aws_instance.example != null ? aws_instance.example.id : null
}Migrasinya murah. Menurut halaman enabled, beralih dari count ke enabled membuat Anda bisa membuang index, dan OpenTofu memindahkan state yang ada secara otomatis, sehingga moved block tidak diperlukan. Biayanya ada di sisi pembacaan: resource yang nonaktif bernilai null, dan mengakses attribute dari null gagal saat plan atau apply. Halaman itu menyarankan try atau pengecekan null, dan memperingatkan bahwa try bisa menutupi error lain, jadi saya memilih perbandingan null yang eksplisit.
Halaman what's new juga mencantumkan item 1.11 yang lebih kecil dan sejalan: S3 backend kini bisa memberi tag pada objek state lewat state_tags, dan pada objek lock lewat lock_tags saat native S3 locking dengan use_lockfile aktif. Halaman itu menyebut pelacakan organisasi dan biaya sebagai kegunaannya. Disebutkan OpenTofu mungkin juga memerlukan izin s3:PutObjectTagging, dan tidak ada catatan versi untuk kedua argumen itu.
terraform {
backend "s3" {
bucket = "acme-tofu-state"
key = "prod/network.tfstate"
region = "ap-southeast-3"
use_lockfile = true
# New in the 1.11 line per the what's new page. You may also need
# s3:PutObjectTagging on the state object.
state_tags = {
"object:type" = "state"
"team" = "platform"
}
lock_tags = {
"object:type" = "lock"
}
}
}Di mana ini bisa salah
Tidak ada kegagalan di sini yang aneh. Masing-masing adalah pembacaan langsung dari aturan yang dinyatakan dokumentasi, dan masing-masing gagal secara diam-diam, bukan dengan keras.
Perubahan write-only yang lupa diberi version tidak menghasilkan diff. State berisi null dan provider mengembalikan null, jadi OpenTofu tidak punya bahan pembanding. Anggap version attribute bagian dari runbook rotasi secret, bukan tambahan opsional.
- Sensitive bukan pengganti. Jika nilai tidak bisa dibuat ephemeral, ia tetap masuk ke state dalam cleartext, jadi saran dokumentasi untuk mengaktifkan enkripsi state dan plan tetap berlaku.
- Output provisioner tidak disembunyikan untuk nilai ephemeral. Secret yang dikirim ke provisioner masih bisa tercetak kecuali Anda juga menandainya sensitive.
- Nilai ephemeral tidak bisa menggerakkan count, for_each, atau lifecycle enabled. Jika jumlah instance bergantung pada secret, susun ulang agar bergantung pada variable biasa.
- Resource yang dinonaktifkan dengan enabled bernilai null. Output atau referensi yang membaca attribute secara langsung akan error saat plan untuk kasus false, jadi uji kedua nilai flag.
- Kompatibilitas adalah tujuan, bukan janji. Halaman ephemerality menyebut desainnya terinspirasi konsep Terraform 1.12 dan mengincar kompatibilitas, tetapi breaking change bisa diterima kelak bila muncul ketidakcocokan. Pin versi yang Anda uji.
Satu batasan lagi: ini hanya tersedia untuk provider yang mengimplementasikannya. Ephemeral resource type dan argumen write-only adalah fitur provider, jadi rilis provider lama mungkin tidak menawarkan keduanya untuk resource yang Anda butuhkan. Saya tidak menyurvei provider mana yang sudah menyediakannya, dan tidak akan menebak.
Yang dikerjakan hari Senin
Mulailah dengan mengukur masalahnya, bukan fiturnya. Perintah pertama di blok berikut mencari nama attribute yang mirip secret di state Anda; ini grep kasar, bukan audit, tetapi satu temuan sudah menunjukkan resource mana yang dikonversi lebih dulu.
- Jalankan grep pada tofu state pull dan pada plan yang disimpan, lalu daftar setiap resource yang menyimpan password, token, atau private key.
- Naikkan required_version ke minimal 1.11.0, dan pastikan versi provider yang dipakai mendokumentasikan ephemeral type atau argumen write-only yang Anda butuhkan.
- Konversi satu secret dari ujung ke ujung: ephemeral variable atau resource di sisi masuk, write-only attribute di sisi keluar, lengkap dengan version attribute pasangannya.
- Ganti satu pola count 0 atau 1 dengan enabled, jalankan plan untuk kedua nilai flag, dan pastikan tidak perlu moved block.
- Tambahkan state_tags ke S3 backend, dan biarkan enkripsi state dan plan tetap aktif untuk semua yang masih sensitive, bukan ephemeral.
# 1. Crude but fast: which attributes in state look like secrets?
tofu state pull | grep -i -E '"[a-z_]*(password|secret|token|private_key)[a-z_]*"'
# The same check on a saved plan, which can also hold values.
tofu plan -out=tfplan
tofu show -json tfplan | grep -i -E '"[a-z_]*(password|secret|token|private_key)[a-z_]*"'
# 2. Confirm the binary before you rely on any of this.
tofu version # ephemeral blocks, write-only attributes, enabled: 1.11 or newer
# 3. After converting a resource, plan with the flag both ways.
tofu plan -var="create_instance=true"
tofu plan -var="create_instance=false"Halaman what's new mencatat bahwa jalur 1.11.x tidak lagi dipelihara aktif dan menunjuk 1.13.x sebagai dokumentasi terbaru, jadi rencanakan upgrade ke rilis yang dipelihara, jangan berhenti di 1.11.0.
Aturan untuk dibawa: putuskan untuk setiap secret apakah ia perlu ada di state sama sekali. Jika tidak, jadikan ephemeral di sisi masuk, write-only di sisi keluar, dan naikkan version saat rotasi. Pakai sensitive dan enkripsi hanya untuk yang memang harus tersimpan.
Sumber (dicek langsung pada 17 Oktober 2026)
- Dokumentasi OpenTofu: what's new di 1.11
- Dokumentasi OpenTofu: ikhtisar ephemerality
- Dokumentasi OpenTofu: write-only attribute
- Dokumentasi OpenTofu: ephemeral resource
- Dokumentasi OpenTofu: meta-argument enabled
- Blog OpenTofu: rilis OpenTofu 1.11.0
- Dokumentasi OpenTofu: input variable
- Dokumentasi OpenTofu: output value
- Dokumentasi OpenTofu: S3 backend

