7
votes

I'm deploying an API gateway and a Lambda function together through Terraform, and the Lambda function is meant to be triggered by the API Gateway. After the resources successfully deploy, I test the API Gateway and I get response:

{ "message": "Internal server error" } .

The actual logs of the API gateway say:

Execution failed due to configuration error: Invalid permissions on Lambda function

I can get the actual api-lambda functionality to work by going to the integration request section of the API gateway, reselecting my existing function, and "saving" it again with the little checkmark, but this breaks automation and I want this to work without having to do that manual step every time. Not sure if this is a bug in Terraform/AWS or if I'm doing something wrong. (Found someone asking the same question but using SAM but no responses: Execution failed due to configuration error: Invalid permissions on Lambda function)

My current setup is deploying the API via a swagger json file, and the Lambda Invoke ARN is used as the URI in the integration section of this file. I have tried switching this between a hard coded ARN and a variable to no avail. I also tried including an aws_api_gateway_deployment and aws_api_gateway_integration resource but I figured that if I'm already using a swagger file, using those would conflict with what the swagger file is already building.

My main.tf for my api_gateway module looks like this:

resource "aws_api_gateway_rest_api" "post_session" {
    name = "${var.api_gateway_name}"
    body = "${data.template_file.post-session.rendered}"

    endpoint_configuration {
        types = ["PRIVATE"]
    }
}

data "template_file" "post-session" {
    template = "${file("../source/aapt-ual-post-session-v1-swagger-apigateway.json")}"

    vars {
        session_init_arn = "${var.session_init_function_arn}"
    }
}

My relevant section of the swagger file looks like this:

"x-amazon-apigateway-integration": {
      "uri": "${session_init_arn}",
      "responses": {
        "default": {
          "statusCode": "200"
        }
      },
      "requestTemplates": {
        "application/json":  ....

And my lambda_permission/api_gateway trigger section of my Lambda module looks like this:

resource "aws_lambda_permission" "post_session_trigger" {
     statement_id  = "Allow_My_Post_Session_Invoke"
     action        = "lambda:InvokeFunction"
     function_name = "${aws_lambda_function.init_function.function_name}"
     principal     = "apigateway.amazonaws.com"
     source_arn = "arn:aws:execute-api:us-east-1:${var.account_id}:${var.post_session_id}/v1/POST/aa/ual/session"

}

Let me know if you have any suggestions, thanks!

2
I do not have an exact answer. But I would follow the steps below to understand the root cause. 1. Invoke your terraform template and create the API gateway and the Lambda function. 2. Go to the Lambda console and make a copy of the function policy of the Lambda function [1] 3. Go to the integration request section of the API gateway, reselecting my existing function, and "saving" it again with the little checkmark 4. Go back to Lambda console and make a copy of the function policy again [1] 5. Compare the difference between the function policies generated in step 2 and 4.Denis Weerasiri
This is the appendix for my previous comment. 6. It will help you to identify what's missing in the terraform template. [1] i0.wp.com/blogs.perficient.com/files/2018/11/…Denis Weerasiri
Hey I tried this out and found that when reselecting my Lambda function, it added a new policy to the Lambda permissions and added exactly what I needed to type in Terraform. This was the perfect troubleshooting solution! Thank you so much!transposeglobal
Thank you sooo much for this post. I had a similar problem and was able to resolve it with the information provided in this post!atom88

2 Answers

6
votes

As per the suggestion from Denis Weerasiri, I checked the Lambda permissions after reselecting the Lambda function name in the Integration section of the API Gateway, and it had added another policy. The change that I needed to make was changing the v1 to a * in the source_arn in the Lambda function resource. So the new API Gateway trigger in my Lambda module looks like this:

resource "aws_lambda_permission" "post_session_trigger" {
     statement_id  = "Allow_My_Post_Session_Invoke"
     action        = "lambda:InvokeFunction"
     function_name = "${aws_lambda_function.init_function.function_name}"
     principal     = "apigateway.amazonaws.com"
     source_arn = "arn:aws:execute-api:us-east-1:${var.account_id}:${var.post_session_id}/*/POST/aa/ual/session"}
1
votes

I had a similar problem and was using Terraform. It needed the policy with the "POST" in it. For some reason the /*/ (wildcard) policy didn't work?

Here's the policy and the example terraform I used to solve the issue.

Many thanks to all above.

Here is what my Lambda function policy JSON looked like and the terraform:

    {
      "Version": "2012-10-17",
      "Id": "default",
      "Statement": [
        {
          "Sid": "AllowAPIGatewayInvoke",
          "Effect": "Allow",
          "Principal": {
            "Service": "apigateway.amazonaws.com"
          },
          "Action": "lambda:InvokeFunction",
          "Resource": "arn:aws:lambda:us-east-1:999999999999:function:MY-APP",
          "Condition": {
            "ArnLike": {
              "AWS:SourceArn": "arn:aws:execute-api:us-east-1:999999999999:d85kyq3jx3/test/*/MY-APP"
            }
          }
        },
        {
          "Sid": "e841fc76-c755-43b5-bd2c-53edf052cb3e",
          "Effect": "Allow",
          "Principal": {
            "Service": "apigateway.amazonaws.com"
          },
          "Action": "lambda:InvokeFunction",
          "Resource": "arn:aws:lambda:us-east-1:999999999999:function:MY-APP",
          "Condition": {
            "ArnLike": {
              "AWS:SourceArn": "arn:aws:execute-api:us-east-1:999999999999:d85kyq3jx3/*/POST/MY-APP"
            }
          }
        }
      ]
    }

    add in a terraform like this:


    //************************************************
    // allows you to read in the ARN and parse out needed info, like region, and account
    //************************************************
    data "aws_arn" "api_gw_deployment_arn" {
        arn = aws_api_gateway_deployment.MY-APP_deployment.execution_arn 
    }

    //************************************************
    // Add in this to support API GW testing in AWS Console.
    //************************************************
    resource "aws_lambda_permission" "apigw-post" {
        statement_id  = "AllowAPIGatewayInvokePOST"
        action        = "lambda:InvokeFunction"
        //function_name = aws_lambda_function.lambda-MY-APP.arn
        function_name = module.lambda.function_name
        principal     = "apigateway.amazonaws.com"

        // "arn:aws:execute-api:us-east-1:473097069755:708lig5xuc/dev/POST1/cloudability-church-ws"
        source_arn = "arn:aws:execute-api:${data.aws_arn.api_gw_deployment_arn.region}:${data.aws_arn.api_gw_deployment_arn.account}:${aws_api_gateway_deployment.MY-APP_deployment.rest_api_id}/*/POST/${var.api_gateway_root_path}"
    }