問題点
Terraformは通常、型に対して柔軟ですが、文字列の補間(interpolation)に関しては厳格な制限があります。単純な文字列が必要な場所に複雑なデータ構造を入れようとすると、planやapplyの実行中にこのエラーが発生します。
Error: Invalid template interpolation value
on main.tf line 42, in resource "aws_instance" "example":
42: user_data = "echo ${var.config_map}"
|----------------
| var.config_map is object with 2 attributes
Cannot include the given value in a string template: string required.
${...}構文の役割はただ一つ、文字列を挿入することです。リスト、マップ、またはオブジェクトを渡しても、Terraformはそれをどのようにフォーマットすべきか推測しません。Terraformはプレーンな文字列を期待しており、多層のオブジェクトを検出すると処理を停止します。
よくあるシナリオ
これは単なる構文の癖ではなく、型の不一致(type mismatch)です。以下のような場合に頻繁に遭遇します:
user_dataのシェルスクリプトに環境変数のマップを渡している。- 設定ファイルに5つのプライベートIPアドレスのリストを出力しようとしている。
- タグやメタデータフィールドにリソースオブジェクト全体を注入している。
3つの解決方法
解決策は、変換後にデータをどのように表示させたいかによって異なります。以下に標準的なアプローチを紹介します。
1. マップとオブジェクトには jsonencode() を使用する
送信先(SSMパラメータや設定ファイルなど)が構造化データを期待している場合は、jsonencode()を使用します。これにより、HCLオブジェクトが自動的に有効なJSON文字列に変換されます。
間違った方法:
resource "aws_ssm_parameter" "config" {
name = "app_settings"
type = "String"
value = "settings = ${var.my_map}" # これによりエラーが発生します
}
正しい方法:
resource "aws_ssm_parameter" "config" {
name = "app_settings"
type = "String"
value = jsonencode(var.my_map) # "{\"key\":\"value\"}" を返します
}
2. リストには join() を使用する
リストの扱いは少し特殊です。文字列のリストがある場合、通常はカンマやスペースなどの区切り文字で結合したいはずです。
例:
resource "aws_instance" "web" {
# 3つのCIDRブロックのリストを、カンマ区切りの1つの文字列に結合する
user_data = <<-EOT
#!/bin/bash
echo "許可されたソース: ${join(", ", var.external_ips)}"
EOT
}
3. 複雑なフォーマットにはHCLディレクティブを使用する
jsonencodeでは出力が煩雑になりすぎる場合があります。templatefile()を使用してカスタム設定(NginxやHaProxyの設定など)を生成する場合は、テンプレート内でforループディレクティブを使用します。
テンプレート (config.tftpl):
%{ for name, ip in servers ~}
server ${name} ${ip}:8080
%{ endfor ~}
Terraformコード:
content = templatefile("config.tftpl", {
servers = { "web01" = "10.0.1.5", "web02" = "10.0.1.6" }
})
デプロイせずにテストする
修正が機能するか確認するために、時間のかかるterraform planを待つ必要はありません。組み込みのコンソールを使用して、すぐにフィードバックを得ることができます:
- ターミナルで
terraform consoleを実行します。 - ロジックを入力します:
join(" | ", ["prod", "staging"]) "prod | staging"と出力されれば、補間は安全です。
専門家のアドバイス
- 厳格な型指定: 変数を明確に定義しましょう。
type = map(string)を使用すると、デプロイの途中で失敗するのではなく、入力段階でエラーをキャッチできます。 - YAMLのサポート: ログに対してJSONが大きすぎる場合は、
yamlencode()がJSON版と同じように機能しますが、よりクリーンなYAMLを出力します。 - セキュリティ: 機密情報を含むオブジェクトに対して
jsonencodeを使用する場合は注意してください。変数にsensitive = trueが設定されている場合、Terraformはパスワードを保護するために、CLI出力内の文字列全体を伏せ字(redact)にします。

