diff --git a/packages/@aws-cdk/aws-cloudfront/README.md b/packages/@aws-cdk/aws-cloudfront/README.md index 6bf3d3ecd86d2..ddfa33f2481da 100644 --- a/packages/@aws-cdk/aws-cloudfront/README.md +++ b/packages/@aws-cdk/aws-cloudfront/README.md @@ -286,6 +286,25 @@ const myFunc = new lambda.Function(this, 'MyFunction', { }); ``` +If the stack is not in `us-east-1`, and you need references from different applications on the same account, +you can also set a specific stack ID for each Lamba@Edge. + +```ts +const myFunc1 = new cloudfront.experimental.EdgeFunction(this, 'MyFunction1', { + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler1')), + stackId: 'edge-lambda-stack-id-1' +}); + +const myFunc2 = new cloudfront.experimental.EdgeFunction(this, 'MyFunction2', { + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler2')), + stackId: 'edge-lambda-stack-id-2' +}); +``` + Lambda@Edge functions can also be associated with additional behaviors, either at or after Distribution creation time. diff --git a/packages/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.ts b/packages/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.ts index 1a93ec84c0584..2d62fe984f9b9 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.ts @@ -16,7 +16,14 @@ import { Construct } from 'constructs'; * Properties for creating a Lambda@Edge function * @experimental */ -export interface EdgeFunctionProps extends lambda.FunctionProps { } +export interface EdgeFunctionProps extends lambda.FunctionProps { + /** + * The stack ID of Lambda@Edge function. + * + * @default - `edge-lambda-stack-${region}` + */ + readonly stackId?: string; +} /** * A Lambda@Edge function. @@ -139,10 +146,10 @@ export class EdgeFunction extends Resource implements lambda.IVersion { } /** Create a support stack and function in us-east-1, and a SSM reader in-region */ - private createCrossRegionFunction(id: string, props: lambda.FunctionProps): FunctionConfig { + private createCrossRegionFunction(id: string, props: EdgeFunctionProps): FunctionConfig { const parameterNamePrefix = 'EdgeFunctionArn'; const parameterName = `${parameterNamePrefix}${id}`; - const functionStack = this.edgeStack(); + const functionStack = this.edgeStack(props.stackId); const edgeFunction = new lambda.Function(functionStack, id, props); addEdgeLambdaToRoleTrustStatement(edgeFunction.role!); @@ -193,7 +200,7 @@ export class EdgeFunction extends Resource implements lambda.IVersion { return resource.getAttString('FunctionArn'); } - private edgeStack(): Stack { + private edgeStack(stackId?: string): Stack { const stage = this.node.root; if (!stage || !Stage.isStage(stage)) { throw new Error('stacks which use EdgeFunctions must be part of a CDK app or stage'); @@ -203,7 +210,7 @@ export class EdgeFunction extends Resource implements lambda.IVersion { throw new Error('stacks which use EdgeFunctions must have an explicitly set region'); } - const edgeStackId = `edge-lambda-stack-${region}`; + const edgeStackId = stackId ?? `edge-lambda-stack-${region}`; let edgeStack = stage.node.tryFindChild(edgeStackId) as Stack; if (!edgeStack) { edgeStack = new Stack(stage, edgeStackId, { diff --git a/packages/@aws-cdk/aws-cloudfront/test/experimental/edge-function.test.ts b/packages/@aws-cdk/aws-cloudfront/test/experimental/edge-function.test.ts index faeffde9edc51..b11ab1bbeb4dd 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/experimental/edge-function.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/experimental/edge-function.test.ts @@ -146,6 +146,18 @@ describe('stacks', () => { const fnStack = getFnStack(); expect(fnStack).toCountResources('AWS::Lambda::Function', 2); }); + + test('can set the stack id for each function', () => { + const fn1StackId = 'edge-lambda-stack-testregion-1'; + new cloudfront.experimental.EdgeFunction(stack, 'MyFn1', defaultEdgeFunctionProps(fn1StackId)); + const fn2StackId = 'edge-lambda-stack-testregion-2'; + new cloudfront.experimental.EdgeFunction(stack, 'MyFn2', defaultEdgeFunctionProps(fn2StackId)); + + const fn1Stack = app.node.findChild(fn1StackId) as cdk.Stack; + expect(fn1Stack).toCountResources('AWS::Lambda::Function', 1); + const fn2Stack = app.node.findChild(fn2StackId) as cdk.Stack; + expect(fn2Stack).toCountResources('AWS::Lambda::Function', 1); + }); }); test('addAlias() creates alias in function stack', () => { @@ -189,11 +201,12 @@ test('metric methods', () => { } }); -function defaultEdgeFunctionProps() { +function defaultEdgeFunctionProps(stackId?: string) { return { code: lambda.Code.fromInline('foo'), handler: 'index.handler', runtime: lambda.Runtime.NODEJS_12_X, + stackId: stackId ?? 'edge-lambda-stack-testregion', }; } diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda-cross-region.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda-cross-region.expected.json index aafb7b7e3982f..412498ec96a3f 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda-cross-region.expected.json +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda-cross-region.expected.json @@ -127,6 +127,22 @@ "CustomCrossRegionStringParameterReaderCustomResourceProviderRole71CD6825" ] }, + "Lambda2ArnReader5ACFBE1F": { + "Type": "Custom::CrossRegionStringParameterReader", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCrossRegionStringParameterReaderCustomResourceProviderHandler65B5F33A", + "Arn" + ] + }, + "Region": "us-east-1", + "ParameterName": "EdgeFunctionArnLambda2", + "RefreshToken": "8f81ceb404ac454f09648e62822d9ca9" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, "DistB3B78991": { "Type": "AWS::CloudFront::Distribution", "Properties": { @@ -162,6 +178,42 @@ ] } } + }, + "Dist286EC08DF": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "CachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad", + "Compress": true, + "LambdaFunctionAssociations": [ + { + "EventType": "origin-request", + "LambdaFunctionARN": { + "Fn::GetAtt": [ + "Lambda2ArnReader5ACFBE1F", + "FunctionArn" + ] + } + } + ], + "TargetOriginId": "integdistributionlambdacrossregionDist2Origin14F08376D", + "ViewerProtocolPolicy": "allow-all" + }, + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Origins": [ + { + "CustomOriginConfig": { + "OriginProtocolPolicy": "https-only" + }, + "DomainName": "www.example2.com", + "Id": "integdistributionlambdacrossregionDist2Origin14F08376D" + } + ] + } + } } }, "Parameters": { @@ -257,5 +309,84 @@ } } } + }, + { + "Resources": { + "Lambda2ServiceRole31A072E1": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "edgelambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "Lambda217CFB423": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "Lambda2ServiceRole31A072E1", + "Arn" + ] + }, + "Runtime": "nodejs10.x" + }, + "DependsOn": [ + "Lambda2ServiceRole31A072E1" + ] + }, + "Lambda2CurrentVersion72012B74b9eef8becb98501bc795baca3c6169c4": { + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "Lambda217CFB423" + } + } + }, + "Lambda2Parameter3444E17A": { + "Type": "AWS::SSM::Parameter", + "Properties": { + "Type": "String", + "Value": { + "Ref": "Lambda2CurrentVersion72012B74b9eef8becb98501bc795baca3c6169c4" + }, + "Name": "EdgeFunctionArnLambda2" + } + } + } } ] \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda-cross-region.ts b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda-cross-region.ts index 5814ab3d6ce59..df2c26aecfd26 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda-cross-region.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-lambda-cross-region.ts @@ -5,7 +5,9 @@ import * as cloudfront from '../lib'; import { TestOrigin } from './test-origin'; const app = new cdk.App(); -const stack = new cdk.Stack(app, 'integ-distribution-lambda-cross-region', { env: { region: 'eu-west-1' } }); + +const region = 'eu-west-1'; +const stack = new cdk.Stack(app, 'integ-distribution-lambda-cross-region', { env: { region: region } }); const lambdaFunction = new cloudfront.experimental.EdgeFunction(stack, 'Lambda', { code: lambda.Code.fromInline('foo'), @@ -13,6 +15,13 @@ const lambdaFunction = new cloudfront.experimental.EdgeFunction(stack, 'Lambda', runtime: lambda.Runtime.NODEJS_10_X, }); +const lambdaFunction2 = new cloudfront.experimental.EdgeFunction(stack, 'Lambda2', { + code: lambda.Code.fromInline('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + stackId: `edge-lambda-stack-${region}-2`, +}); + new cloudfront.Distribution(stack, 'Dist', { defaultBehavior: { origin: new TestOrigin('www.example.com'), @@ -24,4 +33,15 @@ new cloudfront.Distribution(stack, 'Dist', { }, }); +new cloudfront.Distribution(stack, 'Dist2', { + defaultBehavior: { + origin: new TestOrigin('www.example2.com'), + cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED, + edgeLambdas: [{ + functionVersion: lambdaFunction2.currentVersion, + eventType: cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST, + }], + }, +}); + app.synth();