├── VERSION.txt ├── versions.tf ├── .gitignore ├── modules ├── lambda │ ├── versions.tf │ ├── README.md │ ├── outputs.tf │ ├── main.tf │ └── variables.tf └── event │ ├── s3 │ ├── versions.tf │ ├── main.tf │ └── variables.tf │ ├── sns │ ├── versions.tf │ ├── main.tf │ └── variables.tf │ ├── sqs │ ├── versions.tf │ ├── main.tf │ └── variables.tf │ ├── dynamodb │ ├── versions.tf │ ├── main.tf │ └── variables.tf │ ├── kinesis │ ├── versions.tf │ ├── variables.tf │ └── main.tf │ └── cloudwatch-event │ ├── versions.tf │ ├── main.tf │ └── variables.tf ├── examples ├── example-with-vpc │ ├── versions.tf │ ├── README.md │ └── main.tf ├── example-with-s3-event │ ├── versions.tf │ ├── README.md │ └── main.tf ├── example-with-sns-event │ ├── versions.tf │ ├── README.md │ └── main.tf ├── example-with-sqs-event │ ├── versions.tf │ ├── README.md │ └── main.tf ├── example-without-event │ ├── versions.tf │ ├── README.md │ └── main.tf ├── example-with-cloudwatch-event │ ├── versions.tf │ ├── README.md │ └── main.tf ├── example-with-dynamodb-event │ ├── versions.tf │ ├── README.md │ └── main.tf └── example-with-kinesis-event │ ├── versions.tf │ ├── README.md │ └── main.tf ├── outputs.tf ├── .github └── workflows │ └── workflow.yaml ├── LICENSE ├── Makefile ├── CONTRIBUTING.md ├── variables.tf ├── main.tf └── README.md /VERSION.txt: -------------------------------------------------------------------------------- 1 | v5.2.1 2 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | terraform.tfstate 3 | terraform.tfstate.backup 4 | 5 | bin/ -------------------------------------------------------------------------------- /modules/lambda/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /modules/event/s3/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /modules/event/sns/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /modules/event/sqs/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /modules/event/dynamodb/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /modules/event/kinesis/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /examples/example-with-vpc/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /examples/example-with-s3-event/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /examples/example-with-sns-event/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /examples/example-with-sqs-event/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /examples/example-without-event/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /modules/event/cloudwatch-event/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /examples/example-with-cloudwatch-event/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /examples/example-with-dynamodb-event/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /examples/example-with-kinesis-event/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /modules/event/s3/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_lambda_permission" "allow_bucket" { 2 | count = var.enable ? 1 : 0 3 | action = "lambda:InvokeFunction" 4 | function_name = var.lambda_function_arn 5 | principal = "s3.amazonaws.com" 6 | statement_id = "AllowExecutionFromS3Bucket" 7 | source_arn = var.s3_bucket_arn 8 | } 9 | -------------------------------------------------------------------------------- /examples/example-without-event/README.md: -------------------------------------------------------------------------------- 1 | # Example without event 2 | 3 | Creates an AWS Lambda function without an event trigger. 4 | 5 | ## requirements 6 | 7 | - [Terraform 0.12+](https://www.terraform.io/) 8 | - authentication configuration for the [aws provider](https://www.terraform.io/docs/providers/aws/) 9 | 10 | ## usage 11 | 12 | ``` 13 | terraform init 14 | terraform plan 15 | ``` 16 | -------------------------------------------------------------------------------- /examples/example-with-sns-event/README.md: -------------------------------------------------------------------------------- 1 | # Example with SNS event 2 | 3 | Creates an AWS Lambda function subscribed to a SNS topic. 4 | 5 | ## requirements 6 | 7 | - [Terraform 0.12+](https://www.terraform.io/) 8 | - authentication configuration for the [aws provider](https://www.terraform.io/docs/providers/aws/) 9 | 10 | ## usage 11 | 12 | ``` 13 | terraform init 14 | terraform plan 15 | ``` 16 | -------------------------------------------------------------------------------- /examples/example-with-s3-event/README.md: -------------------------------------------------------------------------------- 1 | # Example with S3 event 2 | 3 | Creates an AWS Lambda function triggered by a S3 [event](https://docs.aws.amazon.com/lambda/latest/dg/with-s3.html). 4 | 5 | ## requirements 6 | 7 | - [Terraform 0.12+](https://www.terraform.io/) 8 | - authentication configuration for the [aws provider](https://www.terraform.io/docs/providers/aws/) 9 | 10 | ## usage 11 | 12 | ``` 13 | terraform init 14 | terraform plan 15 | ``` 16 | -------------------------------------------------------------------------------- /examples/example-with-sqs-event/README.md: -------------------------------------------------------------------------------- 1 | # Example with SQS event 2 | 3 | Creates an AWS Lambda function triggered by a SQS [event](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html). 4 | 5 | ## requirements 6 | 7 | - [Terraform 0.12+](https://www.terraform.io/) 8 | - authentication configuration for the [aws provider](https://www.terraform.io/docs/providers/aws/) 9 | 10 | ## usage 11 | 12 | ``` 13 | $ terraform init 14 | $ terraform plan 15 | ``` 16 | -------------------------------------------------------------------------------- /examples/example-with-kinesis-event/README.md: -------------------------------------------------------------------------------- 1 | # Example with Kinesis event 2 | 3 | Creates an AWS Lambda function triggered by a Kinesis [event](https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html). 4 | 5 | ## requirements 6 | 7 | - [Terraform 0.12+](https://www.terraform.io/) 8 | - authentication configuration for the [aws provider](https://www.terraform.io/docs/providers/aws/) 9 | 10 | ## usage 11 | 12 | ``` 13 | $ terraform init 14 | $ terraform plan 15 | ``` 16 | -------------------------------------------------------------------------------- /examples/example-with-cloudwatch-event/README.md: -------------------------------------------------------------------------------- 1 | # Example with CloudWatch events 2 | 3 | Creates AWS Lambda functions triggered by a CloudWatch [events](https://docs.aws.amazon.com/lambda/latest/dg/with-scheduled-events.html). 4 | 5 | ## requirements 6 | 7 | - [Terraform 0.12+](https://www.terraform.io/) 8 | - authentication configuration for the [aws provider](https://www.terraform.io/docs/providers/aws/) 9 | 10 | ## usage 11 | 12 | ```bash 13 | $ terraform init 14 | $ terraform plan 15 | ``` 16 | -------------------------------------------------------------------------------- /examples/example-without-event/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | } 4 | 5 | module "lambda" { 6 | source = "../../" 7 | description = "Example AWS Lambda using go with cloudwatch scheduled event trigger" 8 | filename = "${path.module}/test_function.zip" 9 | function_name = "tf-example-go-basic" 10 | handler = "example-lambda-func" 11 | runtime = "go1.x" 12 | source_code_hash = filebase64sha256("${path.module}/test_function.zip") 13 | } 14 | -------------------------------------------------------------------------------- /modules/event/sns/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_lambda_permission" "sns" { 2 | count = var.enable ? 1 : 0 3 | action = "lambda:InvokeFunction" 4 | function_name = var.function_name 5 | principal = "sns.amazonaws.com" 6 | statement_id = "AllowSubscriptionToSNS" 7 | source_arn = var.topic_arn 8 | } 9 | 10 | resource "aws_sns_topic_subscription" "subscription" { 11 | count = var.enable ? 1 : 0 12 | endpoint = var.endpoint 13 | protocol = "lambda" 14 | topic_arn = var.topic_arn 15 | } 16 | -------------------------------------------------------------------------------- /examples/example-with-dynamodb-event/README.md: -------------------------------------------------------------------------------- 1 | # Example with DynamoDb event 2 | 3 | Creates an AWS Lambda function triggered by a DynamoDb [event](https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html#supported-event-source-dynamo-db). 4 | 5 | ## requirements 6 | 7 | - [Terraform 0.12+](https://www.terraform.io/) 8 | - authentication configuration for the [aws provider](https://www.terraform.io/docs/providers/aws/) 9 | 10 | ## usage 11 | 12 | ``` 13 | $ terraform init 14 | $ terraform plan 15 | ``` 16 | -------------------------------------------------------------------------------- /examples/example-with-vpc/README.md: -------------------------------------------------------------------------------- 1 | # Example with VPC and CloudWatch scheduled event 2 | 3 | Creates an AWS Lambda function inside a VPC triggered by a CloudWatch (scheduled) [event](https://docs.aws.amazon.com/lambda/latest/dg/with-scheduled-events.html). 4 | 5 | ## requirements 6 | 7 | - [Terraform 0.12+](https://www.terraform.io/) 8 | - authentication configuration for the [aws provider](https://www.terraform.io/docs/providers/aws/) 9 | 10 | ## usage 11 | 12 | To generate and show the execution plan run 13 | 14 | ``` 15 | terraform init 16 | terraform plan 17 | ``` 18 | -------------------------------------------------------------------------------- /modules/event/sns/variables.tf: -------------------------------------------------------------------------------- 1 | variable "enable" { 2 | description = "Conditionally enables this module (and all it's ressources)." 3 | type = bool 4 | default = false 5 | } 6 | 7 | variable "endpoint" { 8 | description = "The endpoint to send data to (ARN of the Lambda function)" 9 | } 10 | 11 | variable "function_name" { 12 | description = "Name of the Lambda function whose resource policy should be allowed to subscribe to SNS topics." 13 | } 14 | 15 | variable "topic_arn" { 16 | description = "The ARN of the SNS topic to subscribe to" 17 | } 18 | 19 | -------------------------------------------------------------------------------- /modules/lambda/README.md: -------------------------------------------------------------------------------- 1 | # Lambda Module 2 | 3 | Terraform module to create AWS [Lambda](https://www.terraform.io/docs/providers/aws/r/lambda_function.html) resources with IAM role configuration and VPC support. 4 | 5 | ## How to use this module 6 | 7 | Configure the Lambda function with all required variables: 8 | 9 | ``` 10 | provider "aws" { 11 | region = "eu-west-1" 12 | } 13 | 14 | module "lambda" { 15 | source = "spring-media/lambda/aws//modules/lambda" 16 | filename = "my-package.zip" 17 | function_name = "my-function" 18 | handler = "my-handler" 19 | runtime = "go1.x" 20 | } 21 | ``` 22 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "arn" { 2 | description = "The Amazon Resource Name (ARN) identifying your Lambda Function." 3 | value = module.lambda.arn 4 | } 5 | 6 | output "function_name" { 7 | description = "The unique name of your Lambda Function." 8 | value = module.lambda.function_name 9 | } 10 | 11 | output "invoke_arn" { 12 | description = "The ARN to be used for invoking Lambda Function from API Gateway - to be used in aws_api_gateway_integration's uri" 13 | value = module.lambda.invoke_arn 14 | } 15 | 16 | output "role_name" { 17 | description = "The name of the IAM role attached to the Lambda Function." 18 | value = module.lambda.role_name 19 | } 20 | 21 | -------------------------------------------------------------------------------- /modules/lambda/outputs.tf: -------------------------------------------------------------------------------- 1 | output "arn" { 2 | description = "The Amazon Resource Name (ARN) identifying your Lambda Function." 3 | value = aws_lambda_function.lambda.arn 4 | } 5 | 6 | output "function_name" { 7 | description = "The unique name of your Lambda Function." 8 | value = aws_lambda_function.lambda.function_name 9 | } 10 | 11 | output "invoke_arn" { 12 | description = "The ARN to be used for invoking Lambda Function from API Gateway - to be used in aws_api_gateway_integration's uri" 13 | value = aws_lambda_function.lambda.invoke_arn 14 | } 15 | 16 | output "role_name" { 17 | description = "The name of the IAM attached to the Lambda Function." 18 | value = aws_iam_role.lambda.name 19 | } 20 | 21 | -------------------------------------------------------------------------------- /examples/example-with-sns-event/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | } 4 | 5 | module "lambda" { 6 | source = "../../" 7 | description = "Example AWS Lambda using go with sns trigger" 8 | filename = "${path.module}/test_function.zip" 9 | function_name = "tf-example-go-sns" 10 | handler = "example-lambda-func" 11 | runtime = "go1.x" 12 | source_code_hash = filebase64sha256("${path.module}/test_function.zip") 13 | 14 | event = { 15 | type = "sns" 16 | topic_arn = "arn:aws:sns:eu-west-1:123456789123:test-topic" 17 | } 18 | 19 | tags = { 20 | key = "value" 21 | } 22 | 23 | environment = { 24 | variables = { 25 | key = "value" 26 | } 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /.github/workflows/workflow.yaml: -------------------------------------------------------------------------------- 1 | name: Terraform CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | push: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | validate: 13 | name: Validate 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Check out code 17 | uses: actions/checkout@v1 18 | 19 | - name: Run a Terraform init 20 | uses: docker://hashicorp/terraform:0.12.20 21 | with: 22 | entrypoint: terraform 23 | args: init 24 | 25 | - name: Run a Terraform fmt 26 | uses: docker://hashicorp/terraform:0.12.20 27 | with: 28 | entrypoint: terraform 29 | args: fmt -check=true 30 | 31 | - name: Run a Terraform validate 32 | uses: docker://hashicorp/terraform:0.12.20 33 | env: 34 | AWS_REGION: eu-west-1 35 | with: 36 | entrypoint: terraform 37 | args: validate 38 | -------------------------------------------------------------------------------- /examples/example-with-vpc/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | } 4 | 5 | module "lambda" { 6 | source = "../../" 7 | description = "Example AWS Lambda inside a VPC using go with cloudwatch scheduled event trigger" 8 | filename = "${path.module}/test_function.zip" 9 | function_name = "tf-example-go-basic-vpc" 10 | handler = "example-lambda-func" 11 | runtime = "go1.x" 12 | source_code_hash = filebase64sha256("${path.module}/test_function.zip") 13 | 14 | vpc_config = { 15 | subnet_ids = ["subnet-123456", "subnet-123457"] 16 | security_group_ids = ["sg-123456"] 17 | } 18 | 19 | event = { 20 | type = "cloudwatch-scheduled-event" 21 | schedule_expression = "rate(1 minute)" 22 | } 23 | 24 | tags = { 25 | key = "value" 26 | } 27 | 28 | environment = { 29 | variables = { 30 | key = "value" 31 | } 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /modules/event/cloudwatch-event/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_lambda_permission" "cloudwatch" { 2 | count = var.enable ? 1 : 0 3 | statement_id = "AllowExecutionFromCloudWatch" 4 | action = "lambda:InvokeFunction" 5 | function_name = var.lambda_function_arn 6 | principal = "events.amazonaws.com" 7 | source_arn = aws_cloudwatch_event_rule.lambda[count.index].arn 8 | } 9 | 10 | resource "aws_cloudwatch_event_rule" "lambda" { 11 | count = var.enable ? 1 : 0 12 | description = var.description 13 | event_pattern = var.event_pattern 14 | is_enabled = var.is_enabled 15 | name = var.name 16 | name_prefix = var.name_prefix 17 | schedule_expression = var.schedule_expression 18 | } 19 | 20 | resource "aws_cloudwatch_event_target" "lambda" { 21 | count = var.enable ? 1 : 0 22 | rule = aws_cloudwatch_event_rule.lambda[count.index].name 23 | arn = var.lambda_function_arn 24 | } 25 | 26 | -------------------------------------------------------------------------------- /examples/example-with-sqs-event/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | } 4 | 5 | module "lambda" { 6 | source = "../../" 7 | filename = "${path.module}/test_function.zip" 8 | function_name = "my-function" 9 | handler = "my-handler" 10 | runtime = "go1.x" 11 | source_code_hash = filebase64sha256("${path.module}/test_function.zip") 12 | 13 | event = { 14 | type = "sqs" 15 | event_source_arn = "arn:aws:kinesis:eu-west-1:647379381847:queue-name" 16 | } 17 | 18 | # optionally configure Parameter Store access with decryption 19 | ssm_parameter_names = ["some/config/root/*"] 20 | kms_key_arn = "arn:aws:kms:eu-west-1:647379381847:key/f79f2b-04684-4ad9-f9de8a-79d72f" 21 | 22 | # optionally create a log subscription for streaming log events 23 | logfilter_destination_arn = "arn:aws:lambda:eu-west-1:647379381847:function:cloudwatch_logs_to_es_production" 24 | 25 | tags = { 26 | key = "value" 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /examples/example-with-kinesis-event/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | } 4 | 5 | module "lambda" { 6 | source = "../../" 7 | filename = "${path.module}/test_function.zip" 8 | function_name = "my-function" 9 | handler = "my-handler" 10 | runtime = "go1.x" 11 | source_code_hash = filebase64sha256("${path.module}/test_function.zip") 12 | 13 | event = { 14 | type = "kinesis" 15 | event_source_arn = "arn:aws:kinesis:eu-west-1:647379381847:stream/my-stream" 16 | } 17 | 18 | # optionally configure Parameter Store access with decryption 19 | ssm_parameter_names = ["some/config/root/*"] 20 | kms_key_arn = "arn:aws:kms:eu-west-1:647379381847:key/f79f2b-04684-4ad9-f9de8a-79d72f" 21 | 22 | # optionally create a log subscription for streaming log events 23 | logfilter_destination_arn = "arn:aws:lambda:eu-west-1:647379381847:function:cloudwatch_logs_to_es_production" 24 | 25 | tags = { 26 | key = "value" 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /examples/example-with-s3-event/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | } 4 | 5 | resource "aws_s3_bucket_notification" "bucket_notification" { 6 | bucket = "bucketname" 7 | 8 | lambda_function { 9 | lambda_function_arn = module.lambda.arn 10 | events = ["s3:ObjectCreated:*"] 11 | } 12 | } 13 | 14 | module "lambda" { 15 | source = "../../" 16 | description = "Example AWS Lambda using go with S3 trigger" 17 | filename = "${path.module}/test_function.zip" 18 | function_name = "tf-example-go-s3" 19 | handler = "example-lambda-func" 20 | runtime = "go1.x" 21 | source_code_hash = filebase64sha256("${path.module}/test_function.zip") 22 | 23 | event = { 24 | type = "s3" 25 | s3_bucket_arn = "arn:aws:s3:::bucketname" 26 | s3_bucket_id = "bucketname" 27 | } 28 | 29 | tags = { 30 | key = "value" 31 | } 32 | 33 | environment = { 34 | variables = { 35 | key = "value" 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /examples/example-with-dynamodb-event/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | } 4 | 5 | module "lambda" { 6 | source = "../../" 7 | filename = "${path.module}/test_function.zip" 8 | function_name = "my-function" 9 | handler = "my-handler" 10 | runtime = "go1.x" 11 | source_code_hash = filebase64sha256("${path.module}/test_function.zip") 12 | 13 | event = { 14 | type = "dynamodb" 15 | event_source_arn = "arn:aws:dynamodb:eu-west-1:647379381847:table/some-table/stream/some-identifier" 16 | } 17 | 18 | # optionally configure Parameter Store access with decryption 19 | ssm_parameter_names = ["some/config/root/*"] 20 | kms_key_arn = "arn:aws:kms:eu-west-1:647379381847:key/f79f2b-04684-4ad9-f9de8a-79d72f" 21 | 22 | # optionally create a log subscription for streaming log events 23 | logfilter_destination_arn = "arn:aws:lambda:eu-west-1:647379381847:function:cloudwatch_logs_to_es_production" 24 | 25 | tags = { 26 | key = "value" 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 spring-media 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /modules/event/s3/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "lambda_function_arn" { 7 | description = "The Amazon Resource Name (ARN) identifying the Lambda Function triggered by S3" 8 | } 9 | 10 | variable "s3_bucket_arn" { 11 | description = "The ARN of the bucket." 12 | } 13 | 14 | variable "s3_bucket_id" { 15 | description = "The name of the bucket." 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "enable" { 24 | description = "Conditionally enables this module (and all it's ressources)." 25 | type = bool 26 | default = false 27 | } 28 | -------------------------------------------------------------------------------- /examples/example-with-cloudwatch-event/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-west-1" 3 | } 4 | 5 | module "lambda-scheduled" { 6 | source = "../../" 7 | description = "Example AWS Lambda using go with cloudwatch scheduled event trigger" 8 | filename = "${path.module}/test_function.zip" 9 | function_name = "tf-example-go-basic" 10 | handler = "example-lambda-func" 11 | runtime = "go1.x" 12 | source_code_hash = filebase64sha256("${path.module}/test_function.zip") 13 | 14 | event = { 15 | type = "cloudwatch-event" 16 | schedule_expression = "rate(1 minute)" 17 | } 18 | } 19 | 20 | module "lambda-pattern" { 21 | source = "../../" 22 | description = "Example AWS Lambda using go with cloudwatch event pattern trigger" 23 | filename = "${path.module}/test_function.zip" 24 | function_name = "tf-example-go-basic" 25 | handler = "example-lambda-func" 26 | runtime = "go1.x" 27 | source_code_hash = filebase64sha256("${path.module}/test_function.zip") 28 | 29 | event = { 30 | type = "cloudwatch-event" 31 | event_pattern = < 0 ? "${regex("arn.*\\/", var.event_source_arn)}*" : "" 25 | ] 26 | } 27 | 28 | statement { 29 | actions = [ 30 | "kinesis:DescribeStream", 31 | "kinesis:DescribeStreamSummary", 32 | "kinesis:GetRecords", 33 | "kinesis:GetShardIterator" 34 | ] 35 | 36 | resources = [ 37 | var.event_source_arn 38 | ] 39 | } 40 | } 41 | 42 | resource "aws_iam_policy" "stream_policy" { 43 | count = var.enable ? 1 : 0 44 | name = "${var.function_name}-stream-consumer-${data.aws_region.current.name}" 45 | description = "Gives permission to list and read a Kinesis stream to ${var.function_name}." 46 | policy = data.aws_iam_policy_document.stream_policy_document.json 47 | } 48 | 49 | resource "aws_iam_role_policy_attachment" "stream_policy_attachment" { 50 | count = var.enable ? 1 : 0 51 | role = var.iam_role_name 52 | policy_arn = aws_iam_policy.stream_policy[count.index].arn 53 | } 54 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION := $(shell cat VERSION.txt) 2 | PREFIX?=$(shell pwd) 3 | EXAMPLES_DIR := examples 4 | 5 | ## Tools 6 | BINDIR := $(PREFIX)/bin 7 | export GOBIN :=$(BINDIR) 8 | export PATH := $(GOBIN):$(PATH) 9 | SEMBUMP := $(BINDIR)/sembump 10 | 11 | all: init fmt validate 12 | 13 | .PHONY: init 14 | init: ## Initialize a Terraform working directory 15 | @echo "+ $@" 16 | @terraform init 17 | 18 | .PHONY: fmt 19 | fmt: ## Rewrites config files to canonical format 20 | @echo "+ $@" 21 | @terraform fmt -check=true 22 | 23 | .PHONY: validate 24 | validate: ## Validates the Terraform files 25 | @echo "+ $@" 26 | @AWS_REGION=eu-west-1 terraform validate 27 | 28 | .PHONY: test 29 | test: ## Validates and generates execution plan for all examples. 30 | @echo "+ $@" 31 | @for dir in `ls $(EXAMPLES_DIR)`; do \ 32 | echo "--> $$dir"; \ 33 | terraform init $(PREFIX)/$(EXAMPLES_DIR)/$$dir/ > /dev/null; \ 34 | terraform validate $(PREFIX)/$(EXAMPLES_DIR)/$$dir/; \ 35 | terraform plan $(PREFIX)/$(EXAMPLES_DIR)/$$dir/ > /dev/null; \ 36 | rm -rf $(PREFIX)/$(EXAMPLES_DIR)/$$dir/.terraform; \ 37 | done 38 | 39 | .PHONY: tag 40 | tag: ## Create a new git tag to prepare to build a release 41 | git tag -a $(VERSION) -m "$(VERSION)" 42 | @echo "Run git push origin $(VERSION) to push your new tag to GitHub and trigger a build." 43 | 44 | $(SEMBUMP): 45 | GO111MODULE=off go get -u github.com/jessfraz/junk/sembump 46 | 47 | .PHONY: bump-version 48 | BUMP := patch 49 | bump-version: $(SEMBUMP) ## Bump the version in the version file. Set BUMP to [ patch | major | minor ]. 50 | $(eval NEW_VERSION = $(shell $(BINDIR)/sembump --kind $(BUMP) $(VERSION))) 51 | @echo "Bumping VERSION.txt from $(VERSION) to $(NEW_VERSION)" 52 | echo $(NEW_VERSION) > VERSION.txt 53 | @echo "Updating links in README.md" 54 | sed -i '' s/$(subst v,,$(VERSION))/$(subst v,,$(NEW_VERSION))/g README.md 55 | git add VERSION.txt README.md 56 | git commit -vsam "Bump version to $(NEW_VERSION)" 57 | @echo "Run make tag to create and push the tag for new version $(NEW_VERSION)" 58 | 59 | .PHONY: help 60 | help: ## Display this help screen 61 | @grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' -------------------------------------------------------------------------------- /modules/lambda/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_region" "current" { 2 | } 3 | 4 | resource "aws_lambda_function" "lambda" { 5 | description = var.description 6 | dynamic "environment" { 7 | for_each = length(var.environment) < 1 ? [] : [var.environment] 8 | content { 9 | variables = environment.value.variables 10 | } 11 | } 12 | filename = var.s3_bucket == "" ? var.filename : null 13 | function_name = var.function_name 14 | handler = var.handler 15 | memory_size = var.memory_size 16 | publish = var.publish 17 | reserved_concurrent_executions = var.reserved_concurrent_executions 18 | role = aws_iam_role.lambda.arn 19 | runtime = var.runtime 20 | s3_bucket = var.filename == "" ? var.s3_bucket : null 21 | s3_key = var.filename == "" ? var.s3_key : null 22 | s3_object_version = var.filename == "" ? var.s3_object_version : null 23 | source_code_hash = var.source_code_hash 24 | tags = var.tags 25 | timeout = var.timeout 26 | 27 | dynamic "vpc_config" { 28 | for_each = length(var.vpc_config) < 1 ? [] : [var.vpc_config] 29 | content { 30 | security_group_ids = vpc_config.value.security_group_ids 31 | subnet_ids = vpc_config.value.subnet_ids 32 | } 33 | } 34 | } 35 | 36 | data "aws_iam_policy_document" "assume_role_policy" { 37 | statement { 38 | actions = ["sts:AssumeRole"] 39 | 40 | principals { 41 | type = "Service" 42 | identifiers = ["lambda.amazonaws.com"] 43 | } 44 | } 45 | } 46 | 47 | resource "aws_iam_role" "lambda" { 48 | name = "${var.function_name}-${data.aws_region.current.name}" 49 | assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json 50 | } 51 | 52 | resource "aws_iam_role_policy_attachment" "cloudwatch_logs" { 53 | role = aws_iam_role.lambda.name 54 | policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 55 | } 56 | 57 | resource "aws_iam_role_policy_attachment" "vpc_attachment" { 58 | count = length(var.vpc_config) < 1 ? 0 : 1 59 | role = aws_iam_role.lambda.name 60 | 61 | // see https://docs.aws.amazon.com/lambda/latest/dg/vpc.html 62 | policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" 63 | } 64 | -------------------------------------------------------------------------------- /modules/lambda/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "filename" { 7 | description = "The path to the function's deployment package within the local filesystem." 8 | } 9 | 10 | variable "function_name" { 11 | description = "A unique name for your Lambda Function." 12 | } 13 | 14 | variable "handler" { 15 | description = "The function entrypoint in your code." 16 | } 17 | 18 | variable "runtime" { 19 | description = "The runtime environment for the Lambda function you are uploading." 20 | } 21 | 22 | # --------------------------------------------------------------------------------------------------------------------- 23 | # OPTIONAL PARAMETERS 24 | # These parameters have reasonable defaults. 25 | # --------------------------------------------------------------------------------------------------------------------- 26 | 27 | variable "description" { 28 | description = "Description of what your Lambda Function does." 29 | default = "" 30 | } 31 | 32 | variable "environment" { 33 | description = "Environment (e.g. env variables) configuration for the Lambda function enable you to dynamically pass settings to your function code and libraries" 34 | type = map(map(string)) 35 | default = {} 36 | } 37 | 38 | variable "memory_size" { 39 | description = "Amount of memory in MB your Lambda Function can use at runtime. Defaults to 128." 40 | default = 128 41 | } 42 | 43 | variable "publish" { 44 | description = "Whether to publish creation/change as new Lambda Function Version. Defaults to false." 45 | default = false 46 | } 47 | 48 | variable "reserved_concurrent_executions" { 49 | description = "The amount of reserved concurrent executions for this lambda function. A value of 0 disables lambda from being triggered and -1 removes any concurrency limitations. Defaults to Unreserved Concurrency Limits -1." 50 | default = "-1" 51 | } 52 | 53 | variable "s3_bucket" { 54 | description = "The S3 bucket location containing the function's deployment package. Conflicts with filename. This bucket must reside in the same AWS region where you are creating the Lambda function." 55 | default = "" 56 | } 57 | 58 | variable "s3_key" { 59 | description = " The S3 key of an object containing the function's deployment package. Conflicts with filename." 60 | default = "" 61 | } 62 | 63 | variable "s3_object_version" { 64 | description = "The object version containing the function's deployment package. Conflicts with filename." 65 | default = "" 66 | } 67 | 68 | variable "source_code_hash" { 69 | description = "Used to trigger updates. Must be set to a base64-encoded SHA256 hash of the package file specified with either filename or s3_key. The usual way to set this is filebase64sha256('file.zip') where 'file.zip' is the local filename of the lambda function source archive." 70 | default = "" 71 | } 72 | 73 | variable "tags" { 74 | description = "A mapping of tags to assign to the Lambda function." 75 | type = map(string) 76 | default = {} 77 | } 78 | 79 | variable "timeout" { 80 | description = "The amount of time your Lambda Function has to run in seconds. Defaults to 3." 81 | default = 3 82 | } 83 | 84 | variable "vpc_config" { 85 | description = "Provide this to allow your function to access your VPC (if both 'subnet_ids' and 'security_group_ids' are empty then vpc_config is considered to be empty or unset, see https://docs.aws.amazon.com/lambda/latest/dg/vpc.html for details)." 86 | type = map(list(string)) 87 | default = {} 88 | } 89 | 90 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Contributions to this module are very welcome! We follow a fairly standard [pull request 4 | process](https://help.github.com/articles/about-pull-requests/) for contributions, subject to the following guidelines: 5 | 6 | 1. [File a GitHub issue](#file-a-github-issue) 7 | 1. [Update the examples](#update-the-examples) 8 | 1. [Update the code](#update-the-code) 9 | 1. [Update the documentation](#update-the-documentation) 10 | 1. [Create a pull request](#create-a-pull-request) 11 | 1. [Merge and release](#merge-and-release) 12 | 13 | ## File a GitHub issue 14 | 15 | Before starting any work, we recommend filing a GitHub issue in this repo. This is your chance to ask questions and 16 | get feedback from the maintainers and the community. If there is anything you're unsure about, just ask! 17 | 18 | ## Update the examples 19 | 20 | We also recommend updating the examples/creating new examples _before_ updating any code. This 21 | ensures the examples stay up to date and verify all the functionality in this module, including whatever new 22 | functionality you're adding in your contribution. Check out the [Makefile](https://github.com/spring-media/terraform-aws-lambda/blob/master/Makefile) 23 | for instructions for testing all examples. 24 | 25 | Furthermore we recommend to `terraform apply` all new or updated examples in your AWS account since some errors 26 | won't occur in the planning phase. 27 | 28 | ## Update the code 29 | 30 | At this point, make your code changes and use your updated or new example to verify that everything is working. As you work, 31 | keep in mind two things: 32 | 33 | 1. Backwards compatibility 34 | 1. Downtime 35 | 36 | Please adhere to the Terraform [module guidelines](https://www.terraform.io/docs/modules/index.html). 37 | 38 | ### Backwards compatibility 39 | 40 | Please make every effort to avoid unnecessary backwards incompatible changes. With Terraform code, this means: 41 | 42 | 1. Do not delete, rename, or change the type of input variables. 43 | 1. If you add an input variable, it should have a `default`. 44 | 1. Do not delete, rename, or change the type of output variables. 45 | 1. Do not delete or rename a module in the `modules` folder. 46 | 47 | If a backwards incompatible change cannot be avoided, please make sure to call that out when you submit a pull request, 48 | explaining why the change is absolutely necessary. 49 | 50 | ### Downtime 51 | 52 | Bear in mind that the Terraform code in this module is used by real companies to run real infrastructure in 53 | production, and certain types of changes could cause downtime. For example, consider the following: 54 | 55 | 1. If you rename a resource (e.g. `aws_instance "foo"` -> `aws_instance "bar"`), Terraform will see that as deleting 56 | the old resource and creating a new one. 57 | 1. If you change certain attributes of a resource (e.g. the `name` of an `aws_elb`), the cloud provider (e.g. AWS) may 58 | treat that as an instruction to delete the old resource and a create a new one. 59 | 60 | Deleting certain types of resources (e.g. virtual servers, load balancers) can cause downtime, so when making code 61 | changes, think carefully about how to avoid that. For example, can you avoid downtime by using 62 | [create_before_destroy](https://www.terraform.io/docs/configuration/resources.html#create_before_destroy)? Or via 63 | the `terraform state` command? If so, make sure to note this in our pull request. If downtime cannot be avoided, 64 | please make sure to call that out when you submit a pull request. 65 | 66 | ## Update the documentation 67 | 68 | Please update the documentation of the root and all affected nested modules. Version information will be updated 69 | by tooling. 70 | 71 | ## Create a pull request 72 | 73 | [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) with your changes. Please make sure 74 | to include the following: 75 | 76 | 1. A description of the change, including a link to your GitHub issue. 77 | 1. Any notes on backwards incompatibility or downtime. 78 | 79 | ## Merge and release 80 | 81 | The maintainers for this repo will review your code and provide feedback. If everything looks good, they will merge the 82 | code and release a new version, which you'll be able to find in the [releases page](../../releases). 83 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "function_name" { 7 | description = "A unique name for your Lambda Function." 8 | } 9 | 10 | variable "handler" { 11 | description = "The function entrypoint in your code." 12 | } 13 | 14 | variable "runtime" { 15 | description = "The runtime environment for the Lambda function you are uploading." 16 | } 17 | 18 | 19 | # --------------------------------------------------------------------------------------------------------------------- 20 | # OPTIONAL PARAMETERS 21 | # These parameters have reasonable defaults. 22 | # --------------------------------------------------------------------------------------------------------------------- 23 | 24 | variable "description" { 25 | description = "Description of what your Lambda Function does." 26 | default = "" 27 | } 28 | 29 | variable "environment" { 30 | description = "Environment (e.g. env variables) configuration for the Lambda function enable you to dynamically pass settings to your function code and libraries" 31 | type = map(map(string)) 32 | default = {} 33 | } 34 | 35 | variable "event" { 36 | description = "Event source configuration which triggers the Lambda function. Supported events: cloudwatch-scheduled-event, dynamodb, s3, sns" 37 | type = map(string) 38 | default = {} 39 | } 40 | 41 | variable "filename" { 42 | description = "The path to the function's deployment package within the local filesystem. If defined, The s3_-prefixed options cannot be used." 43 | default = "" 44 | } 45 | 46 | variable "kms_key_arn" { 47 | description = "The Amazon Resource Name (ARN) of the KMS key to decrypt AWS Systems Manager parameters." 48 | default = "" 49 | } 50 | 51 | variable "log_retention_in_days" { 52 | description = "Specifies the number of days you want to retain log events in the specified log group. Defaults to 14." 53 | default = 14 54 | } 55 | 56 | variable "logfilter_destination_arn" { 57 | description = "The ARN of the destination to deliver matching log events to. Kinesis stream or Lambda function ARN." 58 | default = "" 59 | } 60 | 61 | variable "memory_size" { 62 | description = "Amount of memory in MB your Lambda Function can use at runtime. Defaults to 128." 63 | default = 128 64 | } 65 | 66 | variable "publish" { 67 | description = "Whether to publish creation/change as new Lambda Function Version. Defaults to false." 68 | default = false 69 | } 70 | 71 | variable "reserved_concurrent_executions" { 72 | description = "The amount of reserved concurrent executions for this lambda function. A value of 0 disables lambda from being triggered and -1 removes any concurrency limitations. Defaults to Unreserved Concurrency Limits -1." 73 | default = "-1" 74 | } 75 | 76 | variable "s3_bucket" { 77 | description = "The S3 bucket location containing the function's deployment package. Conflicts with filename. This bucket must reside in the same AWS region where you are creating the Lambda function." 78 | default = "" 79 | } 80 | 81 | variable "s3_key" { 82 | description = " The S3 key of an object containing the function's deployment package. Conflicts with filename." 83 | default = "" 84 | } 85 | 86 | variable "s3_object_version" { 87 | description = "The object version containing the function's deployment package. Conflicts with filename." 88 | default = "" 89 | } 90 | 91 | variable "source_code_hash" { 92 | description = "Used to trigger updates. Must be set to a base64-encoded SHA256 hash of the package file specified with either filename or s3_key. The usual way to set this is filebase64sha256('file.zip') where 'file.zip' is the local filename of the lambda function source archive." 93 | default = "" 94 | } 95 | 96 | variable "ssm_parameter_names" { 97 | description = "List of AWS Systems Manager Parameter Store parameters this Lambda will have access to. In order to decrypt secure parameters, a kms_key_arn needs to be provided as well." 98 | default = [] 99 | } 100 | 101 | variable "tags" { 102 | description = "A mapping of tags to assign to the Lambda function." 103 | type = map(string) 104 | default = {} 105 | } 106 | 107 | variable "timeout" { 108 | description = "The amount of time your Lambda Function has to run in seconds. Defaults to 3." 109 | default = 3 110 | } 111 | 112 | variable "vpc_config" { 113 | description = "Provide this to allow your function to access your VPC (if both 'subnet_ids' and 'security_group_ids' are empty then vpc_config is considered to be empty or unset, see https://docs.aws.amazon.com/lambda/latest/dg/vpc.html for details)." 114 | type = map(list(string)) 115 | default = {} 116 | } 117 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | data "aws_region" "current" { 2 | } 3 | 4 | data "aws_caller_identity" "current" { 5 | } 6 | 7 | module "lambda" { 8 | source = "./modules/lambda" 9 | description = var.description 10 | environment = var.environment 11 | filename = var.filename 12 | function_name = var.function_name 13 | handler = var.handler 14 | memory_size = var.memory_size 15 | publish = var.publish 16 | reserved_concurrent_executions = var.reserved_concurrent_executions 17 | runtime = var.runtime 18 | s3_bucket = var.s3_bucket 19 | s3_key = var.s3_key 20 | s3_object_version = var.s3_object_version 21 | source_code_hash = var.source_code_hash 22 | timeout = var.timeout 23 | tags = var.tags 24 | vpc_config = var.vpc_config 25 | } 26 | 27 | module "event-cloudwatch" { 28 | source = "./modules/event/cloudwatch-event" 29 | enable = lookup(var.event, "type", "") == "cloudwatch-event" ? true : false 30 | 31 | lambda_function_arn = module.lambda.arn 32 | description = lookup(var.event, "description", "") 33 | event_pattern = lookup(var.event, "event_pattern", "") 34 | is_enabled = lookup(var.event, "is_enabled", true) 35 | name = lookup(var.event, "name", null) 36 | name_prefix = lookup(var.event, "name_prefix", null) 37 | schedule_expression = lookup(var.event, "schedule_expression", "") 38 | } 39 | 40 | module "event-dynamodb" { 41 | source = "./modules/event/dynamodb" 42 | enable = lookup(var.event, "type", "") == "dynamodb" ? true : false 43 | 44 | batch_size = lookup(var.event, "batch_size", 100) 45 | event_source_mapping_enabled = lookup(var.event, "event_source_mapping_enabled", true) 46 | function_name = module.lambda.function_name 47 | event_source_arn = lookup(var.event, "event_source_arn", "") 48 | iam_role_name = module.lambda.role_name 49 | starting_position = lookup(var.event, "starting_position", "TRIM_HORIZON") 50 | } 51 | 52 | module "event-kinesis" { 53 | source = "./modules/event/kinesis" 54 | enable = lookup(var.event, "type", "") == "kinesis" ? true : false 55 | 56 | batch_size = lookup(var.event, "batch_size", 100) 57 | event_source_mapping_enabled = lookup(var.event, "event_source_mapping_enabled", true) 58 | function_name = module.lambda.function_name 59 | event_source_arn = lookup(var.event, "event_source_arn", "") 60 | iam_role_name = module.lambda.role_name 61 | starting_position = lookup(var.event, "starting_position", "TRIM_HORIZON") 62 | } 63 | 64 | module "event-s3" { 65 | source = "./modules/event/s3" 66 | enable = lookup(var.event, "type", "") == "s3" ? true : false 67 | 68 | lambda_function_arn = module.lambda.arn 69 | s3_bucket_arn = lookup(var.event, "s3_bucket_arn", "") 70 | s3_bucket_id = lookup(var.event, "s3_bucket_id", "") 71 | } 72 | 73 | module "event-sns" { 74 | source = "./modules/event/sns" 75 | enable = lookup(var.event, "type", "") == "sns" ? true : false 76 | 77 | endpoint = module.lambda.arn 78 | function_name = module.lambda.function_name 79 | topic_arn = lookup(var.event, "topic_arn", "") 80 | } 81 | 82 | module "event-sqs" { 83 | source = "./modules/event/sqs" 84 | enable = lookup(var.event, "type", "") == "sqs" ? true : false 85 | 86 | batch_size = lookup(var.event, "batch_size", 10) 87 | event_source_mapping_enabled = lookup(var.event, "event_source_mapping_enabled", true) 88 | function_name = module.lambda.function_name 89 | event_source_arn = lookup(var.event, "event_source_arn", "") 90 | iam_role_name = module.lambda.role_name 91 | } 92 | 93 | resource "aws_cloudwatch_log_group" "lambda" { 94 | name = "/aws/lambda/${module.lambda.function_name}" 95 | retention_in_days = var.log_retention_in_days 96 | } 97 | 98 | resource "aws_lambda_permission" "cloudwatch_logs" { 99 | count = var.logfilter_destination_arn != "" ? 1 : 0 100 | action = "lambda:InvokeFunction" 101 | function_name = var.logfilter_destination_arn 102 | principal = "logs.${data.aws_region.current.name}.amazonaws.com" 103 | source_arn = aws_cloudwatch_log_group.lambda.arn 104 | } 105 | 106 | resource "aws_cloudwatch_log_subscription_filter" "cloudwatch_logs_to_es" { 107 | depends_on = [aws_lambda_permission.cloudwatch_logs] 108 | count = var.logfilter_destination_arn != "" ? 1 : 0 109 | name = "elasticsearch-stream-filter" 110 | log_group_name = aws_cloudwatch_log_group.lambda.name 111 | filter_pattern = "" 112 | destination_arn = var.logfilter_destination_arn 113 | distribution = "ByLogStream" 114 | } 115 | 116 | data "aws_iam_policy_document" "ssm_policy_document" { 117 | count = length(var.ssm_parameter_names) 118 | 119 | statement { 120 | actions = [ 121 | "ssm:GetParameters", 122 | "ssm:GetParametersByPath", 123 | ] 124 | 125 | resources = [ 126 | "arn:aws:ssm:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:parameter/${element(var.ssm_parameter_names, count.index)}", 127 | ] 128 | } 129 | } 130 | 131 | resource "aws_iam_policy" "ssm_policy" { 132 | count = length(var.ssm_parameter_names) 133 | name = "${module.lambda.function_name}-ssm-${count.index}-${data.aws_region.current.name}" 134 | description = "Provides minimum Parameter Store permissions for ${module.lambda.function_name}." 135 | policy = data.aws_iam_policy_document.ssm_policy_document[count.index].json 136 | } 137 | 138 | resource "aws_iam_role_policy_attachment" "ssm_policy_attachment" { 139 | count = length(var.ssm_parameter_names) 140 | role = module.lambda.role_name 141 | policy_arn = aws_iam_policy.ssm_policy[count.index].arn 142 | } 143 | 144 | data "aws_iam_policy_document" "kms_policy_document" { 145 | statement { 146 | actions = [ 147 | "kms:Decrypt", 148 | ] 149 | 150 | resources = [ 151 | var.kms_key_arn, 152 | ] 153 | } 154 | } 155 | 156 | resource "aws_iam_policy" "kms_policy" { 157 | count = var.kms_key_arn != "" ? 1 : 0 158 | name = "${module.lambda.function_name}-kms-${data.aws_region.current.name}" 159 | description = "Provides minimum KMS permissions for ${module.lambda.function_name}." 160 | policy = data.aws_iam_policy_document.kms_policy_document.json 161 | } 162 | 163 | resource "aws_iam_role_policy_attachment" "kms_policy_attachment" { 164 | count = var.kms_key_arn != "" ? 1 : 0 165 | role = module.lambda.role_name 166 | policy_arn = aws_iam_policy.kms_policy[count.index].arn 167 | } 168 | 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Lambda Terraform module 2 | 3 | ![](https://github.com/spring-media/terraform-aws-lambda/workflows/Terraform%20CI/badge.svg) [![Terraform Module Registry](https://img.shields.io/badge/Terraform%20Module%20Registry-5.2.1-blue.svg)](https://registry.terraform.io/modules/spring-media/lambda/aws/5.2.1) ![Terraform Version](https://img.shields.io/badge/Terraform-0.12+-green.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | 5 | --- 6 | 7 | # Deprecation warning 8 | 9 | Further development of this module will be continued in [moritzzimmer/terraform-aws-lambda](https://github.com/moritzzimmer/terraform-aws-lambda). Users of `spring-media/lambda/aws` 10 | should migrate to this module as a drop-in replacement for all provisions up to release/tag `5.2.1` to benefit from new features and bugfixes. 11 | 12 | ```hcl-terraform 13 | module "lambda" { 14 | source = "moritzzimmer/lambda/aws" 15 | version = "5.2.1" 16 | filename = "my-package.zip" 17 | function_name = "my-function" 18 | handler = "my-handler" 19 | runtime = "go1.x" 20 | source_code_hash = filebase64sha256("${path.module}/my-package.zip") 21 | } 22 | ``` 23 | 24 | --- 25 | 26 | 27 | Terraform module to create AWS [Lambda](https://www.terraform.io/docs/providers/aws/r/lambda_function.html) resources with configurable event sources, IAM configuration (following the [principal of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege)), VPC as well as SSM/KMS and log streaming support. 28 | 29 | The following [event sources](https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html) are supported (see [examples](#examples)): 30 | 31 | - [cloudwatch-event](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-cloudwatch-event): configures a [CloudWatch Event Rule](https://www.terraform.io/docs/providers/aws/r/cloudwatch_event_rule.html) to trigger the Lambda by CloudWatch [event pattern](https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/CloudWatchEventsandEventPatterns.html) or on a regular, scheduled basis 32 | - [dynamodb](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-dynamodb-event): configures an [Event Source Mapping](https://www.terraform.io/docs/providers/aws/r/lambda_event_source_mapping.html) to trigger the Lambda by DynamoDb events 33 | - [kinesis](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-kinesis-event): configures an [Event Source Mapping](https://www.terraform.io/docs/providers/aws/r/lambda_event_source_mapping.html) to trigger the Lambda by Kinesis events 34 | - [s3](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-s3-event): configures permission to trigger the Lambda by S3 35 | - [sns](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-sns-event): to trigger Lambda by [SNS Topic Subscription](https://www.terraform.io/docs/providers/aws/r/sns_topic_subscription.html) 36 | - [sqs](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-sqs-event): configures an [Event Source Mapping](https://www.terraform.io/docs/providers/aws/r/lambda_event_source_mapping.html) to trigger the Lambda by SQS events 37 | 38 | Furthermore this module supports: 39 | 40 | - reading configuration and secrets from [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html) including decryption of [SecureString](https://docs.aws.amazon.com/kms/latest/developerguide/services-parameter-store.html) parameters 41 | - [CloudWatch](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html) Log group configuration including retention time and [subscription filters](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/SubscriptionFilters.html) e.g. to stream logs via Lambda to Elasticsearch 42 | 43 | ## Terraform version compatibility 44 | 45 | | module | terraform | branch | 46 | | :----: | :-------: | :-------------: | 47 | | 4.x.x | 0.12+ | master | 48 | | 3.x.x | 0.11.x | terraform_0.11x | 49 | 50 | ## How do I use this module? 51 | 52 | The module can be used for all [runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) supported by AWS Lambda. 53 | 54 | Deployment packages can be specified either directly as a local file (using the `filename` argument) or indirectly via Amazon S3 (using the `s3_bucket`, `s3_key` and `s3_object_versions` arguments), see [documentation](https://www.terraform.io/docs/providers/aws/r/lambda_function.html#specifying-the-deployment-package) for details. 55 | 56 | **basic** 57 | 58 | ```terraform 59 | provider "aws" { 60 | region = "eu-west-1" 61 | } 62 | 63 | module "lambda" { 64 | source = "spring-media/lambda/aws" 65 | version = "5.2.1" 66 | filename = "my-package.zip" 67 | function_name = "my-function" 68 | handler = "my-handler" 69 | runtime = "go1.x" 70 | source_code_hash = filebase64sha256("${path.module}/my-package.zip") 71 | } 72 | ``` 73 | 74 | **with event trigger** 75 | 76 | ```terraform 77 | module "lambda" { 78 | // see above 79 | 80 | event = { 81 | type = "cloudwatch-event" 82 | schedule_expression = "rate(1 minute)" 83 | } 84 | } 85 | ``` 86 | 87 | **in a VPC** 88 | 89 | ```terraform 90 | module "lambda" { 91 | // see above 92 | 93 | vpc_config = { 94 | security_group_ids = ["sg-1"] 95 | subnet_ids = ["subnet-1", "subnet-2"] 96 | } 97 | } 98 | ``` 99 | 100 | **with access to parameter store** 101 | 102 | ```terraform 103 | module "lambda" { 104 | // see above 105 | 106 | ssm_parameter_names = ["some/config/root/*"] 107 | kms_key_arn = "arn:aws:kms:eu-west-1:647379381847:key/f79f2b-04684-4ad9-f9de8a-79d72f" 108 | } 109 | ``` 110 | 111 | **with log subscription (stream to ElasticSearch)** 112 | 113 | ```terraform 114 | module "lambda" { 115 | // see above 116 | 117 | logfilter_destination_arn = "arn:aws:lambda:eu-west-1:647379381847:function:cloudwatch_logs_to_es_production" 118 | } 119 | ``` 120 | 121 | ### Examples 122 | 123 | - [example-with-cloudwatch-event](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-cloudwatch-event) 124 | - [example-with-dynamodb-event](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-dynamodb-event) 125 | - [example-with-kinesis-event](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-kinesis-event) 126 | - [example-with-s3-event](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-s3-event) 127 | - [example-with-sns-event](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-sns-event) 128 | - [example-with-sqs-event](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-sqs-event) 129 | - [example-with-vpc](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-with-vpc) 130 | - [example-without-event](https://github.com/spring-media/terraform-aws-lambda/tree/master/examples/example-without-event) 131 | 132 | ## How do I contribute to this module? 133 | 134 | Contributions are very welcome! Check out the [Contribution Guidelines](https://github.com/spring-media/terraform-aws-lambda/blob/master/CONTRIBUTING.md) for instructions. 135 | 136 | ## How is this module versioned? 137 | 138 | This Module follows the principles of [Semantic Versioning](http://semver.org/). You can find each new release in the [releases page](../../releases). 139 | 140 | During initial development, the major version will be 0 (e.g., `0.x.y`), which indicates the code does not yet have a 141 | stable API. Once we hit `1.0.0`, we will make every effort to maintain a backwards compatible API and use the MAJOR, 142 | MINOR, and PATCH versions on each release to indicate any incompatibilities. 143 | --------------------------------------------------------------------------------