diff --git a/_example/example.tf b/_example/example.tf index 72b1596..941253f 100644 --- a/_example/example.tf +++ b/_example/example.tf @@ -1,17 +1,34 @@ provider "aws" { - region = "eu-west-2" + region = "us-east-1" } +module "private_ecr" { + source = "./../" + + enable_private_ecr = true + name = "private-ecr" + environment = "test" + label_order = ["environment", "name"] + scan_on_push = true + max_image_count = 7 + image_tag_mutability = "IMMUTABLE" + encryption_type = "KMS" +} -module "ecr" { +module "public_ecr" { source = "./../" - name = "ecr" - environment = "test" - label_order = ["environment", "name"] - scan_on_push = true - max_image_count = 7 - image_tag_mutability = "IMMUTABLE" - image_scanning_configuration = {} + enable_public_ecr = true + name = "public-ecr" + environment = "test" + label_order = ["environment", "name"] -} + max_untagged_image_count = 1 + max_image_count = 7 + + public_repository_catalog_data = { + description = "Docker container for some things" + operating_systems = ["Linux"] + architectures = ["x86"] + } +} \ No newline at end of file diff --git a/_example/outputs.tf b/_example/outputs.tf index e4d2dca..a923bde 100644 --- a/_example/outputs.tf +++ b/_example/outputs.tf @@ -1,19 +1,45 @@ -output "arn" { - value = module.ecr.*.arn +######################################################################## +# Private ECR +######################################################################## +output "private_ecr_arn" { + value = module.private_ecr.*.arn description = "Registry name." } -output "tags" { - value = module.ecr.tags +output "private_ecr_tags" { + value = module.private_ecr.tags description = "A mapping of tags to assign to the alb." } -output "registry_id" { - value = module.ecr.registry_id +output "private_ecr_registry_id" { + value = module.private_ecr.registry_id description = "The registry ID where the repository was created." } -output "registry_url" { - value = module.ecr.registry_url +output "private_ecr_registry_url" { + value = module.private_ecr.registry_url + description = "The URL of the repository (in the form." +} + +######################################################################## +# Public ECR +######################################################################## +output "public_ecr_arn" { + value = module.public_ecr.*.arn + description = "Registry name." +} + +output "public_ecr_tags" { + value = module.public_ecr.tags + description = "A mapping of tags to assign to the alb." +} + +output "public_ecr_registry_id" { + value = module.public_ecr.registry_id + description = "The registry ID where the repository was created." +} + +output "public_ecr_registry_url" { + value = module.public_ecr.registry_url description = "The URL of the repository (in the form." } \ No newline at end of file diff --git a/main.tf b/main.tf index 470e447..bf98857 100755 --- a/main.tf +++ b/main.tf @@ -1,5 +1,5 @@ ## Managed By : CloudDrove -## Description : This Script is used to create Aws ECR repository and policy. +## Description : This Script is used to create AWS Public and Private ECR repository and corressponding IAM Policy. ## Copyright @ CloudDrove. All Right Reserved. locals { @@ -21,30 +21,34 @@ module "labels" { environment = var.environment managedby = var.managedby label_order = var.label_order + attributes = var.attributes + delimiter = var.delimiter + extra_tags = var.tags } # Module : ECR REPOSITORY # Description : Provides an Elastic Container Registry Repository. #tfsec:ignore:aws-ecr-enable-image-scans + +################################################################################ +# Private Repository +################################################################################ resource "aws_ecr_repository" "default" { - count = var.enabled_ecr ? 1 : 0 - name = module.labels.id + count = var.enable_private_ecr ? 1 : 0 + name = var.use_fullname != "" ? var.use_fullname : module.labels.id tags = module.labels.tags image_tag_mutability = var.image_tag_mutability + force_delete = var.repository_force_delete - dynamic "encryption_configuration" { - for_each = var.encryption_configuration != null ? [var.encryption_configuration] : [] - content { - encryption_type = lookup(encryption_configuration.value.encryption_type, null) - kms_key = lookup(encryption_configuration.value.kms_key, null) - } + encryption_configuration { + encryption_type = var.encryption_type + kms_key = var.kms_key } - dynamic "image_scanning_configuration" { - for_each = var.image_scanning_configuration - content { - scan_on_push = lookup(image_scanning_configuration.value.scan_on_push, null) - } + + image_scanning_configuration { + scan_on_push = var.scan_on_push } + dynamic "timeouts" { for_each = var.timeouts content { @@ -53,8 +57,8 @@ resource "aws_ecr_repository" "default" { } } -resource "aws_ecr_lifecycle_policy" "default" { - count = var.enabled_ecr ? 1 : 0 +resource "aws_ecr_lifecycle_policy" "private" { + count = var.enable_private_ecr ? 1 : 0 repository = join("", aws_ecr_repository.default.*.name) policy = < 0 ? [var.public_repository_catalog_data] : [] + + content { + about_text = try(catalog_data.value.about_text, null) + architectures = try(catalog_data.value.architectures, null) + description = try(catalog_data.value.description, null) + logo_image_blob = try(catalog_data.value.logo_image_blob, null) + operating_systems = try(catalog_data.value.operating_systems, null) + usage_text = try(catalog_data.value.usage_text, null) + } + } + tags = module.labels.tags } -data "aws_iam_policy_document" "resource_readonly_access" { +################################################################################ +# Private ECR IAM Policies +################################################################################ +data "aws_iam_policy_document" "resource_readonly_access_private" { statement { sid = "ReadonlyAccess" effect = "Allow" @@ -104,19 +131,29 @@ data "aws_iam_policy_document" "resource_readonly_access" { } actions = [ - "ecr:GetAuthorizationToken", - "ecr:BatchCheckLayerAvailability", + "ecr:GetRegistryPolicy", + "ecr:DescribeImageScanFindings", + "ecr:GetLifecyclePolicyPreview", "ecr:GetDownloadUrlForLayer", - "ecr:GetRepositoryPolicy", - "ecr:DescribeRepositories", + "ecr:DescribeRegistry", + "ecr:DescribePullThroughCacheRules", + "ecr:DescribeImageReplicationStatus", + "ecr:GetAuthorizationToken", + "ecr:ListTagsForResource", "ecr:ListImages", - "ecr:DescribeImages", + "ecr:BatchGetRepositoryScanningConfiguration", + "ecr:GetRegistryScanningConfiguration", "ecr:BatchGetImage", + "ecr:DescribeImages", + "ecr:DescribeRepositories", + "ecr:BatchCheckLayerAvailability", + "ecr:GetRepositoryPolicy", + "ecr:GetLifecyclePolicy", ] } } -data "aws_iam_policy_document" "resource_full_access" { +data "aws_iam_policy_document" "resource_full_access_private" { statement { sid = "FullAccess" effect = "Allow" @@ -128,32 +165,78 @@ data "aws_iam_policy_document" "resource_full_access" { } actions = [ - "ecr:GetAuthorizationToken", - "ecr:InitiateLayerUpload", - "ecr:UploadLayerPart", - "ecr:CompleteLayerUpload", - "ecr:PutImage", - "ecr:BatchCheckLayerAvailability", - "ecr:GetDownloadUrlForLayer", - "ecr:GetRepositoryPolicy", - "ecr:DescribeRepositories", - "ecr:ListImages", - "ecr:DescribeImages", - "ecr:BatchGetImage", + "ecr:*" ] } } - -data "aws_iam_policy_document" "resource" { - source_policy_documents = [local.principals_readonly_access_non_empty ? join("", data.aws_iam_policy_document.resource_readonly_access.*.json) : join("", data.aws_iam_policy_document.empty.*.json)] - override_policy_documents = [local.principals_full_access_non_empty ? join("", data.aws_iam_policy_document.resource_full_access.*.json) : join("", data.aws_iam_policy_document.empty.*.json)] +data "aws_iam_policy_document" "resource_private" { + source_policy_documents = [local.principals_readonly_access_non_empty ? join("", data.aws_iam_policy_document.resource_readonly_access_private.*.json) : join("", data.aws_iam_policy_document.empty.*.json)] + override_policy_documents = [local.principals_full_access_non_empty ? join("", data.aws_iam_policy_document.resource_full_access_private.*.json) : join("", data.aws_iam_policy_document.empty.*.json)] } -# Module : ECR REPOSITORY -# Description : Provides an Elastic Container Registry Repository Policy. -resource "aws_ecr_repository_policy" "default" { - count = local.ecr_need_policy && var.enabled_ecr ? 1 : 0 +resource "aws_ecr_repository_policy" "private" { + count = local.ecr_need_policy && var.enable_private_ecr ? 1 : 0 repository = join("", aws_ecr_repository.default.*.name) - policy = join("", data.aws_iam_policy_document.resource.*.json) + policy = join("", data.aws_iam_policy_document.resource_private.*.json) } + +################################################################################ +# Public ECR IAM Policies +################################################################################ +data "aws_iam_policy_document" "resource_readonly_access_public" { + statement { + sid = "ReadonlyAccess" + effect = "Allow" + + principals { + type = "AWS" + + identifiers = var.principals_readonly_access + } + + actions = [ + "ecr-public:DescribeImageTags", + "ecr-public:DescribeImages", + "ecr-public:DescribeRepositories", + "ecr-public:GetAuthorizationToken", + "ecr-public:DescribeRegistries", + "ecr-public:GetRepositoryCatalogData", + "ecr-public:GetRegistryCatalogData", + "ecr-public:ListTagsForResource", + "ecr-public:GetRepositoryPolicy", + "ecr-public:BatchCheckLayerAvailability", + ] + } +} + +data "aws_iam_policy_document" "resource_full_access_public" { + statement { + sid = "FullAccess" + effect = "Allow" + + principals { + type = "AWS" + + identifiers = var.principals_full_access + } + + actions = [ + "ecr-public:*" + ] + } +} + + +data "aws_iam_policy_document" "resource_public" { + source_policy_documents = [local.principals_readonly_access_non_empty ? join("", data.aws_iam_policy_document.resource_readonly_access_public.*.json) : join("", data.aws_iam_policy_document.empty.*.json)] + override_policy_documents = [local.principals_full_access_non_empty ? join("", data.aws_iam_policy_document.resource_full_access_public.*.json) : join("", data.aws_iam_policy_document.empty.*.json)] +} + +resource "aws_ecr_repository_policy" "public" { + count = local.ecr_need_policy && var.enable_public_ecr ? 1 : 0 + repository = join("", aws_ecrpublic_repository.default.*.name) + policy = join("", data.aws_iam_policy_document.resource_public.*.json) +} + +data "aws_iam_policy_document" "empty" {} \ No newline at end of file diff --git a/variables.tf b/variables.tf index 67a962c..90e95c7 100644 --- a/variables.tf +++ b/variables.tf @@ -43,18 +43,24 @@ variable "tags" { description = "Additional tags (e.g. map(`BusinessUnit`,`XYZ`)." } -variable "enabled_ecr" { +variable "enable_private_ecr" { type = bool - default = true + default = false description = "Set to false to prevent the module from creating any resources." } variable "max_image_count" { type = number - default = 7 + default = 10 description = "How many Docker Image versions AWS ECR will store." } +variable "max_untagged_image_count" { + type = number + default = 1 + description = "How many Untagged Docker Image versions AWS ECR will store." +} + variable "principals_readonly_access" { type = list(any) default = [] @@ -62,8 +68,8 @@ variable "principals_readonly_access" { } variable "use_fullname" { - type = bool - default = true + type = string + default = "" description = "Set 'true' to use `namespace-stage-name` for ecr repository name, else `name`." } @@ -91,20 +97,21 @@ variable "image_tag_mutability" { description = "The tag mutability setting for the repository." } -variable "encryption_configuration" { - type = object({ - encryption_type = string - kms_key = any - }) - description = "ECR encryption configuration" - default = null +variable "repository_force_delete" { + type = bool + default = false + description = "If `true`, will delete the repository even if it contains images. Defaults to `false`" } -# Image scanning configuration -variable "image_scanning_configuration" { - type = map(any) - default = {} - description = "Configuration block that defines image scanning configuration for the repository. By default, image scanning must be manually triggered. See the ECR User Guide for more information about image scanning." +variable "encryption_type" { + type = string + default = null + description = "The encryption type for the repository. Must be one of: `KMS` or `AES256`. Defaults to `AES256`" +} +variable "kms_key" { + type = string + default = null + description = "The ARN of the KMS key to use when encryption_type is `KMS`. If not specified, uses the default AWS managed key for ECR" } # Timeouts @@ -113,3 +120,16 @@ variable "timeouts" { default = {} description = "Timeouts map." } + +# Public Repository +variable "enable_public_ecr" { + type = bool + default = false + description = "Set to false to prevent the module from creating any resources." +} + +variable "public_repository_catalog_data" { + description = "Catalog data configuration for the repository" + type = any + default = {} +} \ No newline at end of file