Skip to content

Terragrunt friendly module to create AWS API Gateway (V1) w\Optional WAF, many stages/api keys/usage plans using the OpenAPI 3.x spec. πŸ‡ΊπŸ‡¦

License

Notifications You must be signed in to change notification settings

adamwshero/terraform-aws-api-gateway

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

39 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

SWUbanner

Terraform



GitHub tag (latest by date)

terraform-aws-api-gateway (V1)

Terraform module to create Amazon API Gateway (v1) resources.

Amazon API Gateway is a fully managed service that makes it easy for developers to create, publish, maintain, monitor, and secure APIs at any scale. APIs act as the "front door" for applications to access data, business logic, or functionality from your backend services. Using API Gateway, you can create RESTful APIs and WebSocket APIs that enable real-time two-way communication applications. API Gateway supports containerized and serverless workloads, as well as web applications.

Build RESTful APIs optimized for serverless workloads and HTTP backends using HTTP APIs. HTTP APIs are the best choice for building APIs that only require API proxy functionality. If your APIs require API proxy functionality and API management features in a single solution, API Gateway also offers REST APIs.

Module Capabilities

  • Uses OpenAPI 3.x Specification
  • Deploy REST API to many stages
  • Supports creation of many API Keys
  • Supports creation of many stages
  • Supports stage canaries.
  • Supports creation of domain name and its mapping
  • Supports assigning many API Keys to many usage plans
  • Supports assigning many usage plans to many stages
  • Supports WAF integration for stages
  • Supports VPC Link for EDGE & REGIONAL type API's
  • Supports VPC Endpoints for PRIVATE type API's

