L0cu2s
L0cu2s

Reputation: 565

How to enable CORS on AWS with terraform

I am trying to enable CORS on my aws project which consists of API Gateway and Lambda function. I'm creating an API Gateway with GET and OPTIONS methods. OPTIONS is meant to be a mock endpoint for enabling CORS as per aws documentation. There is a lambda function (aws_lambda_function.app_lambda) which is invoked by GET method and in response headers has:

"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"

But still, I cannot pass CORS.

resource "aws_api_gateway_rest_api" "rest_api" {
  name        = "appAPIGateway"
  description = "App App App"
}

resource "aws_api_gateway_resource" "rest_api_resource" {
  depends_on = ["aws_api_gateway_rest_api.rest_api"]
  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 = "playground"
}

resource "aws_api_gateway_method" "opt" {
  rest_api_id   = "${aws_api_gateway_rest_api.rest_api.id}"
  resource_id   = "${aws_api_gateway_resource.rest_api_resource.id}"
  http_method   = "OPTIONS"
  authorization = "NONE"
  api_key_required = true
}

resource "aws_api_gateway_integration" "opt" {
  rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
  resource_id = "${aws_api_gateway_resource.rest_api_resource.id}"
  http_method = "${aws_api_gateway_method.opt.http_method}"
  type = "MOCK"
}

resource "aws_api_gateway_integration_response" "opt" {
  rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
  resource_id = "${aws_api_gateway_resource.rest_api_resource.id}"
  http_method = "${aws_api_gateway_method.opt.http_method}"
  status_code = 200
  response_parameters = {
    "method.response.header.Access-Control-Allow-Origin" = "'*'",
    "method.response.header.Access-Control-Allow-Headers" = "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Requested-With'",
    "method.response.header.Access-Control-Allow-Methods" = "'GET,OPTIONS,POST,PUT'"
  }
  depends_on = ["aws_api_gateway_integration.opt", "aws_api_gateway_method_response.opt"]
}

resource "aws_api_gateway_method_response" "opt" {
  rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
  resource_id = "${aws_api_gateway_resource.rest_api_resource.id}"
  http_method = "${aws_api_gateway_method.opt.http_method}"
  status_code = 200
  response_parameters = {
    "method.response.header.Access-Control-Allow-Origin" = true,
    "method.response.header.Access-Control-Allow-Methods" = true,
    "method.response.header.Access-Control-Allow-Headers" = true
  }
  response_models = {
    "application/json" = "Empty"
  }
  depends_on = ["aws_api_gateway_method.opt"]
}

resource "aws_api_gateway_method" "app_api_gateway_method" {
  rest_api_id      = "${aws_api_gateway_rest_api.rest_api.id}"
  resource_id      = "${aws_api_gateway_resource.rest_api_resource.id}"
  http_method      = "GET"
  authorization    = "NONE"
  api_key_required = true
}

resource "aws_api_gateway_method_response" "app_cors_method_response_200" {
    rest_api_id   = "${aws_api_gateway_rest_api.rest_api.id}"
    resource_id   = "${aws_api_gateway_resource.rest_api_resource.id}"
    http_method   = "${aws_api_gateway_method.app_api_gateway_method.http_method}"
    status_code   = "200"
    response_parameters = {
    "method.response.header.Access-Control-Allow-Origin" = true,
    "method.response.header.Access-Control-Allow-Methods" = true,
    "method.response.header.Access-Control-Allow-Headers" = true
  }
    depends_on = ["aws_api_gateway_method.app_api_gateway_method"]
}

resource "aws_api_gateway_integration" "app_api_gateway_integration" {
  rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
  resource_id = "${aws_api_gateway_method.app_api_gateway_method.resource_id}"
  http_method = "${aws_api_gateway_method.app_api_gateway_method.http_method}"
  integration_http_method = "POST"
  type                    = "AWS_PROXY"
  uri                     = "${aws_lambda_function.app_lambda.invoke_arn}"
  depends_on    = [
    "aws_api_gateway_method.app_api_gateway_method",
    "aws_lambda_function.app_lambda"
    ]
}

resource "aws_api_gateway_integration_response" "app_api_gateway_integration_response" {
  rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
  resource_id = "${aws_api_gateway_resource.rest_api_resource.id}"
  http_method = "${aws_api_gateway_method.app_api_gateway_method.http_method}"
  status_code = 200
  response_parameters = {
    "method.response.header.Access-Control-Allow-Origin" = "'*'",
    "method.response.header.Access-Control-Allow-Headers" = "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Requested-With'",
    "method.response.header.Access-Control-Allow-Methods" = "'GET,OPTIONS,POST,PUT'"
  }
  depends_on = [
    "aws_api_gateway_integration.app_api_gateway_integration",
    "aws_api_gateway_method_response.app_cors_method_response_200",
  ]
}

resource "aws_api_gateway_deployment" "app_api_gateway_deployment" {
  rest_api_id = "${aws_api_gateway_rest_api.rest_api.id}"
  stage_name  = "app_stage"
  depends_on = [
    "aws_api_gateway_integration_response.app_api_gateway_integration_response",
    "aws_api_gateway_integration_response.opt"
    ]
}

Any help would be appreceated.

Upvotes: 15

Views: 24204

Answers (4)

ben rhouma moez
ben rhouma moez

Reputation: 848

You can use the terraform module to enable cors:

module "api-gateway-enable-cors" {
source  = "squidfunk/api-gateway-enable-cors/aws"
version = "0.3.3"
api_id          = "<your_api_id>"
api_resource_id = "<your_api_resource_id>"
}

Source : api-gateway-enable-cors

Upvotes: 4

Amc_rtty
Amc_rtty

Reputation: 3813

It is useful to check the API GW logs in Cloudwatch to see what is the status code. In my scenario, I had two routes configured with aws_apigatewayv2_route, one for POST and one for OPTIONS, for the same route key. The OPTIONS request was failing with statuscode 429 which is too many requests. This status code is usually returned by throttling settings when its over allowed limit.

Turns out, since the OPTIONS request was not passing CORS, it was because I did not specify throttling in default_route_settings in terraform, so the default throttling for "Default route throttling" was defaulting to 0 for burst and rate. So my OPTIONS request was not passing CORS because it was getting hit by this throttling defaulting to zero. The hint in AWS console is clear:

This throttling limit applies to each route in the stage except those defined for specific routes.

Moral of the story - on your OPTIONS requests, really pay attention to the status code returned by API GW, and check the Cloudwatch logs for API GW. So: all worked, when I added this in terraform, under my resource "aws_apigatewayv2_stage" "lambda":

   default_route_settings {
      throttling_burst_limit  = 1000 
      throttling_rate_limit   = 5000
    }

Upvotes: -1

Vincent J
Vincent J

Reputation: 5808

For newer HTTP API (v2), you can use:

resource "aws_apigatewayv2_api" "lambda" {
  name          = "lambda_gw_api"
  protocol_type = "HTTP"
  cors_configuration {
    allow_origins = ["https://www.mywebsite.fr"]
    allow_methods = ["POST", "GET", "OPTIONS"]
    allow_headers = ["content-type"]
    max_age = 300
  }
}

PS: you may also need to check your OPTIONS route has an "integration" and does not return 401.

Upvotes: 13

L0cu2s
L0cu2s

Reputation: 565

Found out a simple solution. The problem was that on applying newer changes to existing API Gateway, was NOT re-deploying those gateways. So I had to redeploy them by myself manually and think of how to do that in terraform too.

Upvotes: 2

Related Questions