diff --git a/modules/ecr-registry/main.tf b/modules/ecr-registry/main.tf index a78b236..e6f9228 100644 --- a/modules/ecr-registry/main.tf +++ b/modules/ecr-registry/main.tf @@ -3,7 +3,7 @@ locals { package = "terraform-aws-container" version = trimspace(file("${path.module}/../../VERSION")) module = basename(path.module) - name = data.aws_caller_identity.this.id + name = local.account_id } module_tags = { "module.terraform.io/package" = local.metadata.package @@ -27,156 +27,21 @@ locals { # Registry Policy ################################################### -data "aws_iam_policy_document" "replication" { - count = length(try(var.replication_policies, [])) > 0 ? 1 : 0 - - dynamic "statement" { - for_each = try(var.replication_policies, []) - - content { - sid = "ReplicationAccess${statement.value.account_id}" - effect = "Allow" - principals { - type = "AWS" - identifiers = [ - "arn:aws:iam::${statement.value.account_id}:root" - ] - } - actions = compact([ - try(statement.value.allow_create_repository, true) ? "ecr:CreateRepository" : null, - "ecr:ReplicateImage", - ]) - resources = [ - for repository in statement.value.repositories : - "arn:aws:ecr:${local.region}:${local.account_id}:repository/${repository}" - ] - } - } -} - -data "aws_iam_policy_document" "pull_through_cache" { - count = length(try(var.pull_through_cache_policies, [])) > 0 ? 1 : 0 - - dynamic "statement" { - for_each = try(var.pull_through_cache_policies, []) - - content { - sid = "PullThroughCacheAccess-${statement.key}" - effect = "Allow" - principals { - type = "AWS" - identifiers = try(statement.value.iam_entities, []) - } - actions = compact([ - try(statement.value.allow_create_repository, true) ? "ecr:CreateRepository" : null, - "ecr:BatchImportUpstreamImage", - ]) - resources = [ - for repository in statement.value.repositories : - "arn:aws:ecr:${local.region}:${local.account_id}:repository/${repository}" - ] - } - } -} - data "aws_iam_policy_document" "this" { - source_policy_documents = concat( - try(data.aws_iam_policy_document.replication[*].json, []), - try(data.aws_iam_policy_document.pull_through_cache[*].json, []), - ) + source_policy_documents = compact([ + one(data.aws_iam_policy_document.replication[*].json), + one(data.aws_iam_policy_document.pull_through_cache[*].json), + ]) override_policy_documents = var.policy != null ? [var.policy] : null } resource "aws_ecr_registry_policy" "this" { - count = length(var.replication_policies) > 0 || length(var.pull_through_cache_policies) > 0 || var.policy != null ? 1 : 0 + count = anytrue([ + length(var.replication_policies) > 0, + length(var.pull_through_cache_policies) > 0, + var.policy != null, + ]) ? 1 : 0 policy = data.aws_iam_policy_document.this.json } - - -################################################### -# Replication Configurations -################################################### - -resource "aws_ecr_replication_configuration" "this" { - count = length(var.replication_destinations) > 0 ? 1 : 0 - - replication_configuration { - rule { - dynamic "destination" { - for_each = var.replication_destinations - - content { - registry_id = destination.value.registry_id - region = destination.value.region - } - } - } - } -} - - -################################################### -# Pull Through Cache Rule -################################################### - -locals { - default_namespaces = { - "quay.io" = "quay" - "public.ecr.aws" = "ecr-public" - } -} - -resource "aws_ecr_pull_through_cache_rule" "this" { - for_each = { - for rule in var.pull_through_cache_rules : - try(rule.namespace, local.default_namespaces[rule.upstream_url]) => rule - } - - ecr_repository_prefix = each.key - upstream_registry_url = each.value.upstream_url -} - - -################################################### -# Scanning Configuration -################################################### - -resource "aws_ecr_registry_scanning_configuration" "this" { - scan_type = var.scanning_type - - dynamic "rule" { - for_each = length(var.scanning_on_push_filters) > 0 ? ["go"] : [] - - content { - scan_frequency = "SCAN_ON_PUSH" - - dynamic "repository_filter" { - for_each = var.scanning_on_push_filters - - content { - filter = repository_filter.value - filter_type = "WILDCARD" - } - } - } - } - - dynamic "rule" { - for_each = length(var.scanning_continuous_filters) > 0 ? ["go"] : [] - - content { - scan_frequency = "CONTINUOUS_SCAN" - - dynamic "repository_filter" { - for_each = var.scanning_continuous_filters - - content { - filter = repository_filter.value - filter_type = "WILDCARD" - } - } - } - } -} diff --git a/modules/ecr-registry/outputs.tf b/modules/ecr-registry/outputs.tf index 16278e2..52f88fb 100644 --- a/modules/ecr-registry/outputs.tf +++ b/modules/ecr-registry/outputs.tf @@ -13,33 +13,40 @@ output "policy" { value = one(aws_ecr_registry_policy.this[*].policy) } -output "replication_destinations" { - description = "A list of destinations for ECR registry replication." - value = var.replication_destinations +output "replication_policies" { + description = "A list of replication policies for ECR Registry." + value = var.replication_policies +} + +output "replication_rules" { + description = "A list of replication rules for ECR Registry." + value = var.replication_rules +} + +output "pull_through_cache_policies" { + description = "A list of Pull Through Cache policies for ECR Registry." + value = var.pull_through_cache_policies } output "pull_through_cache_rules" { description = "A list of Pull Through Cache Rules for ECR registry." - value = [ - for id, rule in aws_ecr_pull_through_cache_rule.this : { - id = id - namespace = rule.ecr_repository_prefix - upstream_url = rule.upstream_registry_url - } - ] + value = var.pull_through_cache_rules } output "scanning_type" { - description = "The scanning type for the registry." + description = "The scanning type to set for the registry." value = aws_ecr_registry_scanning_configuration.this.scan_type } -output "scanning_on_push_filters" { - description = "A list of repository filter to scan on push." - value = var.scanning_on_push_filters +output "scanning_rules" { + description = "A list of scanning rules to determine which repository filters are used and at what frequency scanning will occur." + value = var.scanning_rules } -output "scanning_continuous_filters" { - description = "A list of repository filter to scan continuous." - value = var.scanning_continuous_filters -} +# output "debug" { +# value = { +# pull_through_cache_rules = aws_ecr_pull_through_cache_rule.this +# replication_rules = aws_ecr_replication_configuration.this +# scanning_rules = aws_ecr_registry_scanning_configuration.this +# } +# } diff --git a/modules/ecr-registry/pull-through-cache.tf b/modules/ecr-registry/pull-through-cache.tf new file mode 100644 index 0000000..1435ad0 --- /dev/null +++ b/modules/ecr-registry/pull-through-cache.tf @@ -0,0 +1,62 @@ +################################################### +# Pull Through Cache Policy +################################################### + +data "aws_iam_policy_document" "pull_through_cache" { + count = length(var.pull_through_cache_policies) > 0 ? 1 : 0 + + dynamic "statement" { + for_each = var.pull_through_cache_policies + iterator = policy + + content { + sid = "PullThroughCacheAccess-${policy.key}" + effect = "Allow" + + principals { + type = "AWS" + identifiers = policy.value.iam_entities + } + actions = (policy.value.allow_create_repository + ? ["ecr:CreateRepository", "ecr:BatchImportUpstreamImage"] + : ["ecr:BatchImportUpstreamImage"] + ) + resources = [ + for repository in policy.value.repositories : + "arn:aws:ecr:${local.region}:${local.account_id}:repository/${repository}" + ] + } + } +} + + +################################################### +# Pull Through Cache Rules +################################################### + +locals { + default_namespaces = { + "ghcr.io" = "github" + "myregistry.azurecr.io" = "azure" + "public.ecr.aws" = "ecr-public" + "quay.io" = "quay" + "registry-1.docker.io" = "docker-hub" + "registry.gitlab.com" = "gitlab" + "registry.k8s.io" = "kubernetes" + } +} + +resource "aws_ecr_pull_through_cache_rule" "this" { + for_each = { + for rule in var.pull_through_cache_rules : + coalesce(rule.namespace, local.default_namespaces[rule.upstream_url]) => rule + } + + ecr_repository_prefix = each.key + upstream_registry_url = each.value.upstream_url + + credential_arn = (each.value.credential != null + ? each.value.credential.secretsmanager_secret + : null + ) +} diff --git a/modules/ecr-registry/replication.tf b/modules/ecr-registry/replication.tf new file mode 100644 index 0000000..88f693d --- /dev/null +++ b/modules/ecr-registry/replication.tf @@ -0,0 +1,69 @@ +################################################### +# Replication Policy +################################################### + +data "aws_iam_policy_document" "replication" { + count = length(var.replication_policies) > 0 ? 1 : 0 + + dynamic "statement" { + for_each = var.replication_policies + iterator = policy + + content { + sid = "ReplicationAccess${policy.value.account}" + effect = "Allow" + + principals { + type = "AWS" + identifiers = [ + "arn:aws:iam::${policy.value.account}:root" + ] + } + actions = (policy.value.allow_create_repository + ? ["ecr:CreateRepository", "ecr:ReplicateImage"] + : ["ecr:ReplicateImage"] + ) + resources = [ + for repository in policy.value.repositories : + "arn:aws:ecr:${local.region}:${local.account_id}:repository/${repository}" + ] + } + } +} + + +################################################### +# Replication Rules +################################################### + +resource "aws_ecr_replication_configuration" "this" { + count = length(var.replication_rules) > 0 ? 1 : 0 + + replication_configuration { + dynamic "rule" { + for_each = var.replication_rules + iterator = rule + + content { + dynamic "destination" { + for_each = rule.value.destinations + + content { + registry_id = coalesce(destination.value.account, local.account_id) + region = destination.value.region + } + } + + dynamic "repository_filter" { + for_each = rule.value.filters + iterator = filter + + content { + filter_type = filter.value.type + filter = filter.value.value + } + } + } + } + } +} diff --git a/modules/ecr-registry/scanning.tf b/modules/ecr-registry/scanning.tf new file mode 100644 index 0000000..ef36335 --- /dev/null +++ b/modules/ecr-registry/scanning.tf @@ -0,0 +1,25 @@ +################################################### +# Scanning Configuration +################################################### + +resource "aws_ecr_registry_scanning_configuration" "this" { + scan_type = var.scanning_type + + dynamic "rule" { + for_each = var.scanning_rules + + content { + scan_frequency = rule.value.frequency + + dynamic "repository_filter" { + for_each = rule.value.filters + iterator = filter + + content { + filter_type = filter.value.type + filter = filter.value.value + } + } + } + } +} diff --git a/modules/ecr-registry/variables.tf b/modules/ecr-registry/variables.tf index 75b5fb9..93524af 100644 --- a/modules/ecr-registry/variables.tf +++ b/modules/ecr-registry/variables.tf @@ -2,39 +2,70 @@ variable "policy" { description = "(Optional) The policy document for ECR registry. This is a JSON formatted string." type = string default = null + nullable = true } variable "replication_policies" { - description = "(Optional) A list of ECR Registry Policies for replication. `account_id` is source AWS account for replication. If `allow_create_repository` is false, you need to create repositories with the same name whithin your registry. `repositories` is a list of target repositories. Support glob expressions for `repositories` like `*`." + description = <