この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、CX事業本部 IoT事業部の若槻です。
今回は、AWS CDKで、Lambda@Edge関数の作成または更新時に自動的に関数の最新バージョンの発行とCloudFront Distributionへの紐付けを行う方法を確認してみました。
Lambda@Edge関数の作成または更新時の手順
CloudFront Distributionに紐付けているLambda@Edge関数を作成または更新する際は、次の手順を踏む必要があります。
- Lambda@Edge関数の
$LATEST
バージョンを作成または更新する。 - 関数の最新バージョンを発行する。
- 発行した関数の最新バージョンをCloudFront Distributionに紐付けする。
Lambda@Edge関数およびCloudFront DistributionをAWS CDKで構築している場合に、上記の手順を行う方法を確認してみます。
環境
- typescript@3.9.10
- aws-cdk@1.145.0
やってみた
スタックの定義は次のようになります。ポイントとしては、Lambda@Edge関数との紐付け設定(edgeLambdas
)でcurrentVersion
により関数バージョンを指定することです。
lib/aws-cdk-app-stack.ts
import * as cdk from '@aws-cdk/core';
import * as cloudfront from '@aws-cdk/aws-cloudfront';
import * as iam from '@aws-cdk/aws-iam';
import * as s3 from '@aws-cdk/aws-s3';
import * as s3deploy from '@aws-cdk/aws-s3-deployment';
import * as cloudfront_origins from '@aws-cdk/aws-cloudfront-origins';
import * as path from 'path';
import * as lambda from '@aws-cdk/aws-lambda';
export class AwsCdkAppStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props: cdk.StackProps) {
super(scope, id, props);
const websiteBucket = new s3.Bucket(this, 'WebsiteBucket', {
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
const originAccessIdentity = new cloudfront.OriginAccessIdentity(
this,
'OriginAccessIdentity',
{
comment: 'website-distribution-originAccessIdentity',
}
);
const webSiteBucketPolicyStatement = new iam.PolicyStatement({
actions: ['s3:GetObject'],
effect: iam.Effect.ALLOW,
principals: [
new iam.CanonicalUserPrincipal(
originAccessIdentity.cloudFrontOriginAccessIdentityS3CanonicalUserId
),
],
resources: [`${websiteBucket.bucketArn}/*`],
});
websiteBucket.addToResourcePolicy(webSiteBucketPolicyStatement);
//Lambda@Edge関数
const websiteAddHeaderFunction = new cloudfront.experimental.EdgeFunction(
this,
'WebsiteAddHeaderFunction',
{
code: lambda.Code.fromAsset(
path.join(__dirname, '../src/lambda/website-add-header')
),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_14_X,
}
);
const distribution = new cloudfront.Distribution(this, 'Distribution', {
comment: 'website-distribution',
defaultRootObject: 'index.html',
errorResponses: [
{
ttl: cdk.Duration.seconds(300),
httpStatus: 403,
responseHttpStatus: 403,
responsePagePath: '/error.html',
},
{
ttl: cdk.Duration.seconds(300),
httpStatus: 404,
responseHttpStatus: 404,
responsePagePath: '/error.html',
},
],
defaultBehavior: {
allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD,
cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD,
cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
origin: new cloudfront_origins.S3Origin(websiteBucket, {
originAccessIdentity,
}),
//Lambda@Edge関数との紐付け設定
edgeLambdas: [
{
eventType: cloudfront.LambdaEdgeEventType.ORIGIN_RESPONSE,
functionVersion: websiteAddHeaderFunction.currentVersion,
},
],
},
priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL,
});
new s3deploy.BucketDeployment(this, 'WebsiteDeploy', {
sources: [
s3deploy.Source.data(
'/index.html',
`<html><body><h1>Hello World</h1></body></html>`
),
s3deploy.Source.data(
'/error.html',
`<html><body><h1>Error!!!!!!!!!!!!!</h1></body></html>`
),
s3deploy.Source.data('/favicon.ico', ''),
],
destinationBucket: websiteBucket,
distribution: distribution,
distributionPaths: ['/*'],
});
}
}
動作
初回作成時
まずは初回作成時の動作を見てみます。
CDKデプロイを行い、Lambda@Edge関数およびDistributionへの紐付けを作成します。
作成後に、関数のバージョン一覧を確認すると、$LATEST
と1
が発行されています。
$ aws lambda list-versions-by-function \
--region us-east-1 \
--function-name ${functionName}
{
"Versions": [
{
"FunctionName": "websiteAddHeaderFunction",
"FunctionArn": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:websiteAddHeaderFunction:$LATEST",
"Runtime": "nodejs14.x",
"Role": "arn:aws:iam::XXXXXXXXXXXX:role/AwsCdkAppStack2-websiteAddHeaderFunctionFnServiceR-1LJSCURJNT1ZD",
"Handler": "index.handler",
"CodeSize": 308,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2022-02-27T15:41:58.463+0000",
"CodeSha256": "8Ux3E5BZPzZ8zf53zCb5sJ5R57r5iKCT/GXhW7k6qSA=",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "8563719c-ea6a-4460-a150-20be01c2dc2a",
"PackageType": "Zip",
"Architectures": [
"x86_64"
]
},
{
"FunctionName": "websiteAddHeaderFunction",
"FunctionArn": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:websiteAddHeaderFunction:1",
"Runtime": "nodejs14.x",
"Role": "arn:aws:iam::XXXXXXXXXXXX:role/AwsCdkAppStack2-websiteAddHeaderFunctionFnServiceR-1LJSCURJNT1ZD",
"Handler": "index.handler",
"CodeSize": 308,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2022-02-27T15:42:19.808+0000",
"CodeSha256": "8Ux3E5BZPzZ8zf53zCb5sJ5R57r5iKCT/GXhW7k6qSA=",
"Version": "1",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "0c280f9f-5d63-464a-b3c2-93d2370147c2",
"PackageType": "Zip",
"Architectures": [
"x86_64"
]
}
]
}
またDistributionの関数紐付け設定を見ると、関数のバージョン1
が設定されています。
$ aws cloudfront get-distribution \
--region us-east-1 \
--id ${distributionId} \
--query 'Distribution.DistributionConfig.DefaultCacheBehavior.LambdaFunctionAssociations'
{
"Quantity": 1,
"Items": [
{
"LambdaFunctionARN": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:websiteAddHeaderFunction:1",
"EventType": "viewer-response",
"IncludeBody": false
}
]
}
関数更新時
Lambda@Edge関数を何かしら更新した際の動きも見てみます。関数のソースコードに変更を加え、CDKデプロイします。
デプロイ後の関数のバージョン一覧を確認すると、新しく2
が発行されています。
$ aws lambda list-versions-by-function \
--region us-east-1 \
--function-name ${functionName} \
--query 'Versions[2]'
{
"FunctionName": "websiteAddHeaderFunction",
"FunctionArn": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:websiteAddHeaderFunction:2",
"Runtime": "nodejs14.x",
"Role": "arn:aws:iam::XXXXXXXXXXXX:role/AwsCdkAppStack2-websiteAddHeaderFunctionFnServiceR-1LJSCURJNT1ZD",
"Handler": "index.handler",
"CodeSize": 321,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2022-02-27T15:56:28.725+0000",
"CodeSha256": "1J6HzcVop9XOdXWhJgUIdjaz585LK6BDhhcUPPYNuRE=",
"Version": "2",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "e6dc5927-155a-49e7-8dc6-2d70d812722a",
"PackageType": "Zip",
"Architectures": [
"x86_64"
]
}
またDistributionの関数紐付け設定を見ると、関数のバージョン2
が設定されています。
$ aws cloudfront get-distribution \
--region us-east-1 \
--id ${distributionId} \
--query 'Distribution.DistributionConfig.DefaultCacheBehavior.LambdaFunctionAssociations'
{
"Quantity": 1,
"Items": [
{
"LambdaFunctionARN": "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:websiteAddHeaderFunction:2",
"EventType": "viewer-response",
"IncludeBody": false
}
]
}
また、関数の設定(タイムアウト値など)を更新した場合でも、同様の動作となることを確認できました。
その他
$LATESTバージョンは紐付けできない
関数の$LATEST
バージョンはDistributionに紐付けできないことを確認してみます。
$LATEST
バージョンの指定はlatestVersion
を使用します。
lib/aws-cdk-app-stack.ts
edgeLambdas: [
{
eventType: cloudfront.LambdaEdgeEventType.ORIGIN_RESPONSE,
functionVersion: websiteAddHeaderFunction.latestVersion,
},
],
変更をCDKデプロイすると、$LATEST
は設定できない旨のエラーとなりました。想定通りの動作です。
$ cdk deploy
/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/aws-cdk-app/node_modules/@aws-cdk/aws-cloudfront/lib/experimental/edge-function.ts:101
throw new Error('$LATEST function version cannot be used for Lambda@Edge');
現在はaddVersionメソッドはDeprecated
aws-lambda/Function
クラスでは、今まで関数の新しいバージョンを発行する際にはaddVersion
を使用してきたかと思いますが、現在はDeprecatedとなっています。そして代わりにcurrentVersion
を使用してバージョンを取得するようにとあります。
⚠️ Deprecated: This method will create an AWS::Lambda::Version resource which snapshots the AWS Lambda function at the time of its creation and it won't get updated when the function changes. Instead, use this.currentVersion to obtain a reference to a version resource that gets automatically recreated when the function configuration (or code) changes.
一方で今回Lambda@Edge関数の作成に使用したaws-cloudfront-origins/experimental/EdgeFunction
では、addVersion
は使えません。currentVersion
などでバージョンを指定する必要があります。
参考
- list-versions-by-function — AWS CLI 2.4.21 Command Reference
- get-distribution — AWS CLI 2.4.21 Command Reference
以上