Terraform で AWS のポリシー JSON をかくときのメモ

ここ最近はずっと AWS まわりの構成を Terraform で書いてます. クセはありますがつぶしたりつくったりすぐできるのが容易でいいと思います.

HCL っていう言語らしいですが Qiita ではハイライトできないですね.

で,若干ひっかかったところをメモ:


AWS のポリシーを書くときに JSON を heredoc で書くことがあると思います:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSpecificIpAddresses",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "es:*"
      ],
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [
            "9.8.7.6",
            "10.11.12.13",
            "100.101.102.103",
            "11.22.33.44"
          ]
        }
      },
      "Resource": "arn:aws:es:ap-northeast-1:000000000000:domain/my-aws-elasticsearch/*"
    }
  ]
}

このなかで特定の IP のリストに対して、たとえば

locals {
  allowed_ips = ["9.8.7.6", "10.11.12.13", "100.101.102.103", "11.22.33.44"]
}

のように Allow 対象の IP をリストで持っている + リストは variable とかで渡ってくるという場合、この list を埋め込む必要があります。

Interpolation?

これに対し普通に Interpolation とかを使うとエラーになりますね:

resource "aws_elasticsearch_domain_policy" "elasticsearch_policy" {
  domain_name = "${aws_elasticsearch_domain.elasticsearch.domain_name}"

  access_policies = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSpecificIpAddresses",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "es:*"
      ],
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "${local.allowed_ips}"
        }
      },
      "Resource": "${aws_elasticsearch_domain.elasticsearch.arn}/*"
    }
  ]
}
  POLICY
}

Error: Invalid template interpolation value local.ngw_public_ips is list of string with 2 elements

どうするか

この場合、リソース + heredoc で書くのをやめて、データソースで定義する方法があります

aws_iam_role_policy リソースに対して aws_iam_policy_document データソースを使えるように: https://www.terraform.io/docs/providers/aws/guides/iam-policy-documents.html

ただ、aws_elasticsearch_domain_policy のようにデータソースで記述できないケースがあります: https://www.terraform.io/docs/providers/aws/r/elasticsearch_domain_policy.html

jsonencode を使う

その場合 join を駆使して……とか考えますが、 そうしなくとも jsonencode を使うのが楽です:

resource "aws_elasticsearch_domain_policy" "elasticsearch_policy" {
  domain_name = "${aws_elasticsearch_domain.elasticsearch.domain_name}"

  access_policies = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSpecificIpAddresses",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "es:*"
      ],
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": ${jsonencode(local.allowed_ips)}
        }
      },
      "Resource": "${aws_elasticsearch_domain.elasticsearch.arn}/*"
    }
  ]
}
  POLICY
}

こうすることで ${jsonencode(local.allowed_ips)}["9.8.7.6", "10.11.12.13", ...]JSONArray に展開されるので、 そのまま書くことができます。

余談

もし各要素に対して map 的な操作が必要であれば、 バージョン 0.12 から List Comprehension が使えるようになったので、 簡単に各要素を変更することができるようになりました:

https://github.com/hashicorp/terraform/issues/8439


Qiita にも同じ内容で投稿してます が重複コンテンツ扱いになったりするだろうか……

👉 よろしければ Twitter でフォロー をお願いいたします.