From 33041ec7b1429fd75185c4aba3025f627f337999 Mon Sep 17 00:00:00 2001 From: Evan Shoshan Date: Thu, 9 Oct 2025 11:28:24 -0400 Subject: [PATCH 1/3] feat: allow for additional policy statements on sqs queue policy --- modules/karpenter/README.md | 3 ++- modules/karpenter/main.tf | 40 ++++++++++++++++++++++++++++++++++ modules/karpenter/variables.tf | 26 ++++++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/modules/karpenter/README.md b/modules/karpenter/README.md index df1b123479..621e5fc0f3 100644 --- a/modules/karpenter/README.md +++ b/modules/karpenter/README.md @@ -142,7 +142,7 @@ No modules. | [iam\_policy\_description](#input\_iam\_policy\_description) | IAM policy description | `string` | `"Karpenter controller IAM policy"` | no | | [iam\_policy\_name](#input\_iam\_policy\_name) | Name of the IAM policy | `string` | `"KarpenterController"` | no | | [iam\_policy\_path](#input\_iam\_policy\_path) | Path of the IAM policy | `string` | `"/"` | no | -| [iam\_policy\_statements](#input\_iam\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | +| [iam\_policy\_statements](#input\_iam\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | | [iam\_policy\_use\_name\_prefix](#input\_iam\_policy\_use\_name\_prefix) | Determines whether the name of the IAM policy (`iam_policy_name`) is used as a prefix | `bool` | `true` | no | | [iam\_role\_description](#input\_iam\_role\_description) | IAM role description | `string` | `"Karpenter controller IAM role"` | no | | [iam\_role\_max\_session\_duration](#input\_iam\_role\_max\_session\_duration) | Maximum API session duration in seconds between 3600 and 43200 | `number` | `null` | no | @@ -169,6 +169,7 @@ No modules. | [queue\_kms\_master\_key\_id](#input\_queue\_kms\_master\_key\_id) | The ID of an AWS-managed customer master key (CMK) for Amazon SQS or a custom CMK | `string` | `null` | no | | [queue\_managed\_sse\_enabled](#input\_queue\_managed\_sse\_enabled) | Boolean to enable server-side encryption (SSE) of message content with SQS-owned encryption keys | `bool` | `true` | no | | [queue\_name](#input\_queue\_name) | Name of the SQS queue | `string` | `null` | no | +| [queue\_policy\_additional\_statements](#input\_queue\_policy\_additional\_statements) | Additional policy statements to add to the SQS queue policy |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | | [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no | | [rule\_name\_prefix](#input\_rule\_name\_prefix) | Prefix used for all event bridge rules | `string` | `"Karpenter"` | no | | [service\_account](#input\_service\_account) | Service account to associate with the Karpenter Pod Identity | `string` | `"karpenter"` | no | diff --git a/modules/karpenter/main.tf b/modules/karpenter/main.tf index 227a2f6aac..0e3dc8f22b 100644 --- a/modules/karpenter/main.tf +++ b/modules/karpenter/main.tf @@ -166,6 +166,46 @@ data "aws_iam_policy_document" "queue" { ] } } + + dynamic "statement" { + for_each = var.queue_policy_additional_statements != null ? var.queue_policy_additional_statements : [] + content { + sid = statement.value.sid + actions = statement.value.actions + not_actions = statement.value.not_actions + effect = statement.value.effect + resources = statement.value.resources + not_resources = statement.value.not_resources + + dynamic "principals" { + for_each = statement.value.principals != null ? statement.value.principals : [] + + content { + type = principals.value.type + identifiers = principals.value.identifiers + } + } + + dynamic "not_principals" { + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] + + content { + type = not_principals.value.type + identifiers = not_principals.value.identifiers + } + } + + dynamic "condition" { + for_each = statement.value.condition != null ? statement.value.condition : [] + + content { + test = condition.value.test + values = condition.value.values + variable = condition.value.variable + } + } + } + } } resource "aws_sqs_queue_policy" "this" { diff --git a/modules/karpenter/variables.tf b/modules/karpenter/variables.tf index f0725f6a58..ff896e6a25 100644 --- a/modules/karpenter/variables.tf +++ b/modules/karpenter/variables.tf @@ -204,6 +204,32 @@ variable "queue_kms_data_key_reuse_period_seconds" { default = null } +variable "queue_policy_additional_statements" { + description = "Additional policy statements to add to the SQS queue policy" + type = list(object({ + sid = optional(string) + actions = optional(list(string)) + not_actions = optional(list(string)) + effect = optional(string) + resources = optional(list(string)) + not_resources = optional(list(string)) + principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + not_principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + condition = optional(list(object({ + test = string + values = list(string) + variable = string + }))) + })) + default = null +} + ################################################################################ # Node IAM Role ################################################################################ From 0d87d99572daaa02a5d136dedf89dae6d1c41715 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Mon, 20 Oct 2025 15:10:42 -0500 Subject: [PATCH 2/3] fix: Update variable type and run `pre-commit` --- .pre-commit-config.yaml | 2 +- modules/karpenter/README.md | 4 ++-- modules/karpenter/main.tf | 6 ++++-- modules/karpenter/variables.tf | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5198bb3f6b..260cc8c757 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.101.0 + rev: v1.103.0 hooks: - id: terraform_fmt - id: terraform_docs diff --git a/modules/karpenter/README.md b/modules/karpenter/README.md index 621e5fc0f3..a7b0be4473 100644 --- a/modules/karpenter/README.md +++ b/modules/karpenter/README.md @@ -142,7 +142,7 @@ No modules. | [iam\_policy\_description](#input\_iam\_policy\_description) | IAM policy description | `string` | `"Karpenter controller IAM policy"` | no | | [iam\_policy\_name](#input\_iam\_policy\_name) | Name of the IAM policy | `string` | `"KarpenterController"` | no | | [iam\_policy\_path](#input\_iam\_policy\_path) | Path of the IAM policy | `string` | `"/"` | no | -| [iam\_policy\_statements](#input\_iam\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | +| [iam\_policy\_statements](#input\_iam\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed |
list(object({ # TODO - change to `map(object({...}))` in next major version
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | | [iam\_policy\_use\_name\_prefix](#input\_iam\_policy\_use\_name\_prefix) | Determines whether the name of the IAM policy (`iam_policy_name`) is used as a prefix | `bool` | `true` | no | | [iam\_role\_description](#input\_iam\_role\_description) | IAM role description | `string` | `"Karpenter controller IAM role"` | no | | [iam\_role\_max\_session\_duration](#input\_iam\_role\_max\_session\_duration) | Maximum API session duration in seconds between 3600 and 43200 | `number` | `null` | no | @@ -169,7 +169,7 @@ No modules. | [queue\_kms\_master\_key\_id](#input\_queue\_kms\_master\_key\_id) | The ID of an AWS-managed customer master key (CMK) for Amazon SQS or a custom CMK | `string` | `null` | no | | [queue\_managed\_sse\_enabled](#input\_queue\_managed\_sse\_enabled) | Boolean to enable server-side encryption (SSE) of message content with SQS-owned encryption keys | `bool` | `true` | no | | [queue\_name](#input\_queue\_name) | Name of the SQS queue | `string` | `null` | no | -| [queue\_policy\_additional\_statements](#input\_queue\_policy\_additional\_statements) | Additional policy statements to add to the SQS queue policy |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | +| [queue\_policy\_additional\_statements](#input\_queue\_policy\_additional\_statements) | Additional policy statements to add to the SQS queue policy |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | | [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no | | [rule\_name\_prefix](#input\_rule\_name\_prefix) | Prefix used for all event bridge rules | `string` | `"Karpenter"` | no | | [service\_account](#input\_service\_account) | Service account to associate with the Karpenter Pod Identity | `string` | `"karpenter"` | no | diff --git a/modules/karpenter/main.tf b/modules/karpenter/main.tf index 0e3dc8f22b..b4f03847ff 100644 --- a/modules/karpenter/main.tf +++ b/modules/karpenter/main.tf @@ -145,6 +145,7 @@ data "aws_iam_policy_document" "queue" { ] } } + statement { sid = "DenyHTTP" effect = "Deny" @@ -168,9 +169,10 @@ data "aws_iam_policy_document" "queue" { } dynamic "statement" { - for_each = var.queue_policy_additional_statements != null ? var.queue_policy_additional_statements : [] + for_each = var.queue_policy_additional_statements != null ? var.queue_policy_additional_statements : {} + content { - sid = statement.value.sid + sid = try(coalesce(statement.value.sid, statement.key)) actions = statement.value.actions not_actions = statement.value.not_actions effect = statement.value.effect diff --git a/modules/karpenter/variables.tf b/modules/karpenter/variables.tf index ff896e6a25..6d3c5500c9 100644 --- a/modules/karpenter/variables.tf +++ b/modules/karpenter/variables.tf @@ -112,7 +112,7 @@ variable "iam_role_source_assume_policy_documents" { variable "iam_policy_statements" { description = "A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific IAM permissions as needed" - type = list(object({ + type = list(object({ # TODO - change to `map(object({...}))` in next major version sid = optional(string) actions = optional(list(string)) not_actions = optional(list(string)) @@ -206,7 +206,7 @@ variable "queue_kms_data_key_reuse_period_seconds" { variable "queue_policy_additional_statements" { description = "Additional policy statements to add to the SQS queue policy" - type = list(object({ + type = map(object({ sid = optional(string) actions = optional(list(string)) not_actions = optional(list(string)) From 6c6028036828aefb54d2a7177ee6d3f5eff0874d Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Mon, 20 Oct 2025 15:13:12 -0500 Subject: [PATCH 3/3] fix: Update variable name and description --- modules/karpenter/README.md | 2 +- modules/karpenter/main.tf | 2 +- modules/karpenter/variables.tf | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/karpenter/README.md b/modules/karpenter/README.md index a7b0be4473..e31bb3073d 100644 --- a/modules/karpenter/README.md +++ b/modules/karpenter/README.md @@ -169,7 +169,7 @@ No modules. | [queue\_kms\_master\_key\_id](#input\_queue\_kms\_master\_key\_id) | The ID of an AWS-managed customer master key (CMK) for Amazon SQS or a custom CMK | `string` | `null` | no | | [queue\_managed\_sse\_enabled](#input\_queue\_managed\_sse\_enabled) | Boolean to enable server-side encryption (SSE) of message content with SQS-owned encryption keys | `bool` | `true` | no | | [queue\_name](#input\_queue\_name) | Name of the SQS queue | `string` | `null` | no | -| [queue\_policy\_additional\_statements](#input\_queue\_policy\_additional\_statements) | Additional policy statements to add to the SQS queue policy |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | +| [queue\_policy\_statements](#input\_queue\_policy\_statements) | A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific SQS queue policy permissions as needed |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | | [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no | | [rule\_name\_prefix](#input\_rule\_name\_prefix) | Prefix used for all event bridge rules | `string` | `"Karpenter"` | no | | [service\_account](#input\_service\_account) | Service account to associate with the Karpenter Pod Identity | `string` | `"karpenter"` | no | diff --git a/modules/karpenter/main.tf b/modules/karpenter/main.tf index b4f03847ff..53e961244c 100644 --- a/modules/karpenter/main.tf +++ b/modules/karpenter/main.tf @@ -169,7 +169,7 @@ data "aws_iam_policy_document" "queue" { } dynamic "statement" { - for_each = var.queue_policy_additional_statements != null ? var.queue_policy_additional_statements : {} + for_each = var.queue_policy_statements != null ? var.queue_policy_statements : {} content { sid = try(coalesce(statement.value.sid, statement.key)) diff --git a/modules/karpenter/variables.tf b/modules/karpenter/variables.tf index 6d3c5500c9..6d3d042516 100644 --- a/modules/karpenter/variables.tf +++ b/modules/karpenter/variables.tf @@ -204,8 +204,8 @@ variable "queue_kms_data_key_reuse_period_seconds" { default = null } -variable "queue_policy_additional_statements" { - description = "Additional policy statements to add to the SQS queue policy" +variable "queue_policy_statements" { + description = "A list of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) - used for adding specific SQS queue policy permissions as needed" type = map(object({ sid = optional(string) actions = optional(list(string))