2727# TODO(liamschn): deploy example bedrock guardrail
2828# TODO(liamschn): deploy example iam role(s) and policy(ies) - lower priority/not necessary?
2929# TODO(liamschn): deploy example bucket policy(ies) - lower priority/not necessary?
30- # TODO(liamschn): deal with linting failures in pipeline
31- # TODO(liamschn): deal with typechecking/mypy
32- # TODO(liamschn): check for unused parameters
30+ # TODO(liamschn): deal with linting failures in pipeline (and deal with typechecking/mypy)
31+ # TODO(liamschn): check for unused parameters (in progress)
3332# TODO(liamschn): make sure things don't fail (create or delete) if the dynamodb table is deleted/doesn't exist (use case, maybe someone deletes it)
3433
3534from typing import TYPE_CHECKING , Sequence # , Union, Literal, Optional
@@ -74,7 +73,6 @@ def load_sra_cloudwatch_dashboard() -> dict:
7473
7574# Global vars
7675RESOURCE_TYPE : str = ""
77- STATE_TABLE : str = "sra_state"
7876SOLUTION_NAME : str = "sra-bedrock-org"
7977GOVERNED_REGIONS = []
8078ORGANIZATION_ID = ""
@@ -86,8 +84,10 @@ def load_sra_cloudwatch_dashboard() -> dict:
8684LAMBDA_START : str = ""
8785LAMBDA_FINISH : str = ""
8886
89- ACCOUNT : str = boto3 .client ("sts" ).get_caller_identity ().get ("Account" )
90- REGION : str = os .environ .get ("AWS_REGION" )
87+ ACCOUNT : str | None = boto3 .client ("sts" ).get_caller_identity ().get ("Account" )
88+ LOGGER .info (f"Account: { ACCOUNT } " )
89+ REGION : str | None = os .environ .get ("AWS_REGION" )
90+ LOGGER .info (f"Region: { REGION } " )
9191CFN_RESOURCE_ID : str = "sra-bedrock-org-function"
9292ALARM_SNS_KEY_ALIAS = "sra-alarm-sns-key"
9393
@@ -158,7 +158,7 @@ def load_sra_cloudwatch_dashboard() -> dict:
158158# propagate solution name to class objects
159159cloudwatch .SOLUTION_NAME = SOLUTION_NAME
160160
161- def get_resource_parameters (event ) :
161+ def get_resource_parameters (event : dict ) -> None :
162162 global DRY_RUN
163163 global GOVERNED_REGIONS
164164 global CFN_RESPONSE_DATA
@@ -251,7 +251,7 @@ def validate_parameters(parameters: Dict[str, str], rules: Dict[str, str]) -> Di
251251 }
252252
253253
254- def get_accounts_and_regions (resource_properties ) :
254+ def get_accounts_and_regions (resource_properties : dict ) -> tuple [ list , list ] :
255255 """Get accounts and regions from event and return them in a tuple
256256
257257 Args:
@@ -280,7 +280,7 @@ def get_accounts_and_regions(resource_properties):
280280 regions = []
281281 return accounts , regions
282282
283- def get_rule_params (rule_name , resource_properties ) :
283+ def get_rule_params (rule_name : str , resource_properties : dict ) -> tuple [ bool , list , list , dict ] :
284284 """Get rule parameters from event and return them in a tuple
285285
286286 Args:
@@ -339,7 +339,7 @@ def get_rule_params(rule_name, resource_properties):
339339 return False , [], [], {}
340340
341341
342- def get_filter_params (filter_name , resource_properties ) :
342+ def get_filter_params (filter_name : str , resource_properties : dict ) -> tuple [ bool , list , list , dict ] :
343343 """Get filter parameters from event resource_properties and return them in a tuple
344344
345345 Args:
@@ -410,7 +410,7 @@ def build_s3_metric_filter_pattern(bucket_names: list, filter_pattern_template:
410410 s3_filter = s3_filter .replace ('&& ($.requestParameters.bucketName = "<BUCKET_NAME_PLACEHOLDER>")' , "" )
411411 return s3_filter
412412
413- def build_cloudwatch_dashboard (dashboard_template , solution , bedrock_accounts , regions ) :
413+ def build_cloudwatch_dashboard (dashboard_template : dict , solution : str , bedrock_accounts : list , regions : list ) -> dict :
414414 i = 0
415415 for bedrock_account in bedrock_accounts :
416416 for region in regions :
@@ -433,7 +433,7 @@ def build_cloudwatch_dashboard(dashboard_template, solution, bedrock_accounts, r
433433 return dashboard_template [solution ]
434434
435435
436- def deploy_state_table ():
436+ def deploy_state_table () -> None :
437437 global DRY_RUN_DATA
438438 global LIVE_RUN_DATA
439439 global CFN_RESPONSE_DATA
@@ -482,7 +482,7 @@ def deploy_state_table():
482482 DRY_RUN_DATA ["StateTableCreate" ] = f"DRY_RUN: Create the { STATE_TABLE } state table"
483483
484484
485- def add_state_table_record (aws_service : str , component_state : str , description : str , component_type : str , resource_arn : str , account_id : str , region : str , component_name : str , key_id : str = "" ):
485+ def add_state_table_record (aws_service : str , component_state : str , description : str , component_type : str , resource_arn : str , account_id : str , region : str , component_name : str , key_id : str = "" ) -> str :
486486 """Add a record to the state table
487487 Args:
488488 aws_service (str): aws service
@@ -534,7 +534,7 @@ def add_state_table_record(aws_service: str, component_state: str, description:
534534 return sra_resource_record_id
535535
536536
537- def remove_state_table_record (resource_arn ) :
537+ def remove_state_table_record (resource_arn : str ) -> dict :
538538 """Remove a record from the state table
539539
540540 Args:
@@ -566,7 +566,7 @@ def remove_state_table_record(resource_arn):
566566 response = {}
567567 return response
568568
569- def update_state_table_record (record_id : str , update_data : dict ):
569+ def update_state_table_record (record_id : str , update_data : dict ) -> None :
570570 dynamodb .DYNAMODB_RESOURCE = sts .assume_role_resource (ssm_params .SRA_SECURITY_ACCT , sts .CONFIGURATION_ROLE , "dynamodb" , sts .HOME_REGION )
571571
572572 try :
@@ -578,11 +578,10 @@ def update_state_table_record(record_id: str, update_data: dict):
578578 )
579579 except Exception as error :
580580 LOGGER .error (f"Error updating { record_id } record in { STATE_TABLE } dynamodb table: { error } " )
581- response = {}
582581 return
583582
584583
585- def deploy_stage_config_rule_lambda_code ():
584+ def deploy_stage_config_rule_lambda_code () -> None :
586585 global DRY_RUN_DATA
587586 global LIVE_RUN_DATA
588587 global CFN_RESPONSE_DATA
@@ -604,7 +603,7 @@ def deploy_stage_config_rule_lambda_code():
604603 LOGGER .info (f"DRY_RUN: Staging config rule code to the { s3 .STAGING_BUCKET } staging bucket" )
605604
606605
607- def deploy_sns_configuration_topics (context ) :
606+ def deploy_sns_configuration_topics (context : Any ) -> str :
608607 global DRY_RUN_DATA
609608 global LIVE_RUN_DATA
610609 global CFN_RESPONSE_DATA
@@ -652,7 +651,7 @@ def deploy_sns_configuration_topics(context):
652651
653652 return topic_arn
654653
655- def deploy_config_rules (region , accounts , resource_properties ) :
654+ def deploy_config_rules (region : str , accounts : list , resource_properties : dict ) -> None :
656655 global DRY_RUN_DATA
657656 global LIVE_RUN_DATA
658657 global CFN_RESPONSE_DATA
@@ -711,7 +710,7 @@ def deploy_config_rules(region, accounts, resource_properties):
711710
712711 # 3c) Deploy the config rule (requires config_org [non-CT] or config_mgmt [CT] solution)
713712 if DRY_RUN is False :
714- config_rule_arn = deploy_config_rule (acct , rule_name , lambda_arn , region , rule_input_params )
713+ deploy_config_rule (acct , rule_name , lambda_arn , region , rule_input_params )
715714 LIVE_RUN_DATA [f"{ rule_name } _{ acct } _{ region } _Config" ] = "Deployed custom config rule"
716715 CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
717716 CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] += 1
@@ -911,7 +910,7 @@ def deploy_metric_filters_and_alarms(region: str, accounts: list, resource_prope
911910 LOGGER .info (f"DRY_RUN: Filter deploy parameter is 'false'; Skip { filter_name } CloudWatch metric filter deployment" )
912911 DRY_RUN_DATA [f"{ filter_name } _CloudWatch" ] = "DRY_RUN: Filter deploy parameter is 'false'; Skip CloudWatch metric filter deployment"
913912
914- def deploy_central_cloudwatch_observability (event ) :
913+ def deploy_central_cloudwatch_observability (event : dict ) -> None :
915914 global DRY_RUN_DATA
916915 global LIVE_RUN_DATA
917916 global CFN_RESPONSE_DATA
@@ -1063,7 +1062,7 @@ def deploy_central_cloudwatch_observability(event):
10631062 # add OAM link state table record
10641063 add_state_table_record ("oam" , "implemented" , "oam link" , "link" , oam_link_arn , bedrock_account , bedrock_region , "oam_link" )
10651064
1066- def deploy_cloudwatch_dashboard (event ) :
1065+ def deploy_cloudwatch_dashboard (event : dict ) -> None :
10671066 global DRY_RUN_DATA
10681067 global LIVE_RUN_DATA
10691068 global CFN_RESPONSE_DATA
@@ -1091,22 +1090,8 @@ def deploy_cloudwatch_dashboard(event):
10911090 else :
10921091 LOGGER .info (f"Cloudwatch dashboard already exists: { search_dashboard [1 ]} " )
10931092 add_state_table_record ("cloudwatch" , "implemented" , "cloudwatch dashboard" , "dashboard" , search_dashboard [1 ], ssm_params .SRA_SECURITY_ACCT , sts .HOME_REGION , SOLUTION_NAME )
1094- # check_dashboard = cloudwatch.compare_dashboard(search_dashboard[1], cloudwatch_dashboard)
1095- # if check_dashboard is False:
1096- # if DRY_RUN is False:
1097- # LOGGER.info("CloudWatch observability dashboard needs updating...")
1098- # cloudwatch.create_dashboard(cloudwatch.SOLUTION_NAME, cloudwatch_dashboard)
1099- # LIVE_RUN_DATA["OAMDashboardUpdate"] = "Updated CloudWatch observability dashboard"
1100- # CFN_RESPONSE_DATA["deployment_info"]["action_count"] += 1
1101- # CFN_RESPONSE_DATA["deployment_info"]["configuration_changes"] += 1
1102- # LOGGER.info("Updated CloudWatch observability dashboard")
1103- # else:
1104- # LOGGER.info("DRY_RUN: CloudWatch observability dashboard needs updating...")
1105- # DRY_RUN_DATA["OAMDashboardUpdate"] = "DRY_RUN: Update CloudWatch observability dashboard"
1106- # else:
1107- # LOGGER.info("CloudWatch observability dashboard is correct")
1108-
1109- def remove_cloudwatch_dashboard ():
1093+
1094+ def remove_cloudwatch_dashboard () -> None :
11101095 global DRY_RUN_DATA
11111096 global LIVE_RUN_DATA
11121097 global CFN_RESPONSE_DATA
@@ -1131,7 +1116,7 @@ def remove_cloudwatch_dashboard():
11311116 remove_state_table_record (f"arn:aws:cloudwatch::{ ssm_params .SRA_SECURITY_ACCT } :dashboard/{ SOLUTION_NAME } " )
11321117
11331118
1134- def create_event (event , context ) :
1119+ def create_event (event : dict , context : Any ) -> str :
11351120 global DRY_RUN_DATA
11361121 global LIVE_RUN_DATA
11371122 global CFN_RESPONSE_DATA
@@ -1144,7 +1129,6 @@ def create_event(event, context):
11441129 LOGGER .info (event_info )
11451130 LOGGER .info (f"CFN_RESPONSE_DATA START: { CFN_RESPONSE_DATA } " )
11461131 # Deploy state table
1147- # TODO(liamschn): need to ensure the solution name for the state table record is sra-common-prerequisites (if it is created here), not bedrock
11481132 deploy_state_table ()
11491133 LOGGER .info (f"CFN_RESPONSE_DATA POST deploy_state_table: { CFN_RESPONSE_DATA } " )
11501134 # add IAM state table record for the lambda execution role
@@ -1197,23 +1181,15 @@ def create_event(event, context):
11971181 return CFN_RESOURCE_ID
11981182
11991183
1200- def update_event (event , context ):
1201- # TODO(liamschn): handle CFN update events; use case: change from DRY_RUN = False to DRY_RUN = True or vice versa
1184+ def update_event (event : dict , context : Any ) -> str :
12021185 # TODO(liamschn): handle CFN update events; use case: add additional config rules via new rules in code (i.e. ...\rules\new_rule\app.py)
12031186 # TODO(liamschn): handle CFN update events; use case: changing config rule parameters (i.e. deploy, accounts, regions, input_params)
1204- # TODO(liamschn): handle CFN update events; use case: setting deploy = false should remove the config rule
12051187 global DRY_RUN_DATA
12061188 LOGGER .info ("update event function" )
1207- # Temp calling create_event so that an update will actually do something; need to determine if this is the best way or not.
12081189 create_event (event , context )
1209- # data = sra_s3.s3_resource_check()
1210- # TODO(liamschn): update data dictionary
1211- # data = {"data": "no info"}
1212- # if RESOURCE_TYPE != "Other":
1213- # cfnresponse.send(event, context, cfnresponse.SUCCESS, data, CFN_RESOURCE_ID)
12141190 return CFN_RESOURCE_ID
12151191
1216- def delete_custom_config_rule (rule_name : str , acct : str , region : str ):
1192+ def delete_custom_config_rule (rule_name : str , acct : str , region : str ) -> None :
12171193 # Delete the config rule
12181194 config .CONFIG_CLIENT = sts .assume_role (acct , sts .CONFIGURATION_ROLE , "config" , region )
12191195 config_rule_search = config .find_config_rule (rule_name )
@@ -1234,7 +1210,6 @@ def delete_custom_config_rule(rule_name: str, acct: str, region: str):
12341210 # Delete lambda for custom config rule
12351211 lambdas .LAMBDA_CLIENT = sts .assume_role (acct , sts .CONFIGURATION_ROLE , "lambda" , region )
12361212 lambda_search = lambdas .find_lambda_function (rule_name )
1237- # TODO(liamschn): this will be a mypy error - need to have lambda_search return string, not None
12381213 if lambda_search is not None :
12391214 if DRY_RUN is False :
12401215 LOGGER .info (f"Deleting { rule_name } lambda function for account { acct } in { region } " )
@@ -1249,7 +1224,7 @@ def delete_custom_config_rule(rule_name: str, acct: str, region: str):
12491224 else :
12501225 LOGGER .info (f"{ rule_name } lambda function for account { acct } in { region } does not exist." )
12511226
1252- def delete_custom_config_iam_role (rule_name : str , acct : str ):
1227+ def delete_custom_config_iam_role (rule_name : str , acct : str ) -> None :
12531228 global DRY_RUN_DATA
12541229 global LIVE_RUN_DATA
12551230 global CFN_RESPONSE_DATA
@@ -1330,10 +1305,9 @@ def delete_custom_config_iam_role(rule_name: str, acct: str):
13301305 else :
13311306 LOGGER .info (f"{ rule_name } IAM role for account { acct } in { region } does not exist." )
13321307
1333- def delete_sns_topic_and_key (acct : str , region : str ):
1308+ def delete_sns_topic_and_key (acct : str , region : str ) -> None :
13341309 # Delete the alarm topic
13351310 sns .SNS_CLIENT = sts .assume_role (acct , sts .CONFIGURATION_ROLE , "sns" , region )
1336- # TODO(liamschn): this will be a mypy error - need to have alarm_topic_search (sns.find_sns_topic) return string, not None
13371311 alarm_topic_search = sns .find_sns_topic (f"{ SOLUTION_NAME } -alarms" , region , acct )
13381312 if alarm_topic_search is not None :
13391313 if DRY_RUN is False :
@@ -1380,7 +1354,7 @@ def delete_sns_topic_and_key(acct: str, region: str):
13801354 LOGGER .info (f"{ ALARM_SNS_KEY_ALIAS } KMS key does not exist." )
13811355
13821356
1383- def delete_metric_filter_and_alarm (filter_name : str , acct : str , region : str , filter_params : dict ):
1357+ def delete_metric_filter_and_alarm (filter_name : str , acct : str , region : str , filter_params : dict ) -> None :
13841358 cloudwatch .CWLOGS_CLIENT = sts .assume_role (acct , sts .CONFIGURATION_ROLE , "logs" , region )
13851359 cloudwatch .CLOUDWATCH_CLIENT = sts .assume_role (acct , sts .CONFIGURATION_ROLE , "cloudwatch" , region )
13861360 if DRY_RUN is False :
@@ -1419,7 +1393,7 @@ def delete_metric_filter_and_alarm(filter_name: str, acct: str, region: str, fil
14191393 LOGGER .info (f"DRY_RUN: Delete { filter_name } CloudWatch metric filter" )
14201394 DRY_RUN_DATA [f"{ filter_name } _CloudWatchDelete" ] = f"DRY_RUN: Delete { filter_name } CloudWatch metric filter"
14211395
1422- def delete_event (event , context ) :
1396+ def delete_event (event : dict , context : Any ) -> None :
14231397 # TODO(liamschn): handle delete error if IAM policy is updated out-of-band - botocore.errorfactory.DeleteConflictException: An error occurred (DeleteConflict) when calling the DeletePolicy operation: This policy has more than one version. Before you delete a policy, you must delete the policy's versions. The default version is deleted with the policy.
14241398 # TODO(liamschn): move re-used delete event operation code to separate functions
14251399 global DRY_RUN_DATA
@@ -1436,7 +1410,6 @@ def delete_event(event, context):
14361410 # 1a) Delete configuration topic
14371411 sns .SNS_CLIENT = sts .assume_role (sts .MANAGEMENT_ACCOUNT , sts .CONFIGURATION_ROLE , "sns" , sts .HOME_REGION )
14381412 topic_search = sns .find_sns_topic (f"{ SOLUTION_NAME } -configuration" )
1439- # TODO(liamschn): this will be a mypy error: need to have topic_search (sns.find_sns_topic) return a str, not None
14401413 if topic_search is not None :
14411414 if DRY_RUN is False :
14421415 LOGGER .info (f"Deleting { SOLUTION_NAME } -configuration SNS topic" )
@@ -1618,7 +1591,7 @@ def create_sns_messages(accounts: list, regions: list, sns_topic_arn: str, resou
16181591 DRY_RUN_DATA ["SNSFanout" ] = "DRY_RUN: Published SNS messages for regional fanout configuration. More dry run data in subsequent log streams."
16191592
16201593
1621- def process_sns_records (event ) -> None :
1594+ def process_sns_records (event : dict ) -> None :
16221595 """Process SNS records.
16231596
16241597 Args:
@@ -1875,7 +1848,7 @@ def deploy_config_rule(account_id: str, rule_name: str, lambda_arn: str, region:
18751848 add_state_table_record ("config" , "implemented" , "config rule" , "rule" , config_rule_arn , account_id , region , rule_name )
18761849
18771850
1878- def deploy_metric_filter (region : str , acct : str , log_group_name : str , filter_name : str , filter_pattern : str , metric_name : str , metric_namespace : str , metric_value : str ):
1851+ def deploy_metric_filter (region : str , acct : str , log_group_name : str , filter_name : str , filter_pattern : str , metric_name : str , metric_namespace : str , metric_value : str ) -> None :
18791852 """Deploy metric filter.
18801853
18811854 Args:
@@ -1918,7 +1891,7 @@ def deploy_metric_alarm(
19181891 metric_comparison_operator : str ,
19191892 metric_treat_missing_data : str ,
19201893 alarm_actions : list ,
1921- ):
1894+ ) -> None :
19221895 """Deploy metric alarm.
19231896
19241897 Args:
@@ -1964,7 +1937,7 @@ def deploy_metric_alarm(
19641937 add_state_table_record ("cloudwatch" , "implemented" , "cloudwatch metric alarm" , "alarm" , alarm_arn , acct , region , alarm_name )
19651938
19661939
1967- def lambda_handler (event , context ) :
1940+ def lambda_handler (event : dict , context : Any ) -> dict :
19681941 global RESOURCE_TYPE
19691942 global LAMBDA_START
19701943 global LAMBDA_FINISH
0 commit comments