AWS API Gateway Integration and Terraform - amazon-web-services

I'm struggling to translate my API Gateway Integration request into Terraform code, I'm trying to pass a multipart/form-data request to a Lambda for storage.
I've been able to set up the API gateway manually from scratch, but when I try the terraform I recieve an Internal server error and Cloudwatch tells me the gateway has been unable to transform the request.
Execution failed due to configuration error: Unable to transform request
The problem seems to be in the Integration request, because if I do the terraform deployment I can get the whole thing to work by changing the Integration Type in the UI to Lambda Proxy, changing it back again and adding re-adding the Mapping Template.
Terraform block for the Integration;
resource "aws_api_gateway_integration" "docuemnt-api-method-integration" {
rest_api_id = aws_api_gateway_rest_api.document-api.id
resource_id = aws_api_gateway_resource.document-resource.id
http_method = aws_api_gateway_method.document-post-method.http_method
type = "AWS"
uri = aws_lambda_function.document_function.invoke_arn
integration_http_method = "POST"
passthrough_behavior = "WHEN_NO_TEMPLATES"
request_templates = {
"multipart/form-data" = file("${path.module}/mapping/mapping_template.json")
}
}
The Mapping template
{
"body":"$input.body",
"content-type": "$util.escapeJavaScript($input.params().header.get('Content-Type'))"
}

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 changing the Integration Type in the UI to Lambda Proxy, the integration request's content_handling will be set to CONVERT_TO_TEXT.
So you need to add this row to the aws_api_gateway_integration:
content_handling = "CONVERT_TO_TEXT"
And normally it will solve your problem.

Related

Terraform Api Gateway Lambda Integration trigger problem

If I manually add an Integration Request of type Lambda function, an Api Gateway trigger is automatically added to the lambda function.
If I do it via Terraform, everything looks correct but when I go look at the Lambda function it has no trigger.
If I then manually update the Integration Request (change to Mock and back to Lambda Function) the trigger is added to the Lambda function? Everything works after that.
What am I missing?
resource "aws_api_gateway_integration" "integration" {
count = var.lambda_definition.apigateway ? 1 : 0
rest_api_id = "${data.terraform_remote_state.apigateway.outputs.apigateway_id}"
resource_id = aws_api_gateway_resource.api_proxy_resource[count.index].id
http_method = "${aws_api_gateway_method.method[count.index].http_method}"
integration_http_method = "ANY"
type = "AWS_PROXY"
uri = aws_lambda_function.lambda.invoke_arn
}
Since you've not mentioned whether you specified proper permissions for your function, my guess is that you are missing aws_lambda_permission. This will explicitly give permissions for the api to invoke your function.
The resource would be (example only):
resource "aws_lambda_permission" "allow_api" {
statement_id = "AllowAPIgatewayInvokation"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.lambda.invoke_arn
principal = "apigateway.amazonaws.com"
}
When you do it manually in console, the AWS setups all these permissions in the background.
Make sure that integration_http_method is set to POST and not to ANY as in your sample:
integration_http_method = "POST"
See AWS Docs - midway - red box that says '! Important':
For Lambda integrations, you must use the HTTP method of POST for the integration request, according to the specification of the Lambda service action for function invocations. The IAM role of apigAwsProxyRole must have policies allowing the apigateway service to invoke Lambda functions. For more information about IAM permissions, see API Gateway permissions model for invoking an API.

Get endpoint from API Gateway for an AWS lambda in terraform

I have a lambda deployed to AWS, integration is lambda-proxy, now I have an API Gateway which gives me an endpoint for calling the lambda. I need to get this endpoint URL in a terraform configuration, how can I do this?
Data sources don't expose any URL
data "aws_lambda_function" "myfunction" {
function_name = "my-function-name"
}
data "aws_api_gateway_rest_api" "my_rest_api" {
name = "my-rest-api"
}
api_gateway_deployment does expose an invoke_url, but infortunately it can't be imported, otherwise I could have declared the resources and then use terraform import ...
resource "aws_api_gateway_rest_api" "my_rest_api" {
name = "my-rest-api"
tags = {
STAGE = "prod"
}
}
resource "aws_api_gateway_deployment" "my_rest_api" {
rest_api_id = aws_api_gateway_rest_api.my_rest_api.id
stage_name = "prod"
}
EDIT: to clarify, the lambda is not deployed or managed by terraform.
I have a slightly different but similar setup working on my end.
I used:
- Terraform to provision AWS API Gateway Custom Domain
- Serverless Framework to deploy & link Lambdas to the API GW URL created above.
I exported API GW URL from Terraform as an Output Variable. And, passed it as an environment variable to Serverless Framework during function deployment.
Here it is how in action.
1/ main.tf responsible for exporting API GW URL
output "tf_output_aws_apigw_domain_name__domain_name"
{
value = "${aws_api_gateway_domain_name.tf_aws_apigw_domain_for_apis.domain_name}"
}
2/ serverless.yml responsible for linking Lambda function to this API GW URL
[...]
customDomain:
domainName: ${env:APIGW_DOMAIN_NAME}
basePath: /myapi
stage: dev
createRoute53Record: true
[...]
Here are the commands which glue them together (in my automation pipeline).
First part is responsible to read the terraform's output variable value (i.e. API GW URL that was created earlier) and sets itself to an environment variable APIGW_DOMAIN_NAME.
Later part initializes serverless-domain-manager plug-in responsible for mapping lambda function to APIGW URL and deploys a serverless setup.
terraform init ...
terraform refresh ...
export APIGW_DOMAIN_NAME=`terraform output tf_output_aws_apigw_domain_name__domain_name`
sls plugin install -n serverless-domain-manager
sls deploy --debug
cheers,
ram

