Using terraform modules for multiple regional api gateway - amazon-web-services

I am using terraform to create aws infrastructure with 4 regional api gateways with corresponding dynamodb in that region.
I want to create one module consisting of ( API + dynamo ) with configurable region specific values. Is it possible with terraform? Or I would have to create 4 separate API + 4 separate dynamodb resources.
Any links or documentation would be helpful as well.
Currently working for regional API gateway and corresponding dynamodb.
variable "access_key" {}
variable "secret_key" {}
provider "aws" {
access_key = "${var.access_key}"
secret_key = "${var.secret_key}"
alias = "us-east-1"
region = "us-east-1"
}
provider "aws" {
access_key = "${var.access_key}"
secret_key = "${var.secret_key}"
alias = "us-west-2"
region = "us-west-2"
}
resource "aws_dynamodb_table" "us-east-1" {
provider = "aws.us-east-1"
hash_key = "test_tf"
name = "test_tf"
stream_enabled = true
stream_view_type = "NEW_AND_OLD_IMAGES"
read_capacity = 1
write_capacity = 1
attribute {
name = "test_tf"
type = "S"
}
}
resource "aws_dynamodb_table" "us-west-2" {
provider = "aws.us-west-2"
hash_key = "test_tf"
name = "test_tf"
stream_enabled = true
stream_view_type = "NEW_AND_OLD_IMAGES"
read_capacity = 1
write_capacity = 1
attribute {
name = "test_tf"
type = "S"
}
}
resource "aws_dynamodb_global_table" "test_tf" {
depends_on = ["aws_dynamodb_table.us-east-1", "aws_dynamodb_table.us-west-2"]
provider = "aws.us-east-1"
name = "test_tf"
replica {
region_name = "us-east-1"
}
replica {
region_name = "us-west-2"
}
}
resource "aws_api_gateway_rest_api" "test-us-east-1" {
name = "test-us-east-1"
endpoint_configuration {
types = ["REGIONAL"]
}
}
resource "aws_api_gateway_resource" "sample_test" {
rest_api_id = "${aws_api_gateway_rest_api.test-us-east-1.id}"
parent_id = "${aws_api_gateway_rest_api.test-us-east-1.root_resource_id}"
path_part = "{testid}"
}
resource "aws_api_gateway_method" "sample_get" {
rest_api_id = "${aws_api_gateway_rest_api.test-us-east-1.id}"
resource_id = "${aws_api_gateway_resource.sample_test.id}"
http_method = "GET"
authorization = "NONE"
}
resource "aws_api_gateway_deployment" "Deployment" {
depends_on = ["aws_api_gateway_method.sample_get"]
rest_api_id = "${aws_api_gateway_rest_api.test-us-east-1.id}"
stage_name = "test"
}
resource "aws_api_gateway_integration" "test" {
rest_api_id = "${aws_api_gateway_rest_api.test-us-east-1.id}"
resource_id = "${aws_api_gateway_resource.sample_test.id}"
http_method = "${aws_api_gateway_method.sample_get.http_method}"
integration_http_method = "POST"
type = "AWS"
uri = "arn:aws:apigateway:us-east-1:dynamodb:action/GetItem"
credentials = "${aws_iam_role.apiGatewayDynamoDbAccessRole.arn}"
passthrough_behavior = "WHEN_NO_TEMPLATES"
request_templates = {
"application/json" = <<EOF
{
"TableName": "test_tf",
"Key":
{
"test_tf":
{
"S": "$input.params('testid')"
}
}
}
EOF
}
}
resource "aws_iam_policy" "api_dbaccess_policy" {
name = "api_dbaccess_policy"
policy = "${file("api-dynamodb-policy.json")}"
depends_on = [
"aws_dynamodb_table.us-east-1"
]
}
resource "aws_iam_role" "apiGatewayDynamoDbAccessRole" {
name = "apiGatewayDynamoDbAccessRole"
assume_role_policy = "${file("assume-role-policy.json")}"
depends_on = [
"aws_dynamodb_table.us-east-1"
]
}
resource "aws_iam_policy_attachment" "api-dbaccess-policy-attach" {
name = "api-dbaccess-policy-attachment"
roles = ["${aws_iam_role.apiGatewayDynamoDbAccessRole.name}"]
policy_arn = "${aws_iam_policy.api_dbaccess_policy.arn}"
}
resource "aws_api_gateway_method_response" "200" {
rest_api_id = "${aws_api_gateway_rest_api.test-us-east-1.id}"
resource_id = "${aws_api_gateway_resource.sample_test.id}"
http_method = "${aws_api_gateway_method.sample_get.http_method}"
status_code = "200"
}
resource "aws_api_gateway_integration_response" "us-east-1-response" {
rest_api_id = "${aws_api_gateway_rest_api.test-us-east-1.id}"
resource_id = "${aws_api_gateway_resource.sample_test.id}"
http_method = "${aws_api_gateway_method.sample_get.http_method}"
status_code = "${aws_api_gateway_method_response.200.status_code}"
response_templates = {
"application/json" = <<EOF
{
#set($sampletest = $input.path('Item.test_tf.S'))
"test": #if ($sampletest && $sampletest != '')
true
#else
false
#end
}
EOF
}
}