Distributed Architecture Example Using PrivateLink (Preferred for public API's)

Distributed Architecture Example Without PrivateLink (Preferred for private API's)

  • COMING SOON...

Assumptions

  • Public API Scenario
    • You already have Network Load Balancer (NLB) with an IP type target group created if you are creating an API using the regional or edge deployment type.
    • You already have VPC Link setup and configured to point to your internal Network Load Balancer (NLB) if you are creating an API using the regional or edge deployment type.
    • You have already configured a VPC endpoint(s) that your NLB is using as targets if you are creating an API using the regional or edge deployment type. That VPC endpoint is connected to a VPC Endpoint Service in the same account or another account. (see architecture diagram)
  • All Scenarios
    • You already have created the CloudWatch Log Group for your access logging. This is different from execution log groups which is created automatically by API Gateway and is not manageable by Terraform.
    • You already have created the IAM role and policy for API Gateway execution. This role is needed so that it can create the CloudWatch Log Group and push log streams to it.

Usage

  • The definition of the API is managed using the OpenAPI 3.x standard and is contained in a openapi.yaml file. This file contains the paths, authorization integrations, VPC Link integrations, responses, authorizers, etc. The goal is to configure as much of the API in this .yaml as possible and use Terragrunt to manage the broader context of API Gateway. This approach also reduces greatly the amount of Terraform resources that need to be created/managed.

  • The resources abstracted from Terraform and into OpenAPI spec are listed below. Because of this, there are no inputs in Terraform for the REST API itself except for the name, description, body, and put_rest_api_mode. To provide inputs for these resources, you must do so in the openapi.yaml file using the AWS OpenAPI Extensions from the developer guide.

    aws_api_gateway_resource
    aws_api_gateway_method
    aws_api_gateway_method_response
    aws_api_gateway_method_settings
    aws_api_gateway_integration
    aws_api_gateway_integration_response
    aws_api_gateway_gateway_response
    aws_api_gateway_model
    
  • The API is deployed every time a change is made in Terraform. This is done to ensure that the deploy being used matches with the most recent deploy. Otherwise, this can be unexpectedly out-of-sync.

  • This module was initially built to suit a public REST API over Private Link and as such, depended on a Network Load Balancer (NLB) to function with VPC Link. You can choose to remove this dependency for private API's not using the regional or edge deployment type.


Special Notes

  • (Merge Mode)
    • When importing Open API Specifications with the body argument, by default the API Gateway REST API will be replaced with the Open API Specification thus removing any existing methods, resources, integrations, or endpoints. Endpoint mutations are asynchronous operations, and race conditions with DNS are possible. To overcome this limitation, use the put_rest_api_mode attribute and set it to merge.
    • Using put_rest_api_mode = merge when importing the OpenAPI Specification, the AWS control plane WILL NOT delete all existing literal properties that are not explicitly set in the OpenAPI definition. Impacted API Gateway properties: ApiKeySourceType, BinaryMediaTypes, Description, EndpointConfiguration, MinimumCompressionSize, Name, Policy).
    • When using put_rest_api_mode = merge, and you rename/remove a resource in the body, that resource change WILL NOT be reflected in the console.
    • When using put_rest_api_mode = overwrite, the AWS APIGW console reflects all changes you make accurately. However, be aware of the warning issued by the provider about using overwrite mode. We have not experienced issues using it so far with this module and implementation but in tests, we have only toggled this mode when we are renaming resource paths or removing resources from the body altogether.
  • (PRIVATE Type API Endpoint)
    • When a REST API type of PRIVATE is needed, the VPC endpoints must be specified in the openapi.yaml definition. This is especially called out since it is not obvious as most modules allow setting this in the Terraform input block.
        x-amazon-apigateway-endpoint-configuration:
          vpcEndpointIds: [""]
          disableExecuteApiEndpoint: true
      
  • (Deployments History vs. Deployment being used)
    • We deploy every time using (timestamp()} in the aws_api_gateway_deployment resource. If we do not, sometimes the deployment history has new deployments but the actual deployment in-use by the stage might be an older one.
    • Currently there is almost always one deployment, which is the most recent one. When there are multiple deployments in history, only the most recent will be used by default.
    • Domain names supports only one ACM Certificate for all the domain names, so the certificate must be valid for all the chosen domain names.

Upcoming/Recent Improvements

  • Want aws_api_gateway_method_settings to allow us to apply different method settings by stage and by method instead of choosing between the full override */* or only a single method to manage (e.g. {resource_path}/{http_method}). Currently whatever the path is dictates all method settings for the stages that have been deployed. Method settings would be represented as a map just as we already do with api keys and usage plans.
  • Want a usage_plan to accept many API keys as a list(string). Currenly a usage plan has a 1:1 relationship with API keys. This should be expanded so that many API keys can be associated with a single usage plan in the event multiple external consumers have similar API needs. This will reduce the number of usage plans needed.
  • Want the ability to create/enable VPC Link in this module since we're already consuming the Network Load Balancer (NLB) outputs when we are using the regional or edge deployment type.

The More You Know

  • CloudWatch Alarms
    • For CloudWatch Cache Hit/Miss alarms to work, you must enable the cache cluster for the stage.
  • NLB Health Checks
    • Ensure you are using the same availability zones from your NLB all the way to the target ALB where your service is running. Otherwise, you will see NLB targets (which are VPC endpint IP's) that are in an unhealthy state.
  • NLB Target Groups
    • At the time of this writing, there is an open issue for NLB's specifically where you will only be able to have 1 target group for a listener in Terraform (e.g. 443). Because of this, we must deploy an NLB, & VPCLink for each API instead of having 1 NLB and many target groups for each API. See this issue opened for the AWS CDK (is not a CDK issue alone)
  • VPCLink + Private Link
    • In the case where we are using private link + VPC Link to connect a REST API to a target service, it is important to know that the integration URI needed for the API integration request needs to match the domain/cert that is being used at the ALB level closest to the target service. Do not point the integration URI to the network load balancer. If the ALB listener for the application has a cert attached for *.nonprod.mycompany.com, the integration URI for the API needs to also use that domain (e.g. my-app.nonprod.mycompany.com) to avoid a cert mismatch error.

Terraform Basic Example

module "rest-api" {
  source = "git::git@github.com:adamwshero/terraform-aws-api-gateway.git//.?ref=1.0.7"


inputs = {
  api_name          = "my-app-dev"
  description       = "Development API for the My App service."
  endpoint_type     = ["REGIONAL"]
  put_rest_api_mode = "merge"   // Toggle to `overwrite` only when renaming a resource path or removing a resource from the openapi definition.

  // API Definition & Vars
  openapi_definition = templatefile("${get_terragrunt_dir()}/openapi.yaml",
    {
      endpoint_uri             = "https://my-app.nonprod.company.com}/my_app_path"
      authorizer_invoke_arn    = "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:111111111111:function:my-app-dev/invocation"
      authorizer_execution_arn = "arn:aws:iam::111111111111:role/my-app-dev"
    }
  )

  // Stage Settings
  stage_names           = ["dev"]
  stage_description     = "Development stage for My App API"
  log_group_name        = "/aws/apigateway/access/my_app/dev"
  access_log_format     = templatefile("${get_terragrunt_dir()}/log_format.json.tpl", {})

  // Method Settings
  method_path = "*/*"

  // Security
  enable_waf = false

  // Execution Role
  cloudwatch_role_arn    = "arn:aws:logs:us-east-1:111111111111:log-group:/aws/lambda/my-app-dev"
  cloudwatch_policy_name = "my-app-dev"

  // Usage Plans & API Keys
  create_usage_plan = false
  enable_api_key    = false

  tags = local.tags
}

