1
votes

I'm trying to deploy an AWS API Gateway and Lambda with Terraform. The gateway is just a proxy to my function. After running terraform apply every web request to the endpoint fails.

API Gateways logs this error message to Cloudwatch:

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

But now it's getting weird: If I hit the deploy button in AWS Web Console and just deploy (nothing else changed) the API Gateway one more time, everything works fine. The next "terraform apply" deploys aws_api_gateway_stage.staging again and that breaks everything again.

This is my Terraform setup:

lambda.tf:

resource "aws_lambda_function" "contactform-api" {
  filename      = "deploy.zip"
  function_name = "contactform-api"
  handler       = "main"
  runtime       = "go1.x"
  role          = "${aws_iam_role.lambda_role.arn}"
  publish       = "false"
  timeout       = "60"
}

resource "aws_iam_role" "lambda_role" {
  name = "iam_for_lambda"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": "IAMRoleForLambda"
    }
  ]
}
EOF
}

resource "aws_lambda_permission" "apigw" {
  statement_id  = "AllowExecutionFromAPIGateway"
  action        = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.contactform-api.arn}"
  principal     = "apigateway.amazonaws.com"
  source_arn    = "${aws_api_gateway_rest_api.contactform.execution_arn}/*/*/*"
}

api_gateway.tf:

resource "aws_api_gateway_rest_api" "contactform" {
  name        = "ContactformAPI"
  description = "Contactform REST API"
}

resource "aws_api_gateway_resource" "api" {
  rest_api_id = "${aws_api_gateway_rest_api.contactform.id}"
  parent_id   = "${aws_api_gateway_rest_api.contactform.root_resource_id}"
  path_part   = "{proxy+}"
}

resource "aws_api_gateway_method" "api" {
  rest_api_id   = "${aws_api_gateway_rest_api.contactform.id}"
  resource_id   = "${aws_api_gateway_resource.api.id}"
  http_method   = "ANY"
  authorization = "NONE"
}

resource "aws_api_gateway_integration" "lambda" {
  rest_api_id             = "${aws_api_gateway_rest_api.contactform.id}"
  resource_id             = "${aws_api_gateway_resource.api.id}"
  http_method             = "${aws_api_gateway_method.api.http_method}"
  integration_http_method = "POST"
  type                    = "AWS_PROXY"
  uri                     = "${aws_lambda_function.contactform-api.invoke_arn}"
}

resource "aws_api_gateway_deployment" "staging" {
  depends_on  = ["module.cors", "aws_api_gateway_method.api", "aws_api_gateway_resource.api", "aws_api_gateway_integration.lambda"]
  stage_name  = "staging"
  rest_api_id = "${aws_api_gateway_rest_api.contactform.id}"
}

resource "aws_api_gateway_stage" "staging" {
  stage_name    = "staging"
  rest_api_id   = "${aws_api_gateway_rest_api.contactform.id}"
  deployment_id = "${aws_api_gateway_deployment.staging.id}"
}
1

1 Answers

1
votes

You are using the Lambda function's ARN as the function_name in your aws_lambda_permission resource.

Changing that to be the name rather than the ARN should solve that for you:

resource "aws_lambda_permission" "apigw" {
  statement_id  = "AllowExecutionFromAPIGateway"
  action        = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.contactform-api.function_name}"
  principal     = "apigateway.amazonaws.com"
  source_arn    = "${aws_api_gateway_rest_api.contactform.execution_arn}/*/*/*"
}

As for why it works when you deploy via the AWS console; the AWS console magically sets permissions for you so you don't need to juggle that via the web UI. When Terraform redeploys changes it will break it again because Terraform needs to explicitly state the permissions for the Lambda function.