skip to Main Content

I have the following piece of code in TFE v.1.13. It works as intended but feels a bit repetitive:

resource "aws_ecr_repository" "repository-a" {
  name                 = "repo-a"
  #skipped 
}
    
resource "aws_ecr_repository" "repository-b" {
  name                 = "repo-b"
  #skipped
}
    
resource "aws_ecr_repository" "repository-c" {
  name                 = "repo-c"
  #skipped
}

So, I have refactored it as follows:

locals {
  repo_table = [
    {
      repo_id = "repo-id-a"
      repo_name = "repo-a"
    },
    {
      repo_id = "repo-id-b"
      repo_name = "repo-b"
    },
    {
      repo_id = "repo-id-c" 
      repo_name = "repo-c"
    }
  ]
}

resource "aws_ecr_repository" "repositories" {
  for_each = {for repo in local.repo_table : repo.repo_id => repo }
    
  name                 = each.value.repo_name
  #skipped
}

I got the following pair of errors on apply for each of the three:

Error: ECR Repository (repo-a) not empty, consider using force_delete: RepositoryNotEmptyException: The repository with name ‘repo-a’ in registry with id ‘123456789’ cannot be deleted because it still contains images

Error: creating ECR Repository (repo-a): RepositoryAlreadyExistsException: The repository with name ‘repo-a’ already exists in the registry with id ‘123456789’

So, Terraform tries to delete and recreate each of them, but it can’t do that because there are images in there. Is there any way to refactor the code but avoid deleting existing images/repos ?

2

Answers


  1. Since the repositories already exist in the state terraform keeps, you cannot just remove the old blocks and recreate the repositories with the same names, as they are already known to terraform. You could try using a combination of locals (which you already have) and import block. Based on your question, the import would look something like the following:

    locals {
      repo_table = [
        {
          repo_id = "repo-id-a"
          repo_name = "repo-a"
        },
        {
          repo_id = "repo-id-b"
          repo_name = "repo-b"
        },
        {
          repo_id = "repo-id-c" 
          repo_name = "repo-c"
        }
      ]
    }
    
    import {
      for_each = local.repo_table
      to       = aws_ecr_repository.repositories[each.key]
      id       = each.value.repo_name
    }
    
    resource "aws_ecr_repository" "repositories" {
      for_each = local.repo_table
      name     = each.value.repo_name
    }
    

    After you do this, you should be able to remove the import block entirely, and nothing would change.

    Login or Signup to reply.
  2. From the information you’ve shared, the issue happens because Terraform treats the new for_each resources as new resources and tries to delete and recreate the repositories. Since your repositories contain images, the deletion will fail.

    Here’s how I suggest you fix this:

    1. Keep your refactored code: Your updated code using for_each is correct, so no changes are needed there.

    2. Import existing repositories into the Terraform state:
      You need to tell Terraform these repositories already exist by running:

    terraform import aws_ecr_repository.repositories["repo-id-a"] repo-a
    terraform import aws_ecr_repository.repositories["repo-id-b"] repo-b
    terraform import aws_ecr_repository.repositories["repo-id-c"] repo-c
    
    1. Use terraform plan to confirm Terraform recognizes the repositories as existing and doesn’t plan to delete or recreate them. If everything looks good, run terraform apply.

    2. Prevent accidental deletions:
      To safeguard your repositories, you can add the following to the resource configuration:

    lifecycle {
      prevent_destroy = true
    }
    

    Hope it helps.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search