|
| 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 | + |
1 | 10 | 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([ |
4 | 50 | for hub, hub_value in var.hub_cloud_permissions : [
|
5 | 51 | for role, role_value in hub_value : flatten([
|
6 | 52 | [
|
7 | 53 | 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 |
10 | 55 | // role should match the id set in irsa.tf
|
11 | 56 | role = role == "user-sa" ? hub : "${hub}-${role}"
|
12 |
| - bucket = bucket |
13 | 57 | actions = ["s3:*"]
|
14 | 58 | }
|
15 | 59 | ],
|
16 | 60 | [
|
17 | 61 | 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}" |
21 | 62 | bucket = bucket
|
| 63 | + // role should match the id set in irsa.tf |
| 64 | + role = role == "user-sa" ? hub : "${hub}-${role}" |
22 | 65 | actions = [
|
23 | 66 | "s3:ListBucket",
|
24 | 67 | "s3:GetObject",
|
25 | 68 | "s3:GetObjectVersion",
|
26 | 69 | ]
|
27 | 70 | }
|
28 |
| - ] |
| 71 | + ], |
29 | 72 | ])
|
30 | 73 | ]
|
31 | 74 | ])
|
32 | 75 | }
|
33 | 76 |
|
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}/*", |
45 | 134 | ]
|
46 | 135 | }
|
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 |
| - ] |
52 | 136 | }
|
53 | 137 | }
|
54 | 138 |
|
| 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. |
55 | 141 | 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 |
59 | 145 | }
|
0 commit comments