AWS APIGateway Lambda proxy integration- Execution failed due to configuration error: Invalid permissions on Lambda function

I am relatively new to AWS and the beast. After working on API Gateway to Lambda proxy integration I am getting Execution failed due to configuration error: Invalid permissions on Lambda function
I followed below setup referred from really well documented terraform documentation and does exactly what was needed for me. But while testing on API Gateway console giving the above error.
resource "aws_lambda_permission" "apigw" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.resource_name.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.resource_name_of_deployment.execution_arn}/*/*"
}
Few learnings from API Gateway Lambda proxy integration
API Gateway is deployed in different stages and ARN for API gateway in stage vs on test console is somewhat different. (atleast thats what I got on terraform output)
As many documentations and fixes for the problem suggests to explicitly configure detailed path as "arn:aws:execute-api:region_name:account_id:${aws_api_gateway_rest_api.api_resource.id}/*/*"
The configured source with granted access permission
arn:aws:execute-api:region:accountid:fu349z93pa/*/*
From terraform documentation
For "${aws_api_gateway_deployment.deployment_rsc_name.execution_arn}"
The configured source with granted access permission is
arn:aws:execute-api:region:accountid:fu349z93pa/stage/*/*
If you test from API Gateway console you would end up with same error and have to manually add permission to lambda or reselect lambda function name on method integration console (which does the same thing). That configures 2 API gateways to access Lambda. (one with /stage deployed ARN and other /*/METHOD/* - used for test console)
But if you test API gateway from ARN of stage environment on postman it works just as fine without any manual updates to infrastructure built with terraform. And in most cases that is the one that would matter.
Even after fixing first error manually / not second challenge is Malformed response from lambda
This one is fairly easy and well documented. AWS Doc
All we have to do is update lambda to respond with a specified format.
for. e.g. add below
callback(null, { "statusCode": 200, "body" : JSON.stringify(sampleResponseJSON) }); on lambda `js`
Once it is working end to end we could always add error handling scenarios.
Hopefully, this should save some time for beginners like me.
So instead of using:
resource "aws_lambda_permission" "apigw" {
... ...
source_arn = "${aws_api_gateway_deployment.resource_name_of_deployment.execution_arn}/*/*"
}
I use the replace method to remove the stage_name from the execution_arn:
resource "aws_lambda_permission" "apigw" {
... ...
source_arn = "${replace(aws_api_gateway_deployment.resource_name_of_deployment.execution_arn, var.stage_name, "")}*/*"
}
And now everything works for me

uri for aws_api_gateway_integration for type AWS (integration) to DynamoDb

I am creating infrastructure with terraform with API Gateway connecting to DynamoDb API. I am creating resource aws_api_gateway_integration to define the integration with DynamoDb with type attribute set as AWS.
But somehow i am unable to get the uri value right for db.
Documentation says it should be of format arn:aws:apigateway:{region}:{subdomain.service|service}:{path|action}/{service_api}.
Current value configured is arn:aws:apigateway:us-east-1:dynamodb:GetItem.
I am not sure what is service_api. Did anyone encounter this before? Kindly help.
This also took me some time to figure out, I think the DynamoDB API-Gateway Integration deserves a dedicated example in the Terraform docs similar to Lambda. Actually service_api just refers to the DynamoDB Action (which you would also enter in the AWS Api Gateway Console Action field) prefixed with the literal action/. So the following block eventually worked for my PUT request mapping:
resource "aws_api_gateway_integration" "my-integration" {
type = "AWS"
integration_http_method = "POST"
uri = "arn:aws:apigateway:eu-central-1:dynamodb:action/PutItem"
# (...)
}

terraform api gateway integration with openapi spec

I'm currently creating an AWS API Gateway with terraform by using an open api spec from a json file.
resource "aws_api_gateway_rest_api" "my_test_gateway" {
name = "test_gateway"
description = "test gateway"
body = "${file("assets/test_openapi_spc.json")}"
}
I need to map the api resources that are created by said openAPI spec to AWS Step Functions.
How can i reference a API Gateway resource created by the openAPI spec to be able to create a new aws_api_gateway_integration?
Normally you would do something like this and describe the resources in terraform
resource "aws_api_gateway_integration" "test" {
resource_id = "${aws_api_gateway_resource.my_resource.id}"
}
In my case, however, i don't have a resource id defined in my terraform scripts because they get created by the openAPI spec.
Is there some way of extracting all the resources of an api gateway that were created as terraform data source so that i can reference them?
Not sure if i'm missing something major here.
seems the best way to do this is with swagger extensions
https://swagger.io/docs/specification/openapi-extensions/
The integration part of the API resource can reside in your swagger spec.
Just like mentioned in the aws API Gateway extensions to swagger https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions.html
So if you want to map a resource to a step function for example, you can do:
x-amazon-apigateway-integration:
credentials: "arn:aws:iam::my_account:role/ApiGWToStepFunction"
responses:
default:
statusCode: "201"
uri: "arn:aws:apigateway:my_zone:states:action/StartExecution"
passthroughBehavior: "when_no_templates"
httpMethod: "POST"
type: "aws"
I'm not finished yet, but i believe that instead of arn:aws:apigateway:my_zone:states:action/StartExecution it should be arn:aws:apigateway:my_zone:states:your_step_function_name