Skip to content

Commit 5b339b6

Browse files
pavanvooturi31Pavan Kumar Vooturi
andauthored
fix: Add Control Tower 4.0 compatibility (#344)
* Add Control Tower 4.0 compatibility fixes * Update sra-easy-setup template * Add CT 4.0 Landing Zone API fallback for account discovery * Auto-discover Config delivery bucket name from CT StackSet * Auto-discover Config delivery bucket from CT StackSet and pass via dynamic reference * Remove dynamic SSM reference from Easy Setup - let nested stack resolve its own SSM parameters * Handle missing Config Aggregator gracefully in CT 4.0 environments * Gate config delivery bucket SSM parameter to CT-only environments * Fix linter issues: extract CT 4.0 helper, fix mypy type annotation, remove analysis docs * Fix remaining flake8 plugin errors: docstrings, string concat, raise chaining, else-after-return --------- Co-authored-by: Pavan Kumar Vooturi <pvooturi@amazon.com>
1 parent 5c0bb4f commit 5b339b6

11 files changed

Lines changed: 922 additions & 162 deletions

File tree

aws_sra_examples/easy_setup/templates/sra-easy-setup.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2365,6 +2365,7 @@ Resources:
23652365
- ssm:GetParameters
23662366
- ssm:PutParameter
23672367
- ssm:AddTagsToResource
2368+
- ssm:ListTagsForResource
23682369
Resource:
23692370
- !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/sra*"
23702371

aws_sra_examples/solutions/common/common_prerequisites/lambda/src/app.py

Lines changed: 274 additions & 35 deletions
Large diffs are not rendered by default.

aws_sra_examples/solutions/common/common_prerequisites/templates/sra-common-prerequisites-management-account-parameters.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,12 @@ Resources:
280280
- cloudformation:DescribeStackSet
281281
- cloudformation:ListStackInstances
282282
Resource: '*'
283+
- Sid: ControlTowerRead
284+
Effect: Allow
285+
Action:
286+
- controltower:ListLandingZones
287+
- controltower:GetLandingZone
288+
Resource: '*'
283289
- Sid: SSMParameterRead
284290
Effect: Allow
285291
Action: ssm:DescribeParameters

aws_sra_examples/solutions/config/config_management_account/lambda/src/app.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,26 @@ def assume_role(role: str, role_session_name: str, account: str = None, session:
6868
)
6969

7070

71-
def get_existing_account_aggregation_sources(config_client: ConfigServiceClient, aggregator: str) -> List[AccountAggregationSourceTypeDef]:
71+
def get_existing_account_aggregation_sources(
72+
config_client: ConfigServiceClient, aggregator: str
73+
) -> Optional[List[AccountAggregationSourceTypeDef]]:
7274
"""Get existing list of source accounts/regions being aggregated.
7375
7476
Args:
7577
config_client: Boto3 client config
7678
aggregator: Name of the configuration aggregator
7779
7880
Returns:
79-
Existing list of source accounts/regions being aggregated
81+
Existing list of source accounts/regions being aggregated, or None if aggregator doesn't exist
8082
"""
81-
response: Any = config_client.describe_configuration_aggregators(ConfigurationAggregatorNames=[aggregator])
82-
api_call_details = {"API_Call": "config:DescribeConfigurationAggregators", "API_Response": response}
83-
LOGGER.info(api_call_details)
84-
return response["ConfigurationAggregators"][0]["AccountAggregationSources"]
83+
try:
84+
response: Any = config_client.describe_configuration_aggregators(ConfigurationAggregatorNames=[aggregator])
85+
api_call_details = {"API_Call": "config:DescribeConfigurationAggregators", "API_Response": response}
86+
LOGGER.info(api_call_details)
87+
return response["ConfigurationAggregators"][0]["AccountAggregationSources"]
88+
except config_client.exceptions.NoSuchConfigurationAggregatorException:
89+
LOGGER.info(f"Configuration aggregator '{aggregator}' does not exist.")
90+
return None
8591

8692

8793
def get_updated_account_aggregation_sources(aggregation_sources: list, account: str, action: str) -> List[Any]:
@@ -150,7 +156,7 @@ def get_validated_parameters(event: CloudFormationCustomResourceEvent) -> dict:
150156
actions = {"Create": "Add", "Update": "Add", "Delete": "Remove"}
151157
params["action"] = actions[event["RequestType"]]
152158

153-
parameter_pattern_validator("AGGREGATOR_NAME", params.get("AGGREGATOR_NAME"), pattern=r"^aws-controltower-GuardrailsComplianceAggregator$")
159+
parameter_pattern_validator("AGGREGATOR_NAME", params.get("AGGREGATOR_NAME"), pattern=r"^[\w-]{1,256}$")
154160
parameter_pattern_validator("AUDIT_ACCOUNT_ID", params.get("AUDIT_ACCOUNT_ID"), pattern=r"^\d{12}$")
155161
parameter_pattern_validator("ROLE_SESSION_NAME", params.get("ROLE_SESSION_NAME"), pattern=r"^[\w=,@.-]+$")
156162
parameter_pattern_validator("ROLE_TO_ASSUME", params.get("ROLE_TO_ASSUME"), pattern=r"^[\w+=,.@-]{1,64}$")
@@ -178,6 +184,12 @@ def process_event(event: CloudFormationCustomResourceEvent, context: Context) ->
178184
config_client: ConfigServiceClient = audit_account_session.client("config", config=BOTO3_CONFIG)
179185

180186
existing_aggregation_sources = get_existing_account_aggregation_sources(config_client, params["AGGREGATOR_NAME"])
187+
if existing_aggregation_sources is None:
188+
LOGGER.info(
189+
f"Config Aggregator '{params['AGGREGATOR_NAME']}' does not exist in account {params['AUDIT_ACCOUNT_ID']}. "
190+
+ "Skipping aggregator update. This is expected for Control Tower 4.0+ environments."
191+
)
192+
return f"{params['AUDIT_ACCOUNT_ID']}-{params['AGGREGATOR_NAME']}-skipped"
181193
updated_aggregation_sources = get_updated_account_aggregation_sources(existing_aggregation_sources, management_account, params["action"])
182194
if existing_aggregation_sources == updated_aggregation_sources:
183195
LOGGER.info(f"{params['action']} {management_account} account in Aggregator was not necessary, as it was already in that state.")

aws_sra_examples/solutions/config/config_management_account/templates/sra-config-management-account-main-ssm.yaml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Metadata:
3030
Parameters:
3131
- pStackSetAdminRole
3232
- pStackExecutionRole
33+
- pCreateConfigServiceLinkedRole
3334
- Label:
3435
default: Config Recorder Properties
3536
Parameters:
@@ -42,6 +43,12 @@ Metadata:
4243
Parameters:
4344
- pFrequency
4445
- pKmsKeyArn
46+
- pConfigDeliveryS3BucketName
47+
- Label:
48+
default: Config Aggregator Properties
49+
Parameters:
50+
- pUpdateConfigAggregator
51+
- pConfigAggregatorName
4552
- Label:
4653
default: General Lambda Function Properties
4754
Parameters:
@@ -54,6 +61,8 @@ Metadata:
5461
default: Stack Set Role
5562
pStackExecutionRole:
5663
default: Stack execution role
64+
pCreateConfigServiceLinkedRole:
65+
default: Create Config Service-Linked Role
5766
pAllSupported:
5867
default: All Supported
5968
pAuditAccountId:
@@ -88,6 +97,12 @@ Metadata:
8897
default: SRA Solution Version
8998
pSRAStagingS3BucketName:
9099
default: SRA Staging S3 Bucket Name
100+
pConfigDeliveryS3BucketName:
101+
default: (Optional) Config Delivery S3 Bucket Name
102+
pUpdateConfigAggregator:
103+
default: Update Config Aggregator
104+
pConfigAggregatorName:
105+
default: Config Aggregator Name
91106

92107
Parameters:
93108
pStackSetAdminRole:
@@ -100,6 +115,11 @@ Parameters:
100115
Default: sra-execution
101116
Description: The execution role name that is used in the stack.
102117
Type: String
118+
pCreateConfigServiceLinkedRole:
119+
AllowedValues: ['true', 'false']
120+
Default: 'true'
121+
Description: Set to false if the Config service-linked role already exists in the account.
122+
Type: String
103123
pAllSupported:
104124
AllowedValues: ['true', 'false']
105125
Default: 'true'
@@ -209,6 +229,28 @@ Parameters:
209229
SSM Parameter for SRA Staging S3 bucket name for the artifacts relevant to solution. (e.g., lambda zips, CloudFormation templates) S3 bucket
210230
name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-).
211231
Type: AWS::SSM::Parameter::Value<String>
232+
pConfigDeliveryS3BucketName:
233+
AllowedPattern: '^([\w.-]{1,900})$|^(\/[\w.-]{1,900})*[\w.-]{1,900}$'
234+
ConstraintDescription:
235+
Must be alphanumeric or special characters [., _, -]. In addition, the slash character ( / ) used to delineate hierarchies in parameter names.
236+
Default: /sra/control-tower/config-delivery-bucket-name
237+
Description:
238+
SSM Parameter for the Config delivery S3 bucket name (auto-populated by Common Prerequisites).
239+
For standalone deployment without Common Prerequisites, create this SSM parameter first with the bucket name value.
240+
Type: AWS::SSM::Parameter::Value<String>
241+
pUpdateConfigAggregator:
242+
AllowedValues: ['true', 'false']
243+
Default: 'true'
244+
Description:
245+
Set to false to skip updating the Config Aggregator in the Audit account. Set to false if using Control Tower 4.0+
246+
with an org-level aggregator that already aggregates from all accounts.
247+
Type: String
248+
pConfigAggregatorName:
249+
AllowedPattern: '^[\w-]{1,256}$'
250+
Default: aws-controltower-GuardrailsComplianceAggregator
251+
Description:
252+
Name of the Config Aggregator in the Audit account. For Control Tower 4.0+, use aws-controltower-ConfigAggregatorForOrganization.
253+
Type: String
212254