Yes, this is possible with Terraform.
In the root module you define 4 AWS providers, giving alias to each one:
provider "aws" {
alias = "oregon"
region = "us-west-2"
}
provider "aws" {
alias = "virginia"
region = "us-east-1"
}
Then, when you instantiate your modules, instead of relying on provider inheritance you pass the provider explicitly by alias:
module "api_gateway" {
source = "./api_gateway"
providers = {
aws = "aws.oregon"
}
}
Rinse and repeat 4 times for each region.
You can find the docs here: https://www.terraform.io/docs/modules/usage.html

Related

Terraform add aws_api_gateway_integration response json

I need help to configure the post methods as lambda type without proxy and the 200 response type application / json => Empty
This is my Terraform file at aws, I'm new to Terraform, it just lacked this configuration to work if someone can help me.
I'm having an error response in terraform apply
Error creating API Gateway Integration Response: NotFoundException: Invalid Integration identifier specified Error creating API Gateway Deployment: BadRequestException: No integration defined for method
resource "aws_dynamodb_table" "basic-dynamodb-table" {
name = "stone-test"
billing_mode = "PROVISIONED"
read_capacity = 20
write_capacity = 20
hash_key = "id"
attribute {
name = "id"
type = "N"
}
ttl {
attribute_name = "TimeToExist"
enabled = false
}
tags = {
Name = "dynamodb-table-1"
Environment = "dev"
}
}
resource "aws_iam_role_policy" "lambda_policy" {
name = "lambda_policy"
role = aws_iam_role.role_for_LDC.id
policy = file("policy.json")
}
resource "aws_iam_role" "role_for_LDC" {
name = "myrole"
assume_role_policy = file("assume_role_policy.json")
}
resource "aws_lambda_function" "stone_register2" {
filename = "stone_register.zip"
function_name = "stone_register3"
role = aws_iam_role.role_for_LDC.arn
handler = "stone_register.lambda_handler"
# The filebase64sha256() function is available in Terraform 0.11.12 and later
# For Terraform 0.11.11 and earlier, use the base64sha256() function and the file() function:
# source_code_hash = "${base64sha256(file("stone_register.zip"))}"
source_code_hash = filebase64sha256("stone_register.zip")
runtime = "python3.6"
environment {
variables = {
DB_TABLE_NAME = "stone-test"
}
}
}
resource "aws_lambda_function" "stone_delete2" {
filename = "stone_delete.zip"
function_name = "stone_delete3"
role = aws_iam_role.role_for_LDC.arn
handler = "stone_delete.lambda_handler"
# The filebase64sha256() function is available in Terraform 0.11.12 and later
# For Terraform 0.11.11 and earlier, use the base64sha256() function and the file() function:
# source_code_hash = "${base64sha256(file("stone_delete.zip"))}"
source_code_hash = filebase64sha256("stone_delete.zip")
runtime = "python3.6"
environment {
variables = {
DB_TABLE_NAME = "stone-test"
}
}
}
resource "aws_lambda_function" "stone_search2" {
filename = "stone_search.zip"
function_name = "stone_search3"
role = aws_iam_role.role_for_LDC.arn
handler = "stone_search.lambda_handler"
# The filebase64sha256() function is available in Terraform 0.11.12 and later
# For Terraform 0.11.11 and earlier, use the base64sha256() function and the file() function:
# source_code_hash = "${base64sha256(file("stone_search.zip"))}"
source_code_hash = filebase64sha256("stone_search.zip")
runtime = "python3.6"
environment {
variables = {
DB_TABLE_NAME = "stone-test"
}
}
}
resource "aws_lambda_function" "stone2" {
filename = "bot.zip"
function_name = "stone3"
role = aws_iam_role.role_for_LDC.arn
handler = "bot.lambda_handler"
# The filebase64sha256() function is available in Terraform 0.11.12 and later
# For Terraform 0.11.11 and earlier, use the base64sha256() function and the file() function:
# source_code_hash = "${base64sha256(file("bot.zip"))}"
source_code_hash = filebase64sha256("bot.zip")
runtime = "python3.6"
environment {
variables = {
DB_TABLE_NAME = "stone-test"
}
}
}
resource "aws_api_gateway_rest_api" "apiLambda" {
name = "myAPI"
}
resource "aws_api_gateway_resource" "Resource" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
parent_id = aws_api_gateway_rest_api.apiLambda.root_resource_id
path_part = "bot"
}
resource "aws_api_gateway_method" "Method" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
resource_id = aws_api_gateway_resource.Resource.id
http_method = "POST"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "lambdaInt" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
resource_id = aws_api_gateway_resource.Resource.id
http_method = aws_api_gateway_method.Method.http_method
integration_http_method = "POST"
type = "AWS"
uri = aws_lambda_function.stone2.invoke_arn
}
resource "aws_api_gateway_method_response" "response_200" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
resource_id = aws_api_gateway_resource.Resource.id
http_method = aws_api_gateway_method.Method.http_method
status_code = "200"
}
resource "aws_api_gateway_integration_response" "MyDemoIntegrationResponse" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
resource_id = aws_api_gateway_resource.Resource.id
http_method = aws_api_gateway_method.Method.http_method
status_code = aws_api_gateway_method_response.response_200.status_code
}
resource "aws_api_gateway_resource" "Resource2" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
parent_id = aws_api_gateway_rest_api.apiLambda.root_resource_id
path_part = "register"
}
resource "aws_api_gateway_method" "Method2" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
resource_id = aws_api_gateway_resource.Resource2.id
http_method = "POST"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "lambdaInt2" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
resource_id = aws_api_gateway_resource.Resource2.id
http_method = aws_api_gateway_method.Method2.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.stone_register2.invoke_arn
}
resource "aws_api_gateway_resource" "Resource3" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
parent_id = aws_api_gateway_rest_api.apiLambda.root_resource_id
path_part = "delete"
}
resource "aws_api_gateway_method" "Method3" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
resource_id = aws_api_gateway_resource.Resource3.id
http_method = "POST"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "lambdaInt3" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
resource_id = aws_api_gateway_resource.Resource3.id
http_method = aws_api_gateway_method.Method3.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.stone_delete2.invoke_arn
}
resource "aws_api_gateway_resource" "Resource4" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
parent_id = aws_api_gateway_rest_api.apiLambda.root_resource_id
path_part = "search"
}
resource "aws_api_gateway_method" "Method4" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
resource_id = aws_api_gateway_resource.Resource4.id
http_method = "POST"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "lambdaInt4" {
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
resource_id = aws_api_gateway_resource.Resource4.id
http_method = aws_api_gateway_method.Method4.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.stone_search2.invoke_arn
}
resource "aws_api_gateway_deployment" "apideploy" {
depends_on = [aws_api_gateway_integration.lambdaInt]
rest_api_id = aws_api_gateway_rest_api.apiLambda.id
stage_name = "Prod"
}
resource "aws_lambda_permission" "apigw" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.stone2.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.apiLambda.execution_arn}/Prod/POST/bot"
}
resource "aws_lambda_permission" "apigw2" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.stone_register2.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.apiLambda.execution_arn}/Prod/POST/register"
}
resource "aws_lambda_permission" "apigw3" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.stone_delete2.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.apiLambda.execution_arn}/Prod/POST/delete"
}
resource "aws_lambda_permission" "apigw4" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.stone_search2.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.apiLambda.execution_arn}/Prod/POST/search"
}
output "base_url" {
value = aws_api_gateway_deployment.apideploy.invoke_url
}
You are already on the correct path, all you need to do create the method_response and use the same for creating the integration_response
And change the integration type
There is a comprehensive list of the types and what they can do and how can you leverage them in documentation
I only adjusted a few settings in the code you shared, which are below:
...
resource "aws_api_gateway_integration" "integration" {
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.resource.id
http_method = aws_api_gateway_method.method.http_method
integration_http_method = "POST"
type = "AWS"
uri = aws_lambda_function.lambda.invoke_arn
}
....
resource "aws_api_gateway_method_response" "response_200" {
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.resource.id
http_method = aws_api_gateway_method.method.http_method
status_code = "200"
}
resource "aws_api_gateway_integration_response" "MyDemoIntegrationResponse" {
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.resource.id
http_method = aws_api_gateway_method.method.http_method
status_code = aws_api_gateway_method_response.response_200.status_code
}
when I test the function from the AWS console I get the following:

