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-deployment-without-access-log-setting.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-deployment-without-access-log-setting

Provider: AWS

Platform: Terraform

Severity: Medium

Category: Observability

Learn More

Description

API Gateway deployments should have access_log_settings defined for each connected API Gateway Stage to ensure proper logging of API requests and responses. Without these settings, as shown below, critical API activity may go unlogged, making it difficult to track access patterns, detect malicious requests, or troubleshoot operational issues:

resource "aws_api_gateway_stage" "example" {
  ...
  access_log_settings {
    destination_arn = "arn:aws:logs:region:account-id:log-group:log-group-name"
    format          = "format"
  }
}

Leaving this misconfiguration unaddressed can hinder security monitoring and auditing, potentially exposing the environment to undetected abuse or data exfiltration.

Compliant Code Examples

resource "aws_api_gateway_deployment" "example5" {
  rest_api_id   = "some rest api id"
  stage_name = "some name"
  stage_description = "some description"

  tags {
    project = "ProjectName"
  }
}

resource "aws_api_gateway_stage" "example0" {
  deployment_id = aws_api_gateway_deployment.example5.id
  rest_api_id   = aws_api_gateway_rest_api.example.id
  stage_name    = "example"

  access_log_settings {
    destination_arn = "dest"
    format = "format"
  }
}
# Documented false positive: this stage IS correctly configured with a
# matching deployment_id and access_log_settings, but still gets flagged
# when evaluated purely from plan-JSON on a fresh-create plan (no prior
# state).
#
# query.rego's deployment_matches handles both the HCL interpolation form
# ("${aws_api_gateway_deployment.<name>.id}", matched via the literal
# reference string) and the plan-JSON resolved form, where deployment_id is
# the deployment's own resolved "id" value (a no-op/refresh plan against
# already-applied state). On a fresh-create plan, deployment_id is computed
# from the not-yet-created deployment's "id" and is entirely absent from
# planned_values pre-apply (resource_changes[].change.after_unknown.deployment_id
# == true), so neither matcher can succeed and a correctly-configured stage
# gets flagged as if its deployment_id were never set. Fixing this for real
# requires the plan-JSON parser (pkg/parser/json/tfplan.go) to also read
# resource_changes[].change.after_unknown / configuration.expressions[].references,
# which it currently does not.
resource "aws_api_gateway_deployment" "fp_example5" {
  rest_api_id        = "some rest api id"
  stage_name         = "some name"
  stage_description  = "some description"

  tags {
    project = "ProjectName"
  }
}

resource "aws_api_gateway_stage" "fp_example0" {
  deployment_id = aws_api_gateway_deployment.fp_example5.id
  rest_api_id   = aws_api_gateway_rest_api.fp_example.id
  stage_name    = "example"

  access_log_settings {
    destination_arn = "dest"
    format = "format"
  }
}

Non-Compliant Code Examples

resource "aws_api_gateway_deployment" "examplee" {
  rest_api_id   = "some rest api id"
  stage_name = "some name"
  tags {
    project = "ProjectName"
  }
}

resource "aws_api_gateway_stage" "example00" {
  deployment_id = aws_api_gateway_deployment.example.id
  rest_api_id   = aws_api_gateway_rest_api.example.id
  stage_name    = "example"
}
resource "aws_api_gateway_deployment" "example3" {
  rest_api_id   = "some rest api id"
  stage_name = "some name"
  tags {
    project = "ProjectName"
  }
}

resource "aws_api_gateway_stage" "example000" {
  deployment_id = aws_api_gateway_deployment.example3.id
  rest_api_id   = aws_api_gateway_rest_api.example.id
  stage_name    = "example"
}
resource "aws_api_gateway_deployment" "example4" {
  rest_api_id   = "some rest api id"
  stage_name = "some name"
  tags {
    project = "ProjectName"
  }
}

resource "aws_api_gateway_stage" "example0000" {
  deployment_id = aws_api_gateway_deployment.example4.id
  rest_api_id   = aws_api_gateway_rest_api.example.id
  stage_name    = "example"

  access_log_settings {
    destination_arn = "dest"
    format = "format"
  }
}