はじめに
本番環境で Terraform を運用する際、パスワードや API キーといった機密情報(シークレット)の扱いには細心の注意が必要です。
Terraform の変数定義(variable)には、値を保護するためのオプションとしてsensitiveとephemeral(日本語で「一時的な・儚い」という意味)の2種類が存在します。しかし、一見すると「どちらも隠すための機能」に見えてしまい、使い分けや内部的な挙動の違いがわかりにくいと感じる方も多いのではないでしょうか。
そこで本記事では、この2つのオプションの組み合わせ(全4パターン)を作成し、「ファイル出力」「画面ログ(CLI)」「Outputsセクション」「Stateファイル」の4箇所でどのような差が出るのかを実際に動かして徹底検証してみました。
TL;DR
この記事を読めば、以下のことができるようになります。
-
sensitiveとephemeralの役割の違いが理解できる - 変数の出力先(ファイル / CLI / Outputsセクション / Stateファイル)ごとに、どのようなセキュリティ保護がかかるかが把握できる
- シークレットの性質に合わせて、どちらのオプションを指定すべきか正しく判断できるようになる
前提条件
- なし
実行環境
- Terraform: v1.15.5
詳細
1. 変数パターン
検証の目的と観点
今回は、Terraform の変数オプションである sensitive と ephemeral の有効・無効を組み合わせた 4つの変数パターン(A〜D) を作成しました。
それらの変数を使い、以下の 4つの出力先(観点) でどのような挙動の違い(平文表示されるか、マスクされるか、エラーになるか)が出るかを検証します。
-
ファイル出力 (
local_file等)- ローカルに作成したテキストファイル内に変数の値が書き込まれるか
-
CLIへの出力 (
local-exec)-
terraform apply実行時の画面ログ(標準出力)で値が表示されるか
-
-
Outputsセクションへの出力 (
outputブロック)-
terraform apply実行完了後に表示されるOutputsセクションに出力できるか
-
-
stateファイルへの出力 (
terraform.tfstate)- インフラ状態を記録する State ファイル内に値が残るか
検証結果サマリー
検証結果の全体像は以下の通りです。
-
1. ファイル出力(
local_file等)- パターンA・Bは出力可能(※パターンBはファイル内には平文で書き込まれる点に注意)
- パターンC・D(
ephemeral)はエラーとなる
-
2. CLIへの出力(実行中の画面ログ)
- パターンAのみ平文表示
- パターンB(
sensitive)だけでなく、パターンC・D(ephemeral)も画面露出を防ぐため自動的に秘匿される
-
3. Outputsセクションへの出力 (
outputブロック)- パターンA・Bは定義可能(※パターンBは
sensitive = trueの明記が必須) - パターンC・D(
ephemeral)はエラーとなる
- パターンA・Bは定義可能(※パターンBは
-
4. stateファイルへの出力 (
terraform.tfstate)- パターンA・Bは平文で残る(※
sensitiveを指定しても State には平文で保存される) - パターンC・D(
ephemeral)は一切記録されない
- パターンA・Bは平文で残る(※
| パターン | 1. ファイル出力 ( local_file 等) |
2. CLIへの出力 (実行中の画面ログ) |
3. Outputsセクションへの出力 ( output ブロック) |
4. stateファイルへの出力 ( terraform.tfstate) |
|---|---|---|---|---|
|
A: 通常変数 ( val_normal) |
出力可能 | 平文表示 | 平文表示 | 平文表示 |
|
B: sensitive のみ ( val_sensitive_only) |
出力可能 | マスクされる | マスク表示(※1) | 平文表示 |
|
C: ephemeral のみ ( val_ephemeral_only) |
エラー | マスクされる | エラー(定義不可) | 一切記録されない |
|
D: 両方 true ( val_both_true) |
エラー | マスクされる | エラー(定義不可) | 一切記録されない |
※1:
outputブロック側にもsensitive = trueの明記が必須です。
-
変数の定義コード
- 検証に使用した A〜D の4パターンの変数定義です。内容は、後述の
main.tf内の変数定義と同じです。
- 検証に使用した A〜D の4パターンの変数定義です。内容は、後述の
variable "val_normal" {
type = string
default = "SECRET_PATTERN_A_NORMAL"
sensitive = false
ephemeral = false
}
variable "val_sensitive_only" {
type = string
default = "SECRET_PATTERN_B_SENSITIVE"
sensitive = true
ephemeral = false
}
variable "val_ephemeral_only" {
type = string
default = "SECRET_PATTERN_C_EPHEMERAL"
sensitive = false
ephemeral = true
}
variable "val_both_true" {
type = string
default = "SECRET_PATTERN_D_BOTH"
sensitive = true
ephemeral = true
}
2. テスト用ファイル
検証に使用したmain.tfの全体コードです。
terraform {
required_version = ">= 1.10.0"
}
# ==========================================
# 変数の定義(4つの組み合わせパターン)
# ==========================================
variable "val_normal" {
type = string
default = "SECRET_PATTERN_A_NORMAL"
sensitive = false
ephemeral = false
}
variable "val_sensitive_only" {
type = string
default = "SECRET_PATTERN_B_SENSITIVE"
sensitive = true
ephemeral = false
}
variable "val_ephemeral_only" {
type = string
default = "SECRET_PATTERN_C_EPHEMERAL"
sensitive = false
ephemeral = true
}
variable "val_both_true" {
type = string
default = "SECRET_PATTERN_D_BOTH"
sensitive = true
ephemeral = true
}
# ==========================================
# 1. ファイル出力
# ==========================================
# A・B:一般的なファイル生成(ファイルの中身および Stateファイル内に平文で残る)
resource "local_file" "test_regular" {
filename = "${path.module}/regular_output.txt"
content = <<EOT
A: ${var.val_normal}
B: ${var.val_sensitive_only}
EOT
}
# C・D:ephemeralはファイル作成リソースに渡せない
# ==========================================
# 2. CLIへの出力
# ------------------------------------------
# depends_on を使うことで、A ➔ B ➔ C ➔ D の順番で直列実行させてログを見やすくしています。
# ==========================================
# A:通常変数
resource "terraform_data" "test_A" {
triggers_replace = [timestamp()]
provisioner "local-exec" {
command = "echo 'A (normal) : ${var.val_normal}'"
}
}
# B:Sensitive変数(Aの後に実行)
resource "terraform_data" "test_B" {
depends_on = [terraform_data.test_A]
triggers_replace = [timestamp()]
provisioner "local-exec" {
command = "echo 'B (sensitive): ${var.val_sensitive_only}'"
}
}
# C:Ephemeral変数(Bの後に実行)
resource "terraform_data" "test_C" {
depends_on = [terraform_data.test_B]
triggers_replace = [timestamp()]
provisioner "local-exec" {
command = "echo 'C (ephemeral): ${var.val_ephemeral_only}'"
}
}
# D:両方 true(Cの後に実行)
resource "terraform_data" "test_D" {
depends_on = [terraform_data.test_C]
triggers_replace = [timestamp()]
provisioner "local-exec" {
command = "echo 'D (both) : ${var.val_both_true}'"
}
}
# ==========================================
# 3. Outputsセクションへの出力
# ==========================================
# A: 記述可能(CLIに平文表示、Stateに平文保存)
output "out_A_normal" {
value = var.val_normal
}
# B: 記述可能(CLIでは <sensitive> マスク、Stateには平文保存)
# ※ sensitiveな変数を直接出力するため sensitive = true が必須
output "out_B_sensitive" {
value = var.val_sensitive_only
sensitive = true
}
# C・D:ephemeralはエラーになるため定義不可
3. 実行結果
main.tfがあるフォルダで以下のコマンドを実行し、結果を確認します。
$ terraform init
$ terraform apply
3-1. ファイル出力
regular_output.txtは以下のようになりました。
パターンB(sensitive = true)の変数を渡していますが、生成されたファイルを開くと中身は平文で書き込まれています。
sensitive = trueは「暗号化機能」ではなく、あくまで「画面表示を隠す機能」であるため、ファイルなどの外部リソースに渡した場合はそのまま出力されます。
A: SECRET_PATTERN_A_NORMAL
B: SECRET_PATTERN_B_SENSITIVE
3-2. CLIへの出力
以下、terraform apply実行時のターミナルログ(一部抽出)です。パターンA以外はすべて (output suppressed due to...) と秘匿されました。
sensitiveはもちろんですが、ephemeralに設定した変数も、CLIログへの事故流出を防ぐため自動で秘匿されます。
terraform_data.test_A (local-exec): A (normal) : SECRET_PATTERN_A_NORMAL
terraform_data.test_B (local-exec): (output suppressed due to sensitive value in config)
terraform_data.test_C (local-exec): (output suppressed due to ephemeral value in config)
terraform_data.test_D (local-exec): (output suppressed due to sensitive, ephemeral value in config)
3-3. Outputsセクションへの出力
以下、terraform apply完了時に画面へ表示されるOutputsセクションの結果です。
パターンBの変数を出力する際は、outputブロック内にもsensitive = trueを明記しないとエラーになります。
画面上では<sensitive>と秘匿されますが、裏のStateファイルには平文で記録されます。
Outputs:
out_A_normal = "SECRET_PATTERN_A_NORMAL"
out_B_sensitive = <sensitive>
3-4. stateファイルへの出力
実行後に生成されたterraform.tfstateファイルの中身(抜粋)です。
画面上では隠されていたパターンB(SECRET_PATTERN_B_SENSITIVE)の値がterraform.tfstate内には平文になっています。
一方で、パターンC・D(ephemeral)の値はStateファイル内のどこにも残っていません。
{
"version": 4,
"terraform_version": "1.15.5",
"serial": 137,
"lineage": "36c2ab77-f59e-024c-3e90-ab3ce84fba8d",
"outputs": {
"out_A_normal": {
"value": "SECRET_PATTERN_A_NORMAL",
"type": "string"
},
"out_B_sensitive": {
"value": "SECRET_PATTERN_B_SENSITIVE",
"type": "string",
"sensitive": true
}
},
"resources": [
{
"mode": "managed",
"type": "local_file",
"name": "test_regular",
"provider": "provider[\"registry.terraform.io/hashicorp/local\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"content": "A: SECRET_PATTERN_A_NORMAL\nB: SECRET_PATTERN_B_SENSITIVE\n",
"content_base64": null,
さいごに
今回の検証を通して、sensitiveとephemeralの役割の違いがはっきりと見えてきました。
- sensitive: 画面表示するかを制御する機能
- CLI ログなどでの映り込みを防ぐ
- ただし、生成ファイルや
terraform.tfstateには平文で保存される
- ephemeral: Stateファイルに残すかを制御する機能
-
terraform.tfstateファイルへの保管を防ぐ - ただし、後でシークレット情報を参照できなくなるので注意
- 一時的なパスワードやトークンなどの重要なシークレットに最適
-
目的を明確にして、正しく使い分けていきましょう!
参考URL
-
HashiCorp Developer > variable block reference
- Terraformにおける変数の設定方法