Terragrunt Basic Example

terraform {
  source = "git::git@github.com:adamwshero/terraform-aws-api-gateway.git//.?ref=1.0.7"
}

inputs = {
  api_name          = "my-app-dev"
  description       = "Development API for the My App service."
  endpoint_type     = ["REGIONAL"]
  put_rest_api_mode = "merge"   // Toggle to `overwrite` only when renaming a resource path or removing a resource from the openapi definition.

  // API Definition & Vars
  openapi_definition = templatefile("${get_terragrunt_dir()}/openapi.yaml",
    {
      endpoint_uri             = "https://my-app.nonprod.company.com}/my_app_path"
      authorizer_invoke_arn    = "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:111111111111:function:my-app-dev/invocation"
      authorizer_execution_arn = "arn:aws:iam::111111111111:role/my-app-dev"
    }
  )

  // Stage Settings
  stage_names       = ["dev"]
  stage_description = "Development stage for My App API"
  log_group_name    = "/aws/apigateway/access/my_app/dev"
  access_log_format = templatefile("${get_terragrunt_dir()}/log_format.json.tpl", {})

  // Method Settings
  method_path = "*/*"

  // Security
  enable_waf = false

  // Execution Role
  cloudwatch_role_arn    = "arn:aws:logs:us-east-1:111111111111:log-group:/aws/lambda/my-app-dev"
  cloudwatch_policy_name = "my-app-dev"

  // Usage Plans & API Keys
  create_usage_plan = false
  enable_api_key    = false

  tags = local.tags
}

Supporting Articles & Documentation

Requirements

Name Version
terraform >= 0.13.1
aws >= 4.30.0
terragrunt >= 0.28.0

Providers

Name Version
aws >= 4.30.0

Modules

No modules.

Resources

Name Type
aws_api_gateway_account.this resource
aws_api_gateway_api_key.this resource
aws_api_gateway_base_path_mapping.edge_acm resource
aws_api_gateway_base_path_mapping.edge_iam resource
aws_api_gateway_base_path_mapping.regional_acm resource
aws_api_gateway_base_path_mapping.regional_iam resource
aws_api_gateway_deployment.this resource
aws_api_gateway_domain_name.edge_acm resource
aws_api_gateway_domain_name.edge_iam resource
aws_api_gateway_domain_name.regional_acm resource
aws_api_gateway_domain_name.regional_iam resource
aws_api_gateway_method_settings.this resource
aws_api_gateway_rest_api.this resource
aws_api_gateway_rest_api_policy.this resource
aws_api_gateway_stage.this resource
aws_api_gateway_usage_plan.this resource
aws_api_gateway_usage_plan_key.this resource
aws_cloudwatch_log_group.this resource
aws_wafv2_web_acl_association.this resource

Inputs

