Terraform lambda source_code_hash update with same code

This works for me and also doesn't trigger an update on the Lambda function when the code hasn't changed

data "archive_file" "lambda_zip" {                                                                                                                                                                                   
  type        = "zip"                                                                                                                                                                                                
  source_dir  = "../dist/go"                                                                                                                                                                                         
  output_path = "../dist/lambda_package.zip"                                                                                                                                                                         
}                                                                                                                                                                                                                    


resource "aws_lambda_function" "aggregator_func" {                                                                                                                                                                   
  description      = "MyFunction"                                                                                                                                                                       
  function_name    = "my-func-${local.env}"                                                                                                                                                                  
  filename         = data.archive_file.lambda_zip.output_path                                                                                                                                                        
  runtime          = "go1.x"                                                                                                                                                                                         
  handler          = "main"                                                                                                                                                                                    
  source_code_hash = data.archive_file.lambda_zip.output_base64sha256                                                                                                                                                
  role             = aws_iam_role.function_role.arn                                                                                                                                                                  


  timeout = 120                                                                                                                                                                                                      
  publish = true                                                                                                                                                                                                     

  tags = {                                                                                                                                                                                                           
    environment = local.env                                                                                                                                                                                                                                                                                                                                                                    
  }                                                                                                                                                                                                                  
}                              

I'm going to add my answer to contrast to the one @ODYN-Kon provided.

The source code hash field in resource "aws_lambda_function" is not compared to some hash of the zip you upload. Instead, the hash is merely checked against the Terraform saved state from the last time it ran. So, the next time you run Terraform, it computes the hash of the actual python file to see if it has changed. If it has, it assumes that the zip has been changed and the Lambda function resource needs to be run again. The source_code_hash can have any value you want to give it or it can be omitted entirely. You could set it to a constant of some arbitrary string, and then it would never change unless you edit your Terraform configuration.

Now, the problem there is that Terraform assumes you updated the zip file. Assuming you only have one directory or one file in the zip archive, you can use the Terraform data source archive_file to create the zip file. I have a case where I cannot use that because I need a directory and a file (JS world: source + node_modules/). But here is how you can use that:

data "archive_file" "lambdaCode" {
  type = "zip"
  source_file = "lambda_process_firewall_updates.js"
  output_path = "${var.lambda_zip}"
}

Alternativly, you can archive an entire directory, if you replace the "source_file" statement with source_dir = "node_modules"

Once you do this, you can reference the hash code of the zip archive file for insertion into resource "aws_lambda_function" "lambda" { block as "${data.archive_file.lambdaCode.output_base64sha256}" for the field source_hash. Then, anytime the zip changes, the lambda function gets updated. And, the data source archive file knows that anytime the source_file changes it must regenerate the zip.

Now, I haven't drilled down to a root cause in your case, but hopefully given some help to get to a better place. You can check the saved state of Terraform via: tf state list - which lists the items of saved state. You can find the one that matches your lambda function block and then execute tf state show <state-name>. For example, for one I am working on:

tf state show aws_lambda_function.test-lambda-networking gives about 30 lines of output, including:

source_code_hash = 2fKX9v/duluQF0H6O9+iRnID2gokhfpXIXpxyeVBUM0=

You can compare the hash via command line commands. Example on MacOS: sha256sum my-lambda.zip, where sha256sum was installed by brew install coreutils.

As mentioned, the use of archive_file doesn't work when you have multiple elements of the zip which are not isolated to a single directory. I think that probably happens a lot, so I wish the Hashicorp guys would extend archive_file to support multiple. I even went looking at the Go code, but that is a rainy day project. One variation I use is to take the source_code_hash to be "${base64sha256(file("my-lambda.zip"))}". But that still requires me to run tf twice.


This is because you are hashing just main.py but uploading dist/subscriber-lambda.zip. Terraform compares the hash to the hash it calculates when the file is uploaded to lambda. Since the hashing is done on two different files, you end up with different hashes. Try running the hash on the exact same file that is being uploaded.