How do I get resource path for Root Resource in ApiGateway?

I am trying to change cache settings in api gateway for GET/OPTIONS methods of root resource using terraform.
resource "aws_api_gateway_method_settings" "root_get_method_settings" {
rest_api_id = aws_api_gateway_rest_api.default.id
stage_name = terraform.workspace
method_path = "<root resource path>/GET"
settings {
metrics_enabled = true
logging_level = "INFO"
caching_enabled = true
}
}
I am getting issue on method_path argument since I couldn't figure out correct value for <root resource path>.
method_path for any other resources like {proxy+} resource looks like below:
resource "aws_api_gateway_method_settings" "proxy_get_method_settings" {
rest_api_id = aws_api_gateway_rest_api.default.id
stage_name = terraform.workspace
method_path = "{proxy+}/GET"
}
The path is "~1/GET". Below is full working example:
resource "aws_api_gateway_rest_api" "MyDemoAPI" {
name = "MyDemoAPI"
description = "This is my API for demonstration purposes"
}
resource "aws_api_gateway_method" "MyDemoMethod" {
rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
resource_id = aws_api_gateway_rest_api.MyDemoAPI.root_resource_id
http_method = "GET"
authorization = "NONE"
}
resource "aws_api_gateway_deployment" "test" {
depends_on = [aws_api_gateway_integration.test]
rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
stage_name = "prod"
}
resource "aws_api_gateway_integration" "test" {
rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
resource_id = aws_api_gateway_rest_api.MyDemoAPI.root_resource_id
http_method = aws_api_gateway_method.MyDemoMethod.http_method
type = "MOCK"
}
resource "aws_api_gateway_method_settings" "root_get_method_settings" {
rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
stage_name = "prod"
method_path = "~1/GET"
settings {
metrics_enabled = true
logging_level = "INFO"
caching_enabled = true
}
depends_on = [aws_api_gateway_deployment.test]
}

