diff --git a/README.md b/README.md index bf847486..fc110287 100644 --- a/README.md +++ b/README.md @@ -621,7 +621,7 @@ Q2: How to force recreate deployment package? Q3: `null_resource.archive[0] must be replaced` -> Answer: This probably mean that zip-archive has been deployed, but is currently absent locally, and it has to be recreated locally. When you run into this issue during CI/CD process (where workspace is clean) or from multiple workspaces, you can set environment variable `TF_RECREATE_MISSING_LAMBDA_PACKAGE=false` or pass `recreate_missing_package = false` as a parameter to the module and run `terraform apply`. +> Answer: This probably mean that zip-archive has been deployed, but is currently absent locally, and it has to be recreated locally. When you run into this issue during CI/CD process (where workspace is clean) or from multiple workspaces, you can set environment variable `TF_RECREATE_MISSING_LAMBDA_PACKAGE=false` or pass `recreate_missing_package = false` as a parameter to the module and run `terraform apply`. Alternatively, you can pass `trigger_on_package_timestamp = false` as a parameter to ignore the file timestamp when deciding to create the archive or not. Q4: What does this error mean - `"We currently do not support adding policies for $LATEST."` ? @@ -651,6 +651,7 @@ Q4: What does this error mean - `"We currently do not support adding policies fo - [Event Source Mapping](https://github.com/terraform-aws-modules/terraform-aws-lambda/tree/master/examples/event-source-mapping) - Create Lambda Function with event source mapping configuration (SQS, DynamoDB, Amazon MQ, and Kinesis). - [Triggers](https://github.com/terraform-aws-modules/terraform-aws-lambda/tree/master/examples/triggers) - Create Lambda Function with some triggers (eg, Cloudwatch Events, EventBridge). - [Code Signing](https://github.com/terraform-aws-modules/terraform-aws-lambda/tree/master/examples/code-signing) - Create Lambda Function with code signing configuration. +- [Simple CI/CD](https://github.com/terraform-aws-modules/terraform-aws-lambda/tree/master/examples/simple-cicd) - Create Lambda Function as if it runs on CI/CD platform where `builds` directory is often absent. # Examples by the users of this module @@ -852,6 +853,7 @@ No modules. | [timeout](#input\_timeout) | The amount of time your Lambda Function has to run in seconds. | `number` | `3` | no | | [timeouts](#input\_timeouts) | Define maximum timeout for creating, updating, and deleting Lambda Function resources | `map(string)` | `{}` | no | | [tracing\_mode](#input\_tracing\_mode) | Tracing mode of the Lambda Function. Valid value can be either PassThrough or Active. | `string` | `null` | no | +| [trigger\_on\_package\_timestamp](#input\_trigger\_on\_package\_timestamp) | Whether to recreate the Lambda package if the timestamp changes | `bool` | `true` | no | | [trusted\_entities](#input\_trusted\_entities) | List of additional trusted entities for assuming Lambda Function role (trust relationship) | `any` | `[]` | no | | [use\_existing\_cloudwatch\_log\_group](#input\_use\_existing\_cloudwatch\_log\_group) | Whether to use an existing CloudWatch log group or create new | `bool` | `false` | no | | [vpc\_security\_group\_ids](#input\_vpc\_security\_group\_ids) | List of security group ids when Lambda Function should run in the VPC. | `list(string)` | `null` | no | diff --git a/examples/fixtures/python3.10-app1/index.py b/examples/fixtures/python3.10-app1/index.py new file mode 100644 index 00000000..396c5054 --- /dev/null +++ b/examples/fixtures/python3.10-app1/index.py @@ -0,0 +1,4 @@ +def lambda_handler(event, context): + print("Hello from app1!") + + return event diff --git a/examples/simple-cicd/.gitignore b/examples/simple-cicd/.gitignore new file mode 100644 index 00000000..a57582cc --- /dev/null +++ b/examples/simple-cicd/.gitignore @@ -0,0 +1 @@ +/src diff --git a/examples/simple-cicd/README.md b/examples/simple-cicd/README.md new file mode 100644 index 00000000..05f93a51 --- /dev/null +++ b/examples/simple-cicd/README.md @@ -0,0 +1,53 @@ +# Simple CI/CD example + +Configuration in this directory creates AWS Lambda Function as it would run in a context of CICD executions, where the Terraform working directory is empty and there is no `builds` directory, that: + +- `terraform plan` doesn't trigger a diff if the source code of the lambda function didn't change. +- `terraform plan` does trigger a diff if the source code of the lambda function has changed. +- `terraform apply` works if the code has changed. + +## Usage + +To run this example you need to execute: + +```bash +./test.sh +``` + +Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.63 | +| [random](#requirement\_random) | >= 2.0 | + +## Providers + +| Name | Version | +|------|---------| +| [random](#provider\_random) | >= 2.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [lambda\_function](#module\_lambda\_function) | ../../ | n/a | + +## Resources + +| Name | Type | +|------|------| +| [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | + +## Inputs + +No inputs. + +## Outputs + +No outputs. + diff --git a/examples/simple-cicd/main.tf b/examples/simple-cicd/main.tf new file mode 100644 index 00000000..66afc150 --- /dev/null +++ b/examples/simple-cicd/main.tf @@ -0,0 +1,26 @@ +provider "aws" { + region = "eu-west-1" + # region = "us-east-1" + + # Make it faster by skipping something + skip_metadata_api_check = true + skip_region_validation = true + skip_credentials_validation = true +} + +resource "random_pet" "this" { + length = 2 +} + +module "lambda_function" { + source = "../../" + + function_name = "${random_pet.this.id}-lambda-simple" + handler = "index.lambda_handler" + runtime = "python3.10" + + source_path = [ + "${path.module}/src/python3.10-app1", + ] + trigger_on_package_timestamp = false +} diff --git a/examples/simple-cicd/outputs.tf b/examples/simple-cicd/outputs.tf new file mode 100644 index 00000000..e69de29b diff --git a/examples/simple-cicd/test.sh b/examples/simple-cicd/test.sh new file mode 100755 index 00000000..90ce9804 --- /dev/null +++ b/examples/simple-cicd/test.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +# vim:ts=4:sw=4:noet + +set -eo pipefail + +trap ctrl_c INT + +ctrl_c() { + echo "** Trapped CTRL-C" + exit 1 +} + +failed=0 + +:echo() { + local color=${2:-"33;1"} + echo -e "\e[${color}m$1\e[0m" +} + +:note() { + :echo "$1" "35;1" +} + +:case() { + if [ $? -ne 0 ] + then failed=1 + fi + + if [ "$failed" -eq 1 ] + then :echo "SKIPPED: $1"; return 1 + else echo; :echo "CASE: $1" + fi +} + +:check_diff() { + expected="$1" + + set +e + terraform plan -detailed-exitcode + status=$? + set -e + # ${status} possible values: + # 0 - Succeeded, diff is empty (no changes) + # 1 - Errored + # 2 - Succeeded, there is a diff + if [ "${status}" -ne "${expected}" ]; then + case "${expected}" in + 0) + :echo "Error: we don't expect any diff here!" + return 1 + ;; + 2) + echo "Error: we DO expect some diff here!" + return 1 + ;; + esac + fi +} + +terraform=$(which terraform) +terraform() { + $terraform "$@" < <(yes yes) +} + +:note "Preparing ..." +rm -rf src +mkdir -p src +cp -r "../fixtures/python3.10-app1" src +terraform init +:echo "Destroy / Remove ZIP files" +terraform destroy +rm -rf builds 2>/dev/null || true + +############################################################# +# Part 1: Check that CICD environment won't detect any diff # +############################################################# + +:echo +:note "Starting Part 1: Check that CICD environment won't detect any diff" + +:case "Apply / No diff" && { + terraform apply + :check_diff 0 +} + +:case "Remove 'builds' dir / No diff" && { + rm -rf builds + :check_diff 0 +} + +############################################################################### +# Part 2: Check that CICD environment will detect diff if lambda code changes # +############################################################################### + +:echo +:note "Starting Part 2: Check that CICD environment will detect diff if lambda code changes" + +:note "Change the source code / Remove 'builds' dir" +echo "" >> src/python3.10-app1/index.py +rm -rf builds + +:case "Plan / Expect diff" && { + terraform plan + :check_diff 2 +} + +:case "Apply / No diff" && { + terraform apply + :check_diff 0 +} + +:note "Remove 'builds' dir" +rm -rf builds + +:case "Plan / No diff" && { + terraform plan + :check_diff 0 +} + +#:case "Destroy / Remove ZIP files" && { +# terraform plan -destroy +# terraform destroy -auto-approve +# rm builds/*.zip +#} + +:note "All tests have passed successfully." diff --git a/examples/simple-cicd/variables.tf b/examples/simple-cicd/variables.tf new file mode 100644 index 00000000..e69de29b diff --git a/examples/simple-cicd/versions.tf b/examples/simple-cicd/versions.tf new file mode 100644 index 00000000..5afa48b5 --- /dev/null +++ b/examples/simple-cicd/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.63" + } + random = { + source = "hashicorp/random" + version = ">= 2.0" + } + } +} diff --git a/package.tf b/package.tf index b68dc89f..34322514 100644 --- a/package.tf +++ b/package.tf @@ -61,7 +61,7 @@ resource "null_resource" "archive" { triggers = { filename = data.external.archive_prepare[0].result.filename - timestamp = data.external.archive_prepare[0].result.timestamp + timestamp = var.trigger_on_package_timestamp ? data.external.archive_prepare[0].result.timestamp : null } provisioner "local-exec" { diff --git a/variables.tf b/variables.tf index 61198bea..28a2b82e 100644 --- a/variables.tf +++ b/variables.tf @@ -761,3 +761,9 @@ variable "recreate_missing_package" { type = bool default = true } + +variable "trigger_on_package_timestamp" { + description = "Whether to recreate the Lambda package if the timestamp changes" + type = bool + default = true +} diff --git a/wrappers/main.tf b/wrappers/main.tf index 1ae65252..a5f8683c 100644 --- a/wrappers/main.tf +++ b/wrappers/main.tf @@ -119,6 +119,7 @@ module "wrapper" { timeout = try(each.value.timeout, var.defaults.timeout, 3) timeouts = try(each.value.timeouts, var.defaults.timeouts, {}) tracing_mode = try(each.value.tracing_mode, var.defaults.tracing_mode, null) + trigger_on_package_timestamp = try(each.value.trigger_on_package_timestamp, var.defaults.trigger_on_package_timestamp, true) trusted_entities = try(each.value.trusted_entities, var.defaults.trusted_entities, []) use_existing_cloudwatch_log_group = try(each.value.use_existing_cloudwatch_log_group, var.defaults.use_existing_cloudwatch_log_group, false) vpc_security_group_ids = try(each.value.vpc_security_group_ids, var.defaults.vpc_security_group_ids, null)