diff --git a/examples/container-image/main.tf b/examples/container-image/main.tf
index 75a36ffc..f1a23d0d 100644
--- a/examples/container-image/main.tf
+++ b/examples/container-image/main.tf
@@ -128,7 +128,8 @@ module "docker_build_from_ecr" {
dir_sha = local.dir_sha
}
- cache_from = ["${module.ecr.repository_url}:latest"]
+ cache_from = ["${module.ecr.repository_url}:latest"]
+ use_cache_from_previous_image = true
}
module "ecr" {
diff --git a/modules/docker-build/README.md b/modules/docker-build/README.md
index 0bfb506c..207dd2f4 100644
--- a/modules/docker-build/README.md
+++ b/modules/docker-build/README.md
@@ -60,6 +60,7 @@ module "docker_image" {
| [terraform](#requirement\_terraform) | >= 1.0 |
| [aws](#requirement\_aws) | >= 4.22 |
| [docker](#requirement\_docker) | >= 3.0 |
+| [external](#requirement\_external) | >= 2.3 |
| [null](#requirement\_null) | >= 2.0 |
## Providers
@@ -68,6 +69,7 @@ module "docker_image" {
|------|---------|
| [aws](#provider\_aws) | >= 4.22 |
| [docker](#provider\_docker) | >= 3.0 |
+| [external](#provider\_external) | >= 2.3 |
| [null](#provider\_null) | >= 2.0 |
## Modules
@@ -85,6 +87,7 @@ No modules.
| [null_resource.sam_metadata_docker_registry_image](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
+| [external_external.latest_ecr_image](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source |
## Inputs
@@ -109,6 +112,7 @@ No modules.
| [scan\_on\_push](#input\_scan\_on\_push) | Indicates whether images are scanned after being pushed to the repository | `bool` | `false` | no |
| [source\_path](#input\_source\_path) | Path to folder containing application code | `string` | `null` | no |
| [triggers](#input\_triggers) | A map of arbitrary strings that, when changed, will force the docker\_image resource to be replaced. This can be used to rebuild an image when contents of source code folders change | `map(string)` | `{}` | no |
+| [use\_cache\_from\_previous\_image](#input\_use\_cache\_from\_previous\_image) | If true, use the most recently pushed image in ECR as Docker cache source (cache\_from). Requires an existing ECR repo. | `bool` | `false` | no |
| [use\_image\_tag](#input\_use\_image\_tag) | Controls whether to use image tag in ECR repository URI or not. Disable this to deploy latest image using ID (sha256:...) | `bool` | `true` | no |
## Outputs
diff --git a/modules/docker-build/main.tf b/modules/docker-build/main.tf
index 1bf27a1f..18bb6cf2 100644
--- a/modules/docker-build/main.tf
+++ b/modules/docker-build/main.tf
@@ -7,6 +7,15 @@ locals {
ecr_repo = var.create_ecr_repo ? aws_ecr_repository.this[0].id : var.ecr_repo
image_tag = var.use_image_tag ? coalesce(var.image_tag, formatdate("YYYYMMDDhhmmss", timestamp())) : null
ecr_image_name = var.use_image_tag ? format("%v/%v:%v", local.ecr_address, local.ecr_repo, local.image_tag) : format("%v/%v", local.ecr_address, local.ecr_repo)
+
+ previous_image_from_ecr = try(data.external.latest_ecr_image[0].result.image_uri, "")
+
+ previous_image_list = (
+ var.use_cache_from_previous_image && local.previous_image_from_ecr != ""
+ ) ? [local.previous_image_from_ecr] : []
+
+ cache_from_effective = concat(var.cache_from, local.previous_image_list)
+
}
resource "docker_image" "this" {
@@ -17,7 +26,7 @@ resource "docker_image" "this" {
dockerfile = var.docker_file_path
build_args = var.build_args
platform = var.platform
- cache_from = var.cache_from
+ cache_from = local.cache_from_effective
}
force_remove = var.force_remove
@@ -33,6 +42,17 @@ resource "docker_registry_image" "this" {
triggers = length(var.triggers) == 0 ? { image_id = docker_image.this.image_id } : var.triggers
}
+data "external" "latest_ecr_image" {
+ count = var.use_cache_from_previous_image ? 1 : 0
+
+ program = ["bash", "${path.module}/scripts/get-latest-ecr-image.sh"]
+
+ query = {
+ repository = var.ecr_repo
+ region = data.aws_region.current.name
+ }
+}
+
resource "aws_ecr_repository" "this" {
count = var.create_ecr_repo ? 1 : 0
diff --git a/modules/docker-build/scripts/get-latest-ecr-image.sh b/modules/docker-build/scripts/get-latest-ecr-image.sh
new file mode 100644
index 00000000..00abf01c
--- /dev/null
+++ b/modules/docker-build/scripts/get-latest-ecr-image.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+read -r INPUT
+REPO=$(echo "$INPUT" | jq -r '.repository // empty')
+REGION=$(echo "$INPUT" | jq -r '.region // empty')
+
+if [[ -z "$REPO" || -z "$REGION" ]]; then
+ echo '{"image_uri": ""}'
+ exit 0
+fi
+
+# Check if repo exists
+if ! aws ecr describe-repositories --repository-names "$REPO" --region "$REGION" >/dev/null 2>&1; then
+ echo 'no{"image_uri": ""}'
+ exit 0
+fi
+
+# Get latest image tag
+IMAGE=$(aws ecr describe-images \
+ --repository-name "$REPO" \
+ --region "$REGION" \
+ --query 'reverse(sort_by(imageDetails, &imagePushedAt))[?imageTags]|[0].imageTags[0]' \
+ --output text 2>/dev/null || echo "")
+
+if [ -z "$IMAGE" ] || [ "$IMAGE" == "None" ]; then
+ echo '{"image_uri": ""}'
+ exit 0
+fi
+
+# Get full image URI
+URI=$(aws ecr describe-repositories \
+ --repository-names "$REPO" \
+ --region "$REGION" \
+ --query 'repositories[0].repositoryUri' \
+ --output text)
+
+echo "{\"image_uri\": \"${URI}:${IMAGE}\"}"
diff --git a/modules/docker-build/variables.tf b/modules/docker-build/variables.tf
index e153fa7f..9543a0e5 100644
--- a/modules/docker-build/variables.tf
+++ b/modules/docker-build/variables.tf
@@ -118,3 +118,9 @@ variable "cache_from" {
type = list(string)
default = []
}
+
+variable "use_cache_from_previous_image" {
+ description = "If true, use the most recently pushed image in ECR as Docker cache source (cache_from). Requires an existing ECR repo."
+ type = bool
+ default = false
+}
diff --git a/modules/docker-build/versions.tf b/modules/docker-build/versions.tf
index 93aadf1a..719c4f82 100644
--- a/modules/docker-build/versions.tf
+++ b/modules/docker-build/versions.tf
@@ -14,5 +14,9 @@ terraform {
source = "hashicorp/null"
version = ">= 2.0"
}
+ external = {
+ source = "hashicorp/external"
+ version = ">= 2.3"
+ }
}
}
diff --git a/wrappers/docker-build/main.tf b/wrappers/docker-build/main.tf
index 7d7614cf..6e2c6e09 100644
--- a/wrappers/docker-build/main.tf
+++ b/wrappers/docker-build/main.tf
@@ -3,24 +3,25 @@ module "wrapper" {
for_each = var.items
- build_args = try(each.value.build_args, var.defaults.build_args, {})
- cache_from = try(each.value.cache_from, var.defaults.cache_from, [])
- create_ecr_repo = try(each.value.create_ecr_repo, var.defaults.create_ecr_repo, false)
- create_sam_metadata = try(each.value.create_sam_metadata, var.defaults.create_sam_metadata, false)
- docker_file_path = try(each.value.docker_file_path, var.defaults.docker_file_path, "Dockerfile")
- ecr_address = try(each.value.ecr_address, var.defaults.ecr_address, null)
- ecr_force_delete = try(each.value.ecr_force_delete, var.defaults.ecr_force_delete, true)
- ecr_repo = try(each.value.ecr_repo, var.defaults.ecr_repo, null)
- ecr_repo_lifecycle_policy = try(each.value.ecr_repo_lifecycle_policy, var.defaults.ecr_repo_lifecycle_policy, null)
- ecr_repo_tags = try(each.value.ecr_repo_tags, var.defaults.ecr_repo_tags, {})
- force_remove = try(each.value.force_remove, var.defaults.force_remove, false)
- image_tag = try(each.value.image_tag, var.defaults.image_tag, null)
- image_tag_mutability = try(each.value.image_tag_mutability, var.defaults.image_tag_mutability, "MUTABLE")
- keep_locally = try(each.value.keep_locally, var.defaults.keep_locally, false)
- keep_remotely = try(each.value.keep_remotely, var.defaults.keep_remotely, false)
- platform = try(each.value.platform, var.defaults.platform, null)
- scan_on_push = try(each.value.scan_on_push, var.defaults.scan_on_push, false)
- source_path = try(each.value.source_path, var.defaults.source_path, null)
- triggers = try(each.value.triggers, var.defaults.triggers, {})
- use_image_tag = try(each.value.use_image_tag, var.defaults.use_image_tag, true)
+ build_args = try(each.value.build_args, var.defaults.build_args, {})
+ cache_from = try(each.value.cache_from, var.defaults.cache_from, [])
+ create_ecr_repo = try(each.value.create_ecr_repo, var.defaults.create_ecr_repo, false)
+ create_sam_metadata = try(each.value.create_sam_metadata, var.defaults.create_sam_metadata, false)
+ docker_file_path = try(each.value.docker_file_path, var.defaults.docker_file_path, "Dockerfile")
+ ecr_address = try(each.value.ecr_address, var.defaults.ecr_address, null)
+ ecr_force_delete = try(each.value.ecr_force_delete, var.defaults.ecr_force_delete, true)
+ ecr_repo = try(each.value.ecr_repo, var.defaults.ecr_repo, null)
+ ecr_repo_lifecycle_policy = try(each.value.ecr_repo_lifecycle_policy, var.defaults.ecr_repo_lifecycle_policy, null)
+ ecr_repo_tags = try(each.value.ecr_repo_tags, var.defaults.ecr_repo_tags, {})
+ force_remove = try(each.value.force_remove, var.defaults.force_remove, false)
+ image_tag = try(each.value.image_tag, var.defaults.image_tag, null)
+ image_tag_mutability = try(each.value.image_tag_mutability, var.defaults.image_tag_mutability, "MUTABLE")
+ keep_locally = try(each.value.keep_locally, var.defaults.keep_locally, false)
+ keep_remotely = try(each.value.keep_remotely, var.defaults.keep_remotely, false)
+ platform = try(each.value.platform, var.defaults.platform, null)
+ scan_on_push = try(each.value.scan_on_push, var.defaults.scan_on_push, false)
+ source_path = try(each.value.source_path, var.defaults.source_path, null)
+ triggers = try(each.value.triggers, var.defaults.triggers, {})
+ use_cache_from_previous_image = try(each.value.use_cache_from_previous_image, var.defaults.use_cache_from_previous_image, false)
+ use_image_tag = try(each.value.use_image_tag, var.defaults.use_image_tag, true)
}
diff --git a/wrappers/docker-build/versions.tf b/wrappers/docker-build/versions.tf
index 93aadf1a..719c4f82 100644
--- a/wrappers/docker-build/versions.tf
+++ b/wrappers/docker-build/versions.tf
@@ -14,5 +14,9 @@ terraform {
source = "hashicorp/null"
version = ">= 2.0"
}
+ external = {
+ source = "hashicorp/external"
+ version = ">= 2.3"
+ }
}
}