diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7f9f794..fcb9d68 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,20 +1,24 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.48.0 + rev: v1.83.5 hooks: # - id: terraform_validate - id: terraform_fmt - - id: terraform_docs_replace - args: ['table'] +# - id: terraform_docs_replace + - id: terraform_docs +# args: ['table'] exclude: common/*.tf exclude: version.tf exclude: examples + args: + - --args=--config=.terraform-docs.yml # - id: terraform_tflint # args: [ "--args=--config=__GIT_WORKING_DIR__/.tflint.hcl"] # exclude: examples - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 + rev: v4.5.0 hooks: - id: check-symlinks - id: detect-aws-credentials + args: [ "--allow-missing-credentials" ] - id: detect-private-key diff --git a/.terraform-docs.yml b/.terraform-docs.yml new file mode 100644 index 0000000..227afcc --- /dev/null +++ b/.terraform-docs.yml @@ -0,0 +1,46 @@ +## https://github.com/antonbabenko/pre-commit-terraform/issues/248#issuecomment-1290829226 +formatter: "markdown table" + +header-from: main.tf +footer-from: "" + +sections: +## hide: [] + show: + - data-sources + - header + - footer + - inputs + - modules + - outputs + - providers + - requirements + - resources + +output: + file: README.md + mode: inject + template: |- + + {{ .Content }} + + +## output-values: +## enabled: false +## from: "" +## +## sort: +## enabled: true +## by: name +## +## settings: +## anchor: true +## color: true +## default: true +## description: false +## escape: true +## indent: 2 +## required: true +## sensitive: true +## type: true + diff --git a/CHANGELOG.md b/CHANGELOG.md index fab04d0..11636ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,3 +19,6 @@ * 2.0.2 -- 2023-08-29 - add repository_urls output (per app_name => URL) + +* 2.0.3 -- 2023-11-14 + - add output repository_names diff --git a/README.md b/README.md index 7c43ea8..a693792 100644 --- a/README.md +++ b/README.md @@ -142,4 +142,154 @@ No modules. | [availability\_zone\_names](#output\_availability\_zone\_names) | VPC Availability zone name list (3) | | [availability\_zone\_suffixes](#output\_availability\_zone\_suffixes) | VPC Availability zone suffix list (3) | | [images](#output\_images) | Final full merge of images with extra details | +| [repository\_names](#output\_repository\_names) | ECR Respository Names | | [repository\_urls](#output\_repository\_urls) | ECR Respository URLs | + + +# About aws-ecr-copy-images +This module will create ECR repositories with the prefix of {application\_name} for the list of +repositories in {application\_list}. This allows for a project to upload their images into +/{application\_name}/{sub\_app}/{image}:{tag}. + +Also, if provided a list of source image configurations, it will download them from their location +and upload them to the prefix of {application\_name} followed by the {name} in the `image_config` +object. + +# Usage + +```hcl +locals { + image_config = [ + { + enabled = true + dest_path = null + name = "openjdk-8" + source_image = "ubi8/openjdk-8" + source_registry = "registry.access.redhat.com" + source_tag = null + tag = "latest" + }, + { + enabled = true + name = "nginx-118" + dest_path = null + source_image = "ubi8/nginx-118" + source_registry = "registry.access.redhat.com" + source_tag = null + tag = "latest" + }, + { + enabled = true + name = "nodejs-14" + dest_path = null + source_image = "ubi8/nodejs-14" + source_registry = "registry.access.redhat.com" + source_tag = null + tag = "latest" + }, + ] +} + +module "images" { + source = "git@github.e.it.census.gov:terraform-modules/aws-ecr-copy-images.git" + + profile = var.profile + application_list = ["app1", "app2"] + application_name = "org-project" + image_config = local.image_config + tags = {} + + ### optional + ## account_alias = "" + ## account_id = "" + ## destination_password = "" + ## destination_username = "" + ## override_prefixes = {} + ## region = "" + ## source_password = "" + ## source_username = "" +} +``` + +This creates the following ECR images + +``` +Repository name URI Created at Tag immutability Scan on push Encryption type + +org-project/app1 817869416306.dkr.ecr.us-gov-east-1.amazonaws.com/org-project/app1 August 22, 2022, 13:12:06 (UTC-04) Enabled Enabled KMS +org-project/app2 817869416306.dkr.ecr.us-gov-east-1.amazonaws.com/org-project/app2 August 22, 2022, 13:12:06 (UTC-04) Enabled Enabled KMS +org-project/nginx-118 817869416306.dkr.ecr.us-gov-east-1.amazonaws.com/org-project/nginx-118 August 22, 2022, 12:43:57 (UTC-04) Enabled Enabled KMS +org-project/nodejs-14 817869416306.dkr.ecr.us-gov-east-1.amazonaws.com/org-project/nodejs-14 August 22, 2022, 12:43:57 (UTC-04) Enabled Enabled KMS +org-project/openjdk-8 817869416306.dkr.ecr.us-gov-east-1.amazonaws.com/org-project/openjdk-8 August 22, 2022, 12:43:57 (UTC-04) Enabled Enabled KMS +``` + +# Variables +## profile +This variable is required because this module calls a script, and it uses `aws` CLI commands. As such, it needs to set the `AWS_PROFILE` environment +variable to call the script properly. + +# Caveats +Currently, a destroy of the images (null\_resources) does **NOT** remove the repository. That is a work in progress. + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 0.13 | +| [aws](#requirement\_aws) | >= 3.0 | +| [null](#requirement\_null) | >= 1.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 3.0 | +| [null](#provider\_null) | >= 1.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_ecr_repository.apps_repos](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository) | resource | +| [null_resource.copy_images](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [aws_arn.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | data source | +| [aws_availability_zone.zone](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zone) | data source | +| [aws_availability_zones.zones](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_ecr_authorization_token.token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ecr_authorization_token) | data source | +| [aws_iam_account_alias.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_account_alias) | data source | +| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [account\_alias](#input\_account\_alias) | AWS Account Alias | `string` | `""` | no | +| [account\_id](#input\_account\_id) | AWS Account ID (default will pull from current user) | `string` | `""` | no | +| [application\_list](#input\_application\_list) | List of application repositories to create for /{application\_name}/{image\_name} for those not in image\_config | `list(string)` | `[]` | no | +| [application\_name](#input\_application\_name) | Appliication name, usually {org}-{project}, which is likely a prefix to the EKS cluster name | `string` | n/a | yes | +| [destination\_password](#input\_destination\_password) | OCI destination repository password | `string` | `null` | no | +| [destination\_username](#input\_destination\_username) | OCI destination repository username | `string` | `null` | no | +| [image\_config](#input\_image\_config) | List of image configuration objects to copy from SOURCE to DESTINATION |
list(object({
name = string,
tag = string,
dest_path = string,
source_registry = string,
source_image = string,
source_tag = string,
enabled = bool,
})) | `[]` | no |
+| [override\_prefixes](#input\_override\_prefixes) | Override built-in prefixes by component. This should be used primarily for common infrastructure things | `map(string)` | `{}` | no |
+| [profile](#input\_profile) | AWS Profile Name, used generating key rotation file | `string` | n/a | yes |
+| [region](#input\_region) | Region in which to create the ECR repositories (default of current region) | `string` | `null` | no |
+| [source\_password](#input\_source\_password) | OCI source repository password | `string` | `null` | no |
+| [source\_username](#input\_source\_username) | OCI source repository username | `string` | `null` | no |
+| [tags](#input\_tags) | AWS Tags to apply to appropriate resources | `map(string)` | `{}` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [availability\_zone\_ids](#output\_availability\_zone\_ids) | VPC Availability zone id list (3) |
+| [availability\_zone\_names](#output\_availability\_zone\_names) | VPC Availability zone name list (3) |
+| [availability\_zone\_suffixes](#output\_availability\_zone\_suffixes) | VPC Availability zone suffix list (3) |
+| [images](#output\_images) | Final full merge of images with extra details |
+| [repository\_names](#output\_repository\_names) | ECR Respository Names |
+| [repository\_urls](#output\_repository\_urls) | ECR Respository URLs |
+
\ No newline at end of file
diff --git a/outputs.tf b/outputs.tf
index ab733d1..0acf994 100644
--- a/outputs.tf
+++ b/outputs.tf
@@ -7,3 +7,8 @@ output "repository_urls" {
description = "ECR Respository URLs"
value = { for k, v in aws_ecr_repository.apps_repos : k => v.repository_url }
}
+
+output "repository_names" {
+ description = "ECR Respository Names"
+ value = { for k, v in aws_ecr_repository.apps_repos : k => v.name }
+}
diff --git a/policy.tf.txt b/policy.tf.txt
new file mode 100644
index 0000000..a12a887
--- /dev/null
+++ b/policy.tf.txt
@@ -0,0 +1,138 @@
+ admin_policy_statements = {
+ ECRRead = {
+ actions = [
+ "ecr:Describe*",
+ "ecr:Get*",
+ "ecr:ListImages",
+ "ecr:BatchGetImage",
+ "ecr:BatchCheckLayerAvailability",
+ "ecr:GetDownloadUrlForLayer",
+ ]
+ resources = ["*"]
+ }
+ ECRWrite = {
+ actions = [
+ "ecr:BatchDeleteImage",
+ "ecr:CompleteLayerUpload",
+ "ecr:CreateRepository",
+ "ecr:DeleteRepository",
+ "ecr:InitiateLayerUpload",
+ "ecr:PutImage",
+ "ecr:UploadLayerPart"
+ ]
+ resources = [format(local.common_arn, "ecr", format("repository/eks/%v/*", var.cluster_name))]
+ }
+ EKSRead = {
+ actions = [
+ "eks:ListClusters",
+ "eks:ListAddons",
+ "eks:ListNodegroups",
+ "eks:DescribeCluster",
+ "eks:DescribeAddon*",
+ "eks:DescribeNodegroup",
+ ]
+ resources = [
+ format(local.common_arn, "eks", "cluster/*"),
+ format(local.common_arn, "eks", "addon/*"),
+ format(local.common_arn, "eks", "addons/*"),
+ format(local.common_arn, "eks", "/addons/*"),
+ format(local.common_arn, "eks", "nodegroup/*"),
+ ]
+ }
+
+
+
+
+data "aws_iam_policy_document" "shared_access" {
+ statement {
+ sid = "SharedECRAccess"
+ effect = "Allow"
+ resources = [ "*" ]
+ principals = *
+ actions = [
+ “ecr:BatchCheckLayerAvailability”,
+ “ecr:BatchGetImage”,
+ “ecr:DescribeImages”,
+ “ecr:DescribeRepositories”,
+ “ecr:GetDownloadUrlForLayer”,
+ ]
+ conditions {
+ condition {
+ test = "StringEquals"
+ variable = "aws:PrincipalOrgID"
+ values = [data.aws_organizations_organization.org.id]
+
+
+ “Condition”: {
+
+ “ForAnyValue:StringLike”: {
+
+ “aws:PrincipalOrgPaths”: “o-xxxxxxxxxx/*/ou-xxxx-xxxxxxxx/*”
+
+ }
+
+ }
+
+ }
+
+ ]
+
+}
+
+
+locals {
+ # org_paths = [for c in data.aws_organizations_organizational_units.ou.children : format("%v/%v/%v", data.aws_organizations_organization.org.id, data.aws_organizations_organizational_units.ou.id, c.id)]
+ org_paths = [for k, v in local.share_organizational_units : format("%v/%v/%v", data.aws_organizations_organization.org.id, data.aws_organizations_organizational_units.ou.id, k)]
+ templates = [
+ "EndEntityCertificate/V1",
+ "SubordinateCACertificate_PathLen0/V1",
+ ]
+ template_arns = [for t in local.templates : format("arn:%v:acm-pca:::template/%v", data.aws_arn.current.partition, t)]
+}
+
+
+
+
+i # # share to whole org, not to path
+ # condition {
+ # test = "ForAnyValue:StringLike"
+ # variable = "aws:PrincipalOrgPaths"
+ # values = formatlist("%v/*", local.org_paths)
+ # }
+
+
+
+data "aws_iam_policy_document" "foopolicy" {
+ statement {
+ sid = "new policy"
+ effect = "Allow"
+
+ principals {
+ type = "AWS"
+ identifiers = ["123456789012"]
+ }
+
+ actions = [
+ "ecr:GetDownloadUrlForLayer",
+ "ecr:BatchGetImage",
+ "ecr:BatchCheckLayerAvailability",
+ "ecr:PutImage",
+ "ecr:InitiateLayerUpload",
+ "ecr:UploadLayerPart",
+ "ecr:CompleteLayerUpload",
+ "ecr:DescribeRepositories",
+ "ecr:GetRepositoryPolicy",
+ "ecr:ListImages",
+ "ecr:DeleteRepository",
+ "ecr:BatchDeleteImage",
+ "ecr:SetRepositoryPolicy",
+ "ecr:DeleteRepositoryPolicy",
+ ]
+ }
+}
+
+resource "aws_ecr_repository_policy" "foopolicy" {
+ repository = aws_ecr_repository.foo.name
+ policy = data.aws_iam_policy_document.foopolicy.json
+}
+
diff --git a/version.tf b/version.tf
index fed7410..66cbccb 100644
--- a/version.tf
+++ b/version.tf
@@ -1,4 +1,4 @@
locals {
_module_name = "aws-ecr-copy-images"
- _module_version = "2.0.2"
+ _module_version = "2.0.3"
}