aws-cdk: [lambda] deployment failure on updates to cross-stack layers

Here is the application structure:

  • Stack A:
    • lambda layer
  • Stack B: Depends on A:
    • Lambda which is using the layer
new lambda.Function(this, 'some-lambda'
   {
     ...
        layers: [stackA.lambdaLayer]
   }
)

where stackA.lambdaLayer is an instance of lambda.LayerVersion.

The initial deployment works well. However, when the layer code is changed and the second deployment takes place, Stack A deployment fails with error message:

lambdas-layer (…) Requested update requires the creation of a new physical resource; hence creating one. Resource creation Initiated dev-StackA Export dev-CoreStack:corelambdaslayerLayerVersionArn261FB3DB cannot be updated as it is in use by dev-StackB

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 37
  • Comments: 20 (10 by maintainers)

Commits related to this issue

Most upvoted comments

@eladb I’ve worked around this by using a SSM parameter store to reference the latest layerVersionArn and then accessing the layer instance via Layer.fromLayerVersionArn(). (Nod to @rhboyd for this idea.)

I wrote a custom construct to act as such a proxy:

const cdk = require('@aws-cdk/core');
const lambda = require('@aws-cdk/aws-lambda');
const ssm = require('@aws-cdk/aws-ssm');

// Proxy to the LATEST version of the Lambda Layer shared between stacks via a
// SSM paramter store.
module.exports = class SharedLayer extends cdk.Construct {
  static parameterName(construct) {
    return `/${cdk.Stack.of(construct).stage}/shared/layerVersionArn`;
  }

  static of(construct) {
    const layerVersionArn = ssm.StringParameter.valueForStringParameter(
      construct,
      this.parameterName(construct)
    );

    return lambda.LayerVersion.fromLayerVersionArn(
      construct,
      'SharedLayer',
      layerVersionArn
    );
  }

  constructor(scope, id, props) {
    super(scope, id, props);

    new ssm.StringParameter(this, 'VersionArn', {
      parameterName: SharedLayer.parameterName(this),
      stringValue: props.layerVersionArn,
    });
  }
};

I split the layers into a stack of their own, and share the ARN via SSM.Params, to then be used by a lambda in another stack. So no hard dependency…

You can see both the layer creation and then usage (which would usually be in a separate stack)

https://github.com/ranguard/cdk-talk-examples/tree/master/code/lambda_layer_example/lib

We have a ‘layers’ stack

AWS keeps layer versions around, whilst any lambda is pointing at them - so new layer deploys won’t break existing functions - but you do need to re-deploy any stack that is using the layers

@fabio-vitali

The code @alexdilley wrote breaks down into:

Creating layer

        const paramName = '/layers/BaseLayer';

        // Create Layer
        const baseLayer = new lambda.LayerVersion(this, 'BaseLayer', {
            code: lambda.Code.asset(__dirname + '/layer/base'),
            compatibleRuntimes: [
                lambda.Runtime.NODEJS_8_10
            ],
            description: 'Our base layer',
        });

        // Save to SSM
        new ssm.StringParameter(this, 'VersionArn', {
            parameterName: paramName,
            stringValue: baseLayer.layerVersionArn,
        });

Using layer:

        const paramName = '/layers/BaseLayer';


         // fetch the Arn from param store
        const baseLayerArn = ssm.StringParameter.valueForStringParameter(
            this,
            paramName
        );
       // generate layerversion from arn 
        const layer1 = lambda.LayerVersion.fromLayerVersionArn(
            this,
            'BaseLayerFromArn',
            baseLayerArn
        );

       // Then supply when you create a lambda
       new lambda.Function(this, 'SomeName, {
              ...,
             layers: [layer1],
      });

@fabio-vitali That solutions works by breaking the coupling between one stack’s export value and another stack’s import value (which is enforced by cloudformation). Instead they are using one stack to put a value into SSM Parameter store then another stack is reading values from the same SSM Parameter store.

@nija-at yeah, personally I’ve worked around that not sharing layers between stacks, in the end it’s not that bad with the little helper function utility and folder structure, so I define the layer once for the project and I create a new one per stack whenever needed. Easier showed than said:

Thanks to you @lapair I try that solution, and it works! This adjusts perfectly with my idea of separate layers in their stack