Skip to content

Commit 0088681

Browse files
feat: Added org_id and source_accounts to allow SNS topic to receive AWS Config events from sub-accounts
Add optional org_id and source_accounts variables to the stack module's SNS topic policy to support AWS Organizations and Control Tower integrations.
1 parent 411c188 commit 0088681

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-0
lines changed

modules/stack/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ You can additionally configure other submodules in this manner:
160160
| [aws_sns_topic.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |
161161
| [aws_sns_topic_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_policy) | resource |
162162
| [aws_sns_topic_subscription.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
163+
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
163164
| [aws_iam_policy_document.sns_topic_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
164165

165166
## Inputs
@@ -174,8 +175,10 @@ You can additionally configure other submodules in this manner:
174175
| <a name="input_logwriter"></a> [logwriter](#input\_logwriter) | Variables for AWS CloudWatch Logs collection. | <pre>object({<br> log_group_name_patterns = optional(list(string))<br> log_group_name_prefixes = optional(list(string))<br> exclude_log_group_name_prefixes = optional(list(string))<br> buffering_interval = optional(number)<br> buffering_size = optional(number)<br> filter_name = optional(string)<br> filter_pattern = optional(string)<br> num_workers = optional(number)<br> discovery_rate = optional(string, "24 hours")<br> lambda_memory_size = optional(number)<br> lambda_timeout = optional(number)<br> lambda_env_vars = optional(map(string))<br> lambda_runtime = optional(string)<br> code_uri = optional(string)<br> sam_release_version = optional(string)<br> })</pre> | `null` | no |
175176
| <a name="input_metricstream"></a> [metricstream](#input\_metricstream) | Variables for AWS CloudWatch Metrics Stream collection. | <pre>object({<br> include_filters = optional(list(object({ namespace = string, metric_names = optional(list(string)) })))<br> exclude_filters = optional(list(object({ namespace = string, metric_names = optional(list(string)) })))<br> buffering_interval = optional(number)<br> buffering_size = optional(number)<br> sam_release_version = optional(string)<br> })</pre> | `null` | no |
176177
| <a name="input_name"></a> [name](#input\_name) | Name of role. Since this name must be unique within the<br>account, it will be reused for most of the resources created by this<br>module. | `string` | n/a | yes |
178+
| <a name="input_org_id"></a> [org\_id](#input\_org\_id) | Optional AWS Organizations ID. If set, adds an AllowAWSConfigFromOrg statement on the SNS topic that allows publishes by aws:PrincipalOrgID. Useful for AWS Control Tower integrations. | `string` | `null` | no |
177179
| <a name="input_s3_bucket_lifecycle_expiration"></a> [s3\_bucket\_lifecycle\_expiration](#input\_s3\_bucket\_lifecycle\_expiration) | Expiration in days for S3 objects in collection bucket | `number` | `4` | no |
178180
| <a name="input_sam_release_version"></a> [sam\_release\_version](#input\_sam\_release\_version) | Release version for SAM apps as defined on github.com/observeinc/aws-sam-apps. | `string` | `null` | no |
181+
| <a name="input_source_accounts"></a> [source\_accounts](#input\_source\_accounts) | List of AWS account IDs allowed to publish to the SNS topic via AWS Config. Useful for sub-accounts in AWS Organizations and Control Tower integrations.<br>The current account ID is automatically included when this list is non-empty. | `list(string)` | <pre>[<br> "891377094505"<br>]</pre> | no |
179182
| <a name="input_verbosity"></a> [verbosity](#input\_verbosity) | Logging verbosity for Lambda. Highest log verbosity is 9. | `number` | `null` | no |
180183

181184
## Outputs

modules/stack/sns.tf

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
data "aws_caller_identity" "current" {}
2+
3+
locals {
4+
# When source_accounts is set, automatically include the current (audit) account
5+
# to ensure the local AWS Config can also publish to the SNS topic
6+
effective_source_accounts = length(var.source_accounts) > 0 ? distinct(concat(
7+
var.source_accounts,
8+
[data.aws_caller_identity.current.account_id]
9+
)) : []
10+
}
11+
112
resource "aws_sns_topic" "this" {
213
name_prefix = "${var.name}-"
314
}
@@ -40,6 +51,65 @@ data "aws_iam_policy_document" "sns_topic_policy" {
4051
aws_sns_topic.this.arn,
4152
]
4253
}
54+
55+
# Optional: if org_id is set, add an AllowAWSConfigFromOrg statement that
56+
# allows config.amazonaws.com to publish, restricted by aws:PrincipalOrgID.
57+
dynamic "statement" {
58+
for_each = var.org_id == null ? [] : [1]
59+
content {
60+
sid = "AllowAWSConfigFromOrg"
61+
effect = "Allow"
62+
63+
principals {
64+
type = "Service"
65+
identifiers = ["config.amazonaws.com"]
66+
}
67+
68+
actions = [
69+
"SNS:Publish",
70+
]
71+
72+
resources = [
73+
aws_sns_topic.this.arn
74+
]
75+
76+
condition {
77+
test = "StringEquals"
78+
variable = "aws:PrincipalOrgID"
79+
values = [var.org_id]
80+
}
81+
}
82+
}
83+
# Optional: if source_accounts is non-empty, add an AllowAWSConfigFromSourceAccounts
84+
# statement that restricts publishes by AWS:SourceAccount.
85+
# This automatically includes the current (audit) account ID.
86+
dynamic "statement" {
87+
for_each = length(local.effective_source_accounts) == 0 ? [] : [1]
88+
content {
89+
sid = "AllowAWSConfigFromSourceAccounts"
90+
effect = "Allow"
91+
92+
principals {
93+
type = "Service"
94+
identifiers = ["config.amazonaws.com"]
95+
}
96+
97+
actions = [
98+
"SNS:Publish",
99+
]
100+
101+
resources = [
102+
aws_sns_topic.this.arn,
103+
]
104+
105+
condition {
106+
test = "StringEquals"
107+
variable = "AWS:SourceAccount"
108+
values = local.effective_source_accounts
109+
}
110+
}
111+
}
112+
43113
}
44114

45115
resource "aws_sns_topic_subscription" "this" {

modules/stack/variables.tf

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,23 @@ variable "sam_release_version" {
137137
type = string
138138
default = null
139139
}
140+
141+
# Optional AWS Organizations ID. If set, we'll add an AllowAWSConfigFromOrg
142+
# statement on the SNS topic that restricts publishes by aws:PrincipalOrgID.
143+
variable "org_id" {
144+
description = "Optional AWS Organizations ID. If set, adds an AllowAWSConfigFromOrg statement on the SNS topic that allows publishes by aws:PrincipalOrgID. Useful for AWS Control Tower integrations."
145+
type = string
146+
default = null
147+
}
148+
149+
# Optional list of AWS SourceAccount IDs. If non-empty, we'll add a
150+
# statement that restricts publishes using the AWS:SourceAccount condition.
151+
# The current (audit) account ID is automatically included in the list.
152+
variable "source_accounts" {
153+
type = list(string)
154+
default = ["891377094505"]
155+
description = <<-EOF
156+
List of AWS account IDs allowed to publish to the SNS topic via AWS Config. Useful for sub-accounts in AWS Organizations and Control Tower integrations.
157+
The current account ID is automatically included when this list is non-empty.
158+
EOF
159+
}

0 commit comments

Comments
 (0)