概要

だいぶニッチなのはわかっているのだが、terraformでJSONを作って、その中で配列を渡したい事例があってドハマリしたのでメモ。

対象サービス

  • AWS EventBridge Scheduler

EventBridge schedulerでJSONを渡す

EventBridge schedulerでJSONを渡したいという要件だった場合、特に難しく考えなければ下記で十分な場合が多い。

resource "aws_scheduler_schedule" "sample" {
...
  target {
     ...
     input = <<EOT
{
  "example_key" : "example_value"
}
     EOT

はたまた、

resource "aws_scheduler_schedule" "batch_scheduler" {
...
  target {
     ...
     input = jsonencode(var.example)
}

variables "example" {
   default = {
     "example_key" : "example_value" 
   }
}

これでOK.

しかし、今回はJSONに <aws.scheduler.execution-id> を含めたら、jsonencodeでエンコードされて、<>\u003c\u003eという文字列になってしまった。

jsonencode

Terraformのjsonencodeでは、< >はエスケープシーケンスが用意されている。

https://developer.hashicorp.com/terraform/language/functions/jsonencode

When encoding strings, this function escapes some characters using Unicode escape sequences: replacing <, >, &, U+2028, and U+2029 with \u003c, \u003e, \u0026, \u2028, and \u2029. This is to preserve compatibility with Terraform 0.11 behavior.

これを使ったら、たしかにTerraform上は正しく <XXXX>となるのだけど、AWS上ではエンコードされたままになる。
AWSコンソールから手で書き換えても、terraformとは差分にならない。
AWSのAPIを実行したときにだけ発生している模様…。

ヒアドキュメント

jsonencodeでまるっとやるとエンコードされてしまうのでヒアドキュメントで渡すことにした。
ヒアドキュメントで渡す場合、valueはstring形式でないといけない。
配列をvariablesから渡したらErrorとなる。

│ Cannot include the given value in a string template: string required.

解決方法

公式ドキュメントに書いてあるのだけど、jsonencodeに配列で渡す時にforを使えば組み立てられる。

If the string you want to generate will be in JSON or YAML syntax, it’s often tricky and tedious to write a template that will generate valid JSON or YAML that will be interpreted correctly when using lots of individual interpolation sequences and directives.

https://developer.hashicorp.com/terraform/language/functions/templatefile#generating-json-or-yaml-from-a-template

${jsonencode({
  "backends": [for addr in ip_addrs : "${addr}:${port}"],
})}

${yamlencode({
  "backends": [for addr in ip_addrs : "${addr}:${port}"],
})}
{"backends":["10.0.0.1:8080","10.0.0.2:8080"]}

これを参考に書いてみる。

    input    = <<EOT
{
  "id" : "<aws.scheduler.execution-id>",
  "name": "example",
  "hogehoge": ${jsonencode([for fuga in var.hogehoge : "${fuga}"])}
}
    EOT

今回はfor_eachも使っていたので、最終的にはこんな感じになった。

resource "aws_scheduler_schedule" "sample" {
  for_each = var.samples
  ...

  target = {
  input    = <<EOT
{
  "id" : "<aws.scheduler.execution-id>",
  "name": "${each.value.name}",
  "hogehoge": ${jsonencode([for fuga in each.value.hogehoge : "${fuga}"])}
}
EOT
  }
}

variables "samples" {
   "test1" = {
     "name" : "sample1",
     "hogehoge" : ["apple", "banana"]
   },
   "test2" = {
     "name": "sample2",
     "hogehoge": ["orange", "pineapple"]
   }
}

感想

IAMなんかはこんなことしなくてもインラインポリシーで書いたり templatefile で渡せば良いが、今回みたいな場合には注意。
※templatefileも上記エラーと同じでstringで渡すとErrorとなる

久しぶりにterraformでドハマリしてしまったが、最終的には公式ドキュメントに記載されている方法で解決できたのは良かった。
aws_scheduler_scheduleを使う際には要注意です。

カテゴリー: Terraform