Name Description Type Default Required
access_log_format (Required) Formatting and values recorded in the logs. For more information on configuring the log format rules visit the AWS documentation string n/a yes
api_key_description (Optional) API key description. Defaults to Managed by Terraform. string null no
api_key_name (Required) Name of the API key. string null no
api_keys Map of objects that define the usage plan to be created.
list(
object({
name = string
key_type = string
usage_plan = string
})
)
[
{
"key_type": null,
"name": null,
"usage_plan": null
}
]
no
api_name (Required) Name of the REST API. If importing an OpenAPI specification via the body argument, this corresponds to the info.title field. If the argument value is different than the OpenAPI value, the argument value will override the OpenAPI value. string null no
burst_limit (Optional) - The API request burst limit, the maximum rate limit over a time ranging from one to a few seconds, depending upon whether the underlying token bucket is at its full capacity. number 5 no
cache_cluster_enabled (Optional) Whether a cache cluster is enabled for the stage. bool true no
cache_cluster_size (Optional) Size of the cache cluster for the stage, if enabled. Allowed values include 0.5, 1.6, 6.1, 13.5, 28.4, 58.2, 118 and 237. number 0.5 no
cache_data_encrypted (Optional) Whether the cached responses are encrypted. bool false no
cache_ttl_in_seconds (Optional) Time to live (TTL), in seconds, for cached responses. The higher the TTL, the longer the response will be cached. number 300 no
caching_enabled (Optional) Whether responses should be cached and returned for requests. A cache cluster must be enabled on the stage for responses to be cached. bool true no
certificate_type This resource currently only supports managing a single value. Valid values: ACM or IAM. If unspecified, defaults to acm string "ACM" no
client_certificate_id (Optional) Identifier of a client certificate for the stage. string null no
client_name client name to use this api. string null no
cloudwatch_role_arn (Required) for the api_gateway_account resource. string n/a yes
create_api_domain_name Whether to create API domain name resource. bool false no
create_rest_api_policy Enables creation of the resource policy for a given API. bool true no
create_usage_plan Allows creation of a usage plan. (Requires var.enable_api_key = true) bool false no
data_trace_enabled (Optional) Whether data trace logging is enabled for this method, which effects the log entries pushed to Amazon CloudWatch Logs. bool false no
description (Optional) Description of the REST API. If importing an OpenAPI specification via the body argument, this corresponds to the info.description field. If the argument value is provided and is different than the OpenAPI value, the argument value will override the OpenAPI value. string null no
documentation_version (Optional) Version of the associated API documentation. string null no
domain_certificate_arn The ARN of an AWS-managed certificate that will be used by the endpoint for the domain name. string null no
domain_certificate_name Unique name to use when registering this certificate as an IAM server certificate. Conflicts with certificate_arn, regional_certificate_arn, and regional_certificate_name. Required if certificate_arn is not set. string null no
domain_names Fully-qualified domain name to register. The domain names to use for API gateway it will use the index of stage_names to select the domain name. list(string) null no
enable_api_key (Optional) Whether the API key can be used by callers. Defaults to false. bool false no
enable_canary (Optional) Whether to use the values supplied for the canary and stage_variable_overrides or not. bool false no
enable_waf Enables associating existing WAF ACL to all stages. bool false no
endpoint_type (Required) List of endpoint types. This resource currently only supports managing a single value. Valid values: EDGE, REGIONAL or PRIVATE. If unspecified, defaults to EDGE. Must be declared as REGIONAL in non-Commercial partitions. If set to PRIVATE recommend to set put_rest_api_mode = merge to not cause the endpoints and associated Route53 records to be deleted. Refer to the documentation for more information on the difference between edge-optimized and regional APIs. string "EDGE" no
iam_certificate_body Certificate issued for the domain name being registered, in PEM format. Only valid for EDGE endpoint configuration type. Conflicts with certificate_arn, regional_certificate_arn, and regional_certificate_name string null no
iam_certificate_chain Certificate for the CA that issued the certificate, along with any intermediate CA certificates required to create an unbroken chain to a certificate trusted by the intended API clients. Only valid for EDGE endpoint configuration type. Conflicts with certificate_arn, regional_certificate_arn, and regional_certificate_name. string null no
iam_certificate_private_key Private key associated with the domain certificate given in certificate_body. Only valid for EDGE endpoint configuration type. Conflicts with certificate_arn, regional_certificate_arn, and regional_certificate_name. string null no
limit (Optional) - Maximum number of requests that can be made in a given time period. number 20 no
log_group_kms_key (Optional) The ARN of the KMS Key to use when encrypting log data. Please note, after the AWS KMS CMK is disassociated from the log group, AWS CloudWatch Logs stops encrypting newly ingested data for the log group. All previously ingested data remains encrypted, and AWS CloudWatch Logs requires permissions for the CMK whenever the encrypted data is requested. string null no
log_group_name (Optional, Forces new resource) The name of the log group. If omitted, Terraform will assign a random, unique name. string null no
log_group_retention_in_days (Optional) Specifies the number of days you want to retain log events in the specified log group. Possible values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653, and 0. If you select 0, the events in the log group are always retained and never expire. string 7 no
logging_level (Optional) Logging level for this method, which effects the log entries pushed to Amazon CloudWatch Logs. The available levels are OFF, ERROR, and INFO. string "INFO" no
method_path (Required) Method path defined as {resource_path}/{http_method} for an individual method override, or */* for overriding all methods in the stage. Ensure to trim any leading forward slashes in the path. string n/a yes
metrics_enabled (Optional) Whether Amazon CloudWatch metrics are enabled for this method. string true no
mutual_tls_authentication An Amazon S3 URL that specifies the truststore for mutual TLS authentication as well as version, keyed at uri and version map(string) {} no
offset (Optional) - Number of requests subtracted from the given limit in the initial time period. number 2 no
openapi_definition (Required) YAML formatted definition file using OpenAPI 3.x specification. This definition contains all API configuration inputs. Any inputs used in Terraform will override inputs in the definition. string n/a yes
percent_traffic (Optional) Percent 0.0 - 100.0 of traffic to divert to the canary deployment. number null no
period (Optional) - Time period in which the limit applies. Valid values are DAY, WEEK or MONTH. string "WEEK" no
put_rest_api_mode (Optional) Mode of the PutRestApi operation when importing an OpenAPI specification via the body argument (create or update operation). Valid values are merge and overwrite. If unspecificed, defaults to overwrite (for backwards compatibility). This corresponds to the x-amazon-apigateway-put-integration-method extension. If the argument value is provided and is different than the OpenAPI value, the argument value will override the OpenAPI value. string "overwrite" no
rate_limit (Optional) - The API request steady-state rate limit. number 10 no
require_authorization_for_cache_control (Optional) Whether authorization is required for a cache invalidation request. bool true no
rest_api_policy (Required) JSON formatted policy document that controls access to the API Gateway. For more information about building AWS IAM policy documents with Terraform, see the AWS IAM Policy Document Guide string "" no
stage_description (Optional) Description of the stage. string null no
stage_names (Required) Name of the stage(s). list(string) null no
stage_variable_overrides (Optional) Map of overridden stage variables (including new variables) for the canary deployment. any {} no
stage_variables (Optional) Map that defines the stage variables. any {} no
throttling_burst_limit (Optional) Throttling burst limit. Default: -1 (throttling disabled). number -1 no
throttling_rate_limit (Optional) Throttling rate limit. Default: -1 (throttling disabled). number -1 no
unauthorized_cache_control_header_strategy (Optional) How to handle unauthorized requests for cache invalidation. The available values are FAIL_WITH_403, SUCCEED_WITH_RESPONSE_HEADER, SUCCEED_WITHOUT_RESPONSE_HEADER. string "SUCCEED_WITH_RESPONSE_HEADER" no
usage_plans Map of objects that define the usage plan to be created.
list(
object({
name = string
description = string
burst_limit = number
rate_limit = number
quota_limit = number
quota_offset = number
quota_period = string
stages = list(string)
})
)
[
{
"burst_limit": null,
"description": null,
"name": null,
"quota_limit": null,
"quota_offset": null,
"quota_period": null,
"rate_limit": null,
"stages": [
null
]
}
]
no
use_stage_cache (Optional) Whether the canary deployment uses the stage cache. Defaults to false. bool false no
waf_acl (Required) The ID of the WAF Regional WebACL to create an association. string null no
xray_tracing_enabled (Optional) Whether active tracing with X-ray is enabled. Defaults to false. bool false no

Outputs

Name Description
api_gateway_rest_api_arn Arn of the REST API.
api_gateway_rest_api_execution_arn Execution Arn of the REST API.
api_gateway_rest_api_id Id of the REST API.
api_gateway_rest_api_name Name of the REST API.
api_gateway_rest_api_stage_arn Arn of the deployed stage(s).
api_gateway_rest_api_stage_execution_arn Execution arn of the deployed stage(s).
api_gateway_rest_api_stage_id Id of the deployed stage(s).
api_gateway_rest_api_stage_invoke_url Invoke URL of the deployed stage(s).
api_gateway_rest_api_stage_web_acl WAF Access Control List for the stage(s)

About

Terragrunt friendly module to create AWS API Gateway (V1) w\Optional WAF, many stages/api keys/usage plans using the OpenAPI 3.x spec. πŸ‡ΊπŸ‡¦

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Languages