add mapping template for API in Terraform - amazon-web-services

I want to add a header (Content-Type) and a mapping template within the integration request of my AWS API Gateway. While following this tutorial, I could easily add those parameters via the console. https://docs.aws.amazon.com/apigateway/latest/developerguide/integrating-api-with-aws-services-kinesis.html
However, I am unable to figure out the syntax to add them using Terraform. This is my integration request so far:
resource "aws_api_gateway_integration" "kinesis_integration" {
rest_api_id = aws_api_gateway_rest_api.shippeo_api_kinesis.id
resource_id = aws_api_gateway_resource.shippeo_api_resource_kinesis.id
http_method = aws_api_gateway_method.post_json_files_kinesis.http_method
credentials = aws_iam_role.shippeo_integration_role.arn
type = "AWS"
uri = "arn:aws:apigateway:eu-central-1:kinesis:action/PutRecord"
integration_http_method = "POST"
depends_on = [
aws_api_gateway_resource.shippeo_api_resource_kinesis
]
}
How can I specify the header (Content-Type)+Mapped From and the mapping templates as shown below using Terraform?
Mapping Template:
{
"StreamName": "$input.params('stream-name')",
"Data": "$util.base64Encode($input.json('$.Data'))",
"PartitionKey": "$input.path('$.PartitionKey')"
}
In the documentation, selection_pattern and response_templates is mentioned but I am not sure how to use it in my case.

Related

Integrate AWS API Gateway methods with multiple Lambdas using Terraform

