Skip to content

Commit c769ec8

Browse files
committed
removal of state records
1 parent 61b3570 commit c769ec8

File tree

4 files changed

+103
-20
lines changed

4 files changed

+103
-20
lines changed

aws_sra_examples/solutions/genai/bedrock_org/lambda/src/app.py

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
550580
def 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}...")

aws_sra_examples/solutions/genai/bedrock_org/lambda/src/sra_config.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,17 @@ def put_organization_config_rule(self):
9090
return response
9191

9292
def find_config_rule(self, rule_name):
93-
"""Get Config Rule."""
94-
# Get the Config Rule
93+
"""Get config rule
94+
95+
Args:
96+
rule_name (str): Config rule name
97+
98+
Raises:
99+
ValueError: If the config rule is not found
100+
101+
Returns:
102+
tuple[bool, dict]: True if the config rule is found, False if not, and the response
103+
"""
95104
try:
96105

97106
response = self.CONFIG_CLIENT.describe_config_rules(

0 commit comments

Comments
 (0)