Terraform Lambda Issues - amazon-web-services

I am trying to build an deploy an Lambda written in Go and want to use terraform to deploy. I followed the steps on the terraform site.
But the difference between that example and my app is I have multiple subroutes. When I try to call the application I get this error in the API Gateway test:
Sat Sep 22 11:06:31 UTC 2018 : Endpoint response headers: {Date=Sat,
22 Sep 2018 11:06:31 GMT, Content-Length=130, Connection=keep-alive,
x-amzn-RequestId=8f57fab6-be57-11e8-a99b-2ba9ede2859c} Sat Sep 22
11:06:31 UTC 2018 : Lambda invocation failed with status: 403. Lambda
request id: 8f57fab6-be57-11e8-a99b-2ba9ede2859c Sat Sep 22 11:06:31
UTC 2018 : Execution failed due to configuration error: Sat Sep 22
11:06:31 UTC 2018 : Method completed with status: 500
I'm not sure what I need, here is my code:
variable "app_version" {
}
variable "region" {
default = "us-east-1"
}
variable account_id {
default = "412092673045"
}
provider "aws" {
region = "us-east-1"
}
resource "aws_lambda_function" "example" {
function_name = "EXAMPLE"
# The bucket name as created earlier with "aws s3api create-bucket"
s3_bucket = "example-core"
s3_key = "v${var.app_version}/main.zip"
# "main" is the filename within the zip file (main.js) and "handler"
# is the name of the property under which the handler function was
# exported in that file.
handler = "main"
runtime = "go1.x"
role = "${aws_iam_role.lambda_exec.arn}"
environment={
variables = {
REDIS_URL = "XXXXXXXX"
REDIS_PASSWORD = "XXXXXXX"
}
}
}
# IAM role which dictates what other AWS services the Lambda function
# may access.
resource "aws_iam_role" "lambda_exec" {
name = "serverless_example_lambda"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_lambda_permission" "allow_api_gateway" {
function_name = "${aws_lambda_function.example.function_name}"
statement_id = "AllowExecutionFromApiGateway"
action = "lambda:InvokeFunction"
principal = "apigateway.amazonaws.com"
source_arn = "${aws_iam_role.lambda_exec.arn}"
}
resource "aws_api_gateway_rest_api" "example" {
name = "ServerlessExample"
description = "Terraform Serverless Application Example"
}
resource "aws_api_gateway_resource" "proxy" {
rest_api_id = "${aws_api_gateway_rest_api.example.id}"
parent_id = "${aws_api_gateway_rest_api.example.root_resource_id}"
path_part = "{proxy+}"
}
resource "aws_api_gateway_method" "proxy" {
rest_api_id = "${aws_api_gateway_rest_api.example.id}"
resource_id = "${aws_api_gateway_resource.proxy.id}"
http_method = "ANY"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "lambda" {
rest_api_id = "${aws_api_gateway_rest_api.example.id}"
resource_id = "${aws_api_gateway_method.proxy.resource_id}"
http_method = "${aws_api_gateway_method.proxy.http_method}"
integration_http_method = "ANY"
type = "AWS_PROXY"
uri = "arn:aws:apigateway:${var.region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${var.region}:${var.account_id}:function:${aws_lambda_function.example.function_name}/invocations"
}
resource "aws_api_gateway_method" "proxy_root" {
rest_api_id = "${aws_api_gateway_rest_api.example.id}"
resource_id = "${aws_api_gateway_rest_api.example.root_resource_id}"
http_method = "ANY"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "lambda_root" {
rest_api_id = "${aws_api_gateway_rest_api.example.id}"
resource_id = "${aws_api_gateway_method.proxy_root.resource_id}"
http_method = "${aws_api_gateway_method.proxy_root.http_method}"
integration_http_method = "ANY"
type = "AWS_PROXY"
uri = "arn:aws:apigateway:${var.region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${var.region}:${var.account_id}:function:${aws_lambda_function.example.function_name}/invocations"
}
resource "aws_api_gateway_deployment" "example" {
depends_on = [
"aws_api_gateway_integration.lambda",
"aws_api_gateway_integration.lambda_root",
]
rest_api_id = "${aws_api_gateway_rest_api.example.id}"
stage_name = "api"
}
resource "aws_lambda_permission" "apigw" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.example.arn}"
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_deployment.example.execution_arn}/*/*"
}
output "base_url" {
value = "${aws_api_gateway_deployment.example.invoke_url}"
}
resource "aws_iam_policy" "lambda_logging" {
name = "lambda_logging"
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.name}"
policy_arn = "${aws_iam_policy.lambda_logging.arn}"
}

Related

Nodes are created with the same name and does not join to the EKS cluster via Terraform

I checked all similar questions on stackoverflow but I couldn't find any decent answer for this issue. So main problem is when I applied my terraform. The instances up and run successfully and I can see the node group under EKS but I can't see any nodes under my EKS cluster. I followed this article aws article I applied below steps but didn't work. The article also mentions about aws-auth and userdata. Should I add also these things and how? (do I need userdata I already added optimized ami?)
In summary my main problems:
my instances running with same name
my instances does not join the EKS cluster
Applied steps via aws article:
I added aws optimized ami but it doesn't
solve my problem:
/aws/service/eks/optimized-ami/1.22/amazon-linux-2/recommended/image_id (New update during installation of node group its failing because of this image probably not suitable for t2.micro)
I set below parameter for vpc what article say
enable_dns_support = true
enable_dns_hostnames = true
I set the tags for my worker nodes
key = "kubernetes.io/cluster/${var.cluster_name}"
value = "owned"
I specified userdata line in launch template. Below you can see my userdata.sh file that Im calling that from launch template
There are no nodes :(
node_grp.tf :Here my EKS worker node terraform file with policies.
resource "aws_iam_role" "eks_nodes" {
name = "eks-node-group"
assume_role_policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
POLICY
}
resource "aws_iam_role_policy" "node_autoscaling" {
name = "${var.base_name}-node_autoscaling_policy"
role = aws_iam_role.eks_nodes.name
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeAutoScalingInstances",
"autoscaling:SetDesiredCapacity",
"autoscaling:TerminateInstanceInAutoScalingGroup",
"autoscaling:DescribeTags"
],
"Resource": "*"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "AmazonEKSWorkerNodePolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
role = aws_iam_role.eks_nodes.name
}
resource "aws_iam_role_policy_attachment" "AmazonEKS_CNI_Policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
role = aws_iam_role.eks_nodes.name
}
resource "aws_iam_role_policy_attachment" "AmazonEC2ContainerRegistryReadOnly" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
role = aws_iam_role.eks_nodes.name
}
resource "aws_eks_node_group" "node" {
cluster_name = var.cluster_name
node_group_name = "${var.base_name}-node-group"
node_role_arn = aws_iam_role.eks_nodes.arn
subnet_ids = var.private_subnet_ids
scaling_config {
desired_size = var.desired_nodes
max_size = var.max_nodes
min_size = var.min_nodes
}
launch_template {
name = aws_launch_template.node_group_template.name
version = aws_launch_template.node_group_template.latest_version
}
depends_on = [
aws_iam_role_policy_attachment.AmazonEKSWorkerNodePolicy,
aws_iam_role_policy_attachment.AmazonEKS_CNI_Policy,
aws_iam_role_policy_attachment.AmazonEC2ContainerRegistryReadOnly,
]
}
resource "aws_launch_template" "node_group_template" {
name = "${var.cluster_name}_node_group"
instance_type = var.instance_type
user_data = base64encode(templatefile("${path.module}/userdata.sh", { API_SERVER_URL = var.cluster_endpoint, B64_CLUSTER_CA = var.ca_certificate, CLUSTER_NAME = var.cluster_name } ))
block_device_mappings {
device_name = "/dev/xvda"
ebs {
volume_size = var.disk_size
}
}
tag_specifications {
resource_type = "instance"
tags = {
"Instance Name" = "${var.cluster_name}-node"
Name = "${var.cluster_name}-node"
key = "kubernetes.io/cluster/${var.cluster_name}"
value = "owned"
}
}
}
cluster.tf : my main eks cluster file
resource "aws_iam_role" "eks_cluster" {
name = var.cluster_name
assume_role_policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "eks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
POLICY
}
resource "aws_iam_role_policy_attachment" "AmazonEKSClusterPolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = aws_iam_role.eks_cluster.name
}
resource "aws_iam_role_policy_attachment" "AmazonEKSServicePolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy"
role = aws_iam_role.eks_cluster.name
}
resource "aws_eks_cluster" "eks_cluster" {
name = var.cluster_name
role_arn = aws_iam_role.eks_cluster.arn
enabled_cluster_log_types = ["api", "audit", "authenticator","controllerManager","scheduler"]
vpc_config {
security_group_ids = [var.security_group_id]
subnet_ids = flatten([ var.private_subnet_ids, var.public_subnet_ids ])
endpoint_private_access = false
endpoint_public_access = true
}
depends_on = [
aws_iam_role_policy_attachment.AmazonEKSClusterPolicy,
aws_iam_role_policy_attachment.AmazonEKSServicePolicy
]
}
resource "aws_iam_openid_connect_provider" "oidc_provider" {
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = var.trusted_ca_thumbprints
url = aws_eks_cluster.eks_cluster.identity[0].oidc[0].issuer
}
user-data.sh : My userdata sh file calling from launch template
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==MYBOUNDARY=="
--==MYBOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"
#!/bin/bash
set -ex
/etc/eks/bootstrap.sh ${CLUSTER_NAME} --b64-cluster-ca ${B64_CLUSTER_CA} --apiserver-endpoint ${API_SERVER_URL}
--==MYBOUNDARY==--\

AccessDeniedException when creating API Gateway through Terraform

I am trying to create API Gateway / Lambda stack through Terraform.
This is my apigateway.tf file (I added "aws_api_gateway_authorizer" as per https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/api_gateway_authorizer):
resource "aws_api_gateway_rest_api" "handler_gateway" {
name = "captainai_webhook_handler"
description = "Entry point for captainai webhook handler"
tags = var.service_tags
}
resource "aws_api_gateway_integration" "lambda" {
rest_api_id = aws_api_gateway_rest_api.handler_gateway.id
resource_id = aws_api_gateway_method.events_post.resource_id
http_method = aws_api_gateway_method.events_post.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.handler_lambda.invoke_arn
}
resource "aws_api_gateway_integration" "lambda_root" {
rest_api_id = aws_api_gateway_rest_api.handler_gateway.id
resource_id = aws_api_gateway_method.events_post.resource_id
http_method = aws_api_gateway_method.events_post.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.handler_lambda.invoke_arn
}
resource "aws_api_gateway_domain_name" "handler_domain_name" {
regional_certificate_arn = var.domain_certificate_arn
domain_name = var.domain_name
tags = var.service_tags
endpoint_configuration {
types = ["REGIONAL"]
}
}
resource "aws_api_gateway_resource" "events_proxy" {
rest_api_id = aws_api_gateway_rest_api.handler_gateway.id
parent_id = aws_api_gateway_rest_api.handler_gateway.root_resource_id
path_part = "{proxy+}"
}
resource "aws_api_gateway_method" "events_post" {
rest_api_id = aws_api_gateway_rest_api.handler_gateway.id
resource_id = aws_api_gateway_resource.events_proxy.id
http_method = "POST"
authorization = "NONE"
}
resource "aws_api_gateway_deployment" "handler" {
depends_on = [
aws_api_gateway_integration.lambda,
aws_api_gateway_integration.lambda_root,
]
rest_api_id = aws_api_gateway_rest_api.handler_gateway.id
stage_name = "default"
}
resource "aws_iam_role" "invocation_role" {
name = "api_gateway_auth_invocation"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy" "invocation_policy" {
name = "default"
role = aws_iam_role.invocation_role.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "lambda:InvokeFunction",
"Effect": "Allow",
"Resource": "${aws_lambda_function.handler_lambda.arn}"
}
]
}
EOF
}
resource "aws_api_gateway_authorizer" "demo" {
name = "demo"
rest_api_id = aws_api_gateway_rest_api.handler_gateway.id
authorizer_uri = aws_lambda_function.handler_lambda.invoke_arn
authorizer_credentials = aws_iam_role.invocation_role.arn
}
Unfortunately, it is throwing me the following error, for both aws_api_gateway_rest_api and aws_api_gateway_domain_name:
Error: Error creating API Gateway: AccessDeniedException:
status code: 403, request id: ad1c834d-5ba9-462a-ac65-ed9852811632
on apigateway.tf line 1, in resource "aws_api_gateway_rest_api" "handler_gateway":
1: resource "aws_api_gateway_rest_api" "handler_gateway" {
Error: Error creating API Gateway Domain Name: AccessDeniedException:
status code: 403, request id: 1c11fda2-5618-4ae0-aeac-783288535d57
on apigateway.tf line 31, in resource "aws_api_gateway_domain_name" "handler_domain_name":
31: resource "aws_api_gateway_domain_name" "handler_domain_name" {
The only other thing that comes to my mind that I should define aws_iam_user resource somewhere but am not sure when and which roles to include.
Lambda function is in a separate lambda.tf file, as:
resource "aws_lambda_function" "handler_lambda" {
description = "The lambda handles webhook events from captainai."
filename = "data/captainai_webhook_handler.zip"
function_name = "captainai_webhook_handler"
role = aws_iam_role.iam_for_lambda.arn
handler = "captainai_webhook_handler.lambda_handler"
source_code_hash = filebase64sha256("data/captainai_webhook_handler.zip")
runtime = "python3.8"
timeout = 120
memory_size = 512
tags = var.service_tags
environment {
variables = {
WEBSOCKET_HTTP_HOST = var.websocket_http_host
WEBSOCKET_HTTP_HOST_LOGIN = var.websocket_http_host_login
WEBSOCKET_HTTP_HOST_PASSWORD = var.websocket_http_host_password
CAPTAINAI_SECRET_TOKEN = var.captainai_secret_token
}
}
}
resource "aws_lambda_permission" "api_gateway" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.handler_lambda.arn
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.handler_gateway.execution_arn}/*/*"
}