I have to integrate more than 50 Lambda functions with relevant api gateway method. So that I create a terraform map with Lambda function name and api gateway resource path as below.
variable "lambdas" {
description = "Map of Lambda function names and API gateway resource paths."
type = map
default = {
user = {
name = "user-lambda-function"
path = "user"
},
products= {
name = "products-lambda-function"
path = "products"
},
orders= {
name = "orders-lambda-function"
path = "orders"
},
Then I iterate lambda function creation through this map using for_each as below.
resource "aws_lambda_function" "lambda_functions" {
for_each = var.lambdas
filename = "lambda_function_code.zip"
function_name = each.value.name
role = data.aws_iam_role.lambda_execution_role.arn
handler = "index.handler"
source_code_hash = filebase64sha256("lambda_function_code.zip")
runtime = "nodejs14.x"
}
After that I start to create API Gateway, resources and methods as below,
resource "aws_api_gateway_rest_api" "api_gateway" {
name = var.api-gateway-name
}
resource "aws_api_gateway_resource" "resources" {
for_each = var.lambdas
rest_api_id = aws_api_gateway_rest_api.api_gateway.id
parent_id = aws_api_gateway_rest_api.api_gateway.root_resource_id
path_part = each.value.path
}
resource "aws_api_gateway_method" "methods" {
for_each = aws_api_gateway_resource.resources
rest_api_id = aws_api_gateway_rest_api.api_gateway.id
resource_id = each.value.id
http_method = "POST"
authorization = "NONE"
api_key_required = false
}
Then I try to integrate above API Gateway method with relevant Lambda function by iterating above above methods. But here I have to input relevant lambda function invocation uri.
resource "aws_api_gateway_integration" "integration" {
for_each = aws_api_gateway_method.methods
rest_api_id = each.value.rest_api_id
resource_id = each.value.resource_id
http_method = each.value.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = ""
}
I am struggling to input this relevant lambda function uri to integrate with relevant method.
My question is, How do I get relevant lambda function uri to input here with this iteration? Or any solution to achieve this without code every lambdas, resources and methods.
The aws_lambda_function has invoke_arn which is:
ARN to be used for invoking Lambda Function from API Gateway - to be used in aws_api_gateway_integration's uri
So you have to use that in your aws_api_gateway_integration.

I'AWS_PROXY' currently only supports Lambda function and Firehose stream invocations

I want to create an API Gateway that is connected to a kinesis stream:
resource "aws_api_gateway_integration" "kinesis_integration" {
rest_api_id = aws_api_gateway_rest_api.kinesis.id
resource_id = aws_api_gateway_resource.api_resource_kinesis.id
http_method = aws_api_gateway_method.post_kinesis.http_method
type = "AWS_PROXY"
uri = "arn:aws:apigateway:eu-central-1:kinesis:action/PutRecord"
integration_http_method = "POST"
depends_on = [
aws_api_gateway_resource.resource_kinesis
]
}
resource "aws_kinesis_firehose_delivery_stream" "kinesis-stream" {
name = "kinesis-stream"
destination = "extended_s3"
extended_s3_configuration {
role_arn = aws_iam_role.integration_role.arn
bucket_arn = aws_s3_bucket.mybucket.arn
processing_configuration {
enabled = "true"
processors {
type = "Lambda"
parameters {
parameter_name = "LambdaArn"
parameter_value = "${aws_lambda_function.mylambda.arn}:$LATEST"
}
}
}
}
}
Error creating API Gateway Integration: BadRequestException: AWS ARN for integration must contain path or action
│
│ with module.api.aws_api_gateway_integration.kinesis_integration,
│ on ../../modules/myapi.tf line 62, in resource "aws_api_gateway_integration" "kinesis_integration":
│ 62: resource "aws_api_gateway_integration" "kinesis_integration" {
Am I using the wrong syntax? Is it not supposed to be a string? I read in another SO answer that the format of the uri is supposed to be like this:
arn:aws:apigateway:{region}:{subdomain.service|service}:{path|action}/{service_api}
but it doesn't seem to work for me. I already tried both sols here but they give me the same error:
What should be service api for aws_api_gateway_integration with firehose in terraform
The links that you are trying to follow are for AWS integration, not AWS_PROXY. AWS_PROXY is only for lambda functions. I know that somewhere it says you can also use Firehose with it (I think some error messages), but its not really documented and advertised. It is either some internal AWS functionality, or something that hasn't been implemented yet.

URI to connect step function from api-gateway

I'm trying to create the terraform for the above aws_api_gateway_integration. I'm not able to create the aws_api_gateway_integration as it returns the error
Error creating API Gateway Integration: BadRequestException: AWS ARN for integration must contain path or action
Please find the terraform code snippet.
resource "aws_api_gateway_integration" "approve_get_integration" {
rest_api_id = "${aws_api_gateway_rest_api.approval_api.id}"
resource_id = "${aws_api_gateway_resource.approve_resource.id}"
http_method = "${aws_api_gateway_method.approve_get_Method.http_method}"
type = "AWS"
integration_http_method = "POST"
uri = "arn:aws:apigateway:ap-southeast-1:states:SendTaskSuccess/${var.step-function-arn}"
timeout_milliseconds = 29000
}
I'm trying to sendTaskuccess to the aws stepfunction activity.
You must be using the following as the uri to send the task status to StepFunction Activity
uri = "arn:aws:apigateway:${var.region}:states:action/SendTaskFailure"

Why is the method response of an API gateway different when being created using terraform?

I have the following terraform script which creates an API gateway that passes requests to a lambda function.
provider "aws" {
access_key = "${var.access_key}"
secret_key = "${var.secret_key}"
#
region = "${var.region}"
version = "~> 2.6"
}
resource "aws_api_gateway_rest_api" "MyDemoAPI" {
name = "MyDemoAPI"
description = "This is my API for demonstration purposes"
}
resource "aws_api_gateway_resource" "MyDemoResource" {
rest_api_id = "${aws_api_gateway_rest_api.MyDemoAPI.id}"
parent_id = "${aws_api_gateway_rest_api.MyDemoAPI.root_resource_id}"
path_part = "mydemoresource"
}
resource "aws_api_gateway_method" "MyDemoMethod" {
rest_api_id = "${aws_api_gateway_rest_api.MyDemoAPI.id}"
resource_id = "${aws_api_gateway_resource.MyDemoResource.id}"
http_method = "POST"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "MyDemoIntegration" {
rest_api_id = "${aws_api_gateway_rest_api.MyDemoAPI.id}"
resource_id = "${aws_api_gateway_resource.MyDemoResource.id}"
http_method = "${aws_api_gateway_method.MyDemoMethod.http_method}"
integration_http_method = "POST"
type = "AWS_PROXY"
uri = "arn:aws:apigateway:ap-southeast-1:lambda:path/2015-03-31/functions/${aws_lambda_function.test_lambda_function.arn}/invocations"
content_handling = "CONVERT_TO_TEXT"
}
resource "aws_api_gateway_method_response" "200" {
rest_api_id = "${aws_api_gateway_rest_api.MyDemoAPI.id}"
resource_id = "${aws_api_gateway_resource.MyDemoResource.id}"
http_method = "${aws_api_gateway_method.MyDemoMethod.http_method}"
status_code = "200"
response_models {
"application/json" = "Empty"
}
}
resource "aws_lambda_function" "test_lambda_function" {
filename = "lambda.zip"
description = "test build api gateway and lambda function using terraform"
function_name = "test_lambda_function"
role = "arn:aws:iam::123456789123:role/my_labmda_role"
handler = "gateway.lambda_handler"
runtime = "python3.6"
memory_size = 128
timeout = 60
}
The Method Response section of the API gateway resource display Select an integration response..
But if I create the same API gateway using AWS console, the Method Response section displays something different:
Why does this happen?
The following steps are how I use AWS console to create the API gateway:
Select Create Method under the resource.
Select POST method.
Select the desired options.
I've tried creating the above resources manually first, then execute terraform apply. Then terraform tells me that nothing needs to be changed.
terraform apply
aws_api_gateway_rest_api.MyDemoAPI: Refreshing state... (ID: 1qa34vs1k7)
aws_lambda_function.test_lambda_function: Refreshing state... (ID: test_lambda_function)
aws_api_gateway_resource.MyDemoResource: Refreshing state... (ID: 4xej81)
aws_api_gateway_method.MyDemoMethod: Refreshing state... (ID: agm-1qa34vs1k7-4xej81-POST)
aws_api_gateway_method_response.200: Refreshing state... (ID: agmr-1qa34vs1k7-4xej81-POST-200)
aws_api_gateway_integration.MyDemoIntegration: Refreshing state... (ID: agi-1qa34vs1k7-4xej81-POST)
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
This seems to mean that the manually built structure is the same as the structure built by terraform.
Because API Gateway is a complex AWS component and you can control pretty much everything on it (basically every single part of it is managed independently, giving you a lot of control over what you create but also making things harder to deal with).
See that it says "Select an Integration Response", but since your Terraform code didn't create one, it is therefore empty.
I had come across this very same problem a few weeks ago and I found the solution on Terraform's GitHub. I think Terraform should better document this as you're not the first one nor will you be the last to come up with this question. Well, at least we have this documented in StackOverflow now :)
Long story short, you need to add a aws_api_gateway_integration_response terraform resource to your API Gateway.
resource "aws_api_gateway_integration_response" "MyDemoIntegrationResponse" {
rest_api_id = "${aws_api_gateway_rest_api.MyDemoAPI.id}"
resource_id = "${aws_api_gateway_resource.MyDemoResource.id}"
http_method = "${aws_api_gateway_method.MyDemoMethod.http_method}"
status_code = "${aws_api_gateway_method_response.200.status_code}"
response_templates = {
"application/json" = ""
}
}
If you can, however, I suggest you use a proper framework to hook events to your Lambda functions (like the Serverless Framework or AWS SAM) as it's very verbose and error prone to create them in Terraform.
Usually, I combine Terraform and Serverless Framework together: I use Terraform to create infrastructure resources - even if they are serverless - like DynamoDB tables, SQS queues, SNS topics, etc. and the Serverless Framework to create the Lambda functions and their corresponding events.
I had the same issue. What Thales suggested didn't work for me (using terraform 0.11.14 with AWS plugin 2.52.0).
Instead, I used api_gateway_method_response resource and set the response_models parameter as:
response_models = {
"application/json" = "Empty"
}
Then it showed me the default response that all other APIs that I created through the console show!
Not very clear in terraform docs what this should look like, had to figure out myself after many attempts.

Terraform: What is the uri-parameter for an aws_api_gateway_integration if target is an aws_sfn_state_machine

I want to create a step function API using API gateway using Terraforms api_gateway_integration and sfn_state_machine.
I am at the point where I have to fill the uri-parameter at the api_gateway_integration.
My step function was created, I can reference the id of the step function (something like arn:aws:states:*region*:*account*:stateMachine:*step-function-name*:stateMachine:*step-function-entry-point*).
Can anyone tell me the scheme or an example of how the uri-parameter will have to look, if an AWS step function is the target?
resource "aws_api_gateway_integration" "endpoint_integration" {
...
integration_http_method = "POST"
type = "AWS"
uri = <<<<< What to place here???
}
In contrast to Terraform-integrate an AWS lambda with an API gateway, you cannot point from an API gateway to a specific AWS state machine "directly" (using the "uri"-parameter"). Instead, the aws_api_gateway_integration-resource points to the AWS state machine in general, the specific AWS state machine will be referenced by AWS ARN as part of the request. You can use an API gateway request template for mapping from the API gateway to the specific state machine, so that you can omit die stateMachineArn when requesting your API. For detailed explanation have a look at AWS documentation "Creating a Step Functions API Using API Gateway".
Working example
# var.aws_region = eu-central-1
# var.sfn_orchestrater_arn = arn:aws:states:eu-central-1:*account*:stateMachine:*step-function-entry-point*
resource "aws_api_gateway_integration" "endpoint_integration" {
http_method = "POST"
integration_http_method = "POST"
type = "AWS"
passthrough_behavior = "NEVER"
uri = "arn:aws:apigateway:${var.aws_region}:states:action/StartExecution"
request_templates = {
"application/json" = <<EOF
{
"input": "$util.escapeJavaScript($input.json('$'))",
"stateMachineArn": "${var.sfn_orchestrater_arn}"
}
EOF
}
}