-
-
Notifications
You must be signed in to change notification settings - Fork 2
fix: module/roles-to-principals has long trust policies #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
## Breaking changes | ||
|
||
### overridable_team_permission_sets_enabled deprecated, default changed | ||
|
||
In `modules/roles-to-principals`, the input `overridable_team_permission_sets_enabled` | ||
has been deprecated and the default value has been changed to `false`. This will | ||
cause changes in the Terraform plan, but it is likely that they will be | ||
inconsequential, because this feature never worked with Dynamic Terraform Roles, | ||
even though it was introduced in the same PR. | ||
|
||
To enable the intended behavior, a new feature has been added to `aws-team-roles` | ||
and `modules/iam-roles`: `trusted_identity_permission_sets`. This feature | ||
allows you to explicitly configure permission sets in the `identity` account to | ||
be allowed to assume roles in other accounts, just as you do with `trusted_teams`. | ||
This has the added advantage of being able to configure non-team permissions | ||
sets to be trusted. |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,123 @@ | ||||||||||||||
# Part of the Terraform Dynamic Roles implementation, | ||||||||||||||
# this files reads in all the `aws-team` and `aws-team-roles` stacks | ||||||||||||||
# and extracts the relevant information. | ||||||||||||||
|
||||||||||||||
# The `utils_describe_stacks` data resources use the Cloud Posse Utils provider to describe Atmos stacks, and then | ||||||||||||||
# we merge the results into `local.all_team_vars`. This is the same as running the following locally: | ||||||||||||||
# ``` | ||||||||||||||
# atmos describe stacks --components=aws-teams,aws-team-roles --component-types=terraform --sections=vars | ||||||||||||||
# ``` | ||||||||||||||
# The result of these stack descriptions includes all metadata for the given components. For example, we now | ||||||||||||||
# can filter the result to find all stacks where either `aws-teams` or `aws-team-roles` are deployed. | ||||||||||||||
# Note that unlike in earlier implementations, we now expect `aws-team-roles` to be deployed in all accounts, | ||||||||||||||
# including the `identity` account. | ||||||||||||||
# | ||||||||||||||
# In particular, we can use this data to find the name of the account via `null-label` (defined by | ||||||||||||||
# `null-label.descriptor_formats.account_name`, typically `<tenant>-<stage>`) where team roles are deployed. | ||||||||||||||
# We then determine which roles are provisioned and which teams can access any given role in any particular account. | ||||||||||||||
# | ||||||||||||||
# `descriptor_formats.account_name` is typically defined in `stacks/orgs/NAMESPACE/_defaults.yaml`, and if not | ||||||||||||||
# defined, the stack name will default to `stage`.` | ||||||||||||||
# | ||||||||||||||
# If `namespace` is included in `descriptor_formats.account_name`, then we additionally filter to only stacks with | ||||||||||||||
# the same `namespace` as `module.this.namespace`. See `local.stack_namespace_index` and `local.stack_namespace_index` | ||||||||||||||
# | ||||||||||||||
# https://atmos.tools/cli/commands/describe/stacks/ | ||||||||||||||
# https://registry.terraform.io/providers/cloudposse/utils/latest/docs/data-sources/describe_stacks | ||||||||||||||
|
||||||||||||||
locals { | ||||||||||||||
# We would like to use code like this: | ||||||||||||||
# teams_stacks = local.dynamic_role_enabled ? { for k, v ... } : {} | ||||||||||||||
# but that generates an error: "Inconsistent conditional result types" | ||||||||||||||
# See https://github.com/hashicorp/terraform/issues/33303 | ||||||||||||||
# To work around this, we have "empty" values that depend on the condition. | ||||||||||||||
empty_map = { | ||||||||||||||
true = null | ||||||||||||||
false = {} | ||||||||||||||
} | ||||||||||||||
empty = local.empty_map[local.dynamic_role_enabled] | ||||||||||||||
|
||||||||||||||
# If a namespace is included with the stack name, only loop through stacks in the same namespace | ||||||||||||||
# zero-based index showing position of the namespace in the stack name | ||||||||||||||
stack_namespace_index = try(index(module.this.normalized_context.descriptor_formats.stack.labels, "namespace"), -1) | ||||||||||||||
stack_has_namespace = local.stack_namespace_index >= 0 | ||||||||||||||
# stack_account_map maps stack name to account short name | ||||||||||||||
stack_account_map = { for k, v in module.atmos : k => lookup(v.descriptors, "account_name", v.stage) } | ||||||||||||||
|
||||||||||||||
# ASSUMPTIONS: The stack pattern is the same for all accounts and uses the same delimiter as null-label | ||||||||||||||
|
||||||||||||||
# Part 1, get all the aws-teams information: | ||||||||||||||
# Get all the aws-teams stacks, optionally filtered by namespace, so that we only work in the current namespace. | ||||||||||||||
teams_stacks = local.dynamic_role_enabled ? { | ||||||||||||||
for k, v in yamldecode(data.utils_describe_stacks.teams[0].output) : k => v if !local.stack_has_namespace || try(split(module.this.delimiter, k)[local.stack_namespace_index] == module.this.namespace, false) | ||||||||||||||
} : local.empty | ||||||||||||||
|
||||||||||||||
# Extract components.terraform.aws-teams.vars. Key is the stack name, value is the vars. | ||||||||||||||
teams_vars = { for k, v in local.teams_stacks : k => v.components.terraform.aws-teams.vars if try(v.components.terraform.aws-teams.vars, null) != null } | ||||||||||||||
# Extract components.terraform.aws-teams.vars.teams_config, drop the stack name. | ||||||||||||||
teams_config = local.dynamic_role_enabled ? values(local.teams_vars)[0].teams_config : local.empty | ||||||||||||||
Comment on lines
+56
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard against empty - teams_config = local.dynamic_role_enabled ? values(local.teams_vars)[0].teams_config : local.empty
+ teams_config = local.dynamic_role_enabled ? try(values(local.teams_vars)[0].teams_config, {}) : local.empty 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||
# Extract enabled team names from components.terraform.aws-teams.vars.teams_config | ||||||||||||||
team_names = [for k, v in local.teams_config : k if try(v.enabled, true)] | ||||||||||||||
# Convert team names to IAM role ARNs | ||||||||||||||
team_arns = { for team_name in local.team_names : team_name => format(local.iam_role_arn_templates[local.account_role_map.identity], team_name) } | ||||||||||||||
# Now we have local.team_arns which is a list of IAM role ARNs for each team. | ||||||||||||||
# This covers all the roles we have explicitly configured as teams. | ||||||||||||||
|
||||||||||||||
# Part 2, get all the aws-team-roles information: | ||||||||||||||
# Get all the aws-team-roles stacks, optionally filtered by namespace, so that we only work in the current namespace. | ||||||||||||||
team_roles_stacks = local.dynamic_role_enabled ? { | ||||||||||||||
for k, v in yamldecode(data.utils_describe_stacks.team_roles[0].output) : k => v if !local.stack_has_namespace || try(split(module.this.delimiter, k)[local.stack_namespace_index] == module.this.namespace, false) | ||||||||||||||
} : local.empty | ||||||||||||||
|
||||||||||||||
# Extract components.terraform.aws-team-roles.vars | ||||||||||||||
team_roles_vars = { for k, v in local.team_roles_stacks : k => v.components.terraform.aws-team-roles.vars if try(v.components.terraform.aws-team-roles.vars, null) != null } | ||||||||||||||
|
||||||||||||||
Comment on lines
+73
to
+74
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dash-in-key traversal is invalid; fix aws-team-roles access. - team_roles_vars = { for k, v in local.team_roles_stacks : k => v.components.terraform.aws-team-roles.vars if try(v.components.terraform.aws-team-roles.vars, null) != null }
+ team_roles_vars = { for k, v in local.team_roles_stacks : k => v.components.terraform["aws-team-roles"].vars if try(v.components.terraform["aws-team-roles"].vars, null) != null } 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||
# Merge all the vars together so that for each stack name (the keys in the maps), | ||||||||||||||
# we can map it to the appropriate account short name, which we will use as keys in the final map. | ||||||||||||||
all_team_vars = merge(local.teams_vars, local.team_roles_vars) | ||||||||||||||
|
||||||||||||||
} | ||||||||||||||
|
||||||||||||||
data "utils_describe_stacks" "teams" { | ||||||||||||||
count = local.dynamic_role_enabled ? 1 : 0 | ||||||||||||||
|
||||||||||||||
components = ["aws-teams"] | ||||||||||||||
component_types = ["terraform"] | ||||||||||||||
sections = ["vars"] | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
data "utils_describe_stacks" "team_roles" { | ||||||||||||||
count = local.dynamic_role_enabled ? 1 : 0 | ||||||||||||||
|
||||||||||||||
components = ["aws-team-roles"] | ||||||||||||||
component_types = ["terraform"] | ||||||||||||||
sections = ["vars"] | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
module "atmos" { | ||||||||||||||
# local.all_team_vars is empty map when dynamic_role_enabled is false | ||||||||||||||
for_each = local.all_team_vars | ||||||||||||||
|
||||||||||||||
source = "cloudposse/label/null" | ||||||||||||||
version = "0.25.0" | ||||||||||||||
|
||||||||||||||
enabled = true | ||||||||||||||
namespace = lookup(each.value, "namespace", null) | ||||||||||||||
tenant = lookup(each.value, "tenant", null) | ||||||||||||||
environment = lookup(each.value, "environment", null) | ||||||||||||||
stage = lookup(each.value, "stage", null) | ||||||||||||||
name = lookup(each.value, "name", null) | ||||||||||||||
delimiter = lookup(each.value, "delimiter", null) | ||||||||||||||
attributes = lookup(each.value, "attributes", []) | ||||||||||||||
tags = lookup(each.value, "tags", {}) | ||||||||||||||
additional_tag_map = lookup(each.value, "additional_tag_map", {}) | ||||||||||||||
label_order = lookup(each.value, "label_order", []) | ||||||||||||||
regex_replace_chars = lookup(each.value, "regex_replace_chars", null) | ||||||||||||||
id_length_limit = lookup(each.value, "id_length_limit", null) | ||||||||||||||
label_key_case = lookup(each.value, "label_key_case", null) | ||||||||||||||
label_value_case = lookup(each.value, "label_value_case", null) | ||||||||||||||
descriptor_formats = lookup(each.value, "descriptor_formats", {}) | ||||||||||||||
labels_as_tags = lookup(each.value, "labels_as_tags", []) | ||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use
jsondecode
instead ofyamldecode
The
output
attribute ofdata.utils_describe_stacks
is JSON, soyamldecode
may misinterpret it. Switch tojsondecode
for reliable parsing:Apply the same change on lines 68–70 for
team_roles
.Also applies to: 68-70
🤖 Prompt for AI Agents