@@ -547,6 +547,36 @@ def add_state_table_record(aws_service: str, component_state: str, description:
547547 )
548548
549549
550+ def remove_state_table_record (resource_arn ):
551+ """Remove a record from the state table
552+
553+ Args:
554+ resource_arn (str): arn of the resource
555+
556+ Returns:
557+ response: response from dynamodb delete_item
558+ """
559+ # TODO(liamschn): move dynamodb resource to the dynamo class object/module
560+ dynamodb_resource = sts .assume_role_resource (ssm_params .SRA_SECURITY_ACCT , sts .CONFIGURATION_ROLE , "dynamodb" , sts .HOME_REGION )
561+ LOGGER .info (f"Searching for { resource_arn } in { STATE_TABLE } dynamodb table..." )
562+ item_found , find_result = dynamodb .find_item (
563+ STATE_TABLE ,
564+ dynamodb_resource ,
565+ SOLUTION_NAME ,
566+ {
567+ "arn" : resource_arn ,
568+ },
569+ )
570+ if item_found is False :
571+ LOGGER .info (f"Record not found in { STATE_TABLE } dynamodb table" )
572+ else :
573+ sra_resource_record_id = find_result ["record_id" ]
574+ LOGGER .info (f"Found record id { sra_resource_record_id } " )
575+ LOGGER .info (f"Removing { sra_resource_record_id } from { STATE_TABLE } dynamodb table..." )
576+ response = dynamodb .delete_item (STATE_TABLE , dynamodb_resource , SOLUTION_NAME , sra_resource_record_id )
577+ return response
578+
579+
550580def deploy_stage_config_rule_lambda_code ():
551581 global DRY_RUN_DATA
552582 global LIVE_RUN_DATA
@@ -717,7 +747,7 @@ def deploy_metric_filters_and_alarms(region, accounts, resource_properties):
717747 LOGGER .info (f"{ filter } filter not requested for { acct } . Skipping..." )
718748 continue
719749 kms .KMS_CLIENT = sts .assume_role (acct , sts .CONFIGURATION_ROLE , "kms" , region )
720- search_alarm_kms_key , alarm_key_alias , alarm_key_id = kms .check_alias_exists (kms .KMS_CLIENT , f"alias/{ ALARM_SNS_KEY_ALIAS } " )
750+ search_alarm_kms_key , alarm_key_alias , alarm_key_id , alarm_key_arn = kms .check_alias_exists (kms .KMS_CLIENT , f"alias/{ ALARM_SNS_KEY_ALIAS } " )
721751 if search_alarm_kms_key is False :
722752 LOGGER .info (f"alias/{ ALARM_SNS_KEY_ALIAS } not found." )
723753 # TODO(liamschn): search for key itself (by policy) before creating the key; then separate the alias creation from this section
@@ -782,7 +812,7 @@ def deploy_metric_filters_and_alarms(region, accounts, resource_properties):
782812 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
783813 CFN_RESPONSE_DATA ["deployment_info" ]["configuration_changes" ] += 1
784814 # add SNS state table record
785- add_state_table_record ("sns" , "implemented" , "sns topic for alarms" , "topic" , SRA_ALARM_TOPIC_ARN , acct , region , "topic " )
815+ add_state_table_record ("sns" , "implemented" , "sns topic for alarms" , "topic" , SRA_ALARM_TOPIC_ARN , acct , region , f" { SOLUTION_NAME } -alarms " )
786816
787817 else :
788818 LOGGER .info (f"DRY_RUN: Create { SOLUTION_NAME } -alarms SNS topic" )
@@ -801,7 +831,7 @@ def deploy_metric_filters_and_alarms(region, accounts, resource_properties):
801831 LOGGER .info (f"{ SOLUTION_NAME } -alarms SNS topic already exists." )
802832 SRA_ALARM_TOPIC_ARN = topic_search
803833 # add SNS state table record
804- add_state_table_record ("sns" , "implemented" , "sns topic for alarms" , "topic" , SRA_ALARM_TOPIC_ARN , acct , region , "topic " )
834+ add_state_table_record ("sns" , "implemented" , "sns topic for alarms" , "topic" , SRA_ALARM_TOPIC_ARN , acct , region , f" { SOLUTION_NAME } -alarms " )
805835
806836 # 4c) Cloudwatch metric filters and alarms
807837 # metric_filter_arn = f"arn:aws:logs:{region}:{acct}:metric-filter:{filter}"
@@ -1107,13 +1137,15 @@ def delete_event(event, context):
11071137 # 1a) Delete configuration topic
11081138 sns .SNS_CLIENT = sts .assume_role (sts .MANAGEMENT_ACCOUNT , sts .CONFIGURATION_ROLE , "sns" , sts .HOME_REGION )
11091139 topic_search = sns .find_sns_topic (f"{ SOLUTION_NAME } -configuration" )
1140+ # TODO(liamschn): this will be a mypy error: need to have topic_search (sns.find_sns_topic) return a str, not None
11101141 if topic_search is not None :
11111142 if DRY_RUN is False :
11121143 LOGGER .info (f"Deleting { SOLUTION_NAME } -configuration SNS topic" )
11131144 LIVE_RUN_DATA ["SNSDelete" ] = f"Deleted { SOLUTION_NAME } -configuration SNS topic"
11141145 sns .delete_sns_topic (topic_search )
11151146 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
11161147 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
1148+ remove_state_table_record (topic_search )
11171149 else :
11181150 LOGGER .info (f"DRY_RUN: Deleting { SOLUTION_NAME } -configuration SNS topic" )
11191151 DRY_RUN_DATA ["SNSDelete" ] = f"DRY_RUN: Delete { SOLUTION_NAME } -configuration SNS topic"
@@ -1146,6 +1178,7 @@ def delete_event(event, context):
11461178 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
11471179 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
11481180 LOGGER .info ("Deleted CloudWatch observability access manager link" )
1181+ remove_state_table_record (search_oam_link [1 ])
11491182 else :
11501183 LOGGER .info ("DRY_RUN: CloudWatch observability access manager link found, deleting..." )
11511184 DRY_RUN_DATA ["OAMLinkDelete" ] = "DRY_RUN: Delete CloudWatch observability access manager link"
@@ -1186,6 +1219,7 @@ def delete_event(event, context):
11861219 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
11871220 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
11881221 LOGGER .info (f"Deleted { cloudwatch .CROSS_ACCOUNT_ROLE_NAME } IAM role" )
1222+ remove_state_table_record (search_iam_role [1 ])
11891223 else :
11901224 LOGGER .info (f"DRY_RUN: Deleting { cloudwatch .CROSS_ACCOUNT_ROLE_NAME } IAM role..." )
11911225 DRY_RUN_DATA ["OAMCrossAccountRoleDelete" ] = f"DRY_RUN: Delete { cloudwatch .CROSS_ACCOUNT_ROLE_NAME } IAM role"
@@ -1202,6 +1236,7 @@ def delete_event(event, context):
12021236 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
12031237 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
12041238 LOGGER .info ("Deleted CloudWatch observability access manager sink" )
1239+ remove_state_table_record (search_oam_sink [1 ])
12051240 else :
12061241 LOGGER .info ("DRY_RUN: CloudWatch observability access manager sink found, deleting..." )
12071242 DRY_RUN_DATA ["OAMSinkDelete" ] = "DRY_RUN: Delete CloudWatch observability access manager sink"
@@ -1213,20 +1248,27 @@ def delete_event(event, context):
12131248 filter_deploy , filter_accounts , filter_regions , filter_params = get_filter_params (filter , event )
12141249 for acct in filter_accounts :
12151250 for region in filter_regions :
1216- # 3a) Delete KMS key (schedule deletion)
1251+ # 3a) Delete KMS key (schedule deletion) and delete kms alias
12171252 kms .KMS_CLIENT = sts .assume_role (acct , sts .CONFIGURATION_ROLE , "kms" , region )
1218- search_alarm_kms_key , alarm_key_alias , alarm_key_id = kms .check_alias_exists (kms .KMS_CLIENT , f"alias/{ ALARM_SNS_KEY_ALIAS } " )
1253+ search_alarm_kms_key , alarm_key_alias , alarm_key_id , alarm_key_arn = kms .check_alias_exists (kms .KMS_CLIENT , f"alias/{ ALARM_SNS_KEY_ALIAS } " )
12191254 if search_alarm_kms_key is True :
12201255 if DRY_RUN is False :
12211256 LOGGER .info (f"Deleting { ALARM_SNS_KEY_ALIAS } KMS key" )
1222- LIVE_RUN_DATA ["KMSDelete" ] = f"Deleted { ALARM_SNS_KEY_ALIAS } KMS key"
12231257 kms .delete_alias (kms .KMS_CLIENT , f"alias/{ ALARM_SNS_KEY_ALIAS } " )
1258+ LIVE_RUN_DATA ["KMSDelete" ] = f"Deleted { ALARM_SNS_KEY_ALIAS } KMS key"
12241259 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
12251260 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
12261261 LOGGER .info (f"Deleting { ALARM_SNS_KEY_ALIAS } KMS key ({ alarm_key_id } )" )
1262+ remove_state_table_record (alarm_key_arn )
1263+
12271264 kms .schedule_key_deletion (kms .KMS_CLIENT , alarm_key_id )
1265+ LIVE_RUN_DATA ["KMSDelete" ] = f"Deleted { ALARM_SNS_KEY_ALIAS } KMS key ({ alarm_key_id } )"
12281266 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
12291267 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
1268+ LOGGER .info (f"Scheduled deletion of { ALARM_SNS_KEY_ALIAS } KMS key ({ alarm_key_id } )" )
1269+ kms_key_arn = f"arn:{ sts .PARTITION } :kms:{ region } :{ acct } :key/{ alarm_key_id } "
1270+ remove_state_table_record (kms_key_arn )
1271+
12301272 else :
12311273 LOGGER .info (f"DRY_RUN: Deleting { ALARM_SNS_KEY_ALIAS } KMS key" )
12321274 DRY_RUN_DATA ["KMSDelete" ] = f"DRY_RUN: Delete { ALARM_SNS_KEY_ALIAS } KMS key"
@@ -1244,8 +1286,12 @@ def delete_event(event, context):
12441286 search_metric_alarm = cloudwatch .find_metric_alarm (f"{ filter } -alarm" )
12451287 if search_metric_alarm is True :
12461288 cloudwatch .delete_metric_alarm (f"{ filter } -alarm" )
1289+ LIVE_RUN_DATA [f"{ filter } -alarm_CloudWatchDelete" ] = f"Deleted { filter } -alarm CloudWatch metric alarm"
12471290 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
12481291 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
1292+ LOGGER .info (f"Deleted { filter } -alarm CloudWatch metric alarm" )
1293+ metric_alarm_arn = f"arn:{ sts .PARTITION } :cloudwatch:{ region } :{ acct } :alarm:{ filter } -alarm"
1294+ remove_state_table_record (metric_alarm_arn )
12491295 else :
12501296 LOGGER .info (f"{ filter } -alarm CloudWatch metric alarm does not exist." )
12511297
@@ -1255,17 +1301,23 @@ def delete_event(event, context):
12551301 search_metric_filter = cloudwatch .find_metric_filter (filter_params ["log_group_name" ], filter )
12561302 if search_metric_filter is True :
12571303 cloudwatch .delete_metric_filter (filter_params ["log_group_name" ], filter )
1304+ LIVE_RUN_DATA [f"{ filter } _CloudWatchDelete" ] = f"Deleted { filter } CloudWatch metric filter"
12581305 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
12591306 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
1307+ LOGGER .info (f"Deleted { filter } CloudWatch metric filter" )
1308+ metric_filter_arn = f"arn:{ sts .PARTITION } :logs:{ region } :{ acct } :metric-filter:{ filter } "
1309+ remove_state_table_record (metric_filter_arn )
1310+
12601311 else :
12611312 LOGGER .info (f"{ filter } CloudWatch metric filter does not exist." )
12621313
12631314 else :
1264- LOGGER .info (f"DRY_RUN: Deleting { filter } CloudWatch metric filter" )
1315+ LOGGER .info (f"DRY_RUN: Delete { filter } CloudWatch metric filter" )
12651316 DRY_RUN_DATA [f"{ filter } _CloudWatchDelete" ] = f"DRY_RUN: Delete { filter } CloudWatch metric filter"
12661317
12671318 # 3d) Delete the alarm topic
12681319 sns .SNS_CLIENT = sts .assume_role (acct , sts .CONFIGURATION_ROLE , "sns" , region )
1320+ # TODO(liamschn): this will be a mypy error - need to have alarm_topic_search (sns.find_sns_topic) return string, not None
12691321 alarm_topic_search = sns .find_sns_topic (f"{ SOLUTION_NAME } -alarms" , region , acct )
12701322 if alarm_topic_search is not None :
12711323 if DRY_RUN is False :
@@ -1274,21 +1326,22 @@ def delete_event(event, context):
12741326 sns .delete_sns_topic (alarm_topic_search )
12751327 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
12761328 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
1329+ LOGGER .info (f"Deleted { SOLUTION_NAME } -alarms SNS topic" )
1330+ remove_state_table_record (alarm_topic_search )
12771331 else :
1278- LOGGER .info (f"DRY_RUN: Deleting { SOLUTION_NAME } -alarms SNS topic" )
1332+ LOGGER .info (f"DRY_RUN: Delete { SOLUTION_NAME } -alarms SNS topic" )
12791333 DRY_RUN_DATA ["SNSDelete" ] = f"DRY_RUN: Delete { SOLUTION_NAME } -alarms SNS topic"
12801334 else :
12811335 LOGGER .info (f"{ SOLUTION_NAME } -alarms SNS topic does not exist." )
12821336
12831337 # 4) Delete config rules
1284- # TODO(liamschn): deal with invalid rule names
1285- # TODO(liamschn): deal with invalid account IDs
1338+ # TODO(liamschn): deal with invalid rule names?
1339+ # TODO(liamschn): deal with invalid account IDs?
12861340 accounts , regions = get_accounts_and_regions (event ["ResourceProperties" ])
12871341 for prop in event ["ResourceProperties" ]:
12881342 if prop .startswith ("SRA-BEDROCK-CHECK-" ):
12891343 rule_name : str = prop
12901344 LOGGER .info (f"Delete operation: retrieving { rule_name } parameters..." )
1291- # rule_deploy, rule_input_params = get_rule_params(rule_name, event["ResourceProperties"])
12921345 rule_name = rule_name .lower ()
12931346 LOGGER .info (f"Delete operation: examining { rule_name } resources..." )
12941347
@@ -1304,22 +1357,25 @@ def delete_event(event, context):
13041357 LIVE_RUN_DATA [f"{ rule_name } _{ acct } _{ region } _Delete" ] = f"Deleted { rule_name } custom config rule"
13051358 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
13061359 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
1360+ remove_state_table_record (config_rule_search [1 ]["ConfigRule" ]["ConfigRuleArn" ])
13071361 else :
13081362 LOGGER .info (f"DRY_RUN: Deleting { rule_name } config rule for account { acct } in { region } " )
1363+ DRY_RUN_DATA [f"{ rule_name } _{ acct } _{ region } _Delete" ] = f"DRY_RUN: Delete { rule_name } custom config rule"
13091364 else :
13101365 LOGGER .info (f"{ rule_name } config rule for account { acct } in { region } does not exist." )
1311- DRY_RUN_DATA [f"{ rule_name } _{ acct } _{ region } _Delete" ] = f"DRY_RUN: Delete { rule_name } custom config rule"
13121366
13131367 # 4b) Delete lambda for custom config rule
13141368 lambdas .LAMBDA_CLIENT = sts .assume_role (acct , sts .CONFIGURATION_ROLE , "lambda" , region )
13151369 lambda_search = lambdas .find_lambda_function (rule_name )
1370+ # TODO(liamschn): this will be a mypy error - need to have lambda_search return string, not None
13161371 if lambda_search is not None :
13171372 if DRY_RUN is False :
13181373 LOGGER .info (f"Deleting { rule_name } lambda function for account { acct } in { region } " )
13191374 lambdas .delete_lambda_function (rule_name )
13201375 LIVE_RUN_DATA [f"{ rule_name } _{ acct } _{ region } _Delete" ] = f"Deleted { rule_name } lambda function"
13211376 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
13221377 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
1378+ remove_state_table_record (lambda_search ["Configuration" ]["FunctionArn" ])
13231379 else :
13241380 LOGGER .info (f"DRY_RUN: Deleting { rule_name } lambda function for account { acct } in { region } " )
13251381 DRY_RUN_DATA [f"{ rule_name } _{ acct } _{ region } _Delete" ] = f"DRY_RUN: Delete { rule_name } lambda function"
@@ -1356,11 +1412,14 @@ def delete_event(event, context):
13561412 LIVE_RUN_DATA [f"{ rule_name } _{ acct } _{ region } _Delete" ] = f"Deleted { rule_name } IAM policy"
13571413 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
13581414 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
1415+ remove_state_table_record (policy_arn )
13591416 else :
13601417 LOGGER .info (f"DRY_RUN: Delete { rule_name } -lamdba-basic-execution IAM policy for account { acct } in { region } " )
13611418 DRY_RUN_DATA [
13621419 f"{ rule_name } _{ acct } _{ region } _PolicyDelete"
13631420 ] = f"DRY_RUN: Delete { rule_name } -lamdba-basic-execution IAM policy for account { acct } in { region } "
1421+ else :
1422+ LOGGER .info (f"{ rule_name } -lamdba-basic-execution IAM policy for account { acct } in { region } does not exist." )
13641423
13651424 policy_arn2 = f"arn:{ sts .PARTITION } :iam::{ acct } :policy/{ rule_name } "
13661425 LOGGER .info (f"Policy ARN: { policy_arn2 } " )
@@ -1372,11 +1431,14 @@ def delete_event(event, context):
13721431 LIVE_RUN_DATA [f"{ rule_name } _{ acct } _{ region } _Delete" ] = f"Deleted { rule_name } IAM policy"
13731432 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
13741433 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
1434+ remove_state_table_record (policy_arn2 )
13751435 else :
13761436 LOGGER .info (f"DRY_RUN: Delete { rule_name } IAM policy for account { acct } in { region } " )
13771437 DRY_RUN_DATA [
13781438 f"{ rule_name } _{ acct } _{ region } _PolicyDelete"
13791439 ] = f"DRY_RUN: Delete { rule_name } IAM policy for account { acct } in { region } "
1440+ else :
1441+ LOGGER .info (f"{ rule_name } IAM policy for account { acct } in { region } does not exist." )
13801442
13811443 # 7) Delete IAM execution role for custom config rule lambda
13821444 role_search = iam .check_iam_role_exists (rule_name )
@@ -1387,6 +1449,7 @@ def delete_event(event, context):
13871449 LIVE_RUN_DATA [f"{ rule_name } _{ acct } _{ region } _Delete" ] = f"Deleted { rule_name } IAM role"
13881450 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
13891451 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] -= 1
1452+ remove_state_table_record (role_search [1 ])
13901453 else :
13911454 LOGGER .info (f"DRY_RUN: Delete { rule_name } IAM role for account { acct } in { region } " )
13921455 DRY_RUN_DATA [f"{ rule_name } _{ acct } _{ region } _RoleDelete" ] = f"DRY_RUN: Delete { rule_name } IAM role for account { acct } in { region } "
@@ -1672,7 +1735,7 @@ def deploy_metric_filter(region: str, acct: str, log_group_name: str, filter_nam
16721735 metric_namespace: metric namespace
16731736 metric_value: metric value
16741737 """
1675- metric_filter_arn = f"arn:aws :logs:{ region } :{ acct } :metric-filter:{ filter_name } "
1738+ metric_filter_arn = f"arn:{ sts . PARTITION } :logs:{ region } :{ acct } :metric-filter:{ filter_name } "
16761739 search_metric_filter = cloudwatch .find_metric_filter (log_group_name , filter_name )
16771740 if search_metric_filter is False :
16781741 if DRY_RUN is False :
@@ -1722,7 +1785,7 @@ def deploy_metric_alarm(
17221785 metric_treat_missing_data: metric treat missing data
17231786 alarm_actions: alarm actions
17241787 """
1725- alarm_arn = f"arn:aws :cloudwatch:{ region } :{ acct } :alarm:{ alarm_name } "
1788+ alarm_arn = f"arn:{ sts . PARTITION } :cloudwatch:{ region } :{ acct } :alarm:{ alarm_name } "
17261789 search_metric_alarm = cloudwatch .find_metric_alarm (alarm_name )
17271790 if search_metric_alarm is False :
17281791 LOGGER .info (f"Deploying metric alarm { alarm_name } ..." )
0 commit comments