Terraform AWS API Gateway with Proxy BadRequest Exception Invalid Mapping - amazon-web-services

I am trying to create an aws proxy with terraform. I am currently stuck when applying the changes.
The error I get within the resource "aws_api_gateway_integration" is the following:
Error: Error creating API Gateway Integration: BadRequestException: Invalid mapping expression specified: Validation Result: warnings : [], errors : [Parameter name should match the following regular expression: ^[a-zA-Z0-9:._$-]+$]
Here is my code:
resource "aws_api_gateway_integration" "itemPutMethod-ApiProxyIntegration" {
rest_api_id = "${aws_api_gateway_rest_api.apiGateway.id}"
resource_id = "${aws_api_gateway_resource.itemResource.id}"
http_method = "${aws_api_gateway_method.itemPutMethod.http_method}"
type = "AWS"
integration_http_method = "PUT"
credentials = "${aws_iam_role.s3_proxy_role.arn}"
uri = "arn:aws:apigateway:${var.region}:s3:path/{bucket}/{folder}/{item}"
request_parameters = {
"integration.request.header.x-amz-meta-fileinfo" = "method.request.header.x-amz-meta-fileinfo"
"integration.request.header.Accept" = "method.request.header.Accept"
"integration.request.header.Content-Type " = "method.request.header.Content-Type"
"integration.request.path.item" = "method.request.path.item"
"integration.request.path.folder" = "method.request.path.folder"
"integration.request.path.bucket" = "method.request.path.bucket"
}
}
And here is my code for the method:
resource "aws_api_gateway_method" "itemPutMethod" {
rest_api_id = "${aws_api_gateway_rest_api.apiGateway.id}"
resource_id = "${aws_api_gateway_resource.itemResource.id}"
http_method = "PUT"
authorization = "NONE"
api_key_required = true
request_parameters = {
"method.request.header.Accept" = false
"method.request.header.Content-Type" = false
"method.request.header.x-amz-meta-fileinfo" = false
"method.request.path.bucket" = true
"method.request.path.folder" = true
"method.request.path.item" = true
}
}
As far as I understand I have a bad character in the parameter names but I can't figure out where. Any help would be greatly appreciated, thank you!
API Gateway Resource
resource "aws_api_gateway_resource" "bucketResource" {
rest_api_id = "${aws_api_gateway_rest_api.apiGateway.id}"
parent_id = "${aws_api_gateway_rest_api.apiGateway.root_resource_id}"
path_part = "{bucket}"
}
resource "aws_api_gateway_resource" "folderResource" {
rest_api_id = "${aws_api_gateway_rest_api.apiGateway.id}"
parent_id = "${aws_api_gateway_resource.bucketResource.id}"
path_part = "{folder}"
}
resource "aws_api_gateway_resource" "itemResource" {
rest_api_id = "${aws_api_gateway_rest_api.apiGateway.id}"
parent_id = "${aws_api_gateway_resource.folderResource.id}"
path_part = "{item}"
}
Link to tutorial code:
https://github.com/robty123/s3-proxy

Your request_parameters argument in aws_api_gateway_integration resource have a space in one of the mappings. That seems to be the cause of failing regex match.
Change this
"integration.request.header.Content-Type " = "method.request.header.Content-Type"
to
"integration.request.header.Content-Type" = "method.request.header.Content-Type"

Related

Enable caching for url query parameters in aws api gateway with terraform

resource "aws_api_gateway_method" "MyDemoMethod" {
rest_api_id = aws_api_gateway_rest_api.example.id
resource_id = aws_api_gateway_resource.MyDemoResource.id
http_method = "ANY"
authorization = "NONE"
request_parameters = {
"method.request.path.proxy" = true
"method.request.querystring.tableid" = true
}
}
With this script, I am trying to add a URL query parameter named tableid with caching enabled. But I can see no documentation referring to enabling the caching.
To do so, this can be done using cache_key_parameters and cache_namespace below the aws_api_gateway_integration as follows:
resource "aws_api_gateway_method" "MyDemoMethod" {
rest_api_id = aws_api_gateway_rest_api.example.id
resource_id = aws_api_gateway_resource.MyDemoResource.id
http_method = "ANY"
authorization = "NONE"
request_parameters = {
"method.request.path.proxy" = true
"method.request.querystring.tableid" = true
}
}
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
type = "MOCK"
cache_key_parameters = ["method.request.querystring.tableid"]
cache_namespace = "mycache"
}
This was introduced in this Pull Request https://github.com/hashicorp/terraform-provider-aws/pull/893.

