LoginSignup
0
0

Terraform の `The "for_each" value depends on resource attributes that cannot be determined until apply` に対する対応

Posted at

この記事は リンクバルアドベントカレンダー2023 の6日目の記事です。

昨日は @Togo_Yokoyama さんの記事です!

はじめに

Terraformで for_each を使ってループさせるとき、以下のエラーが起こることがあります。

The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.

これはなんのエラーか

for_each で指定する値に、作成前のリソースの情報があると起こるエラーです。

詳しくは以下の公式ドキュメントや参考記事を御覧ください。

エラーが起こる例

例えば以下のような場合です。
AWS前提で書きます。

  • moduleで、aws_iam_role_policy_attachment をする
  • aws_iam_role_policy_attachment するポリシーにカスタムポリシーが含まれる

ec2にアタッチするIAMロールにポリシーをアタッチする場合で考えてみましょう。

※勢いで書いて動作確認していないので、他でエラーが出るかもです🙏

module

path/to/main.tf
locals {
  custom_policy_arns = [
    var.sample_policy_arn,
    xxx,
    ...,
  ]
  iam_policy_arns = concat(
    var.role_policies,
    custom_policy_arns
  )
}

# 略

resource "aws_iam_role_policy_attachment" "ec2" {
  for_each = toset(local.iam_policy_arns) # エラーになる

  role       = var.iam_role_name
  policy_arn = each.value
}

# 略

module利用側

module "ec2_web" {
  source  = "path/to/sample"

  role_policies = [
    "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore",
    "arn:aws:iam::aws:policy/AmazonSSMPatchAssociation",
  ]

  iam_role_name     = aws_iam_role.ec2_web.name
  sample_policy_arn = aws_iam_policy.sample.arn

  # 略
}

resource "aws_iam_role" "ec2_web" {
  name               = "aaaaa"
  assume_role_policy = # 略
}

resource "aws_iam_policy" "sample" {
  # 略
}

# 略

解決策

何点かあります。

  1. エラーが起こるリソースだけ -target オプションか手動で先に作る
  2. 諦めて一つ一つリソースを作る
    • さきほどの例だと、カスタムポリシーのリソース数だけ aws_iam_role_policy_attachment を作ります
  3. lengthcount を使う ※注意点あり

3つ目の例を紹介します。

aws_iam_role_policy_attachment の部分だけ書きますね。

# before
resource "aws_iam_role_policy_attachment" "ec2" {
  for_each = toset(local.iam_policy_arns) # エラーになる

  role       = var.iam_role_name
  policy_arn = each.value
}

# after
resource "aws_iam_role_policy_attachment" "ec2" {
  count = length(local.iam_policy_arns)

  role       = var.iam_role_name
  policy_arn = var.role_policy_arns[count.index]
}

これだとエラーにならずに無事リソース作成ができます!
なぜかは知らん。

lengthcount 使用時の注意点

末尾以外の aws_iam_role_policy_attachment をなくすとリソースの再作成が起こります。

countlist なので、今回の例だと以下のようなstate名になります。
(ここも動作確認していないので、ちょっと違うかも)

module.ec2_web.aws_iam_role_policy_attachment.ec2[0]
module.ec2_web.aws_iam_role_policy_attachment.ec2[1]
module.ec2_web.aws_iam_role_policy_attachment.ec2[2]
# ポリシーの数だけ続く

要素数が3つだけだとして、インデックスが0のリソースを消したら、次のようにリソースは再作成されてしまいます。

  • module.ec2_web.aws_iam_role_policy_attachment.ec2[1]module.ec2_web.aws_iam_role_policy_attachment.ec2[0]
  • module.ec2_web.aws_iam_role_policy_attachment.ec2[2]module.ec2_web.aws_iam_role_policy_attachment.ec2[1]

countlist の仕様上これは避けられません。
ただし、リソースが削除後に state mv をすれば、変更だけで済む?(未検証)
リソース次第な気ががしますね。。。

$ terraform state mv module.ec2_web.aws_iam_role_policy_attachment.ec2[1] module.ec2_web.aws_iam_role_policy_attachment.ec2[0]
$ terraform state mv module.ec2_web.aws_iam_role_policy_attachment.ec2[2] module.ec2_web.aws_iam_role_policy_attachment.ec2[1]

基本的に for_each を使ったほうがいいです。
詳細は後述の参考記事をご覧ください。

参考

lengthcount の方法

count より for_each を使ったほうがよい理由

なお、上記記事では「count を使用したリソース作成有無 (0/1) の判定は昔から行われていた手法であるが、その場合においては count を使うほうが好ましい。」とありますが、 for_each でも同じようなことはできます!

おわりに

おわり

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0