top of page
Search
  • Writer's pictureJuan Matías de la Cámara

AWS Lambda: the “two updating-points” problem

In this article, we’ll discuss a solution for managing Lambda functions effectively using Terraform within the AWS Well-Architected Framework. It addresses the challenge of handling multiple sources for Lambda functions and ensures that updating the infrastructure doesn’t overwrite changes made by developer repositories.

AWS Lambda: the two updating-points problem

By leveraging lifecycle management and appropriate configuration, developers can update Lambda code independently while maintaining the integrity of the infrastructure.

In our architecture, we utilize an infrastructure repository powered by binbash Leverage, which operates Terraform in the background. The objective is to create a Lambda function and grant it access to specific resources such as SQS, SNS, and buckets, all provisioned through the Leverage Infrastructure repository. Additionally, developers should have the capability to update the Lambda code from their own repositories.


The problem

The challenge arises from having two different sources for the Lambda function. One source is the Leverage Infrastructure repository, responsible for creating the Lambda function and applying policies. The other source is the developers’ repository, where code updates are made. To prevent overwriting the Lambda function during subsequent application of the Leverage Infrastructure repository, a solution is needed.

Facts

So, summing up, these are the facts:

  • There is an Infrastructure repository (binbash Leverage)

  • There is a Lambda code repository

  • The Lambda should be created during the infrastructure creation so the permissions can be easily applied

  • The Lambda code should be updated when new code is pushed into the Lamba code repository

  • The Lambda code should not be updated if the Leverage Infrastructure repository is applied again


AWS Lambda

The solution

Here, life cycle management for Terraform becomes handy.

The approach involves creating the Lambda function in a way that fetches the code from an S3 bucket. Additionally, we instruct Terraform to ignore certain values that can be modified by developers, such as environment variables, memory size, and timeout. By omitting these values during evaluation, Terraform can determine whether an update is necessary.

Here’s an example of the lifecycle block in Terraform:

  lifecycle {
    ignore_changes = [
      environment,
      memory_size,
      timeout
    ]
  }

Since we pull the code from a ZIP file, there is no need to instruct Terraform to omit any code, since it won’t be taken into account when evaluating the update. (Note for forcing Terraform to update the Lambda when ZIPped code changes, we should use source_code_hash , which we'll use later)

  • update the lambda function from the dev repository

  • try to update from infra, it should show no changes to apply

Applying the solution

The complete solution is shown in this binbash Leverage library experimental branch.

Here there are just the needed parts for the demo.

Under the layer apps-devstg/us-east-1/aws-lambda-poc (inside the binbash Leverage context, we call the leafs of this directory tree layers) and apps-devstg/us-east-1/aws-lambda-poc-lambda-update are all the files.

Creating the Lambda

For this, an S3 object is created, then the lambda should pull code from there.

Note the lambda has the lifecycle directive:

resource "aws_s3_object" "initial-lambda" {

    bucket = aws_s3_bucket.lambda.bucket
    key    = "bb-lambda-test-test"
    source = "initial-lambda.zip"

}

resource "aws_lambda_function" "func" {

    depends_on = [

        aws_s3_object.initial-lambda,

    ]

    function_name = "bb-lambda-test-test"
    role          = aws_iam_role.lambda.arn
    handler       = "lambda_function.lambda_handler"
    runtime       = "python3.10"
    memory_size   = 256
    timeout       = 600

    s3_bucket = aws_s3_bucket.lambda.bucket
    s3_key    = "bb-lambda-test-test"


    environment {

      variables = local.lamba_environment_variables
    }

  lifecycle {
    ignore_changes = [
      environment,
      memory_size,
      timeout
    ]
  }

}

initial-lambda.zip contains dummy code.

Apply this layer, Lambda with dummy code is created.

Updating the lambda

This update can be done by a few means, in this case Terraform is being used (AWS CLI and manual changes can be used as well). Note that by using Terraform for doing this, first the lambda function has to be imported into the current state. It is as easy as:

leverage tf import aws_lambda_function.func "bb-lambda-test-test"

And this is the actual Terraform code, file updated-lambda.zip contains the code updated by the developer:

resource "aws_s3_object" "initial-lambda" {

    bucket = "bb-lambda-test"
    key    = "bb-lambda-test-test-update/updated-lambda.zip"
    source = "updated-lambda.zip"
    etag   = filebase64sha256("updated-lambda.zip")

}

resource "aws_lambda_function" "func" {

    depends_on = [ aws_s3_object.initial-lambda ]

    function_name = "bb-lambda-test-test"
    handler       = "lambda_function.lambda_handler"
    runtime       = "python3.10"

    role             = "arn:aws:iam::523857393444:role/bb-lambda-test-sts"
    source_code_hash = filebase64sha256("updated-lambda.zip")
    publish          = true
    timeout          = 60
    memory_size      = 128

    s3_bucket = "bb-lambda-test"
    s3_key    = "bb-lambda-test-test-update/updated-lambda.zip"


    environment {

      variables = local.lamba_environment_variables

    }

}

Note in these Terraform resources the following elements are being used:

  • etag for the S3 object

  • source_code_hash for the lambda function

In this code, the etag and source_code_hash attributes ensure that the resources will be updated when the code changes. After applying this update, the Lambda code will be successfully updated.

Note this step can be easily run in a pipeline in the Lambda code repository.

What can be changed?

As per this directive:

lifecycle {
    ignore_changes = [
      environment,
      memory_size,
      timeout
    ]
  }

…the elements that can be changed safely (i.e. preventing the Leverage Infrastructure repository from updating the object) are:

  • the lambda code itself (using the ZIP file method)

  • the environment variables

  • the memory size

  • the timeout

By incorporating the lifecycle block mentioned earlier, these elements remain unchanged during the application of the Leverage Infrastructure repository. Therefore, subsequent re-application of the infrastructure layer will not impact the Lambda function.

Now you can re-apply the Leverage Infrastructure layer without panicking about the code!


Conclusion

By leveraging Terraform’s lifecycle management and appropriately configuring Lambda functions, developers can update Lambda code independently while ensuring the integrity of the infrastructure. This solution allows for efficient management of Lambda functions within the AWS Well-Architected Framework.

7 views0 comments
bottom of page