From 04fd14dcbd18b7f68ae40ab92db4505a0d77e726 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Thu, 26 Dec 2024 11:30:53 -0600 Subject: [PATCH] feat: Allow generic assume role conditions --- .pre-commit-config.yaml | 2 +- examples/iam-assumable-role/main.tf | 16 +++++++++++- .../iam-assumable-role-with-oidc/README.md | 2 +- .../iam-assumable-role-with-oidc/variables.tf | 8 ++++-- modules/iam-assumable-role/README.md | 1 + modules/iam-assumable-role/main.tf | 26 ++++++++++++++++++- modules/iam-assumable-role/variables.tf | 10 +++++++ modules/iam-assumable-roles/README.md | 1 + modules/iam-assumable-roles/main.tf | 20 ++++++++++++++ modules/iam-assumable-roles/variables.tf | 10 +++++++ wrappers/iam-assumable-role/main.tf | 1 + wrappers/iam-assumable-roles/main.tf | 1 + 12 files changed, 92 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7e4e7daf..19691e52 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.96.1 + rev: v1.96.3 hooks: - id: terraform_fmt - id: terraform_wrapper_module_for_each diff --git a/examples/iam-assumable-role/main.tf b/examples/iam-assumable-role/main.tf index f4e6c3f0..0721acb8 100644 --- a/examples/iam-assumable-role/main.tf +++ b/examples/iam-assumable-role/main.tf @@ -5,6 +5,7 @@ provider "aws" { ############################### # IAM assumable role for admin ############################### + module "iam_assumable_role_admin" { source = "../../modules/iam-assumable-role" @@ -36,9 +37,12 @@ module "iam_assumable_role_admin" { ########################################## # IAM assumable role with custom policies ########################################## + module "iam_assumable_role_custom" { source = "../../modules/iam-assumable-role" + create_role = true + trusted_role_arns = [ "arn:aws:iam::307990089504:root", ] @@ -47,7 +51,13 @@ module "iam_assumable_role_custom" { "codedeploy.amazonaws.com" ] - create_role = true + trust_policy_conditions = [ + { + test = "StringEquals" + variable = "aws:PrincipalOrgID" + values = ["o-someorgid"] + } + ] role_name_prefix = "custom-" role_requires_mfa = false @@ -65,6 +75,7 @@ module "iam_assumable_role_custom" { ########################################## # IAM assumable role with inline policy ########################################## + module "iam_assumable_role_inline_policy" { source = "../../modules/iam-assumable-role" @@ -109,6 +120,7 @@ module "iam_assumable_role_inline_policy" { #################################################### # IAM assumable role with multiple sts external ids #################################################### + module "iam_assumable_role_sts" { source = "../../modules/iam-assumable-role" @@ -141,6 +153,7 @@ module "iam_assumable_role_sts" { ######################################### # IAM assumable role with custom trust policy ######################################### + module "iam_assumable_role_custom_trust_policy" { source = "../../modules/iam-assumable-role" @@ -180,6 +193,7 @@ data "aws_iam_policy_document" "custom_trust_policy" { ######################################### # IAM policy ######################################### + module "iam_policy" { source = "../../modules/iam-policy" diff --git a/modules/iam-assumable-role-with-oidc/README.md b/modules/iam-assumable-role-with-oidc/README.md index f73c086a..58bddad9 100644 --- a/modules/iam-assumable-role-with-oidc/README.md +++ b/modules/iam-assumable-role-with-oidc/README.md @@ -50,7 +50,7 @@ No modules. | [oidc\_fully\_qualified\_audiences](#input\_oidc\_fully\_qualified\_audiences) | The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise. | `set(string)` | `[]` | no | | [oidc\_fully\_qualified\_subjects](#input\_oidc\_fully\_qualified\_subjects) | The fully qualified OIDC subjects to be added to the role policy | `set(string)` | `[]` | no | | [oidc\_subjects\_with\_wildcards](#input\_oidc\_subjects\_with\_wildcards) | The OIDC subject using wildcards to be added to the role policy | `set(string)` | `[]` | no | -| [provider\_trust\_policy\_conditions](#input\_provider\_trust\_policy\_conditions) | [Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy | `any` | `[]` | no | +| [provider\_trust\_policy\_conditions](#input\_provider\_trust\_policy\_conditions) | [Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy |
list(object({
test = string
variable = string
values = list(string)
}))
| `[]` | no | | [provider\_url](#input\_provider\_url) | URL of the OIDC Provider. Use provider\_urls to specify several URLs. | `string` | `""` | no | | [provider\_urls](#input\_provider\_urls) | List of URLs of the OIDC Providers | `list(string)` | `[]` | no | | [role\_description](#input\_role\_description) | IAM Role description | `string` | `""` | no | diff --git a/modules/iam-assumable-role-with-oidc/variables.tf b/modules/iam-assumable-role-with-oidc/variables.tf index dcbe117f..dc06d733 100644 --- a/modules/iam-assumable-role-with-oidc/variables.tf +++ b/modules/iam-assumable-role-with-oidc/variables.tf @@ -114,6 +114,10 @@ variable "allow_self_assume_role" { variable "provider_trust_policy_conditions" { description = "[Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy" - type = any - default = [] + type = list(object({ + test = string + variable = string + values = list(string) + })) + default = [] } diff --git a/modules/iam-assumable-role/README.md b/modules/iam-assumable-role/README.md index e2c0828f..822fcc76 100644 --- a/modules/iam-assumable-role/README.md +++ b/modules/iam-assumable-role/README.md @@ -70,6 +70,7 @@ No modules. | [role\_session\_name](#input\_role\_session\_name) | role\_session\_name for roles which require this parameter when being assumed. By default, you need to set your own username as role\_session\_name | `list(string)` |
[
"${aws:username}"
]
| no | | [role\_sts\_externalid](#input\_role\_sts\_externalid) | STS ExternalId condition values to use with a role (when MFA is not required) | `any` | `[]` | no | | [tags](#input\_tags) | A map of tags to add to IAM role resources | `map(string)` | `{}` | no | +| [trust\_policy\_conditions](#input\_trust\_policy\_conditions) | [Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy |
list(object({
test = string
variable = string
values = list(string)
}))
| `[]` | no | | [trusted\_role\_actions](#input\_trusted\_role\_actions) | Additional trusted role actions | `list(string)` |
[
"sts:AssumeRole",
"sts:TagSession"
]
| no | | [trusted\_role\_arns](#input\_trusted\_role\_arns) | ARNs of AWS entities who can assume these roles | `list(string)` | `[]` | no | | [trusted\_role\_services](#input\_trusted\_role\_services) | AWS Services that can assume these roles | `list(string)` | `[]` | no | diff --git a/modules/iam-assumable-role/main.tf b/modules/iam-assumable-role/main.tf index 719a3873..ed5f39a0 100644 --- a/modules/iam-assumable-role/main.tf +++ b/modules/iam-assumable-role/main.tf @@ -50,6 +50,7 @@ data "aws_iam_policy_document" "assume_role" { dynamic "condition" { for_each = length(local.role_sts_externalid) != 0 ? [true] : [] + content { test = "StringEquals" variable = "sts:ExternalId" @@ -59,12 +60,23 @@ data "aws_iam_policy_document" "assume_role" { dynamic "condition" { for_each = var.role_requires_session_name ? [1] : [] + content { test = "StringEquals" variable = "sts:RoleSessionName" values = var.role_session_name } } + + dynamic "condition" { + for_each = var.trust_policy_conditions + + content { + test = condition.value.test + variable = condition.value.variable + values = condition.value.values + } + } } } @@ -121,6 +133,7 @@ data "aws_iam_policy_document" "assume_role_with_mfa" { dynamic "condition" { for_each = length(local.role_sts_externalid) != 0 ? [true] : [] + content { test = "StringEquals" variable = "sts:ExternalId" @@ -130,12 +143,23 @@ data "aws_iam_policy_document" "assume_role_with_mfa" { dynamic "condition" { for_each = var.role_requires_session_name ? [1] : [] + content { test = "StringEquals" variable = "sts:RoleSessionName" values = var.role_session_name } } + + dynamic "condition" { + for_each = var.trust_policy_conditions + + content { + test = condition.value.test + variable = condition.value.variable + values = condition.value.values + } + } } } @@ -255,6 +279,6 @@ resource "aws_iam_role_policy" "inline" { count = local.create_iam_role_inline_policy ? 1 : 0 role = aws_iam_role.this[0].name - name_prefix = "${var.role_name}_inline_" + name_prefix = "${try(coalesece(var.role_name, var.role_name_prefix), "")}_inline_" policy = data.aws_iam_policy_document.inline[0].json } diff --git a/modules/iam-assumable-role/variables.tf b/modules/iam-assumable-role/variables.tf index fc9bd2c9..2ac20084 100644 --- a/modules/iam-assumable-role/variables.tf +++ b/modules/iam-assumable-role/variables.tf @@ -16,6 +16,16 @@ variable "trusted_role_services" { default = [] } +variable "trust_policy_conditions" { + description = "[Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy" + type = list(object({ + test = string + variable = string + values = list(string) + })) + default = [] +} + variable "mfa_age" { description = "Max age of valid MFA (in seconds) for roles which require MFA" type = number diff --git a/modules/iam-assumable-roles/README.md b/modules/iam-assumable-roles/README.md index 36af68a1..3780f916 100644 --- a/modules/iam-assumable-roles/README.md +++ b/modules/iam-assumable-roles/README.md @@ -66,6 +66,7 @@ No modules. | [readonly\_role\_policy\_arns](#input\_readonly\_role\_policy\_arns) | List of policy ARNs to use for readonly role | `list(string)` |
[
"arn:aws:iam::aws:policy/ReadOnlyAccess"
]
| no | | [readonly\_role\_requires\_mfa](#input\_readonly\_role\_requires\_mfa) | Whether readonly role requires MFA | `bool` | `true` | no | | [readonly\_role\_tags](#input\_readonly\_role\_tags) | A map of tags to add to readonly role resource. | `map(string)` | `{}` | no | +| [trust\_policy\_conditions](#input\_trust\_policy\_conditions) | [Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy |
list(object({
test = string
variable = string
values = list(string)
}))
| `[]` | no | | [trusted\_role\_actions](#input\_trusted\_role\_actions) | Additional trusted role actions | `list(string)` |
[
"sts:AssumeRole",
"sts:TagSession"
]
| no | | [trusted\_role\_arns](#input\_trusted\_role\_arns) | ARNs of AWS entities who can assume these roles | `list(string)` | `[]` | no | | [trusted\_role\_services](#input\_trusted\_role\_services) | AWS Services that can assume these roles | `list(string)` | `[]` | no | diff --git a/modules/iam-assumable-roles/main.tf b/modules/iam-assumable-roles/main.tf index 73dd7986..73de0dde 100644 --- a/modules/iam-assumable-roles/main.tf +++ b/modules/iam-assumable-roles/main.tf @@ -86,6 +86,16 @@ data "aws_iam_policy_document" "assume_role" { type = "Service" identifiers = var.trusted_role_services } + + dynamic "condition" { + for_each = var.trust_policy_conditions + + content { + test = condition.value.test + variable = condition.value.variable + values = condition.value.values + } + } } } @@ -181,6 +191,16 @@ data "aws_iam_policy_document" "assume_role_with_mfa" { variable = "aws:MultiFactorAuthAge" values = [var.mfa_age] } + + dynamic "condition" { + for_each = var.trust_policy_conditions + + content { + test = condition.value.test + variable = condition.value.variable + values = condition.value.values + } + } } } diff --git a/modules/iam-assumable-roles/variables.tf b/modules/iam-assumable-roles/variables.tf index 83f8907e..7f797550 100644 --- a/modules/iam-assumable-roles/variables.tf +++ b/modules/iam-assumable-roles/variables.tf @@ -16,6 +16,16 @@ variable "trusted_role_services" { default = [] } +variable "trust_policy_conditions" { + description = "[Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy" + type = list(object({ + test = string + variable = string + values = list(string) + })) + default = [] +} + variable "mfa_age" { description = "Max age of valid MFA (in seconds) for roles which require MFA" type = number diff --git a/wrappers/iam-assumable-role/main.tf b/wrappers/iam-assumable-role/main.tf index aef04cfe..fa47ba48 100644 --- a/wrappers/iam-assumable-role/main.tf +++ b/wrappers/iam-assumable-role/main.tf @@ -33,4 +33,5 @@ module "wrapper" { trusted_role_actions = try(each.value.trusted_role_actions, var.defaults.trusted_role_actions, ["sts:AssumeRole", "sts:TagSession"]) trusted_role_arns = try(each.value.trusted_role_arns, var.defaults.trusted_role_arns, []) trusted_role_services = try(each.value.trusted_role_services, var.defaults.trusted_role_services, []) + trust_policy_conditions = try(each.value.trust_policy_conditions, var.defaults.trust_policy_conditions, []) } diff --git a/wrappers/iam-assumable-roles/main.tf b/wrappers/iam-assumable-roles/main.tf index ca3a5c92..b3f4bbf2 100644 --- a/wrappers/iam-assumable-roles/main.tf +++ b/wrappers/iam-assumable-roles/main.tf @@ -31,4 +31,5 @@ module "wrapper" { trusted_role_actions = try(each.value.trusted_role_actions, var.defaults.trusted_role_actions, ["sts:AssumeRole", "sts:TagSession"]) trusted_role_arns = try(each.value.trusted_role_arns, var.defaults.trusted_role_arns, []) trusted_role_services = try(each.value.trusted_role_services, var.defaults.trusted_role_services, []) + trust_policy_conditions = try(each.value.trust_policy_conditions, var.defaults.trust_policy_conditions, []) }