Skip to content

Commit 1862fd3

Browse files
committed
feat: added the ability to support multiple repositories
1 parent 2cb3185 commit 1862fd3

10 files changed

Lines changed: 180 additions & 187 deletions

File tree

examples/role/main.tf

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,53 @@ module "common_provider_example" {
6565
Name = "Example Common Provider"
6666
}
6767
}
68+
69+
## Provision a role with multiple repositories
70+
module "mutlple_repositories" {
71+
source = "../../modules/role"
72+
73+
// Basic role details
74+
name = "test-common-role"
75+
description = "Creates a role using the GitHub OIDC provider"
76+
tags = {}
77+
repositories = [
78+
"appvia/app1",
79+
"appvia/app2",
80+
]
81+
permission_boundary_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
82+
read_only_policy_arns = ["arn:aws:iam::aws:policy/ReadOnlyAccess"]
83+
read_write_policy_arns = ["arn:aws:iam::aws:policy/AdministratorAccess"]
84+
85+
read_only_inline_policies = {
86+
"additional" = jsonencode({
87+
Version = "2012-10-17"
88+
Statement = [
89+
{
90+
Effect = "Allow"
91+
Action = [
92+
"s3:GetObject",
93+
"s3:ListBucket",
94+
]
95+
Resource = "*"
96+
},
97+
]
98+
})
99+
}
100+
101+
read_write_inline_policies = {
102+
"additional" = jsonencode({
103+
Version = "2012-10-17"
104+
Statement = [
105+
{
106+
Effect = "Allow"
107+
Action = [
108+
"s3:GetObject",
109+
"s3:PutObject",
110+
"s3:ListBucket",
111+
]
112+
Resource = "*"
113+
},
114+
]
115+
})
116+
}
117+
}

examples/test-bitbucket/README.md

Lines changed: 0 additions & 13 deletions
This file was deleted.

examples/test-bitbucket/main.tf

Lines changed: 0 additions & 57 deletions
This file was deleted.

examples/test-bitbucket/terraform.tf

Lines changed: 0 additions & 15 deletions
This file was deleted.

