|
| 1 | +import * as cdk from 'aws-cdk-lib'; |
| 2 | +import { Construct } from 'constructs'; |
| 3 | +import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'; |
| 4 | +import * as lambda from 'aws-cdk-lib/aws-lambda'; |
| 5 | +import * as lambdaNode from 'aws-cdk-lib/aws-lambda-nodejs'; |
| 6 | +import path = require('path'); |
| 7 | +import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam'; |
| 8 | + |
| 9 | +export class CdkStack extends cdk.Stack { |
| 10 | + constructor(scope: Construct, id: string, props?: cdk.StackProps) { |
| 11 | + super(scope, id, props); |
| 12 | + |
| 13 | + // Create the Lambda |
| 14 | + const simpleLambda = new lambdaNode.NodejsFunction(this, 'simpleLambda', { |
| 15 | + entry: 'lambda/handler.ts', |
| 16 | + handler: 'handler', |
| 17 | + runtime: lambda.Runtime.NODEJS_18_X, |
| 18 | + functionName: 'simpleLambda' |
| 19 | + }); |
| 20 | + |
| 21 | + const authFunction = this.createAuthEdgeFunction(simpleLambda.functionArn); |
| 22 | + |
| 23 | + // Configure the Lambda URL |
| 24 | + const lambdaUrl = simpleLambda.addFunctionUrl({ |
| 25 | + authType: lambda.FunctionUrlAuthType.AWS_IAM, |
| 26 | + }); |
| 27 | + |
| 28 | + // Create the CloudFront distribution redirecting calls to the Lambda URL |
| 29 | + const cfDistribution = new cloudfront.CloudFrontWebDistribution(this, 'CFDistribution', { |
| 30 | + originConfigs: [ |
| 31 | + { |
| 32 | + customOriginSource: { |
| 33 | + domainName: this.getURLDomain(lambdaUrl), |
| 34 | + originProtocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY, |
| 35 | + }, |
| 36 | + behaviors: [{ |
| 37 | + isDefaultBehavior: true, |
| 38 | + allowedMethods: cloudfront.CloudFrontAllowedMethods.ALL, |
| 39 | + lambdaFunctionAssociations: [{ |
| 40 | + eventType: cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST, |
| 41 | + lambdaFunction: authFunction.currentVersion, |
| 42 | + includeBody: true |
| 43 | + }], |
| 44 | + }], |
| 45 | + } |
| 46 | + ], |
| 47 | + }); |
| 48 | + |
| 49 | + new cdk.CfnOutput(this, 'CloudFrontDistributionURL', { |
| 50 | + value: cfDistribution.distributionDomainName, |
| 51 | + }); |
| 52 | + |
| 53 | + } |
| 54 | + |
| 55 | + /** |
| 56 | + * Extracts the domain from a Lambda URL |
| 57 | + * |
| 58 | + * Example: https://my-lambda.execute-api.us-east-1.amazonaws.com/ -> my-lambda.execute-api.us-east-1.amazonaws.com |
| 59 | + */ |
| 60 | + getURLDomain(lambdaUrl: lambda.FunctionUrl) { |
| 61 | + return cdk.Fn.select(2, cdk.Fn.split('/', lambdaUrl.url)); |
| 62 | + } |
| 63 | + |
| 64 | + private createAuthEdgeFunction(functionArn: string) { |
| 65 | + const authFunction = new cloudfront.experimental.EdgeFunction(this, 'AuthLambdaEdge', { |
| 66 | + handler: 'authEdge.handler', |
| 67 | + runtime: lambda.Runtime.NODEJS_16_X, |
| 68 | + code: lambda.Code.fromAsset(path.join(__dirname, '../lambda-edge'), { |
| 69 | + bundling: { |
| 70 | + command: [ |
| 71 | + "bash", |
| 72 | + "-c", |
| 73 | + "npm install && cp -rT /asset-input/ /asset-output/", |
| 74 | + ], |
| 75 | + image: lambda.Runtime.NODEJS_16_X.bundlingImage, |
| 76 | + user: "root", |
| 77 | + }, |
| 78 | + }), |
| 79 | + currentVersionOptions: { |
| 80 | + removalPolicy: cdk.RemovalPolicy.DESTROY |
| 81 | + }, |
| 82 | + timeout: cdk.Duration.seconds(7), |
| 83 | + }); |
| 84 | + |
| 85 | + authFunction.addToRolePolicy(new PolicyStatement({ |
| 86 | + sid: 'AllowInvokeFunctionUrl', |
| 87 | + effect: Effect.ALLOW, |
| 88 | + actions: ['lambda:InvokeFunctionUrl'], |
| 89 | + resources: [functionArn], |
| 90 | + conditions: { |
| 91 | + "StringEquals": { "lambda:FunctionUrlAuthType": "AWS_IAM" } |
| 92 | + } |
| 93 | + })); |
| 94 | + return authFunction; |
| 95 | + } |
| 96 | +} |
0 commit comments