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

如何在CDK部署时为UserData配置仅部署时可知的资源参数?

解决方案:CDK v2 + TypeScript 实现部署时资源变量替换

核心思路

利用CDK的Token机制自动解析部署时生成的资源属性(比如LogGroup名称),结合Fn.sub或直接字符串替换,将UserData脚本中的占位变量替换为实际AWS资源值,同时通过流水线确保资源部署顺序依赖。


步骤1:创建带自定义保留期的CloudWatch LogGroup

首先定义一个独立的Stack(或在堡垒机Stack内)创建LogGroup,支持自定义保留期,并暴露其logGroupName属性供后续使用:

import { Stack, StackProps, RemovalPolicy } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';

export class BastionLogStack extends Stack {
  public readonly bastionLogGroup: LogGroup;

  constructor(scope: Construct, id: string, props?: StackProps & {
    logRetention?: RetentionDays // 允许外部传入自定义保留期
  }) {
    super(scope, id, props);

    this.bastionLogGroup = new LogGroup(this, 'BastionHostLogGroup', {
      logGroupName: undefined, // 让CDK自动生成带栈后缀的唯一名称(部署时确定)
      retention: props?.logRetention ?? RetentionDays.THIRTY_DAYS, // 默认30天,可自定义
      removalPolicy: RemovalPolicy.DESTROY, // 根据业务需求调整(保留/销毁)
    });
  }
}

步骤2:替换堡垒机UserData中的变量

在堡垒机EC2实例的Stack中,通过Fn.sub批量替换脚本模板中的占位变量,CDK会自动将logGroupName Token解析为部署时的实际值:

import { Stack, StackProps, Fn } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Instance, InstanceType, AmazonLinuxImage, UserData } from 'aws-cdk-lib/aws-ec2';
import { Role, ServicePrincipal, PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { BastionLogStack } from './bastion-log-stack';

export class BastionHostStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps & {
    logStack: BastionLogStack // 依赖LogGroup Stack
  }) {
    super(scope, id, props);
    const { bastionLogGroup } = props.logStack;

    // 1. 给堡垒机实例添加CloudWatch Logs写入权限
    const bastionRole = new Role(this, 'BastionIamRole', {
      assumedBy: new ServicePrincipal('ec2.amazonaws.com'),
    });
    bastionRole.addToPolicy(new PolicyStatement({
      actions: ['logs:CreateLogStream', 'logs:PutLogEvents', 'logs:DescribeLogStreams'],
      resources: [bastionLogGroup.logGroupArn],
    }));

    // 2. 定义带占位变量的UserData模板
    const userDataTemplate = `#!/bin/bash
# 配置CloudWatch Logs Agent
cat > /etc/awslogs/awslogs.conf << EOF
[general]
state_file = /var/lib/awslogs/agent-state

[/var/log/bastion]
file = /var/log/bastion/*.log
log_group_name = ${TF_APP_LOG_GROUP}
log_stream_name = {instance_id}/bastion.log
datetime_format = %Y-%m-%d %H:%M:%S
EOF

# 安装并启动日志服务
yum install -y awslogs
systemctl enable --now awslogs
`;

    // 3. 替换模板中的变量,CDK会自动解析Token为实际LogGroup名称
    const userData = UserData.forLinux();
    userData.addCommands(Fn.sub(userDataTemplate, {
      TF_APP_LOG_GROUP: bastionLogGroup.logGroupName,
    }));

    // 4. 创建堡垒机EC2实例
    new Instance(this, 'BastionHost', {
      instanceType: InstanceType.of('t2', 'micro'),
      machineImage: new AmazonLinuxImage(),
      userData: userData,
      role: bastionRole,
      // 补充VPC、安全组等其他配置
    });
  }
}

步骤3:CDK流水线中配置部署顺序

在流水线Stack中,先部署LogGroup Stack,再部署堡垒机Stack,确保资源依赖关系正确:

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { CodePipeline, CodePipelineSource, ShellStep } from 'aws-cdk-lib/pipelines';
import { BastionLogStack } from './bastion-log-stack';
import { BastionHostStack } from './bastion-host-stack';

export class BastionPipelineStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const pipeline = new CodePipeline(this, 'BastionDeploymentPipeline', {
      pipelineName: 'Bastion-Pipeline',
      synth: new ShellStep('Synth', {
        input: CodePipelineSource.gitHub('your-username/your-repo', 'main'),
        commands: ['npm install', 'npm run build', 'npx cdk synth'],
      }),
    });

    // 先部署LogGroup Stack
    const logStage = pipeline.addStage('LogGroup-Deployment');
    const logStack = new BastionLogStack(logStage, 'BastionLogStack', {
      logRetention: RetentionDays.SIX_MONTHS // 自定义保留期为6个月
    });

    // 再部署堡垒机Stack,传入LogGroup依赖
    const bastionStage = pipeline.addStage('BastionHost-Deployment');
    new BastionHostStack(bastionStage, 'BastionHostStack', {
      logStack: logStack,
    });
  }
}

关键说明

  1. Token机制:bastionLogGroup.logGroupName是CDK生成的Token,部署时会被替换为实际的LogGroup名称(即使是CDK自动生成的带随机后缀的名称)。
  2. 权限最小化:避免给堡垒机实例赋予CloudWatchLogsFullAccess,而是仅授予目标LogGroup的写入权限,符合安全最佳实践。
  3. 自定义保留期:通过Stack参数logRetention可以灵活设置日志保留时长,支持RetentionDays枚举的所有值(如ONE_DAY、ONE_YEAR等)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 13:06:17