You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跨账号通过Code Pipeline部署时从AWS Secrets Manager获取密钥失败

跨账号CDK传递Secrets Manager密钥到Lambda的解决方案

问题根源

你当前用secretValueFromJson(key).unsafeUnwrap()生成的是CloudFormation的{{resolve:secretsmanager:arn:...}}引用,这种引用仅支持同账号内解析。跨账号场景下,Prod/Dev账号的CloudFormation没有权限直接访问Security账号的Secrets Manager,导致部署时无法解析引用,触发"找不到密钥"的错误。

可行解决方案

方案1:CodePipeline前置动作预取密钥到同账号SSM

在CodePipeline的部署阶段前,添加一个CodeBuild或Lambda动作,先获取Security账号的密钥值,存入Prod/Dev账号的SSM参数中,再让CDK从SSM读取值:

  • 配置CodeBuild项目的角色,允许切换到Prod/Dev账号的角色(该角色已拥有Security账号密钥的读取权限)。
  • 在CodeBuild脚本中用AWS CLI或SDK调用Secrets Manager获取密钥字段:
    # 切换到Prod角色(示例)
    aws sts assume-role --role-arn arn:aws:iam::PROD_ACCOUNT_ID:role/ProdSecretsReaderRole --role-session-name "fetch-secret" > creds.json
    export AWS_ACCESS_KEY_ID=$(cat creds.json | jq -r '.Credentials.AccessKeyId')
    export AWS_SECRET_ACCESS_KEY=$(cat creds.json | jq -r '.Credentials.SecretAccessKey')
    export AWS_SESSION_TOKEN=$(cat creds.json | jq -r '.Credentials.SessionToken')
    
    # 获取密钥字段并存入SSM
    SECRET_VALUE=$(aws secretsmanager get-secret-value --secret-id arn:aws:secretsmanager:SECURITY_REGION:SECURITY_ACCOUNT_ID:secret:MySecret --query 'SecretString' | jq -r '.myKey')
    aws ssm put-parameter --name "/prod/my-secret-key" --value "$SECRET_VALUE" --type "SecureString" --overwrite
    
  • CDK中从SSM参数读取值传递给Lambda:
    const secretValue = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'ProdSecret', {
      parameterName: '/prod/my-secret-key',
      version: 1,
    }).stringValue;
    
    new lambda.Function(this, 'MyLambda', {
      // ...其他配置
      environment: {
        MY_SECRET: secretValue,
      },
    });
    

方案2:用自定义资源跨账号获取密钥

在Prod/Dev的LambdaStack中创建自定义资源,通过Lambda函数跨账号读取Security账号的密钥,再将值传递给Lambda:

  • 创建一个自定义资源提供者Lambda(同账号),该Lambda的角色拥有切换到Security账号密钥读取角色的权限。
  • 自定义资源触发时,调用Secrets Manager获取密钥值,返回给CloudFormation。
  • CDK中引用自定义资源的输出值作为Lambda的环境变量:
    const fetchSecretResource = new customResources.AwsCustomResource(this, 'FetchProdSecret', {
      onCreate: {
        service: 'SecretsManager',
        action: 'getSecretValue',
        parameters: {
          SecretId: 'arn:aws:secretsmanager:SECURITY_REGION:SECURITY_ACCOUNT_ID:secret:MySecret',
        },
        physicalResourceId: customResources.PhysicalResourceId.of(Date.now().toString()),
        assumedRoleArn: 'arn:aws:iam::PROD_ACCOUNT_ID:role/ProdSecretsReaderRole', // 切换到有权限的角色
      },
      policy: customResources.AwsCustomResourcePolicy.fromSdkCalls({
        resources: customResources.AwsCustomResourcePolicy.ANY_RESOURCE,
      }),
    });
    
    const secretFieldValue = fetchSecretResource.getResponseField('SecretString').apply(s => JSON.parse(s).myKey);
    
    new lambda.Function(this, 'MyLambda', {
      // ...其他配置
      environment: {
        MY_SECRET: secretFieldValue,
      },
    });
    

方案3:CDK Synth阶段提前解析密钥(适合允许密钥出现在模板的场景)

直接在CDK的部署脚本中,提前用AWS SDK获取Security账号的密钥值,作为参数传递给LambdaStack,这样synth出来的模板会包含实际密钥值:

  • 确保执行CDK synth的角色(Pipeline的执行角色)拥有读取Security账号密钥的权限。
  • 在bin目录的CDK入口文件中添加代码:
    import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
    
    const secretsManagerClient = new SecretsManagerClient({ region: 'SECURITY_REGION' });
    const getSecretCmd = new GetSecretValueCommand({
      SecretId: 'arn:aws:secretsmanager:SECURITY_REGION:SECURITY_ACCOUNT_ID:secret:MySecret',
    });
    const secretResponse = await secretsManagerClient.send(getSecretCmd);
    const secretObj = JSON.parse(secretResponse.SecretString!);
    const mySecretValue = secretObj.myKey;
    
    // 传递给LambdaStack
    new LambdaStack(app, 'ProdLambdaStack', {
      env: { account: 'PROD_ACCOUNT_ID', region: 'PROD_REGION' },
      mySecretValue: mySecretValue,
    });
    
  • LambdaStack中直接使用传入的参数:
    interface LambdaStackProps extends cdk.StackProps {
      mySecretValue: string;
    }
    
    export class LambdaStack extends cdk.Stack {
      constructor(scope: Construct, id: string, props: LambdaStackProps) {
        super(scope, id, props);
    
        new lambda.Function(this, 'MyLambda', {
          // ...其他配置
          environment: {
            MY_SECRET: props.mySecretValue,
          },
        });
      }
    }
    

注意事项

  • 权限验证:所有涉及跨账号访问的角色,必须确保信任关系正确(比如Security账号的密钥策略允许Prod/Dev角色读取,Prod/Dev的角色信任CodeBuild或自定义资源的角色)。
  • 密钥轮换:如果需要密钥自动轮换,方案1和方案2可搭配EventBridge规则,触发重新获取并更新Lambda环境变量/SSM参数;方案3需要重新执行Pipeline来更新模板。

内容的提问来源于stack exchange,提问作者Nicolas Gorga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 03:53:10