Skip to content

Commit 9e87661

Browse files
author
Elad Ben-Israel
authored
feat(core): verify CfnOutput has a value and fix VPC export (#2219)
Throw an error if `value` is undefined when defining a `CfnOutput`. Fixes #2012 such that a VPC can be exported without a VPN gateway (there is already unit test coverage).
1 parent 109625d commit 9e87661

File tree

5 files changed

+22
-12
lines changed

5 files changed

+22
-12
lines changed

packages/@aws-cdk/aws-ec2/lib/vpc.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,13 @@ export class VpcNetwork extends VpcNetworkBase {
483483
const priv = new ExportSubnetGroup(this, 'PrivateSubnetIDs', this.privateSubnets, SubnetType.Private, this.availabilityZones.length);
484484
const iso = new ExportSubnetGroup(this, 'IsolatedSubnetIDs', this.isolatedSubnets, SubnetType.Isolated, this.availabilityZones.length);
485485

486+
const vpnGatewayId = this.vpnGatewayId
487+
? new cdk.CfnOutput(this, 'VpnGatewayId', { value: this.vpnGatewayId }).makeImportValue().toString()
488+
: undefined;
489+
486490
return {
487491
vpcId: new cdk.CfnOutput(this, 'VpcId', { value: this.vpcId }).makeImportValue().toString(),
488-
vpnGatewayId: new cdk.CfnOutput(this, 'VpnGatewayId', { value: this.vpnGatewayId }).makeImportValue().toString(),
492+
vpnGatewayId,
489493
availabilityZones: this.availabilityZones,
490494
publicSubnetIds: pub.ids,
491495
publicSubnetNames: pub.names,

packages/@aws-cdk/cdk/lib/cfn-output.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export interface CfnOutputProps {
1313
* The value of an output can include literals, parameter references, pseudo-parameters,
1414
* a mapping value, or intrinsic functions.
1515
*/
16-
readonly value?: any;
16+
readonly value: any;
1717

1818
/**
1919
* The name used to export the value of this output across stacks.
@@ -75,9 +75,13 @@ export class CfnOutput extends CfnElement {
7575
* @param parent The parent construct.
7676
* @param props CfnOutput properties.
7777
*/
78-
constructor(scope: Construct, id: string, props: CfnOutputProps = {}) {
78+
constructor(scope: Construct, id: string, props: CfnOutputProps) {
7979
super(scope, id);
8080

81+
if (props.value === undefined) {
82+
throw new Error(`Missing value for CloudFormation output at path "${this.node.path}"`);
83+
}
84+
8185
this.description = props.description;
8286
this._value = props.value;
8387
this.condition = props.condition;

packages/@aws-cdk/cdk/test/test.include.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export = {
2121

2222
new Include(stack, 'T1', { template: clone(template) });
2323
new CfnResource(stack, 'MyResource3', { type: 'ResourceType3', properties: { P3: 'Hello' } });
24-
new CfnOutput(stack, 'MyOutput', { description: 'Out!', disableExport: true });
24+
new CfnOutput(stack, 'MyOutput', { description: 'Out!', disableExport: true, value: 'hey' });
2525
new CfnParameter(stack, 'MyParam2', { type: 'Integer' });
2626

2727
test.deepEqual(stack._toCloudFormation(), {
@@ -33,7 +33,7 @@ export = {
3333
MyResource2: { Type: 'ResourceType2' },
3434
MyResource3: { Type: 'ResourceType3', Properties: { P3: 'Hello' } } },
3535
Outputs: {
36-
MyOutput: { Description: 'Out!' } } });
36+
MyOutput: { Description: 'Out!', Value: 'hey' } } });
3737

3838
test.done();
3939
},
@@ -43,7 +43,7 @@ export = {
4343

4444
new Include(stack, 'T1', { template });
4545
new CfnResource(stack, 'MyResource3', { type: 'ResourceType3', properties: { P3: 'Hello' } });
46-
new CfnOutput(stack, 'MyOutput', { description: 'Out!' });
46+
new CfnOutput(stack, 'MyOutput', { description: 'Out!', value: 'in' });
4747
new CfnParameter(stack, 'MyParam', { type: 'Integer' }); // duplicate!
4848

4949
test.throws(() => stack._toCloudFormation());

packages/@aws-cdk/cdk/test/test.output.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -23,41 +23,43 @@ export = {
2323

2424
'outputs cannot be referenced'(test: Test) {
2525
const stack = new Stack();
26-
const output = new CfnOutput(stack, 'MyOutput', { description: 'My CfnOutput' });
26+
const output = new CfnOutput(stack, 'MyOutput', { description: 'My CfnOutput', value: 'boom' });
2727
test.throws(() => output.ref);
2828
test.done();
2929
},
3030

3131
'disableExport can be used to disable the auto-export behavior'(test: Test) {
3232
const stack = new Stack();
33-
const output = new CfnOutput(stack, 'MyOutput', { disableExport: true });
33+
const output = new CfnOutput(stack, 'MyOutput', { disableExport: true, value: 'boom' });
3434

3535
test.equal(output.export, null);
3636

3737
// cannot specify `export` and `disableExport` at the same time.
3838
test.throws(() => new CfnOutput(stack, 'YourOutput', {
3939
disableExport: true,
40-
export: 'bla'
40+
export: 'bla',
41+
value: 'boom'
4142
}), /Cannot set `disableExport` and specify an export name/);
4243

4344
test.done();
4445
},
4546

4647
'if stack name is undefined, we will only use the logical ID for the export name'(test: Test) {
4748
const stack = new Stack();
48-
const output = new CfnOutput(stack, 'MyOutput');
49+
const output = new CfnOutput(stack, 'MyOutput', { value: 'boom' });
4950
test.deepEqual(stack.node.resolve(output.makeImportValue()), { 'Fn::ImportValue': 'Stack:MyOutput' });
5051
test.done();
5152
},
5253

5354
'makeImportValue can be used to create an Fn::ImportValue from an output'(test: Test) {
5455
const stack = new Stack(undefined, 'MyStack');
55-
const output = new CfnOutput(stack, 'MyOutput');
56+
const output = new CfnOutput(stack, 'MyOutput', { value: 'boom' });
5657
test.deepEqual(stack.node.resolve(output.makeImportValue()), { 'Fn::ImportValue': 'MyStack:MyOutput' });
5758

5859
test.deepEqual(stack._toCloudFormation(), {
5960
Outputs: {
6061
MyOutput: {
62+
Value: 'boom',
6163
Export: { Name: 'MyStack:MyOutput' }
6264
}
6365
}

packages/@aws-cdk/cdk/test/test.stack.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export = {
118118
const stack = new Stack();
119119

120120
const p = new CfnParameter(stack, 'MyParam', { type: 'String' });
121-
const o = new CfnOutput(stack, 'MyOutput');
121+
const o = new CfnOutput(stack, 'MyOutput', { value: 'boom' });
122122
const c = new CfnCondition(stack, 'MyCondition');
123123

124124
test.equal(stack.node.findChild(p.node.path), p);

0 commit comments

Comments
 (0)