aws-cdk: @aws-cdk/aws-cloudfront: Unable to deploy with EdgeFunction

I can successfully deploy a CloudFront distribution using the CDK. However, as soon as I try to add a Lambda@Edge function to this, my deployment fails.

Reproduction Steps

Here is the Typescript code I used to deploy:

import * as cdk from '@aws-cdk/core';
import * as cdkCertificateManager from '@aws-cdk/aws-certificatemanager';
import * as cdkCloudfront from '@aws-cdk/aws-cloudfront';
import * as cdkLambda from '@aws-cdk/aws-lambda';
import * as cdkOrigins from '@aws-cdk/aws-cloudfront-origins';
import * as cdkRoute53 from '@aws-cdk/aws-route53';
import * as cdkRoute53Targets from '@aws-cdk/aws-route53-targets';

import { CustomStackProps } from '@resilientplc/hubble-cdk-utils';

import { CDK_UTILS } from '../config/config';

import { CfProps } from './cf-props';

export class CfConstruct extends cdk.Construct {
  constructor(
    stack: cdk.Stack,
    {
      stage,
      env,
      tags,
      customProps: { hostedZoneId, hostedZoneDomainName, certificateArn, vpcName, graphQlEndpoint, apiR53RecordName, hubbleAlertsS3BucketName }
    }: CustomStackProps<CfProps>
  ) {
    super(stack, 'CloudFrontConstruct');

    const lambdaAtEdgeFunction = new cdkCloudfront.experimental.EdgeFunction(this, 'LambdaAtEdgeFunction', {
      runtime: cdkLambda.Runtime.NODEJS_12_X,
      code: cdkLambda.Code.fromAsset('.build/request-routing-lambda'),
      handler: 'request-routing-lambda.requestRoutingLambda'
    });

    const distribution = new cdkCloudfront.Distribution(this, 'CloudFrontDist', {
      comment: `${CDK_UTILS.resourceName('cloudfront')}: CloudFront distribution for GraphQL endpoint`,
      domainNames: [`${apiR53RecordName}.${hostedZoneDomainName}`],
      certificate: cdkCertificateManager.Certificate.fromCertificateArn(this, 'Certificate', certificateArn),
      minimumProtocolVersion: cdkCloudfront.SecurityPolicyProtocol.TLS_V1,
      priceClass: cdkCloudfront.PriceClass.PRICE_CLASS_100,
      defaultBehavior: {
        allowedMethods: cdkCloudfront.AllowedMethods.ALLOW_ALL,
        cachePolicy: new cdkCloudfront.CachePolicy(this, 'CloudFrontCachePolicy', {
          cachePolicyName: CDK_UTILS.resourceName('cloudfront-cache-policy'),
          headerBehavior: cdkCloudfront.CacheHeaderBehavior.allowList('Authorization'),
          defaultTtl: cdk.Duration.seconds(0),
          minTtl: cdk.Duration.seconds(0),
          maxTtl: cdk.Duration.seconds(1)
        }),
        edgeLambdas: [
          {
            eventType: cdkCloudfront.LambdaEdgeEventType.ORIGIN_REQUEST,
            functionVersion: lambdaAtEdgeFunction.currentVersion
          }
        ],
        origin: new cdkOrigins.HttpOrigin(cdk.Fn.select(2, cdk.Fn.split('/', graphQlEndpoint)), {
          originPath: '',
          originSslProtocols: [cdkCloudfront.OriginSslPolicy.TLS_V1, cdkCloudfront.OriginSslPolicy.TLS_V1_1, cdkCloudfront.OriginSslPolicy.TLS_V1_2]
        }),
        originRequestPolicy: new cdkCloudfront.OriginRequestPolicy(this, 'CloudFrontOriginRequestPolicy', {
          originRequestPolicyName: CDK_UTILS.resourceName('cloudfront-origin-request-policy'),
          headerBehavior: cdkCloudfront.OriginRequestHeaderBehavior.allowList('Origin', 'Access-Control-Request-Method', 'Access-Control-Request-Headers'),
          queryStringBehavior: cdkCloudfront.OriginRequestQueryStringBehavior.all(),
          cookieBehavior: cdkCloudfront.OriginRequestCookieBehavior.all()
        }),
        viewerProtocolPolicy: cdkCloudfront.ViewerProtocolPolicy.HTTPS_ONLY
      }
    });

    const zone = cdkRoute53.HostedZone.fromHostedZoneAttributes(this, 'R53HostedZone', { hostedZoneId, zoneName: hostedZoneDomainName });

    new cdkRoute53.ARecord(this, 'ARecord', {
      zone,
      recordName: apiR53RecordName,
      target: cdkRoute53.RecordTarget.fromAlias(new cdkRoute53Targets.CloudFrontTarget(distribution))
    });
  }
}

