Aws-cdk: [aws-cloudfront] - Multiple Behaviors & Origins

Created on 2 Aug 2020  路  4Comments  路  Source: aws/aws-cdk

How to create multiple behaviors & origins with stable API?

https://docs.aws.amazon.com/cdk/api/latest/docs/aws-cloudfront-readme.html#cloudfrontwebdistribution-api-stable

Specifically, I'm interested in how to provide behavior for API Gateway.


This is a 馃摃 documentation issue

@aws-cdaws-cloudfront documentation feature-request needs-triage

All 4 comments

I have a CloudFront distribution that serves an S3 bucket and an APIGateway, where the GET requests are cached by CF.

I'm not using the API part right now, but it should still work unless some API changed. I hope this gives you a starting point

cloudfront
````js

export class Frontend extends cdk.Construct {
constructor(scope: cdk.Construct, id: string) {
super(scope, id);

    const hostedZone = route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
        zoneName: 'DOMAIN', //replace 
        hostedZoneId: 'HOSTEDZOMEID', //replace
    });

    const cert = new acm.DnsValidatedCertificate(this, 'Certificate', {
        domainName: 'DOMAIN', //replace
        hostedZone,
    });

    const frontendBucket = new s3.Bucket(this, 'FrontendBucket', {
        websiteIndexDocument: 'index.html',
        publicReadAccess: true,
    });

    const distribution = new cloudfront.CloudFrontWebDistribution(this, 'Distribution', {
        originConfigs: [
            {
                customOriginSource: {
                    domainName: 'API.DOMAIN', //replace
                },
                behaviors: [{
                    pathPattern: '/api/*',
                    compress: true,
                }]
            },
            {
                customOriginSource: {
                    domainName: frontendBucket.bucketWebsiteDomainName,
                    originProtocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
                },
                behaviors : [ {
                    isDefaultBehavior: true,
                    compress: true,
                }]
            }
        ],
        errorConfigurations: [
        ],
        aliasConfiguration: {
            names: ['DOMAIN'], //replace
            acmCertRef: cert.certificateArn,
            sslMethod: SSLMethod.SNI,
            securityPolicy: SecurityPolicyProtocol.TLS_V1_2_2018,
        }
    });

    new s3deploy.BucketDeployment(this, 'DeployWebsite', {
        sources: [s3deploy.Source.asset('PATH_TO_FRONTEND_CODE')],
        destinationBucket: frontendBucket,
        distribution,
    });

    new route53.ARecord(this, 'Alias', {
        zone: hostedZone,
        target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),
    });

    new route53.AaaaRecord(this, 'AliasAAA', {
        zone: hostedZone,
        target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution))
    });

}

}

API, truncated js

export class Api extends cdk.Construct {
constructor(scope: cdk.Construct, id: string, props: DatabaseProps) {
super(scope, id);

    const exampleLambda = new lambda.Function(this, 'ExampleLambda', {
        //...
    })

    const hostedZone = route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
        zoneName: 'DOMAIN', //replace
        hostedZoneId: 'HOSTEDZONEID', //replace
    });
    const cert = new acm.DnsValidatedCertificate(this, 'Certificate', {
        domainName: 'API.DOMAIN', //replace
        hostedZone,
    });

    const api = new apigateway.LambdaRestApi(this, 'api', {
        handler: exampleLambda,
        proxy: false,
        endpointTypes: [apigateway.EndpointType.REGIONAL],
        domainName: {
            domainName: 'API.DOMAIN', //replace
            certificate: cert,
            securityPolicy: apigateway.SecurityPolicy.TLS_1_2,
        }
    });

    new route53.ARecord(this, 'CustomDomainAliasRecord', {
        zone: hostedZone,
        target: route53.RecordTarget.fromAlias(new targets.ApiGateway(api)),
        recordName: 'API.DOMAIN', //replace
    });
    //some more lambda code
}

}

````

Ohh.... yes, using the custom domain name for API gateway is a good workaround.
Thank you!

@DioNNiS can you close the corresponding Stack Overflow issue pls =)

Another alternative is to not set up an A/AAAA Record for api.domain and point the CF Origin to the GW execution url.

You'll need to define your GW DomainName separately and set up the base path mapping there (you don't actually have to do this, but it simplifies things significantly).

const api = new apigateway.RestApi(this, '...', { ... })

const apiDomain = new apigateway.DomainName(this, '...', {
      certificate: props.certificate,
      domainName: '...'
      ...
})

apiDomain.addBasePathMapping(api, { basePath: 'prod', stage: api.deploymentStage });

And for your CF Origin...

new cloudfront.CloudFrontWebDistribution(this, '...', {
      originConfigs: [
        {
          customOriginSource: {
            domainName: `${api.restApiId}.execute-api.${cdk.Stack.of(this).region}.amazonaws.com`,
            originPath: '/prod',
            originProtocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY
...

In my situation (multi-tenancy SaaS) I used an *.[APEX] domain for both API Gateway DomainName and CloudFront CNAME, as well as a wildcard Route53 record to point any *.[APEX] lookup to my CF DomainName.

The official aws-solution-constructs package implements it similarly (takes the api.url value and reduces it to a naked domain): https://github.com/awslabs/aws-solutions-constructs/blob/58f54de71b6aa18b130866f10462f65c2b5a80bd/source/patterns/%40aws-solutions-constructs/core/lib/cloudfront-distribution-defaults.ts#L20

Was this page helpful?
0 / 5 - 0 ratings