You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Terraform创建ECR仓库报错:RepositoryAlreadyExistsException问题排查

问题:Terraform重复创建ECR仓库报错

场景概述

尝试通过Terraform部署两个基于Docker镜像的AWS Lambda函数,对应两个独立ECR仓库,文件夹结构如下:

- main.tf
- variables.tf 
- lambda_functions/
  - main.tf
  - ...
- ecr_repository/
  - main.tf
  - ...

ECR仓库模块代码(ecr_repository/main.tf)

resource "aws_ecr_repository" "converter_repo" {
  name = "cs-lambda-converter-repo-${var.environment}"
  force_delete = true
}

resource "aws_ecr_repository" "uploader_repo" {
  name = "cs-lambda-uploader-repo-${var.environment}"
  force_delete = true
}

output "converter_repo_url" {
  value = aws_ecr_repository.converter_repo.repository_url
}

output "uploader_repo_url" {
  value = aws_ecr_repository.uploader_repo.repository_url
}

output "converter_repo_name" {
  value = aws_ecr_repository.converter_repo.name
}

output "uploader_repo_name" {
  value = aws_ecr_repository.uploader_repo.name
}

Lambda函数模块代码(lambda_functions/main.tf)

module "ecr_repository" {
  source      = "../ecr_repository"
  environment = var.environment
}

locals {
  uploader_image_tag        = "latest"
  converter_image_tag       = "latest"
}

data "aws_ecr_image" "uploader_lambda_image" {
  depends_on = [
    null_resource.ecr_uploader_image
  ]
  repository_name = module.ecr_repository.uploader_repo_name
  image_tag       = local.uploader_image_tag
}

data "aws_ecr_image" "converter_lambda_image" {
  depends_on = [
    null_resource.ecr_converter_image
  ]
  repository_name =  module.ecr_repository.converter_repo_name
  image_tag       =  local.converter_image_tag
}

resource "null_resource" "ecr_uploader_image" {

  triggers = {
    python_file = md5(file("${var.lambda_uploader_path}/lambda_function.py"))
    docker_file = md5(file("${var.lambda_uploader_path}/Dockerfile"))
  }
  provisioner "local-exec" {
    command     = <<EOF
      rm ~/.docker/config.json 2> /dev/null
      aws ecr get-login-password --region ${var.region} | docker login --username AWS --password-stdin ${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com
      docker buildx build --platform linux/amd64 -t ${module.ecr_repository.uploader_repo_url}:${local.uploader_image_tag} .
      docker push ${module.ecr_repository.uploader_repo_url}:${local.uploader_image_tag}
    EOF
    interpreter = ["bash", "-c"]
    working_dir = var.lambda_uploader_path
  }
}

resource "null_resource" "ecr_converter_image" {

  triggers = {
    python_file = md5(file("${var.lambda_converter_path}/lambda_function.py"))
    docker_file = md5(file("${var.lambda_converter_path}/Dockerfile"))
  }
  provisioner "local-exec" {
    command     = <<EOF
      rm ~/.docker/config.json 2> /dev/null
      aws ecr get-login-password --region ${var.region} | docker login --username AWS --password-stdin ${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com
      docker buildx build --platform linux/amd64 -t ${module.ecr_repository.converter_repo_url}:${local.converter_image_tag} .
      docker push ${module.ecr_repository.converter_repo_url}:${local.converter_image_tag}
    EOF
    interpreter = ["bash", "-c"]
    working_dir = var.lambda_converter_path
  }
}

resource "aws_lambda_function" "pdf-uploader" {
  depends_on = [
    null_resource.ecr_uploader_image
  ]
  function_name = "capsphere-ocr-uploader-${var.environment}"
  role          = aws_iam_role.lambda-role.arn
  timeout       = 300
  memory_size   = 512
  image_uri     = "${module.ecr_repository.uploader_repo_url}:${local.uploader_image_tag}"
  package_type  = "Image"
}

resource "aws_lambda_function" "pdf-converter" {
  depends_on = [
    null_resource.ecr_converter_image
  ]
  function_name = "capsphere-ocr-converter-${var.environment}"
  role          = aws_iam_role.lambda-role.arn
  timeout       = 300
  memory_size   = 512
  image_uri     = "${module.ecr_repository.converter_repo_url}:${local.converter_image_tag}"
  package_type  = "Image"
}

output "uploader_lambda_name" {
  value = aws_lambda_function.pdf-uploader.function_name
}

output "converter_lambda_name" {
  value = aws_lambda_function.pdf-converter.function_name
}

执行报错

Error: creating ECR Repository (cs-lambda-converter-repo-dev): RepositoryAlreadyExistsException: The repository with name 'cs-lambda-converter-repo-dev' already exists in the registry with id 'XXXXX'

  with module.lambda_functions.module.ecr_repository.aws_ecr_repository.converter_repo,
  on modules\ecr_repository\main.tf line 1, in resource "aws_ecr_repository" "converter_repo":
   1: resource "aws_ecr_repository" "converter_repo" {


Error: creating ECR Repository (cs-lambda-uploader-repo-dev): RepositoryAlreadyExistsException: The repository with name 'cs-lambda-uploader-repo-dev' already exists in the registry with id 'XXXXX'

  with module.ecr_repository.aws_ecr_repository.uploader_repo,
  on modules\ecr_repository\main.tf line 6, in resource "aws_ecr_repository" "uploader_repo":
   6: resource "aws_ecr_repository" "uploader_repo" {

排查思路

  1. 检查模块重复调用:从报错信息可见,ECR仓库被两个不同模块实例创建——module.lambda_functions.module.ecr_repository和module.ecr_repository,说明根目录main.tf大概率也直接调用了ecr_repository模块,和lambda_functions模块内部的调用形成重复创建。
  2. 核对Terraform状态:执行terraform state list,查看状态中是否存在两个同名ECR仓库资源实例,确认重复定义的来源。
  3. 检查根模块代码:查看根目录main.tf,是否存在独立调用ecr_repository模块的代码,比如:
    module "ecr_repository" {
      source      = "./ecr_repository"
      environment = var.environment
    }
    
    若存在则会和lambda_functions模块内的调用冲突。

解决方法

方法1:移除重复的模块调用

推荐将ECR模块的调用放在根模块,通过变量传递输出值给lambda_functions模块,避免嵌套调用重复创建:

  • 调整根目录main.tf:
    module "ecr_repository" {
      source      = "./ecr_repository"
      environment = var.environment
    }
    
    module "lambda_functions" {
      source                = "./lambda_functions"
      environment           = var.environment
      region                = var.region
      lambda_uploader_path  = var.lambda_uploader_path
      lambda_converter_path = var.lambda_converter_path
      # 传递ECR仓库的输出值
      uploader_repo_url     = module.ecr_repository.uploader_repo_url
      uploader_repo_name    = module.ecr_repository.uploader_repo_name
      converter_repo_url    = module.ecr_repository.converter_repo_url
      converter_repo_name   = module.ecr_repository.converter_repo_name
    }
    
  • 修改lambda_functions/main.tf,移除内部的ecr_repository模块调用,改用变量接收:
    variable "uploader_repo_url" {}
    variable "uploader_repo_name" {}
    variable "converter_repo_url" {}
    variable "converter_repo_name" {}
    
    # 移除原来的module "ecr_repository"块
    
    # 后续代码直接使用var.uploader_repo_url等变量替代原module调用
    

方法2:导入现有资源到Terraform状态

如果ECR仓库已存在且需要保留,执行terraform import将现有仓库导入状态,避免重复创建:

# 根据实际保留的模块路径选择,比如保留根模块调用则用module.ecr_repository
terraform import module.ecr_repository.aws_ecr_repository.converter_repo cs-lambda-converter-repo-dev
terraform import module.ecr_repository.aws_ecr_repository.uploader_repo cs-lambda-uploader-repo-dev

方法3:清理重复的状态条目

如果状态中存在重复资源实例,执行terraform state rm删除多余条目:

# 删除lambda模块内的重复ECR资源
terraform state rm module.lambda_functions.module.ecr_repository.aws_ecr_repository.converter_repo
terraform state rm module.lambda_functions.module.ecr_repository.aws_ecr_repository.uploader_repo

内容的提问来源于stack exchange,提问作者clattenburg cake

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 23:07:00