diff --git a/.github/workflows/easy-ecr.yaml b/.github/workflows/easy-ecr.yaml
new file mode 100644
index 0000000..6da2e5e
--- /dev/null
+++ b/.github/workflows/easy-ecr.yaml
@@ -0,0 +1,47 @@
+---
+name: easy-ecr - verify and build
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - 'easy-ecr/**'
+ pull_request:
+ branches:
+ - main
+ paths:
+ - 'easy-ecr/**'
+
+jobs:
+ test-and-verify:
+ runs-on: ubuntu-24.04
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ fetch-tags: true
+ - name: Setup Go
+ uses: actions/setup-go@v4
+ with:
+ go-version: '1.23'
+ - name: Setup Terraform
+ uses: hashicorp/setup-terraform@v2
+ with:
+ terraform_version: 1.14.0
+ - name: Setup tflint
+ uses: terraform-linters/setup-tflint@v6
+ with:
+ tflint_version: 'v0.60.0'
+ - name: Install Checkov
+ run: |
+ pip install checkov
+ - name: Run verification
+ run: |
+ make codeartifact-repo-verify
+ release:
+ needs: test-and-verify
+ uses: ./.github/workflows/common-release.yaml
+ with:
+ tag-prefix: 'easy-ecr-'
diff --git a/Makefile b/Makefile
index e7ecefd..5e95414 100644
--- a/Makefile
+++ b/Makefile
@@ -9,4 +9,10 @@ codeartifact-repo-format: init
cd ./codeartifact-repo && ../scripts/format.sh
codeartifact-repo-verify: init
- cd ./codeartifact-repo && ../scripts/verify.sh
\ No newline at end of file
+ cd ./codeartifact-repo && ../scripts/verify.sh
+
+easy-ecr-format: init
+ cd ./easy-ecr && ../scripts/format.sh
+
+easy-ecr-verify: init
+ cd ./easy-ecr && ../scripts/verify.sh
\ No newline at end of file
diff --git a/README.md b/README.md
index 5bf3872..aac9b5d 100644
--- a/README.md
+++ b/README.md
@@ -6,3 +6,5 @@ Included modules:
* [tf-bootstrap-aws](./tf-bootstrap-aws/README.md) - Cloudformation template to easily bootstrap Terraform S3 backend
* [codeartifact-repo](./codeartifact-repo/README.md) - Terraform module to setup and configure AWS CodeArtifact domain and repository
+* [easy-ecr](./easy-ecr/README.md) - Terraform module to setup and configure AWS ECR repository
+
diff --git a/easy-ecr/README.md b/easy-ecr/README.md
new file mode 100644
index 0000000..7ed30b0
--- /dev/null
+++ b/easy-ecr/README.md
@@ -0,0 +1,95 @@
+
+# Easy ECR
+
+This Terraform module provides production-ready ECR repository for storing container images.
+
+## Contents
+* [Requirements](#requirements)
+* [Providers](#providers)
+* [Resources](#resources)
+* [Inputs](#inputs)
+* [Outputs](#outputs)
+* [Examples](#examples)
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.14.0 |
+| [aws](#requirement\_aws) | >= 6.21.0 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | 6.25.0 |
+
+## Resources
+| Name | Type |
+|------|------|
+| [aws_ecr_account_setting.account_scan_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_account_setting) | resource |
+|**Description:** ||
+| [aws_ecr_lifecycle_policy.repo_lifecycle_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_lifecycle_policy) | resource |
+|**Description:** ||
+| [aws_ecr_pull_through_cache_rule.custom_pullthrough_cache_rule](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_pull_through_cache_rule) | resource |
+|**Description:** ||
+| [aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_pull_through_cache_rule) | resource |
+|**Description:** ||
+| [aws_ecr_registry_policy.registry_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_registry_policy) | resource |
+|**Description:** ||
+| [aws_ecr_repository.ecr_private_repo](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository) | resource |
+|**Description:** ||
+| [aws_ecr_repository_policy.repo_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository_policy) | resource |
+|**Description:** ||
+| [aws_ecrpublic_repository.ecr_public_repo](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecrpublic_repository) | resource |
+|**Description:** ||
+| [aws_ecrpublic_repository_policy.public_repo_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecrpublic_repository_policy) | resource |
+|**Description:** ||
+| [aws_kms_key.domain_encryption_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
+|**Description:** ||
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [aws\_public\_pullthrough\_cache\_rule](#input\_aws\_public\_pullthrough\_cache\_rule) | Pullthrough cache rule for AWS public registry. Override default values to customize |
object({
enabled = optional(bool, false)
ecr_repository_prefix = optional(string, "ROOT")
upstream_repository_prefix = optional(string, "ROOT")
}) | `{}` | no |
+| [default\_account\_scan\_config](#input\_default\_account\_scan\_config) | Default ECR basic scan type configuration. | object({
name = string
value = string
}) | {
"name": "BASIC_SCAN_TYPE_VERSION",
"value": "AWS_NATIVE"
} | no |
+| [docker\_hub\_pullthrough\_cache\_rule](#input\_docker\_hub\_pullthrough\_cache\_rule) | Pullthrough cache rule for Docker Hub registry. Override default values to customize | object({
enabled = optional(bool, false)
ecr_repository_prefix = optional(string, "ROOT")
upstream_repository_prefix = optional(string, "ROOT")
credential_arn = optional(string, null)
}) | `{}` | no |
+| [domain\_encryption\_key\_policy\_path](#input\_domain\_encryption\_key\_policy\_path) | Local path to policy file to be applied to created KMS key. If not specified, no custom policy is applied. | `string` | `null` | no |
+| [ecr\_region](#input\_ecr\_region) | Region in which repositories will be managed. If not specified, defaults to region configured for provider | `string` | `null` | no |
+| [encryption\_key\_arn](#input\_encryption\_key\_arn) | ARN of KMS key used for repository encryption. If not specified, and use\_default\_ecnryption\_key is false, creates new KMS key | `string` | `null` | no |
+| [force\_delete](#input\_force\_delete) | If 'true', deletes repository even if it has contents | `bool` | `false` | no |
+| [github\_cr\_pullthrough\_cache\_rule](#input\_github\_cr\_pullthrough\_cache\_rule) | Pullthrough cache rule for Github Container registry. Override default values to customize | object({
enabled = optional(bool, false)
ecr_repository_prefix = optional(string, "ROOT")
upstream_repository_prefix = optional(string, "ROOT")
credential_arn = optional(string, null)
}) | `{}` | no |
+| [gitlab\_cr\_pullthrough\_cache\_rule](#input\_gitlab\_cr\_pullthrough\_cache\_rule) | Pullthrough cache rule for Gitlab Container registry. Override default values to customize | object({
enabled = optional(bool, false)
ecr_repository_prefix = optional(string, "ROOT")
upstream_repository_prefix = optional(string, "ROOT")
credential_arn = optional(string, null)
}) | `{}` | no |
+| [image\_lifecycle\_policy\_path](#input\_image\_lifecycle\_policy\_path) | Path to JSON file providing lifecycle policy for the repository | `string` | `null` | no |
+| [image\_tag\_mutable](#input\_image\_tag\_mutable) | Whether image tags are mutable. Only applicable for private repositories. | `bool` | `true` | no |
+| [k8s\_pullthrough\_cache\_rule](#input\_k8s\_pullthrough\_cache\_rule) | Pullthrough cache rule for Kubernetes public registry. Override default values to customize | object({
enabled = optional(bool, false)
ecr_repository_prefix = optional(string, "ROOT")
upstream_repository_prefix = optional(string, "ROOT")
}) | `{}` | no |
+| [mutability\_exclusion\_filters](#input\_mutability\_exclusion\_filters) | List of tag prefixes to exclude from image tag mutability. Setting this will result in IMMUTABLE\_WITH\_EXLUSIONSo MUTABLE\_WITH\_EXCLUSION behavior. Only applicable for private repositories. | `list(string)` | `[]` | no |
+| [public\_catalog\_data](#input\_public\_catalog\_data) | Catalog data for public repositories (optional) | object({
about = optional(string, ""),
description = optional(string, ""),
architectures = optional(list(string), []),
operating_systems = optional(list(string), []),
usage = optional(string, ""),
logo_image_path = optional(string, null)
}) | `{}` | no |
+| [public\_repo\_policy\_path](#input\_public\_repo\_policy\_path) | Path to JSON policy file (optional). If specified, policy will be applied to public repository. | `string` | `null` | no |
+| [pullthrough\_cache\_rules](#input\_pullthrough\_cache\_rules) | List of custom pullthrough cache rules to apply to repository | list(object({
ecr_repository_prefix = optional(string, "ROOT")
upstream_repository_prefix = optional(string, "ROOT")
credential_arn = optional(string, null)
custom_role_arn = optional(string, null)
upstream_registry_url = string
})) | `[]` | no |
+| [quay\_pullthrough\_cache\_rule](#input\_quay\_pullthrough\_cache\_rule) | Pullthrough cache rule for Quay public registry. Override default values to customize | object({
enabled = optional(bool, false)
ecr_repository_prefix = optional(string, "ROOT")
upstream_repository_prefix = optional(string, "ROOT")
}) | `{}` | no |
+| [registry\_policy\_path](#input\_registry\_policy\_path) | Path to JSON policy file (optional). If specified, policy will be applied to registry | `string` | `null` | no |
+| [repo\_policy\_path](#input\_repo\_policy\_path) | Path to JSON policy file (optional). If specified, policy will be applied to repository | `string` | `null` | no |
+| [repository\_name](#input\_repository\_name) | Name of the repository | `string` | n/a | yes |
+| [scan\_images\_on\_push](#input\_scan\_images\_on\_push) | Whether images are scanned after being pushed to the repository (true) or not scanned (false). Default is true. | `bool` | `true` | no |
+| [tags](#input\_tags) | Tags to be applied to resources | `map(string)` | `{}` | no |
+| [use\_default\_ecnryption\_key](#input\_use\_default\_ecnryption\_key) | Whether to use default ECR encryption key (defaults to true) | `bool` | `true` | no |
+| [use\_default\_image\_lifecycle\_policy](#input\_use\_default\_image\_lifecycle\_policy) | Whether to use default image lifecycle or not. Defaults to true. | `bool` | `true` | no |
+| [visibility](#input\_visibility) | Visibility of the repository. Allowed values are 'PRIVATE' (default) and 'PUBLIC'. | `string` | `"PRIVATE"` | no |
+
+## Outputs
+
+No outputs.
+
+## Examples
+
+Examples configuration for using the module:
+
+```hcl
+
+module "easy_ecr" {
+ source = ""https://github.com/bitshifted/cloud-tools//easy-ecr?ref=easy-ecr-"
+}
+```
+
\ No newline at end of file
diff --git a/easy-ecr/cache.tf b/easy-ecr/cache.tf
new file mode 100644
index 0000000..9dea563
--- /dev/null
+++ b/easy-ecr/cache.tf
@@ -0,0 +1,48 @@
+# Copyright 2025 Bitshift
+# SPDX-License-Identifier: MPL-2.0
+
+
+
+locals {
+ default_cache_rules = {
+ aws_public = merge(var.aws_public_pullthrough_cache_rule, {
+ upstream_registry_url = "public.ecr.aws"
+ })
+ k8s_public = merge(var.k8s_pullthrough_cache_rule, {
+ upstream_registry_url = "registry.k8s.io"
+ })
+ quay = merge(var.quay_pullthrough_cache_rule, {
+ upstream_registry_url = "quay.io"
+ })
+ docker_hub = merge(var.docker_hub_pullthrough_cache_rule, {
+ upstream_registry_url = "registry-1.docker.io"
+ })
+ github = merge(var.github_cr_pullthrough_cache_rule, {
+ upstream_registry_url = "ghcr.io"
+ })
+ gitlab = merge(var.gitlab_cr_pullthrough_cache_rule, {
+ upstream_registry_url = "registry.gitlab.com"
+ })
+ }
+
+}
+
+resource "aws_ecr_pull_through_cache_rule" "default_pullthrough_cache_rule" {
+ for_each = { for k, v in local.default_cache_rules : k => v if v.enabled == true }
+ region = var.ecr_region != null ? var.ecr_region : data.aws_region.current_region.region
+ ecr_repository_prefix = each.value.ecr_repository_prefix
+ credential_arn = can(each.value.credential_arn) ? each.value.credential_arn : null
+ custom_role_arn = can(each.value.custom_role_arn) ? each.value.custom_role_arn : null
+ upstream_registry_url = each.value.upstream_registry_url
+ upstream_repository_prefix = each.value.upstream_repository_prefix
+}
+
+resource "aws_ecr_pull_through_cache_rule" "custom_pullthrough_cache_rule" {
+ for_each = { for k, v in var.pullthrough_cache_rules : k => v }
+ region = var.ecr_region != null ? var.ecr_region : data.aws_region.current_region.region
+ ecr_repository_prefix = each.value.ecr_repository_prefix
+ credential_arn = can(each.value.credential_arn) ? each.value.credential_arn : null
+ custom_role_arn = can(each.value.custom_role_arn) ? each.value.custom_role_arn : null
+ upstream_registry_url = each.value.upstream_registry_url
+ upstream_repository_prefix = each.value.upstream_repository_prefix
+}
\ No newline at end of file
diff --git a/easy-ecr/default-lifecycle-policy.json b/easy-ecr/default-lifecycle-policy.json
new file mode 100644
index 0000000..78e2b28
--- /dev/null
+++ b/easy-ecr/default-lifecycle-policy.json
@@ -0,0 +1,30 @@
+{
+ "rules": [
+ {
+ "rulePriority": 10,
+ "description": "Untagged images policy",
+ "selection": {
+ "tagStatus": "untagged",
+ "countType": "sinceImagePushed",
+ "countUnit": "days",
+ "countNumber": 30
+ },
+ "action": {
+ "type": "expire"
+ }
+ },
+ {
+ "rulePriority": 20,
+ "description": "Any image policy",
+ "selection": {
+ "tagStatus": "tagged",
+ "tagPatternList": ["*"],
+ "countType": "imageCountMoreThan",
+ "countNumber": 10
+ },
+ "action": {
+ "type": "expire"
+ }
+ }
+ ]
+}
diff --git a/easy-ecr/docs/examples.md b/easy-ecr/docs/examples.md
new file mode 100644
index 0000000..ee045e0
--- /dev/null
+++ b/easy-ecr/docs/examples.md
@@ -0,0 +1,10 @@
+## Examples
+
+Examples configuration for using the module:
+
+```hcl
+
+module "easy_ecr" {
+ source = ""https://github.com/bitshifted/cloud-tools//easy-ecr?ref=easy-ecr-"
+}
+```
\ No newline at end of file
diff --git a/easy-ecr/docs/intro.md b/easy-ecr/docs/intro.md
new file mode 100644
index 0000000..436afdf
--- /dev/null
+++ b/easy-ecr/docs/intro.md
@@ -0,0 +1,3 @@
+# Easy ECR
+
+This Terraform module provides production-ready ECR repository for storing container images.
\ No newline at end of file
diff --git a/easy-ecr/main.tf b/easy-ecr/main.tf
new file mode 100644
index 0000000..f7c1494
--- /dev/null
+++ b/easy-ecr/main.tf
@@ -0,0 +1,65 @@
+# Copyright 2025 Bitshift
+# SPDX-License-Identifier: MPL-2.0
+
+
+
+locals {
+ resolved_region = var.ecr_region != null ? var.ecr_region : data.aws_region.current_region.region
+ should_create_kms_key = (!var.use_default_ecnryption_key && var.encryption_key_arn == null) ? true : false
+ image_tag_mutability = var.image_tag_mutable ? (length(var.mutability_exclusion_filters) > 0 ? "MUTABLE_WITH_EXCLUSION" : "MUTABLE") : (length(var.mutability_exclusion_filters) > 0 ? "IMMUTABLE_WITH_EXCLUSION" : "IMUTABLE")
+}
+
+data "aws_region" "current_region" {}
+
+resource "aws_ecr_repository" "ecr_private_repo" {
+ count = var.visibility == "PRIVATE" ? 1 : 0
+ name = var.repository_name
+ region = local.resolved_region
+ force_delete = var.force_delete
+ image_tag_mutability = local.image_tag_mutability
+
+ #checkov:skip=CKV_AWS_136: Using AWS-managed AES256 is acceptable if user enables it
+ encryption_configuration {
+ encryption_type = var.use_default_ecnryption_key ? "AES256" : "KMS"
+ kms_key = !var.use_default_ecnryption_key ? var.encryption_key_arn != null ? var.encryption_key_arn : aws_kms_key.domain_encryption_key[0].arn : null
+ }
+
+ dynamic "image_tag_mutability_exclusion_filter" {
+ for_each = var.mutability_exclusion_filters
+ content {
+ filter = image_tag_mutability_exclusion_filter.value
+ filter_type = "WILDCARD"
+ }
+ }
+
+ image_scanning_configuration {
+ scan_on_push = var.scan_images_on_push
+ }
+
+ tags = var.tags
+}
+
+resource "aws_ecrpublic_repository" "ecr_public_repo" {
+ count = var.visibility == "PUBLIC" ? 1 : 0
+ repository_name = var.repository_name
+ region = local.resolved_region
+
+ catalog_data {
+ about_text = var.public_catalog_data.about
+ architectures = var.public_catalog_data.architectures
+ description = var.public_catalog_data.description
+ logo_image_blob = var.public_catalog_data.logo_image_path != null ? filebase64(var.public_catalog_data.logo_image_path) : null
+ operating_systems = var.public_catalog_data.operating_systems
+ usage_text = var.public_catalog_data.usage
+ }
+
+ tags = var.tags
+}
+
+resource "aws_kms_key" "domain_encryption_key" {
+ count = local.should_create_kms_key ? 1 : 0
+ description = "KMS key for ECR repository domain ${var.repository_name}"
+ enable_key_rotation = true
+ policy = var.domain_encryption_key_policy_path != null ? file(var.domain_encryption_key_policy_path) : null
+ tags = var.tags
+}
\ No newline at end of file
diff --git a/easy-ecr/policies.tf b/easy-ecr/policies.tf
new file mode 100644
index 0000000..f14aa4b
--- /dev/null
+++ b/easy-ecr/policies.tf
@@ -0,0 +1,34 @@
+# Copyright 2025 Bitshift
+# SPDX-License-Identifier: MPL-2.0
+
+locals {
+ use_any_lifecycle_policy = var.visibility == "PRIVATE" && (var.use_default_image_lifecycle_policy || var.image_lifecycle_policy_path != null)
+ apply_default_lifecycle_policy = var.use_default_image_lifecycle_policy && var.image_lifecycle_policy_path == null
+}
+
+resource "aws_ecr_registry_policy" "registry_policy" {
+ count = var.registry_policy_path != null ? 1 : 0
+ region = var.ecr_region != null ? var.ecr_region : data.aws_region.current_region.region
+ policy = file(var.registry_policy_path)
+}
+
+resource "aws_ecr_repository_policy" "repo_policy" {
+ count = var.repo_policy_path != null ? 1 : 0
+ region = var.ecr_region != null ? var.ecr_region : data.aws_region.current_region.region
+ repository = aws_ecr_repository.ecr_private_repo[0].name
+ policy = file(var.repo_policy_path)
+}
+
+resource "aws_ecr_lifecycle_policy" "repo_lifecycle_policy" {
+ count = local.use_any_lifecycle_policy ? 1 : 0
+ region = var.ecr_region != null ? var.ecr_region : data.aws_region.current_region.region
+ repository = aws_ecr_repository.ecr_private_repo[0].name
+ policy = local.apply_default_lifecycle_policy ? file("${path.module}/default-lifecycle-policy.json") : file(var.image_lifecycle_policy_path)
+}
+
+resource "aws_ecrpublic_repository_policy" "public_repo_policy" {
+ count = var.public_repo_policy_path != null ? 1 : 0
+ region = var.ecr_region != null ? var.ecr_region : data.aws_region.current_region.region
+ repository_name = aws_ecrpublic_repository.ecr_public_repo[0].id
+ policy = file(var.public_repo_policy_path)
+}
\ No newline at end of file
diff --git a/easy-ecr/providers.tf b/easy-ecr/providers.tf
new file mode 100644
index 0000000..cd6a56b
--- /dev/null
+++ b/easy-ecr/providers.tf
@@ -0,0 +1,12 @@
+# Copyright 2025 Bitshift
+# SPDX-License-Identifier: MPL-2.0
+
+terraform {
+ required_version = ">= 1.14.0"
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 6.21.0"
+ }
+ }
+}
\ No newline at end of file
diff --git a/easy-ecr/scan_config.tf b/easy-ecr/scan_config.tf
new file mode 100644
index 0000000..63c3270
--- /dev/null
+++ b/easy-ecr/scan_config.tf
@@ -0,0 +1,8 @@
+# Copyright 2025 Bitshift
+# SPDX-License-Identifier: MPL-2.0
+
+
+resource "aws_ecr_account_setting" "account_scan_config" {
+ name = var.default_account_scan_config.name
+ value = var.default_account_scan_config.value
+}
\ No newline at end of file
diff --git a/easy-ecr/tests/cache_rules.tftest.hcl b/easy-ecr/tests/cache_rules.tftest.hcl
new file mode 100644
index 0000000..feb2383
--- /dev/null
+++ b/easy-ecr/tests/cache_rules.tftest.hcl
@@ -0,0 +1,257 @@
+# Copyright 2025 Bitshift
+# SPDX-License-Identifier: MPL-2.0
+
+mock_provider "aws" {
+
+}
+
+run "no_rules_should_be_created_by_default" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ }
+
+ assert {
+ condition = length(aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule) == 0
+ error_message = "Pullthrough cache rule was created, but should not be"
+ }
+}
+
+run "rule_override_creates_aws_public_rule" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ aws_public_pullthrough_cache_rule = {
+ enabled = true
+ upstream_repository_prefix = "my-prefix"
+ }
+ }
+
+ assert {
+ condition = length(aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule) == 1
+ error_message = "Pullthrough cache rule was not created, but should be"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["aws_public"].ecr_repository_prefix == "ROOT"
+ error_message = "Invalid ECR repository prefix"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["aws_public"].upstream_repository_prefix == "my-prefix"
+ error_message = "Invalid upstream repository prefix"
+ }
+}
+
+run "rule_override_creates_k8s_public_rule" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ k8s_pullthrough_cache_rule = {
+ enabled = true
+ upstream_repository_prefix = "my-prefix"
+ }
+ }
+
+ assert {
+ condition = length(aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule) == 1
+ error_message = "Pullthrough cache rule was not created, but should be"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["k8s_public"].ecr_repository_prefix == "ROOT"
+ error_message = "Invalid ECR repository prefix"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["k8s_public"].upstream_repository_prefix == "my-prefix"
+ error_message = "Invalid upstream repository prefix"
+ }
+}
+
+run "rule_override_creates_quay_rule" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ quay_pullthrough_cache_rule = {
+ enabled = true
+ upstream_repository_prefix = "my-prefix"
+ }
+ }
+
+ assert {
+ condition = length(aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule) == 1
+ error_message = "Pullthrough cache rule was not created, but should be"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["quay"].ecr_repository_prefix == "ROOT"
+ error_message = "Invalid ECR repository prefix"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["quay"].upstream_repository_prefix == "my-prefix"
+ error_message = "Invalid upstream repository prefix"
+ }
+}
+
+run "rule_override_creates_docker_hub_rule" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ docker_hub_pullthrough_cache_rule = {
+ enabled = true
+ upstream_repository_prefix = "my-prefix"
+ credential_arn = "arn:aws:iam::123456789012:role/my-role"
+ }
+ }
+
+ assert {
+ condition = length(aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule) == 1
+ error_message = "Pullthrough cache rule was not created, but should be"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["docker_hub"].ecr_repository_prefix == "ROOT"
+ error_message = "Invalid ECR repository prefix"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["docker_hub"].upstream_repository_prefix == "my-prefix"
+ error_message = "Invalid upstream repository prefix"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["docker_hub"].credential_arn == "arn:aws:iam::123456789012:role/my-role"
+ error_message = "Invalid credential ARN"
+ }
+}
+
+run "rule_override_creates_github_cr_rule" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ github_cr_pullthrough_cache_rule = {
+ enabled = true
+ upstream_repository_prefix = "my-prefix"
+ credential_arn = "arn:aws:iam::123456789012:role/my-role"
+ }
+ }
+
+ assert {
+ condition = length(aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule) == 1
+ error_message = "Pullthrough cache rule was not created, but should be"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["github"].ecr_repository_prefix == "ROOT"
+ error_message = "Invalid ECR repository prefix"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["github"].upstream_repository_prefix == "my-prefix"
+ error_message = "Invalid upstream repository prefix"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["github"].credential_arn == "arn:aws:iam::123456789012:role/my-role"
+ error_message = "Invalid credential ARN"
+ }
+}
+
+run "rule_override_creates_gitlab_cr_rule" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ gitlab_cr_pullthrough_cache_rule = {
+ enabled = true
+ upstream_repository_prefix = "my-prefix"
+ credential_arn = "arn:aws:iam::123456789012:role/my-role"
+ }
+ }
+
+ assert {
+ condition = length(aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule) == 1
+ error_message = "Pullthrough cache rule was not created, but should be"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["gitlab"].ecr_repository_prefix == "ROOT"
+ error_message = "Invalid ECR repository prefix"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["gitlab"].upstream_repository_prefix == "my-prefix"
+ error_message = "Invalid upstream repository prefix"
+ }
+
+ assert {
+ condition = aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule["gitlab"].credential_arn == "arn:aws:iam::123456789012:role/my-role"
+ error_message = "Invalid credential ARN"
+ }
+}
+
+run "custom_rule_should_be_created_when_specified" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ pullthrough_cache_rules = [
+ {
+ upstream_repository_prefix = "my-prefix"
+ upstream_registry_url = "my-registry.example.com"
+ }
+ ]
+ }
+
+ assert {
+ condition = length(aws_ecr_pull_through_cache_rule.custom_pullthrough_cache_rule) == 1
+ error_message = "Custom rule not created, but should be"
+ }
+}
+
+run "should_create_both_default_and_custom_rules" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ docker_hub_pullthrough_cache_rule = {
+ enabled = true
+ upstream_repository_prefix = "my-prefix"
+ credential_arn = "arn:aws:iam::123456789012:role/my-role"
+ }
+ github_cr_pullthrough_cache_rule = {
+ enabled = true
+ upstream_repository_prefix = "my-prefix"
+ credential_arn = "arn:aws:iam::123456789012:role/my-role"
+ }
+ pullthrough_cache_rules = [
+ {
+ upstream_repository_prefix = "my-prefix"
+ upstream_registry_url = "my-registry.example.com"
+ }
+ ]
+ }
+
+ assert {
+ condition = contains(keys(aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule),"docker_hub")
+ error_message = "Docker Hub rule not created, but should be"
+ }
+
+ assert {
+ condition = contains(keys(aws_ecr_pull_through_cache_rule.default_pullthrough_cache_rule),"github")
+ error_message = "Github rule not created, but should be"
+ }
+
+ assert {
+ condition = length(aws_ecr_pull_through_cache_rule.custom_pullthrough_cache_rule) == 1
+ error_message = "Custom rule not created, but should be"
+ }
+}
\ No newline at end of file
diff --git a/easy-ecr/tests/kms.tftest.hcl b/easy-ecr/tests/kms.tftest.hcl
new file mode 100644
index 0000000..150d868
--- /dev/null
+++ b/easy-ecr/tests/kms.tftest.hcl
@@ -0,0 +1,49 @@
+# Copyright 2025 Bitshift
+# SPDX-License-Identifier: MPL-2.0
+
+mock_provider "aws" {
+
+}
+
+run "kms_key_should_be_created_when_specified" {
+ command = plan
+
+ variables {
+ use_default_ecnryption_key = false
+ encryption_key_arn = null
+ repository_name = "test-repo"
+ }
+
+ assert {
+ condition = length(aws_kms_key.domain_encryption_key) == 1
+ error_message = "KMS key was not created when use_default_ecnryption_key is false and encryption_key_arn is null."
+ }
+}
+
+run "specific_kms_key_should_be_used_when_specified" {
+ command = plan
+
+ variables {
+ use_default_ecnryption_key = false
+ encryption_key_arn = "arn:aws:kms:us-west-2:123456789012:key/abcd-efgh-ijkl-mnop"
+ repository_name = "test-repo"
+ }
+
+ assert {
+ condition = length(aws_kms_key.domain_encryption_key) == 0
+ error_message = "KMS key was created despite encryption_key_arn being specified."
+ }
+}
+
+run "default_kms_key_should_be_used_when_specified" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ }
+
+ assert {
+ condition = length(aws_kms_key.domain_encryption_key) == 0
+ error_message = "KMS key was created despite use_default_ecnryption_key being true."
+ }
+}
\ No newline at end of file
diff --git a/easy-ecr/tests/policies.tftest.hcl b/easy-ecr/tests/policies.tftest.hcl
new file mode 100644
index 0000000..27cb75f
--- /dev/null
+++ b/easy-ecr/tests/policies.tftest.hcl
@@ -0,0 +1,117 @@
+# Copyright 2025 Bitshift
+# SPDX-License-Identifier: MPL-2.0
+
+mock_provider "aws" {
+
+}
+
+run "registry_policy_is_created_when_policy_file_path_provided" {
+ command = apply
+
+ variables {
+ repository_name = "test-repo"
+ registry_policy_path = "tests/test-policy.json"
+ }
+
+ assert {
+ condition = length(aws_ecr_registry_policy.registry_policy) == 1
+ error_message = "The registry policy was not created."
+ }
+
+ assert {
+
+ condition = aws_ecr_registry_policy.registry_policy[0].registry_id != ""
+ error_message = "reigstry_id was not set"
+ }
+
+}
+
+run "registry_policy_not_created_when_policy_file_path_not_provided" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ }
+
+ assert {
+ condition = length(aws_ecr_registry_policy.registry_policy) == 0
+ error_message = "The registry policy was created."
+ }
+
+}
+
+run "custom_repostitory_policy_should_be_created_when_policy_file_path_provided" {
+ command = apply
+
+ variables {
+ repository_name = "test-repo"
+ repo_policy_path = "tests/test-policy.json"
+ }
+
+ assert {
+ condition = length(aws_ecr_repository_policy.repo_policy) == 1
+ error_message = "The repository policy was not created."
+ }
+
+ assert {
+ condition = aws_ecr_repository_policy.repo_policy[0].registry_id != ""
+ error_message = "reigstry_id was not set"
+ }
+}
+
+run "custom_image_lifecycle_policy_should_be_created_when_policy_file_path_provided" {
+ command = apply
+
+ variables {
+ repository_name = "test-repo"
+ image_lifecycle_policy_path = "tests/test-policy.json"
+ }
+
+ assert {
+ condition = length(aws_ecr_lifecycle_policy.repo_lifecycle_policy) == 1
+ error_message = "The lifecycle policy was not created."
+ }
+}
+
+run "default_image_lifecycle_policy_should_be_created_when_policy_file_path_not_provided" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ }
+
+ assert {
+ condition = length(aws_ecr_lifecycle_policy.repo_lifecycle_policy) == 1
+ error_message = "The lifecycle policy was not created."
+ }
+}
+
+run "no_image_lifecycle_policy_when_policy_file_path_not_provided_and_default_policy_is_false" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ use_default_image_lifecycle_policy = false
+ }
+
+ assert {
+ condition = length(aws_ecr_lifecycle_policy.repo_lifecycle_policy) == 0
+ error_message = "The lifecycle policy was created."
+ }
+}
+
+run "public_repo_policy_should_be_created_when_policy_file_path_provided" {
+ command = apply
+
+ variables {
+ repository_name = "test-repo"
+ visibility = "PUBLIC"
+ public_repo_policy_path = "tests/test-policy.json"
+ }
+
+ assert {
+ condition = length(aws_ecrpublic_repository_policy.public_repo_policy) == 1
+ error_message = "The repository policy was not created."
+ }
+
+}
\ No newline at end of file
diff --git a/easy-ecr/tests/private_repo.tftest.hcl b/easy-ecr/tests/private_repo.tftest.hcl
new file mode 100644
index 0000000..4ffa604
--- /dev/null
+++ b/easy-ecr/tests/private_repo.tftest.hcl
@@ -0,0 +1,92 @@
+# Copyright 2025 Bitshift
+# SPDX-License-Identifier: MPL-2.0
+
+mock_provider "aws" {
+
+}
+
+run "invalid_encryption_key_arn_should_produce_error" {
+ command = plan
+
+ variables {
+ use_default_ecnryption_key = false
+ encryption_key_arn = "invalid-arn"
+ repository_name = "test-repo"
+ }
+ expect_failures = [
+ var.encryption_key_arn
+ ]
+}
+
+run "invalid_visibility_should_produce_error" {
+ command = plan
+
+ variables {
+ visibility = "INVALID"
+ repository_name = "test-repo"
+ }
+ expect_failures = [
+ var.visibility
+ ]
+}
+
+run "private_repository_is_created_correctly" {
+ command = plan
+
+ variables {
+ visibility = "PRIVATE"
+ repository_name = "private-repo"
+ scan_images_on_push = false
+ }
+
+ assert {
+ condition = aws_ecr_repository.ecr_private_repo[0].name == "private-repo"
+ error_message = "The private ECR repository was not created with the correct name."
+ }
+
+ assert {
+ condition = length(aws_ecrpublic_repository.ecr_public_repo) == 0
+ error_message = "A public ECR repository was created despite visibility being set to PRIVATE."
+ }
+
+ assert {
+ condition = !aws_ecr_repository.ecr_private_repo[0].image_scanning_configuration[0].scan_on_push
+ error_message = "Image scan on push is enabled"
+ }
+}
+
+run "image_tag_mutable_setting" {
+ command = plan
+
+ variables {
+ visibility = "PRIVATE"
+ repository_name = "mutable-repo"
+ image_tag_mutable = false
+ }
+
+ assert {
+ condition = aws_ecr_repository.ecr_private_repo[0].image_tag_mutability == "IMMUTABLE"
+ error_message = "The image tag mutability setting is incorrect."
+ }
+}
+
+run "image_tag_mutable_with_exclusion_filters" {
+ command = plan
+
+ variables {
+ visibility = "PRIVATE"
+ repository_name = "mutable-exclusion-repo"
+ image_tag_mutable = true
+ mutability_exclusion_filters = ["latest*", "stable*"]
+ }
+
+ assert {
+ condition = aws_ecr_repository.ecr_private_repo[0].image_tag_mutability == "MUTABLE_WITH_EXCLUSION"
+ error_message = "The image tag mutability with exclusion filters setting is incorrect."
+ }
+
+ assert {
+ condition = length(aws_ecr_repository.ecr_private_repo[0].image_tag_mutability_exclusion_filter) == 2
+ error_message = "Expected two image_tag_mutability_exclusion_filter blocks to be set."
+ }
+}
\ No newline at end of file
diff --git a/easy-ecr/tests/public_repo.tftest.hcl b/easy-ecr/tests/public_repo.tftest.hcl
new file mode 100644
index 0000000..99b1927
--- /dev/null
+++ b/easy-ecr/tests/public_repo.tftest.hcl
@@ -0,0 +1,72 @@
+# Copyright 2025 Bitshift
+# SPDX-License-Identifier: MPL-2.0
+
+mock_provider "aws" {
+
+}
+
+run "should_create_public_repository" {
+ command = plan
+
+ variables {
+ visibility = "PUBLIC"
+ repository_name = "public-repo"
+ }
+ assert {
+ condition = length(aws_ecrpublic_repository.ecr_public_repo) == 1
+ error_message = "The public ECR repository was not created with the correct name."
+ }
+}
+
+run "should_create_public_repo_with_catalog_data" {
+ command = plan
+
+ variables {
+ visibility = "PUBLIC"
+ repository_name = "public-repo"
+ public_catalog_data = {
+ about = "About text"
+ architectures = ["ARM 64", "x86-64"]
+ description = "descritpion text"
+ operating_systems = ["Linux"]
+ }
+ }
+ assert {
+ condition = aws_ecrpublic_repository.ecr_public_repo[0].catalog_data[0].about_text == "About text"
+ error_message = "Wrong about text"
+ }
+
+ assert {
+ condition = aws_ecrpublic_repository.ecr_public_repo[0].catalog_data[0].description == "descritpion text"
+ error_message = "Wrong description text"
+ }
+}
+
+run "should_fail_on_invalid_architectures_data" {
+ command = plan
+
+ variables {
+ visibility = "PUBLIC"
+ repository_name = "public-repo"
+ public_catalog_data = {
+ architectures = ["INVALID"]
+ }
+ }
+ expect_failures = [
+ var.public_catalog_data.architectures
+ ]
+}
+
+run "should_fail_on_invalie_operating_system" {
+ command = plan
+
+ variables {
+ visibility = "PUBLIC"
+ repository_name = "public-repo"
+ public_catalog_data = {
+ architectures = ["x86-64", "ARM 64"]
+ operating_systems = ["Mac Os"]
+ }
+ }
+ expect_failures = [ var.public_catalog_data.operating_systems ]
+}
\ No newline at end of file
diff --git a/easy-ecr/tests/scan_config.tftest.hcl b/easy-ecr/tests/scan_config.tftest.hcl
new file mode 100644
index 0000000..c2a942a
--- /dev/null
+++ b/easy-ecr/tests/scan_config.tftest.hcl
@@ -0,0 +1,46 @@
+# Copyright 2025 Bitshift
+# SPDX-License-Identifier: MPL-2.0
+
+mock_provider "aws" {
+
+}
+
+run "default_scan_configuration_should_be_created" {
+ command = plan
+
+ variables {
+ repository_name = "test-repo"
+ }
+
+ assert {
+ condition = aws_ecr_account_setting.account_scan_config.name == "BASIC_SCAN_TYPE_VERSION"
+ error_message = "Scan configuration resource was not created with correct name"
+ }
+
+ assert {
+ condition = aws_ecr_account_setting.account_scan_config.value == "AWS_NATIVE"
+ error_message = "Scan configuration resource was not created with correct value"
+ }
+}
+
+run "default_scan_config_is_overriden_when_specified" {
+ command = apply
+
+ variables {
+ repository_name = "test-repo"
+ default_account_scan_config = {
+ name = "BASIC_SCAN_TYPE_VERSION"
+ value = "CLAIR"
+ }
+ }
+
+ assert {
+ condition = aws_ecr_account_setting.account_scan_config.name == "BASIC_SCAN_TYPE_VERSION"
+ error_message = "Scan configuration resource was not created with correct name"
+ }
+
+ assert {
+ condition = aws_ecr_account_setting.account_scan_config.value == "CLAIR"
+ error_message = "Scan configuration resource was not created with correct value"
+ }
+}
diff --git a/easy-ecr/tests/test-policy.json b/easy-ecr/tests/test-policy.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/easy-ecr/tests/test-policy.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/easy-ecr/variables.tf b/easy-ecr/variables.tf
new file mode 100644
index 0000000..18ab90e
--- /dev/null
+++ b/easy-ecr/variables.tf
@@ -0,0 +1,220 @@
+# Copyright 2025 Bitshift
+# SPDX-License-Identifier: MPL-2.0
+
+
+variable "repository_name" {
+ type = string
+ description = "Name of the repository"
+}
+
+variable "ecr_region" {
+ type = string
+ default = null
+ description = "Region in which repositories will be managed. If not specified, defaults to region configured for provider"
+}
+
+variable "visibility" {
+ type = string
+ description = "Visibility of the repository. Allowed values are 'PRIVATE' (default) and 'PUBLIC'."
+ default = "PRIVATE"
+
+ validation {
+ condition = contains(["PRIVATE", "PUBLIC"], var.visibility)
+ error_message = "visibility must be either 'PRIVATE' or 'PUBLIC'."
+ }
+}
+
+variable "force_delete" {
+ type = bool
+ description = "If 'true', deletes repository even if it has contents"
+ default = false
+}
+
+
+variable "use_default_ecnryption_key" {
+ type = bool
+ default = true
+ description = "Whether to use default ECR encryption key (defaults to true)"
+}
+
+variable "encryption_key_arn" {
+ type = string
+ default = null
+ description = "ARN of KMS key used for repository encryption. If not specified, and use_default_ecnryption_key is false, creates new KMS key"
+
+ validation {
+ # If a value is provided for encryption_key_arn, it must be a valid KMS key ARN.
+ condition = (var.use_default_ecnryption_key == false && var.encryption_key_arn != null) ? can(regex("^arn:aws:kms:[a-z0-9-]*:[0-9]*:key\\/[0-9a-z-]+$", var.encryption_key_arn)) : true
+ error_message = "If provided, encryption_key_arn must be a valid KMS key ARN."
+ }
+}
+
+variable "domain_encryption_key_policy_path" {
+ type = string
+ default = null
+ description = " Local path to policy file to be applied to created KMS key. If not specified, no custom policy is applied."
+}
+
+variable "image_tag_mutable" {
+ type = bool
+ default = true
+ description = "Whether image tags are mutable. Only applicable for private repositories."
+}
+
+variable "mutability_exclusion_filters" {
+ type = list(string)
+ default = []
+ description = "List of tag prefixes to exclude from image tag mutability. Setting this will result in IMMUTABLE_WITH_EXLUSIONSo MUTABLE_WITH_EXCLUSION behavior. Only applicable for private repositories."
+}
+
+variable "scan_images_on_push" {
+ type = bool
+ default = true
+ description = "Whether images are scanned after being pushed to the repository (true) or not scanned (false). Default is true."
+}
+
+variable "public_catalog_data" {
+ type = object({
+ about = optional(string, ""),
+ description = optional(string, ""),
+ architectures = optional(list(string), []),
+ operating_systems = optional(list(string), []),
+ usage = optional(string, ""),
+ logo_image_path = optional(string, null)
+ })
+ default = {}
+ description = "Catalog data for public repositories (optional)"
+
+ validation {
+ condition = length(var.public_catalog_data.architectures) == 0 || length([for a in var.public_catalog_data.architectures : a if contains(["ARM", "x86", "ARM 64", "x86-64"], a)]) == length(var.public_catalog_data.architectures)
+ error_message = "Allowed values for architecture: 'ARM', 'ARM 64', 'x86', 'x86-64'"
+ }
+
+ validation {
+ condition = length(var.public_catalog_data.operating_systems) == 0 || length([for a in var.public_catalog_data.operating_systems : a if contains(["Windows", "Linux"], a)]) == length(var.public_catalog_data.operating_systems)
+ error_message = "Allowed values for operating system: 'Windows', 'Linux'"
+ }
+}
+
+variable "registry_policy_path" {
+ type = string
+ default = null
+ description = "Path to JSON policy file (optional). If specified, policy will be applied to registry"
+}
+
+variable "repo_policy_path" {
+ type = string
+ default = null
+ description = "Path to JSON policy file (optional). If specified, policy will be applied to repository"
+}
+
+variable "public_repo_policy_path" {
+ type = string
+ default = null
+ description = "Path to JSON policy file (optional). If specified, policy will be applied to public repository."
+}
+
+variable "image_lifecycle_policy_path" {
+ type = string
+ default = null
+ description = "Path to JSON file providing lifecycle policy for the repository"
+}
+
+variable "use_default_image_lifecycle_policy" {
+ type = bool
+ default = true
+ description = "Whether to use default image lifecycle or not. Defaults to true."
+}
+
+variable "aws_public_pullthrough_cache_rule" {
+ type = object({
+ enabled = optional(bool, false)
+ ecr_repository_prefix = optional(string, "ROOT")
+ upstream_repository_prefix = optional(string, "ROOT")
+ })
+ default = {}
+ description = "Pullthrough cache rule for AWS public registry. Override default values to customize"
+}
+
+variable "k8s_pullthrough_cache_rule" {
+ type = object({
+ enabled = optional(bool, false)
+ ecr_repository_prefix = optional(string, "ROOT")
+ upstream_repository_prefix = optional(string, "ROOT")
+ })
+ default = {}
+ description = "Pullthrough cache rule for Kubernetes public registry. Override default values to customize"
+}
+
+variable "quay_pullthrough_cache_rule" {
+ type = object({
+ enabled = optional(bool, false)
+ ecr_repository_prefix = optional(string, "ROOT")
+ upstream_repository_prefix = optional(string, "ROOT")
+ })
+ default = {}
+ description = "Pullthrough cache rule for Quay public registry. Override default values to customize"
+}
+
+variable "docker_hub_pullthrough_cache_rule" {
+ type = object({
+ enabled = optional(bool, false)
+ ecr_repository_prefix = optional(string, "ROOT")
+ upstream_repository_prefix = optional(string, "ROOT")
+ credential_arn = optional(string, null)
+ })
+ default = {}
+ description = "Pullthrough cache rule for Docker Hub registry. Override default values to customize"
+}
+
+variable "github_cr_pullthrough_cache_rule" {
+ type = object({
+ enabled = optional(bool, false)
+ ecr_repository_prefix = optional(string, "ROOT")
+ upstream_repository_prefix = optional(string, "ROOT")
+ credential_arn = optional(string, null)
+ })
+ default = {}
+ description = "Pullthrough cache rule for Github Container registry. Override default values to customize"
+}
+
+variable "gitlab_cr_pullthrough_cache_rule" {
+ type = object({
+ enabled = optional(bool, false)
+ ecr_repository_prefix = optional(string, "ROOT")
+ upstream_repository_prefix = optional(string, "ROOT")
+ credential_arn = optional(string, null)
+ })
+ default = {}
+ description = "Pullthrough cache rule for Gitlab Container registry. Override default values to customize"
+}
+
+variable "pullthrough_cache_rules" {
+ type = list(object({
+ ecr_repository_prefix = optional(string, "ROOT")
+ upstream_repository_prefix = optional(string, "ROOT")
+ credential_arn = optional(string, null)
+ custom_role_arn = optional(string, null)
+ upstream_registry_url = string
+ }))
+ default = []
+ description = "List of custom pullthrough cache rules to apply to repository"
+}
+
+variable "default_account_scan_config" {
+ type = object({
+ name = string
+ value = string
+ })
+ default = {
+ name = "BASIC_SCAN_TYPE_VERSION"
+ value = "AWS_NATIVE"
+ }
+ description = "Default ECR basic scan type configuration."
+}
+
+variable "tags" {
+ type = map(string)
+ default = {}
+ description = "Tags to be applied to resources"
+}