//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 }