From c11b70ce609e91afc6e5ab9079330a8f4e2e6b5b Mon Sep 17 00:00:00 2001 From: Markus Wolf Date: Thu, 13 Jun 2019 17:10:43 +0200 Subject: [PATCH] feat(cloudfront): define lambda@edge as resolvable resource This declaration is required for deploying custom resources as lamdba association, which by itself is required to deploy a lambda@edge for a stack which is in a different region as 'us-east-1'. Relates to #1575 --- .../aws-cloudfront/lib/web_distribution.ts | 27 ++- .../aws-cloudfront/test/test.basic.ts | 167 +++++++++++++++++- 2 files changed, 187 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts index 23feb3e48658b..cff241e859a74 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts @@ -363,9 +363,9 @@ export interface LambdaFunctionAssociation { readonly eventType: LambdaEdgeEventType; /** - * A version of the lambda to associate + * A version of the lambda to associate or a resolvable resource */ - readonly lambdaFunction: lambda.IVersion; + readonly lambdaFunction: lambda.IVersion | cdk.IResolvable; } export enum LambdaEdgeEventType { @@ -719,6 +719,10 @@ export class CloudFrontWebDistribution extends cdk.Construct implements IDistrib } private toBehavior(input: BehaviorWithOrigin, protoPolicy?: ViewerProtocolPolicy) { + function isResolvable(resolvableOrVersion: cdk.IResolvable | lambda.IVersion): resolvableOrVersion is cdk.IResolvable { + return Boolean((resolvableOrVersion as cdk.IResolvable).resolve); + } + let toReturn = { allowedMethods: this.METHOD_LOOKUP_MAP[input.allowedMethods || CloudFrontAllowedMethods.GET_HEAD], cachedMethods: this.METHOD_LOOKUP_MAP[input.cachedMethods || CloudFrontAllowedCachedMethods.GET_HEAD], @@ -737,10 +741,21 @@ export class CloudFrontWebDistribution extends cdk.Construct implements IDistrib if (input.lambdaFunctionAssociations) { toReturn = Object.assign(toReturn, { lambdaFunctionAssociations: input.lambdaFunctionAssociations - .map(fna => ({ - eventType: fna.eventType, - lambdaFunctionArn: fna.lambdaFunction && fna.lambdaFunction.versionArn, - })) + .map(fna => { + if (!fna.lambdaFunction) { + return undefined; + } + if (isResolvable(fna.lambdaFunction)) { + return { + eventType: fna.eventType, + lambdaFunctionArn: fna.lambdaFunction, + }; + } + return { + eventType: fna.eventType, + lambdaFunctionArn: fna.lambdaFunction.versionArn, + }; + }) }); } return toReturn; diff --git a/packages/@aws-cdk/aws-cloudfront/test/test.basic.ts b/packages/@aws-cdk/aws-cloudfront/test/test.basic.ts index 37fa731d04e03..e9fb02f330c69 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/test.basic.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/test.basic.ts @@ -1,8 +1,9 @@ import { expect } from '@aws-cdk/assert'; +import lambda = require('@aws-cdk/aws-lambda'); import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; -import { CloudFrontWebDistribution, ViewerProtocolPolicy } from '../lib'; +import { CloudFrontWebDistribution, LambdaEdgeEventType, ViewerProtocolPolicy } from '../lib'; // tslint:disable:object-literal-key-quotes @@ -322,4 +323,168 @@ export = { test.done(); }, + 'distribution with resolvable lambda-association'(test: Test) { + const stack = new cdk.Stack(); + const sourceBucket = new s3.Bucket(stack, 'Bucket'); + + const lambdaFunction = new lambda.Function(stack, 'Lambda', { + code: lambda.Code.inline('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.Nodejs810 + }); + + const lambdaVersion = new lambda.Version(stack, 'LambdaVersion', { + lambda: lambdaFunction + }); + + new CloudFrontWebDistribution(stack, 'AnAmazingWebsiteProbably', { + originConfigs: [ + { + s3OriginSource: { + s3BucketSource: sourceBucket + }, + behaviors: [ + { + isDefaultBehavior: true, + lambdaFunctionAssociations: [{ + eventType: LambdaEdgeEventType.OriginRequest, + lambdaFunction: cdk.Token.asAny(lambdaVersion.versionArn) + }] + } + ] + } + ] + }); + + expect(stack).toMatch({ + "Resources": { + "Bucket83908E77": { + "Type": "AWS::S3::Bucket", + "DeletionPolicy": "Retain", + }, + "LambdaServiceRoleA8ED4D3B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "lambda.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "LambdaD247545B": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "LambdaServiceRoleA8ED4D3B", + "Arn" + ] + }, + "Runtime": "nodejs8.10" + }, + "DependsOn": [ + "LambdaServiceRoleA8ED4D3B" + ] + }, + "LambdaVersionFA49E61E": { + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "LambdaD247545B" + } + } + }, + "AnAmazingWebsiteProbablyCFDistribution47E3983B": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultRootObject": "index.html", + "Origins": [ + { + "DomainName": { + "Fn::GetAtt": [ + "Bucket83908E77", + "RegionalDomainName" + ] + }, + "Id": "origin1", + "S3OriginConfig": {} + } + ], + "ViewerCertificate": { + "CloudFrontDefaultCertificate": true + }, + "PriceClass": "PriceClass_100", + "DefaultCacheBehavior": { + "AllowedMethods": [ + "GET", + "HEAD" + ], + "CachedMethods": [ + "GET", + "HEAD" + ], + "TargetOriginId": "origin1", + "ViewerProtocolPolicy": "redirect-to-https", + "ForwardedValues": { + "QueryString": false, + "Cookies": { "Forward": "none" } + }, + "LambdaFunctionAssociations": [ + { + "EventType": "origin-request", + "LambdaFunctionARN": { + "Ref": "LambdaVersionFA49E61E" + } + } + ] + }, + "Enabled": true, + "IPV6Enabled": true, + "HttpVersion": "http2", + "CacheBehaviors": [] + } + } + } + } + }); + test.done(); + }, + };