modules/remote_state/locals.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ locals {
44
account = var.account_id
55

66
## Use provided region or default to the current region
7-
region = coalesce(var.region, data.aws_region.current.name)
7+
region = coalesce(var.region, data.aws_region.current.region)
88

99
## Terraform state bucket name
1010
tf_state_bucket = format("%s-%s-tfstate", local.account, local.region)

modules/role/README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,6 @@ No modules.
578578
|------|-------------|------|---------|:--------:|
579579
| <a name="input_description"></a> [description](#input\_description) | Description of the role being created | `string` | n/a | yes |
580580
| <a name="input_name"></a> [name](#input\_name) | Name of the role to create | `string` | n/a | yes |
581-
| <a name="input_repository"></a> [repository](#input\_repository) | Repository to be allowed in the OIDC federation mapping | `string` | n/a | yes |
582581
| <a name="input_tags"></a> [tags](#input\_tags) | Tags to apply resoures created by this module | `map(string)` | n/a | yes |
583582
| <a name="input_account_id"></a> [account\_id](#input\_account\_id) | The AWS account ID to create the role in | `string` | `null` | no |
584583
| <a name="input_additional_audiences"></a> [additional\_audiences](#input\_additional\_audiences) | Additional audiences to be allowed in the OIDC federation mapping | `list(string)` | `[]` | no |
@@ -598,12 +597,11 @@ No modules.
598597
| <a name="input_read_write_max_session_duration"></a> [read\_write\_max\_session\_duration](#input\_read\_write\_max\_session\_duration) | The maximum session duration (in seconds) that you want to set for the specified role | `number` | `null` | no |
599598
| <a name="input_read_write_policy_arns"></a> [read\_write\_policy\_arns](#input\_read\_write\_policy\_arns) | List of IAM policy ARNs to attach to the read-write role | `list(string)` | `[]` | no |
600599
| <a name="input_region"></a> [region](#input\_region) | The region in which the role will be used (defaulting to the provider region) | `string` | `null` | no |
601-
| <a name="input_repository_uuid"></a> [repository\_uuid](#input\_repository\_uuid) | Repository UUID. You can get it in the repository settings in the OpenID connect provider. | `string` | `null` | no |
600+
| <a name="input_repositories"></a> [repositories](#input\_repositories) | A collection of repositories to to bind the permissions | `list(string)` | `[]` | no |
601+
| <a name="input_repository"></a> [repository](#input\_repository) | Repository to be allowed in the OIDC federation mapping | `string` | `null` | no |
602602
| <a name="input_role_path"></a> [role\_path](#input\_role\_path) | Path under which to create IAM role. | `string` | `null` | no |
603603
| <a name="input_shared_repositories"></a> [shared\_repositories](#input\_shared\_repositories) | List of repositories to provide read access to the remote state | `list(string)` | `[]` | no |
604604
| <a name="input_tf_state_suffix"></a> [tf\_state\_suffix](#input\_tf\_state\_suffix) | A suffix for the terraform statefile, e.g. <repo>-<tf\_state\_suffix>.tfstate | `string` | `""` | no |
605-
| <a name="input_workspace_name"></a> [workspace\_name](#input\_workspace\_name) | The name of the workspace. | `string` | `null` | no |
606-
| <a name="input_workspace_uuid"></a> [workspace\_uuid](#input\_workspace\_uuid) | Workspace UUID. You can get it in the repository settings in the OpenID connect provider. Don't include the brackets and make sure it is lower cased. | `string` | `null` | no |
607605

608606
## Outputs
609607

modules/role/locals.tf

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
locals {
2-
workspace_name = var.workspace_name
3-
workspace_uuid = var.workspace_uuid
4-
repository_uuid = var.repository_uuid
5-
}
6-
71
locals {
82
# The current account ID, if not provided
93
account_id = var.account_id != null ? var.account_id : data.aws_caller_identity.current.account_id
@@ -37,29 +31,14 @@ locals {
3731
subject_env_mapping = ""
3832
subject_tag_mapping = "project_path:{repo}:ref_type:{type}:ref:{ref}"
3933
}
40-
41-
bitbucket = {
42-
url = local.workspace_name != null ? "https://api.bitbucket.org/2.0/workspaces/${local.workspace_name}/pipelines-config/identity/oidc" : ""
43-
44-
audiences = local.workspace_uuid != null ? [
45-
"ari:cloud:bitbucket::workspace/${local.workspace_uuid}",
46-
] : []
47-
48-
subject_reader_mapping = local.repository_uuid != null ? "${local.repository_uuid}:*" : ""
49-
subject_branch_mapping = local.repository_uuid != null ? "${local.repository_uuid}:*" : ""
50-
subject_env_mapping = ""
51-
subject_tag_mapping = ""
52-
}
5334
}
5435
# The devired permission_boundary arn
5536
permission_boundary_by_name = var.permission_boundary != null ? format("arn:aws:iam::%s:policy/%s", local.account_id, var.permission_boundary) : null
5637
# The full ARN of the permission boundary to attach to the role
5738
permission_boundary_arn = var.permission_boundary_arn == null ? local.permission_boundary_by_name : var.permission_boundary_arn
5839
# The region where the iam role will be used
5940
region = var.region != null ? var.region : data.aws_region.current.region
60-
}
6141

62-
locals {
6342
# Find the source control provider from supplied list
6443
common_provider = lookup(local.common_providers, var.common_provider, null)
6544
# The selected provider from the supplied list

modules/role/main.tf

Lines changed: 52 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ data "aws_iam_policy_document" "readonly_assume_role" {
1515

1616
principals {
1717
type = "Federated"
18-
1918
identifiers = [
2019
data.aws_iam_openid_connect_provider.this.arn
2120
]
@@ -27,21 +26,26 @@ data "aws_iam_policy_document" "readonly_assume_role" {
2726
values = concat(local.selected_provider.audiences, var.additional_audiences)
2827
}
2928

30-
condition {
31-
test = "StringLike"
32-
variable = format("%s:sub", trimprefix(local.selected_provider.url, "https://"))
33-
values = [
34-
format(replace(local.selected_provider.subject_reader_mapping, format("/%s/", local.template_keys_regex), "%s"), [
35-
for v in flatten(regexall(local.template_keys_regex, local.selected_provider.subject_reader_mapping)) : {
36-
repo = var.repository
37-
}[v]
38-
]...)
39-
]
29+
## Support the repositories variable
30+
dynamic "condition" {
31+
for_each = toset(concat([var.repository], var.repositories))
32+
33+
content {
34+
test = "StringLike"
35+
variable = format("%s:sub", trimprefix(local.selected_provider.url, "https://"))
36+
values = [
37+
format(replace(local.selected_provider.subject_reader_mapping, format("/%s/", local.template_keys_regex), "%s"), [
38+
for v in flatten(regexall(local.template_keys_regex, local.selected_provider.subject_reader_mapping)) : {
39+
repo = condition.value
40+
}[v]
41+
]...)
42+
]
43+
}
4044
}
4145
}
4246
}
4347

44-
## Provision the read only role
48+
## Provision a read only role to run terraform plan
4549
resource "aws_iam_role" "ro" {
4650
name = local.readonly_role_name
4751
description = var.description
@@ -80,16 +84,11 @@ resource "aws_iam_role_policy_attachment" "ro" {
8084
## Craft the trust policy for the read write role
8185
data "aws_iam_policy_document" "readwrite_assume_role" {
8286
statement {
83-
actions = [
84-
"sts:AssumeRoleWithWebIdentity",
85-
]
86-
87+
actions = ["sts:AssumeRoleWithWebIdentity"]
8788
principals {
8889
type = "Federated"
8990

90-
identifiers = [
91-
data.aws_iam_openid_connect_provider.this.arn
92-
]
91+
identifiers = [data.aws_iam_openid_connect_provider.this.arn]
9392
}
9493

9594
condition {
@@ -98,33 +97,37 @@ data "aws_iam_policy_document" "readwrite_assume_role" {
9897
values = concat(local.selected_provider.audiences, var.additional_audiences)
9998
}
10099

101-
condition {
102-
test = "StringLike"
103-
variable = format("%s:sub", trimprefix(local.selected_provider.url, "https://"))
104-
values = compact([
105-
var.protected_by.branch != null ? format(replace(local.selected_provider.subject_branch_mapping, format("/%s/", local.template_keys_regex), "%s"), [
106-
for v in flatten(regexall(local.template_keys_regex, local.selected_provider.subject_branch_mapping)) : {
107-
repo = var.repository
108-
type = "branch"
109-
ref = var.protected_by.branch
110-
}[v]
111-
]...) : "",
112-
113-
var.protected_by.environment != null ? format(replace(local.selected_provider.subject_env_mapping, format("/%s/", local.template_keys_regex), "%s"), [
114-
for v in flatten(regexall(local.template_keys_regex, local.selected_provider.subject_env_mapping)) : {
115-
repo = var.repository
116-
env = var.protected_by.environment
117-
}[v]
118-
]...) : "",
119-
120-
var.protected_by.tag != null ? format(replace(local.selected_provider.subject_tag_mapping, format("/%s/", local.template_keys_regex), "%s"), [
121-
for v in flatten(regexall(local.template_keys_regex, local.selected_provider.subject_tag_mapping)) : {
122-
repo = var.repository
123-
type = "tag"
124-
ref = var.protected_by.tag
125-
}[v]
126-
]...) : ""
127-
])
100+
dynamic "condition" {
101+
for_each = toset(concat([var.repository], var.repositories))
102+
103+
content {
104+
test = "StringLike"
105+
variable = format("%s:sub", trimprefix(local.selected_provider.url, "https://"))
106+
values = compact([
107+
var.protected_by.branch != null ? format(replace(local.selected_provider.subject_branch_mapping, format("/%s/", local.template_keys_regex), "%s"), [
108+
for v in flatten(regexall(local.template_keys_regex, local.selected_provider.subject_branch_mapping)) : {
109+
repo = condition.value
110+
type = "branch"
111+
ref = var.protected_by.branch
112+
}[v]
113+
]...) : "",
114+
115+
var.protected_by.environment != null ? format(replace(local.selected_provider.subject_env_mapping, format("/%s/", local.template_keys_regex), "%s"), [
116+
for v in flatten(regexall(local.template_keys_regex, local.selected_provider.subject_env_mapping)) : {
117+
repo = condition.value
118+
env = var.protected_by.environment
119+
}[v]
120+
]...) : "",
121+
122+
var.protected_by.tag != null ? format(replace(local.selected_provider.subject_tag_mapping, format("/%s/", local.template_keys_regex), "%s"), [
123+
for v in flatten(regexall(local.template_keys_regex, local.selected_provider.subject_tag_mapping)) : {
124+
repo = condition.value
125+
type = "tag"
126+
ref = var.protected_by.tag
127+
}[v]
128+
]...) : ""
129+
])
130+
}
128131
}
129132
}
130133
}
@@ -168,16 +171,11 @@ resource "aws_iam_role_policy_attachment" "rw" {
168171
## Craft the trust policy for the state reader role
169172
data "aws_iam_policy_document" "state_reader_assume_role" {
170173
statement {
171-
actions = [
172-
"sts:AssumeRoleWithWebIdentity",
173-
]
174+
actions = ["sts:AssumeRoleWithWebIdentity"]
174175

175176
principals {
176-
type = "Federated"
177-
178-
identifiers = [
179-
data.aws_iam_openid_connect_provider.this.arn
180-
]
177+
type = "Federated"
178+
identifiers = [data.aws_iam_openid_connect_provider.this.arn]
181179
}
182180

183181
condition {

0 commit comments

Comments
 (0)