//See https://learn.hashicorp.com/tutorials/terraform/lambda-api-gateway
//===============================//
variable "code_bucket_name" {
}
//a path
variable "dummy_code_file" {
}
//used to access s3 bucket
variable "code_bucket_user_arn" {
}
variable "function_name" {
}
variable "function_handler" {
}
variable "lambda_runtime" {
}
variable "exec_role_name" {}
locals {
code_file_bucket_key = "lambda-code.zip"
}
resource "aws_s3_bucket" "code_bucket" {
bucket = var.code_bucket_name
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "user-and-bucket",
"Effect": "Allow",
"Principal": {
"AWS": "${var.code_bucket_user_arn}"
},
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::${var.code_bucket_name}"
},
{
"Sid": "user-and-objects",
"Effect": "Allow",
"Principal": {
"AWS": "${var.code_bucket_user_arn}"
},
"Action": [
"s3:GetObject",
"s3:DeleteObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::${var.code_bucket_name}/*"
}
]
}
POLICY
}
//You have to put a dummy code file into S3 bucket
// otherwise lambda creation will fail with "Error occurred while GetObject. S3 Error Code: NoSuchKey"
//When you rerun terraform, the dummy code file won't replace the read code file.
// Don't worry. https://stackoverflow.com/a/56120462
resource "aws_s3_bucket_object" "dummy_code" {
bucket = aws_s3_bucket.code_bucket.bucket
key = local.code_file_bucket_key
source = var.dummy_code_file
}
resource "aws_lambda_function" "lambda_function" {
depends_on = [
aws_s3_bucket_object.dummy_code,
aws_iam_role_policy_attachment.lambda_logs,
aws_cloudwatch_log_group.lambda_log_group
]
function_name = var.function_name
s3_bucket = aws_s3_bucket.code_bucket.bucket
s3_key = "lambda-code.zip"
handler = var.function_handler
runtime = var.lambda_runtime
timeout = 30
memory_size = 256
role = aws_iam_role.lambda_exec_role.arn
}
# IAM role which dictates what other AWS services the Lambda function may access.
resource "aws_iam_role" "lambda_exec_role" {
name = var.exec_role_name
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
//the gateway
resource "aws_api_gateway_rest_api" "gateway_api" {
name = "${aws_lambda_function.lambda_function.function_name}-rest-api"
}
resource "aws_api_gateway_resource" "gateway_resource" {
rest_api_id = aws_api_gateway_rest_api.gateway_api.id
parent_id = aws_api_gateway_rest_api.gateway_api.root_resource_id
path_part = "{proxy+}"
}
resource "aws_api_gateway_method" "gateway_method" {
rest_api_id = aws_api_gateway_rest_api.gateway_api.id
resource_id = aws_api_gateway_resource.gateway_resource.id
http_method = "ANY"
authorization = "NONE"
}
////this is stupid, but you have to do it according to the document
resource "aws_api_gateway_method" "gateway_method_for_root" {
rest_api_id = aws_api_gateway_rest_api.gateway_api.id
resource_id = aws_api_gateway_rest_api.gateway_api.root_resource_id
http_method = "ANY"
authorization = "NONE"
}
//the integration of gateway and function
resource "aws_api_gateway_integration" "gateway_integration" {
rest_api_id = aws_api_gateway_rest_api.gateway_api.id
resource_id = aws_api_gateway_method.gateway_method.resource_id
http_method = aws_api_gateway_method.gateway_method.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.lambda_function.invoke_arn
}
////this is stupid, but you have to do it according to the document
resource "aws_api_gateway_integration" "gateway_integration_for_root" {
rest_api_id = aws_api_gateway_rest_api.gateway_api.id
resource_id = aws_api_gateway_method.gateway_method_for_root.resource_id
http_method = aws_api_gateway_method.gateway_method_for_root.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.lambda_function.invoke_arn
}
//deployment
resource "aws_api_gateway_deployment" "gateway_deployment" {
depends_on = [
aws_api_gateway_integration.gateway_integration,
aws_api_gateway_integration.gateway_integration_for_root,
]
rest_api_id = aws_api_gateway_rest_api.gateway_api.id
stage_name = "default"
}
//permission
resource "aws_lambda_permission" "lambda_permission" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.lambda_function.function_name
principal = "apigateway.amazonaws.com"
# The "/*/*" portion grants access from any method on any resource
# within the API Gateway REST API.
source_arn = "${aws_api_gateway_rest_api.gateway_api.execution_arn}/*/*"
}
# See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function#cloudwatch-logging-and-permissions
resource "aws_cloudwatch_log_group" "lambda_log_group" {
name = "/aws/lambda/${var.function_name}"
retention_in_days = 14
}
resource "aws_iam_policy" "lambda_logging_policy" {
name = "lambda-logging-${var.function_name}"
path = "/"
description = "IAM policy for logging from a lambda"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*",
"Effect": "Allow"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "lambda_logs" {
role = aws_iam_role.lambda_exec_role.name
policy_arn = aws_iam_policy.lambda_logging_policy.arn
}
To call such a module,
module "some-system" {
source = "./modules/lambda_and_api_gateway"
code_bucket_name = "some-bucket-name" # will create it for you
dummy_code_file = "path/to/the/dummy/code/file"
function_name = "some-function-name"
function_handler = "some-function-handler"
lambda_runtime = "some-runtime-such-as-nodejs"
code_bucket_user_arn = "some-iam-user-arn"
exec_role_name = "some-exec-role-name" #Please create it in advance
}