Skip to content

Commit

Permalink
feat(cloudfront): allow to specify stack ID for Lambda@Edge (#12163)
Browse files Browse the repository at this point in the history
Closes #12136

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
sano307 committed Jan 4, 2021
1 parent d7062f0 commit 049e70c
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 7 deletions.
19 changes: 19 additions & 0 deletions packages/@aws-cdk/aws-cloudfront/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
17 changes: 12 additions & 5 deletions packages/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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!);
Expand Down Expand Up @@ -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');
Expand All @@ -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, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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',
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -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": {
Expand Down Expand Up @@ -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"
}
}
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,23 @@ 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'),
handler: 'index.handler',
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'),
Expand All @@ -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();

0 comments on commit 049e70c

Please sign in to comment.