1+ import copy
12import json
23import os
34import logging
@@ -57,14 +58,15 @@ def load_kms_key_policies() -> dict:
5758def load_cloudwatch_oam_sink_policy () -> dict :
5859 with open ("sra_cloudwatch_oam_sink_policy.json" , "r" ) as file :
5960 return json .load (file )
60- # ["sra-oam-sink-policy"]["Statement"][0]["Condition"]["ForAnyValue:StringEquals"]["aws:PrincipalOrgID"]
6161
6262
6363def load_sra_cloudwatch_oam_trust_policy () -> dict :
6464 with open ("sra_cloudwatch_oam_trust_policy.json" , "r" ) as file :
6565 return json .load (file )
66- # ["Statement"][0]["Principal"]["AWS"]
6766
67+ def load_sra_cloudwatch_dashboard () -> dict :
68+ with open ("sra_cloudwatch_dashboard.json" , "r" ) as file :
69+ return json .load (file )
6870
6971# Global vars
7072RESOURCE_TYPE : str = ""
@@ -84,6 +86,7 @@ def load_sra_cloudwatch_oam_trust_policy() -> dict:
8486ACCOUNT : str = boto3 .client ("sts" ).get_caller_identity ().get ("Account" )
8587REGION : str = os .environ .get ("AWS_REGION" )
8688CFN_RESOURCE_ID : str = "sra-bedrock-org-function"
89+ ALARM_SNS_KEY_ALIAS = "sra-alarm-sns-key"
8790
8891# CFN_RESPONSE_DATA definition:
8992# dry_run: bool - type of run
@@ -107,7 +110,7 @@ def load_sra_cloudwatch_oam_trust_policy() -> dict:
107110KMS_KEY_POLICIES : dict = load_kms_key_policies ()
108111CLOUDWATCH_OAM_SINK_POLICY : dict = load_cloudwatch_oam_sink_policy ()
109112CLOUDWATCH_OAM_TRUST_POLICY : dict = load_sra_cloudwatch_oam_trust_policy ()
110- ALARM_SNS_KEY_ALIAS = "sra-alarm-sns-key"
113+ CLOUDWATCH_DASHBOARD : dict = load_sra_cloudwatch_dashboard ()
111114
112115# Instantiate sra class objects
113116# todo(liamschn): can these files exist in some central location to be shared with other solutions?
@@ -325,6 +328,27 @@ def build_s3_metric_filter_pattern(bucket_names: list, filter_pattern_template:
325328 s3_filter = s3_filter .replace ('&& ($.requestParameters.bucketName = "<BUCKET_NAME_PLACEHOLDER>")' , "" )
326329 return s3_filter
327330
331+ def build_cloudwatch_dashboard (dashboard_template , bedrock_accounts , regions ):
332+ i = 0
333+ for bedrock_account in bedrock_accounts :
334+ for region in regions :
335+ if i == 0 :
336+ injection_template = copy .deepcopy (dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["metrics" ][2 ])
337+ sensitive_info_template = copy .deepcopy (dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["metrics" ][3 ])
338+ else :
339+ dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["metrics" ].append (copy .deepcopy (injection_template ))
340+ dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["metrics" ].append (copy .deepcopy (sensitive_info_template ))
341+ dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["metrics" ][2 + i ][2 ]["accountId" ] = bedrock_account
342+ dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["metrics" ][2 + i ][2 ]["region" ] = region
343+ dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["metrics" ][3 + i ][2 ]["accountId" ] = bedrock_account
344+ dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["metrics" ][3 + i ][2 ]["region" ] = region
345+ i += 2
346+ dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["metrics" ][0 ][2 ]["accountId" ] = sts .MANAGEMENT_ACCOUNT
347+ dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["metrics" ][0 ][2 ]["region" ] = sts .HOME_REGION
348+ dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["metrics" ][1 ][2 ]["accountId" ] = sts .MANAGEMENT_ACCOUNT
349+ dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["metrics" ][1 ][2 ]["region" ] = sts .HOME_REGION
350+ dashboard_template ["sra-bedrock-org" ]["widgets" ][0 ]["properties" ]["region" ] = sts .HOME_REGION
351+ return dashboard_template
328352
329353def create_event (event , context ):
330354 global DRY_RUN_DATA
@@ -704,7 +728,7 @@ def create_event(event, context):
704728 "OAMCrossAccountRolePolicyAttach"
705729 ] = f"DRY_RUN: Attach { policy_arn } policy to { cloudwatch .CROSS_ACCOUNT_ROLE_NAME } IAM role"
706730
707- # 5d ) OAM link in bedrock account
731+ # 5e ) OAM link in bedrock account
708732 cloudwatch .CWOAM_CLIENT = sts .assume_role (bedrock_account , sts .CONFIGURATION_ROLE , "oam" , bedrock_region )
709733 search_oam_link = cloudwatch .find_oam_link (oam_sink_arn )
710734 if search_oam_link [0 ] is False :
@@ -721,6 +745,41 @@ def create_event(event, context):
721745 else :
722746 LOGGER .info ("CloudWatch observability access manager link found" )
723747
748+ # 6) Cloudwatch dashboard in security account
749+ cloudwatch_dashboard = build_cloudwatch_dashboard (CLOUDWATCH_DASHBOARD , central_observability_params ["bedrock_accounts" ], central_observability_params ["regions" ])
750+ cloudwatch .CLOUDWATCH_CLIENT = sts .assume_role (SECURITY_ACCOUNT , sts .CONFIGURATION_ROLE , "cloudwatch" , sts .HOME_REGION )
751+ # sra-bedrock-filter-prompt-injection-metric template ["sra-bedrock-org"]["widgets"][0]["properties"]["metrics"][2]
752+ # sra-bedrock-filter-sensitive-info-metric template ["sra-bedrock-org"]["widgets"][0]["properties"]["metrics"][3]
753+
754+ search_dashboard = cloudwatch .find_dashboard (SOLUTION_NAME )
755+ if search_dashboard [0 ] is False :
756+ if DRY_RUN is False :
757+ LOGGER .info ("CloudWatch observability dashboard not found, creating..." )
758+ cloudwatch .create_dashboard (cloudwatch .SOLUTION_NAME , cloudwatch_dashboard )
759+ LIVE_RUN_DATA ["CloudWatchDashboardCreate" ] = "Created CloudWatch observability dashboard"
760+ CFN_RESPONSE_DATA ["deployment_info" ]["action_count" ] += 1
761+ CFN_RESPONSE_DATA ["deployment_info" ]["resources_deployed" ] += 1
762+ LOGGER .info ("Created CloudWatch observability dashboard" )
763+ else :
764+ LOGGER .info ("DRY_RUN: CloudWatch observability dashboard not found, creating..." )
765+ DRY_RUN_DATA ["CloudWatchDashboardCreate" ] = "DRY_RUN: Create CloudWatch observability dashboard"
766+ else :
767+ LOGGER .info (f"Cloudwatch dashboard already exists: { search_dashboard [1 ]} " )
768+ # check_dashboard = cloudwatch.compare_dashboard(search_dashboard[1], cloudwatch_dashboard)
769+ # if check_dashboard is False:
770+ # if DRY_RUN is False:
771+ # LOGGER.info("CloudWatch observability dashboard needs updating...")
772+ # cloudwatch.create_dashboard(cloudwatch.SOLUTION_NAME, cloudwatch_dashboard)
773+ # LIVE_RUN_DATA["OAMDashboardUpdate"] = "Updated CloudWatch observability dashboard"
774+ # CFN_RESPONSE_DATA["deployment_info"]["action_count"] += 1
775+ # CFN_RESPONSE_DATA["deployment_info"]["configuration_changes"] += 1
776+ # LOGGER.info("Updated CloudWatch observability dashboard")
777+ # else:
778+ # LOGGER.info("DRY_RUN: CloudWatch observability dashboard needs updating...")
779+ # DRY_RUN_DATA["OAMDashboardUpdate"] = "DRY_RUN: Update CloudWatch observability dashboard"
780+ # else:
781+ # LOGGER.info("CloudWatch observability dashboard is correct")
782+
724783 # End
725784 # TODO(liamschn): Consider the 256 KB limit for any cloudwatch log message
726785 if DRY_RUN is False :
0 commit comments