213255
Rules:
214256
ResourceTypesValidation:
@@ -217,6 +259,9 @@ Rules:
217259
- AssertDescription: "'Resource Types' parameter is required if the 'All Supported' parameter is set to 'true'."
218260
Assert: !Equals [!Ref pAllSupported, 'true']
219261

262+
Conditions:
263+
cUpdateConfigAggregator: !Equals [!Ref pUpdateConfigAggregator, 'true']
264+
220265
Resources:
221266
rConfigRoleStack:
222267
Type: AWS::CloudFormation::Stack
@@ -227,6 +272,8 @@ Resources:
227272
Tags:
228273
- Key: sra-solution
229274
Value: !Ref pSRASolutionName
275+
Parameters:
276+
pCreateConfigServiceLinkedRole: !Ref pCreateConfigServiceLinkedRole
230277

231278
rConfigStackSet:
232279
DependsOn: rConfigRoleStack
@@ -272,9 +319,12 @@ Resources:
272319
ParameterValue: !Ref pOrganizationId
273320
- ParameterKey: pResourceTypes
274321
ParameterValue: !Ref pResourceTypes
322+
- ParameterKey: pConfigDeliveryS3BucketName
323+
ParameterValue: !Ref pConfigDeliveryS3BucketName
275324

276325
rConfigAggregatorStack:
277326
Type: AWS::CloudFormation::Stack
327+
Condition: cUpdateConfigAggregator
278328
DeletionPolicy: Delete
279329
DependsOn: rConfigStackSet
280330
UpdateReplacePolicy: Delete
@@ -285,6 +335,7 @@ Resources:
285335
Value: !Ref pSRASolutionName
286336
Parameters:
287337
pAuditAccountId: !Ref pAuditAccountId
338+
pConfigAggregatorName: !Ref pConfigAggregatorName
288339
pLambdaLogGroupKmsKey: !Ref pLambdaLogGroupKmsKey
289340
pLambdaLogGroupRetention: !Ref pLambdaLogGroupRetention
290341
pLambdaLogLevel: !Ref pLambdaLogLevel