What did you expect to happen?

I expected it to deploy without errors.

What actually happened?

The deployment failed with the following errors:

cf (users-api-cf-dev): deploying...
[0%] start: Publishing 45b7ed524ce2b119dd4f2b8642ae8bfaf0df45bc6bd705072ae4ee6d1a999241:current
[100%] success: Published 45b7ed524ce2b119dd4f2b8642ae8bfaf0df45bc6bd705072ae4ee6d1a999241:current
users-api-cf-dev: creating CloudFormation changeset...
 0/7 | 13:11:30 | UPDATE_IN_PROGRESS   | AWS::CloudFormation::Stack               | users-api-cf-dev User Initiated
 1/7 | 13:11:57 | UPDATE_IN_PROGRESS   | AWS::CDK::Metadata                       | cf/CDKMetadata/Default (CDKMetadata) 
 1/7 | 13:11:57 | CREATE_IN_PROGRESS   | AWS::IAM::Role                           | cf/Custom::CrossRegionStringParameterReaderCustomResourceProvider/Role (CustomCrossRegionStringParameterReaderCustomResourceProviderRole71CD6825) 
 1/7 | 13:11:57 | CREATE_IN_PROGRESS   | AWS::IAM::Role                           | cf/Custom::CrossRegionStringParameterReaderCustomResourceProvider/Role (CustomCrossRegionStringParameterReaderCustomResourceProviderRole71CD6825) Resource creation Initiated
 1/7 | 13:11:58 | UPDATE_COMPLETE      | AWS::CDK::Metadata                       | cf/CDKMetadata/Default (CDKMetadata) 
 2/7 | 13:12:16 | CREATE_COMPLETE      | AWS::IAM::Role                           | cf/Custom::CrossRegionStringParameterReaderCustomResourceProvider/Role (CustomCrossRegionStringParameterReaderCustomResourceProviderRole71CD6825) 
 3/7 | 13:12:18 | CREATE_IN_PROGRESS   | AWS::Lambda::Function                    | cf/Custom::CrossRegionStringParameterReaderCustomResourceProvider/Handler (CustomCrossRegionStringParameterReaderCustomResourceProviderHandler65B5F33A) 
 3/7 | 13:12:19 | CREATE_IN_PROGRESS   | AWS::Lambda::Function                    | cf/Custom::CrossRegionStringParameterReaderCustomResourceProvider/Handler (CustomCrossRegionStringParameterReaderCustomResourceProviderHandler65B5F33A) Resource creation Initiated
 3/7 | 13:12:19 | CREATE_COMPLETE      | AWS::Lambda::Function                    | cf/Custom::CrossRegionStringParameterReaderCustomResourceProvider/Handler (CustomCrossRegionStringParameterReaderCustomResourceProviderHandler65B5F33A) 
 3/7 | 13:12:21 | CREATE_IN_PROGRESS   | Custom::CrossRegionStringParameterReader | cf/CloudFrontConstruct/LambdaAtEdgeFunction/ArnReader/Default (CloudFrontConstructLambdaAtEdgeFunctionArnReader5C1A0822) 
 3/7 | 13:12:27 | CREATE_IN_PROGRESS   | Custom::CrossRegionStringParameterReader | cf/CloudFrontConstruct/LambdaAtEdgeFunction/ArnReader/Default (CloudFrontConstructLambdaAtEdgeFunctionArnReader5C1A0822) Resource creation Initiated
 3/7 | 13:12:27 | CREATE_FAILED        | Custom::CrossRegionStringParameterReader | cf/CloudFrontConstruct/LambdaAtEdgeFunction/ArnReader/Default (CloudFrontConstructLambdaAtEdgeFunctionArnReader5C1A0822) Failed to create resource. ParameterNotFound: null
    at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:52:27)
    at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
    at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
    at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:688:14)
    at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:690:12)
    at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:116:18)
        new CustomResource (/Users/anjum/Documents/workspace/resilient/users-api/node_modules/@aws-cdk/core/lib/custom-resource.js:30:25)
        \_ EdgeFunction.createCrossRegionArnReader (/Users/anjum/Documents/workspace/resilient/users-api/node_modules/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.js:255:26)
        \_ EdgeFunction.createCrossRegionFunction (/Users/anjum/Documents/workspace/resilient/users-api/node_modules/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.js:232:30)
        \_ new EdgeFunction (/Users/anjum/Documents/workspace/resilient/users-api/node_modules/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.js:43:20)
        \_ new CfConstruct (/Users/anjum/Documents/workspace/resilient/users-api/.build/stacks/cf/cf-construct.js:35:38)
        \_ new CfStack (/Users/anjum/Documents/workspace/resilient/users-api/.build/stacks/cf/cf-stack.js:28:9)
        \_ Object.<anonymous> (/Users/anjum/Documents/workspace/resilient/users-api/.build/stacks/stacks.js:52:1)
        \_ Module._compile (internal/modules/cjs/loader.js:774:30)
        \_ Object.Module._extensions..js (internal/modules/cjs/loader.js:785:10)
        \_ Module.load (internal/modules/cjs/loader.js:641:32)
        \_ Function.Module._load (internal/modules/cjs/loader.js:556:12)
        \_ Function.Module.runMain (internal/modules/cjs/loader.js:837:10)
        \_ internal/main/run_main_module.js:17:11
 3/7 | 13:12:28 | UPDATE_ROLLBACK_IN_P | AWS::CloudFormation::Stack               | users-api-cf-dev The following resource(s) failed to create: [CloudFrontConstructLambdaAtEdgeFunctionArnReader5C1A0822]. 
...

Environment

  • CDK CLI Version : 1.91.0
  • Framework Version: 1.91.0
  • Node.js Version: v12.4.0
  • OS : macOS Big Sur Version 11.2.3 (20D91)
  • Language (Version): Typescript (4.2.2)

Other

I have also raised this as a question in the slack channel: https://cdk-dev.slack.com/archives/C018XT6REKT/p1615987569068600


This is 🐛 Bug Report

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 26 (7 by maintainers)

Most upvoted comments

Ping me if you want to discuss this further! I’ll be closing this issue now since the original issue has been resolved.

If I remove the --exclusively flag then will they all be deployed rather only the one we specify via the cf name directly after cdk deploy?

If you specify the stack, but remove the --exclusively flag, then the stack you specify, and all stacks which are necessary to deploy the stack you specified, will be deployed.

We have some stacks in there that pass along their exported stack variables to other stacks.

If Stack A exports a variable and Stack B consumes it, then Stack B is dependent on Stack A. If you deploy Stack B, Stack A will be automatically deployed (without the exclusively flag); if you deploy Stack A, Stack B will not be deployed.

@ekeyser Thank you for taking the time to look into this issue - greatly appreciated 👍