Skip to content

Commit 53d4c9c

Browse files
committed
Update Embedded Module for Multi-Region
Why these changes are being introduced: We need to ensure that the embedded module can correctly create resources in multiple regions. Since IAM Roles/Policies are "global" we only want to create those resources in the default `us-east-1` region. The ECR Repository is regional, so we create it each time the module is called based on the submitted provider. How this addresses that need: * Add the `aws_region.current` data object so that we can track the region provided when the module is called * Add conditionals to all the "global" resources so that they only get created when the module is called for the us-east-1 region * Reorganize the resource creation in the embedded module slightly Side effects of this change: None Relevant ticket(s): * https://mitlibraries.atlassian.net/browse/IR-238
1 parent e36ffc0 commit 53d4c9c

File tree

5 files changed

+151
-121
lines changed

5 files changed

+151
-121
lines changed

modules/ecr/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
# Embedded ECR Creation Module
2+
3+
Creates the ECR Repository and all the necessary infrastructure glue around it.
4+
5+
## What is created
6+
7+
The following resources are generated when this module is called
8+
9+
* an ECR repository with a lifecycle policy to only store the five (5) most recent containers
10+
* an IAM Policy providing read/write access to the ECR Repository
11+
* a trust policy for OIDC connections from GitHub Actions that is tightly coupled to the GitHub repository name
12+
* an IAM Role for GitHub Actions OIDC connections to AWS
13+
114
<!-- BEGIN_TF_DOCS -->
215
## Requirements
316

