使用aws-cdk@2.59.0部署CloudFormation栈时自定义资源偶发状态权限错误求助
错误原因与解决方案:Custom Resource 报
Resource is not in the state functionActiveV2 错误原因
这个偶发错误的核心是自定义资源触发执行时,目标Lambda函数(或其依赖的Lambda)尚未完成状态切换到functionActiveV2:
- CloudFormation默认会并行创建/更新资源,即使显式设置了资源依赖,Lambda内部的状态同步(比如发布新版本、更新配置后的状态刷新)可能存在不稳定的延迟。
- 当Lambda处于
Pending或Updating等过渡状态时,对其发起调用请求就会返回AccessDeniedException并附带该状态提示,这种情况只会在Lambda状态切换较慢时出现,因此错误具有偶发性。
解决方法
1. 依赖Lambda的稳定版本/别名而非原始函数
CloudFormation会等待Lambda的Version或Alias资源完全就绪后,再创建依赖它的自定义资源,能从根源上避免状态未就绪的问题。在CDK中可以这样实现:
import * as cdk from 'aws-cdk-lib'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as custom_resources from 'aws-cdk-lib/custom-resources'; import { Construct } from 'constructs'; export class MyStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // 创建Lambda函数,开启自动发布版本 const myLambda = new lambda.Function(this, 'TargetLambda', { runtime: lambda.Runtime.NODEJS_18_X, handler: 'index.handler', code: lambda.Code.fromAsset('lambda'), publish: true, }); // 使用函数的当前稳定版本作为自定义资源的事件处理器 const lambdaStableVersion = myLambda.currentVersion; const customProvider = new custom_resources.Provider(this, 'CustomResourceProvider', { onEventHandler: lambdaStableVersion, }); // 创建自定义资源,依赖稳定版本的Lambda new cdk.CustomResource(this, 'MyCustomResource', { serviceToken: customProvider.serviceToken, }); } }
2. 在自定义资源代码中添加重试逻辑
针对偶发的状态延迟,在自定义资源的Lambda处理函数中捕获特定错误并进行指数退避重试,等待Lambda状态就绪:
const AWS = require('aws-sdk'); const lambda = new AWS.Lambda(); const MAX_RETRIES = 3; const BASE_DELAY = 1000; // 初始延迟1秒 async function handler(event) { let retryCount = 0; while (retryCount < MAX_RETRIES) { try { // 替换为你的业务逻辑,比如调用目标Lambda const response = await lambda.invoke({ FunctionName: process.env.TARGET_LAMBDA_ARN, Payload: JSON.stringify(event) }).promise(); return { Status: 'SUCCESS', Data: { result: JSON.parse(response.Payload) } }; } catch (error) { // 判断是否为目标状态错误 if (error.code === 'AccessDeniedException' && error.message.includes('functionActiveV2')) { retryCount++; const delay = BASE_DELAY * Math.pow(2, retryCount - 1); // 指数退避:1s, 2s, 4s await new Promise(resolve => setTimeout(resolve, delay)); continue; } // 其他错误直接抛出,标记自定义资源失败 return { Status: 'FAILED', Reason: error.message, PhysicalResourceId: event.PhysicalResourceId }; } } // 重试耗尽仍失败 return { Status: 'FAILED', Reason: `重试${MAX_RETRIES}次后,目标Lambda仍未就绪`, PhysicalResourceId: event.PhysicalResourceId }; } exports.handler = handler;
3. 优化Lambda部署配置
- 始终为用于自定义资源的Lambda设置
publish: true,确保函数创建时就生成稳定的版本,避免调用未发布的过渡态函数。 - 避免在自定义资源中直接引用Lambda的原始ARN(无版本/别名后缀),优先使用带版本或别名的ARN,确保调用的是稳定状态的函数实例。
4. 验证IAM权限
虽然错误由状态问题引发,但仍需确认自定义资源的执行角色拥有lambda:InvokeFunction权限,且权限范围覆盖目标Lambda的完整ARN(包括版本/别名的ARN),避免因权限不足误报状态错误。
内容的提问来源于stack exchange,提问作者Pavlo Zhoholiev
相关产品推荐
相关产品推荐

