diff --git a/packages/@aws-cdk/aws-lambda-python/package.json b/packages/@aws-cdk/aws-lambda-python/package.json index 727a511f1b872..1696063ef8729 100644 --- a/packages/@aws-cdk/aws-lambda-python/package.json +++ b/packages/@aws-cdk/aws-lambda-python/package.json @@ -67,12 +67,14 @@ "dependencies": { "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/core": "0.0.0", + "@aws-cdk/aws-ec2": "0.0.0", "constructs": "^3.0.4" }, "homepage": "https://github.com/aws/aws-cdk", "peerDependencies": { "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/core": "0.0.0", + "@aws-cdk/aws-ec2": "0.0.0", "constructs": "^3.0.4" }, "engines": { diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.expected.json new file mode 100644 index 0000000000000..ac0c83c38cbdd --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.expected.json @@ -0,0 +1,394 @@ +{ + "Resources": { + "myvpc9455A260": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "cdk-integ-lambda-python-vpc/my_vpc" + } + ] + } + }, + "myvpcingressSubnet1Subnet82F0259C": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/24", + "VpcId": { + "Ref": "myvpc9455A260" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "ingress" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "cdk-integ-lambda-python-vpc/my_vpc/ingressSubnet1" + } + ] + } + }, + "myvpcingressSubnet1RouteTableD6322DD5": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "myvpc9455A260" + }, + "Tags": [ + { + "Key": "Name", + "Value": "cdk-integ-lambda-python-vpc/my_vpc/ingressSubnet1" + } + ] + } + }, + "myvpcingressSubnet1RouteTableAssociation12FE9C06": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "myvpcingressSubnet1RouteTableD6322DD5" + }, + "SubnetId": { + "Ref": "myvpcingressSubnet1Subnet82F0259C" + } + } + }, + "myvpcingressSubnet1DefaultRoute6FCDFDDF": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "myvpcingressSubnet1RouteTableD6322DD5" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "myvpcIGW24C0BBAE" + } + }, + "DependsOn": [ + "myvpcVPCGWD483DB64" + ] + }, + "myvpcingressSubnet2Subnet56945106": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.1.0/24", + "VpcId": { + "Ref": "myvpc9455A260" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "ingress" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "cdk-integ-lambda-python-vpc/my_vpc/ingressSubnet2" + } + ] + } + }, + "myvpcingressSubnet2RouteTable2112B53A": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "myvpc9455A260" + }, + "Tags": [ + { + "Key": "Name", + "Value": "cdk-integ-lambda-python-vpc/my_vpc/ingressSubnet2" + } + ] + } + }, + "myvpcingressSubnet2RouteTableAssociation08B3BE06": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "myvpcingressSubnet2RouteTable2112B53A" + }, + "SubnetId": { + "Ref": "myvpcingressSubnet2Subnet56945106" + } + } + }, + "myvpcingressSubnet2DefaultRouteBF3C1EC8": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "myvpcingressSubnet2RouteTable2112B53A" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "myvpcIGW24C0BBAE" + } + }, + "DependsOn": [ + "myvpcVPCGWD483DB64" + ] + }, + "myvpcingressSubnet3Subnet5E973EB6": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.2.0/24", + "VpcId": { + "Ref": "myvpc9455A260" + }, + "AvailabilityZone": "test-region-1c", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "ingress" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "cdk-integ-lambda-python-vpc/my_vpc/ingressSubnet3" + } + ] + } + }, + "myvpcingressSubnet3RouteTable84B01952": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "myvpc9455A260" + }, + "Tags": [ + { + "Key": "Name", + "Value": "cdk-integ-lambda-python-vpc/my_vpc/ingressSubnet3" + } + ] + } + }, + "myvpcingressSubnet3RouteTableAssociation22645059": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "myvpcingressSubnet3RouteTable84B01952" + }, + "SubnetId": { + "Ref": "myvpcingressSubnet3Subnet5E973EB6" + } + } + }, + "myvpcingressSubnet3DefaultRouteD30AAE70": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "myvpcingressSubnet3RouteTable84B01952" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "myvpcIGW24C0BBAE" + } + }, + "DependsOn": [ + "myvpcVPCGWD483DB64" + ] + }, + "myvpcIGW24C0BBAE": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "cdk-integ-lambda-python-vpc/my_vpc" + } + ] + } + }, + "myvpcVPCGWD483DB64": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "myvpc9455A260" + }, + "InternetGatewayId": { + "Ref": "myvpcIGW24C0BBAE" + } + } + }, + "myhandlerServiceRole77891068": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" + ] + ] + } + ] + } + }, + "myhandlerSecurityGroupF566A239": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Automatic security group for Lambda Function cdkinteglambdapythonvpcmyhandlerCA7DB4EE", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "myvpc9455A260" + } + } + }, + "myhandlerD202FA8E": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParametersf7ff2d5c8b3e609d156f7eccbc393419969e89acef6fcc4be4a7091684c1e4d4S3Bucket3D64A262" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersf7ff2d5c8b3e609d156f7eccbc393419969e89acef6fcc4be4a7091684c1e4d4S3VersionKey676CE5F0" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersf7ff2d5c8b3e609d156f7eccbc393419969e89acef6fcc4be4a7091684c1e4d4S3VersionKey676CE5F0" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "myhandlerServiceRole77891068", + "Arn" + ] + }, + "Runtime": "python3.6", + "VpcConfig": { + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "myhandlerSecurityGroupF566A239", + "GroupId" + ] + } + ], + "SubnetIds": [ + { + "Ref": "myvpcingressSubnet1Subnet82F0259C" + }, + { + "Ref": "myvpcingressSubnet2Subnet56945106" + }, + { + "Ref": "myvpcingressSubnet3Subnet5E973EB6" + } + ] + } + }, + "DependsOn": [ + "myhandlerServiceRole77891068" + ] + } + }, + "Parameters": { + "AssetParametersf7ff2d5c8b3e609d156f7eccbc393419969e89acef6fcc4be4a7091684c1e4d4S3Bucket3D64A262": { + "Type": "String", + "Description": "S3 bucket for asset \"f7ff2d5c8b3e609d156f7eccbc393419969e89acef6fcc4be4a7091684c1e4d4\"" + }, + "AssetParametersf7ff2d5c8b3e609d156f7eccbc393419969e89acef6fcc4be4a7091684c1e4d4S3VersionKey676CE5F0": { + "Type": "String", + "Description": "S3 key for asset version \"f7ff2d5c8b3e609d156f7eccbc393419969e89acef6fcc4be4a7091684c1e4d4\"" + }, + "AssetParametersf7ff2d5c8b3e609d156f7eccbc393419969e89acef6fcc4be4a7091684c1e4d4ArtifactHashAFB301FE": { + "Type": "String", + "Description": "Artifact hash for asset \"f7ff2d5c8b3e609d156f7eccbc393419969e89acef6fcc4be4a7091684c1e4d4\"" + } + }, + "Outputs": { + "FunctionArn": { + "Value": { + "Fn::GetAtt": [ + "myhandlerD202FA8E", + "Arn" + ] + } + } + } +} diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.ts b/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.ts new file mode 100644 index 0000000000000..1584208b2335d --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.ts @@ -0,0 +1,38 @@ +import * as path from 'path'; +import { Vpc, SubnetType } from '@aws-cdk/aws-ec2'; +import { Runtime } from '@aws-cdk/aws-lambda'; +import { App, CfnOutput, Construct, Stack, StackProps } from '@aws-cdk/core'; +import * as lambda from '../lib'; + +/* + * Stack verification steps: + * * aws lambda invoke --function-name --invocation-type Event --payload '"OK"' response.json + */ + +class TestStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const vpc = new Vpc(this, 'my_vpc', { + natGateways: 0, + subnetConfiguration: [ + { cidrMask: 24, name: 'ingress', subnetType: SubnetType.PUBLIC }, + ], + }); + + const fn = new lambda.PythonFunction(this, 'my_handler', { + entry: path.join(__dirname, 'lambda-handler'), + runtime: Runtime.PYTHON_3_6, + vpc, + allowPublicSubnet: true, + }); + + new CfnOutput(this, 'FunctionArn', { + value: fn.functionArn, + }); + } +} + +const app = new App(); +new TestStack(app, 'cdk-integ-lambda-python-vpc'); +app.synth(); diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index 97b70b700db3f..38de4583abeb0 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -264,6 +264,22 @@ export interface FunctionOptions extends EventInvokeConfigOptions { * @default - default options as described in `VersionOptions` */ readonly currentVersionOptions?: VersionOptions; + + /** + * The filesystem configuration for the lambda function + * + * @default - will not mount any filesystem + */ + readonly filesystem?: FileSystem; + + /** + * Lambda Functions in a public subnet can NOT access the internet. + * Use this property to acknowledge this limitation and still place the function in a public subnet. + * @see https://stackoverflow.com/questions/52992085/why-cant-an-aws-lambda-function-inside-a-public-subnet-in-a-vpc-connect-to-the/52994841#52994841 + * + * @default false + */ + readonly allowPublicSubnet?: boolean; } export interface FunctionProps extends FunctionOptions { @@ -292,22 +308,6 @@ export interface FunctionProps extends FunctionOptions { * the handler. */ readonly handler: string; - - /** - * The filesystem configuration for the lambda function - * - * @default - will not mount any filesystem - */ - readonly filesystem?: FileSystem; - - /** - * Lambda Functions in a public subnet can NOT access the internet. - * Use this property to acknowledge this limitation and still place the function in a public subnet. - * @see https://stackoverflow.com/questions/52992085/why-cant-an-aws-lambda-function-inside-a-public-subnet-in-a-vpc-connect-to-the/52994841#52994841 - * - * @default false - */ - readonly allowPublicSubnet?: boolean; } /**