Skip to content

Commit b46fdc9

Browse files
fix(lambda): cannot create lambda in public subnets (#9468)
---- Closes #8935 *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent aad951a commit b46fdc9

File tree

2 files changed

+128
-10
lines changed

2 files changed

+128
-10
lines changed

packages/@aws-cdk/aws-lambda/lib/function.ts

+13-6
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,15 @@ export interface FunctionProps extends FunctionOptions {
299299
* @default - will not mount any filesystem
300300
*/
301301
readonly filesystem?: FileSystem;
302+
303+
/**
304+
* Lambda Functions in a public subnet can NOT access the internet.
305+
* Use this property to acknowledge this limitation and still place the function in a public subnet.
306+
* @see https://stackoverflow.com/questions/52992085/why-cant-an-aws-lambda-function-inside-a-public-subnet-in-a-vpc-connect-to-the/52994841#52994841
307+
*
308+
* @default false
309+
*/
310+
readonly allowPublicSubnet?: boolean;
302311
}
303312

304313
/**
@@ -819,15 +828,13 @@ export class Function extends FunctionBase {
819828
}
820829
}
821830

822-
// Pick subnets, make sure they're not Public. Routing through an IGW
823-
// won't work because the ENIs don't get a Public IP.
824-
// Why are we not simply forcing vpcSubnets? Because you might still be choosing
825-
// Isolated networks or selecting among 2 sets of Private subnets by name.
831+
const allowPublicSubnet = props.allowPublicSubnet ?? false;
826832
const { subnetIds } = props.vpc.selectSubnets(props.vpcSubnets);
827833
const publicSubnetIds = new Set(props.vpc.publicSubnets.map(s => s.subnetId));
828834
for (const subnetId of subnetIds) {
829-
if (publicSubnetIds.has(subnetId)) {
830-
throw new Error('Not possible to place Lambda Functions in a Public subnet');
835+
if (publicSubnetIds.has(subnetId) && !allowPublicSubnet) {
836+
throw new Error('Lambda Functions in a public subnet can NOT access the internet. ' +
837+
'If you are aware of this limitation and would still like to place the function int a public subnet, set `allowPublicSubnet` to true');
831838
}
832839
}
833840

packages/@aws-cdk/aws-lambda/test/test.vpc-lambda.ts

+115-4
Original file line numberDiff line numberDiff line change
@@ -212,22 +212,133 @@ export = {
212212
test.done();
213213
},
214214

215-
'picking public subnets is not allowed'(test: Test) {
215+
'can pick public subnet for Lambda'(test: Test) {
216216
// GIVEN
217217
const stack = new cdk.Stack();
218218
const vpc = new ec2.Vpc(stack, 'VPC');
219219

220+
// WHEN
221+
new lambda.Function(stack, 'PublicLambda', {
222+
allowPublicSubnet: true,
223+
code: new lambda.InlineCode('foo'),
224+
handler: 'index.handler',
225+
runtime: lambda.Runtime.NODEJS_10_X,
226+
vpc,
227+
vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
228+
});
229+
230+
// THEN
231+
expect(stack).to(haveResource('AWS::Lambda::Function', {
232+
VpcConfig: {
233+
SecurityGroupIds: [
234+
{'Fn::GetAtt': [ 'PublicLambdaSecurityGroup61D896FD', 'GroupId' ]},
235+
],
236+
SubnetIds: [
237+
{Ref: 'VPCPublicSubnet1SubnetB4246D30'},
238+
{Ref: 'VPCPublicSubnet2Subnet74179F39'},
239+
],
240+
},
241+
}));
242+
test.done();
243+
},
244+
245+
'can pick private subnet for Lambda'(test: Test) {
246+
// GIVEN
247+
const stack = new cdk.Stack();
248+
const vpc = new ec2.Vpc(stack, 'VPC');
249+
250+
// WHEN
251+
new lambda.Function(stack, 'PrivateLambda', {
252+
code: new lambda.InlineCode('foo'),
253+
handler: 'index.handler',
254+
runtime: lambda.Runtime.NODEJS_10_X,
255+
vpc,
256+
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE },
257+
});
258+
259+
// THEN
260+
261+
expect(stack).to(haveResource('AWS::Lambda::Function', {
262+
VpcConfig: {
263+
SecurityGroupIds: [
264+
{'Fn::GetAtt': [ 'PrivateLambdaSecurityGroupF53C8342', 'GroupId' ]},
265+
],
266+
SubnetIds: [
267+
{Ref: 'VPCPrivateSubnet1Subnet8BCA10E0'},
268+
{Ref: 'VPCPrivateSubnet2SubnetCFCDAA7A'},
269+
],
270+
},
271+
}));
272+
test.done();
273+
},
274+
275+
'can pick isolated subnet for Lambda'(test: Test) {
276+
// GIVEN
277+
const stack = new cdk.Stack();
278+
const vpc = new ec2.Vpc(stack, 'VPC', {
279+
subnetConfiguration: [
280+
{
281+
name: 'Isolated',
282+
subnetType: ec2.SubnetType.ISOLATED,
283+
},
284+
],
285+
});
286+
287+
// WHEN
288+
new lambda.Function(stack, 'IsolatedLambda', {
289+
code: new lambda.InlineCode('foo'),
290+
handler: 'index.handler',
291+
runtime: lambda.Runtime.NODEJS_10_X,
292+
vpc,
293+
vpcSubnets: { subnetType: ec2.SubnetType.ISOLATED },
294+
});
295+
296+
// THEN
297+
298+
expect(stack).to(haveResource('AWS::Lambda::Function', {
299+
VpcConfig: {
300+
SecurityGroupIds: [
301+
{'Fn::GetAtt': [ 'IsolatedLambdaSecurityGroupCE25B6A9', 'GroupId' ]},
302+
],
303+
SubnetIds: [
304+
{Ref: 'VPCIsolatedSubnet1SubnetEBD00FC6'},
305+
{Ref: 'VPCIsolatedSubnet2Subnet4B1C8CAA'},
306+
],
307+
},
308+
}));
309+
test.done();
310+
},
311+
312+
'picking public subnet type is not allowed if not overriding allowPublicSubnet'(test: Test) {
313+
// GIVEN
314+
const stack = new cdk.Stack();
315+
const vpc = new ec2.Vpc(stack, 'VPC', {
316+
subnetConfiguration: [
317+
{
318+
name: 'Public',
319+
subnetType: ec2.SubnetType.PUBLIC,
320+
},
321+
{
322+
name: 'Private',
323+
subnetType: ec2.SubnetType.PRIVATE,
324+
},
325+
{
326+
name: 'Isolated',
327+
subnetType: ec2.SubnetType.ISOLATED,
328+
},
329+
],
330+
});
331+
220332
// WHEN
221333
test.throws(() => {
222-
new lambda.Function(stack, 'Lambda', {
334+
new lambda.Function(stack, 'PublicLambda', {
223335
code: new lambda.InlineCode('foo'),
224336
handler: 'index.handler',
225337
runtime: lambda.Runtime.NODEJS_10_X,
226338
vpc,
227339
vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
228340
});
229-
});
230-
341+
}, /Lambda Functions in a public subnet/);
231342
test.done();
232343
},
233344
};

0 commit comments

Comments
 (0)