Terraformのエフェメラルリソースを使ってみた
お疲れさまです。とーちです。
みなさんはTerraformのエフェメラルリソースをご存知でしょうか?Terraformをよりセキュアに使える素晴らしいアップデートなのですが、使ってみるとなかなか使い所が難しいと感じたので実際のコードも交えて使い方を紹介したいと思います。
エフェメラルリソースとは
そもそもエフェメラルリソースとはという話ですが、Terraform公式ドキュメントによると、以下のような特徴があります
- エフェメラルリソースはTerraform v1.10以降で利用可能
- 通常のリソースと異なり、tfstateファイルに保存されない
- AWS Secrets Manager(以後Secrets Manager)などのサービスに保存されたDBパスワードなどの機密情報を、Terraformコード内で参照するために使用
- Dataソースに近い使い方
- エフェメラルリソースにより取得した値を使える場所は限られている
- 別のエフェメラルリソース
- local values
- ephemeral variables
- ephemeral outputs
provider
ブロック内のプロバイダー設定- provisionerブロックとconnectionブロック
特徴としてはtfstateファイルに保存されないということでしょう。従来はtfstateファイルに値がプレーンテキストで保存されていたので、例えばS3バケットへのアクセス権限を絞るなどして、tfstateの参照をある程度厳密に考える必要がありました。エフェメラルリソースを使うとDBパスワード等の機微な情報を安全にTerraformで扱えるようになります。
またエフェメラルリソースにより取得した値を使える場所が限られている点についても注意が必要です。これについては後ほど詳しく見ていきます。
AWSプロバイダーの対応状況
AWSプロバイダーでの対応状況は以下のページに記載があります
上記によると、2025/1/28時点で以下の値を取得できます
- secretmanagerに保存されたの値:aws_secretsmanager_secret_version
- AWS Lambda(以後Lambda)関数を実行して得た値(機密情報を生成するようなLambda関数を想定しているようです):aws_lambda_invocation
- SSM Parameter Storeに保存された値:aws_ssm_parameter
- AWS Key Management Service(以後KMS)によって暗号化された文字列を復号して得た値(エフェメラルリソースを定義する際に暗号化された文字列を指定):aws_kms_secrets
- Amazon Cognito(以後Cognito)IDプールにより発行されたOpen IDトークン:aws_cognito_identity_openid_token_for_developer_identity
- Amazon Elastic Kubernetes Service(以後EKS)クラスターと通信するための認証トークン:aws_eks_cluster_auth
エフェメラルリソースの使用場所の制限について
エフェメラルリソースの使用制限について、実際に試してみましょう。
あらかじめSecretsManagerを作成しておき、 dummy-value
という値をセットしておきます。そして以下のようなSecretsManagerから値を取得してパラメータストアに取得した値を入れるだけの簡単なTerraformコードを実行します。
data "aws_secretsmanager_secret_version" "example" {
secret_id = var.datastore_sysdig_secret_params_secret_id
}
resource "aws_ssm_parameter" "test" {
name = "testParam"
type = "SecureString"
value = jsondecode(data.aws_secretsmanager_secret_version.example.secret_string)["sysdig_agent_access_key"]
}
なおこの状態では以下のようにtfstateファイルに本来は隠したい値である dummy-value
が記録されてしまっています。
"type": "aws_ssm_parameter",
"name": "test",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
<中略>
"type": "SecureString",
"value": "dummy-value", # 生の値が記録
"version": 1
},
続いてこのコードを以下のようにエフェメラルリソースを使うように修正してみます。
# data "aws_secretsmanager_secret_version" "example" {
ephemeral "aws_secretsmanager_secret_version" "example" {
secret_id = var.datastore_sysdig_secret_params_secret_id
}
resource "aws_ssm_parameter" "test" {
name = "testParam"
type = "SecureString"
value = jsondecode(ephemeral.aws_secretsmanager_secret_version.example.secret_string)["sysdig_agent_access_key"]
}
terraform applyを実行すると以下のエラーメッセージが表示されました
Ephemeral values are not valid in resource arguments, because resource instances must persist between Terraform phases.
(日本語訳:リソース引数では一時的な値は有効ではありません。リソースインスタンスはTerraformのフェーズ間で持続する必要があるためです。)
resourceブロックでエフェメラルリソースを使用しようとしたのでエラーとなったようです。
続いて、エフェメラルリソースを参照可能なlocal valuesに値を入れてみます。
# data "aws_secretsmanager_secret_version" "example" {
ephemeral "aws_secretsmanager_secret_version" "example" {
secret_id = var.datastore_sysdig_secret_params_secret_id
}
locals {
token = jsondecode(ephemeral.aws_secretsmanager_secret_version.example.secret_string)["sysdig_agent_access_key"]
}
# resource "aws_ssm_parameter" "test" {
# name = "testParam"
# type = "SecureString"
# value = jsondecode(ephemeral.aws_secretsmanager_secret_version.example.secret_string)["sysdig_agent_access_key"]
# }
当たり前ですが、これは正常に実行ができました。
続いてlocal valuesを経由して、ParameterStoreを作成しようとしてみます。
# data "aws_secretsmanager_secret_version" "example" {
ephemeral "aws_secretsmanager_secret_version" "example" {
secret_id = var.datastore_sysdig_secret_params_secret_id
}
locals {
token = jsondecode(ephemeral.aws_secretsmanager_secret_version.example.secret_string)["sysdig_agent_access_key"]
}
resource "aws_ssm_parameter" "test" {
name = "testParam"
type = "SecureString"
# value = jsondecode(ephemeral.aws_secretsmanager_secret_version.example.secret_string)["sysdig_agent_access_key"]
value = local.token
}
このコードを実行すると先程と同様に以下のエラーとなりました。
Ephemeral values are not valid in resource arguments, because resource instances must persist between Terraform phases.
このように、エフェメラルリソースで得た値は何かを経由したとしても指定の場所でしか使えないということが分かります。
実践:Sysdigプロバイダーでの活用例
それでは実際のユースケースを想定した使い方をしてみましょう。今回はエフェメラルリソースを使ってコンテナやクラウド環境のセキュリティ監視・可視化を行うためのプラットフォームであるSysdigのAPIトークンを取得し、Sysdigのプロバイダーにトークンを入れることでSysdigリソースを作成してみます。
なお、このリソースはSysdigに監視対象のAWSアカウントを登録するためのもので、正常に実行されるとSysdigの管理画面に登録したAWSアカウントが表示されます。
ephemeral "aws_secretsmanager_secret_version" "sysdig_secret_params_secret_id" {
secret_id = var.datastore_sysdig_secret_params_secret_id
}
provider "sysdig" {
sysdig_secure_url = "https://us2.app.sysdig.com"
sysdig_secure_api_token = jsondecode(ephemeral.aws_secretsmanager_secret_version.sysdig_secret_params_secret_id.secret_string)["sysdig_api_token"]
}
module "onboarding" {
source = "sysdiglabs/secure/aws//modules/onboarding"
version = "~>1.1"
}
module "config-posture" {
source = "sysdiglabs/secure/aws//modules/config-posture"
version = "~>1.1"
sysdig_secure_account_id = module.onboarding.sysdig_secure_account_id
}
resource "sysdig_secure_cloud_auth_account_feature" "config_posture" {
account_id = module.onboarding.sysdig_secure_account_id
type = "FEATURE_SECURE_CONFIG_POSTURE"
enabled = true
components = [module.config-posture.config_posture_component_id]
depends_on = [module.config-posture]
}
上記のコードを実行すると正常にSysdigにAWSアカウントが登録されました。
Dataソースで作成した場合とのtfstateファイルの差分を比較してみます。以下のように機微な情報が含まれるSecretsManagerを参照した箇所がエフェメラルリソースでは丸ごとなくなっているのがわかります。
まとめ
以上、エフェメラルリソースの紹介でした。
エフェメラルリソースは機密情報を安全に扱うための素晴らしい機能ですが、使用できる場所が限定されているため、注意してください。特にプロバイダーの設定やプロビジョナーでの使用など、限定的なユースケースで活用するのが良さそうです。
以上、とーちでした。