From 94c714bf2a4719c8c59ca9ca1ef03b6ac2af991b Mon Sep 17 00:00:00 2001 From: Islam Heggy Date: Tue, 6 Feb 2024 04:30:21 +0200 Subject: [PATCH 01/11] Add support for organization aggregator --- main.tf | 78 ++++++++++++++++++++++++++++++++++++++++++++++++---- outputs.tf | 8 ++++++ variables.tf | 29 +++++++++++++++++++ 3 files changed, 109 insertions(+), 6 deletions(-) diff --git a/main.tf b/main.tf index d0c84cb..24b4b38 100644 --- a/main.tf +++ b/main.tf @@ -95,8 +95,9 @@ module "aws_config_findings_label" { } #----------------------------------------------------------------------------------------------------------------------- -# Optionally create an IAM Role +# Optionally create IAM Roles #----------------------------------------------------------------------------------------------------------------------- +# Create Optional IAM ROLE for S3 bucket and SNS module "iam_role" { count = module.this.enabled && local.create_iam_role ? 1 : 0 source = "cloudposse/iam-role/aws" @@ -124,6 +125,31 @@ module "iam_role" { context = module.this.context } +# Create Optional IAM ROLE for organization wide aggregator +module "iam_role_organization_aggregator" { + count = module.this.enabled && local.create_organization_aggregator_iam_role ? 1 : 0 + source = "cloudposse/iam-role/aws" + version = "0.15.0" + + principals = { + "Service" = ["config.amazonaws.com"] + } + + use_fullname = true + + #policy_documents = [ data.aws_iam_policy_document.config_organization_aggregator_policy[0].json ] + + + policy_document_count = 0 + policy_description = "AWS Config IAM policy for organization aggregator" + role_description = "AWS Config IAM role for organization aggregator" + + attributes = ["aggregator", "config"] + + context = module.this.context +} + + resource "aws_iam_role_policy_attachment" "config_policy_attachment" { count = module.this.enabled && local.create_iam_role ? 1 : 0 @@ -131,10 +157,20 @@ resource "aws_iam_role_policy_attachment" "config_policy_attachment" { policy_arn = data.aws_iam_policy.aws_config_built_in_role.arn } +resource "aws_iam_role_policy_attachment" "organization_config_policy_attachment" { + count = module.this.enabled && local.create_iam_role && local.organization_aggregator ? 1 : 0 + + role = module.iam_role_organization_aggregator[0].name + policy_arn = data.aws_iam_policy.aws_config_organization_role.arn +} data "aws_iam_policy" "aws_config_built_in_role" { arn = "arn:aws:iam::aws:policy/service-role/AWS_ConfigRole" } +data "aws_iam_policy" "aws_config_organization_role" { + arn = "arn:aws:iam::aws:policy/service-role/AWSConfigRoleForOrganizations" +} + data "aws_iam_policy_document" "config_s3_policy" { count = local.create_iam_role ? 1 : 0 @@ -156,6 +192,15 @@ data "aws_iam_policy_document" "config_s3_policy" { } } } +data "aws_iam_policy_document" "config_organization_aggregator_policy" { + + count = local.organization_aggregator ? 1 : 0 + + statement { + effect = "Allow" + actions = ["sts:AssumeRole"] + } +} data "aws_iam_policy_document" "config_sns_policy" { count = local.create_iam_role && local.create_sns_topic ? 1 : 0 @@ -188,11 +233,28 @@ resource "aws_config_configuration_aggregator" "this" { count = local.enabled && local.is_central_account && local.is_global_recorder_region ? 1 : 0 name = module.aws_config_aggregator_label.id - account_aggregation_source { - account_ids = local.child_resource_collector_accounts - all_regions = true + +# Create normal account aggregation source + dynamic "account_aggregation_source" { + for_each = local.organization_aggregator ? []: [1] + content { + account_ids = local.child_resource_collector_accounts + all_regions = true + } + } + +# Create organization aggregation source + dynamic "organization_aggregation_source" { + for_each = local.organization_aggregator ? [1]: [] + content { + all_regions = true + role_arn = local.create_organization_aggregator_iam_role ? module.iam_role_organization_aggregator[0].arn : var.iam_role_organization_aggregator_arn + } } + + + tags = module.this.tags } @@ -202,7 +264,7 @@ resource "aws_config_aggregate_authorization" "child" { # # Authorize each region in a child account to send its data to the global_resource_collector_region of the # central_resource_collector_account - count = local.enabled && var.central_resource_collector_account != null ? 1 : 0 + count = local.enabled && var.central_resource_collector_account != null && local.organization_aggregator == false ? 1 : 0 account_id = var.central_resource_collector_account region = var.global_resource_collector_region @@ -215,7 +277,7 @@ resource "aws_config_aggregate_authorization" "central" { # Multi-Account Multi-Region Data Aggregation and only enabling Multi-Region aggregation within the same account: # # Authorize each region to send its data to the global_resource_collector_region - count = local.enabled && var.central_resource_collector_account == null ? 1 : 0 + count = local.enabled && var.central_resource_collector_account == null && local.organization_aggregator == false ? 1 : 0 account_id = data.aws_caller_identity.this.account_id region = var.global_resource_collector_region @@ -240,4 +302,8 @@ locals { create_sns_topic = module.this.enabled && var.create_sns_topic findings_notification_arn = local.enable_notifications ? (var.findings_notification_arn != null ? var.findings_notification_arn : module.sns_topic[0].sns_topic.arn) : null create_iam_role = module.this.enabled && var.create_iam_role + + organization_aggregator = var.is_organization_aggregator + + create_organization_aggregator_iam_role = var.create_organization_aggregator_iam_role } diff --git a/outputs.tf b/outputs.tf index 41de9e8..0e71aa3 100644 --- a/outputs.tf +++ b/outputs.tf @@ -21,6 +21,14 @@ output "iam_role" { value = local.create_iam_role ? module.iam_role[0].arn : var.iam_role_arn } +output "iam_role_organization_aggregator" { + description = <<-DOC + IAM Role used to make read or write requests to the delivery channel and to describe the AWS resources associated with + the account. + DOC + value = local.create_organization_aggregator_iam_role ? module.iam_role_organization_aggregator[0].arn : var.iam_role_organization_aggregator_arn +} + output "sns_topic" { description = "SNS topic" value = local.create_sns_topic ? module.sns_topic[0].sns_topic : null diff --git a/variables.tf b/variables.tf index 3a8a367..2a24e4a 100644 --- a/variables.tf +++ b/variables.tf @@ -69,6 +69,14 @@ variable "create_iam_role" { default = false } +variable "create_organization_aggregator_iam_role" { + description = "Flag to indicate whether an IAM Role should be created to grant the proper permissions for AWS Config to send logs from organization accounts" + type = bool + default = false +} + + + variable "iam_role_arn" { description = <<-DOC The ARN for an IAM Role AWS Config uses to make read or write requests to the delivery channel and to describe the @@ -84,6 +92,21 @@ variable "iam_role_arn" { type = string } +variable "iam_role_organization_aggregator_arn" { + description = <<-DOC + The ARN for an IAM Role AWS Config uses for the organization aggregator that fetches AWS config data from aws accounts. + This is only used if create_organization_aggregator_iam_role is false. + + If you want to use an existing IAM Role, set the value of this to the ARN of the existing topic and set + create_organization_aggregator_iam_role to false. + + See the AWS Docs for further information: + http://docs.aws.amazon.com/config/latest/developerguide/iamrole-permissions.html + DOC + default = null + type = string +} + variable "global_resource_collector_region" { description = "The region that collects AWS Config data for global resources such as IAM" type = string @@ -146,6 +169,12 @@ variable "disabled_aggregation_regions" { default = ["ap-northeast-3"] } +variable "is_organization_aggregator" { + type = bool + default = false + description = "The aggregator is an AWS Organizations aggrgator" +} + variable "allowed_aws_services_for_sns_published" { type = list(string) description = "AWS services that will have permission to publish to SNS topic. Used when no external JSON policy is used" From 434d11adf28bd308ba99586cff0d0ea72c6a8f9a Mon Sep 17 00:00:00 2001 From: Islam Heggy Date: Tue, 6 Feb 2024 04:53:49 +0200 Subject: [PATCH 02/11] chore: apply terraform fmt --- main.tf | 26 +++++++++++++------------- variables.tf | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/main.tf b/main.tf index 24b4b38..bec01a3 100644 --- a/main.tf +++ b/main.tf @@ -137,10 +137,10 @@ module "iam_role_organization_aggregator" { use_fullname = true - #policy_documents = [ data.aws_iam_policy_document.config_organization_aggregator_policy[0].json ] + #policy_documents = [ data.aws_iam_policy_document.config_organization_aggregator_policy[0].json ] - policy_document_count = 0 + policy_document_count = 0 policy_description = "AWS Config IAM policy for organization aggregator" role_description = "AWS Config IAM role for organization aggregator" @@ -193,11 +193,11 @@ data "aws_iam_policy_document" "config_s3_policy" { } } data "aws_iam_policy_document" "config_organization_aggregator_policy" { - + count = local.organization_aggregator ? 1 : 0 statement { - effect = "Allow" + effect = "Allow" actions = ["sts:AssumeRole"] } } @@ -234,26 +234,26 @@ resource "aws_config_configuration_aggregator" "this" { name = module.aws_config_aggregator_label.id -# Create normal account aggregation source + # Create normal account aggregation source dynamic "account_aggregation_source" { - for_each = local.organization_aggregator ? []: [1] - content { + for_each = local.organization_aggregator ? [] : [1] + content { account_ids = local.child_resource_collector_accounts all_regions = true } } -# Create organization aggregation source + # Create organization aggregation source dynamic "organization_aggregation_source" { - for_each = local.organization_aggregator ? [1]: [] - content { + for_each = local.organization_aggregator ? [1] : [] + content { all_regions = true role_arn = local.create_organization_aggregator_iam_role ? module.iam_role_organization_aggregator[0].arn : var.iam_role_organization_aggregator_arn } } - - + + tags = module.this.tags } @@ -303,7 +303,7 @@ locals { findings_notification_arn = local.enable_notifications ? (var.findings_notification_arn != null ? var.findings_notification_arn : module.sns_topic[0].sns_topic.arn) : null create_iam_role = module.this.enabled && var.create_iam_role - organization_aggregator = var.is_organization_aggregator + organization_aggregator = var.is_organization_aggregator create_organization_aggregator_iam_role = var.create_organization_aggregator_iam_role } diff --git a/variables.tf b/variables.tf index 2a24e4a..33b87d0 100644 --- a/variables.tf +++ b/variables.tf @@ -170,8 +170,8 @@ variable "disabled_aggregation_regions" { } variable "is_organization_aggregator" { - type = bool - default = false + type = bool + default = false description = "The aggregator is an AWS Organizations aggrgator" } From 3fe4e27c8ec982fee251d9a4c452db43a0d5363d Mon Sep 17 00:00:00 2001 From: Islam Heggy Date: Tue, 6 Feb 2024 05:39:12 +0200 Subject: [PATCH 03/11] chore: add aws partition to arns --- main.tf | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/main.tf b/main.tf index bec01a3..001b971 100644 --- a/main.tf +++ b/main.tf @@ -137,9 +137,6 @@ module "iam_role_organization_aggregator" { use_fullname = true - #policy_documents = [ data.aws_iam_policy_document.config_organization_aggregator_policy[0].json ] - - policy_document_count = 0 policy_description = "AWS Config IAM policy for organization aggregator" role_description = "AWS Config IAM role for organization aggregator" @@ -164,11 +161,11 @@ resource "aws_iam_role_policy_attachment" "organization_config_policy_attachment policy_arn = data.aws_iam_policy.aws_config_organization_role.arn } data "aws_iam_policy" "aws_config_built_in_role" { - arn = "arn:aws:iam::aws:policy/service-role/AWS_ConfigRole" + arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/service-role/AWS_ConfigRole" } data "aws_iam_policy" "aws_config_organization_role" { - arn = "arn:aws:iam::aws:policy/service-role/AWSConfigRoleForOrganizations" + arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/service-role/AWSConfigRoleForOrganizations" } data "aws_iam_policy_document" "config_s3_policy" { @@ -291,6 +288,7 @@ resource "aws_config_aggregate_authorization" "central" { #----------------------------------------------------------------------------------------------------------------------- data "aws_region" "this" {} data "aws_caller_identity" "this" {} +data "aws_partition" "current" {} locals { enabled = module.this.enabled && !contains(var.disabled_aggregation_regions, data.aws_region.this.name) From cdcbce4f19c2d0fef69df60d509485d8b5289869 Mon Sep 17 00:00:00 2001 From: Islam Heggy Date: Wed, 7 Feb 2024 02:25:37 +0200 Subject: [PATCH 04/11] docs: Update README.md & terraform.md --- README.md | 90 +++++++++++++++++++++++++++++++---------------- docs/terraform.md | 11 +++++- 2 files changed, 70 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index db3a440..4a8b441 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# terraform-aws-config [![Latest Release](https://img.shields.io/github/release/cloudposse/terraform-aws-config.svg)](https://github.com/cloudposse/terraform-aws-config/releases/latest) [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) [![Discourse Forum](https://img.shields.io/discourse/https/ask.sweetops.com/posts.svg)](https://ask.sweetops.com/) +# terraform-aws-config +Latest ReleaseSlack CommunityDiscourse Forum -