Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aws_lambda: Lambda version resource id changes on each synth without any changes #31427

Open
1 task
GentileFulvio opened this issue Sep 12, 2024 · 3 comments
Open
1 task
Labels
@aws-cdk/aws-lambda Related to AWS Lambda bug This issue is a bug. p3

Comments

@GentileFulvio
Copy link

GentileFulvio commented Sep 12, 2024

Describe the bug

When running the cdk synth command, the resource id of the AWS::LAMBDA::VERSION always changes on each run.

Regression Issue

  • Select this option if this issue appears to be a regression.

Last Known Working CDK Version

No response

Expected Behavior

The version resource id should only change when the function actually changed or if we force a change.

Current Behavior

The resource id changes each and every time the synth command is executed both locally and on CI/CD pipelines.

Reproduction Steps

The following stack reproduces the exact issue that I have in my project now.

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";

import { RemovalPolicy } from "aws-cdk-lib";
import { ManagedPolicy } from "aws-cdk-lib/aws-iam";
import type { FunctionProps, IVersion } from "aws-cdk-lib/aws-lambda";
import {
  Alias,
  Architecture,
  Code,
  Function,
  LayerVersion,
  Runtime,
} from "aws-cdk-lib/aws-lambda";
import { LogGroup, RetentionDays } from "aws-cdk-lib/aws-logs";
import { Provider } from "aws-cdk-lib/custom-resources";

/// Available architectures found here:
// https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Lambda-Insights-extension-versions.html
const getLambdaInsightsLayerArn = (architectureName: string): string => {
  if (architectureName === Architecture.ARM_64.name) {
    return "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension-Arm64:18";
  }

  if (architectureName === Architecture.X86_64.name) {
    return "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:51";
  }

  throw new Error(`Unsupported architecture found: [${architectureName}]`);
};

export class ICLambda extends Function {
  alias: Alias;

  constructor(scope: Construct, id: string, functionProperties: FunctionProps) {
    super(scope, id, functionProperties);
    const layerArn = getLambdaInsightsLayerArn(this.architecture.name);
    const lambdaInsightsLayer = LayerVersion.fromLayerVersionArn(
      this,
      `LambdaInsightsLayer`,
      layerArn
    );

    this.addLayers(lambdaInsightsLayer);

    this.role?.addManagedPolicy(
      ManagedPolicy.fromAwsManagedPolicyName(
        "CloudWatchLambdaInsightsExecutionRolePolicy"
      )
    );

    this.currentVersion.applyRemovalPolicy(
      RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE
    );

    new LogGroup(this, "LogGroup", {
      logGroupName: `/aws/lambda/${this.functionName}`,
      retention: RetentionDays.TEN_YEARS,
      removalPolicy: RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE,
    });

    this.alias = new Alias(this, `Alias`, {
      aliasName: "v0",
      version: this.currentVersion as IVersion,
    });
  }
}

export class LambdaIssueStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const justALambda = new ICLambda(this, "JustALambda", {
      runtime: Runtime.NODEJS_20_X,
      handler: "index.handler",
      architecture: Architecture.X86_64,
      code: Code.fromInline(
        'exports.handler = () => { console.log("Hello, world!"); };'
      ),
    });

    const myCustomResourceLambda = new ICLambda(this, "MigrateToLatestLambda", {
      runtime: Runtime.NODEJS_20_X,
      handler: "index.handler",
      architecture: Architecture.X86_64,
      code: Code.fromInline(
        'exports.handler = () => { console.log("Hello, world!"); };'
      ),
    });

    const migrateToLatestProvider = new Provider(
      this,
      "MigrateToLatestProvider",
      {
        onEventHandler: myCustomResourceLambda,
      }
    );

    new cdk.CustomResource(this, "Custom::DbSchemaMigration", {
      serviceToken: migrateToLatestProvider.serviceToken,
      resourceType: "Custom::DbSchemaMigration",
      properties: {
        migrationDirectoryHash: "<hash-computed-by-dir>",
      },
    });
  }
}

What I already have discovered is that it only breaks when BOTH the .addLayers is called AND the Provider class is initialized. If one of both is removed, the issue no longer happens. But we need the Provider class as we want to add a custom resource in our cloudformation stack.
Another important note is that it doesn't matter if the ICLambda construct is initialized inside the same stack or another stack under bin/stack.ts. It also affects other stacks for some reason.

Simply run cdk synth mutiple times and you'll see that there is a diff in the resourceId of the lambda version on each run.