@@ -29,8 +42,10 @@ No modules.
2942
| [aws_ssm_parameter.ecr_repository_name](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
3043
| [aws_ssm_parameter.ecr_repository_url](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
3144
| [aws_ssm_parameter.gha_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
45+
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
3246
| [aws_iam_policy_document.gh_trust](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
3347
| [aws_iam_policy_document.rw_this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
48+
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
3449

3550
## Inputs
3651

modules/ecr/ecr.tf

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
##############################################################################
2+
##############################################################################
3+
# Create the ECR repository to store the ECS image(s) along with a lifecycle
4+
# policy.
5+
resource "aws_ecr_repository" "this" {
6+
#checkov:skip=CKV_AWS_51:We do not currently use releases for this, but may choose to turn this on in the future
7+
#checkov:skip=CKV_AWS_136:We do not store any private information in our images, encryption is unnecessary
8+
name = "${var.repo_name}-${var.environment}"
9+
image_scanning_configuration {
10+
scan_on_push = true
11+
}
12+
tags = var.tags
13+
}
14+
15+
resource "aws_ecr_lifecycle_policy" "this" {
16+
#checkov:skip=CKV_AWS_136:We do not store any private information in our images, encryption is unnecessary
17+
#checkov:skip=CKV_AWS_163:We do not use image scanning by AWS right now
18+
#checkov:skip=CKV_AWS_51:We do not currently use releases for this, but may choose to turn this on in the future
19+
repository = aws_ecr_repository.this.name
20+
policy = jsonencode({
21+
rules = [{
22+
rulePriority = 1
23+
description = "keep last 5 images"
24+
action = {
25+
type = "expire"
26+
}
27+
selection = {
28+
tagStatus = "any"
29+
countType = "imageCountMoreThan"
30+
countNumber = 5
31+
}
32+
}]
33+
})
34+
}
35+
36+
### Read-write permissions ECR repository
37+
### Since IAM Policies are "global" we ONLY create the policy statement if this
38+
### module is called with the "default" us-east-1 AWS Provider so that we only
39+
### have one IAM Policy that is good for all regions.
40+
data "aws_iam_policy_document" "rw_this" {
41+
#checkov:skip=CKV_AWS_111:This policy needs unconstrained CreateRepository privileges
42+
#checkov:skip=CKV_AWS_356:This policy should allow "*" as a resource for restrictable actions
43+
count = data.aws_region.current.name == "us-east-1" ? 1 : 0
44+
statement {
45+
actions = [
46+
"ecr:CreateRepository",
47+
"ecr:GetAuthorizationToken",
48+
]
49+
resources = ["*"]
50+
}
51+
statement {
52+
actions = [
53+
"ecr:BatchCheckLayerAvailability",
54+
"ecr:BatchGetImage",
55+
"ecr:CompleteLayerUpload",
56+
"ecr:DeleteRepository",
57+
"ecr:DescribeImages",
58+
"ecr:DescribeImageScanFindings",
59+
"ecr:DescribeRepositories",
60+
"ecr:GetDownloadUrlForLayer",
61+
"ecr:GetLifecyclePolicy",
62+
"ecr:GetLifecyclePolicyPreview",
63+
"ecr:InitiateLayerUpload",
64+
"ecr:ListImages",
65+
"ecr:ListTagsForResource",
66+
"ecr:PutImage",
67+
"ecr:TagResource",
68+
"ecr:UntagResource",
69+
"ecr:UploadLayerPart",
70+
]
71+
resources = [
72+
"arn:aws:ecr:*:${data.aws_caller_identity.current.account_id}:repository/${aws_ecr_repository.this.name}"
73+
]
74+
}
75+
}
76+
77+
resource "aws_iam_policy" "rw_this" {
78+
count = data.aws_region.current.name == "us-east-1" ? 1 : 0
79+
name = "${var.repo_name}-ecr-rw-${var.environment}"
80+
description = "policy to allow read/write into ${var.repo_name} ECR"
81+
policy = data.aws_iam_policy_document.rw_this[0].json
82+
83+
tags = var.tags
84+
}
85+
86+
# The trust policy that allows GitHub Actions OIDC-based connections from the
87+
# repository. The definition is explicitly linked to the name of the GitHub repo.
88+
data "aws_iam_policy_document" "gh_trust" {
89+
count = data.aws_region.current.name == "us-east-1" ? 1 : 0
90+
statement {
91+
actions = ["sts:AssumeRoleWithWebIdentity"]
92+
principals {
93+
type = "Federated"
94+
identifiers = [var.oidc_arn]
95+
}
96+
condition {
97+
test = "StringLike"
98+
variable = "token.actions.githubusercontent.com:sub"
99+
values = ["repo:${var.gh_organization}/${var.repo_name}:*"] # "repo:MITLibraries//<repo-name>:ref:refs/branch/${var.environment}" eventually
100+
}
101+
condition {
102+
test = "StringLike"
103+
variable = "token.actions.githubusercontent.com:aud"
104+
values = ["sts.amazonaws.com"]
105+
}
106+
}
107+
}
108+
109+
# Role for GitHub Action OIDC connections from the lambdas repository
110+
resource "aws_iam_role" "gha_this" {
111+
count = data.aws_region.current.name == "us-east-1" ? 1 : 0
112+
name = "${var.repo_name}-gha-${var.environment}"
113+
assume_role_policy = data.aws_iam_policy_document.gh_trust[0].json
114+
115+
tags = var.tags
116+
}
117+
118+
resource "aws_iam_role_policy_attachment" "gha_ecr_rw" {
119+
count = data.aws_region.current.name == "us-east-1" ? 1 : 0
120+
role = aws_iam_role.gha_this[0].name
121+
policy_arn = aws_iam_policy.rw_this[0].arn
122+
}
123+
resource "aws_iam_role_policy_attachment" "gha_ecr_login" {
124+
count = data.aws_region.current.name == "us-east-1" ? 1 : 0
125+
role = aws_iam_role.gha_this[0].name
126+
policy_arn = var.login_policy_arn
127+
}

modules/ecr/main.tf

Lines changed: 5 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,7 @@
1-
##############################################################################
2-
##############################################################################
3-
# Create the ECR repository to store the ECS image(s) along with a lifecycle
4-
# policy.
5-
resource "aws_ecr_repository" "this" {
6-
#checkov:skip=CKV_AWS_51:We do not currently use releases for this, but may choose to turn this on in the future
7-
#checkov:skip=CKV_AWS_136:We do not store any private information in our images, encryption is unnecessary
8-
name = "${var.repo_name}-${var.environment}"
9-
image_scanning_configuration {
10-
scan_on_push = true
11-
}
1+
# Not much here other than capturing the caller_identity and aws_region
122

13-
tags = var.tags
14-
}
3+
# Capture the AWS account number and capture the default managed key for SSM in KMS
4+
data "aws_caller_identity" "current" {}
155

16-
resource "aws_ecr_lifecycle_policy" "this" {
17-
#checkov:skip=CKV_AWS_136:We do not store any private information in our images, encryption is unnecessary
18-
#checkov:skip=CKV_AWS_163:We do not use image scanning by AWS right now
19-
#checkov:skip=CKV_AWS_51:We do not currently use releases for this, but may choose to turn this on in the future
20-
repository = aws_ecr_repository.this.name
21-
22-
policy = jsonencode({
23-
rules = [{
24-
rulePriority = 1
25-
description = "keep last 5 images"
26-
action = {
27-
type = "expire"
28-
}
29-
selection = {
30-
tagStatus = "any"
31-
countType = "imageCountMoreThan"
32-
countNumber = 5
33-
}
34-
}]
35-
})
36-
}
37-
38-
### Read-write permissions ECR repository
39-
data "aws_iam_policy_document" "rw_this" {
40-
#checkov:skip=CKV_AWS_111:This policy needs unconstrained CreateRepository privileges
41-
#checkov:skip=CKV_AWS_356:This policy should allow "*" as a resource for restrictable actions
42-
statement {
43-
actions = [
44-
"ecr:CreateRepository",
45-
"ecr:GetAuthorizationToken",
46-
]
47-
resources = ["*"]
48-
}
49-
statement {
50-
actions = [
51-
"ecr:BatchCheckLayerAvailability",
52-
"ecr:BatchGetImage",
53-
"ecr:CompleteLayerUpload",
54-
"ecr:DeleteRepository",
55-
"ecr:DescribeImages",
56-
"ecr:DescribeImageScanFindings",
57-
"ecr:DescribeRepositories",
58-
"ecr:GetDownloadUrlForLayer",
59-
"ecr:GetLifecyclePolicy",
60-
"ecr:GetLifecyclePolicyPreview",
61-
"ecr:InitiateLayerUpload",
62-
"ecr:ListImages",
63-
"ecr:ListTagsForResource",
64-
"ecr:PutImage",
65-
"ecr:TagResource",
66-
"ecr:UntagResource",
67-
"ecr:UploadLayerPart",
68-
]
69-
resources = [aws_ecr_repository.this.arn]
70-
}
71-
}
72-
73-
resource "aws_iam_policy" "rw_this" {
74-
name = "${var.repo_name}-ecr-rw-${var.environment}"
75-
description = "policy to allow read/write into ${var.repo_name} ECR"
76-
policy = data.aws_iam_policy_document.rw_this.json
77-
78-
tags = var.tags
79-
}
80-
81-
# The trust policy that allows GitHub Actions OIDC-based connections from the
82-
# repository. The definition is explicitly linked to the name of the GitHub repo.
83-
data "aws_iam_policy_document" "gh_trust" {
84-
statement {
85-
actions = ["sts:AssumeRoleWithWebIdentity"]
86-
principals {
87-
type = "Federated"
88-
identifiers = [var.oidc_arn]
89-
}
90-
condition {
91-
test = "StringLike"
92-
variable = "token.actions.githubusercontent.com:sub"
93-
values = ["repo:${var.gh_organization}/${var.repo_name}:*"] # "repo:MITLibraries//<repo-name>:ref:refs/branch/${var.environment}" eventually
94-
}
95-
condition {
96-
test = "StringLike"
97-
variable = "token.actions.githubusercontent.com:aud"
98-
values = ["sts.amazonaws.com"]
99-
}
100-
}
101-
}
102-
103-
# Role for GitHub Action OIDC connections from the lambdas repository
104-
resource "aws_iam_role" "gha_this" {
105-
name = "${var.repo_name}-gha-${var.environment}"
106-
assume_role_policy = data.aws_iam_policy_document.gh_trust.json
107-
108-
tags = var.tags
109-
}
110-
111-
resource "aws_iam_role_policy_attachment" "gha_ecr_rw" {
112-
role = aws_iam_role.gha_this.name
113-
policy_arn = aws_iam_policy.rw_this.arn
114-
}
115-
resource "aws_iam_role_policy_attachment" "gha_ecr_login" {
116-
role = aws_iam_role.gha_this.name
117-
policy_arn = var.login_policy_arn
118-
}
6+
# Capture the AWS Region for the provider that was used to invoke this module call
7+
data "aws_region" "current" {}

modules/ecr/outputs.tf

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
1-
21
### Outputs
2+
33
# ecr repository_name
44
output "repository_name" {
55
description = "The name of the ECR repository"
66
value = aws_ecr_repository.this.name
77
sensitive = false
88
}
99

10-
1110
# ecr repository_url
1211
output "repository_url" {
1312
description = "The URL of the ECR repository"
1413
value = aws_ecr_repository.this.repository_url
1514
sensitive = false
1615
}
1716

18-
1917
# ecr role so that we can add the updatefunctioncode to it after the lambda itself is created
2018
output "gha_role" {
2119
description = "Github action role used to update the ECR repository"
22-
value = aws_iam_role.gha_this.name
20+
value = data.aws_region.current.name == "us-east-1" ? aws_iam_role.gha_this[0].name : null
2321
sensitive = false
2422
}
2523

modules/ecr/ssm_outputs.tf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ resource "aws_ssm_parameter" "ecr_repository_url" {
2929
resource "aws_ssm_parameter" "gha_role" {
3030
#checkov:skip=CKV_AWS_337:By default we are not encrypting parameters in tfoutput_ssm_path
3131
#checkov:skip=CKV2_AWS_34:By default we are not encrypting parameters in tfoutput_ssm_path
32+
count = data.aws_region.current.name == "us-east-1" ? 1 : 0
3233
type = "String"
3334
name = "${var.tfoutput_ssm_path}/${var.repo_name}/gha-role"
34-
value = aws_iam_role.gha_this.name
35+
value = aws_iam_role.gha_this[0].name
3536
description = "Github action role used to update the ${var.repo_name} ECR repository"
3637
tags = var.tags
3738
}

0 commit comments

Comments
 (0)