For_each depends_on

how does one do a depends_on a for_each resource?

depends_on = [“aws_ecr_repository.ecr”]

Hi @FernandoMiguel,

What you showed there, aside from the 0.11-style quoting of the resource address, is the correct syntax. Did you encounter a problem when you tried that?

what is the equivalent for 0.12?

    Warning: Quoted references are deprecated

  on .terraform/modules/ecr/ecr.tf line 30, in resource "aws_ecr_lifecycle_policy" "lifecycle_policy":
  30:   depends_on = ["aws_ecr_repository.ecr"]

In this context, references are expected literally rather than in quotes.
Terraform 0.11 and earlier required quotes, but quoted references are now
deprecated and will be removed in a future version of Terraform. Remove the
quotes surrounding this reference to silence this warning.

ok that helps … wasn’t showing before

resource "aws_ecr_repository" "ecr" {
  for_each = toset(var.ecr_name)
  name     = each.key
}

resource "aws_ecr_lifecycle_policy" "lifecycle_policy" {
  for_each   = toset(var.ecr_name)
  repository = each.key

  policy = <<EOF
{
    "rules": [
        {
            "rulePriority": 30,
            "description": "Expire untaged images older than 1 days",
            "selection": {
                "tagStatus": "untagged",
                "countType": "sinceImagePushed",
                "countUnit": "days",
                "countNumber": ${var.untag_days_countNumber}
            },
            "action": {
                "type": "expire"
            }
        }
    ]
}
EOF

  depends_on = [aws_ecr_repository.ecr]
}

resource "aws_ecr_repository_policy" "repository_policy" {
  for_each   = toset(var.ecr_name)
  repository = each.key

  policy = <<EOF
{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                "arn:aws:iam::0123456789:root",
                ]
            },
            "Action": [
                "ecr:BatchCheckLayerAvailability",
                "ecr:BatchGetImage",
                "ecr:DescribeImages",
                "ecr:GetAuthorizationToken",
                "ecr:GetDownloadUrlForLayer"
            ]
        }
    ]
}
EOF

#   depends_on = [aws_ecr_repository.ecr]
}
  + create

Terraform will perform the following actions:

  # module.ecr.aws_ecr_lifecycle_policy.lifecycle_policy["foobaar"] will be created
  + resource "aws_ecr_lifecycle_policy" "lifecycle_policy" {
      + id          = (known after apply)
      + policy      = jsonencode(
            {
              + rules = [
                  + {
                      + action       = {
                          + type = "expire"
                        }
                      + description  = "Expire untaged images older than 1 days"
                      + rulePriority = 30
                      + selection    = {
                          + countNumber = 1
                          + countType   = "sinceImagePushed"
                          + countUnit   = "days"
                          + tagStatus   = "untagged"
                        }
                    },
                ]
            }
        )
      + registry_id = (known after apply)
      + repository  = "foobaar"
    }

  # module.ecr.aws_ecr_repository.ecr["foobaar"] will be created
  + resource "aws_ecr_repository" "ecr" {
      + arn                  = (known after apply)
      + id                   = (known after apply)
      + image_tag_mutability = "MUTABLE"
      + name                 = "foobaar"
      + registry_id          = (known after apply)
      + repository_url       = (known after apply)
    }

  # module.ecr.aws_ecr_repository_policy.repository_policy["foobaar"] will be created
  + resource "aws_ecr_repository_policy" "repository_policy" {
      + id          = (known after apply)
      + policy      = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = [
                          + "ecr:BatchCheckLayerAvailability",
                          + "ecr:BatchGetImage",
                          + "ecr:DescribeImages",
                          + "ecr:GetAuthorizationToken",
                          + "ecr:GetDownloadUrlForLayer",
                        ]
                      + Effect    = "Allow"
                      + Principal = {
                          + AWS = [
                              + "arn:aws:iam::0123456789:root",
                            ]
                        }
                    },
                ]
              + Version   = "2008-10-17"
            }
        )
      + registry_id = (known after apply)
      + repository  = "foobaar"
    }

Plan: 3 to add, 0 to change, 0 to destroy.

get’s the following error

module.ecr.aws_ecr_repository.ecr["foobaar"]: Creating...
module.ecr.aws_ecr_repository_policy.repository_policy["foobaar"]: Creating...
module.ecr.aws_ecr_repository.ecr["foobaar"]: Creation complete after 0s [id=foobaar]
module.ecr.aws_ecr_lifecycle_policy.lifecycle_policy["foobaar"]: Creating...
module.ecr.aws_ecr_lifecycle_policy.lifecycle_policy["foobaar"]: Creation complete after 1s [id=foobaar]

Error: Error creating ECR Repository Policy: RepositoryNotFoundException: The repository with name 'foobaar' does not exist in the registry with id '0123456789'

  on .terraform/modules/ecr/ecr.tf line 33, in resource "aws_ecr_repository_policy" "repository_policy":
  33: resource "aws_ecr_repository_policy" "repository_policy" {

something is failing to use implicit dependency on a resource.
so i’m trying to use explicit.

In the log we can see that the repository creation is completing before Terraform tries to create the lifecycle policy:

module.ecr.aws_ecr_repository.ecr["foobaar"]: Creation complete after 0s [id=foobaar]
module.ecr.aws_ecr_lifecycle_policy.lifecycle_policy["foobaar"]: Creating...

So it looks like the dependencies in the configuration are correct, but that the aws_ecr_repository implementation in the AWS provider is returning success before the repository has fully committed. If that is true, unfortunately I don’t think there’s any way to strengthen that dependency from within the Terraform language: the provider would need to make sure the repository is ready to use before indicating that the creation has completed successfully.

Although the error message is not exactly the same, the overall behavior here seems similar to the following issue in the AWS provider:

Since that was originally opened in 2017, I think it’s plausible that the exact error message text returned from the remote API could’ve changed in the meantime, because both your message and the one reported there both seem to be saying (in different words) that the policy refers to a non-existing repository.

Thing is, the moment you add an explicit depends_on it works as expected.
That is why I was trying to find the correct syntax for for_each

Looking again at your configuration, it does seem like it was missing a dependency edge, though the log you shared showed the operations happening in the correct order anyway so perhaps the problem is a mixture of Terraform dependency handling and eventual consistency weirdness.

A different way to tell Terraform about the dependency, rather than falling back on depends_on, would be to refer to the repository resource from your for_each:

resource "aws_ecr_lifecycle_policy" "lifecycle_policy" {
  for_each = aws_ecr_repository.ecr

  repository = each.eky
  policy = jsonencode(...)
}

As well as declaring that the lifecycle policy objects depend on the repository objects, this would also allow using each.value in those blocks to refer to the full objects representing the repository. That’s not necessary in this case because the two policies are not variable per repository anyway, but I’m sharing it just as a general pattern to follow when your intent is to say e.g. “create one repository policy per repository”, which seems to be the case here.

If you do want to use depends_on here then indeed the syntax would be to just refer to the resource as normal: dependencies happen prior to resource expansion, so dependencies are always between resource blocks, not between the individual instances of those resource blocks:

  depends_on = [aws_ecr_repository.ecr]

Just as with the for_each strategy I described earlier, the dependency here is that all of the policies depend on all of the repositories, rather than each individual policy depending only on its corresponding repository instance. In practice that distinction rarely matters, but I just wanted to point it out since the question was about how depends_on and for_each interact.

Oh wow
For_each of the resource created, and not of the var that produces the array.
Never thought of that before.

Time to rewrite some code.

Cheers