typescriptlambdaaws-cdk

When using CDK with multiple stacks in typescript, how can I stop the cdk deploy command from bundling all lambda functions?


I have 2 stacks defined, for 2 separate lambda functions. Each one has a separate package.json and it's dependencies. When I have them built locally, I can run a cdk deploy Stack1 command successfully. But in the background, it builds Stack1 and Stack2. This is ok when running locally where all modules are setup. But when trying to run through CI, I only run the npm install command for the required module that I want to deploy.

This causes the build to fail because the other stack is trying to bundle the function which hasn't installed any dependences.

Is there a way around this other than having a separate CDK instance for each function or biting the bullet and installing dependencies for each function?

I have put checks to detect whether dist folders exist and use that to prevent running the lambda stack lambda specific create function. But this only works when creating a bundle via esbuild manually outside of cdk. I don't do that for all the functions, only the layers.

UPDATE Investigating this further, it seems to be the way CDK is intentionally built. If you specify a stack name in the deploy, the CDK will still run the code to prepare all stacks in the code. It is just that it won't actually deploy the output of that prepared code if it isn't in the named stack.

This poses a problem when the CDK is being used for compiling code instead of managing infrastructure. I guess this is a sign that the CDK shouldn't really be used to deploy code as it is a platform orchestrator, but the lines are really blurred for lambda best practices. As a result of this, hiding:

const function1 = new NodejsFunction(this, 'lambda-function-1, {
  ...commonProps,
  entry: `${resolve(__dirname)}/../../functions/lambda1.ts`,
  handler: 'main',
  functionName: 'Lambda1Main',
  description: 'Function 1 Main method',
  environment,
  layers: [
    LayerVersion.fromLayerVersionArn(
      this,
      `ALayer-lambda-function-1`,
       Fn.importValue('ALayer-layer-latest'),
    ),
  ],
});

inside a check like existsSync('node_modules') isn't enough, because further down the stack I run function1.functionArn which fails because function1 is undefined.

So does this mean an entire stack needs syncing and validating each time, regardless of which stack is being deployed?


Solution

  • I feel this should be documented, or potentially removed as an issue. But there is a workaround for this I have found. An answer to this ticket: https://github.com/aws/aws-cdk/issues/11625 highlights a way to get the currently defined stack: https://github.com/aws/aws-cdk/issues/11625#issuecomment-1470867494. Updating the stacks in app.ts to check whether they are being targeted prevents them from being initialised every time. This, fundamentally to me, feels like something that should happen inside the parent Stack class.

    Code example for app.ts:

    const app = new App();
    const bundlingStacks = app.node.tryGetContext("aws:cdk:bundling-stacks") as Array<string>;
    const buildAllStacks = bundlingStacks.includes("**");
    
    const stacks: string[] = ['Stack1', 'Stack2'];
    
    if (buildAllStacks || bundlingStacks.includes(stacks[0])) {
        new Stack1(app, stacks[0], {});
    }
    
    if (buildAllStacks || bundlingStacks.includes(stacks[1])) {
        new Stack2(app, stacks[1], {});
    }
    

    Deploy one stack or the other like this:

    cdk deploy Stack1 --exclusively
    cdk deploy Stack2 --exclusively