-
Notifications
You must be signed in to change notification settings - Fork 9.2k
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
source_code_hash does not update #7385
Comments
I do the same for my Go lambda function but I don´t use the base64encode as the sha256 sum is in the file. Try this |
Dear all, we do have the issue as described above. The code looks similar. Each time we run |
Hi All, So the case here is pretty much the same: My lambda function is fetching its source code from S3 object. I struggling to generate the proper source code hash string, which make the the lambda being updated every single run (even the source code is the same)
In order to get *.jar filesum I creates another txt file that contains sha256sum of the file
If I use the buildin terraform filebase64sha256 function I see the same filesum as the one that terraform gets for the S3 object But we I generate the filesum base64 encoded locally - the string that I'm getting is different. result from the terraform console In the documents for 0.11.11 is explisitly said:
So the question here is how to generate the properly encoded SHA256 string on the host Linux bash tf looks liket that
|
We're seeing the exact same issue,
|
An easier (alternative) way to update lambda function on code change, when sourced from S3, would be to set S3 bucket versioning and set lambda zip version:
|
Using a version id will not work for us, because we want to use snapshot-versions during development time, without always deploying and referencing a new version number. |
I'm also experiencing this issue on v0.12.25, aws provider v2.62.0, with a jar that is uploaded directly to the lambda. I've tried different hashing algorithms but the ones generated on apply never match the ones in state. EDIT: I'm using: |
I'm experiencing this with v0.12.20 and aws provider v2.65.0 with a zip file that's referenced from an s3 bucket.
I'm using the When I run apply twice in a row, the input hash is always the same, but the new hash is not being persisted to the state and the next run shows the same output.
|
I am experiencing the same issue, specifically inside a CI/CD pipeline. It does not occur on OSX and it does not occur in Docker on OSX when the project directory is mounted from OSX.
However, with the same Docker image, TF version, and AWS Provider version, the hashes in the CI pipeline never match. The one generated by I thought this was an issue of something else getting hashed, such as a timestamp or similar, but the generated hash is the same. Somehow, that hash that gets computed doesn't get stored under source_code_hash. This is actually quite a nasty problem because when the Lambda is used with CloudFront, the latter redeploys each time - because AWS thinks that a new version of the Lambda has been created. This then adds an additional at least 3, but often 10+ minutes to CD pipeline. |
I've done a little digging into this issue as I recently encountered it. In my use case I generate the zip files frequently, even if the underlying contents don't change, the meta data changes in the zip file cause a different hash. I tried to generate the hash of the contents outside of the zip and set it as the source code hash to get around this. From my observations it appears that the source_code_hash field get's set in the state file from the filename field regardless of the content supplied to it. ie: |
I have found a workaround that works for my case - using the Terraform built-in zip provider (archive_file). Generating the zip outside Terraform seems to be causing issues, even though it shouldn't. Something like this works fine for me and doesn't cause the lambda to be updated between subsequent runs of the CI/CD pipeline, even days apart.
|
I'm reporting the same concern, too. The main problem is the purpose of Looking at the source code, it is a computed field. After a successful deploy, the value of In short, the value assigned to What we needWe need a way to deterministically trigger lambda deployments (e.g. after code change is detected) without presumptions that everyone uses the same process in packaging their code. Is Suggestion
@aeschright does this sound like something that we can do? I'll submit a PR if yes =========================== |
I had a very similar problem where the statefile was not getting an updated So when you see something like:
The value on the left is the AWS calculated hash, and the value on the right is the value you are providing terraform in your lambda definition. If you calculate the hash yourself with shell, use the following algorithm: openssl dgst -sha256 -binary ${FILE_NAME}.zip | openssl enc -base64 If you calculate it with a python script, use something like the following: import base64
import hashlib
def get_aws_hash(zip_file):
'''Compute bash64 sha256 hash of zip archive, with aws algorithm.'''
with open(zip_file, "rb") as f:
sha256_hash = hashlib.sha256()
# Read and update hash string value in blocks of 4K
while byte_block := f.read(4096):
sha256_hash.update(byte_block)
hash_value = base64.b64encode(sha256_hash.digest()).decode('utf-8')
return hash_value |
If you are deploying the lambda from a container image (https://aws.amazon.com/blogs/aws/new-for-aws-lambda-container-image-support/), then the AWS calculated hash will be the image digest. Making a change to the image source, building and pushing, results in a different digest hash, which triggers the lambda updaate. You can do something like: terraform {
required_providers {
# Note that non hashicorp providers need to be declared in each child module - https://github.com/hashicorp/terraform/issues/27361
docker = {
source = "kreuzwerker/docker"
version = "2.9.0"
}
}
}
provider "docker" {
registry_auth {
address = "<account id>.dkr.ecr.<region>.amazonaws.com"
username = "AWS"
// The password is read from DOCKER_REGISTRY_PASS
}
}
data "docker_registry_image" "my-image" {
name = "<account id>.dkr.ecr.<region>.amazonaws.com/my-repo/image-name:latest"
}
resource "aws_lambda_function" "my_lambda" {
function_name = "my-lambda"
role = aws_iam_role.lambda.arn
package_type = "Image"
image_uri = data.docker_registry_image.avping-ingest.name
source_code_hash = split("sha256:", data.docker_registry_image.my-image.sha256_digest)[1]
}
The only other limitation I'd note with container images at present is that they are restricted to using an ECR repository in the same AWS account. |
If someone is still running into this issue, a fix that worked for me was the etag in the This tag triggers an update on the zip file on deployment. resource "aws_s3_bucket_object" "lambda" {
bucket = var.s3_bucket
key = "lambda.zip"
etag = filemd5("lambda.zip")
} If you have the lambda resource block pointing to the right bucket and key, the lambda should get updated. I have tried several steps involving manually zipping up the lambdas and using the EDIT: |
The etag has a big warning there, it is not compatible with buckets with KMS encryption, also with large files the etag is not an md5 digest, that will give problems too: This is has been working fine for me to keep track of many files: resource "aws_s3_object" "file" {
for_each = fileset("${path.module}/..", "*.zip")
bucket = aws_s3_bucket.deploy_bucket.id
key = each.value
source = "${path.module}/../${each.value}"
source_hash = filemd5("${path.module}/../${each.value}")
} |
I ran into this issue for aws lambda layers where I was using Not sure why this worked, but I switched to using the So, my fix is: |
The issue with this is if you are considering the zip already exists however dynamically generating zips using "archive_file" data block doesn't fall under this situation since in that case the source_code_hash will fail as it wont find the zip during plan . |
I'm using a similar approach to resource "null_resource" "lambda" {
provisioner "local-exec" {
command = "./build.sh lambda.zip"
}
triggers = {
source_hash = filebase64sha256("lambda.py")
}
} Since null resource state gets persisted, but the built zip file does not, I cannot rely on recalculating the hash on every run. resource "aws_s3_object" "lambda" {
bucket = "mybucket"
key = "lambda.zip"
source = "lambda.zip"
source_hash = base64sha256(jsonencode(null_resource.lambda_channel_controller.triggers))
}
resource "aws_lambda_function" "lambda" {
function_name = "lambda"
depends_on = [aws_s3_object.lambda]
s3_bucket = aws_s3_object.lambda.bucket
s3_key = aws_s3_object.lambda.key
#source_code_hash = aws_s3_object.lambda.source_hash
} The only way to reliably trigger a deployment is by adding |
* Created a working version of the Lambda function * Created a zip file of the Lambda code and uploaded to the S3 code bucket * Added a CloudWatch Group and IAM role to be used by the Lambda function * Added an IAM policy for logging to CloudWatch and attached to the IAM Role * Enabled versioning on the S3 code bucket * Ended up having to switch from using a SourceCodeHash to the S3 version... * Tried to use both the asset and source hash of both the zip file as well as the uploaded S3 object and both were causing a continuous update of the function (hashicorp/terraform-provider-aws#7385) * Tried to manually create the base64 encoded hash from the Lambda code zip, but CDKTF was trying to access the zip before it was actually created * Seems like this is a better solution anyway, as the S3 bucket for the code should probably be versioned anyway
My case what slightly different, but same effect: I am building a Then I faced the same issue like described here (and several forum posts). I also ended up storing the hash alongside the zip file in s3 during build time. In the deployment terraform code I tried to apply the following, somewhat ugly construct: data "aws_s3_object" "layer_zip_hash" {
bucket = "the-bucket"
key = "layer-common.zip.hash"
}
resource "aws_lambda_layer_version" "lambda_layer" {
layer_name = "layer-common"
s3_bucket = "the-bucket"
s3_key = "layer-common.zip"
# Nothing else works other than the hash of the zip file.
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_layer_version#source_code_hash
# "Must be set to a base64-encoded SHA256 hash of the package file specified with either filename or s3_key"
# Earlier we tried to use the hash of the poetry.lock file, but that didn't work,
# although terraform plan did show a change in the source_code_hash, forcing recreation,
# in the end the source_code_hash was not recording the custom hash of the poetry.lock file.
# https://github.com/hashicorp/terraform-provider-aws/issues/7385
# see this comment especially: https://github.com/hashicorp/terraform-provider-aws/issues/7385#issuecomment-733995977
# See storing the hash as an object technique here:
# https://discuss.hashicorp.com/t/lambda-function-error-call-to-function-filebase64sha256-failed-no-file-exists/20233/4
source_code_hash = data.aws_s3_object.layer_zip_hash.body
skip_destroy = false
compatible_runtimes = ["python3.9"]
} My terraform plan finally has stopped marking my layer to be replaced. I have also looked into the fact that zipping twice the same content causes different hashes (no matter that the zips' content are the same) I use the My 2 cents, and I admit, I was facepalming myself as well, when I found out, but look at the documentation:
This is not a new entry (I went there and looked up in the git history, this piece of information was there for more than 4 years, 992d697). I am not yet sure, though, how to ergonomically eliminate the hashing function there. |
how would we use this to solve the issue? |
Any work arounds for this? |
Look at the answer from @systematicguy: #7385 (comment)
Another solution is to use 's3_object_version':
|
Hello. In my case, I solved the issue with JAR archives redeployment. The archive stores the modified date for each file as metadata, and because of this, the output hash differs depending on build time. It is possible to change the outputTimestamp in maven-jar-plugin to a specific static date:
After that hash becomes consistent for recurring builds, it should work till there is no additional variable metadata in the JAR file. It will be nice to calculate hashes of source files or directories without packaging or other ways to find differences in sources using Terraform. I have been working with it for only a week, so maybe such an option already exists, but I don't find such -_- |
Old topic, but throwing in a summary and a possible workaround, as of If you don't include a If you do include a
Some people say they have success with If you are building/compiling/bundling your code dynamically, then you can't rely on the sha hash of the zip, because the zip archive specification includes the created and last-modified timestamps for files. So every time you run something like typescript or webpack or esbuild or whatever build tool your language has, if the files are recreated, then the zip will change. i.e. if you re-run Code that won't change:
Build and zip the code once:
Build and zip again:
The hashes of the typescript compiled I would say that its a flawed approach to rely on the zip file hashes; you really need the hashes of the contents of the zip archives, not the archives themselves. But since aws seems to internally use those, I guess there isn't much TF can do about that... I'm not sure how CDK and CloudFormation get around this (or maybe they are just as broken?) The most viable solution I have found so far is to make your own zip files using a utility where you can override the created/last-modified dates. For example in a JS project I am using zip-stream to add files to the zip archive and set their dates to
which results in the resulting zip file now being the same when the code is the same. Then in terraform the
|
@jvalore-medallia My zip files are all in one bucket and to download them is one simple command, I do:
In my case these are all lambda functions, and they are in a common folder, we can get what changed from GIT using:
then you can skip those that have not changed, for me it save me a lot of time, on normal runs when only a couple of functions change it was a more than 80% time reduction, and besides the time it takes to zip is also the "yarn install" and any other compilation steps. An alternative to zip-stream that I use is git-restore-mtime |
Hi all 👋 I believe this may have been resolved with #31887 in version 5.32.0 of the provider. It may be worth testing with that version to see if you're still experiencing this behavior. |
@justinretzolk 5.32.0 didn't help in my case. I was hitting the same issue documented here, where an In my case I was able to narrow down the issue to the way the .zip file was generated. Specifically, on the second platform the source file was copied and its final timestamps and permissions were looking different from those found on the first platform. I managed to resolve the issue by "normalizing" the source file by resetting timestamps and permissions before the Just reporting my experience in case it could help someone else who finds themselves being in my same situation. |
For Python developers, I'd like to report the trouble I'm having with Archive files created from pip installation directories ( This is not a provider issue. It's how If you'd like to reproduce my issue, I've made the following minimal project to test: ./pyproject.toml[project]
name = "ziphashtest"
version = "0.0.0"
requires-python = ">=3.10"
dependencies = [
"mypy==1.8.0"
] ./test.shset -e
cleanup () {
rm -r install_*
rm zip_*
}
trap cleanup EXIT
pip install -U pip --quiet
pip install deterministic_zip --quiet
for i in {0..1}; do
pip install . --target install_"${i}" --quiet
deterministic_zip zip_"${i}" install_"${i}"
if [ "$i" = 0 ]; then former_md5=$(md5sum < zip_"${i}" | cut -c -32)
else
new_md5=$(md5sum < zip_"${i}" | cut -c -32)
if [ ! "$former_md5" = "$new_md5" ]; then
echo "hashes are not equal: ${former_md5} != ${new_md5}"
fi
former_md5=$new_md5
fi
done Terminal$ /bin/bash ./execute_test.sh
Wrote zip_0
Wrote zip_1
hashes are not equal: f6fa3b5eee984ac735867df9b418bb5b != 6444fa5bc05903aea148cb4c5daeca12 |
We're seeing this issue with 5.36.0, using the hash of a single python source file with no dependencies (so it can't possibly be a pip/packaging issue).
it looks like the state is somehow just not being updated. If I run |
I am seeing the exact same issue with the |
This issue was originally opened by @joerggross as hashicorp/terraform#20152. It was migrated here as a result of the provider split. The original body of the issue is below.
Terraform Version
v0.11.11
Terraform Configuration Files
Debug Output
...
~ module.comp-price-import-data-reader-scheduled-lambda.aws_lambda_function.lambda_function_s3
last_modified: "2019-01-30T11:58:32.826+0000" =>
source_code_hash: "6HVMIk6vxvBy4AApmHbQis5Av2uQeSJh3XRosmKtv0U=" => "ZTg3NTRjMjI0ZWFmYzZmMDcyZTAwMDI5OTg3NmQwOGFjZTQwYmY2YjkwNzkyMjYxZGQ3NDY4YjI2MmFkYmY0NQ=="
Plan: 0 to add, 1 to change, 0 to destroy.
Crash Output
~ module.comp-price-import-data-reader-scheduled-lambda.aws_lambda_function.lambda_function_s3
last_modified: "2019-01-30T11:58:32.826+0000" =>
source_code_hash: "6HVMIk6vxvBy4AApmHbQis5Av2uQeSJh3XRosmKtv0U=" => "ZTg3NTRjMjI0ZWFmYzZmMDcyZTAwMDI5OTg3NmQwOGFjZTQwYmY2YjkwNzkyMjYxZGQ3NDY4YjI2MmFkYmY0NQ=="
Plan: 0 to add, 1 to change, 0 to destroy.
Expected Behavior
We generate an additional file in the s3 bucket along with the lambda jar file to be deployed in s3. The additional file contains a SHA256 hash of the deployed jar file. The hash value of the file is set to the source_code_hash property of the lamba function, by using the bas64 encode function.
We would expect that the hash is stored in the tfsate and reused when applying the scripts, so that the lambda jar file is not redeployed unless the hash changes.
Actual Behavior
We applied the scripts different times without changing the jar or hash file in s3. Nevertheless terraform always redeployes the jar. The output (see above) is always the same ("6HVMIk6vxvBy4AApmHbQis5Av2uQeSJh3XRosmKtv0U=" => "ZTg3NTRjMjI0ZWFmYzZmMDcyZTAwMDI5OTg3NmQwOGFjZTQwYmY2YjkwNzkyMjYxZGQ3NDY4YjI2MmFkYmY0NQ=="). It seems the the given hash is never stored in the tfstate.
The text was updated successfully, but these errors were encountered: