Skip to content

Commit

Permalink
CCL-2122: Initial cut of ECR terraform module (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
chej-hod authored Feb 24, 2025
1 parent 0091860 commit de80963
Show file tree
Hide file tree
Showing 11 changed files with 367 additions and 0 deletions.
35 changes: 35 additions & 0 deletions .github/dependabot.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
commit-message:
prefix: "(GHA)"
reviewers:
- "UKHomeOffice/core-cloud-devops"
labels:
- "dependencies"
- "patch"
- package-ecosystem: "terraform"
directory: "/stateful"
schedule:
interval: "daily"
commit-message:
prefix: "(TF)"
reviewers:
- "UKHomeOffice/core-cloud-devops"
labels:
- "dependencies"
- "patch"
- package-ecosystem: "terraform"
directory: "/stateless"
schedule:
interval: "daily"
commit-message:
prefix: "(TF)"
reviewers:
- "UKHomeOffice/core-cloud-devops"
labels:
- "dependencies"
- "patch"
27 changes: 27 additions & 0 deletions .github/workflows/pull-request-sast.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Validate Terraform with Trivy

on:
push:
branches:
- main
pull_request:

permissions:
contents: read

jobs:
RunTerraformValidation:
name: Run Terraform SAST
runs-on: ubuntu-latest

steps:
- name: Clone the Repository
uses: actions/checkout@v4

# Results have to be a table as the organisation does not have Advanced Security license.
- name: Terraform Trivy Scan
uses: aquasecurity/[email protected]
with:
scan-type: 'config'
#trivyignores: ".trivyignore"
exit-code: '1'
39 changes: 39 additions & 0 deletions .github/workflows/pull-request-semver-label-check.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: 'Check PR for SemVer Label'
on:
pull_request:
types:
- labeled
- unlabeled
- opened
- reopened
- synchronize
branches:
- main

permissions:
pull-requests: read
contents: read

jobs:
semver-check:
name: 'Check PR for SemVer Label'
if: |
contains(github.event.pull_request.labels.*.name, 'skip-release') == false
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Parse the SemVer label
id: label
uses: UKHomeOffice/match-label-action@v1
with:
labels: minor,major,patch
mode: singular

- name: Calculate SemVer value
id: calculate
uses: UKHomeOffice/semver-calculate-action@v2
with:
increment: ${{ steps.label.outputs.matchedLabels }}
github_token: ${{ secrets.GITHUB_TOKEN }}
46 changes: 46 additions & 0 deletions .github/workflows/pull-request-semver-tag-merge.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: 'SemVer Tag on Main Merge'
on:
pull_request:
types:
- closed
branches:
- main

permissions:
pull-requests: read
contents: write

concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

jobs:
semver-tag:
name: 'Tag Repository with SemVer'
if: |
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'skip-release') == false
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Parse the SemVer label
id: label
uses: UKHomeOffice/match-label-action@v1
with:
labels: minor,major,patch
mode: singular

- name: Calculate SemVer value
id: calculate
uses: UKHomeOffice/semver-calculate-action@v2
with:
increment: ${{ steps.label.outputs.matchedLabels }}
github_token: ${{ secrets.GITHUB_TOKEN }}

