From cdf8bd04003e51f3497dc60dd16a7a88842a6b8e Mon Sep 17 00:00:00 2001 From: zqumei0 <92476851+zqumei0@users.noreply.github.com> Date: Thu, 6 Jul 2023 15:23:57 -0400 Subject: [PATCH] feat(SecretsManager): Add support for secrets count metric (#333) Added new Secrets Manager Monitor to support Account-Level Secrets Count. This includes a new SecretsManagerMonitor, a new SecretsManagerMetricsFactory, and a new SecretsManagerAlarmFactory. There is alarm support for Min/Max Secrets count and change in secrets count. Closes #137 --- _By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license_ --- API.md | 1568 ++++++++++++++++- README.md | 3 +- .../alarms/SecretsManagerAlarmFactory.ts | 118 ++ lib/common/monitoring/alarms/index.ts | 1 + lib/facade/MonitoringFacade.ts | 8 + .../SecretsManagerMetricFactory.ts | 37 + .../SecretsManagerMonitoring.ts | 139 ++ lib/monitoring/aws-secretsmanager/index.ts | 2 + .../SecretsManagerMonitoring.test.ts | 57 + .../SecretsManagerMonitoring.test.ts.snap | 341 ++++ 10 files changed, 2271 insertions(+), 3 deletions(-) create mode 100644 lib/common/monitoring/alarms/SecretsManagerAlarmFactory.ts create mode 100644 lib/monitoring/aws-secretsmanager/SecretsManagerMetricFactory.ts create mode 100644 lib/monitoring/aws-secretsmanager/SecretsManagerMonitoring.ts create mode 100644 test/monitoring/aws-secretsmanager/SecretsManagerMonitoring.test.ts create mode 100644 test/monitoring/aws-secretsmanager/__snapshots__/SecretsManagerMonitoring.test.ts.snap diff --git a/API.md b/API.md index bd712755..776f2c76 100644 --- a/API.md +++ b/API.md @@ -1061,6 +1061,7 @@ new MonitoringFacade(scope: Construct, id: string, props?: MonitoringFacadeProps | monitorRedshiftCluster | *No description.* | | monitorS3Bucket | *No description.* | | monitorScope | Uses an aspect to automatically monitor all resources in the given scope. | +| monitorSecretsManager | *No description.* | | monitorSecretsManagerSecret | *No description.* | | monitorSimpleEc2Service | *No description.* | | monitorSimpleFargateService | *No description.* | @@ -1812,6 +1813,18 @@ Optional configuration. --- +##### `monitorSecretsManager` + +```typescript +public monitorSecretsManager(props: SecretsManagerMonitoringProps): MonitoringFacade +``` + +###### `props`Required + +- *Type:* SecretsManagerMonitoringProps + +--- + ##### `monitorSecretsManagerSecret` ```typescript @@ -8655,6 +8668,319 @@ public readonly certificate: ICertificate; --- +### ChangeInSecretCountThreshold + +#### Initializer + +```typescript +import { ChangeInSecretCountThreshold } from 'cdk-monitoring-constructs' + +const changeInSecretCountThreshold: ChangeInSecretCountThreshold = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| actionOverride | IAlarmActionStrategy | Allows to override the default alarm action. | +| actionsEnabled | boolean | Enables the configured CloudWatch alarm ticketing actions. | +| alarmDescriptionOverride | string | A text included in the generated ticket description body, which fully replaces the generated text. | +| alarmNameOverride | string | If this is defined, the alarm name is set to this exact value. | +| comparisonOperatorOverride | aws-cdk-lib.aws_cloudwatch.ComparisonOperator | Comparison operator used to compare actual value against the threshold. | +| customParams | {[ key: string ]: any} | This allows user to attach custom parameters to this alarm, which can later be accessed from the "useCreatedAlarms" method. | +| customTags | string[] | This allows user to attach custom values to this alarm, which can later be accessed from the "useCreatedAlarms" method. | +| datapointsToAlarm | number | Number of breaches required to transition into an ALARM state. | +| dedupeStringOverride | string | If this is defined, the alarm dedupe string is set to this exact value. | +| documentationLink | string | An optional link included in the generated ticket description body. | +| evaluateLowSampleCountPercentile | boolean | Used only for alarms based on percentiles. | +| evaluationPeriods | number | Number of periods to consider when checking the number of breaching datapoints. | +| fillAlarmRange | boolean | Indicates whether the alarming range of values should be highlighted in the widget. | +| minMetricSamplesToAlarm | number | Specifies how many samples (N) of the metric is needed to trigger the alarm. | +| period | aws-cdk-lib.Duration | Period override for the metric to alarm on. | +| runbookLink | string | An optional link included in the generated ticket description body. | +| treatMissingDataOverride | aws-cdk-lib.aws_cloudwatch.TreatMissingData | Behaviour in case the metric data is missing. | +| alarmWhenDecreased | boolean | *No description.* | +| alarmWhenIncreased | boolean | *No description.* | +| requiredSecretCount | number | *No description.* | +| additionalDescription | string | *No description.* | + +--- + +##### `actionOverride`Optional + +```typescript +public readonly actionOverride: IAlarmActionStrategy; +``` + +- *Type:* IAlarmActionStrategy +- *Default:* undefined (default action will be used, if any) + +Allows to override the default alarm action. + +--- + +##### `actionsEnabled`Optional + +```typescript +public readonly actionsEnabled: boolean; +``` + +- *Type:* boolean +- *Default:* the same as monitoring facade default + +Enables the configured CloudWatch alarm ticketing actions. + +--- + +##### `alarmDescriptionOverride`Optional + +```typescript +public readonly alarmDescriptionOverride: string; +``` + +- *Type:* string +- *Default:* default auto-generated content only + +A text included in the generated ticket description body, which fully replaces the generated text. + +--- + +##### `alarmNameOverride`Optional + +```typescript +public readonly alarmNameOverride: string; +``` + +- *Type:* string + +If this is defined, the alarm name is set to this exact value. + +Please be aware that you need to specify prefix for different stages (Beta, Prod...) and realms (EU, NA...) manually. + +--- + +##### `comparisonOperatorOverride`Optional + +```typescript +public readonly comparisonOperatorOverride: ComparisonOperator; +``` + +- *Type:* aws-cdk-lib.aws_cloudwatch.ComparisonOperator +- *Default:* alarm-specific default + +Comparison operator used to compare actual value against the threshold. + +--- + +##### `customParams`Optional + +```typescript +public readonly customParams: {[ key: string ]: any}; +``` + +- *Type:* {[ key: string ]: any} +- *Default:* no parameters + +This allows user to attach custom parameters to this alarm, which can later be accessed from the "useCreatedAlarms" method. + +--- + +##### `customTags`Optional + +```typescript +public readonly customTags: string[]; +``` + +- *Type:* string[] +- *Default:* no tags + +This allows user to attach custom values to this alarm, which can later be accessed from the "useCreatedAlarms" method. + +--- + +##### `datapointsToAlarm`Optional + +```typescript +public readonly datapointsToAlarm: number; +``` + +- *Type:* number +- *Default:* the same as monitoring facade default + +Number of breaches required to transition into an ALARM state. + +--- + +##### `dedupeStringOverride`Optional + +```typescript +public readonly dedupeStringOverride: string; +``` + +- *Type:* string +- *Default:* undefined (no override) + +If this is defined, the alarm dedupe string is set to this exact value. + +Please be aware that you need to handle deduping for different stages (Beta, Prod...) and realms (EU, NA...) manually. +Dedupe strings are global and not unique per CTI. + +--- + +##### `documentationLink`Optional + +```typescript +public readonly documentationLink: string; +``` + +- *Type:* string +- *Default:* no additional link will be added + +An optional link included in the generated ticket description body. + +--- + +##### `evaluateLowSampleCountPercentile`Optional + +```typescript +public readonly evaluateLowSampleCountPercentile: boolean; +``` + +- *Type:* boolean +- *Default:* true + +Used only for alarms based on percentiles. + +If you specify false, the alarm state does not change during periods with too few data points to be statistically significant. +If you specify true, the alarm is always evaluated and possibly changes state no matter how many data points are available. + +--- + +##### `evaluationPeriods`Optional + +```typescript +public readonly evaluationPeriods: number; +``` + +- *Type:* number +- *Default:* the same as monitoring facade default + +Number of periods to consider when checking the number of breaching datapoints. + +--- + +##### `fillAlarmRange`Optional + +```typescript +public readonly fillAlarmRange: boolean; +``` + +- *Type:* boolean +- *Default:* false + +Indicates whether the alarming range of values should be highlighted in the widget. + +--- + +##### `minMetricSamplesToAlarm`Optional + +```typescript +public readonly minMetricSamplesToAlarm: number; +``` + +- *Type:* number +- *Default:* default behaviour - no condition on sample count will be added to the alarm + +Specifies how many samples (N) of the metric is needed to trigger the alarm. + +If this property is specified, a composite alarm is created of the following: + +This composite alarm will be returned as a result and uses the specified alarm actions. + +--- + +##### `period`Optional + +```typescript +public readonly period: Duration; +``` + +- *Type:* aws-cdk-lib.Duration +- *Default:* the default specified in MetricFactory + +Period override for the metric to alarm on. + +--- + +##### `runbookLink`Optional + +```typescript +public readonly runbookLink: string; +``` + +- *Type:* string +- *Default:* no additional link will be added + +An optional link included in the generated ticket description body. + +--- + +##### `treatMissingDataOverride`Optional + +```typescript +public readonly treatMissingDataOverride: TreatMissingData; +``` + +- *Type:* aws-cdk-lib.aws_cloudwatch.TreatMissingData +- *Default:* alarm-specific default + +Behaviour in case the metric data is missing. + +--- + +##### `alarmWhenDecreased`Required + +```typescript +public readonly alarmWhenDecreased: boolean; +``` + +- *Type:* boolean + +--- + +##### `alarmWhenIncreased`Required + +```typescript +public readonly alarmWhenIncreased: boolean; +``` + +- *Type:* boolean + +--- + +##### `requiredSecretCount`Required + +```typescript +public readonly requiredSecretCount: number; +``` + +- *Type:* number + +--- + +##### `additionalDescription`Optional + +```typescript +public readonly additionalDescription: string; +``` + +- *Type:* string + +--- + ### CloudFrontDistributionMetricFactoryProps #### Initializer @@ -24578,6 +24904,286 @@ public readonly maxMessageCount: number; --- +### MaxSecretCountThreshold + +#### Initializer + +```typescript +import { MaxSecretCountThreshold } from 'cdk-monitoring-constructs' + +const maxSecretCountThreshold: MaxSecretCountThreshold = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| actionOverride | IAlarmActionStrategy | Allows to override the default alarm action. | +| actionsEnabled | boolean | Enables the configured CloudWatch alarm ticketing actions. | +| alarmDescriptionOverride | string | A text included in the generated ticket description body, which fully replaces the generated text. | +| alarmNameOverride | string | If this is defined, the alarm name is set to this exact value. | +| comparisonOperatorOverride | aws-cdk-lib.aws_cloudwatch.ComparisonOperator | Comparison operator used to compare actual value against the threshold. | +| customParams | {[ key: string ]: any} | This allows user to attach custom parameters to this alarm, which can later be accessed from the "useCreatedAlarms" method. | +| customTags | string[] | This allows user to attach custom values to this alarm, which can later be accessed from the "useCreatedAlarms" method. | +| datapointsToAlarm | number | Number of breaches required to transition into an ALARM state. | +| dedupeStringOverride | string | If this is defined, the alarm dedupe string is set to this exact value. | +| documentationLink | string | An optional link included in the generated ticket description body. | +| evaluateLowSampleCountPercentile | boolean | Used only for alarms based on percentiles. | +| evaluationPeriods | number | Number of periods to consider when checking the number of breaching datapoints. | +| fillAlarmRange | boolean | Indicates whether the alarming range of values should be highlighted in the widget. | +| minMetricSamplesToAlarm | number | Specifies how many samples (N) of the metric is needed to trigger the alarm. | +| period | aws-cdk-lib.Duration | Period override for the metric to alarm on. | +| runbookLink | string | An optional link included in the generated ticket description body. | +| treatMissingDataOverride | aws-cdk-lib.aws_cloudwatch.TreatMissingData | Behaviour in case the metric data is missing. | +| maxSecretCount | number | *No description.* | + +--- + +##### `actionOverride`Optional + +```typescript +public readonly actionOverride: IAlarmActionStrategy; +``` + +- *Type:* IAlarmActionStrategy +- *Default:* undefined (default action will be used, if any) + +Allows to override the default alarm action. + +--- + +##### `actionsEnabled`Optional + +```typescript +public readonly actionsEnabled: boolean; +``` + +- *Type:* boolean +- *Default:* the same as monitoring facade default + +Enables the configured CloudWatch alarm ticketing actions. + +--- + +##### `alarmDescriptionOverride`Optional + +```typescript +public readonly alarmDescriptionOverride: string; +``` + +- *Type:* string +- *Default:* default auto-generated content only + +A text included in the generated ticket description body, which fully replaces the generated text. + +--- + +##### `alarmNameOverride`Optional + +```typescript +public readonly alarmNameOverride: string; +``` + +- *Type:* string + +If this is defined, the alarm name is set to this exact value. + +Please be aware that you need to specify prefix for different stages (Beta, Prod...) and realms (EU, NA...) manually. + +--- + +##### `comparisonOperatorOverride`Optional + +```typescript +public readonly comparisonOperatorOverride: ComparisonOperator; +``` + +- *Type:* aws-cdk-lib.aws_cloudwatch.ComparisonOperator +- *Default:* alarm-specific default + +Comparison operator used to compare actual value against the threshold. + +--- + +##### `customParams`Optional + +```typescript +public readonly customParams: {[ key: string ]: any}; +``` + +- *Type:* {[ key: string ]: any} +- *Default:* no parameters + +This allows user to attach custom parameters to this alarm, which can later be accessed from the "useCreatedAlarms" method. + +--- + +##### `customTags`Optional + +```typescript +public readonly customTags: string[]; +``` + +- *Type:* string[] +- *Default:* no tags + +This allows user to attach custom values to this alarm, which can later be accessed from the "useCreatedAlarms" method. + +--- + +##### `datapointsToAlarm`Optional + +```typescript +public readonly datapointsToAlarm: number; +``` + +- *Type:* number +- *Default:* the same as monitoring facade default + +Number of breaches required to transition into an ALARM state. + +--- + +##### `dedupeStringOverride`Optional + +```typescript +public readonly dedupeStringOverride: string; +``` + +- *Type:* string +- *Default:* undefined (no override) + +If this is defined, the alarm dedupe string is set to this exact value. + +Please be aware that you need to handle deduping for different stages (Beta, Prod...) and realms (EU, NA...) manually. +Dedupe strings are global and not unique per CTI. + +--- + +##### `documentationLink`Optional + +```typescript +public readonly documentationLink: string; +``` + +- *Type:* string +- *Default:* no additional link will be added + +An optional link included in the generated ticket description body. + +--- + +##### `evaluateLowSampleCountPercentile`Optional + +```typescript +public readonly evaluateLowSampleCountPercentile: boolean; +``` + +- *Type:* boolean +- *Default:* true + +Used only for alarms based on percentiles. + +If you specify false, the alarm state does not change during periods with too few data points to be statistically significant. +If you specify true, the alarm is always evaluated and possibly changes state no matter how many data points are available. + +--- + +##### `evaluationPeriods`Optional + +```typescript +public readonly evaluationPeriods: number; +``` + +- *Type:* number +- *Default:* the same as monitoring facade default + +Number of periods to consider when checking the number of breaching datapoints. + +--- + +##### `fillAlarmRange`Optional + +```typescript +public readonly fillAlarmRange: boolean; +``` + +- *Type:* boolean +- *Default:* false + +Indicates whether the alarming range of values should be highlighted in the widget. + +--- + +##### `minMetricSamplesToAlarm`Optional + +```typescript +public readonly minMetricSamplesToAlarm: number; +``` + +- *Type:* number +- *Default:* default behaviour - no condition on sample count will be added to the alarm + +Specifies how many samples (N) of the metric is needed to trigger the alarm. + +If this property is specified, a composite alarm is created of the following: + +This composite alarm will be returned as a result and uses the specified alarm actions. + +--- + +##### `period`Optional + +```typescript +public readonly period: Duration; +``` + +- *Type:* aws-cdk-lib.Duration +- *Default:* the default specified in MetricFactory + +Period override for the metric to alarm on. + +--- + +##### `runbookLink`Optional + +```typescript +public readonly runbookLink: string; +``` + +- *Type:* string +- *Default:* no additional link will be added + +An optional link included in the generated ticket description body. + +--- + +##### `treatMissingDataOverride`Optional + +```typescript +public readonly treatMissingDataOverride: TreatMissingData; +``` + +- *Type:* aws-cdk-lib.aws_cloudwatch.TreatMissingData +- *Default:* alarm-specific default + +Behaviour in case the metric data is missing. + +--- + +##### `maxSecretCount`Required + +```typescript +public readonly maxSecretCount: number; +``` + +- *Type:* number + +--- + ### MaxTimeToDrainThreshold #### Initializer @@ -26652,6 +27258,286 @@ public readonly minRunningTasks: number; --- +### MinSecretCountThreshold + +#### Initializer + +```typescript +import { MinSecretCountThreshold } from 'cdk-monitoring-constructs' + +const minSecretCountThreshold: MinSecretCountThreshold = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| actionOverride | IAlarmActionStrategy | Allows to override the default alarm action. | +| actionsEnabled | boolean | Enables the configured CloudWatch alarm ticketing actions. | +| alarmDescriptionOverride | string | A text included in the generated ticket description body, which fully replaces the generated text. | +| alarmNameOverride | string | If this is defined, the alarm name is set to this exact value. | +| comparisonOperatorOverride | aws-cdk-lib.aws_cloudwatch.ComparisonOperator | Comparison operator used to compare actual value against the threshold. | +| customParams | {[ key: string ]: any} | This allows user to attach custom parameters to this alarm, which can later be accessed from the "useCreatedAlarms" method. | +| customTags | string[] | This allows user to attach custom values to this alarm, which can later be accessed from the "useCreatedAlarms" method. | +| datapointsToAlarm | number | Number of breaches required to transition into an ALARM state. | +| dedupeStringOverride | string | If this is defined, the alarm dedupe string is set to this exact value. | +| documentationLink | string | An optional link included in the generated ticket description body. | +| evaluateLowSampleCountPercentile | boolean | Used only for alarms based on percentiles. | +| evaluationPeriods | number | Number of periods to consider when checking the number of breaching datapoints. | +| fillAlarmRange | boolean | Indicates whether the alarming range of values should be highlighted in the widget. | +| minMetricSamplesToAlarm | number | Specifies how many samples (N) of the metric is needed to trigger the alarm. | +| period | aws-cdk-lib.Duration | Period override for the metric to alarm on. | +| runbookLink | string | An optional link included in the generated ticket description body. | +| treatMissingDataOverride | aws-cdk-lib.aws_cloudwatch.TreatMissingData | Behaviour in case the metric data is missing. | +| minSecretCount | number | *No description.* | + +--- + +##### `actionOverride`Optional + +```typescript +public readonly actionOverride: IAlarmActionStrategy; +``` + +- *Type:* IAlarmActionStrategy +- *Default:* undefined (default action will be used, if any) + +Allows to override the default alarm action. + +--- + +##### `actionsEnabled`Optional + +```typescript +public readonly actionsEnabled: boolean; +``` + +- *Type:* boolean +- *Default:* the same as monitoring facade default + +Enables the configured CloudWatch alarm ticketing actions. + +--- + +##### `alarmDescriptionOverride`Optional + +```typescript +public readonly alarmDescriptionOverride: string; +``` + +- *Type:* string +- *Default:* default auto-generated content only + +A text included in the generated ticket description body, which fully replaces the generated text. + +--- + +##### `alarmNameOverride`Optional + +```typescript +public readonly alarmNameOverride: string; +``` + +- *Type:* string + +If this is defined, the alarm name is set to this exact value. + +Please be aware that you need to specify prefix for different stages (Beta, Prod...) and realms (EU, NA...) manually. + +--- + +##### `comparisonOperatorOverride`Optional + +```typescript +public readonly comparisonOperatorOverride: ComparisonOperator; +``` + +- *Type:* aws-cdk-lib.aws_cloudwatch.ComparisonOperator +- *Default:* alarm-specific default + +Comparison operator used to compare actual value against the threshold. + +--- + +##### `customParams`Optional + +```typescript +public readonly customParams: {[ key: string ]: any}; +``` + +- *Type:* {[ key: string ]: any} +- *Default:* no parameters + +This allows user to attach custom parameters to this alarm, which can later be accessed from the "useCreatedAlarms" method. + +--- + +##### `customTags`Optional + +```typescript +public readonly customTags: string[]; +``` + +- *Type:* string[] +- *Default:* no tags + +This allows user to attach custom values to this alarm, which can later be accessed from the "useCreatedAlarms" method. + +--- + +##### `datapointsToAlarm`Optional + +```typescript +public readonly datapointsToAlarm: number; +``` + +- *Type:* number +- *Default:* the same as monitoring facade default + +Number of breaches required to transition into an ALARM state. + +--- + +##### `dedupeStringOverride`Optional + +```typescript +public readonly dedupeStringOverride: string; +``` + +- *Type:* string +- *Default:* undefined (no override) + +If this is defined, the alarm dedupe string is set to this exact value. + +Please be aware that you need to handle deduping for different stages (Beta, Prod...) and realms (EU, NA...) manually. +Dedupe strings are global and not unique per CTI. + +--- + +##### `documentationLink`Optional + +```typescript +public readonly documentationLink: string; +``` + +- *Type:* string +- *Default:* no additional link will be added + +An optional link included in the generated ticket description body. + +--- + +##### `evaluateLowSampleCountPercentile`Optional + +```typescript +public readonly evaluateLowSampleCountPercentile: boolean; +``` + +- *Type:* boolean +- *Default:* true + +Used only for alarms based on percentiles. + +If you specify false, the alarm state does not change during periods with too few data points to be statistically significant. +If you specify true, the alarm is always evaluated and possibly changes state no matter how many data points are available. + +--- + +##### `evaluationPeriods`Optional + +```typescript +public readonly evaluationPeriods: number; +``` + +- *Type:* number +- *Default:* the same as monitoring facade default + +Number of periods to consider when checking the number of breaching datapoints. + +--- + +##### `fillAlarmRange`Optional + +```typescript +public readonly fillAlarmRange: boolean; +``` + +- *Type:* boolean +- *Default:* false + +Indicates whether the alarming range of values should be highlighted in the widget. + +--- + +##### `minMetricSamplesToAlarm`Optional + +```typescript +public readonly minMetricSamplesToAlarm: number; +``` + +- *Type:* number +- *Default:* default behaviour - no condition on sample count will be added to the alarm + +Specifies how many samples (N) of the metric is needed to trigger the alarm. + +If this property is specified, a composite alarm is created of the following: + +This composite alarm will be returned as a result and uses the specified alarm actions. + +--- + +##### `period`Optional + +```typescript +public readonly period: Duration; +``` + +- *Type:* aws-cdk-lib.Duration +- *Default:* the default specified in MetricFactory + +Period override for the metric to alarm on. + +--- + +##### `runbookLink`Optional + +```typescript +public readonly runbookLink: string; +``` + +- *Type:* string +- *Default:* no additional link will be added + +An optional link included in the generated ticket description body. + +--- + +##### `treatMissingDataOverride`Optional + +```typescript +public readonly treatMissingDataOverride: TreatMissingData; +``` + +- *Type:* aws-cdk-lib.aws_cloudwatch.TreatMissingData +- *Default:* alarm-specific default + +Behaviour in case the metric data is missing. + +--- + +##### `minSecretCount`Required + +```typescript +public readonly minSecretCount: number; +``` + +- *Type:* number + +--- + ### MinUsageCountThreshold #### Initializer @@ -33225,6 +34111,316 @@ Calls provided function to process all alarms created. --- +### SecretsManagerMonitoringOptions + +#### Initializer + +```typescript +import { SecretsManagerMonitoringOptions } from 'cdk-monitoring-constructs' + +const secretsManagerMonitoringOptions: SecretsManagerMonitoringOptions = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| alarmFriendlyName | string | Plain name, used in naming alarms. | +| humanReadableName | string | Human-readable name is a freeform string, used as a caption or description. | +| localAlarmNamePrefixOverride | string | If this is defined, the local alarm name prefix used in naming alarms for the construct will be set to this value. | +| addToAlarmDashboard | boolean | Flag indicating if the widgets should be added to alarm dashboard. | +| addToDetailDashboard | boolean | Flag indicating if the widgets should be added to detailed dashboard. | +| addToSummaryDashboard | boolean | Flag indicating if the widgets should be added to summary dashboard. | +| useCreatedAlarms | IAlarmConsumer | Calls provided function to process all alarms created. | +| addChangeInSecretsAlarm | {[ key: string ]: ChangeInSecretCountThreshold} | *No description.* | +| addMaxNumberSecretsAlarm | {[ key: string ]: MaxSecretCountThreshold} | *No description.* | +| addMinNumberSecretsAlarm | {[ key: string ]: MinSecretCountThreshold} | *No description.* | + +--- + +##### `alarmFriendlyName`Optional + +```typescript +public readonly alarmFriendlyName: string; +``` + +- *Type:* string +- *Default:* derives name from the construct itself + +Plain name, used in naming alarms. + +This unique among other resources, and respect the AWS CDK restriction posed on alarm names. +The length must be 1 - 255 characters and although the validation rules are undocumented, we recommend using ASCII and hyphens. + +--- + +##### `humanReadableName`Optional + +```typescript +public readonly humanReadableName: string; +``` + +- *Type:* string +- *Default:* use alarmFriendlyName + +Human-readable name is a freeform string, used as a caption or description. + +There are no limitations on what it can be. + +--- + +##### `localAlarmNamePrefixOverride`Optional + +```typescript +public readonly localAlarmNamePrefixOverride: string; +``` + +- *Type:* string + +If this is defined, the local alarm name prefix used in naming alarms for the construct will be set to this value. + +The length must be 1 - 255 characters and although the validation rules are undocumented, we recommend using ASCII and hyphens. + +> [AlarmNamingStrategy for more details on alarm name prefixes](AlarmNamingStrategy for more details on alarm name prefixes) + +--- + +##### `addToAlarmDashboard`Optional + +```typescript +public readonly addToAlarmDashboard: boolean; +``` + +- *Type:* boolean +- *Default:* true + +Flag indicating if the widgets should be added to alarm dashboard. + +--- + +##### `addToDetailDashboard`Optional + +```typescript +public readonly addToDetailDashboard: boolean; +``` + +- *Type:* boolean +- *Default:* true + +Flag indicating if the widgets should be added to detailed dashboard. + +--- + +##### `addToSummaryDashboard`Optional + +```typescript +public readonly addToSummaryDashboard: boolean; +``` + +- *Type:* boolean +- *Default:* true + +Flag indicating if the widgets should be added to summary dashboard. + +--- + +##### `useCreatedAlarms`Optional + +```typescript +public readonly useCreatedAlarms: IAlarmConsumer; +``` + +- *Type:* IAlarmConsumer + +Calls provided function to process all alarms created. + +--- + +##### `addChangeInSecretsAlarm`Optional + +```typescript +public readonly addChangeInSecretsAlarm: {[ key: string ]: ChangeInSecretCountThreshold}; +``` + +- *Type:* {[ key: string ]: ChangeInSecretCountThreshold} + +--- + +##### `addMaxNumberSecretsAlarm`Optional + +```typescript +public readonly addMaxNumberSecretsAlarm: {[ key: string ]: MaxSecretCountThreshold}; +``` + +- *Type:* {[ key: string ]: MaxSecretCountThreshold} + +--- + +##### `addMinNumberSecretsAlarm`Optional + +```typescript +public readonly addMinNumberSecretsAlarm: {[ key: string ]: MinSecretCountThreshold}; +``` + +- *Type:* {[ key: string ]: MinSecretCountThreshold} + +--- + +### SecretsManagerMonitoringProps + +#### Initializer + +```typescript +import { SecretsManagerMonitoringProps } from 'cdk-monitoring-constructs' + +const secretsManagerMonitoringProps: SecretsManagerMonitoringProps = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| alarmFriendlyName | string | Plain name, used in naming alarms. | +| humanReadableName | string | Human-readable name is a freeform string, used as a caption or description. | +| localAlarmNamePrefixOverride | string | If this is defined, the local alarm name prefix used in naming alarms for the construct will be set to this value. | +| addToAlarmDashboard | boolean | Flag indicating if the widgets should be added to alarm dashboard. | +| addToDetailDashboard | boolean | Flag indicating if the widgets should be added to detailed dashboard. | +| addToSummaryDashboard | boolean | Flag indicating if the widgets should be added to summary dashboard. | +| useCreatedAlarms | IAlarmConsumer | Calls provided function to process all alarms created. | +| addChangeInSecretsAlarm | {[ key: string ]: ChangeInSecretCountThreshold} | *No description.* | +| addMaxNumberSecretsAlarm | {[ key: string ]: MaxSecretCountThreshold} | *No description.* | +| addMinNumberSecretsAlarm | {[ key: string ]: MinSecretCountThreshold} | *No description.* | + +--- + +##### `alarmFriendlyName`Optional + +```typescript +public readonly alarmFriendlyName: string; +``` + +- *Type:* string +- *Default:* derives name from the construct itself + +Plain name, used in naming alarms. + +This unique among other resources, and respect the AWS CDK restriction posed on alarm names. +The length must be 1 - 255 characters and although the validation rules are undocumented, we recommend using ASCII and hyphens. + +--- + +##### `humanReadableName`Optional + +```typescript +public readonly humanReadableName: string; +``` + +- *Type:* string +- *Default:* use alarmFriendlyName + +Human-readable name is a freeform string, used as a caption or description. + +There are no limitations on what it can be. + +--- + +##### `localAlarmNamePrefixOverride`Optional + +```typescript +public readonly localAlarmNamePrefixOverride: string; +``` + +- *Type:* string + +If this is defined, the local alarm name prefix used in naming alarms for the construct will be set to this value. + +The length must be 1 - 255 characters and although the validation rules are undocumented, we recommend using ASCII and hyphens. + +> [AlarmNamingStrategy for more details on alarm name prefixes](AlarmNamingStrategy for more details on alarm name prefixes) + +--- + +##### `addToAlarmDashboard`Optional + +```typescript +public readonly addToAlarmDashboard: boolean; +``` + +- *Type:* boolean +- *Default:* true + +Flag indicating if the widgets should be added to alarm dashboard. + +--- + +##### `addToDetailDashboard`Optional + +```typescript +public readonly addToDetailDashboard: boolean; +``` + +- *Type:* boolean +- *Default:* true + +Flag indicating if the widgets should be added to detailed dashboard. + +--- + +##### `addToSummaryDashboard`Optional + +```typescript +public readonly addToSummaryDashboard: boolean; +``` + +- *Type:* boolean +- *Default:* true + +Flag indicating if the widgets should be added to summary dashboard. + +--- + +##### `useCreatedAlarms`Optional + +```typescript +public readonly useCreatedAlarms: IAlarmConsumer; +``` + +- *Type:* IAlarmConsumer + +Calls provided function to process all alarms created. + +--- + +##### `addChangeInSecretsAlarm`Optional + +```typescript +public readonly addChangeInSecretsAlarm: {[ key: string ]: ChangeInSecretCountThreshold}; +``` + +- *Type:* {[ key: string ]: ChangeInSecretCountThreshold} + +--- + +##### `addMaxNumberSecretsAlarm`Optional + +```typescript +public readonly addMaxNumberSecretsAlarm: {[ key: string ]: MaxSecretCountThreshold}; +``` + +- *Type:* {[ key: string ]: MaxSecretCountThreshold} + +--- + +##### `addMinNumberSecretsAlarm`Optional + +```typescript +public readonly addMinNumberSecretsAlarm: {[ key: string ]: MinSecretCountThreshold}; +``` + +- *Type:* {[ key: string ]: MinSecretCountThreshold} + +--- + ### SecretsManagerSecretMetricFactoryProps #### Initializer @@ -56353,6 +57549,374 @@ Returns widgets for the requested dashboard type. +### SecretsManagerAlarmFactory + +#### Initializers + +```typescript +import { SecretsManagerAlarmFactory } from 'cdk-monitoring-constructs' + +new SecretsManagerAlarmFactory(alarmFactory: AlarmFactory) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| alarmFactory | AlarmFactory | *No description.* | + +--- + +##### `alarmFactory`Required + +- *Type:* AlarmFactory + +--- + +#### Methods + +| **Name** | **Description** | +| --- | --- | +| addChangeInSecretCountAlarm | *No description.* | +| addMaxSecretCountAlarm | *No description.* | +| addMinSecretCountAlarm | *No description.* | + +--- + +##### `addChangeInSecretCountAlarm` + +```typescript +public addChangeInSecretCountAlarm(metric: Metric | MathExpression, props: ChangeInSecretCountThreshold, disambiguator?: string): AlarmWithAnnotation +``` + +###### `metric`Required + +- *Type:* aws-cdk-lib.aws_cloudwatch.Metric | aws-cdk-lib.aws_cloudwatch.MathExpression + +--- + +###### `props`Required + +- *Type:* ChangeInSecretCountThreshold + +--- + +###### `disambiguator`Optional + +- *Type:* string + +--- + +##### `addMaxSecretCountAlarm` + +```typescript +public addMaxSecretCountAlarm(metric: Metric | MathExpression, props: MaxSecretCountThreshold, disambiguator?: string): AlarmWithAnnotation +``` + +###### `metric`Required + +- *Type:* aws-cdk-lib.aws_cloudwatch.Metric | aws-cdk-lib.aws_cloudwatch.MathExpression + +--- + +###### `props`Required + +- *Type:* MaxSecretCountThreshold + +--- + +###### `disambiguator`Optional + +- *Type:* string + +--- + +##### `addMinSecretCountAlarm` + +```typescript +public addMinSecretCountAlarm(metric: Metric | MathExpression, props: MinSecretCountThreshold, disambiguator?: string): AlarmWithAnnotation +``` + +###### `metric`Required + +- *Type:* aws-cdk-lib.aws_cloudwatch.Metric | aws-cdk-lib.aws_cloudwatch.MathExpression + +--- + +###### `props`Required + +- *Type:* MinSecretCountThreshold + +--- + +###### `disambiguator`Optional + +- *Type:* string + +--- + + + + +### SecretsManagerMetricFactory + +#### Initializers + +```typescript +import { SecretsManagerMetricFactory } from 'cdk-monitoring-constructs' + +new SecretsManagerMetricFactory(metricFactory: MetricFactory) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| metricFactory | MetricFactory | *No description.* | + +--- + +##### `metricFactory`Required + +- *Type:* MetricFactory + +--- + +#### Methods + +| **Name** | **Description** | +| --- | --- | +| metricSecretCount | *No description.* | + +--- + +##### `metricSecretCount` + +```typescript +public metricSecretCount(): Metric | MathExpression +``` + + + + +### SecretsManagerMonitoring + +#### Initializers + +```typescript +import { SecretsManagerMonitoring } from 'cdk-monitoring-constructs' + +new SecretsManagerMonitoring(scope: MonitoringScope, props: SecretsManagerMonitoringProps) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| scope | MonitoringScope | *No description.* | +| props | SecretsManagerMonitoringProps | *No description.* | + +--- + +##### `scope`Required + +- *Type:* MonitoringScope + +--- + +##### `props`Required + +- *Type:* SecretsManagerMonitoringProps + +--- + +#### Methods + +| **Name** | **Description** | +| --- | --- | +| addAlarm | Adds an alarm. | +| alarmWidgets | Returns widgets for all alarms. | +| createAlarmFactory | Creates a new alarm factory. | +| createdAlarms | Returns all the alarms created. | +| createMetricFactory | Creates a new metric factory. | +| createWidgetFactory | Creates a new widget factory. | +| summaryWidgets | Returns widgets to be placed on the summary dashboard. | +| widgets | Returns widgets to be placed on the main dashboard. | +| widgetsForDashboard | Returns widgets for the requested dashboard type. | +| createSecretsCountWidget | *No description.* | +| createTitleWidget | *No description.* | + +--- + +##### `addAlarm` + +```typescript +public addAlarm(alarm: AlarmWithAnnotation): void +``` + +Adds an alarm. + +###### `alarm`Required + +- *Type:* AlarmWithAnnotation + +alarm to add. + +--- + +##### `alarmWidgets` + +```typescript +public alarmWidgets(): IWidget[] +``` + +Returns widgets for all alarms. + +These can go to runbook or to service dashboard. + +##### `createAlarmFactory` + +```typescript +public createAlarmFactory(alarmNamePrefix: string): AlarmFactory +``` + +Creates a new alarm factory. + +Alarms created will be named with the given prefix, unless a local name override is present. + +###### `alarmNamePrefix`Required + +- *Type:* string + +alarm name prefix. + +--- + +##### `createdAlarms` + +```typescript +public createdAlarms(): AlarmWithAnnotation[] +``` + +Returns all the alarms created. + +##### `createMetricFactory` + +```typescript +public createMetricFactory(): MetricFactory +``` + +Creates a new metric factory. + +##### `createWidgetFactory` + +```typescript +public createWidgetFactory(): IWidgetFactory +``` + +Creates a new widget factory. + +##### `summaryWidgets` + +```typescript +public summaryWidgets(): IWidget[] +``` + +Returns widgets to be placed on the summary dashboard. + +##### `widgets` + +```typescript +public widgets(): IWidget[] +``` + +Returns widgets to be placed on the main dashboard. + +##### `widgetsForDashboard` + +```typescript +public widgetsForDashboard(name: string): IWidget[] +``` + +Returns widgets for the requested dashboard type. + +###### `name`Required + +- *Type:* string + +--- + +##### `createSecretsCountWidget` + +```typescript +public createSecretsCountWidget(width: number, height: number): GraphWidget +``` + +###### `width`Required + +- *Type:* number + +--- + +###### `height`Required + +- *Type:* number + +--- + +##### `createTitleWidget` + +```typescript +public createTitleWidget(): MonitoringHeaderWidget +``` + + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| secretsCountAnnotation | aws-cdk-lib.aws_cloudwatch.HorizontalAnnotation[] | *No description.* | +| secretsCountMetric | aws-cdk-lib.aws_cloudwatch.Metric \| aws-cdk-lib.aws_cloudwatch.MathExpression | *No description.* | +| secretsManagerAlarmFactory | SecretsManagerAlarmFactory | *No description.* | +| title | string | *No description.* | + +--- + +##### `secretsCountAnnotation`Required + +```typescript +public readonly secretsCountAnnotation: HorizontalAnnotation[]; +``` + +- *Type:* aws-cdk-lib.aws_cloudwatch.HorizontalAnnotation[] + +--- + +##### `secretsCountMetric`Required + +```typescript +public readonly secretsCountMetric: Metric | MathExpression; +``` + +- *Type:* aws-cdk-lib.aws_cloudwatch.Metric | aws-cdk-lib.aws_cloudwatch.MathExpression + +--- + +##### `secretsManagerAlarmFactory`Required + +```typescript +public readonly secretsManagerAlarmFactory: SecretsManagerAlarmFactory; +``` + +- *Type:* SecretsManagerAlarmFactory + +--- + +##### `title`Required + +```typescript +public readonly title: string; +``` + +- *Type:* string + +--- + + ### SecretsManagerSecretMetricFactory #### Initializers @@ -62087,7 +63651,7 @@ Dashboard placement override props. ### IDashboardSegment -- *Implemented By:* ApiGatewayMonitoring, ApiGatewayV2HttpApiMonitoring, AppSyncMonitoring, AutoScalingGroupMonitoring, BillingMonitoring, CertificateManagerMonitoring, CloudFrontDistributionMonitoring, CodeBuildProjectMonitoring, CustomMonitoring, DocumentDbMonitoring, DynamoTableGlobalSecondaryIndexMonitoring, DynamoTableMonitoring, EC2Monitoring, Ec2ServiceMonitoring, ElastiCacheClusterMonitoring, FargateServiceMonitoring, GlueJobMonitoring, KinesisDataAnalyticsMonitoring, KinesisDataStreamMonitoring, KinesisFirehoseMonitoring, LambdaFunctionMonitoring, LogMonitoring, Monitoring, NetworkLoadBalancerMonitoring, OpenSearchClusterMonitoring, RdsClusterMonitoring, RedshiftClusterMonitoring, S3BucketMonitoring, SecretsManagerSecretMonitoring, SingleWidgetDashboardSegment, SnsTopicMonitoring, SqsQueueMonitoring, SqsQueueMonitoringWithDlq, StepFunctionActivityMonitoring, StepFunctionLambdaIntegrationMonitoring, StepFunctionMonitoring, StepFunctionServiceIntegrationMonitoring, SyntheticsCanaryMonitoring, WafV2Monitoring, IDashboardSegment +- *Implemented By:* ApiGatewayMonitoring, ApiGatewayV2HttpApiMonitoring, AppSyncMonitoring, AutoScalingGroupMonitoring, BillingMonitoring, CertificateManagerMonitoring, CloudFrontDistributionMonitoring, CodeBuildProjectMonitoring, CustomMonitoring, DocumentDbMonitoring, DynamoTableGlobalSecondaryIndexMonitoring, DynamoTableMonitoring, EC2Monitoring, Ec2ServiceMonitoring, ElastiCacheClusterMonitoring, FargateServiceMonitoring, GlueJobMonitoring, KinesisDataAnalyticsMonitoring, KinesisDataStreamMonitoring, KinesisFirehoseMonitoring, LambdaFunctionMonitoring, LogMonitoring, Monitoring, NetworkLoadBalancerMonitoring, OpenSearchClusterMonitoring, RdsClusterMonitoring, RedshiftClusterMonitoring, S3BucketMonitoring, SecretsManagerMonitoring, SecretsManagerSecretMonitoring, SingleWidgetDashboardSegment, SnsTopicMonitoring, SqsQueueMonitoring, SqsQueueMonitoringWithDlq, StepFunctionActivityMonitoring, StepFunctionLambdaIntegrationMonitoring, StepFunctionMonitoring, StepFunctionServiceIntegrationMonitoring, SyntheticsCanaryMonitoring, WafV2Monitoring, IDashboardSegment #### Methods @@ -62178,7 +63742,7 @@ Gets the dashboard for the requested dashboard type. ### IDynamicDashboardSegment -- *Implemented By:* ApiGatewayMonitoring, ApiGatewayV2HttpApiMonitoring, AppSyncMonitoring, AutoScalingGroupMonitoring, BillingMonitoring, CertificateManagerMonitoring, CloudFrontDistributionMonitoring, CodeBuildProjectMonitoring, CustomMonitoring, DocumentDbMonitoring, DynamoTableGlobalSecondaryIndexMonitoring, DynamoTableMonitoring, EC2Monitoring, Ec2ServiceMonitoring, ElastiCacheClusterMonitoring, FargateServiceMonitoring, GlueJobMonitoring, KinesisDataAnalyticsMonitoring, KinesisDataStreamMonitoring, KinesisFirehoseMonitoring, LambdaFunctionMonitoring, LogMonitoring, Monitoring, NetworkLoadBalancerMonitoring, OpenSearchClusterMonitoring, RdsClusterMonitoring, RedshiftClusterMonitoring, S3BucketMonitoring, SecretsManagerSecretMonitoring, SingleWidgetDashboardSegment, SnsTopicMonitoring, SqsQueueMonitoring, SqsQueueMonitoringWithDlq, StaticSegmentDynamicAdapter, StepFunctionActivityMonitoring, StepFunctionLambdaIntegrationMonitoring, StepFunctionMonitoring, StepFunctionServiceIntegrationMonitoring, SyntheticsCanaryMonitoring, WafV2Monitoring, IDynamicDashboardSegment +- *Implemented By:* ApiGatewayMonitoring, ApiGatewayV2HttpApiMonitoring, AppSyncMonitoring, AutoScalingGroupMonitoring, BillingMonitoring, CertificateManagerMonitoring, CloudFrontDistributionMonitoring, CodeBuildProjectMonitoring, CustomMonitoring, DocumentDbMonitoring, DynamoTableGlobalSecondaryIndexMonitoring, DynamoTableMonitoring, EC2Monitoring, Ec2ServiceMonitoring, ElastiCacheClusterMonitoring, FargateServiceMonitoring, GlueJobMonitoring, KinesisDataAnalyticsMonitoring, KinesisDataStreamMonitoring, KinesisFirehoseMonitoring, LambdaFunctionMonitoring, LogMonitoring, Monitoring, NetworkLoadBalancerMonitoring, OpenSearchClusterMonitoring, RdsClusterMonitoring, RedshiftClusterMonitoring, S3BucketMonitoring, SecretsManagerMonitoring, SecretsManagerSecretMonitoring, SingleWidgetDashboardSegment, SnsTopicMonitoring, SqsQueueMonitoring, SqsQueueMonitoringWithDlq, StaticSegmentDynamicAdapter, StepFunctionActivityMonitoring, StepFunctionLambdaIntegrationMonitoring, StepFunctionMonitoring, StepFunctionServiceIntegrationMonitoring, SyntheticsCanaryMonitoring, WafV2Monitoring, IDynamicDashboardSegment #### Methods diff --git a/README.md b/README.md index f3d5f4a2..dde7d0f9 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,8 @@ You can browse the documentation at https://constructs.dev/packages/cdk-monitori | AWS RDS (`.monitorRdsCluster()`) | Query duration, connections, latency, disk/CPU usage | Connections, disk and CPU usage | | | AWS Redshift (`.monitorRedshiftCluster()`) | Query duration, connections, latency, disk/CPU usage | Query duration, connections, disk and CPU usage | | | AWS S3 Bucket (`.monitorS3Bucket()`) | Bucket size and number of objects | | | -| AWS SecretsManager (`.monitorSecretsManagerSecret()`) | Days since last rotation | Days since last change or rotation | | +| AWS SecretsManager (`.monitorSecretsManager()`) | Max secret count, min secret sount, secret count change | Min/max secret count or change in secret count | | +| AWS SecretsManager Secret (`.monitorSecretsManagerSecret()`) | Days since last rotation | Days since last change or rotation | | | AWS SNS Topic (`.monitorSnsTopic()`) | Message count, size, failed notifications | Failed notifications, min/max published messages | | | AWS SQS Queue (`.monitorSqsQueue()`, `.monitorSqsQueueWithDlq()`) | Message count, age, size | Message count, age, DLQ incoming messages | | | AWS Step Functions (`.monitorStepFunction()`, `.monitorStepFunctionActivity()`, `monitorStepFunctionLambdaIntegration()`, `.monitorStepFunctionServiceIntegration()`) | Execution count and breakdown per state | Duration, failed, failed rate, aborted, throttled, timed out executions | | diff --git a/lib/common/monitoring/alarms/SecretsManagerAlarmFactory.ts b/lib/common/monitoring/alarms/SecretsManagerAlarmFactory.ts new file mode 100644 index 00000000..87990591 --- /dev/null +++ b/lib/common/monitoring/alarms/SecretsManagerAlarmFactory.ts @@ -0,0 +1,118 @@ +import { + ComparisonOperator, + TreatMissingData, +} from "aws-cdk-lib/aws-cloudwatch"; + +import { AlarmFactory, CustomAlarmThreshold } from "../../alarm"; +import { MetricWithAlarmSupport } from "../../metric"; + +const NUMBER_OF_DATAPOINTS = 1; + +export interface MinSecretCountThreshold extends CustomAlarmThreshold { + readonly minSecretCount: number; +} + +export interface MaxSecretCountThreshold extends CustomAlarmThreshold { + readonly maxSecretCount: number; +} + +export interface ChangeInSecretCountThreshold extends CustomAlarmThreshold { + readonly requiredSecretCount: number; + readonly alarmWhenIncreased: boolean; + readonly alarmWhenDecreased: boolean; + readonly additionalDescription?: string; +} + +export class SecretsManagerAlarmFactory { + protected readonly alarmFactory: AlarmFactory; + + constructor(alarmFactory: AlarmFactory) { + this.alarmFactory = alarmFactory; + } + + addMinSecretCountAlarm( + metric: MetricWithAlarmSupport, + props: MinSecretCountThreshold, + disambiguator?: string + ) { + return this.alarmFactory.addAlarm(metric, { + treatMissingData: + props.treatMissingDataOverride ?? TreatMissingData.MISSING, + datapointsToAlarm: props.datapointsToAlarm ?? NUMBER_OF_DATAPOINTS, + comparisonOperator: + props.comparisonOperatorOverride ?? + ComparisonOperator.LESS_THAN_THRESHOLD, + ...props, + disambiguator, + threshold: props.minSecretCount, + alarmNameSuffix: "Secrets-Count-Min", + alarmDescription: "Number of secrets is too low.", + }); + } + + addMaxSecretCountAlarm( + metric: MetricWithAlarmSupport, + props: MaxSecretCountThreshold, + disambiguator?: string + ) { + return this.alarmFactory.addAlarm(metric, { + treatMissingData: + props.treatMissingDataOverride ?? TreatMissingData.MISSING, + comparisonOperator: + props.comparisonOperatorOverride ?? + ComparisonOperator.GREATER_THAN_THRESHOLD, + datapointsToAlarm: props.datapointsToAlarm ?? NUMBER_OF_DATAPOINTS, + ...props, + disambiguator, + threshold: props.maxSecretCount, + alarmNameSuffix: "Secrets-Count-Max", + alarmDescription: "Number of secrets is too high.", + }); + } + + addChangeInSecretCountAlarm( + metric: MetricWithAlarmSupport, + props: ChangeInSecretCountThreshold, + disambiguator?: string + ) { + return this.alarmFactory.addAlarm(metric, { + ...props, + disambiguator, + treatMissingData: + props.treatMissingDataOverride ?? TreatMissingData.MISSING, + threshold: props.requiredSecretCount, + comparisonOperator: this.getComparisonOperator(props), + datapointsToAlarm: props.datapointsToAlarm ?? NUMBER_OF_DATAPOINTS, + alarmNameSuffix: "Secrets-Count-Change", + alarmDescription: this.getDefaultDescription(props), + }); + } + + private getDefaultDescription(props: ChangeInSecretCountThreshold) { + if (props.alarmWhenIncreased && props.alarmWhenDecreased) { + return "Secret count: Secret count has changed."; + } else if (props.alarmWhenIncreased) { + return "Secret count: Secret count has increased."; + } else if (props.alarmWhenDecreased) { + return "Secret count: Secret count has decreased."; + } else { + throw new Error( + "You need to alarm when the value has increased, decreased, or both." + ); + } + } + + private getComparisonOperator(props: ChangeInSecretCountThreshold) { + if (props.alarmWhenIncreased && props.alarmWhenDecreased) { + return ComparisonOperator.LESS_THAN_LOWER_OR_GREATER_THAN_UPPER_THRESHOLD; + } else if (props.alarmWhenDecreased) { + return ComparisonOperator.LESS_THAN_THRESHOLD; + } else if (props.alarmWhenIncreased) { + return ComparisonOperator.GREATER_THAN_THRESHOLD; + } else { + throw new Error( + "You need to alarm when the value has increased, decreased, or both." + ); + } + } +} diff --git a/lib/common/monitoring/alarms/index.ts b/lib/common/monitoring/alarms/index.ts index 2759785f..fb7cc70f 100644 --- a/lib/common/monitoring/alarms/index.ts +++ b/lib/common/monitoring/alarms/index.ts @@ -11,6 +11,7 @@ export * from "./LatencyAlarmFactory"; export * from "./LogLevelAlarmFactory"; export * from "./OpenSearchClusterAlarmFactory"; export * from "./QueueAlarmFactory"; +export * from "./SecretsManagerAlarmFactory"; export * from "./TaskHealthAlarmFactory"; export * from "./ThroughputAlarmFactory"; export * from "./TopicAlarmFactory"; diff --git a/lib/facade/MonitoringFacade.ts b/lib/facade/MonitoringFacade.ts index 98e44a6f..d49b4946 100644 --- a/lib/facade/MonitoringFacade.ts +++ b/lib/facade/MonitoringFacade.ts @@ -94,6 +94,8 @@ import { RedshiftClusterMonitoringProps, S3BucketMonitoring, S3BucketMonitoringProps, + SecretsManagerMonitoring, + SecretsManagerMonitoringProps, SecretsManagerSecretMonitoring, SecretsManagerSecretMonitoringProps, SimpleEc2ServiceMonitoringProps, @@ -637,6 +639,12 @@ export class MonitoringFacade extends MonitoringScope { return this; } + monitorSecretsManager(props: SecretsManagerMonitoringProps) { + const segment = new SecretsManagerMonitoring(this, props); + this.addSegment(segment, props); + return this; + } + monitorSecretsManagerSecret(props: SecretsManagerSecretMonitoringProps) { const segment = new SecretsManagerSecretMonitoring(this, props); this.addSegment(segment, props); diff --git a/lib/monitoring/aws-secretsmanager/SecretsManagerMetricFactory.ts b/lib/monitoring/aws-secretsmanager/SecretsManagerMetricFactory.ts new file mode 100644 index 00000000..c2dbc73b --- /dev/null +++ b/lib/monitoring/aws-secretsmanager/SecretsManagerMetricFactory.ts @@ -0,0 +1,37 @@ +import { Duration } from "aws-cdk-lib"; +import { MetricFactory, MetricStatistic } from "../../common"; + +const CLASS = "None"; +const DEFAULT_METRIC_PERIOD = Duration.hours(1); +const METRICNAMESECRETCOUNT = "ResourceCount"; +const NAMESPACE = "AWS/SecretsManager"; +const RESOURCE = "SecretCount"; +const SERVICE = "Secrets Manager"; +const TYPE = "Resource"; + +export class SecretsManagerMetricFactory { + protected readonly metricFactory: MetricFactory; + + constructor(metricFactory: MetricFactory) { + this.metricFactory = metricFactory; + } + + metricSecretCount() { + const dimensionsMap = { + Class: CLASS, + Resource: RESOURCE, + Service: SERVICE, + Type: TYPE, + }; + + return this.metricFactory.createMetric( + METRICNAMESECRETCOUNT, + MetricStatistic.AVERAGE, + "Count", + dimensionsMap, + undefined, + NAMESPACE, + DEFAULT_METRIC_PERIOD + ); + } +} diff --git a/lib/monitoring/aws-secretsmanager/SecretsManagerMonitoring.ts b/lib/monitoring/aws-secretsmanager/SecretsManagerMonitoring.ts new file mode 100644 index 00000000..1801b655 --- /dev/null +++ b/lib/monitoring/aws-secretsmanager/SecretsManagerMonitoring.ts @@ -0,0 +1,139 @@ +import { + GraphWidget, + HorizontalAnnotation, + IWidget, +} from "aws-cdk-lib/aws-cloudwatch"; +import { SecretsManagerMetricFactory } from "./SecretsManagerMetricFactory"; +import { + BaseMonitoringProps, + ChangeInSecretCountThreshold, + CountAxisFromZero, + DefaultGraphWidgetHeight, + DefaultSummaryWidgetHeight, + HalfWidth, + MaxSecretCountThreshold, + MetricWithAlarmSupport, + MinSecretCountThreshold, + Monitoring, + MonitoringScope, + SecretsManagerAlarmFactory, + ThirdWidth, +} from "../../common"; +import { + MonitoringHeaderWidget, + MonitoringNamingStrategy, +} from "../../dashboard"; + +export interface SecretsManagerMonitoringOptions extends BaseMonitoringProps { + readonly addMinNumberSecretsAlarm?: Record; + readonly addMaxNumberSecretsAlarm?: Record; + readonly addChangeInSecretsAlarm?: Record< + string, + ChangeInSecretCountThreshold + >; +} + +export interface SecretsManagerMonitoringProps + extends SecretsManagerMonitoringOptions {} + +export class SecretsManagerMonitoring extends Monitoring { + readonly title: string; + + readonly secretsManagerAlarmFactory: SecretsManagerAlarmFactory; + readonly secretsCountAnnotation: HorizontalAnnotation[]; + + readonly secretsCountMetric: MetricWithAlarmSupport; + + constructor(scope: MonitoringScope, props: SecretsManagerMonitoringProps) { + super(scope); + + const namingStrategy = new MonitoringNamingStrategy({ + ...props, + fallbackConstructName: "SecretsManager", + }); + + this.title = namingStrategy.resolveHumanReadableName(); + + const alarmFactory = this.createAlarmFactory( + namingStrategy.resolveAlarmFriendlyName() + ); + this.secretsManagerAlarmFactory = new SecretsManagerAlarmFactory( + alarmFactory + ); + this.secretsCountAnnotation = []; + + const metricFactory = new SecretsManagerMetricFactory( + scope.createMetricFactory() + ); + this.secretsCountMetric = metricFactory.metricSecretCount(); + + for (const disambiguator in props.addMaxNumberSecretsAlarm) { + const alarmProps = props.addMaxNumberSecretsAlarm[disambiguator]; + const createdAlarm = + this.secretsManagerAlarmFactory.addMaxSecretCountAlarm( + this.secretsCountMetric, + alarmProps, + disambiguator + ); + this.secretsCountAnnotation.push(createdAlarm.annotation); + this.addAlarm(createdAlarm); + } + + for (const disambiguator in props.addMinNumberSecretsAlarm) { + const alarmProps = props.addMinNumberSecretsAlarm[disambiguator]; + const createdAlarm = + this.secretsManagerAlarmFactory.addMinSecretCountAlarm( + this.secretsCountMetric, + alarmProps, + disambiguator + ); + this.secretsCountAnnotation.push(createdAlarm.annotation); + this.addAlarm(createdAlarm); + } + + for (const disambiguator in props.addChangeInSecretsAlarm) { + const alarmProps = props.addChangeInSecretsAlarm[disambiguator]; + const createdAlarm = + this.secretsManagerAlarmFactory.addChangeInSecretCountAlarm( + this.secretsCountMetric, + alarmProps, + disambiguator + ); + this.secretsCountAnnotation.push(createdAlarm.annotation); + this.addAlarm(createdAlarm); + } + props.useCreatedAlarms?.consume(this.createdAlarms()); + } + + summaryWidgets(): IWidget[] { + return [ + this.createTitleWidget(), + this.createSecretsCountWidget(HalfWidth, DefaultSummaryWidgetHeight), + ]; + } + + widgets(): IWidget[] { + return [ + this.createTitleWidget(), + this.createSecretsCountWidget(ThirdWidth, DefaultGraphWidgetHeight), + ]; + } + + createTitleWidget() { + return new MonitoringHeaderWidget({ + family: "Secrets Manager Secrets", + title: this.title, + }); + } + + createSecretsCountWidget(width: number, height: number) { + return new GraphWidget({ + width, + height, + title: "Secret Count", + left: [this.secretsCountMetric], + leftYAxis: CountAxisFromZero, + leftAnnotations: this.secretsCountAnnotation, + }); + } +} diff --git a/lib/monitoring/aws-secretsmanager/index.ts b/lib/monitoring/aws-secretsmanager/index.ts index b630e2b2..78ade206 100644 --- a/lib/monitoring/aws-secretsmanager/index.ts +++ b/lib/monitoring/aws-secretsmanager/index.ts @@ -1,3 +1,5 @@ +export * from "./SecretsManagerMetricFactory"; export * from "./SecretsManagerMetricsPublisher"; export * from "./SecretsManagerSecretMetricFactory"; +export * from "./SecretsManagerMonitoring"; export * from "./SecretsManagerSecretMonitoring"; diff --git a/test/monitoring/aws-secretsmanager/SecretsManagerMonitoring.test.ts b/test/monitoring/aws-secretsmanager/SecretsManagerMonitoring.test.ts new file mode 100644 index 00000000..83d83f9d --- /dev/null +++ b/test/monitoring/aws-secretsmanager/SecretsManagerMonitoring.test.ts @@ -0,0 +1,57 @@ +import { Stack } from "aws-cdk-lib"; +import { Template } from "aws-cdk-lib/assertions"; + +import { AlarmWithAnnotation, SecretsManagerMonitoring } from "../../../lib"; +import { addMonitoringDashboardsToStack } from "../../utils/SnapshotUtil"; +import { TestMonitoringScope } from "../TestMonitoringScope"; + +test("snapshot test", () => { + const stack = new Stack(); + + const scope = new TestMonitoringScope(stack, "Scope"); + + const monitoring = new SecretsManagerMonitoring(scope, { + humanReadableName: "SecretsManager", + }); + + addMonitoringDashboardsToStack(stack, monitoring); + expect(Template.fromStack(stack)).toMatchSnapshot(); +}); + +test("snapshot test: all alarms", () => { + const stack = new Stack(); + + const scope = new TestMonitoringScope(stack, "Scope"); + + let numAlarmsCreated = 0; + + const monitoring = new SecretsManagerMonitoring(scope, { + humanReadableName: "SecretsManager", + addChangeInSecretsAlarm: { + Warning: { + alarmWhenDecreased: false, + alarmWhenIncreased: true, + requiredSecretCount: 4, + }, + }, + addMinNumberSecretsAlarm: { + Warning: { + minSecretCount: 3, + }, + }, + addMaxNumberSecretsAlarm: { + Warning: { + maxSecretCount: 5, + }, + }, + useCreatedAlarms: { + consume(alarms: AlarmWithAnnotation[]) { + numAlarmsCreated = alarms.length; + }, + }, + }); + + addMonitoringDashboardsToStack(stack, monitoring); + expect(numAlarmsCreated).toStrictEqual(3); + expect(Template.fromStack(stack)).toMatchSnapshot(); +}); diff --git a/test/monitoring/aws-secretsmanager/__snapshots__/SecretsManagerMonitoring.test.ts.snap b/test/monitoring/aws-secretsmanager/__snapshots__/SecretsManagerMonitoring.test.ts.snap new file mode 100644 index 00000000..151d3f03 --- /dev/null +++ b/test/monitoring/aws-secretsmanager/__snapshots__/SecretsManagerMonitoring.test.ts.snap @@ -0,0 +1,341 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`snapshot test 1`] = ` +Object { + "Parameters": Object { + "BootstrapVersion": Object { + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]", + "Type": "AWS::SSM::Parameter::Value", + }, + }, + "Resources": Object { + "Alarm7103F465": Object { + "Properties": Object { + "DashboardBody": "{\\"widgets\\":[]}", + }, + "Type": "AWS::CloudWatch::Dashboard", + }, + "Resource": Object { + "Properties": Object { + "DashboardBody": Object { + "Fn::Join": Array [ + "", + Array [ + "{\\"widgets\\":[{\\"type\\":\\"text\\",\\"width\\":24,\\"height\\":1,\\"x\\":0,\\"y\\":0,\\"properties\\":{\\"markdown\\":\\"### Secrets Manager Secrets **SecretsManager**\\"}},{\\"type\\":\\"metric\\",\\"width\\":8,\\"height\\":5,\\"x\\":0,\\"y\\":1,\\"properties\\":{\\"view\\":\\"timeSeries\\",\\"title\\":\\"Secret Count\\",\\"region\\":\\"", + Object { + "Ref": "AWS::Region", + }, + "\\",\\"metrics\\":[[\\"AWS/SecretsManager\\",\\"ResourceCount\\",\\"Class\\",\\"None\\",\\"Resource\\",\\"SecretCount\\",\\"Service\\",\\"Secrets Manager\\",\\"Type\\",\\"Resource\\",{\\"label\\":\\"Count\\",\\"period\\":3600}]],\\"yAxis\\":{\\"left\\":{\\"min\\":0,\\"label\\":\\"Count\\",\\"showUnits\\":false}}}}]}", + ], + ], + }, + }, + "Type": "AWS::CloudWatch::Dashboard", + }, + "Summary68521F81": Object { + "Properties": Object { + "DashboardBody": Object { + "Fn::Join": Array [ + "", + Array [ + "{\\"widgets\\":[{\\"type\\":\\"text\\",\\"width\\":24,\\"height\\":1,\\"x\\":0,\\"y\\":0,\\"properties\\":{\\"markdown\\":\\"### Secrets Manager Secrets **SecretsManager**\\"}},{\\"type\\":\\"metric\\",\\"width\\":12,\\"height\\":6,\\"x\\":0,\\"y\\":1,\\"properties\\":{\\"view\\":\\"timeSeries\\",\\"title\\":\\"Secret Count\\",\\"region\\":\\"", + Object { + "Ref": "AWS::Region", + }, + "\\",\\"metrics\\":[[\\"AWS/SecretsManager\\",\\"ResourceCount\\",\\"Class\\",\\"None\\",\\"Resource\\",\\"SecretCount\\",\\"Service\\",\\"Secrets Manager\\",\\"Type\\",\\"Resource\\",{\\"label\\":\\"Count\\",\\"period\\":3600}]],\\"yAxis\\":{\\"left\\":{\\"min\\":0,\\"label\\":\\"Count\\",\\"showUnits\\":false}}}}]}", + ], + ], + }, + }, + "Type": "AWS::CloudWatch::Dashboard", + }, + }, + "Rules": Object { + "CheckBootstrapVersion": Object { + "Assertions": Array [ + Object { + "Assert": Object { + "Fn::Not": Array [ + Object { + "Fn::Contains": Array [ + Array [ + "1", + "2", + "3", + "4", + "5", + ], + Object { + "Ref": "BootstrapVersion", + }, + ], + }, + ], + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.", + }, + ], + }, + }, +} +`; + +exports[`snapshot test: all alarms 1`] = ` +Object { + "Parameters": Object { + "BootstrapVersion": Object { + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]", + "Type": "AWS::SSM::Parameter::Value", + }, + }, + "Resources": Object { + "Alarm7103F465": Object { + "Properties": Object { + "DashboardBody": Object { + "Fn::Join": Array [ + "", + Array [ + "{\\"widgets\\":[{\\"type\\":\\"metric\\",\\"width\\":6,\\"height\\":4,\\"x\\":0,\\"y\\":0,\\"properties\\":{\\"view\\":\\"timeSeries\\",\\"region\\":\\"", + Object { + "Ref": "AWS::Region", + }, + "\\",\\"annotations\\":{\\"alarms\\":[\\"", + Object { + "Fn::GetAtt": Array [ + "ScopeTestSecretsManagerSecretsCountMaxWarning9EE14055", + "Arn", + ], + }, + "\\"]},\\"yAxis\\":{}}},{\\"type\\":\\"metric\\",\\"width\\":6,\\"height\\":4,\\"x\\":6,\\"y\\":0,\\"properties\\":{\\"view\\":\\"timeSeries\\",\\"region\\":\\"", + Object { + "Ref": "AWS::Region", + }, + "\\",\\"annotations\\":{\\"alarms\\":[\\"", + Object { + "Fn::GetAtt": Array [ + "ScopeTestSecretsManagerSecretsCountMinWarning40C64CA4", + "Arn", + ], + }, + "\\"]},\\"yAxis\\":{}}},{\\"type\\":\\"metric\\",\\"width\\":6,\\"height\\":4,\\"x\\":12,\\"y\\":0,\\"properties\\":{\\"view\\":\\"timeSeries\\",\\"region\\":\\"", + Object { + "Ref": "AWS::Region", + }, + "\\",\\"annotations\\":{\\"alarms\\":[\\"", + Object { + "Fn::GetAtt": Array [ + "ScopeTestSecretsManagerSecretsCountChangeWarning78F355CB", + "Arn", + ], + }, + "\\"]},\\"yAxis\\":{}}}]}", + ], + ], + }, + }, + "Type": "AWS::CloudWatch::Dashboard", + }, + "Resource": Object { + "Properties": Object { + "DashboardBody": Object { + "Fn::Join": Array [ + "", + Array [ + "{\\"widgets\\":[{\\"type\\":\\"text\\",\\"width\\":24,\\"height\\":1,\\"x\\":0,\\"y\\":0,\\"properties\\":{\\"markdown\\":\\"### Secrets Manager Secrets **SecretsManager**\\"}},{\\"type\\":\\"metric\\",\\"width\\":8,\\"height\\":5,\\"x\\":0,\\"y\\":1,\\"properties\\":{\\"view\\":\\"timeSeries\\",\\"title\\":\\"Secret Count\\",\\"region\\":\\"", + Object { + "Ref": "AWS::Region", + }, + "\\",\\"metrics\\":[[\\"AWS/SecretsManager\\",\\"ResourceCount\\",\\"Class\\",\\"None\\",\\"Resource\\",\\"SecretCount\\",\\"Service\\",\\"Secrets Manager\\",\\"Type\\",\\"Resource\\",{\\"label\\":\\"Count\\",\\"period\\":3600}]],\\"annotations\\":{\\"horizontal\\":[{\\"label\\":\\"Count > 5 for 1 datapoints within 60 minutes\\",\\"value\\":5,\\"yAxis\\":\\"left\\"},{\\"label\\":\\"Count < 3 for 1 datapoints within 60 minutes\\",\\"value\\":3,\\"yAxis\\":\\"left\\"},{\\"label\\":\\"Count > 4 for 1 datapoints within 60 minutes\\",\\"value\\":4,\\"yAxis\\":\\"left\\"}]},\\"yAxis\\":{\\"left\\":{\\"min\\":0,\\"label\\":\\"Count\\",\\"showUnits\\":false}}}}]}", + ], + ], + }, + }, + "Type": "AWS::CloudWatch::Dashboard", + }, + "ScopeTestSecretsManagerSecretsCountChangeWarning78F355CB": Object { + "Properties": Object { + "ActionsEnabled": true, + "AlarmDescription": "Secret count: Secret count has increased.", + "AlarmName": "Test-SecretsManager-Secrets-Count-Change-Warning", + "ComparisonOperator": "GreaterThanThreshold", + "DatapointsToAlarm": 1, + "EvaluationPeriods": 1, + "Metrics": Array [ + Object { + "Id": "m1", + "Label": "Count", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "Class", + "Value": "None", + }, + Object { + "Name": "Resource", + "Value": "SecretCount", + }, + Object { + "Name": "Service", + "Value": "Secrets Manager", + }, + Object { + "Name": "Type", + "Value": "Resource", + }, + ], + "MetricName": "ResourceCount", + "Namespace": "AWS/SecretsManager", + }, + "Period": 3600, + "Stat": "Average", + }, + "ReturnData": true, + }, + ], + "Threshold": 4, + "TreatMissingData": "missing", + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "ScopeTestSecretsManagerSecretsCountMaxWarning9EE14055": Object { + "Properties": Object { + "ActionsEnabled": true, + "AlarmDescription": "Number of secrets is too high.", + "AlarmName": "Test-SecretsManager-Secrets-Count-Max-Warning", + "ComparisonOperator": "GreaterThanThreshold", + "DatapointsToAlarm": 1, + "EvaluationPeriods": 1, + "Metrics": Array [ + Object { + "Id": "m1", + "Label": "Count", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "Class", + "Value": "None", + }, + Object { + "Name": "Resource", + "Value": "SecretCount", + }, + Object { + "Name": "Service", + "Value": "Secrets Manager", + }, + Object { + "Name": "Type", + "Value": "Resource", + }, + ], + "MetricName": "ResourceCount", + "Namespace": "AWS/SecretsManager", + }, + "Period": 3600, + "Stat": "Average", + }, + "ReturnData": true, + }, + ], + "Threshold": 5, + "TreatMissingData": "missing", + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "ScopeTestSecretsManagerSecretsCountMinWarning40C64CA4": Object { + "Properties": Object { + "ActionsEnabled": true, + "AlarmDescription": "Number of secrets is too low.", + "AlarmName": "Test-SecretsManager-Secrets-Count-Min-Warning", + "ComparisonOperator": "LessThanThreshold", + "DatapointsToAlarm": 1, + "EvaluationPeriods": 1, + "Metrics": Array [ + Object { + "Id": "m1", + "Label": "Count", + "MetricStat": Object { + "Metric": Object { + "Dimensions": Array [ + Object { + "Name": "Class", + "Value": "None", + }, + Object { + "Name": "Resource", + "Value": "SecretCount", + }, + Object { + "Name": "Service", + "Value": "Secrets Manager", + }, + Object { + "Name": "Type", + "Value": "Resource", + }, + ], + "MetricName": "ResourceCount", + "Namespace": "AWS/SecretsManager", + }, + "Period": 3600, + "Stat": "Average", + }, + "ReturnData": true, + }, + ], + "Threshold": 3, + "TreatMissingData": "missing", + }, + "Type": "AWS::CloudWatch::Alarm", + }, + "Summary68521F81": Object { + "Properties": Object { + "DashboardBody": Object { + "Fn::Join": Array [ + "", + Array [ + "{\\"widgets\\":[{\\"type\\":\\"text\\",\\"width\\":24,\\"height\\":1,\\"x\\":0,\\"y\\":0,\\"properties\\":{\\"markdown\\":\\"### Secrets Manager Secrets **SecretsManager**\\"}},{\\"type\\":\\"metric\\",\\"width\\":12,\\"height\\":6,\\"x\\":0,\\"y\\":1,\\"properties\\":{\\"view\\":\\"timeSeries\\",\\"title\\":\\"Secret Count\\",\\"region\\":\\"", + Object { + "Ref": "AWS::Region", + }, + "\\",\\"metrics\\":[[\\"AWS/SecretsManager\\",\\"ResourceCount\\",\\"Class\\",\\"None\\",\\"Resource\\",\\"SecretCount\\",\\"Service\\",\\"Secrets Manager\\",\\"Type\\",\\"Resource\\",{\\"label\\":\\"Count\\",\\"period\\":3600}]],\\"annotations\\":{\\"horizontal\\":[{\\"label\\":\\"Count > 5 for 1 datapoints within 60 minutes\\",\\"value\\":5,\\"yAxis\\":\\"left\\"},{\\"label\\":\\"Count < 3 for 1 datapoints within 60 minutes\\",\\"value\\":3,\\"yAxis\\":\\"left\\"},{\\"label\\":\\"Count > 4 for 1 datapoints within 60 minutes\\",\\"value\\":4,\\"yAxis\\":\\"left\\"}]},\\"yAxis\\":{\\"left\\":{\\"min\\":0,\\"label\\":\\"Count\\",\\"showUnits\\":false}}}}]}", + ], + ], + }, + }, + "Type": "AWS::CloudWatch::Dashboard", + }, + }, + "Rules": Object { + "CheckBootstrapVersion": Object { + "Assertions": Array [ + Object { + "Assert": Object { + "Fn::Not": Array [ + Object { + "Fn::Contains": Array [ + Array [ + "1", + "2", + "3", + "4", + "5", + ], + Object { + "Ref": "BootstrapVersion", + }, + ], + }, + ], + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.", + }, + ], + }, + }, +} +`;