Skip to content

Commit 470ea87

Browse files
committed
terraform, aws: fix implementation no longer hardcoding user-sa role
1 parent 4cd2848 commit 470ea87

File tree

1 file changed

+114
-28
lines changed

1 file changed

+114
-28
lines changed

terraform/aws/bucket-access.tf

Lines changed: 114 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,145 @@
1+
/*
2+
Creates one aws_s3_bucket_policy per bucket - there can't be more than one as
3+
they otherwise replace each other when applied.
4+
5+
The bucket policies grant bucket specific permissions to specific IAM Roles
6+
based on them having `bucket_admin_access` or `bucket_readonly_access`
7+
referencing the bucket via `var.hub_cloud_permissions`.
8+
*/
9+
110
locals {
2-
# Nested for loop, thanks to https://www.daveperrett.com/articles/2021/08/19/nested-for-each-with-terraform/
3-
hub_role_bucket = flatten([
11+
/*
12+
The bucket_role_actions local variable defined below is a list of objects
13+
generated from `var.hub_cloud_permissions` roles and their respective
14+
bucket_admin_access and bucket_readonly_access lists.
15+
16+
If for example `var.hub_cloud_permissions` is:
17+
18+
hub_cloud_permissions:
19+
staging:
20+
user-sa:
21+
bucket_admin_access: [scratch-staging]
22+
sciencecore:
23+
user-sa:
24+
bucket_admin_access: [scratch-sciencecore]
25+
bucket_readonly_access: [persistent-sciencecore]
26+
admin-sa:
27+
bucket_admin_access: [scratch-sciencecore, persistent-sciencecore]
28+
29+
Then, the `local.bucket_role_actions` will look like below, with one list
30+
item for each element in all `bucket_admin/readonly_access` lists:
31+
32+
bucket_role_actions:
33+
- bucket: scratch-staging
34+
role: staging
35+
actions: ["s3:*"]
36+
- bucket: scratch-sciencecore
37+
role: sciencecore
38+
actions: ["s3:*"]
39+
- bucket: scratch-sciencecore
40+
role: sciencecore-admin-sa
41+
actions: ["s3:*"]
42+
- bucket: persistent-sciencecore
43+
role: sciencecore
44+
actions: ["s3:ListBucket", "s3:GetObject", "s3:GetObjectVersion"]
45+
- bucket: persistent-sciencecore
46+
role: sciencecore
47+
actions: ["s3:*"]
48+
*/
49+
bucket_role_actions = flatten([
450
for hub, hub_value in var.hub_cloud_permissions : [
551
for role, role_value in hub_value : flatten([
652
[
753
for bucket in role_value.bucket_admin_access : {
8-
// id can be simplified, it was set to not change anything
9-
id = role == "user-sa" ? "${hub}.${bucket}" : "${hub}.${role}.${bucket}.admin"
54+
bucket = bucket
1055
// role should match the id set in irsa.tf
1156
role = role == "user-sa" ? hub : "${hub}-${role}"
12-
bucket = bucket
1357
actions = ["s3:*"]
1458
}
1559
],
1660
[
1761
for bucket in role_value.bucket_readonly_access : {
18-
id = "${hub}.${role}.${bucket}.readonly"
19-
// role should match the id set in irsa.tf
20-
role = role == "user-sa" ? hub : "${hub}-${role}"
2162
bucket = bucket
63+
// role should match the id set in irsa.tf
64+
role = role == "user-sa" ? hub : "${hub}-${role}"
2265
actions = [
2366
"s3:ListBucket",
2467
"s3:GetObject",
2568
"s3:GetObjectVersion",
2669
]
2770
}
28-
]
71+
],
2972
])
3073
]
3174
])
3275
}
3376

34-
// FIXME: there can only be one declared per bucket, so if we have multiple
35-
// roles that has permissions, we need to merge them
36-
data "aws_iam_policy_document" "bucket_admin_access" {
37-
for_each = { for index, hrb in local.hub_role_bucket : hrb.id => hrb }
38-
statement {
39-
effect = "Allow"
40-
actions = each.value.actions
41-
principals {
42-
type = "AWS"
43-
identifiers = [
44-
aws_iam_role.irsa_role[each.value.role].arn
77+
locals {
78+
/*
79+
The `local.bucket_role_actions_lists` variable defined below is reprocessing
80+
`local.bucket_role_actions` to a dictionary with one key per bucket with
81+
associated permissions.
82+
83+
bucket_role_actions_lists:
84+
scratch-staging:
85+
- bucket: scratch-staging
86+
role: staging
87+
actions: ["s3:*"]
88+
scratch-sciencecore:
89+
- bucket: scratch-sciencecore
90+
role: sciencecore
91+
actions: ["s3:*"]
92+
- bucket: scratch-sciencecore
93+
role: sciencecore-admin-sa
94+
actions: ["s3:*"]
95+
persistent-sciencecore:
96+
- bucket: persistent-sciencecore
97+
role: sciencecore
98+
actions: ["s3:ListBucket", "s3:GetObject", "s3:GetObjectVersion"]
99+
- bucket: persistent-sciencecore
100+
role: sciencecore
101+
actions: ["s3:*"]
102+
*/
103+
bucket_role_actions_lists = {
104+
for bucket, _ in var.user_buckets :
105+
bucket => [for bra in local.bucket_role_actions : bra if bra.bucket == bucket]
106+
// Filter out user_buckets not mentioned in hub_cloud_permissions
107+
if length([for bra in local.bucket_role_actions : bra if bra.bucket == bucket]) != 0
108+
}
109+
}
110+
111+
112+
113+
data "aws_iam_policy_document" "bucket_policy" {
114+
for_each = local.bucket_role_actions_lists
115+
116+
// Only one policy document can be declared per bucket, so we provide multiple
117+
// "statement" in this policy.
118+
dynamic "statement" {
119+
for_each = { for index, bra in each.value : "${bra.bucket}.${bra.role}" => bra }
120+
121+
content {
122+
effect = "Allow"
123+
actions = statement.value.actions
124+
principals {
125+
type = "AWS"
126+
identifiers = [
127+
aws_iam_role.irsa_role[statement.value.role].arn
128+
]
129+
}
130+
resources = [
131+
# Grant access only to the bucket and its contents
132+
aws_s3_bucket.user_buckets[statement.value.bucket].arn,
133+
"${aws_s3_bucket.user_buckets[statement.value.bucket].arn}/*",
45134
]
46135
}
47-
resources = [
48-
# Grant access only to the bucket and its contents
49-
aws_s3_bucket.user_buckets[each.value.bucket].arn,
50-
"${aws_s3_bucket.user_buckets[each.value.bucket].arn}/*",
51-
]
52136
}
53137
}
54138

139+
// There can only be one of these per bucket, if more are defined they will end
140+
// up replacing each other without terraform indicating there is trouble.
55141
resource "aws_s3_bucket_policy" "user_bucket_access" {
56-
for_each = { for index, hrb in local.hub_role_bucket : hrb.id => hrb }
57-
bucket = aws_s3_bucket.user_buckets[each.value.bucket].id
58-
policy = data.aws_iam_policy_document.bucket_admin_access[each.key].json
142+
for_each = local.bucket_role_actions_lists
143+
bucket = aws_s3_bucket.user_buckets[each.key].id
144+
policy = data.aws_iam_policy_document.bucket_policy[each.key].json
59145
}

0 commit comments

Comments
 (0)