I have an ApplicationStack which created a S3Bucket:
export class ApplicationStack extends Cdk.Stack {
public readonly websiteBucket : S3.Bucket;
constructor(scope: Construct, id: string, props: ApplicationStackProps) {
super(scope, id, props);
// Amazon S3 bucket to host the store website artifact
this.websiteBucket = new S3.Bucket(this, "eCommerceWebsite", {
bucketName: `${props.websiteDomain}-${account}-${region}`,
websiteIndexDocument: "index.html",
websiteErrorDocument: "error.html",
removalPolicy: Cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true,
accessControl: S3.BucketAccessControl.PRIVATE,
encryption: S3.BucketEncryption.S3_MANAGED,
publicReadAccess: false,
blockPublicAccess: S3.BlockPublicAccess.BLOCK_ALL,
});
// Create a dummy export.
// https://www.endoflineblog.com/cdk-tips-03-how-to-unblock-cross-stack-references
this.exportValue(this.websiteBucket.bucketArn);
...
...
...
}
}
I also defines ApplicationStage
which contains above ApplicationStack
export class ApplicationStage extends Cdk.Stage {
public readonly websiteBucket : S3.Bucket;
constructor(scope: Construct, id: string, props: ApplicationStageProps) {
super(scope, id, props);
const applicationStack = new ApplicationStack(this, `eCommerceDatabaseStack-${props.stageName}`, {
stageName: props.stageName,
websiteDomain: props.websiteDomain,
});
this.websiteBucket = applicationStack.websiteBucket;
}
public getWebsiteBucket() {
return this.websiteBucket;
}
}
In my pipeline stack, I want to create application stage for each stage that need deploy the website artifact to its corresponding S3 bucket. This is a cross-account CI/CD pipeline, and I have 3 separate AWS accounts(Alpha, Gamma, Prod) for this website. Whenever I ship code out, the pipeline should deploy the new artifact to Alpha then Gamma then Prod, and the alpha.ecommerce.com
, gamma.ecommerce.com
, ecommerce.com
should be updated in this order. The problem happens when reference the S3Bucket in S3DeployAction below:
export class CodePipelineStack extends CDK.Stack {
constructor(scope: CDK.App, id: string, props: CodePipelineStackProps) {
super(scope, id, props);
...
...
// Here the pipelineStageInfoList contains Gamma and Prod information.
pipelineStageInfoList.forEach((pipelineStage: PipelineStageInfo) => {
const applicationStage = new ApplicationStage(this, pipelineStage.stageName, {
stageName: pipelineStage.stageName,
pipelineName: props.pipelineName,
websiteDomain: props.websiteDomain,
env: {
account: pipelineStage.awsAccount,
region: pipelineStage.awsRegion,
},
});
const stage = pipeline.addStage(applicationStage);
// Here is what went wrong. It is trying to deploy the S3Bucket for that stage.
stage.addAction(
new codepipeline_actions.S3DeployAction({
actionName: "Deploy-Website",
input: outputWebsite,
bucket: applicationStage.getWebsiteBucket(),
})
);
});
}
...
...
...
}
Run cdk synthesize
got below error:
/Users/yangliu/Projects/eCommerce/eCommerceWebsitePipelineCdk/node_modules/aws-cdk-lib/core/lib/deps.ts:39
throw new Error(`You cannot add a dependency from '${source.node.path}' (in ${describeStage(sourceStage)}) to '${target.node.path}' (in ${describeStage(targetStage)}): dependency cannot cross stage boundaries`);
^
Error: You cannot add a dependency from 'eCommerceWebsitePipelineCdk-CodePipeline-Stack' (in the App) to 'eCommerceWebsitePipelineCdk-CodePipeline-Stack/ALPHA/eCommerceDatabaseStack-ALPHA' (in Stage 'eCommerceWebsitePipelineCdk-CodePipeline-Stack/ALPHA'): dependency cannot cross stage boundaries
I think this means that I didn't pass the S3Bucket reference in the right way here.
How to fix it?
Update with my solution 2022-09-06
Based on matthew-bonig@'s advice, I am able to get this work.
I have a separate stack to be deployed to each account to create the S3 buckets and its required CloudFront distribution. Then my pipeline stack just focus on tracking my GitHub repository, and update S3 bucket whenever a new commit is pushed.