Websocket Connection on AWS always results in TOO MANY REQUESTS even with one request

So I have this terraform that seems to deploy a websocket api connection to AWS BUT....
Once deployed, when I connect, I consistently get "429 too many requests" errors.
Using terraform 0.13.4.
I've turned up the requests manually in the console but every time I wscat -c {MYENDPOINT} I get a 429.
Can't find anything online or anything in the manuals about this.
Here is the terraform. Wondering if anyone can see if I'm missing something in my routes or integrations?
Here is the response I keep getting from the logs:
(VH_SDESljoEF7tg=) Gateway response body: { "message": "Too Many Requests", "connectionId": "VH_SDd21joECIeg=", "requestId": "VH_SDESljoEF7tg=" }
and
(VH_SDESljoEF7tg=) Key throttle limit exceeded for RestApi k27g2ypii6, Stage test, Resource $connect, HttpMethod GET. Limit: 42.00 Burst: 0
resource "aws_apigatewayv2_api" "websocket-api" {
name = "websocket-api"
protocol_type = "WEBSOCKET"
}
resource "aws_apigatewayv2_integration" "chatRoomConnectIntegration" {
api_id = aws_apigatewayv2_api.websocket-api.id
integration_type = "AWS_PROXY"
integration_uri = aws_lambda_function.ChatRoomConnectFunction.invoke_arn
integration_method = "POST"
}
resource "aws_apigatewayv2_route" "connectRoute" {
api_id = aws_apigatewayv2_api.websocket-api.id
route_key = "$connect"
target = "integrations/${aws_apigatewayv2_integration.chatRoomConnectIntegration.id}"
}
resource "aws_apigatewayv2_deployment" "deploy" {
api_id = aws_apigatewayv2_api.websocket-api.id
description = "testing deployment"
triggers = {
redeployment = sha1(join(",", list(
jsonencode(aws_apigatewayv2_integration.chatRoomConnectIntegration),
jsonencode(aws_apigatewayv2_route.connectRoute),
)))
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_apigatewayv2_stage" "test-stage" {
api_id = aws_apigatewayv2_api.websocket-api.id
name = "test"
access_log_settings {
destination_arn = aws_cloudwatch_log_group.access_logs.arn
format = "$context.identity.sourceIp - - [$context.requestTime] \"$context.httpMethod $context.routeKey $context.protocol\" $context.status $context.responseLength $context.requestId $context.integrationErrorMessage"
}
default_route_settings {
data_trace_enabled = true
logging_level = "INFO"
throttling_rate_limit = 42
}
route_settings {
route_key = "$connect"
data_trace_enabled = true
logging_level = "INFO"
throttling_rate_limit = 42
}
}
resource "aws_api_gateway_account" "api_gateway_accesslogs" {
cloudwatch_role_arn = aws_iam_role.cloudwatch.arn
}
resource "aws_iam_role" "cloudwatch" {
name = "api_gateway_cloudwatch_global"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "cloudwatch" {
name = "default"
role = aws_iam_role.cloudwatch.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:PutLogEvents",
"logs:GetLogEvents",
"logs:FilterLogEvents"
],
"Resource": "*"
}
]
}
EOF
}
resource "aws_lambda_permission" "allow_api_gateway" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.ChatRoomConnectFunction.arn
statement_id = "AllowExecutionFromApiGateway"
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.websocket-api.execution_arn}/*/*/*"
}
output "endpoint" {
value = aws_apigatewayv2_stage.test-stage.invoke_url
}
I can't explain the cause of the throttling, but I added this block to my aws_apigatewayv2_stage resource, triggered a new deployment, and now I'm able to connect using wscat:
default_route_settings {
throttling_rate_limit = 100
throttling_burst_limit = 50
}
(relevant docs here)

