Skip to content

Commit d19640b

Browse files
authored
feat(appconfig-alpha): support for composite alarms (#28156)
Supporting composite alarm role creation on the AppConfig environment. *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 0abd0b5 commit d19640b

11 files changed

+447
-15
lines changed

packages/@aws-cdk/aws-appconfig-alpha/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,11 +356,13 @@ Basic environment with monitors:
356356
```ts
357357
declare const application: appconfig.Application;
358358
declare const alarm: cloudwatch.Alarm;
359+
declare const compositeAlarm: cloudwatch.CompositeAlarm;
359360

360361
new appconfig.Environment(this, 'MyEnvironment', {
361362
application,
362363
monitors: [
363364
appconfig.Monitor.fromCloudWatchAlarm(alarm),
365+
appconfig.Monitor.fromCloudWatchAlarm(compositeAlarm),
364366
],
365367
});
366368
```

packages/@aws-cdk/aws-appconfig-alpha/lib/environment.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ export class Environment extends EnvironmentBase {
256256
return {
257257
alarmArn: monitor.alarmArn,
258258
...(monitor.monitorType === MonitorType.CLOUDWATCH
259-
? { alarmRoleArn: monitor.alarmRoleArn || this.createAlarmRole(monitor.alarmArn, index).roleArn }
259+
? { alarmRoleArn: monitor.alarmRoleArn || this.createAlarmRole(monitor, index).roleArn }
260260
: { alarmRoleArn: monitor.alarmRoleArn }),
261261
};
262262
}),
@@ -274,7 +274,20 @@ export class Environment extends EnvironmentBase {
274274
this.application.addExistingEnvironment(this);
275275
}
276276

