Skip to content

Commit d185888

Browse files
l2 imp of AWS::CloudFormation::ResourceVersion
- CF type https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-resourceversion.html - creates log group + log access role if not provided - param documentation taken from cloudformation docs
1 parent a928748 commit d185888

File tree

3 files changed

+182
-0
lines changed

3 files changed

+182
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './cloud-formation-capabilities';
22
export * from './custom-resource';
33
export * from './nested-stack';
4+
export * from './resource-version';
45

56
// AWS::CloudFormation CloudFormation Resources:
67
export * from './cloudformation.generated';
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { Construct } from 'constructs';
2+
import { CfnResourceVersion } from './cloudformation.generated';
3+
import { IRole, Role, CompositePrincipal, ServicePrincipal, PolicyDocument, PolicyStatement, Effect } from '../../aws-iam';
4+
import { ILogGroup, LogGroup, RetentionDays } from '../../aws-logs';
5+
import { Asset } from '../../aws-s3-assets';
6+
/**
7+
* logging configuration for handler
8+
*/
9+
export interface ILogConfiguration {
10+
/**
11+
* The ARN of the role that CloudFormation should assume when sending log entries to CloudWatch logs.
12+
*/
13+
readonly logRole?: IRole;
14+
/**
15+
* The Amazon CloudWatch logs group to which CloudFormation sends error logging information when invoking the type's handlers.
16+
*/
17+
readonly logGroup?: ILogGroup;
18+
}
19+
20+
/**
21+
* properties for ResourceVersion construct
22+
*/
23+
export interface ResourceVersionProps {
24+
/**
25+
* The name of the resource being registered.
26+
* We recommend that resource names adhere to the following pattern: company_or_organization::service::type.
27+
*/
28+
readonly typeName: string;
29+
/**
30+
* Logging configuration information for a resource.
31+
* @default if not provided, it will be auto generated
32+
*/
33+
readonly logging?: ILogConfiguration;
34+
/**
35+
* Contains the resource project package that contains the necessary files for the resource you want to register.
36+
*/
37+
readonly handler: Asset;
38+
/**
39+
* IAM role for CloudFormation to assume when invoking the resource. If your resource calls AWS APIs in any of its handlers, you must create an IAM execution role that includes the necessary permissions to call those AWS APIs,
40+
* and provision that execution role in your account. When CloudFormation needs to invoke the resource type handler, CloudFormation assumes this execution role to create a temporary session token,
41+
* which it then passes to the resource type handler, thereby supplying your resource type with the appropriate credentials.
42+
* @default no role will be passed to the resourceVersion
43+
*/
44+
readonly executionRole?: IRole;
45+
}
46+
47+
/**
48+
* L2 construct for https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-resourceversion.html
49+
*/
50+
export class ResourceVersion extends Construct {
51+
52+
/**
53+
* resourceVersion resource created by this construct
54+
*/
55+
public readonly resourceVersion: CfnResourceVersion;
56+
/**
57+
* logRole referenced by resourceVersion
58+
*/
59+
public readonly logRole: IRole;
60+
/**
61+
* logGroup referenced by resourceVersion
62+
*/
63+
public readonly logGroup: ILogGroup;
64+
65+
constructor(scope: Construct, id: string, props: ResourceVersionProps) {
66+
super(scope, id);
67+
68+
// default perms from https://github.com/aws-cloudformation/cloudformation-cli/blob/a73666d6b38099549c55f674748c095e0ef628ee/src/rpdk/core/data/managed-upload-infrastructure.yaml#L129
69+
this.logRole =
70+
props.logging?.logRole ??
71+
new Role(this, 'LogRole', {
72+
assumedBy: new CompositePrincipal(
73+
new ServicePrincipal('hooks.cloudformation.amazonaws.com'),
74+
new ServicePrincipal('resources.cloudformation.amazonaws.com'),
75+
),
76+
description: 'Role for CloudFormation Resource Version logging',
77+
inlinePolicies: {
78+
LogAndMetricsDeliveryRolePolicy:
79+
new PolicyDocument({
80+
statements: [
81+
new PolicyStatement({
82+
effect: Effect.ALLOW,
83+
actions: ['cloudwatch:PutMetricData', 'cloudwatch:ListMetrics', 'logs:DescribeLogGroups'],
84+
resources: ['*'],
85+
}),
86+
],
87+
}),
88+
89+
},
90+
});
91+
92+
this.logGroup =
93+
props.logging?.logGroup ??
94+
new LogGroup(this, 'LogGroup', {
95+
logGroupName: `/aws/cloudformation/${props.typeName.split('::').join('-')}`,
96+
retention: RetentionDays.TEN_YEARS,
97+
});
98+
this.logGroup.grantWrite(this.logRole);
99+
100+
this.resourceVersion = new CfnResourceVersion(this, 'CfnResourceVersion', {
101+
typeName: props.typeName,
102+
schemaHandlerPackage: props.handler.s3ObjectUrl,
103+
loggingConfig: {
104+
logRoleArn: this.logRole.roleArn,
105+
logGroupName: this.logGroup.logGroupName,
106+
},
107+
executionRoleArn: props.executionRole?.roleArn,
108+
});
109+
}
110+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { join } from 'path';
2+
import { Template } from '../../assertions';
3+
import { Role, ServicePrincipal } from '../../aws-iam';
4+
import { Asset } from '../../aws-s3-assets';
5+
import { App, Stack } from '../../core';
6+
import { ResourceVersion } from '../lib';
7+
8+
test('it creates the resource-version and its related resources', () => {
9+
const app = new App();
10+
const stack = new Stack(app, 'Stack', {
11+
env: { account: '1234', region: 'testregion' },
12+
});
13+
const id = 'unittest';
14+
const asset = new Asset(stack, 'SampleAsset', {
15+
path: join(__dirname, 'asset-directory-fixture'),
16+
});
17+
const executionRole = new Role(stack, 'role', {
18+
assumedBy: new ServicePrincipal('unittest.amazonaws.com'),
19+
});
20+
const typeName = 'UNIT::TEST::TEST';
21+
const rv = new ResourceVersion(stack, id, {
22+
typeName,
23+
handler: asset,
24+
executionRole,
25+
});
26+
27+
// it should create the l1 resource
28+
Template.fromStack(stack).hasResourceProperties('AWS::CloudFormation::ResourceVersion', {
29+
TypeName: typeName,
30+
SchemaHandlerPackage: asset.s3ObjectUrl,
31+
LoggingConfig: {
32+
LogGroupName: {
33+
Ref: 'unittestLogGroup321F9C37',
34+
},
35+
LogRoleArn: {
36+
'Fn::GetAtt': ['unittestLogRoleEC6FE6AD', 'Arn'],
37+
},
38+
},
39+
ExecutionRoleArn: {
40+
'Fn::GetAtt': ['roleC7B7E775', 'Arn'],
41+
},
42+
});
43+
44+
// it should create the log group (if not provided)
45+
Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', {
46+
LogGroupName: `/aws/cloudformation/${typeName.split('::').join('-')}`,
47+
});
48+
49+
// it should create the log delivery role (if not provided)
50+
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', {
51+
AssumeRolePolicyDocument: {
52+
Statement: [
53+
{
54+
Action: 'sts:AssumeRole',
55+
Effect: 'Allow',
56+
Principal: {
57+
Service: 'hooks.cloudformation.amazonaws.com',
58+
},
59+
},
60+
{
61+
Action: 'sts:AssumeRole',
62+
Effect: 'Allow',
63+
Principal: {
64+
Service: 'resources.cloudformation.amazonaws.com',
65+
},
66+
},
67+
],
68+
Version: '2012-10-17',
69+
},
70+
});
71+
});

0 commit comments

Comments
 (0)