invalid ARN error on terraform when creating eks cluster

**resource "aws_iam_role" "eks_role" {
name = "eks_role"
assume_role_policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "eks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
POLICY
}
resource "aws_iam_role_policy_attachment" "AmazonEKSClusterPolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = "aws_iam_role.eks_role.name"
}
resource "aws_iam_role_policy_attachment" "AmazonEKSServicePolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy"
role = "aws_iam_role.eks_role.name"
}
resource "aws_eks_cluster" "t3_eks" {
name = "t3_eks"
role_arn = "aws_iam_role.eks_role.arn"
vpc_config {
security_group_ids = var.sg
subnet_ids = var.subnets
endpoint_private_access = false
endpoint_public_access = true
}
depends_on = [
aws_iam_role_policy_attachment.AmazonEKSClusterPolicy,
aws_iam_role_policy_attachment.AmazonEKSServicePolicy,
]
}**
Error message
Error: "role_arn" (aws_iam_role.eks_role.arn) is an invalid ARN: arn: invalid prefix
on EKS\main.tf line 30, in resource "aws_eks_cluster" "t3_eks":
30: resource "aws_eks_cluster" "t3_eks" {
Please could someone guide as to what could be wrong?
Quotes are important with terraform. In 0.12 the quoted string "aws_iam_role.eks_role.arn" is just a string. In order for it to be interpolated as an actual variable, you need to remove the quotes:
resource "aws_eks_cluster" "t3_eks" {
name = "t3_eks"
role_arn = aws_iam_role.eks_role.arn
It is also possible to interpolate a variable inside of a string, which is required for terraform 0.11 or older:
resource "aws_eks_cluster" "t3_eks" {
name = "t3_eks"
role_arn = "${aws_iam_role.eks_role.arn}"

Terraform API Gateway with Lambda integration

I've started using terraform a few days ago, so I am a beginner in this topic. I want to create an API Gateway and a Lambda with a Layer as a custom runtime to run R script. Here are my Terraform files:
lambda.tf
resource "aws_lambda_function" "testlambda" {
function_name = "rlambda"
s3_bucket = "mybucket"
s3_key = "rlambda/v1.0.3/rlambda.zip"
handler = "main.main"
runtime = "provided"
memory_size = 512
timeout = 30
layers = [aws_lambda_layer_version.r_layer.arn]
role = aws_iam_role.lambda_role.arn
}
# IAM role for lambda
resource "aws_iam_role" "lambda_role" {
name = "lambda_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
# Lambda Policy
resource "aws_lambda_permission" "apigw" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.testlambda.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.apigateway.execution_arn}/*/POST/dev"
depends_on = [
aws_api_gateway_rest_api.apigateway,
aws_api_gateway_resource.apiresource,
]
}
# Lambda Layer
resource "aws_lambda_layer_version" "r_layer" {
layer_name = "rlayer"
s3_bucket = "mybucket"
s3_key = "lambdalayer/v1.0.3/rlayer.zip"
}
# Cloudwatch Logging for Lambda
resource "aws_iam_policy" "lambda_logging" {
name = "lambda_logging"
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_role.name
policy_arn = aws_iam_policy.lambda_logging.arn
}
api_gateway.tf
resource "aws_api_gateway_rest_api" "apigateway" {
name = "ApiGatewayTest"
description = "Terraform Created Api Gateway"
binary_media_types = ["multipart/form-data", "application/octet-stream"]
}
resource "aws_api_gateway_resource" "apiresource" {
rest_api_id = aws_api_gateway_rest_api.apigateway.id
parent_id = aws_api_gateway_rest_api.apigateway.root_resource_id
path_part = "dev"
}
# Method
resource "aws_api_gateway_method" "method" {
rest_api_id = aws_api_gateway_rest_api.apigateway.id
resource_id = aws_api_gateway_resource.apiresource.id
http_method = "POST"
authorization = "NONE"
}
# Integration
resource "aws_api_gateway_integration" "integration" {
rest_api_id = aws_api_gateway_rest_api.apigateway.id
resource_id = aws_api_gateway_method.method.resource_id
http_method = aws_api_gateway_method.method.http_method
integration_http_method = "POST"
type = "AWS"
uri = aws_lambda_function.testlambda.invoke_arn
passthrough_behavior = "WHEN_NO_TEMPLATES"
request_templates = {
"multipart/form-data" = file("api_gateway_body_mapping.template")
}
depends_on = [aws_api_gateway_method.method]
}
# Method Response
resource "aws_api_gateway_method_response" "methodresponse" {
rest_api_id = aws_api_gateway_rest_api.apigateway.id
resource_id = aws_api_gateway_resource.apiresource.id
http_method = aws_api_gateway_method.method.http_method
status_code = "200"
response_parameters = {
"method.response.header.Access-Control-Allow-Origin" = true,
"method.response.header.Access-Control-Expose-Headers" = true,
"method.response.header.Content-Disposition" = true,
"method.response.header.Content-Type" = true
}
}
# Integration Response
resource "aws_api_gateway_integration_response" "integrationresponse" {
rest_api_id = aws_api_gateway_rest_api.apigateway.id
resource_id = aws_api_gateway_resource.apiresource.id
http_method = aws_api_gateway_method.method.http_method
status_code = aws_api_gateway_method_response.methodresponse.status_code
content_handling = "CONVERT_TO_BINARY"
response_parameters = {
"method.response.header.Access-Control-Allow-Origin" = "'*'",
"method.response.header.Access-Control-Expose-Headers" = "'Content-Disposition'",
"method.response.header.Content-Disposition" = "'attachment'",
"method.response.header.Content-Type" = "'application/octet-stream'"
}
depends_on = [aws_api_gateway_integration.integration]
}
# CORS Method
resource "aws_api_gateway_method" "cors_method" {
rest_api_id = aws_api_gateway_rest_api.apigateway.id
resource_id = aws_api_gateway_resource.apiresource.id
http_method = "OPTIONS"
authorization = "NONE"
}
# CORS Integration
resource "aws_api_gateway_integration" "cors_integration" {
rest_api_id = aws_api_gateway_rest_api.apigateway.id
resource_id = aws_api_gateway_resource.apiresource.id
http_method = aws_api_gateway_method.cors_method.http_method
type = "MOCK"
request_templates = {
"application/json" = <<EOF
{"statusCode": 200}
EOF
}
depends_on = [aws_api_gateway_method.cors_method]
}
# CORS Method Response
resource "aws_api_gateway_method_response" "cors_methodresponse" {
rest_api_id = aws_api_gateway_rest_api.apigateway.id
resource_id = aws_api_gateway_resource.apiresource.id
http_method = aws_api_gateway_method.cors_method.http_method
status_code = "200"
response_models = {
"application/json" = "Empty"
}
response_parameters = {
"method.response.header.Access-Control-Allow-Headers" = true,
"method.response.header.Access-Control-Allow-Methods" = true,
"method.response.header.Access-Control-Allow-Origin" = true
}
depends_on = [aws_api_gateway_method.cors_method]
}
# CORS Integration Response
resource "aws_api_gateway_integration_response" "cors_integrationresponse" {
rest_api_id = aws_api_gateway_rest_api.apigateway.id
resource_id = aws_api_gateway_resource.apiresource.id
http_method = aws_api_gateway_method.cors_method.http_method
status_code = aws_api_gateway_method_response.cors_methodresponse.status_code
response_parameters = {
"method.response.header.Access-Control-Allow-Headers" = "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
"method.response.header.Access-Control-Allow-Methods" = "'OPTIONS,POST'",
"method.response.header.Access-Control-Allow-Origin" = "'*'"
}
response_templates = {
"application/json" = <<EOF
EOF
}
depends_on = [
aws_api_gateway_method_response.cors_methodresponse,
aws_api_gateway_integration.cors_integration,
]
}
# Deployment
resource "aws_api_gateway_deployment" "apideployment" {
depends_on = [
aws_api_gateway_integration.integration,
aws_api_gateway_integration.cors_integration
]
rest_api_id = aws_api_gateway_rest_api.apigateway.id
stage_name = "dev"
}
output "base_url" {
value = aws_api_gateway_deployment.apideployment.invoke_url
}
api_gateway_body_mapping.template
{
"body": "$input.body",
"headers": {
#foreach($param in $input.params().header.keySet())
"$param": "$util.escapeJavaScript($input.params().header.get($param))"
#if($foreach.hasNext),#end
#end
}
}
So terraform init and apply completes, but API Gateway shows Internal Server Error. (Calling it from postman, with this url: https://(randomchars).execute-api.eu-central-1.amazonaws.com/dev/dev)
"Execution failed due to configuration error: Unable to transform request" - This is the error from the api's cloudwatch log.
If I go to the Integration Request of the POST method execution on the AWS console, and I re-add the Function name (rlambda) it adds another policy to the lambda (Exactly the same as it was before, with different Sid of course) and I re-deploy my API, then it works completely. (It does not work if I only do the deploy API part.)
So my questions are:
Is there anything happening in the background when I re-add my Function name ? (Except the policy thing)
What to change in my code to be able to call api gateway right after terraform apply?
Okay so after 5 days of suffering I realized what is the problem.
On the AWS console you are not able to set the Integration Request's content_handling and it is only an Optional parameter in Terraform as well.
When you are re-assigning your lambda's name on the console, not only the lambda's policy got updated, but also the integration request's content_handling got set to CONVERT_TO_TEXT. I don't know why it is changing and why to this value. Maybe it's based on the lambda - there is no information about this.
So I added this row to the aws_api_gateway_integration:
content_handling = "CONVERT_TO_TEXT"
And it solved my problem. Hope nobody will run into this again.