Possible Solution

No response

Additional Information/Context

As described in the repro steps. Important to know is that it happens when both addLayers is called on a Function construct AND Provider class is initialized somewhere. It doesn't matter if the Function instance is provided to the Provider or not.

I have been digging and it's always the calculateFunctionHash in packages/aws-cdk-lib/aws-lambda/lib/function-hash.ts that returns a different hash on each synth.

CDK CLI Version

2.157.0 (build 7315a59)

Framework Version

2.157.0

Node.js Version

v20.13.1

OS

MacOS

Language

TypeScript

Language Version

5.1.6

Other information

No response

@GentileFulvio GentileFulvio added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Sep 12, 2024
@github-actions github-actions bot added the @aws-cdk/aws-lambda Related to AWS Lambda label Sep 12, 2024
@GentileFulvio
Copy link
Author

I created a public repo to easily reproduce the issue
https://github.com/GentileFulvio/cdk-issue-lambda-version

@pahud pahud self-assigned this Sep 12, 2024
@pahud
Copy link
Contributor

pahud commented Sep 12, 2024

Are you able to check if this.currentVersion would change on synth?

this.alias = new Alias(this, `Alias`, {
      aliasName: "v0",
      version: this.currentVersion as IVersion,
    });

Check the document about currentVersion for more details as I am guessing it's relevant to how the hash is calculated.

As described in the repro steps. Important to know is that it happens when both addLayers is called on a Function construct AND Provider class is initialized somewhere.

Can you share the output of cdk diff ? Sound like it would happen when you this.addLayers() on a different layer? Will the hash change if you do nothing between 2 cdk deploy executions?

@pahud pahud added response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. p3 labels Sep 12, 2024
@pahud pahud removed their assignment Sep 12, 2024
@pahud pahud removed the needs-triage This issue or PR still needs to be triaged. label Sep 12, 2024
@GentileFulvio
Copy link
Author

GentileFulvio commented Sep 13, 2024

This is the output when I deploy it first and run cdk diff immediately after deployment. There was no change in the cdk code

Resources
[-] AWS::Lambda::Version JustALambda/CurrentVersion JustALambdaCurrentVersionCFAFAFEDca54477f98ce6323c154bb0ad7bc9af3 destroy
[-] AWS::Lambda::Version MigrateToLatestLambda/CurrentVersion MigrateToLatestLambdaCurrentVersion55CDA1268ad396c557bee432b812e325773e2a35 destroy
[+] AWS::Lambda::Version JustALambda/CurrentVersion JustALambdaCurrentVersionCFAFAFEDc5866350d467f70f04ad3574940de95e 
[+] AWS::Lambda::Version MigrateToLatestLambda/CurrentVersion MigrateToLatestLambdaCurrentVersion55CDA1263371291771caba20e1a81ba70234e575 
[~] AWS::Lambda::Alias JustALambda/Alias JustALambdaAliasC0E5E01D 
 └─ [~] FunctionVersion
     └─ [~] .Fn::GetAtt:
         └─ @@ -1,4 +1,4 @@
            [ ] [
            [-]   "JustALambdaCurrentVersionCFAFAFEDca54477f98ce6323c154bb0ad7bc9af3",
            [+]   "JustALambdaCurrentVersionCFAFAFEDc5866350d467f70f04ad3574940de95e",
            [ ]   "Version"
            [ ] ]
[~] AWS::Lambda::Alias MigrateToLatestLambda/Alias MigrateToLatestLambdaAliasF0579AA0 
 └─ [~] FunctionVersion
     └─ [~] .Fn::GetAtt:
         └─ @@ -1,4 +1,4 @@
            [ ] [
            [-]   "MigrateToLatestLambdaCurrentVersion55CDA1268ad396c557bee432b812e325773e2a35",
            [+]   "MigrateToLatestLambdaCurrentVersion55CDA1263371291771caba20e1a81ba70234e575",
            [ ]   "Version"

Obviously cloudformation fails during deployment as there was no change to the lambda code or configuration, because of this the creation of the new versio fails with the following error

Please modify the function to create a new version. (Service: Lambda, Status Code: 409, Request ID: 2fa18c9b-cb7a-4443-822a-70e34d56cdb3)" (RequestToken: bac91c5b-9509-64db-afbd-9a137d04c576, HandlerErrorCode: AlreadyExists)

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Sep 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-lambda Related to AWS Lambda bug This issue is a bug. p3
Projects
None yet
Development

No branches or pull requests

2 participants