Skip to content

Commit 400965e

Browse files
authored
fix(SA-603): enabling AWS Config root (mgmt) account (#34)
1 parent 752121d commit 400965e

File tree

15 files changed

+221
-6
lines changed

15 files changed

+221
-6
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,7 @@ The `terraform-docs` utility is used to generate this README. Follow the below s
707707
| <a name="input_region"></a> [region](#input\_region) | The region to deploy the resources | `string` | n/a | yes |
708708
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to the resources | `map(string)` | n/a | yes |
709709
| <a name="input_access_analyzer"></a> [access\_analyzer](#input\_access\_analyzer) | Configuration for the AWS Access Analyzer service | <pre>object({<br/> enable_unused_analyzer = optional(bool, true)<br/> # Indicates whether to enable the unused AWS Access Analyzer service<br/> unused_analyzer_name = optional(string, "lza-unused-access-analyzer")<br/> # The name of the unused AWS Access Analyzer service<br/> unused_access_age = optional(number, 90)<br/> })</pre> | `null` | no |
710-
| <a name="input_config"></a> [config](#input\_config) | Configuration for the securityhub organization managed rules | <pre>object({<br/> stackset_name_prefix = optional(string, "lza-config-")<br/> # The prefix added to the stacksets<br/> rule_groups = optional(map(object({<br/> associations = list(string)<br/> # List of organizational units to deploy the managed rules<br/> description = string<br/> # Description for the rule group<br/> enabled_regions = optional(list(string), null)<br/> # List of regions to enable these rules<br/> exclude_accounts = optional(list(string), null)<br/> # The list of accounts to exclude from the organization managed rule<br/> rules = map(object({<br/> description = string<br/> # The description of the organization managed rules<br/> identifier = string<br/> # The identifier of the organization managed rule<br/> inputs = optional(map(string), {})<br/> # The identifier of the organization managed rule scope<br/> resource_types = list(string)<br/> # The list of resource types to scope the organization managed rule<br/> max_execution_frequency = optional(string, null)<br/> # The max_execution_frequency of the rule<br/> }))<br/> })), {})<br/> # The configuration for the securityhub organization managed rules<br/> })</pre> | <pre>{<br/> "rule_groups": {}<br/>}</pre> | no |
710+
| <a name="input_config"></a> [config](#input\_config) | Configuration for the securityhub organization managed rules | <pre>object({<br/> stackset_name_prefix = optional(string, "lza-config-")<br/><br/> # The prefix added to the stacksets<br/> rule_groups = optional(map(object({<br/> associations = list(string)<br/> # List of organizational units to deploy the managed rules<br/> description = string<br/> # Description for the rule group<br/> enabled_regions = optional(list(string), null)<br/> # List of regions to enable these rules<br/> exclude_accounts = optional(list(string), null)<br/> # The list of accounts to exclude from the organization managed rule<br/> rules = map(object({<br/> description = string<br/> # The description of the organization managed rules<br/> identifier = string<br/> # The identifier of the organization managed rule<br/> inputs = optional(map(string), {})<br/> # The identifier of the organization managed rule scope<br/> resource_types = list(string)<br/> # The list of resource types to scope the organization managed rule<br/> max_execution_frequency = optional(string, null)<br/> # The max_execution_frequency of the rule<br/> }))<br/> })), {})<br/> # The configuration for the securityhub organization managed rules<br/> })</pre> | <pre>{<br/> "rule_groups": {}<br/>}</pre> | no |
711711
| <a name="input_inspector"></a> [inspector](#input\_inspector) | Organizational configuration for the AWS Inspector service | <pre>object({<br/> account_id = optional(string, null)<br/> # The delegated administrator account ID for the AWS Inspector service<br/> enable = optional(bool, false)<br/> # Indicates whether to enable the AWS Inspector service<br/> enable_ec2_scan = optional(bool, false)<br/> # Indicates whether to enable the AWS Inspector service for EC2 instances<br/> enable_ecr_scan = optional(bool, false)<br/> # Indicates whether to enable the AWS Inspector service for ECR repositories<br/> enable_lambda_scan = optional(bool, false)<br/> # Indicates whether to enable the AWS Inspector service for Lambda functions<br/> enable_lambda_code_scan = optional(bool, false)<br/> # Indicates whether to enable the AWS Inspector service for Lambda code<br/> })</pre> | <pre>{<br/> "enable": false<br/>}</pre> | no |
712712
| <a name="input_macie"></a> [macie](#input\_macie) | Configuration for the AWS Macie service | <pre>object({<br/> enable = optional(bool, false)<br/> # Indicates whether to enable the AWS Macie service should be enabled in all accounts<br/> excluded_accounts = optional(list(string), null)<br/> # The list of accounts to exclude from the AWS Macie service<br/> frequency = optional(string, "FIFTEEN_MINUTES")<br/> # The frequency at which the AWS Macie service should be enabled<br/> organizational_units = optional(list(string), null)<br/> # The list of member accounts to associate with the AWS Macie service<br/> stackset_name = optional(string, "lza-macie-configuration")<br/> })</pre> | `null` | no |
713713
| <a name="input_notifications"></a> [notifications](#input\_notifications) | Configuration for the notifications | <pre>object({<br/> email = optional(object({<br/> addresses = optional(list(string), [])<br/> }), null)<br/> slack = optional(object({<br/> lamdba_name = optional(string, "lz-securityhub-all-notifications-slack")<br/> webhook_url = string<br/> }), null)<br/> teams = optional(object({<br/> lamdba_name = optional(string, "lz-securityhub-all-notifications-teams")<br/> webhook_url = string<br/> }), null)<br/> })</pre> | <pre>{<br/> "email": {<br/> "addresses": []<br/> },<br/> "slack": null,<br/> "teams": null<br/>}</pre> | no |
@@ -721,3 +721,4 @@ The `terraform-docs` utility is used to generate this README. Follow the below s
721721
| <a name="output_securityhub_policy_associations"></a> [securityhub\_policy\_associations](#output\_securityhub\_policy\_associations) | A map of policy associations by policy name |
722722
| <a name="output_securityhub_policy_configurations"></a> [securityhub\_policy\_configurations](#output\_securityhub\_policy\_configurations) | A map of all the policies to the central configuration arns |
723723
<!-- END_TF_DOCS -->
724+

config.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module "config_rule_groups" {
1111
enabled_regions = try(each.value.enabled_regions, null)
1212
exclude_accounts = each.value.exclude_accounts
1313
organizational_units = each.value.associations
14+
accounts = [local.mgmt_account_id]
1415
permission_model = "SERVICE_MANAGED"
1516
tags = local.tags
1617

@@ -20,4 +21,3 @@ module "config_rule_groups" {
2021
"rules" = each.value.rules
2122
})
2223
}
23-

data.tf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11

22
## Find the current region
33
data "aws_region" "current" {}
4+
5+
# Get the mgmt account id via the Organization
6+
data "aws_organizations_organization" "this" {}

examples/basic/main.tf

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,23 @@ module "guardduty_us_east_1" {
4949
}
5050
}
5151

52+
module "config_home" {
53+
source = "github.com/appvia/terraform-aws-compliance//modules/config?ref=fix/sa-603-pushing-aws-config-rules-to-mgmt"
54+
55+
control_tower_sns_topic_arn = "arn:aws:sns:eu-west-1:123456789033:aws-controltower-AllConfigNotifications"
56+
logarchive_account_id = "987654321012"
57+
tags = local.tags
58+
}
59+
60+
module "config_us_east_1" {
61+
source = "github.com/appvia/terraform-aws-compliance//modules/config?ref=fix/sa-603-pushing-aws-config-rules-to-mgmt"
62+
63+
control_tower_sns_topic_arn = "arn:aws:sns:eu-west-1:123456789033:aws-controltower-AllConfigNotifications"
64+
logarchive_account_id = "987654321012"
65+
config_retention_in_days = 90
66+
tags = local.tags
67+
}
68+
5269
module "compliance" {
5370
source = "../.."
5471

examples/basic/providers.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,13 @@ provider "aws" {
1010
alias = "audit_eu_west_2"
1111
region = "eu-west-2"
1212
}
13+
14+
provider "aws" {
15+
alias = "management_us_east_1"
16+
region = "us-east-1"
17+
}
18+
19+
provider "aws" {
20+
alias = "management_eu_west_2"
21+
region = "eu-west-2"
22+
}

locals.tf

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11

22
locals {
33
## The current region
4-
region = data.aws_region.current.name
4+
region = data.aws_region.current.region
5+
56
## Tag applied to all resources
67
tags = merge(var.tags, {})
8+
9+
# using Organization - resolve the mgmt account id (it's only ever the one account in the Management OU)
10+
mgmt_account_id = data.aws_organizations_organization.this.master_account_id
711
}

modules/config/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!-- BEGIN_TF_DOCS -->
2+
## Requirements
3+
4+
| Name | Version |
5+
|------|---------|
6+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0.7 |
7+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.0.0 |
8+
9+
## Providers
10+
11+
| Name | Version |
12+
|------|---------|
13+
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.0.0 |
14+
15+
## Modules
16+
17+
No modules.
18+
19+
## Resources
20+
21+
| Name | Type |
22+
|------|------|
23+
| [aws_config_configuration_recorder.mgmt_config_recorder](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_configuration_recorder) | resource |
24+
| [aws_config_delivery_channel.mgmt_config_delivery_channel](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_delivery_channel) | resource |
25+
| [aws_config_retention_configuration.mgmt_config_retention](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_retention_configuration) | resource |
26+
| [aws_iam_role.mgmt_config_recorder_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
27+
| [aws_iam_policy_document.mgmt_config_recorder_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
28+
| [aws_organizations_organization.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization) | data source |
29+
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
30+
31+
## Inputs
32+
33+
| Name | Description | Type | Default | Required |
34+
|------|-------------|------|---------|:--------:|
35+
| <a name="input_config_retention_in_days"></a> [config\_retention\_in\_days](#input\_config\_retention\_in\_days) | The number of days to store config historical data (defaults to one year) | `number` | `366` | no |
36+
| <a name="input_control_tower_sns_topic_arn"></a> [control\_tower\_sns\_topic\_arn](#input\_control\_tower\_sns\_topic\_arn) | The ARN of the SNS topic created by Control Tower for AWS notifications | `string` | n/a | yes |
37+
| <a name="input_logarchive_account_id"></a> [logarchive\_account\_id](#input\_logarchive\_account\_id) | The AWS account id for the logarchive account created by Control Tower | `string` | n/a | yes |
38+
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to the resources | `map(string)` | `{}` | no |
39+
40+
## Outputs
41+
42+
| Name | Description |
43+
|------|-------------|
44+
| <a name="output_aws_config_delivery_channel_id"></a> [aws\_config\_delivery\_channel\_id](#output\_aws\_config\_delivery\_channel\_id) | The ID of Config delivery channel |
45+
| <a name="output_aws_config_recorder_id"></a> [aws\_config\_recorder\_id](#output\_aws\_config\_recorder\_id) | The ID of Config recorder |
46+
<!-- END_TF_DOCS -->

modules/config/data.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
## Find the current region
3+
data "aws_region" "current" {}
4+
5+
# Get the mgmt account id via the Organization
6+
data "aws_organizations_organization" "this" {}

modules/config/locals.tf

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
locals {
3+
## The current region
4+
region = data.aws_region.current.region
5+
6+
## Tag applied to all resources
7+
tags = merge(var.tags, {})
8+
9+
## used to resovle the target S3 bucket for Config data
10+
logarchive_account_id = var.logarchive_account_id
11+
12+
# capture the Organization - to then resolve the mgmt account id in locals
13+
organization_id = data.aws_organizations_organization.this.id
14+
}

modules/config/main.tf

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
## the mgmt account is not configured by Control Tower, for AWS Config
2+
# Create a recorder in mgmt account
3+
# Create and associate a delivery channel - that delivery channel is to existing logarchive S3 bucket and SNS topic created by Control Tower
4+
data "aws_iam_policy_document" "mgmt_config_recorder_policy" {
5+
statement {
6+
effect = "Allow"
7+
8+
principals {
9+
type = "Service"
10+
identifiers = ["config.amazonaws.com"]
11+
}
12+
13+
actions = ["sts:AssumeRole"]
14+
}
15+
}
16+
17+
resource "aws_iam_role" "mgmt_config_recorder_role" {
18+
name = format("lz-mgmt-awsconfig-recorder-%s-role", local.region)
19+
assume_role_policy = data.aws_iam_policy_document.mgmt_config_recorder_policy.json
20+
21+
tags = local.tags
22+
}
23+
24+
# this AWS resources has no tags attribute
25+
resource "aws_config_configuration_recorder" "mgmt_config_recorder" {
26+
27+
name = "lz-mgmt-recorder"
28+
role_arn = aws_iam_role.mgmt_config_recorder_role.arn
29+
30+
recording_group {
31+
all_supported = true
32+
include_global_resource_types = true
33+
resource_types = null
34+
# if all_supported is true, must be specify any exclusions
35+
# exclusion_by_resource_types {
36+
# resource_types = []
37+
# }
38+
recording_strategy {
39+
use_only = "ALL_SUPPORTED_RESOURCE_TYPES"
40+
}
41+
}
42+
43+
recording_mode {
44+
recording_frequency = "CONTINUOUS"
45+
# recording_mode_override {}
46+
}
47+
}
48+
49+
# this AWS resources has no tags attribute
50+
resource "aws_config_delivery_channel" "mgmt_config_delivery_channel" {
51+
name = "lz-mgmt-delivery-channel"
52+
s3_bucket_name = format("aws-controltower-logs-%s-%s", local.logarchive_account_id, local.region)
53+
s3_key_prefix = local.organization_id
54+
sns_topic_arn = var.control_tower_sns_topic_arn
55+
56+
snapshot_delivery_properties {
57+
# default is TwentyFour_Hours; but this is the mgmt account
58+
delivery_frequency = "Three_Hours"
59+
}
60+
61+
depends_on = [aws_config_configuration_recorder.mgmt_config_recorder]
62+
}
63+
64+
resource "aws_config_retention_configuration" "mgmt_config_retention" {
65+
retention_period_in_days = var.config_retention_in_days
66+
}

0 commit comments

Comments
 (0)