aws_sra_examples/solutions/config/config_management_account/templates/sra-config-management-account-role.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,19 @@ Parameters:
4242
Default: sra-solution
4343
Description: The SRA solution tag key applied to all resources created by the solution that support tagging. The value is the pSRASolutionName.
4444
Type: String
45+
pCreateConfigServiceLinkedRole:
46+
AllowedValues: ['true', 'false']
47+
Default: 'true'
48+
Description: Set to false if the Config service-linked role already exists in the account.
49+
Type: String
50+
51+
Conditions:
52+
cCreateConfigServiceLinkedRole: !Equals [!Ref pCreateConfigServiceLinkedRole, 'true']
4553

4654
Resources:
4755
rConfigServiceLinkedRole:
4856
Type: AWS::IAM::ServiceLinkedRole
57+
Condition: cCreateConfigServiceLinkedRole
4958
Properties:
5059
AWSServiceName: config.amazonaws.com
5160
Description: A service-linked role for the ConfigRecorder.

aws_sra_examples/solutions/config/config_management_account/templates/sra-config-management-account-update-aggregator.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ Parameters:
7272
Description: AWS Account ID of the Control Tower Audit account.
7373
Type: String
7474
pConfigAggregatorName:
75-
AllowedValues: [aws-controltower-GuardrailsComplianceAggregator]
75+
AllowedPattern: '^[\w-]{1,256}$'
7676
Default: aws-controltower-GuardrailsComplianceAggregator
77-
Description: AWS Config Aggregator Name in the Control Tower Audit Account
77+
Description: AWS Config Aggregator Name in the Control Tower Audit Account. For CT 4.0+, use aws-controltower-ConfigAggregatorForOrganization.
7878
Type: String
7979
pConfigUpdateAggregatorLambdaFunctionName:
8080
AllowedPattern: '^[\w-]{1,64}$'

