forked from cdklabs/cdk-monitoring-constructs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(SecretsManager): Add support for secrets count metric (cdklabs#137)
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.
- Loading branch information
Showing
12 changed files
with
25,539 additions
and
1,031 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
118 changes: 118 additions & 0 deletions
118
lib/common/monitoring/alarms/SecretsManagerAlarmFactory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 currentSecretCount: 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 in the queue 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 in the queue is too high.", | ||
}); | ||
} | ||
|
||
addChangeInSecretCountAlarm( | ||
metric: MetricWithAlarmSupport, | ||
props: ChangeInSecretCountThreshold, | ||
disambiguator?: string | ||
) { | ||
return this.alarmFactory.addAlarm(metric, { | ||
...props, | ||
disambiguator, | ||
treatMissingData: | ||
props.treatMissingDataOverride ?? TreatMissingData.MISSING, | ||
threshold: props.currentSecretCount, | ||
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." | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
lib/monitoring/aws-secretsmanager/SecretsManagerMetricFactory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { Duration } from "aws-cdk-lib"; | ||
import { MetricFactory, MetricStatistic } from "../../common"; | ||
|
||
const DEFAULT_METRIC_PERIOD = Duration.hours(1); | ||
|
||
export class SecretsManagerMetricFactory { | ||
static readonly Namespace = "AWS/SecretsManager"; | ||
static readonly Class = "None"; | ||
static readonly Resource = "SecretCount"; | ||
static readonly Service = "Secrets Manager"; | ||
static readonly Type = "Resource"; | ||
static readonly MetricSecretCount = "ResourceCount"; | ||
protected readonly metricFactory: MetricFactory; | ||
|
||
constructor(metricFactory: MetricFactory) { | ||
this.metricFactory = metricFactory; | ||
} | ||
|
||
metricSecretCount() { | ||
const dimensionsMap = { | ||
Class: SecretsManagerMetricFactory.Class, | ||
Resource: SecretsManagerMetricFactory.Resource, | ||
Service: SecretsManagerMetricFactory.Service, | ||
Type: SecretsManagerMetricFactory.Type, | ||
}; | ||
|
||
return this.metricFactory.createMetric( | ||
SecretsManagerMetricFactory.MetricSecretCount, | ||
MetricStatistic.AVERAGE, | ||
"Count", | ||
dimensionsMap, | ||
undefined, | ||
SecretsManagerMetricFactory.Namespace, | ||
DEFAULT_METRIC_PERIOD | ||
); | ||
} | ||
} |
139 changes: 139 additions & 0 deletions
139
lib/monitoring/aws-secretsmanager/SecretsManagerMonitoring.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<string, MinSecretCountThreshold>; | ||
readonly addMaxNumberSecretsAlarm?: Record<string, MaxSecretCountThreshold>; | ||
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, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
export * from "./SecretsManagerMetricFactory"; | ||
export * from "./SecretsManagerMetricsPublisher"; | ||
export * from "./SecretsManagerSecretMetricFactory"; | ||
export * from "./SecretsManagerMonitoring"; | ||
export * from "./SecretsManagerSecretMonitoring"; |
Oops, something went wrong.