277-
private createAlarmRole(alarmArn: string, index: number): iam.IRole {
277+
private createAlarmRole(monitor: Monitor, index: number): iam.IRole {
278+
const logicalId = monitor.isCompositeAlarm ? 'RoleCompositeAlarm' : `Role${index}`;
279+
const existingRole = this.node.tryFindChild(logicalId) as iam.IRole;
280+
if (existingRole) {
281+
return existingRole;
282+
}
283+
const alarmArn = monitor.isCompositeAlarm
284+
? this.stack.formatArn({
285+
service: 'cloudwatch',
286+
resource: 'alarm',
287+
resourceName: '*',
288+
arnFormat: ArnFormat.COLON_RESOURCE_NAME,
289+
})
290+
: monitor.alarmArn;
278291
const policy = new iam.PolicyStatement({
279292
effect: iam.Effect.ALLOW,
280293
actions: ['cloudwatch:DescribeAlarms'],
@@ -283,7 +296,7 @@ export class Environment extends EnvironmentBase {
283296
const document = new iam.PolicyDocument({
284297
statements: [policy],
285298
});
286-
const role = new iam.Role(this, `Role${index}`, {
299+
const role = new iam.Role(this, logicalId, {
287300
roleName: PhysicalName.GENERATE_IF_NEEDED,
288301
assumedBy: new iam.ServicePrincipal('appconfig.amazonaws.com'),
289302
inlinePolicies: {
@@ -325,6 +338,7 @@ export abstract class Monitor {
325338
alarmArn: alarm.alarmArn,
326339
alarmRoleArn: alarmRole?.roleArn,
327340
monitorType: MonitorType.CLOUDWATCH,
341+
isCompositeAlarm: alarm instanceof cloudwatch.CompositeAlarm,
328342
};
329343
}
330344

@@ -355,6 +369,11 @@ export abstract class Monitor {
355369
* The IAM role ARN for AWS AppConfig to view the alarm state.
356370
*/
357371
public abstract readonly alarmRoleArn?: string;
372+
373+
/**
374+
* Indicates whether a CloudWatch alarm is a composite alarm.
375+
*/
376+
public abstract readonly isCompositeAlarm?: boolean;
358377
}
359378

360379
export interface IEnvironment extends IResource {

packages/@aws-cdk/aws-appconfig-alpha/test/environment.test.ts

Lines changed: 180 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as cdk from 'aws-cdk-lib';
22
import { App } from 'aws-cdk-lib';
33
import { Template } from 'aws-cdk-lib/assertions';
4-
import { Alarm, Metric } from 'aws-cdk-lib/aws-cloudwatch';
4+
import { Alarm, CompositeAlarm, Metric } from 'aws-cdk-lib/aws-cloudwatch';
55
import * as iam from 'aws-cdk-lib/aws-iam';
66
import { Application, Environment, Monitor } from '../lib';
77

@@ -230,6 +230,185 @@ describe('environment', () => {
230230
});
231231
});
232232

233+
test('environment with composite alarm', () => {
234+
const stack = new cdk.Stack();
235+
const app = new Application(stack, 'MyAppConfig');
236+
const alarm = new Alarm(stack, 'Alarm', {
237+
threshold: 5,
238+
evaluationPeriods: 5,
239+
metric: new Metric(
240+
{
241+
namespace: 'aws',
242+
metricName: 'myMetric',
243+
},
244+
),
245+
});
246+
const compositeAlarm = new CompositeAlarm(stack, 'MyCompositeAlarm', {
247+
alarmRule: alarm,
248+
});
249+
const env = new Environment(stack, 'MyEnvironment', {
250+
name: 'TestEnv',
251+
application: app,
252+
monitors: [
253+
Monitor.fromCloudWatchAlarm(compositeAlarm),
254+
],
255+
});
256+
257+
expect(env).toBeDefined();
258+
Template.fromStack(stack).resourceCountIs('AWS::CloudWatch::Alarm', 1);
259+
Template.fromStack(stack).resourceCountIs('AWS::CloudWatch::CompositeAlarm', 1);
260+
Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Environment', {
261+
Name: 'TestEnv',
262+
ApplicationId: {
263+
Ref: 'MyAppConfigB4B63E75',
264+
},
265+
Monitors: [
266+
{
267+
AlarmArn: {
268+
'Fn::GetAtt': [
269+
'MyCompositeAlarm0F045229',
270+
'Arn',
271+
],
272+
},
273+
AlarmRoleArn: {
274+
'Fn::GetAtt': [
275+
'MyEnvironmentRoleCompositeAlarm8C2A0542',
276+
'Arn',
277+
],
278+
},
279+
},
280+
],
281+
});
282+
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', {
283+
Policies: [
284+
{
285+
PolicyDocument: {
286+
Statement: [
287+
{
288+
Effect: iam.Effect.ALLOW,
289+
Resource: {
290+
'Fn::Join': [
291+
'',
292+
[
293+
'arn:',
294+
{ Ref: 'AWS::Partition' },
295+
':cloudwatch:',
296+
{ Ref: 'AWS::Region' },
297+
':',
298+
{ Ref: 'AWS::AccountId' },
299+
':alarm:*',
300+
],
301+
],
302+
},
303+
Action: 'cloudwatch:DescribeAlarms',
304+
},
305+
],
306+
},
307+
PolicyName: 'AllowAppConfigMonitorAlarmPolicy',
308+
},
309+
],
310+
});
311+
});
312+
313+
test('environment with two composite alarms', () => {
314+
const stack = new cdk.Stack();
315+
const app = new Application(stack, 'MyAppConfig');
316+
const alarm = new Alarm(stack, 'Alarm', {
317+
threshold: 5,
318+
evaluationPeriods: 5,
319+
metric: new Metric(
320+
{
321+
namespace: 'aws',
322+
metricName: 'myMetric',
323+
},
324+
),
325+
});
326+
const compositeAlarm1 = new CompositeAlarm(stack, 'MyCompositeAlarm1', {
327+
alarmRule: alarm,
328+
});
329+
const compositeAlarm2 = new CompositeAlarm(stack, 'MyCompositeAlarm2', {
330+
alarmRule: alarm,
331+
});
332+
const env = new Environment(stack, 'MyEnvironment', {
333+
name: 'TestEnv',
334+
application: app,
335+
monitors: [
336+
Monitor.fromCloudWatchAlarm(compositeAlarm1),
337+
Monitor.fromCloudWatchAlarm(compositeAlarm2),
338+
],
339+
});
340+
341+
expect(env).toBeDefined();
342+
Template.fromStack(stack).resourceCountIs('AWS::CloudWatch::Alarm', 1);
343+
Template.fromStack(stack).resourceCountIs('AWS::CloudWatch::CompositeAlarm', 2);
344+
Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 1);
345+
Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Environment', {
346+
Name: 'TestEnv',
347+
ApplicationId: {
348+
Ref: 'MyAppConfigB4B63E75',
349+
},
350+
Monitors: [
351+
{
352+
AlarmArn: {
353+
'Fn::GetAtt': [
354+
'MyCompositeAlarm159A950D0',
355+
'Arn',
356+
],
357+
},
358+
AlarmRoleArn: {
359+
'Fn::GetAtt': [
360+
'MyEnvironmentRoleCompositeAlarm8C2A0542',
361+
'Arn',
362+
],
363+
},
364+
},
365+
{
366+
AlarmArn: {
367+
'Fn::GetAtt': [
368+
'MyCompositeAlarm2195BFA48',
369+
'Arn',
370+
],
371+
},
372+
AlarmRoleArn: {
373+
'Fn::GetAtt': [
374+
'MyEnvironmentRoleCompositeAlarm8C2A0542',
375+
'Arn',
376+
],
377+
},
378+
},
379+
],
380+
});
381+
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', {
382+
Policies: [
383+
{
384+
PolicyDocument: {
385+
Statement: [
386+
{
387+
Effect: iam.Effect.ALLOW,
388+
Resource: {
389+
'Fn::Join': [
390+
'',
391+
[
392+
'arn:',
393+
{ Ref: 'AWS::Partition' },
394+
':cloudwatch:',
395+
{ Ref: 'AWS::Region' },
396+
':',
397+
{ Ref: 'AWS::AccountId' },
398+
':alarm:*',
399+
],
400+
],
401+
},
402+
Action: 'cloudwatch:DescribeAlarms',
403+
},
404+
],
405+
},
406+
PolicyName: 'AllowAppConfigMonitorAlarmPolicy',
407+
},
408+
],
409+
});
410+
});
411+
233412
test('environment with monitors with two alarms', () => {
234413
const stack = new cdk.Stack();
235414
const app = new Application(stack, 'MyAppConfig');

packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/appconfigenvironmentDefaultTestDeployAssert75BD28E7.assets.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk/aws-appconfig-alpha/test/integ.environment.js.snapshot/aws-appconfig-environment.assets.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)