Terraformed AWS API Gateway Custom Domain Names throws 403 Forbidden

I am trying to expose all the stages of my Regional API Gateway through a regional Custom Domain.
Problem
If I curl directly my API Gateway (ie. https://xx.execute-api.eu-west-3.amazonaws.com/default/users), it works, but I get a 403 if I curl de domain name (ie. https://api.acme.com/default/users).
Configuration
My Terraform files looks like that:
data "aws_route53_zone" "acme" {
name = "acme.com."
}
resource "aws_api_gateway_rest_api" "backend" {
name = "acme-backend-api"
description = "Backend API"
body = "SOMETHING"
endpoint_configuration {
types = ["REGIONAL"]
}
}
resource "aws_api_gateway_deployment" "backend" {
rest_api_id = aws_api_gateway_rest_api.backend.id
stage_name = "default"
lifecycle {
create_before_destroy = true
}
}
resource "aws_api_gateway_domain_name" "backend" {
domain_name = "api.acme.com"
regional_certificate_arn = "arn:aws:acm:xx:certificate/xx"
endpoint_configuration {
types = ["REGIONAL"]
}
}
resource "aws_route53_record" "backend" {
name = aws_api_gateway_domain_name.backend.domain_name
type = "A"
zone_id = data.aws_route53_zone.acme.id
alias {
evaluate_target_health = true
name = aws_api_gateway_domain_name.backend.regional_domain_name
zone_id = aws_api_gateway_domain_name.backend.regional_zone_id
}
}
resource "aws_api_gateway_base_path_mapping" "backend" {
api_id = aws_api_gateway_rest_api.backend.id
domain_name = aws_api_gateway_domain_name.backend.domain_name
# No stage_name: expose all stages
}
According to the Terraform api_gateway_domain_name and api_gateway_base_path_mapping examples, it should be ok.
I have also followed many howtos, and I have these elements:
The certificate
The A record to the API custom domain
The mapping to the deployed stage (which works if you call it directly)
What do I miss/do wrong?
This is v2 example working for me as off today, this "aws_apigatewayv2_api_mapping" is key to avoid port 80: Connection refused
or {"message":"Forbidden"} errors which I see you have but I did struggle with.
// ACM
resource "aws_acm_certificate" "cert_api" {
domain_name = var.api_domain
validation_method = "DNS"
tags = {
Name = var.api_domain
}
}
resource "aws_acm_certificate_validation" "cert_api" {
certificate_arn = aws_acm_certificate.cert_api.arn
}
// API Gateway V2
resource "aws_apigatewayv2_api" "lambda" {
name = "serverless_lambda_gw"
protocol_type = "HTTP"
}
resource "aws_apigatewayv2_stage" "lambda" {
api_id = aws_apigatewayv2_api.lambda.id
name = "serverless_lambda_stage"
auto_deploy = true
access_log_settings {
destination_arn = aws_cloudwatch_log_group.api_gw.arn
format = jsonencode({
requestId = "$context.requestId"
sourceIp = "$context.identity.sourceIp"
requestTime = "$context.requestTime"
protocol = "$context.protocol"
httpMethod = "$context.httpMethod"
resourcePath = "$context.resourcePath"
routeKey = "$context.routeKey"
status = "$context.status"
responseLength = "$context.responseLength"
integrationErrorMessage = "$context.integrationErrorMessage"
}
)
}
}
resource "aws_apigatewayv2_integration" "testimonials" {
api_id = aws_apigatewayv2_api.lambda.id
integration_uri = aws_lambda_function.testimonials.invoke_arn
integration_type = "AWS_PROXY"
integration_method = "POST"
}
resource "aws_apigatewayv2_route" "testimonials" {
api_id = aws_apigatewayv2_api.lambda.id
route_key = "GET /testimonials"
target = "integrations/${aws_apigatewayv2_integration.testimonials.id}"
}
resource "aws_cloudwatch_log_group" "api_gw" {
name = "/aws/api_gw/${aws_apigatewayv2_api.lambda.name}"
retention_in_days = 30
}
resource "aws_lambda_permission" "api_gw" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.testimonials.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.lambda.execution_arn}/*/*"
}
resource "aws_apigatewayv2_domain_name" "api" {
domain_name = var.api_domain
domain_name_configuration {
certificate_arn = aws_acm_certificate.cert_api.arn
endpoint_type = "REGIONAL"
security_policy = "TLS_1_2"
}
}
resource "aws_apigatewayv2_api_mapping" "api" {
api_id = aws_apigatewayv2_api.lambda.id
domain_name = aws_apigatewayv2_domain_name.api.id
stage = aws_apigatewayv2_stage.lambda.id
}
// Route53
resource "aws_route53_zone" "api" {
name = var.api_domain
}
resource "aws_route53_record" "cert_api_validations" {
allow_overwrite = true
count = length(aws_acm_certificate.cert_api.domain_validation_options)
zone_id = aws_route53_zone.api.zone_id
name = element(aws_acm_certificate.cert_api.domain_validation_options.*.resource_record_name, count.index)
type = element(aws_acm_certificate.cert_api.domain_validation_options.*.resource_record_type, count.index)
records = [element(aws_acm_certificate.cert_api.domain_validation_options.*.resource_record_value, count.index)]
ttl = 60
}
resource "aws_route53_record" "api-a" {
name = aws_apigatewayv2_domain_name.api.domain_name
type = "A"
zone_id = aws_route53_zone.api.zone_id
alias {
name = aws_apigatewayv2_domain_name.api.domain_name_configuration[0].target_domain_name
zone_id = aws_apigatewayv2_domain_name.api.domain_name_configuration[0].hosted_zone_id
evaluate_target_health = false
}
}

AWS API Gateway and static HTML: "Execution failed due to configuration error: statusCode should be an integer which defined in request template"

I am trying to serve a static content using AWS API Gateway.
When I attempt to invoke the URL, both from the test page and from curl, I get the error:
"Execution failed due to configuration error: statusCode should be an integer which defined in request template".
This is my configuration on Terraform:
resource "aws_api_gateway_rest_api" "raspberry_api" {
name = "raspberry_api"
}
resource "aws_acm_certificate" "raspberry_alexa_mirko_io" {
domain_name = "raspberry.alexa.mirko.io"
validation_method = "DNS"
lifecycle {
create_before_destroy = true
}
}
resource "aws_route53_record" "raspberry_alexa_mirko_io_cert_validation" {
name = aws_acm_certificate.raspberry_alexa_mirko_io.domain_validation_options.0.resource_record_name
type = aws_acm_certificate.raspberry_alexa_mirko_io.domain_validation_options.0.resource_record_type
zone_id = var.route53_zone_id
records = [aws_acm_certificate.raspberry_alexa_mirko_io.domain_validation_options.0.resource_record_value]
ttl = 60
}
resource "aws_route53_record" "raspberry_alexa_mirko_io" {
zone_id = var.route53_zone_id
name = aws_acm_certificate.raspberry_alexa_mirko_io.domain_name
type = "A"
alias {
name = aws_api_gateway_domain_name.raspberry_alexa_mirko_io.cloudfront_domain_name
zone_id = aws_api_gateway_domain_name.raspberry_alexa_mirko_io.cloudfront_zone_id
evaluate_target_health = true
}
}
resource "aws_acm_certificate_validation" "raspberry_alexa_mirko_io" {
certificate_arn = aws_acm_certificate.raspberry_alexa_mirko_io.arn
validation_record_fqdns = [aws_route53_record.raspberry_alexa_mirko_io_cert_validation.fqdn]
provider = aws.useast1
}
resource "aws_api_gateway_domain_name" "raspberry_alexa_mirko_io" {
certificate_arn = aws_acm_certificate_validation.raspberry_alexa_mirko_io.certificate_arn
domain_name = aws_acm_certificate.raspberry_alexa_mirko_io.domain_name
}
resource "aws_api_gateway_base_path_mapping" "raspberry_alexa_mirko_io_base_path_mapping" {
api_id = aws_api_gateway_rest_api.raspberry_api.id
domain_name = aws_api_gateway_domain_name.raspberry_alexa_mirko_io.domain_name
}
resource "aws_api_gateway_resource" "home" {
rest_api_id = aws_api_gateway_rest_api.raspberry_api.id
parent_id = aws_api_gateway_rest_api.raspberry_api.root_resource_id
path_part = "login"
}
resource "aws_api_gateway_method" "login" {
rest_api_id = aws_api_gateway_rest_api.raspberry_api.id
resource_id = aws_api_gateway_resource.home.id
http_method = "GET"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "integration" {
rest_api_id = aws_api_gateway_rest_api.raspberry_api.id
resource_id = aws_api_gateway_resource.subscribe_raspberry.id
http_method = aws_api_gateway_method.subscribe.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.raspberry_lambda.invoke_arn
# This was just a failed attempt. It did not fix anything
request_templates = {
"text/html" = "{\"statusCode\": 200}"
}
}
resource "aws_api_gateway_integration" "login_page" {
rest_api_id = aws_api_gateway_rest_api.raspberry_api.id
resource_id = aws_api_gateway_resource.home.id
http_method = aws_api_gateway_method.login.http_method
type = "MOCK"
timeout_milliseconds = 29000
}
resource "aws_api_gateway_method_response" "response_200" {
rest_api_id = aws_api_gateway_rest_api.raspberry_api.id
resource_id = aws_api_gateway_resource.home.id
http_method = aws_api_gateway_method.login.http_method
status_code = "200"
}
resource "aws_api_gateway_integration_response" "login_page" {
rest_api_id = aws_api_gateway_rest_api.raspberry_api.id
resource_id = aws_api_gateway_resource.home.id
http_method = aws_api_gateway_method.login.http_method
status_code = aws_api_gateway_method_response.response_200.status_code
response_templates = {
"text/html" = data.template_file.login_page.rendered
}
}
resource "aws_api_gateway_deployment" "example" {
depends_on = [
aws_api_gateway_integration.login_page
]
rest_api_id = aws_api_gateway_rest_api.raspberry_api.id
stage_name = "production"
}
I have followed the instructions as in this blog, with no success.
"200" (with quotes) is considered a string, not an integer
try status_code = 200 (without quotes)
Just to repost the excellent answer of TheClassic here, the format seems to be:
request_templates = {
"application/json" = jsonencode(
{
statusCode = 200
}
)
}
I also had this same problem, but looks like this works.
I had the same error because my code looked like this beforehand - inspired by the terraform docs.
resource "aws_api_gateway_integration" "api_gateway" {
http_method = aws_api_gateway_method.api_gateway.http_method
resource_id = aws_api_gateway_resource.api_gateway.id
rest_api_id = aws_api_gateway_rest_api.api_gateway.id
type = "MOCK"
}
After reading this thread it now works looking like this:
resource "aws_api_gateway_integration" "api_gateway" {
http_method = aws_api_gateway_method.api_gateway.http_method
resource_id = aws_api_gateway_resource.api_gateway.id
rest_api_id = aws_api_gateway_rest_api.api_gateway.id
type = "MOCK"
request_templates = {
"application/json" = jsonencode(
{
statusCode = 200
}
)
}
}
As per Bernie comment, this status code needs to be explicitly provided in request_templates attribute in aws_api_gateway_integration terraform resource.
After adding it I finally got 200 for OPTIONS that are integrated via MOCK endpoint.
For others who might see this, this error can also be caused by a need to verify that when you use Mock as your Integration type, you confirm that your RequestTemplates contain statusCode and the value of statusCode is equal to one of your IntegrationResponses/ResponseTemplates/StatusCode
Something like:
requestTemplates: {
"application/json": "{\"statusCode\": 200}"
}

How do I create an API Proxy using Terraform and AWS API Gateway

I am trying to use Terraform to be able to stand up a simple API Proxy in API Gateway on AWS. Basically, I want to wrap root and proxy the requests back to another end point. Its probably the simplest setup and I can't seem to get it to work in Terraform.
Below you will find the script. At this point I am able to create the REST API, define a Resource, create a method but there doesn't seem to be any way to define it the end-point.
provider "aws" {
region = "us-east-1"
}
resource "aws_api_gateway_rest_api" "TerraTest" {
name = "TerraTest"
description = "This is my API for demonstration purposes"
}
resource "aws_api_gateway_resource" "TerraProxyResource" {
rest_api_id = "${aws_api_gateway_rest_api.TerraTest.id}"
parent_id = "${aws_api_gateway_rest_api.TerraTest.root_resource_id}"
path_part = "{proxy+}"
}
resource "aws_api_gateway_integration" "integration" {
rest_api_id = "${aws_api_gateway_rest_api.TerraTest.id}"
resource_id = "${aws_api_gateway_resource.TerraProxyResource.id}"
http_method = "${aws_api_gateway_method.mymethod.http_method}"
type = "HTTP_PROXY"
uri = "http://api.endpoint.com/{proxy+}"
}
Here I set the type to proxy, but I don't think URI is the right property for setting the endpoint.
resource "aws_api_gateway_method" "mymethod" {
rest_api_id = "${aws_api_gateway_rest_api.TerraTest.id}"
resource_id = "${aws_api_gateway_resource.TerraProxyResource.id}"
http_method = "ANY"
authorization = "NONE"
}
I expect somewhere here to be able to create that mapping to some other endpoint, but there doesn't appear to be any properties for that. (https://github.com/hashicorp/terraform/blob/master/builtin/providers/aws/resource_aws_api_gateway_method.go)
resource "aws_api_gateway_api_key" "TerraTestKey" {
name = "Terra_Test_Key"
stage_key {
rest_api_id = "${aws_api_gateway_rest_api.TerraTest.id}"
stage_name = "${aws_api_gateway_deployment.TerraTestDeployment.stage_name}"
}
}
resource "aws_api_gateway_deployment" "TerraTestDeployment" {
rest_api_id = "${aws_api_gateway_rest_api.TerraTest.id}"
stage_name = "dev"
}
I scanned the source code and I didn't see any properties that I can set.
Can anyone share any advice/snipets?
Tim
Ps. If you want to try to run the script yourself, I put it here: http://textuploader.com/d14sx
This is the relevant module which shows a working solution. It doesn't stand alone since it relies on some variables defined elsewhere but it should be enough to help anyone struggling to get a AWS Proxy setup and also shows Lambda authorizer integration as a bonus.
provider "aws" {
region = "${var.region}"
profile = "${var.profile}"
}
data "aws_iam_role" "api_user" {
role_name = "api_user"
}
module "authorizer_lambda" {
source = "../lambda"
name = "${var.api_name}-authorizer_lambda"
filename = "authorizer_lambda"
runtime = "nodejs4.3"
role = "${data.aws_iam_role.api_user.arn}"
}
resource "aws_api_gateway_authorizer" "custom_authorizer" {
name = "${var.api_name}-custom_authorizer"
rest_api_id = "${aws_api_gateway_rest_api.ApiGateway.id}"
authorizer_uri = "${module.authorizer_lambda.uri}"
authorizer_credentials = "${data.aws_iam_role.api_user.arn}"
authorizer_result_ttl_in_seconds = 1
}
resource "aws_api_gateway_rest_api" "ApiGateway" {
name = "${var.api_name}"
description = "${var.api_description}"
}
resource "aws_api_gateway_resource" "ApiProxyResource" {
rest_api_id = "${aws_api_gateway_rest_api.ApiGateway.id}"
parent_id = "${aws_api_gateway_rest_api.ApiGateway.root_resource_id}"
path_part = "{proxy+}"
}
resource "aws_api_gateway_integration" "ApiProxyIntegration" {
rest_api_id = "${aws_api_gateway_rest_api.ApiGateway.id}"
resource_id = "${aws_api_gateway_resource.ApiProxyResource.id}"
http_method = "${aws_api_gateway_method.ApiProxyMethod.http_method}"
type = "HTTP_PROXY"
integration_http_method = "ANY"
uri = "${format("%s/{proxy}", "${var.base_url}")}"
passthrough_behavior = "WHEN_NO_MATCH"
request_parameters = "${var.aws_api_gateway_integration_request_parameters}"
}
resource "aws_api_gateway_method" "ApiProxyMethod" {
rest_api_id = "${aws_api_gateway_rest_api.ApiGateway.id}"
resource_id = "${aws_api_gateway_resource.ApiProxyResource.id}"
http_method = "ANY"
authorization = "CUSTOM"
authorizer_id = "${aws_api_gateway_authorizer.custom_authorizer.id}"
request_parameters = {"method.request.path.proxy" = true}
}
resource "aws_api_gateway_deployment" "ApiDeployment" {
depends_on = ["aws_api_gateway_method.ApiProxyMethod"]
rest_api_id = "${aws_api_gateway_rest_api.ApiGateway.id}"
stage_name = "${var.stage_name}"
}