Terraformを使ったIAM Policyの効率的な記述方法

この記事では、Terraform で IAM Policy の管理をより効率的に行うためのテクニックをいくつか紹介します。Terraform は v1.7.5、AWS Provider は v5.46.0 で動作確認しました。

目次

  1. jsonencode 関数を使う
  2. ヒアドキュメントを使う
  3. IAM Policy を再利用する
  4. IAM Policy をファイルから読み込む

jsonencode 関数を使う

IAM Role を書く時の例。assume_role_policyinline_policy に HCL で IAM Policy を書いて、jsonencode 関数で JSON に変換します。IAM Role を書くときは以下のパターンを一番よく使います。

resource "aws_iam_role" "example" {
  name               = "my_role"
  assume_role_policy = jsonencode({
    Version   = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Sid    = ""
        Principal = {
          Service = "ec2.amazonaws.com"
        }
      },
    ]
  })

  inline_policy {
    name = "my_inline_policy"

    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Action   = ["ec2:Describe*"]
          Effect   = "Allow"
          Resource = "*"
        },
      ]
    })
  }
}

jsonencode 関数の良いところは以下の通り。

  • オブジェクトの後ろにカンマがあっても大丈夫
  • ダブルクォーテーションの量が減る
  • terraform fmt コマンドで整形される
  • HCL ではなく、間違って JSON を書いても JSON に変換してくれる

参考ドキュメント

ヒアドキュメントを使う

上記の IAM Role の例を heredoc にしたのが以下のコード。IAM Policy がすでにドキュメントに載っていて、それをコピーして利用する場合は heredoc を使います。heredoc は通常 << と書きますが、<<- のようにハイフンをつけることでインデントに対応させて、可読性を上げることができます。

resource "aws_iam_role" "example" {
  name               = "my_role"

  assume_role_policy = <<-EOT
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": "sts:AssumeRole"
        "Effect": "Allow"
        "Sid": ""
        "Principal": {
          "Service": "ec2.amazonaws.com"
        }
      }
    ]
  }
  EOT

  inline_policy {
    name = "my_inline_policy"

    policy = <<-EOT
    {
      "Version": "2012-10-17"
      "Statement": [
        {
          "Action": ["ec2:Describe*"]
          "Effect": "Allow"
          "Resource": "*"
        }
      ]
    }
    EOT
  }
}

通常のヒアドキュメント<< の場合は、以下のようにインデントがずれて見づらくなります。

resource "aws_iam_role" "example" {
  name               = "my_role"
  assume_role_policy = <<EOT
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole"
      "Effect": "Allow"
      "Sid": ""
      "Principal": {
        "Service": "ec2.amazonaws.com"
      }
    }
  ]
}
EOT

  inline_policy {
    name = "my_inline_policy"

    policy = <<EOT
{
  "Version": "2012-10-17"
  "Statement": [
    {
      "Action": ["ec2:Describe*"]
      "Effect": "Allow"
      "Resource": "*"
    }
  ]
}
EOT
  }
}

参考ドキュメント

IAM Policy を再利用する

assume_role_policyは、同じ文字列が何度も出てくることがあります。このようなときは data "aws_iam_policy_document" を使って再利用できるようにします。

data "aws_iam_policy_document" "instance_assume_role_policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "example" {
  name               = "my_role"
  assume_role_policy = data.aws_iam_policy_document.instance_assume_role_policy.json
}

IAM Policy をファイルから読み込む

IAM Policy の記述量が多い場合は、全体の見通しを良くするために、templatefile 関数を使って外部ファイルに定義された IAM Policy を読み込むことがあります。ファイルには Terraform コードから値を渡すことができます。

resource "aws_iam_role" "example" {
  name               = "my_role"

  inline_policy {
    name = "my_inline_policy

    policy = templatefile("./iam_role_policy.tftpl", {
      region      = data.aws_region.current.name
      account_id  = data.aws_caller_identity.current.account_id
      instance_id = "i-0123456789abcdef0"
    })
  }
}

iam_role_policy.tftpl

{
  "Version": "2012-10-17"
  "Statement": [
    {
      "Action": ["ec2:Describe*"]
      "Effect": "Allow"
      "Resource": "arn:aws:ec2:${region}:${account_id}:instance/${instance_id}"
    }
  ]
}

実は、私はこの方法をあまり使いません。IAM Policy の量が多い場合はエディタの機能で折りたたみます。IAM Policy だけファイルが分かれることで、可読性を下げてしまうこともあります。

参考ドキュメント

-技術ブログ
-