https://johnroach.io/2020/09/04/deploying-lambda-functions-with-terraform-just-dont/
One thing I love about Terraform is how declarative it is. Managing Lambda functions with Terraform is a blast. You can use also Terraform to deploy Lambda functions however there are two issues with this:
Now what to do? You don't want to slow down development but you also want to manage cloud infra with IaC (Infrastructure as Code) solutions.
This post will go over a middle ground. However, let us first go over how a normal terraform deployment would look like:
resource "aws_lambda_function" "test_lambda" {
filename = "lambda_function_payload.zip"
function_name = "lambda_function_name"
role = aws_iam_role.iam_for_lambda.arn
handler = "exports.test"
source_code_hash = filebase64sha256("lambda_function_payload.zip")
runtime = "nodejs12.x"
}
The main issue with this approach is that you are coupling the payload location with Terraform code.
So let us try to de-couple this. The better approach would be to use a S3 bucket:
resource "aws_lambda_function" "test_lambda" {
function_name = "lambda_function_name"
role = aws_iam_role.iam_for_lambda.arn
handler = "exports.test"
source_code_hash = "<source-code-hash>"
s3_bucket = "bucket-for-lambda-zips"
s3_key = "path/lambda_function_payload.zip"
runtime = "nodejs12.x"
}
So now we have decoupled the Lambda artifact from the Terraform code however there is still the issue of the need to update source_code_hash
or other attributes each time the developers update the code.
A good way to solve this is to simply place the source code hash in the bucket.
The deployment code will look like so:
openssl dgst -sha256 -binary lambda_function_payload.zip | openssl enc -base64 | tr -d "\\n" > lambda_function_payload.zip.base64sha256
aws s3 cp --content-type text/plain lambda_function_payload.zip.base64sha256 s3://bucket-for-lambda-zips/path
aws lambda update-function-code --function-name lambda_function_name --s3-bucket bucket-for-lambda-zips --s3-key path/lambda_function_payload.zip
The first two lines of code will need to be added to the developer's CI/CD pipeline which will generate the payload base64 sha and push it as a text/plain
object to the S3 bucket Terraform will reference to this will be needed if you want to keep source_code_hash
in state.
The third line will ensure deployment is done.
So now how to reference a plain object in a S3 bucket in the resource definition?
Well there is an easy solution for that. Simply reference it as a data source.
data "aws_s3_bucket_object" "test_lambda_function_hash" {
bucket = "bucket-for-lambda-zips"
key = "path/lambda_function_payload.zip.base64sha256"
}
resource "aws_lambda_function" "test_lambda" {
function_name = "lambda_function_name"
role = aws_iam_role.iam_for_lambda.arn
handler = "exports.test"
publish = true
source_code_hash = data.aws_s3_bucket_object.test_lambda_hash.body
s3_bucket = "bucket-for-lambda-zips"
s3_key = "path/lambda_function_payload.zip"
runtime = "nodejs12.x"
lifecycle {
ignore_changes = [
"source_code_hash",
"last_modified",
"qualified_arn",
"version"
]
}
}