From c33a02254ce3ded8d0bdc61fe7c87dce9db63d28 Mon Sep 17 00:00:00 2001 From: "David W.H. Swenson" Date: Mon, 23 Mar 2026 19:29:45 -0500 Subject: [PATCH 1/2] lambda-image-republish: Digest change should trigger update This handles the case that user deploys with tag "latest", and expects a future update to pull in the newest "latest." Note that we recommend using immutable tags, but my intuition is that if someone chooses "latest," then they probably want to autoupdate when they do a new plan. I don't particularly like this implementation, but from what I can tell, the aws_ecr_image doesn't work on public repositories, and there's no equivalent in aws_ecrpublic until provider version 6.19. Unfortunately, I'm still stuck on 4.x for local reasons. --- modules/lambda-image-republish/main.tf | 36 ++++++++++++++++++++++ modules/lambda-image-republish/versions.tf | 4 +++ 2 files changed, 40 insertions(+) diff --git a/modules/lambda-image-republish/main.tf b/modules/lambda-image-republish/main.tf index 78d423c..c4dee8c 100644 --- a/modules/lambda-image-republish/main.tf +++ b/modules/lambda-image-republish/main.tf @@ -21,6 +21,41 @@ data "aws_caller_identity" "current" {} data "aws_region" "current" {} +# TODO: apparently this ugly thing is needed because it's a public repo, and +# I'm stuck on an old provider. Looks like 6.19 introduces what I need +data "external" "source_image" { + # Inspect the current tag target so stable tags like "latest" trigger a + # republish when the upstream manifest digest changes. + program = [ + "/bin/sh", + "-c", + <<-EOC + set -euo pipefail + image_uri="$1" + export DOCKER_CONFIG="$(mktemp -d)" + trap 'rm -rf "$DOCKER_CONFIG"' EXIT + image_digest="$( + docker manifest inspect --verbose "$image_uri" | awk ' + /"Descriptor"[[:space:]]*:/ { in_descriptor = 1; next } + in_descriptor && /}/ { in_descriptor = 0 } + in_descriptor && /"digest"[[:space:]]*:/ { + digest_line = $0 + sub(/^[^"]*"digest"[[:space:]]*:[[:space:]]*"/, "", digest_line) + sub(/".*$/, "", digest_line) + print digest_line + exit + } + ' + )" + [ -n "$image_digest" ] + printf '{"image_digest":"%s"}\n' "$image_digest" + EOC + , + "source-image-digest", + local.source_image_uri, + ] +} + resource "aws_ecr_repository" "destination" { name = local.repository_name force_delete = true @@ -82,6 +117,7 @@ resource "aws_ecr_repository_policy" "self_access" { resource "null_resource" "republish_image" { triggers = { + source_image_digest = data.external.source_image.result.image_digest source_repository = local.source_repo source_tag = var.source_lambda_tag destination_repository = aws_ecr_repository.destination.repository_url diff --git a/modules/lambda-image-republish/versions.tf b/modules/lambda-image-republish/versions.tf index 69cb52b..15ffe18 100644 --- a/modules/lambda-image-republish/versions.tf +++ b/modules/lambda-image-republish/versions.tf @@ -6,5 +6,9 @@ terraform { source = "hashicorp/aws" version = "~> 4.0" } + external = { + source = "hashicorp/external" + version = "~> 2.0" + } } } From 7e0f0c14ba067a43b77dc3e33d2b695dd3cb3937 Mon Sep 17 00:00:00 2001 From: "David W.H. Swenson" Date: Mon, 23 Mar 2026 20:07:42 -0500 Subject: [PATCH 2/2] Slightly better implementation (use external docker provider) --- modules/lambda-image-republish/main.tf | 39 +++------------------- modules/lambda-image-republish/versions.tf | 8 +++-- 2 files changed, 10 insertions(+), 37 deletions(-) diff --git a/modules/lambda-image-republish/main.tf b/modules/lambda-image-republish/main.tf index c4dee8c..85c751a 100644 --- a/modules/lambda-image-republish/main.tf +++ b/modules/lambda-image-republish/main.tf @@ -21,39 +21,10 @@ data "aws_caller_identity" "current" {} data "aws_region" "current" {} -# TODO: apparently this ugly thing is needed because it's a public repo, and -# I'm stuck on an old provider. Looks like 6.19 introduces what I need -data "external" "source_image" { - # Inspect the current tag target so stable tags like "latest" trigger a - # republish when the upstream manifest digest changes. - program = [ - "/bin/sh", - "-c", - <<-EOC - set -euo pipefail - image_uri="$1" - export DOCKER_CONFIG="$(mktemp -d)" - trap 'rm -rf "$DOCKER_CONFIG"' EXIT - image_digest="$( - docker manifest inspect --verbose "$image_uri" | awk ' - /"Descriptor"[[:space:]]*:/ { in_descriptor = 1; next } - in_descriptor && /}/ { in_descriptor = 0 } - in_descriptor && /"digest"[[:space:]]*:/ { - digest_line = $0 - sub(/^[^"]*"digest"[[:space:]]*:[[:space:]]*"/, "", digest_line) - sub(/".*$/, "", digest_line) - print digest_line - exit - } - ' - )" - [ -n "$image_digest" ] - printf '{"image_digest":"%s"}\n' "$image_digest" - EOC - , - "source-image-digest", - local.source_image_uri, - ] +# Remove this once the AWS provider is >= 6.19 and can read public ECR image +# metadata directly. +data "docker_registry_image" "source_image" { + name = local.source_image_uri } resource "aws_ecr_repository" "destination" { @@ -117,7 +88,7 @@ resource "aws_ecr_repository_policy" "self_access" { resource "null_resource" "republish_image" { triggers = { - source_image_digest = data.external.source_image.result.image_digest + source_image_digest = data.docker_registry_image.source_image.sha256_digest source_repository = local.source_repo source_tag = var.source_lambda_tag destination_repository = aws_ecr_repository.destination.repository_url diff --git a/modules/lambda-image-republish/versions.tf b/modules/lambda-image-republish/versions.tf index 15ffe18..abd393d 100644 --- a/modules/lambda-image-republish/versions.tf +++ b/modules/lambda-image-republish/versions.tf @@ -6,9 +6,11 @@ terraform { source = "hashicorp/aws" version = "~> 4.0" } - external = { - source = "hashicorp/external" - version = "~> 2.0" + # Remove this once the AWS provider is >= 6.19 and can read public ECR + # image metadata directly. + docker = { + source = "kreuzwerker/docker" + version = "~> 3.5" } } }