この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、CX事業本部の若槻です。
今回は、Lambda@Edge FunctionのConstructを含んだAWS CDKスタックの削除でエラーが発生して失敗する際の対処についてです。
エラーが発生したCDK定義
CloudFront Distribution + S3 Bucket + Lambda@Edge Functionから成る静的ホスティングされたWebサイトを構築しているスタックです。
lib/sample-app-stack.ts
import * as cdk from "@aws-cdk/core";
import * as cloudfront from "@aws-cdk/aws-cloudfront";
import * as s3 from "@aws-cdk/aws-s3";
import * as s3deploy from "@aws-cdk/aws-s3-deployment";
import * as iam from "@aws-cdk/aws-iam";
import * as lambda from "@aws-cdk/aws-lambda";
export class SampleAppStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const accountId = cdk.Stack.of(this).account;
const websiteBucket = new s3.Bucket(this, "WebsiteBucket", {
bucketName: `sample-app-stack-website-${accountId}`,
websiteErrorDocument: "index.html",
websiteIndexDocument: "index.html",
});
const websiteIdentity = new cloudfront.OriginAccessIdentity(
this,
"WebsiteIdentity",
{
comment: `sample-stack-identity`,
}
);
const webSiteBucketPolicyStatement = new iam.PolicyStatement({
actions: ["s3:GetObject"],
effect: iam.Effect.ALLOW,
principals: [
new iam.CanonicalUserPrincipal(
websiteIdentity.cloudFrontOriginAccessIdentityS3CanonicalUserId
),
],
resources: [`${websiteBucket.bucketArn}/*`],
});
websiteBucket.addToResourcePolicy(webSiteBucketPolicyStatement);
const websiteAddHeaderFunction = new lambda.Function(
this,
"WebsiteAddHeaderFunction",
{
code: lambda.Code.asset("lambda/add-header"),
functionName: "add-header-function",
handler: "index.handler",
role: new iam.Role(this, "AllowLambdaServiceToAssumeRole", {
assumedBy: new iam.CompositePrincipal(
new iam.ServicePrincipal("lambda.amazonaws.com"),
new iam.ServicePrincipal("edgelambda.amazonaws.com")
),
managedPolicies: [
{
managedPolicyArn:
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
},
],
}),
runtime: lambda.Runtime.NODEJS_14_X,
}
);
const websiteAddHeaderFunctionVersion =
websiteAddHeaderFunction.addVersion("v3");
const websiteDistribution = new cloudfront.CloudFrontWebDistribution(
this,
"WebsiteDistribution",
{
comment: `sample-distribution`,
errorConfigurations: [
{
errorCachingMinTtl: 300,
errorCode: 403,
responseCode: 200,
responsePagePath: "/index.html",
},
{
errorCachingMinTtl: 300,
errorCode: 404,
responseCode: 200,
responsePagePath: "/index.html",
},
],
originConfigs: [
{
s3OriginSource: {
s3BucketSource: websiteBucket,
originAccessIdentity: websiteIdentity,
},
behaviors: [
{
isDefaultBehavior: true,
lambdaFunctionAssociations: [
{
eventType: cloudfront.LambdaEdgeEventType.ORIGIN_RESPONSE,
lambdaFunction: lambda.Version.fromVersionArn(
this,
"WebsiteAddHeaderFunctionVersion",
websiteAddHeaderFunctionVersion.functionArn
),
},
],
},
],
},
],
priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL,
}
);
new s3deploy.BucketDeployment(this, "WebsiteDeploy", {
sources: [s3deploy.Source.asset("./web/build")],
destinationBucket: websiteBucket,
distribution: websiteDistribution,
distributionPaths: ["/*"],
});
}
}
スタックを削除しようとすると失敗する
Webサイトを削除するためにcdk destroy
によりスタックを削除しようとすると下記のようにエラーとなり削除が失敗しました。
% cdk destroy
Are you sure you want to delete: SampleAppStack (y/n)? y
SampleAppStack: destroying...
11:13:34 PM | DELETE_FAILED | AWS::Lambda::Function | WebsiteAddHeaderFunctionE7F821C0
Resource handler returned message: "Lambda was unable to delete arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:add-header-function:1 because it is a replicated function. Please see our documentation for Deleting Lambda@Edge Functions and Replicas. (Service: Lambda, Status Code: 400, Request ID: 5dde41cf-48a8-450f-9691-01c5a768a9d2, Extended Request ID: null)"
CloudFormationのコンソールからも同様のエラーが確認できます。
原因
調べると下記のドキュメントが見つかりました。
You can delete a Lambda@Edge function only when the replicas of the function have been deleted by CloudFront. Replicas of a Lambda function are automatically deleted in the following situations:
- After you remove the last association for the function from all of your CloudFront distributions. If more than one distribution uses a function, the replicas are deleted only after you remove the function association from the last distribution.
- After you delete the last distribution that a function was associated with.
Lambda@EdgeではFunctionのレプリカが作成され、Functionの削除はそのレプリカが削除されている必要があり、レプリカが削除されるためにはCloudFront Distributionとの関連付けが削除される必要があるとのことです。
対処
Lambda@Edge Function以外のリソースを先に削除し、その後にLambda@Edge Functionを削除するようにしてみます。
まずスタックのDELETE_FAILEDを
解消します。CloudFormationのコンソールで該当のDELETE_FAILED
となっているスタックを選択し、[削除]をクリックします。
削除に失敗しているリソースの保持にチェックを入れ、[スタックの削除]をクリックします。
スタックの削除が開始され、少しするとDELETE_COMPLETE
となり削除が完了しました。
Lambda@EdgeのFunctionはまだ残っています。コンソールから手動削除してみます。
削除できました。CloudFront Distributionとの関連付け削除から10分ほどしか経っていませんが、すでにFunctionのレプリカは削除されていたようです。
DELETE_FAILED発生時はCDKデプロイできない
ちなみに、CloudFront Distributionとの関連付けをCDKスタック上で削除して再デプロイすれば削除可能となるのでは?と思い、スタックを修正してcdk deploy
したら下記のようにエラーとなりました。
% cdk deploy
SampleAppStack: deploying...
[0%] start: Publishing e121c7550422d68a5a46ba7cc1a34840337180d668d962e42fec3ac53f0681a8:current
[100%] success: Published e121c7550422d68a5a46ba7cc1a34840337180d668d962e42fec3ac53f0681a8:current
SampleAppStack: creating CloudFormation changeset...
❌ SampleAppStack failed: Error [ValidationError]: Stack:arn:aws:cloudformation:us-east-1:XXXXXXXXXXXX:stack/SampleAppStack/c43ef540-e319-11eb-bcf8-0e688fb565d9 is in DELETE_FAILED state and can not be updated.
DELETE_FAILED
となったスタックはコンソールから手動で手当してあげる必要があるんですね。
CloudFront Functionでは同事象は発生しなかった
掲題通りですが、Lambda@Edge Functionと同じくCloudFront Distributionと関連付けられてエッジで動作するCloudFront Functionの場合は、スタックを削除しても同事象は発生しませんでした。
参考
以上