For AI agents: A markdown version of this page is available at https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/terraform-aws-api-gateway-stage-without-api-gateway-usage-plan-associated.md. A documentation index is available at /llms.txt.
This product is not supported for your selected Datadog site. ().

Metadata

Id: terraform-aws-api-gateway-stage-without-api-gateway-usage-plan-associated

Provider: AWS

Platform: Terraform

Severity: Low

Category: Resource Management

Learn More

Description

API Gateway stages should always be associated with an API Gateway UsagePlan, which enforces throttling and quota limits for clients accessing your APIs. Without a defined aws_api_gateway_usage_plan resource and its association via the api_stages block, as shown below, the API stage can be accessed without usage restrictions, leading to potential misuse, abuse, or denial of service due to unlimited traffic.

resource "aws_api_gateway_stage" "example" {
  deployment_id = "some deployment id"
  rest_api_id   = "some rest api id"
  stage_name    = "development"
}

Configuring a UsagePlan, such as the one in the example below, helps mitigate these risks by controlling consumption through quotas and throttling, protecting backend resources and maintaining predictable API performance.

resource "aws_api_gateway_usage_plan" "example" {
  name        = "my-usage-plan"
  description = "usage plan description"
  api_stages {
    api_id = "some rest api id"
    stage  = "development"
  }
}

Compliant Code Examples

resource "aws_api_gateway_stage" "negative1" {
  deployment_id = "some deployment id"
  rest_api_id   = "rest_api_1"
  stage_name    = "development"
}

resource "aws_api_gateway_usage_plan" "negative2" {
  name         = "my-usage-plan"
  description  = "my description"
  product_code = "MYCODE"

  api_stages {
    api_id = "rest_api_1"
    stage  = "development"
  }
}
resource "aws_api_gateway_rest_api" "api_a" {
  name = "api-a"
}

resource "aws_api_gateway_deployment" "api_a" {
  rest_api_id = aws_api_gateway_rest_api.api_a.id
}

resource "aws_api_gateway_stage" "api_a" {
  stage_name    = "prod"
  rest_api_id   = aws_api_gateway_rest_api.api_a.id
  deployment_id = aws_api_gateway_deployment.api_a.id
}

resource "aws_api_gateway_usage_plan" "api_a" {
  name = "api-a-usage-plan"

  api_stages {
    api_id = aws_api_gateway_rest_api.api_a.id
    stage  = "prod"
  }
}

resource "aws_api_gateway_rest_api" "api_b" {
  name = "api-b"
}

resource "aws_api_gateway_deployment" "api_b" {
  rest_api_id = aws_api_gateway_rest_api.api_b.id
}

resource "aws_api_gateway_stage" "api_b" {
  stage_name    = "prod"
  rest_api_id   = aws_api_gateway_rest_api.api_b.id
  deployment_id = aws_api_gateway_deployment.api_b.id
}

resource "aws_api_gateway_usage_plan" "api_b" {
  name = "api-b-usage-plan"

  api_stages {
    api_id = aws_api_gateway_rest_api.api_b.id
    stage  = "prod"
  }
}

Non-Compliant Code Examples

resource "aws_api_gateway_stage" "positive1" {
  rest_api_id   = "some deployment id"
  deployment_id = "some rest api id"
  stage_name = "some name"
  tags {
    project = "ProjectName"
  }
}

resource "aws_api_gateway_stage" "positive2" {
  deployment_id = "some deployment id"
  rest_api_id   = "some rest api id"
  stage_name    = "development"
}

resource "aws_api_gateway_usage_plan" "positive3" {
  name         = "my-usage-plan"
  description  = "my description"
  product_code = "MYCODE"

  api_stages {
    api_id = "another id"
    stage  = "development"
  }
}
resource "aws_api_gateway_rest_api" "api" {
  name = "example"
}

resource "aws_api_gateway_deployment" "example" {
  rest_api_id = aws_api_gateway_rest_api.api.id
}

resource "aws_api_gateway_stage" "example" {
  stage_name    = "positive2-stage"
  rest_api_id   = aws_api_gateway_rest_api.api.id
  deployment_id = aws_api_gateway_deployment.example.id
}
resource "aws_api_gateway_rest_api" "protected_api" {
  name = "protected-api"
}

resource "aws_api_gateway_deployment" "protected" {
  rest_api_id = aws_api_gateway_rest_api.protected_api.id
}

resource "aws_api_gateway_stage" "protected" {
  stage_name    = "prod"
  rest_api_id   = aws_api_gateway_rest_api.protected_api.id
  deployment_id = aws_api_gateway_deployment.protected.id
}

resource "aws_api_gateway_usage_plan" "protected" {
  name = "protected-usage-plan"

  api_stages {
    api_id = aws_api_gateway_rest_api.protected_api.id
    stage  = "prod"
  }
}

resource "aws_api_gateway_rest_api" "unprotected_api" {
  name = "unprotected-api"
}

resource "aws_api_gateway_deployment" "unprotected" {
  rest_api_id = aws_api_gateway_rest_api.unprotected_api.id
}

resource "aws_api_gateway_stage" "unprotected" {
  stage_name    = "prod"
  rest_api_id   = aws_api_gateway_rest_api.unprotected_api.id
  deployment_id = aws_api_gateway_deployment.unprotected.id
}