CDKv2 Pipeline生成无效嵌套模板引用致部署失败
CDK v2 Pipeline部署失败:资产清单错误引用阶段栈模板
问题描述
按照《CDK Book》的CDK Pipelines章节指导,基于cdk init app --language typescript创建的项目构建CDK v2 Pipeline,填充正确账号信息后部署始终失败。
问题根源在于synth步骤生成的资产清单(Pipeline.assets.json)中,额外引用了属于各阶段栈的模板文件,但这些模板实际存在于嵌套云组装目录中,根目录下并无对应文件,导致引用无效。
已将代码简化为仅保留自定义限定符和栈名称的最小可运行示例,升级CDK版本从2.93.0到2.94.0后,问题仍然存在。期望根Pipeline栈的资产文件不包含这些针对阶段栈模板的错误引用。
代码示例
cdk.json
{ "app": "npx ts-node --prefer-ts-exts bin/create-pipeline.ts", "toolkitStackName": "csd-demo-ssm", "requireApproval": "never", "watch": { "include": [ "**" ], "exclude": [ "README.md", "cdk*.json", "**/*.d.ts", "**/*.js", "tsconfig.json", "package*.json", "yarn.lock", "node_modules", "test" ] }, "context": { "@aws-cdk/core:bootstrapQualifier": "demo-ssm", "@aws-cdk/aws-lambda:recognizeLayerVersion": true, "@aws-cdk/core:checkSecretUsage": true, "@aws-cdk/core:target-partitions": [ "aws", "aws-cn" ], "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, "@aws-cdk/aws-iam:minimizePolicies": true, "@aws-cdk/core:validateSnapshotRemovalPolicy": true, "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, "@aws-cdk/core:enablePartitionLiterals": true, "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, "@aws-cdk/aws-iam:standardizedServicePrincipals": true, "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, "@aws-cdk/aws-route53-patters:useCertificate": true, "@aws-cdk/customresources:installLatestAwsSdkDefault": false, "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, "@aws-cdk/aws-redshift:columnId": true, "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, "@aws-cdk/aws-kms:aliasNameRef": true, "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, "@aws-cdk/core:newStyleStackSynthesis": true } }
bin/create-pipeline.ts
import * as cdk from 'aws-cdk-lib' import * as ec2 from 'aws-cdk-lib/aws-ec2' import * as iam from 'aws-cdk-lib/aws-iam' import { CdkPipelineStack } from '../lib/CdkPipelineStack' const env1: cdk.Environment = { account: '111111111111', region: 'us-east-1' } const env2: cdk.Environment = { account: '999999999999', region: 'us-east-1' } const app = new cdk.App({ defaultStackSynthesizer: new cdk.DefaultStackSynthesizer({ qualifier: 'demo-ssm', bootstrapStackVersionSsmParameter: '/cdk-bootstrap/demo-ssm/version' }) }) iam.Role.customizeRoles(app, { preventSynthesis: false }) const pipeline = new CdkPipelineStack(app, 'Pipeline', { ciEnv: env1, devEnv: env1, prodEnv: env2 }); app.synth();
lib/CdkPipelineStack.ts
import { Stack, Environment } from 'aws-cdk-lib' import { CodePipeline, CodePipelineSource, ShellStep } from 'aws-cdk-lib/pipelines'; import { Construct } from 'constructs'; import { ProjectStage } from './ProjectStage' export class CdkPipelineStack extends Stack { constructor(scope: Construct, id: string, props: CdkPipelineStackProps ) { super(scope, id, { env: props.ciEnv }); const codeSource = CodePipelineSource.connection('john-heinnickel/automation-runbook-demo', 'main', { connectionArn: 'arn:aws:codestar-connections:us-east-1:123456789012:connection/f1f1f1f1-eeee-5555-aaaa-cdccc0000099', codeBuildCloneOutput: true, triggerOnPush: true }); const pipeline = new CodePipeline(this, 'Pipeline', { // we need to activate this for cross account deployments pipelineName: 'Pipeline', crossAccountKeys: true, synth: new ShellStep('Synth', { // configure source repo here input: codeSource, // configure installation of dependencies here installCommands: ['npm install --frozen-lockfile'], // configure build steps here commands: ['npm ci', 'npm run build', 'npx cdk synth'], }) }); const wl1 = pipeline.addStage( new ProjectStage(this, 'DevWorkload', { env: props.devEnv })); const wl2 = pipeline.addStage( new ProjectStage(this, 'ProdWorkload', { env: props.prodEnv })); } } export interface CdkPipelineStackProps { ciEnv: Environment, devEnv: Environment, prodEnv: Environment }
lib/ProjectStage.ts
import { Stage, StageProps } from 'aws-cdk-lib' import { Construct } from 'constructs' import { SharedInfraStack } from './SharedInfraStack' import { StatefulStack } from './StatefulStack' export class ProjectStage extends Stage { constructor(scope: Construct, id: string, props: StageProps) { super(scope, id, props) const sharedStack = new SharedInfraStack(this, 'SharedInfraStack'); const statefulStack = new StatefulStack(this, 'StatefulStack', { vpc: sharedStack.vpc } ); } }
package.json
{ "name": "stack-overflow-gist", "version": "0.1.0", "bin": { "create-pipeline": "bin/create-pipeline.js" }, "scripts": { "build": "tsc", "watch": "tsc -w", "test": "jest", "cdk": "cdk" }, "devDependencies": { "@types/jest": "^29.5.4", "@types/node": "20.5.3", "aws-cdk": "2.94.0", "jest": "^29.6.3", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "typescript": "~5.1.6" }, "dependencies": { "aws-cdk-lib": "2.94.0", "constructs": "^10.0.0", "source-map-support": "^0.5.21" } }
lib/StatefulStack.ts
import * as iam from 'aws-cdk-lib/aws-iam' import * as ec2 from 'aws-cdk-lib/aws-ec2' import * as kms from 'aws-cdk-lib/aws-kms' import { Size } from 'aws-cdk-lib/core' import { Stack } from 'aws-cdk-lib' import { Construct } from 'constructs' import { StatefulStackProps } from './StatefulStackProps' export class StatefulStack extends Stack { constructor(scope: Construct, id: string, props: StatefulStackProps) { super(scope, id, props) const myVpc = props.vpc const nodeRole = new iam.Role(this, 'NodeRole', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com') }) const nodeInstProf = new iam.InstanceProfile(this, 'NodeInstanceProfile', { role: nodeRole }) const secGrp = new ec2.SecurityGroup(this, 'SgNode', { vpc: myVpc }) const ebsKey = kms.Key.fromLookup(this, 'EbsKey', { aliasName: 'alias/aws/ebs' }) const dataVolume: ec2.IVolume = new ec2.Volume(this, 'DataVolume', { size: Size.gibibytes(50), availabilityZone: 'us-east-1c', encryptionKey: ebsKey, encrypted: true }) // Create the EC2 instance const ec2Instance = new ec2.Instance(this, 'EC2Instance', { vpc: myVpc, instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3A, ec2.InstanceSize.MEDIUM), machineImage: new ec2.AmazonLinuxImage({ generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 }), role: nodeRole, securityGroup: secGrp, requireImdsv2: true, detailedMonitoring: true, ssmSessionPermissions: true, associatePublicIpAddress: false, propagateTagsToVolumeOnCreation: true, }) // Attach the data volumes dataVolume.grantAttachVolumeByResourceTag(ec2Instance, [ec2Instance]); dataVolume.grantDetachVolumeByResourceTag(ec2Instance, [ec2Instance]); } }
lib/StatefulStackProps.ts
import { StackProps } from 'aws-cdk-lib' import { IVpc } from 'aws-cdk-lib/aws-ec2' export interface StatefulStackProps extends StackProps{ vpc: IVpc }
lib/SharedInfraStack.ts
// file: lib/shared-infra-stack.ts import { Stack, StackProps } from 'aws-cdk-lib'; import { Vpc, SubnetType } from 'aws-cdk-lib/aws-ec2'; import { Construct } from 'constructs'; export class SharedInfraStack extends Stack { public readonly vpc: Vpc; constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // assign a VPC to the class property SharedInfraStack this.vpc = new Vpc(this, 'TheVPC', { cidr: '10.0.0.0/16', natGateways: 1, maxAzs: 3, subnetConfiguration: [ { cidrMask: 20, name: 'public', subnetType: SubnetType.PUBLIC, }, { cidrMask: 20, name: 'application', subnetType: SubnetType.PRIVATE_WITH_EGRESS, }, { cidrMask: 20, name: 'data', subnetType: SubnetType.PRIVATE_ISOLATED, }, ], }); } }
tsconfig.json
{ "compilerOptions": { "target": "ES2020", "module": "commonjs", "lib": [ "es2020", "dom" ], "declaration": true, "strict": true, "noImplicitAny": true, "strictNullChecks": true, "noImplicitThis": true, "alwaysStrict": true, "noUnusedLocals": false, "noUnusedParameters": false, "noImplicitReturns": true, "noFallthroughCasesInSwitch": false, "inlineSourceMap": true, "inlineSources": true, "experimentalDecorators": true, "strictPropertyInitialization": false, "typeRoots": [ "./node_modules/@types" ] }, "exclude": [ "node_modules", "cdk.out" ] }
内容的提问来源于stack exchange,提问作者John Heinnickel
相关产品推荐
相关产品推荐

