Skip to content

AppSync GraphQL APIs should have field-level logging enabled

Field-level logging in AppSync captures resolver execution details, including request mappings, response mappings, and errors at the individual field level. Without it, you lose visibility into which resolvers are failing, how long they take, and what data they return. This makes debugging production issues slow and leaves you blind to data exfiltration patterns or injection attempts against your GraphQL API.

AppSync APIs often sit in front of multiple data sources (DynamoDB, Lambda, RDS) and act as an aggregation layer. A single GraphQL query can fan out to dozens of resolvers. Logging at the API level alone won't tell you which resolver misbehaved. Field-level logs sent to CloudWatch give you the granularity needed for both operational troubleshooting and security investigations.

Retrofit consideration

Setting field_log_level to ALL on a high-traffic API can generate substantial CloudWatch Logs volume. Estimate log costs before switching from NONE to ALL on production APIs; ERROR satisfies the control at a fraction of the ingestion cost.

Implementation

Choose the approach that matches how you manage Terraform.

Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.

module "appsync" {
  source  = "pcidss.compliance.tf/terraform-aws-modules/appsync/aws"
  version = ">=3.0.0"

  authentication_type = "AWS_IAM"
  log_field_log_level = "ERROR"
  logging_enabled     = true
  name                = "pofix-abc123"
  schema              = "type Query { hello: String }"
}

module "appsync" {
  source  = "nistcsf.compliance.tf/terraform-aws-modules/appsync/aws"
  version = ">=3.0.0"

  authentication_type = "AWS_IAM"
  log_field_log_level = "ERROR"
  logging_enabled     = true
  name                = "pofix-abc123"
  schema              = "type Query { hello: String }"
}

module "appsync" {
  source  = "acscessentialeight.compliance.tf/terraform-aws-modules/appsync/aws"
  version = ">=3.0.0"

  authentication_type = "AWS_IAM"
  log_field_log_level = "ERROR"
  logging_enabled     = true
  name                = "pofix-abc123"
  schema              = "type Query { hello: String }"
}

If you use terraform-aws-modules/appsync/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "appsync" {
  source  = "terraform-aws-modules/appsync/aws"
  version = ">=3.0.0"

  authentication_type = "AWS_IAM"
  log_field_log_level = "ERROR"
  logging_enabled     = true
  name                = "pofix-abc123"
  schema              = "type Query { hello: String }"
}

Use AWS provider resources directly. See docs for the resources involved: aws_appsync_graphql_api.

resource "aws_appsync_graphql_api" "this" {
  authentication_type = "AWS_IAM"

  log_config {
    cloudwatch_logs_role_arn = "arn:aws:iam::123456789012:role/example-role"
    field_log_level          = "ERROR"
  }

  name = "pofix-abc123"
}

What this control checks

The aws_appsync_graphql_api resource passes when its log_config block sets field_log_level to "ERROR" or "ALL". It fails when field_log_level is "NONE" or the log_config block is absent entirely. log_config also requires a cloudwatch_logs_role_arn pointing to an IAM role with permissions to write to CloudWatch Logs: a trust policy allowing appsync.amazonaws.com to assume the role, and an attached policy granting logs:CreateLogGroup, logs:CreateLogStream, and logs:PutLogEvents. Without that role, the API cannot deliver logs even if the log level is set correctly.

Common pitfalls

  • Missing or underprivileged CloudWatch IAM role

    AppSync silently fails to write logs when cloudwatch_logs_role_arn is missing or the referenced role lacks write permissions, even though the control may still report a pass based on field_log_level alone. Create an IAM role that trusts appsync.amazonaws.com and grants logs:CreateLogGroup, logs:CreateLogStream, and logs:PutLogEvents. Without it, your configuration passes the check but produces no logs.

  • Confusing field_log_level with request-level logging

    The log_config block has both field_log_level and exclude_verbose_content. Flipping exclude_verbose_content to true reduces log detail but has no effect on the control result. What the policy evaluates is field_log_level; setting cloudwatch_logs_role_arn without also setting field_log_level to a non-NONE value still fails.

  • High log volume with ALL on busy APIs

    Use "ERROR" rather than "ALL" on high-throughput APIs. "ALL" logs every resolver invocation, including successful ones, and on an API processing thousands of queries per second that adds up to gigabytes of CloudWatch Logs per day. "ERROR" satisfies the control at a fraction of the cost; the tradeoff is losing trace data for requests that succeed.

  • Terraform apply fails when IAM role and API are created together

    If the IAM role and the AppSync API are created in the same terraform apply, the apply can fail because the role ARN isn't resolved when the log_config block is evaluated. Reference the role's arn attribute directly in cloudwatch_logs_role_arn rather than constructing the ARN string manually; that reference establishes the dependency implicitly. Add depends_on only if the direct attribute reference isn't sufficient.

Audit evidence

Config rule evaluation results showing each AppSync API as compliant, or Security Hub findings with PASSED status for this control, are the primary audit artifacts. Supporting those, the associated CloudWatch Logs log group should contain recent field-level log entries, confirming that logging is actively functioning and not just configured.

The AppSync API settings page in the AWS Console shows the current logging configuration, including the field resolver log level and the associated IAM role ARN. CLI output from aws appsync get-graphql-api --api-id <id> showing fieldLogLevel as ERROR or ALL in the logConfig object provides direct proof.

Framework-specific interpretation

PCI DSS v4.0: Requirement 10 calls for audit trails on all access to system components and cardholder data. For AppSync APIs that process or proxy cardholder data, resolver-level logging is what gives you that trail at the granularity examiners expect.

NIST Cybersecurity Framework v2.0: DE.CM and DE.AE both require continuous monitoring of system components for anomalies. Field-level resolver logs give you the error patterns and request details needed to actually do that for anything going through your GraphQL API.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: appsync_graphql_api_field_level_logging_enabled

  • AWS Config Managed Rule: APPSYNC_LOGGING_ENABLED

  • Checkov Checks: CKV_AWS_193, CKV_AWS_194

  • Powerpipe Control: aws_compliance.control.appsync_graphql_api_field_level_logging_enabled

  • Prowler Check: appsync_field_level_logging_enabled

  • AWS Security Hub Control: AppSync.2

Last reviewed: 2026-03-08