'CodePipeline: create vpc in stack and use it in post CodeBuildStep

I am creating a pipeline using AWS CDK. I first create my stack which consists in a vpc and an rds, I export the vpc object and use it in a post CodeBuildStep which migrates the database:

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

    const repo = new codecommit.Repository(this, 'CloudFrontCDKRepo', {
      repositoryName: 'backend',
    })
    const pipeline = new CodePipeline(this, 'Pipeline', {
      crossAccountKeys: false,
      pipelineName: 'MyPipeline2',
      synth: new ShellStep('Synth', {
        input: CodePipelineSource.codeCommit(repo, 'cdk'),
        commands: ['cd cdk', 'npm ci', 'npm run build', 'npx cdk synth'],
        primaryOutputDirectory: 'cdk/cdk.out',
      }),
    })

    const deploy = new DevStage(this, 'Deploy-dev')
    const deployStage = pipeline.addStage(deploy)
    deployStage.addPost(
      new CodeBuildStep('SLS Deploy', {
        commands: ['./build.sh'],
        vpc: deploy.vpc
      }),
    )
  }
}

However I am getting the following error:

Stack "MyPipelineStack" cannot consume a cross reference from stack "MyPipelineStack/Deploy-dev/S3Stack". Cross stack references are only supported for stacks deployed to the same environment or between nested stacks and their parent stack

How can I correctly import the vpc here? And if this is not possible, how do I run a codebuildstep as a separate stack which executes only after the main stack has finished?



Solution 1:[1]

As @gshpychka says in the comments, you are asking CodePipeline to perform an impossible order of operations:

  1. The SLS Deploy Project is created as part of the pipeline stack (needs VPC)
  2. The Deploy-dev stack deploys (creates VPC)
  3. The SLS Deploy Project runs

One way around this ordering problem is to add SLS Deploy's logic to the Deploy-dev Stack itself. There are a couple ways to do this:

  1. The simplest approach is to translate build.sh into a TriggerFunction Lambda that has access to the VPC. A Trigger construct calls the Lambda as part of the Deploy-dev Stack's deployment lifecycle. As the docs say, Triggers are designed for tasks like "Data priming: add initial data to resources after they are created".
  2. If you really need a CodeBuild project in the mix, a second option is to move SLS Deploy into Deploy-dev and have it executed with a AwsCustomResource. It is a lower-level cousin of Trigger for runing arbitrary SDK calls (like codebuild:StartBuild) as part of the stack deployment cycle.

N.B. Both these methods have the added advantage of finer-grained control over when your migration script runs (e.g. on first deploy only? after each update?)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 fedonev