Skip to content

Commit 9de3a84

Browse files
author
Elad Ben-Israel
authored
feat(aws-cloudformation): allow specifying custom resource type (#943)
Adds the `resourceType` property to `CustomResource` which allows specifying the type name for the resource. Custom resource type names must start with "Custom::" and have some naming restrictions, which are validated.
1 parent 5996442 commit 9de3a84

File tree

2 files changed

+97
-2
lines changed

2 files changed

+97
-2
lines changed

packages/@aws-cdk/aws-cloudformation/lib/custom-resource.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,27 @@ export interface CustomResourceProps {
3232
* Properties to pass to the Lambda
3333
*/
3434
properties?: Properties;
35+
36+
/**
37+
* For custom resources, you can specify AWS::CloudFormation::CustomResource
38+
* (the default) as the resource type, or you can specify your own resource
39+
* type name. For example, you can use "Custom::MyCustomResourceTypeName".
40+
*
41+
* Custom resource type names must begin with "Custom::" and can include
42+
* alphanumeric characters and the following characters: _@-. You can specify
43+
* a custom resource type name up to a maximum length of 60 characters. You
44+
* cannot change the type during an update.
45+
*
46+
* Using your own resource type names helps you quickly differentiate the
47+
* types of custom resources in your stack. For example, if you had two custom
48+
* resources that conduct two different ping tests, you could name their type
49+
* as Custom::PingTester to make them easily identifiable as ping testers
50+
* (instead of using AWS::CloudFormation::CustomResource).
51+
*
52+
* @see
53+
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cfn-customresource.html#aws-cfn-resource-type-name
54+
*/
55+
resourceType?: string;
3556
}
3657

3758
/**
@@ -57,6 +78,10 @@ export class CustomResource extends cloudformation.CustomResource {
5778
});
5879

5980
this.userProperties = props.properties;
81+
82+
if (props.resourceType) {
83+
this.useCustomResourceType(props.resourceType);
84+
}
6085
}
6186

6287
/**
@@ -67,6 +92,23 @@ export class CustomResource extends cloudformation.CustomResource {
6792
return Object.assign(props, uppercaseProperties(this.userProperties || {}));
6893
}
6994

95+
private useCustomResourceType(resourceType: string) {
96+
if (!resourceType.startsWith('Custom::')) {
97+
throw new Error(`Custom resource type must begin with "Custom::" (${resourceType})`);
98+
}
99+
100+
const typeName = resourceType.substr(resourceType.indexOf('::') + 2);
101+
if (typeName.length > 60) {
102+
throw new Error(`Custom resource type length > 60 (${resourceType})`);
103+
}
104+
105+
if (!/^[a-z0-9_@-]+$/i.test(typeName)) {
106+
throw new Error(`Custom resource type name can only include alphanumeric characters and _@- (${typeName})`);
107+
}
108+
109+
this.addOverride('Type', resourceType);
110+
}
111+
70112
}
71113

72114
/**

packages/@aws-cdk/aws-cloudformation/test/test.resource.ts

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { expect } from '@aws-cdk/assert';
1+
import { expect, haveResource } from '@aws-cdk/assert';
22
import lambda = require('@aws-cdk/aws-lambda');
3+
import sns = require('@aws-cdk/aws-sns');
34
import cdk = require('@aws-cdk/cdk');
45
import { Test } from 'nodeunit';
56
import { CustomResource } from '../lib';
@@ -85,7 +86,59 @@ export = {
8586
}
8687
});
8788
test.done();
88-
}
89+
},
90+
91+
'custom resources can specify a resource type that starts with Custom::'(test: Test) {
92+
const stack = new cdk.Stack();
93+
new CustomResource(stack, 'MyCustomResource', {
94+
resourceType: 'Custom::MyCustomResourceType',
95+
topicProvider: new sns.Topic(stack, 'Provider')
96+
});
97+
expect(stack).to(haveResource('Custom::MyCustomResourceType'));
98+
test.done();
99+
},
100+
101+
'fails if custom resource type is invalid': {
102+
'does not start with "Custom::"'(test: Test) {
103+
const stack = new cdk.Stack();
104+
105+
test.throws(() => {
106+
new CustomResource(stack, 'MyCustomResource', {
107+
resourceType: 'NoCustom::MyCustomResourceType',
108+
topicProvider: new sns.Topic(stack, 'Provider')
109+
});
110+
}, /Custom resource type must begin with "Custom::"/);
111+
112+
test.done();
113+
},
114+
115+
'has invalid characters'(test: Test) {
116+
const stack = new cdk.Stack();
117+
118+
test.throws(() => {
119+
new CustomResource(stack, 'MyCustomResource', {
120+
resourceType: 'Custom::My Custom?ResourceType',
121+
topicProvider: new sns.Topic(stack, 'Provider')
122+
});
123+
}, /Custom resource type name can only include alphanumeric characters and/);
124+
125+
test.done();
126+
},
127+
128+
'is longer than 60 characters'(test: Test) {
129+
const stack = new cdk.Stack();
130+
131+
test.throws(() => {
132+
new CustomResource(stack, 'MyCustomResource', {
133+
resourceType: 'Custom::0123456789012345678901234567890123456789012345678901234567891',
134+
topicProvider: new sns.Topic(stack, 'Provider')
135+
});
136+
}, /Custom resource type length > 60/);
137+
138+
test.done();
139+
},
140+
141+
},
89142
};
90143

91144
class TestCustomResource extends cdk.Construct {

0 commit comments

Comments
 (0)