[AWS CDK] Lambda@Edge Functionを含んだスタックの削除が失敗する際の対処
こんにちは、CX事業本部の若槻です。
今回は、Lambda@Edge FunctionのConstructを含んだAWS CDKスタックの削除でエラーが発生して失敗する際の対処についてです。
エラーが発生したCDK定義
CloudFront Distribution + S3 Bucket + Lambda@Edge Functionから成る静的ホスティングされたWebサイトを構築しているスタックです。
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の場合は、スタックを削除しても同事象は発生しませんでした。
参考
以上