- name: Tag Repository
uses: UKHomeOffice/semver-tag-action@v4
with:
tag: ${{ steps.calculate.outputs.version }}
github_token: ${{ secrets.GITHUB_TOKEN }}
96 changes: 96 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# core-cloud-ecr-tf-module
This module aims to provide a common pattern for deploying your AWS Elastic Container Registry (ECR) repositories on either a central AWS account or individual workload accounts. This module utilises the official TF module for ECR (https://registry.terraform.io/modules/terraform-aws-modules/ecr/aws/latest).

By default the ECR repository creates a READ/WRITE policy that defaults to the AWS account. You will need to specify additional Amazon Resource Numbers (ARNs) in the respective readwrite/readonly lists. Should you need need to provide access to other AWS accounts. You may also consider matching the OU (Organizational unit) instead using custom policy statements.

You may set common options and override them on a per-repository basis with an exception around Lambda Access.

Lambda ARNS must be declared in a separate list that can only be defined at a per-repository level. This adds additional permissions that allow Lambda to access ECR repositories to use as a runtime container.

## Expected YAML config with Explanations
```
tenant: <TENANT NAME> #This is used as a prefix for your ECR repo. i.e. <prefix>/<repo name>
common_options: # These are common options that can be re-used by all of your ECR repositories
create_lifecycle_policy: true # Defaults to false. If set to true you will need to specify repository_lifecycle_policy - this is done via filepath to a json file
repository_lifecycle_policy: ./policies/example_common_repo_lifecycle_policy.json
repository_read_write_access_arns: # These are sets of ARNs that are allowed READ WRITE access to your ECR Repo
- arn:aws:iam::<ACCOUNT>:root
- ...
repository_read_access_arns: # These are sets of ARNs that are allowed READONLY access to your ECR Repos
- arn:aws:iam::<ACCOUNT_2>:root
- ...
repository_policy_statements: # Custom policy statements to attach to your ECR repos, example shown below is an example to allow read only access to all AWS accounts belonging to a certain AWS organisation.
orgID_readonly:
sid: orgRO
actions:
- "ecr:GetAuthorizationToken"
- "ecr:BatchCheckLayerAvailability"
- "ecr:BatchGetImage"
- "ecr:DescribeImageScanFindings"
- "ecr:DescribeImages"
- "ecr:DescribeRepositories"
- "ecr:GetDownloadUrlForLayer"
- "ecr:GetLifecyclePolicy"
- "ecr:GetLifecyclePolicyPreview"
- "ecr:GetRepositoryPolicy"
- "ecr:ListImages"
- "ecr:ListTagsForResource"
resources: ["*"]
effect: Allow
conditions:
- orgMatch:
test: "StringLike"
variable: "aws:PrincipalOrgID"
values:
- o-<ORG-ID>
- ...
repo_list: # This is where you will define your list of ECR repositories as keys. This can be done as `key: `or `key: ~` if there are no changes from the common options
hello-world:
foo-bar:
custom-oci:
repository_read_write_access_arns: # You can override the common options
- arn:aws:iam::<ACCOUNT_3>:root
- ...
repository_read_access_arns:
- arn:aws:iam::<ACCOUNT_4>:root
- ...
repository_lambda_read_access_arns: # This is where you'll list lambda arns that are allowed access to a particular ECR repos. This cannot be defined under common
- ...
repository_policy_statements: {} # Example to remove common repository_policy_statements if defined
```

Please see example directory for an example usage in both Terraform and Terragrunt.


<!-- BEGIN_TF_DOCS -->
## Requirements

No requirements.

## Providers

No providers.

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_ecr"></a> [ecr](#module\_ecr) | terraform-aws-modules/ecr/aws | 2.3.1 |

## Resources

No resources.

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_ecr_config"></a> [ecr\_config](#input\_ecr\_config) | PAth to YAML file that contains ECR repositories | `any` | n/a | yes |
| <a name="input_tags"></a> [tags](#input\_tags) | n/a | `map(string)` | `{}` | no |

## Outputs

No outputs.
<!-- END_TF_DOCS -->
13 changes: 13 additions & 0 deletions example/example-terraform.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module "ecr_repos" {
source = "../"

ecr_config = yamldecode(file("./example_repos.yaml"))

tags = {
cost-centre = "..."
finance-account-id = "..."
portfolio-id = "..."
project-id = "..."
service-id = "..."
}
}
15 changes: 15 additions & 0 deletions example/example-terragrunt.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
terraform {
source = "../"
}

inputs = {
association_config = yamldecode(file("./example_repos.yaml"))

tags = {
cost-centre = "..."
finance-account-id = "..."
portfolio-id = "..."
project-id = "..."
service-id = "..."
}
}
49 changes: 49 additions & 0 deletions example/example_repos.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
tenant: <TENANT NAME> #This is used as a prefix for your ECR repo. i.e. <prefix>/<repo name>
common_options: # These are common options that can be re-used by all of your ECR repositories
create_lifecycle_policy: true # Defaults to false. If set to true you will need to specify repository_lifecycle_policy - this is done via filepath to a json file
repository_lifecycle_policy: ./policies/example_common_repo_lifecycle_policy.json
repository_read_write_access_arns: # These are sets of ARNs that are allowed READ WRITE access to your ECR Repo
- arn:aws:iam::<ACCOUNT>:root
- ...
repository_read_access_arns: # These are sets of ARNs that are allowed READONLY access to your ECR Repos
- arn:aws:iam::<ACCOUNT_2>:root
- ...
repository_policy_statements: # Custom policy statements to attach to your ECR repos, example shown below is an example to allow read only access to all AWS accounts belonging to a certain AWS organisation.
orgID_readonly:
sid: orgRO
actions:
- "ecr:GetAuthorizationToken"
- "ecr:BatchCheckLayerAvailability"
- "ecr:BatchGetImage"
- "ecr:DescribeImageScanFindings"
- "ecr:DescribeImages"
- "ecr:DescribeRepositories"
- "ecr:GetDownloadUrlForLayer"
- "ecr:GetLifecyclePolicy"
- "ecr:GetLifecyclePolicyPreview"
- "ecr:GetRepositoryPolicy"
- "ecr:ListImages"
- "ecr:ListTagsForResource"
resources: ["*"]
effect: Allow
conditions:
- orgMatch:
test: "StringLike"
variable: "aws:PrincipalOrgID"
values:
- o-<ORG-ID>
- ...

repo_list: # This is where you will define your list of ECR repositories as keys. This can be done as `key: `or `key: ~` if there are no changes from the common options
hello-world:
foo-bar:
custom-oci:
repository_read_write_access_arns: # You can override the common options
- arn:aws:iam::<ACCOUNT_3>:root
- ...
repository_read_access_arns:
- arn:aws:iam::<ACCOUNT_4>:root
- ...
repository_lambda_read_access_arns: # This is where you'll list lambda arns that are allowed access to a particular ECR repos. This cannot be defined under common
- ...
repository_policy_statements: {} # Example to remove common repository_policy_statements if defined
19 changes: 19 additions & 0 deletions example/policies/example_common_repo_lifecycle_policy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"rules": [
{
"rulePriority": 1,
"description": "Keep last 30 images",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": [
"v"
],
"countType": "imageCountMoreThan",
"countNumber": 30
},
"action": {
"type": "expire"
}
}
]
}
19 changes: 19 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module "ecr" {
source = "terraform-aws-modules/ecr/aws"
version = "2.3.1"

for_each = try(var.ecr_config.repo_list, {})

repository_name = "${var.ecr_config.tenant}/${each.key}"
repository_type = "private"

create_lifecycle_policy = try(each.value.create_lifecycle_policy, var.ecr_config.common_options.create_lifecycle_policy, false)

repository_read_access_arns = try(each.value.repository_read_write_access_arns, var.ecr_config.common_options.repository_read_write_access_arns, [])
repository_read_write_access_arns = try(each.value.repository_read_access_arns, var.ecr_config.common_options.repository_read_access_arns, [])
repository_lambda_read_access_arns = try(each.value.repository_lambda_read_access_arns, []) # Lambda ECR access to be done on a repo by repo basis only
repository_policy_statements = try(each.value.repository_policy_statements, var.ecr_config.common_options.repository_policy_statements, {})
repository_lifecycle_policy = try(file(each.value.repository_lifecycle_policy), file(var.ecr_config.common_options.repository_lifecycle_policy), null)

tags = var.tags
}
9 changes: 9 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
variable "ecr_config" {
type = any
description = "PAth to YAML file that contains ECR repositories"
}

variable "tags" {
type = map(string)
default = {}
}

0 comments on commit de80963

Please sign in to comment.