Error creating API Gateway Integration Response: NotFoundException: Invalid Integration identifier specified

Objective
Solution or workaround for the problem.
Problem
The Terraform API Gateway integration with Firehose below works if Firehose is created separately in advance.
resource "aws_api_gateway_integration" "click_put" {
rest_api_id = data.aws_api_gateway_rest_api.mysfit.id
resource_id = aws_api_gateway_resource.click.id
type = "AWS"
uri = "arn:aws:apigateway:${var.REGION}:firehose:action/PutRecord"
credentials = aws_iam_role.api_click.arn
http_method = aws_api_gateway_method.click_put.http_method
integration_http_method = "POST"
request_parameters = {
"integration.request.header.Content-Type" = "'application/x-amz-json-1.1'"
}
passthrough_behavior = "NEVER"
request_templates = {
"application/json" = <<EOF
{
"DeliveryStreamName": "${local.firehose_name}",
"Record": {
"Data": "$util.base64Encode($input.json('$'))"
}
}
EOF
}
}
...
resource "aws_api_gateway_integration_response" "click_put" {
rest_api_id = data.aws_api_gateway_rest_api.mysfit.id
resource_id = aws_api_gateway_resource.click.id
http_method = aws_api_gateway_method.click_put.http_method
status_code = aws_api_gateway_method_response.click_put.status_code
response_parameters = {
"method.response.header.Access-Control-Allow-Origin" = "'*'"
}
}
However, if they are created in the same root module, it causes the error.
Error creating API Gateway Integration Response: NotFoundException: Invalid Integration identifier specified
on api_click.tf line 185, in resource "aws_api_gateway_integration_response" "click_put":
185: resource "aws_api_gateway_integration_response" "click_put" {
Workaround/Solution
Place a dependency on the aws_api_gateway_integration from the resource causing "NotFoundException: Invalid Integration identifier specified".
resource "aws_api_gateway_integration_response" "click_put" {
rest_api_id = data.aws_api_gateway_rest_api.mysfit.id
resource_id = aws_api_gateway_resource.click.id
http_method = aws_api_gateway_method.click_put.http_method
status_code = aws_api_gateway_method_response.click_put.status_code
response_parameters = {
"method.response.header.Access-Control-Allow-Origin" = "'*'"
}
depends_on = [
aws_api_gateway_integration.click_put
]
}
References
There are indications that depends_on aws_api_gateway_integration or placing a wait would be the way.
Probably waiting for the complete completion of the aws_api_gateway_integration resource would be a recommended practice.
aws_api_gateway_integration_response
Note: Depends on having aws_api_gateway_integration inside your rest api. To ensure this you might need to add an explicit depends_on for clean runs.
API gateway integration issue - Invalid Method identifier specified Error #4001
I'm using the following workaround to solve a similar issue. I'm getting the Invalid Method identifier error on the first run when all the resources are created during the same apply. A proxy API for Lambda is created:
resource "aws_api_gateway_resource" "proxy" {
rest_api_id = aws_api_gateway_rest_api.rest-api.id
parent_id = aws_api_gateway_rest_api.rest-api.root_resource_id
path_part = "{proxy+}"
}
resource "null_resource" "method-delay" {
provisioner "local-exec" {
command = "sleep 5"
}
triggers = {
response = aws_api_gateway_resource.proxy.id
}
}
resource "aws_api_gateway_method_response" "response" {
depends_on = [null_resource.method-delay]
http_method = "ANY"
resource_id = aws_api_gateway_resource.proxy.id
rest_api_id = aws_api_gateway_rest_api.rest-api.id
}
you can just add depends_on. Should be something like:
resource "aws_api_gateway_integration" "create-oauth-lambda" {
rest_api_id = aws_api_gateway_rest_api.create-oauth-token-api-gw.id
resource_id = aws_api_gateway_resource.auth-token-resource.id
http_method = aws_api_gateway_method.method.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = "some URI"
}
resource "aws_api_gateway_method_response" "response-200" {
depends_on = [aws_api_gateway_integration.create-oauth-lambda]
rest_api_id = aws_api_gateway_rest_api.create-oauth-token-api-gw.id
resource_id = aws_api_gateway_resource.auth-token-resource.id
http_method = aws_api_gateway_method.method.http_method
status_code = "200"
response_models = {
"application/json" = "Empty"
}
}

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 to use api_gateway_base_path_mapping with terraform?

I'm trying to setup a custom domain name for an api in api gateway on aws. I have setup the api fine using terraform. However when I try to setup the custom domain it fails with the following error.
Error applying plan:
1 error(s) occurred:
module.BillingMetrics.aws_api_gateway_base_path_mapping.billing: 1 error(s) occurred:
aws_api_gateway_base_path_mapping.billing: Error creating Gateway base path mapping: Error creating Gateway base path mapping: BadRequestException: Invalid REST API identifier specified
status code: 400, request id: b14bbd4c-5823-11e7-a4ea-93525a34b321
I can see in the terraform log that it does get the correct api_id. But I don't understand why it's saying the rest api identifier is invalid.
Below is an excerpt of my terraform file showing how I'm configuring the api_gateway_base_path_mapping.
resource "aws_api_gateway_resource" "views_resource" {
provider = "aws.regional"
rest_api_id = "${aws_api_gateway_rest_api.billing_api.id}"
parent_id = "${aws_api_gateway_rest_api.billing_api.root_resource_id}"
path_part = "views"
}
resource "aws_api_gateway_method" "views-get" {
provider = "aws.regional"
rest_api_id = "${aws_api_gateway_rest_api.billing_api.id}"
resource_id = "${aws_api_gateway_resource.views_resource.id}"
http_method = "GET"
authorization = "NONE"
}
resource "aws_api_gateway_method_response" "views_200" {
provider = "aws.regional"
rest_api_id = "${aws_api_gateway_rest_api.billing_api.id}"
resource_id = "${aws_api_gateway_resource.views_resource.id}"
http_method = "${aws_api_gateway_method.views-get.http_method}"
status_code = "200"
}
resource "aws_api_gateway_integration" "views-integration" {
provider = "aws.regional"
rest_api_id = "${aws_api_gateway_rest_api.billing_api.id}"
resource_id = "${aws_api_gateway_resource.views_resource.id}"
http_method = "${aws_api_gateway_method.views-get.http_method}"
type = "AWS"
uri = "arn:aws:apigateway:${var.region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${var.region}:${var.account_id}:function:${aws_lambda_function.get_views.function_name}/invocations"
credentials = "${var.metrics_role_arn}"
http_method = "${aws_api_gateway_method.views-get.http_method}"
integration_http_method = "POST"
}
resource "aws_api_gateway_integration_response" "Views_Get_IntegrationResponse" {
provider = "aws.regional"
rest_api_id = "${aws_api_gateway_rest_api.billing_api.id}"
resource_id = "${aws_api_gateway_resource.views_resource.id}"
http_method = "${aws_api_gateway_method.views-get.http_method}"
status_code = "${aws_api_gateway_method_response.views_200.status_code}"
}
/* Deploy api */
resource "aws_api_gateway_deployment" "metric_deploy" {
provider = "aws.regional"
depends_on = ["aws_api_gateway_integration.metrics-integration", "aws_api_gateway_integration.hours-integration"]
stage_name = "beta"
rest_api_id = "${aws_api_gateway_rest_api.billing_api.id}"
}
resource "aws_api_gateway_domain_name" "billing" {
domain_name = "billing.example.com"
certificate_arn = "arn:aws:acm:us-east-1:6--:certificate/5--"
}
resource "aws_api_gateway_base_path_mapping" "billing" {
api_id = "${aws_api_gateway_rest_api.billing_api.id}"
stage_name = "${aws_api_gateway_deployment.metric_deploy.stage_name}"
domain_name = "${aws_api_gateway_domain_name.billing.domain_name}"
}
resource "aws_route53_record" "billing" {
zone_id = "Z-------"
name = "${aws_api_gateway_domain_name.billing.domain_name}"
type = "A"
alias {
name = "${aws_api_gateway_domain_name.billing.cloudfront_domain_name}"
zone_id = "${aws_api_gateway_domain_name.billing.cloudfront_zone_id}"
evaluate_target_health = true
}
}
Are there any more elements that needed to be configured to have the base_path_mapping apply correctly? Any other hints what I might be doing wrong?
I should also mention I'm on terraform 0.9.7.

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}"
}