aws_sra_examples/solutions/config/config_management_account/templates/sra-config-management-account.yaml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,16 @@ Metadata:
5555
default: Organization ID
5656
pResourceTypes:
5757
default: (Optional) Resource Types
58+
pConfigDeliveryS3BucketName:
59+
default: (Optional) Config Delivery S3 Bucket Name
5860

5961
Parameters:
6062
pAllConfigTopicName:
61-
AllowedValues: [aws-controltower-AllConfigNotifications]
63+
AllowedPattern: '^[a-zA-Z0-9_-]{1,256}$'
6264
Default: aws-controltower-AllConfigNotifications
63-
Description: All Configuration Notification SNS Topic in Audit Account that AWS Config delivers notifications to.
65+
Description:
66+
All Configuration Notification SNS Topic in Audit Account that AWS Config delivers notifications to.
67+
For Control Tower 4.0+, verify the topic name in your Audit account.
6468
Type: String
6569
pAllSupported:
6670
AllowedValues: ['true', 'false']
@@ -115,6 +119,10 @@ Parameters:
115119
(Optional) A list of valid AWS resource types to include in this recording group. Eg. AWS::CloudTrail::Trail. If 'All Supported' parameter is
116120
set to 'false', then this parameter becomes required.
117121
Type: String
122+
pConfigDeliveryS3BucketName:
123+
AllowedPattern: '^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$'
124+
Description: S3 bucket name for Config delivery.
125+
Type: String
118126

119127
Rules:
120128
ResourceTypesValidation:
@@ -153,7 +161,7 @@ Resources:
153161
- mSettings
154162
- FrequencyMap
155163
- !Ref pFrequency
156-
S3BucketName: !Sub aws-controltower-logs-${pLogArchiveAccountId}-${pHomeRegion}
164+
S3BucketName: !Ref pConfigDeliveryS3BucketName
157165
S3KeyPrefix: !Ref pOrganizationId
158166
S3KmsKeyArn: !If
159167
- cIsUsingKmsKey

aws_sra_examples/solutions/config/config_org/templates/sra-config-org-main-ssm.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ Resources:
553553
pRecorderName: !Ref pRecorderName
554554
pKMSKeyArnSecretName: !Ref pKMSKeyArnSecretName
555555
pDeliveryChannelName: !Ref pDeliveryChannelName
556-
pPublishingDestinationBucketName: !Sub ${pConfigOrgDeliveryBucketPrefix}-${pLogArchiveAccountId}-${AWS::Region}
556+
pPublishingDestinationBucketName: !Sub ${pConfigOrgDeliveryBucketPrefix}-${pLogArchiveAccountId}-${AWS::Region}
557557
pKMSKeyArn: !Sub '{{resolve:secretsmanager:arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${pAuditAccountId}:secret:sra/config_org_delivery_key_arn:SecretString:ConfigDeliveryKeyArn:AWSCURRENT}}'
558558
Tags:
559559
- Key: sra-solution

0 commit comments

Comments
 (0)