title: Terraform API Gateway with Lambda integration
body: <p>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:</p>
  
  <h1>lambda.tf</h1>
  
  <pre><code>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 = &lt;&lt;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 = &lt;&lt;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
  }
  </code></pre>
  
  <h1>api_gateway.tf</h1>
  
  <pre><code>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" = &lt;&lt;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" = &lt;&lt;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
  }
  </code></pre>
  
  <h1>api_gateway_body_mapping.template</h1>
  
  <pre><code>{
      "body": "$input.body",
      "headers": {
          #foreach($param in $input.params().header.keySet())
          "$param": "$util.escapeJavaScript($input.params().header.get($param))"
          #if($foreach.hasNext),#end
          #end
      }
  }
  </code></pre>
  
  <p>So terraform init and apply completes, but API Gateway shows Internal Server Error. (Calling it from postman, with this url: <a href="https://(randomchars).execute-api.eu-central-1.amazonaws.com/dev/dev" rel="nofollow noreferrer">https://(randomchars).execute-api.eu-central-1.amazonaws.com/dev/dev</a>)</p>
  
  <p>"Execution failed due to configuration error: Unable to transform request" - This is the error from the api's cloudwatch log.</p>
  
  <p>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.)</p>
  
  <p>So my questions are:</p>
  
  <ul>
  <li>Is there anything happening in the background when I re-add my Function name ? (Except the policy thing)</li>
  <li>What to change in my code to be able to call api gateway right after terraform apply?</li>
  </ul>