How to update aws_lambda_function Terraform resource when ZIP package is changed on S3?

Viewed 1467

Zip package is uploaded to S3 not by Terraform.
Lambda is provisioned by Terraform aws_lambda_function resource. When I change Zip package on S3 and run terraform apply command, Terraform says nothing is changed.
There is source_code_hash field in aws_lambda_function resource that can be set to a hash of package content. But whatever value for this hash I provide it's not updated in Terraform state.

How to tell Terraform to update Lambda in case of Zip package update in S3?

2 Answers

After numerous experiments to verify how Terraform handles hash I found the following:

  1. source_code_hash of aws_lambda_function resource is stored in Terraform state at the moment Lambda is provisioned.
  2. source_code_hash is updated only if you provide a new value for it in aws_lambda_function resource and this new value corresponds to hash of actual Zip package in S3.
    So Terraform checks actual hash code of the package on S3 only at this moment, it doesn't check it when we run terraform apply.

So to make it work we have the following options:

  1. Download Zip package from S3, calculate its hash and pass it to source_code_hash field of aws_lambda_function resource OR
  2. Upload Zip package to S3 by Terraform using aws_s3_bucket_object resource. Set source_hash field in that resource to save it in Terraform state. This value can be used by aws_lambda_function resource for updates.

Unfortunately this behaviour is not documented and I spent lots of time discovering it. Moreover it can be changed any moment since it's not documented and nobody knows that :-(

So how I solved this problem?
I generate base64-encoded SHA256 hash of Lambda Zip file and store it as metadata for actual Zip file. Then I read this metadata in Terraform and pass it to source_code_hash.

Details:

  1. Generate hash using openssl dgst -binary -sha256 lambda_package.zip | openssl base64 command.
  2. Store hash as metadata during package uploading using aws s3 cp lambda_package.zip s3://my-best-bucket/lambda_package.zip --metadata hash=[HASH_VALUE] command.
  3. Pass hash to source_code_hash in Terraform
data "aws_s3_bucket_object" "package" {
  bucket = "my-best-bucket"
  key    = "lambda_package.zip"
}

resource "aws_lambda_function" "main" {
  ...
  source_code_hash = data.aws_s3_bucket_object.package.metadata.Hash
  ...
}

Another way of handling this if you can't do the hash of the s3 object you can do the version.

i.e.

data "aws_s3_bucket_object" "application_zip" {
  bucket = var.apps_bucket
  key    = var.app_key
}

resource "aws_lambda_function" "lambda" {
  function_name                  = var.function_name
  s3_bucket                      = var.apps_bucket
  s3_key                         = var.app_key
  handler                        = var.handler
  runtime                        = var.runtime
  memory_size                    = var.memory_size
  timeout                        = var.timeout
  role                           = aws_iam_role.lambda.arn
  s3_object_version = data.aws_s3_bucket_object.application_zip.version_id
}

This mean when the object version changes in s3 does the lambda deploy

Related