Skip to content

Commit

Permalink
feat(lambda): function can be attached to a list of security groups i…
Browse files Browse the repository at this point in the history
…n the vpc (#5049)

* feat(aws-lambda): allow lambda function to accept list of Security
Groups

- add test case to cover the existing feature of accepting single SG
- add a new function prop that can accept list of SGs, and is optional and conditional

* feat(aws-lambda): work on review comments and mark securityGroup prop as deprecated
  • Loading branch information
ashwgupt authored and mergify[bot] committed Nov 25, 2019
1 parent 0823396 commit 4c1a9ec
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 9 deletions.
46 changes: 37 additions & 9 deletions packages/@aws-cdk/aws-lambda/lib/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,32 @@ export interface FunctionProps {

/**
* What security group to associate with the Lambda's network interfaces.
* This property is being deprecated, consider using securityGroups instead.
*
* Only used if 'vpc' is supplied.
*
* Use securityGroups property instead.
* Function constructor will throw an error if both are specified.
*
* @default - If the function is placed within a VPC and a security group is
* not specified, a dedicated security group will be created for this
* function.
* not specified, either by this or securityGroups prop, a dedicated security
* group will be created for this function.
*
* @deprecated - This property is deprecated, use securityGroups instead
*/
readonly securityGroup?: ec2.ISecurityGroup;

/**
* The list of security groups to associate with the Lambda's network interfaces.
*
* Only used if 'vpc' is supplied.
*
* @default - If the function is placed within a VPC and a security group is
* not specified, either by this or securityGroup prop, a dedicated security
* group will be created for this function.
*/
readonly securityGroups?: ec2.ISecurityGroup[];

/**
* Whether to allow the Lambda to send all network traffic
*
Expand Down Expand Up @@ -560,13 +577,24 @@ export class Function extends FunctionBase {
throw new Error(`Configure 'allowAllOutbound' directly on the supplied SecurityGroup.`);
}

const securityGroup = props.securityGroup || new ec2.SecurityGroup(this, 'SecurityGroup', {
vpc: props.vpc,
description: 'Automatic security group for Lambda Function ' + this.node.uniqueId,
allowAllOutbound: props.allowAllOutbound
});
let securityGroups: ec2.ISecurityGroup[];

if (props.securityGroup && props.securityGroups) {
throw new Error('Only one of the function props, securityGroup or securityGroups, is allowed');
}

if (props.securityGroups) {
securityGroups = props.securityGroups;
} else {
const securityGroup = props.securityGroup || new ec2.SecurityGroup(this, 'SecurityGroup', {
vpc: props.vpc,
description: 'Automatic security group for Lambda Function ' + this.node.uniqueId,
allowAllOutbound: props.allowAllOutbound
});
securityGroups = [securityGroup];
}

this._connections = new ec2.Connections({ securityGroups: [securityGroup] });
this._connections = new ec2.Connections({ securityGroups });

// Pick subnets, make sure they're not Public. Routing through an IGW
// won't work because the ENIs don't get a Public IP.
Expand All @@ -586,7 +614,7 @@ export class Function extends FunctionBase {

return {
subnetIds,
securityGroupIds: [securityGroup.securityGroupId]
securityGroupIds: securityGroups.map(sg => sg.securityGroupId)
};
}

Expand Down
73 changes: 73 additions & 0 deletions packages/@aws-cdk/aws-lambda/test/test.vpc-lambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,79 @@ export = {
test.done();
}

public 'has securitygroup that is passed in props'(test: Test) {
// WHEN
new lambda.Function(this.stack, 'LambdaWithCustomSG', {
code: new lambda.InlineCode('foo'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_8_10,
vpc: this.vpc,
securityGroup: new ec2.SecurityGroup(this.stack, 'CustomSecurityGroupX', { vpc: this.vpc }),
});
// THEN
expect(this.stack).to(haveResource('AWS::Lambda::Function', {
VpcConfig: {
SecurityGroupIds: [
{"Fn::GetAtt": [ "CustomSecurityGroupX6C7F3A78", "GroupId" ]}
],
SubnetIds: [
{Ref: "VPCPrivateSubnet1Subnet8BCA10E0"},
{Ref: "VPCPrivateSubnet2SubnetCFCDAA7A"},
]
}
}));

test.done();
}

public 'has all the securitygroups that are passed as a list of SG in props'(test: Test) {
// WHEN
new lambda.Function(this.stack, 'LambdaWithCustomSGList', {
code: new lambda.InlineCode('foo'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_8_10,
vpc: this.vpc,
securityGroups: [
new ec2.SecurityGroup(this.stack, 'CustomSecurityGroupA', { vpc: this.vpc }),
new ec2.SecurityGroup(this.stack, 'CustomSecurityGroupB', { vpc: this.vpc }),
],
});
// THEN
expect(this.stack).to(haveResource('AWS::Lambda::Function', {
VpcConfig: {
SecurityGroupIds: [
{"Fn::GetAtt": [ "CustomSecurityGroupA267F62DE", "GroupId" ]},
{"Fn::GetAtt": [ "CustomSecurityGroupB1118D0D5", "GroupId" ]}
],
SubnetIds: [
{Ref: "VPCPrivateSubnet1Subnet8BCA10E0"},
{Ref: "VPCPrivateSubnet2SubnetCFCDAA7A"},
]
}
}));

test.done();
}

public 'fails if both of securityGroup and securityGroups are passed in props at once'(test: Test) {
// THEN
test.throws(() => {
new lambda.Function(this.stack, 'LambdaWithWrongProps', {
code: new lambda.InlineCode('foo'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_8_10,
vpc: this.vpc,
securityGroup: new ec2.SecurityGroup(this.stack, 'CustomSecurityGroupB', { vpc: this.vpc }),
securityGroups: [
new ec2.SecurityGroup(this.stack, 'CustomSecurityGroupC', { vpc: this.vpc }),
new ec2.SecurityGroup(this.stack, 'CustomSecurityGroupD', { vpc: this.vpc }),
],
});
}, /Only one of the function props, securityGroup or securityGroups, is allowed/);

test.done();
}

public 'participates in Connections objects'(test: Test) {
// GIVEN
const securityGroup = new ec2.SecurityGroup(this.stack, 'SomeSecurityGroup', { vpc: this.vpc });
Expand Down

0 comments on commit 4c1a9ec

Please sign in to comment.