Skip to content

Open feat(terraform): Add TF parity updates for v0.4.0#48

Merged
kaleko merged 8 commits intoawslabs:mainfrom
kunanit:tf-0.3.2
Mar 11, 2026
Merged

Open feat(terraform): Add TF parity updates for v0.4.0#48
kaleko merged 8 commits intoawslabs:mainfrom
kunanit:tf-0.3.2

Conversation

@kunanit
Copy link
Contributor

@kunanit kunanit commented Mar 5, 2026

Summary

Sync Terraform infrastructure with CDK state in preparation for v0.4.0 release, which includes OAuth2 Credential Provider changes (PR #38) and VPC deployment mode (MR !41).

Version

  • Target FAST Release: v0.4.0
  • Terraform Version: 0.4.0-tf.0 (prerelease)

Changes (Infra parity)

OAuth2 Credential Provider Integration

Deploys OAuth2 Credential Provider infrastructure to enable Runtime → Gateway authentication using Token Vault pattern.

CDK change references

Components

  • Lambda function (oauth2_provider.tf) that manages OAuth2 Credential Provider lifecycle simulating CloudFormation Custom Resource pattern
  • IAM role and policy with permissions:
    • CloudWatch Logs access
    • Read machine client secret from Secrets Manager
    • OAuth2 Credential Provider operations (Create/Get/Update/Delete)
    • Token Vault operations (Create/Get/Delete)
    • Token Vault secret management in Secrets Manager
  • null_resource for Custom Resource invocation pattern (Create on apply, Delete on destroy)
  • Updated Runtime IAM policy (runtime.tf):
    • Removed direct machine client secret access
    • Added SecretsManagerOAuth2Access for Token Vault secrets
    • Added OAuth2CredentialProviderAccess (GetOauth2CredentialProvider, GetResourceOauth2Token)
  • Updated Runtime configuration (runtime.tf):
    • Added GATEWAY_CREDENTIAL_PROVIDER_NAME environment variable
    • Changed allowed_audienceallowed_clients (parameter name change)
    • Added dependency on OAuth2 provider registration

Variables/Outputs:

  • No new module-level inputs/outputs

Implementation Notes:

The CDK implementation uses CloudFormation's Custom Resource pattern with the Provider construct, which automatically handles the full CRUD lifecycle (Create/Update/Delete). In Terraform, we replicate this functionality using a null_resource with local-exec provisioner that invokes the same Lambda function directly. The Lambda code is shared from infra-cdk/lambdas/oauth2-provider/index.py with a transport-agnostic design that supports both CloudFormation's callback protocol and direct invocation.

The Terraform implementation only uses Create and Delete events - we don't need explicit Update logic because changes to the trigger values (client ID, discovery URL, etc.) automatically force recreation of the null_resource, which re-invokes the Lambda with updated configuration. The Lambda itself has full CRUD support to maintain compatibility with CDK's CloudFormation lifecycle.

VPC Deployment Mode

Aligns Terraform VPC deployment support with CDK's VPC deployment mode (MR !41), ensuring consistent terminology and configuration.

CDK change references

  • MR: !41 (feat/vpc_deployment)
  • Commit: 7bc9038 "added support for private vpc deployment"

Components

  • Renamed network_mode value from "PRIVATE" to "VPC" across all files for CDK parity
  • Added VPC-specific variables: backend_vpc_id, backend_vpc_subnet_ids, backend_vpc_security_group_ids
  • Added VPC configuration validation (locals.tf):
    • check block validates that VPC fields are provided when backend_network_mode = "VPC"
    • Mirrors CDK ConfigManager validation logic
  • Default security group auto-created when backend_vpc_security_group_ids is omitted (HTTPS self-referencing ingress + all-traffic egress)
  • Updated deployment documentation with VPC requirements:
    • Required VPC endpoints table (Bedrock, SSM, Secrets Manager, CloudWatch, ECR, S3, DynamoDB)
    • Subnet requirements
    • NAT Gateway requirement for Cognito OAuth2 token endpoint
    • Security group configuration guidance

Variables/Outputs:

  • Added 3 new VPC variables (backend_vpc_id, backend_vpc_subnet_ids, backend_vpc_security_group_ids)
  • backend_network_mode accepts "PUBLIC" (default) or "VPC"

Implementation Notes:

The Terraform VPC mode uses the network_mode_config block within the aws_bedrockagentcore_agent_runtime resource's network_configuration, which maps directly to the AWS API. Unlike CDK, which can create a default security group when none is provided, the Terraform provider requires explicit security_groups in the network_mode_config block. This difference is documented in the variable descriptions and README.

Variable Interface Cleanup (CDK Parity)

Streamlines the Terraform variable interface to match the CDK config.yaml surface. Variables that don't exist in CDK's configuration are either removed or converted to module-internal locals.

Removed variables (7):

Variable Action Rationale
aws_region Removed Provider uses standard AWS SDK resolution chain (env vars, config, profile) -- same as CDK
backend_agent_name Converted to local (StrandsAgent) CDK has as CfnParameter default, not in config.yaml
backend_memory_event_expiry_days Converted to local (30) Hardcoded in CDK backend-stack.ts
callback_urls Converted to local Hardcoded in CDK cognito-stack.ts
password_minimum_length Converted to local (8) Hardcoded in CDK cognito-stack.ts
environment Removed Only used for tagging; users add via provider default_tags
tags Removed Redundant with provider default_tags

Components

  • Removed ~40 explicit tags = var.tags lines from resources (inherited via provider default_tags)
  • Simplified local.common_tags to fixed tags (Project, ManagedBy, Repository)
  • Added locals in backend module (locals.tf): agent_name, memory_event_expiry_days
  • Added locals in cognito module (main.tf): default_callback_urls, password_minimum_length
  • Updated build-and-push-image.sh: region resolved from AWS SDK chain instead of tfvars
  • Updated terraform.tfvars.example: removed deleted variables, added guidance for region and tags

Remaining user-facing variables (8):
stack_name_base, admin_user_email, backend_pattern, backend_deployment_type, backend_network_mode, backend_vpc_id, backend_vpc_subnet_ids, backend_vpc_security_group_ids

Output Pruning (CDK Parity)

Removed 9 TF-only outputs that have no CDK CfnOutput counterpart and are not consumed by any deployment or test scripts.

Removed outputs:

Output Rationale
cognito_user_pool_arn ARN variant; CDK only exports ID
cognito_hosted_ui_url Convenience URL; not in CDK
memory_id CDK only exports memory_arn
ecr_repository_url build-and-push-image.sh constructs ECR URL independently
agent_code_bucket Zip-mode only; no script reads it
deployment_type Informational; no script reads it
feedback_api_id API Gateway ID; no script reads it
feedback_table_name DynamoDB table name; no script reads it
feedback_lambda_arn Lambda ARN; no script reads it

Also removed cognito_login field from deployment_summary (referenced pruned hosted_ui_url).

Remaining outputs (19): All match a CDK CfnOutput export or are consumed by deployment/test scripts (deploy-frontend.sh/py, test-agent.py, test-oauth2-auth.py).

Other Changes

AWS Provider Version Constraint

During testing, discovered AWS Terraform provider regression introduced in v6.35.0 that breaks Gateway Target resources with nested input_schema.property blocks. Provider cannot deserialize schema during state refresh. This is fixed in v6.35.1 .

Components

  • Regenerated .terraform.lock.hcl to v6.35.1 (latest working version)
  • Added troubleshooting section to README.md documenting:
    • Error message and cause
    • Provider version compatibility guidance

Implementation Notes:

6.22 is the minimum AWS provider version that supports the code_configuration source type block in the AgentCore Runtime resource, which is used for the "zip" deployment type, which determined the minimum version constraint.

Testing

  • Deployed and tested with OAuth2 authentication test script (test-oauth2-auth.py)
  • Agent successfully invoked Gateway tools with OAuth2 authentication
  • Verified token retrieval in AgentCore Identity logs (GetResourceOauth2Token: Success, TokenFetched: true)
  • Frontend deployed and tested end-to-end user authentication flow
  • Gateway authorizer configuration verified (machine client only)
  • Terraform destroy cleanup tested (OAuth2 provider deletion via Lambda destroy provisioner)
  • Tested provider versions v6.22.0 through v6.35.1 to identify working range
  • terraform validate - [PASS]
  • terraform fmt -check -recursive - [PASS]
  • Deployed and tested with private VPC with SG
  • Deployed and tested with private VPC without SG

Documentation

Terraform Deployment Guide (docs/TERRAFORM_DEPLOYMENT.md)

Created a dedicated Terraform deployment guide in docs/ that mirrors the structure of the CDK docs/DEPLOYMENT.md:

  • Prerequisites, configuration, step-by-step deployment, post-deployment, cleanup, troubleshooting
  • Links back to existing deployment doc for details on VPC deployment prerequisites (endpoints, subnets, NAT gateway, security groups) and Docker cross-platform build setup, to avoid redundancy.
  • Updated existing deployment doc to link to the new Terraform guide

infra-terraform/README.md Refactored

Slimmed down content to focus on design:

  • Kept: Architecture, quick start, configuration reference, CDK mapping, module structure, deployment order, outputs, state management, resource reference
  • Moved to terraform deployment guide: Detailed deployment steps, VPC configuration, post-deployment, troubleshooting, cleanup

Other Documentation

  • Updated README.md with OAuth2 provider architecture and module structure
  • Added inline comments in oauth2_provider.tf and runtime.tf explaining implementation approach and IAM requirements
  • Updated terraform.tfvars.example with VPC mode terminology and documentation references

@kunanit kunanit marked this pull request as draft March 5, 2026 17:45
@kunanit
Copy link
Contributor Author

kunanit commented Mar 5, 2026

Planning to incorporate private VPC support before merging

@kaleko
Copy link
Contributor

kaleko commented Mar 5, 2026

Looks good for merging. BTW the next release might be 0.4.0 instead of 0.3.2 because there are some fairly major additions

@kunanit
Copy link
Contributor Author

kunanit commented Mar 5, 2026

Updated with VPC mode and some additional improvements

@kunanit kunanit marked this pull request as ready for review March 6, 2026 01:33
@kaleko kaleko requested a review from a team March 10, 2026 14:06
@github-actions github-actions bot added documentation Improvements or additions to documentation infrastructure labels Mar 10, 2026
@github-actions
Copy link

github-actions bot commented Mar 10, 2026

Latest scan for commit: 0e7a210 | Updated: 2026-03-11 13:17:09 UTC

Security Scan Results

Scan Metadata

  • Project: ASH
  • Scan executed: 2026-03-11T13:16:54+00:00
  • ASH version: 3.2.2

Summary

Scanner Results

The table below shows findings by scanner, with status based on severity thresholds and dependencies:

Column Explanations:

Severity Levels (S/C/H/M/L/I):

  • Suppressed (S): Security findings that have been explicitly suppressed/ignored and don't affect the scanner's pass/fail status
  • Critical (C): The most severe security vulnerabilities requiring immediate remediation (e.g., SQL injection, remote code execution)
  • High (H): Serious security vulnerabilities that should be addressed promptly (e.g., authentication bypasses, privilege escalation)
  • Medium (M): Moderate security risks that should be addressed in normal development cycles (e.g., weak encryption, input validation issues)
  • Low (L): Minor security concerns with limited impact (e.g., information disclosure, weak recommendations)
  • Info (I): Informational findings for awareness with minimal security risk (e.g., code quality suggestions, best practice recommendations)

Other Columns:

  • Time: Duration taken by each scanner to complete its analysis
  • Action: Total number of actionable findings at or above the configured severity threshold that require attention

Scanner Results:

  • PASSED: Scanner found no security issues at or above the configured severity threshold - code is clean for this scanner
  • FAILED: Scanner found security vulnerabilities at or above the threshold that require attention and remediation
  • MISSING: Scanner could not run because required dependencies/tools are not installed or available
  • SKIPPED: Scanner was intentionally disabled or excluded from this scan
  • ERROR: Scanner encountered an execution error and could not complete successfully

Severity Thresholds (Thresh Column):

  • CRITICAL: Only Critical severity findings cause scanner to fail
  • HIGH: High and Critical severity findings cause scanner to fail
  • MEDIUM (MED): Medium, High, and Critical severity findings cause scanner to fail
  • LOW: Low, Medium, High, and Critical severity findings cause scanner to fail
  • ALL: Any finding of any severity level causes scanner to fail

Threshold Source: Values in parentheses indicate where the threshold is configured:

  • (g) = global: Set in the global_settings section of ASH configuration
  • (c) = config: Set in the individual scanner configuration section
  • (s) = scanner: Default threshold built into the scanner itself

Statistics calculation:

  • All statistics are calculated from the final aggregated SARIF report
  • Suppressed findings are counted separately and do not contribute to actionable findings
  • Scanner status is determined by comparing actionable findings to the threshold
Scanner S C H M L I Time Action Result Thresh
bandit 0 1 0 0 1 0 942ms 1 FAILED MED (g)
cdk-nag 0 0 0 0 0 0 37.5s 0 PASSED MED (g)
cfn-nag 0 0 0 0 0 0 7ms 0 PASSED MED (g)
checkov 0 65 0 0 0 0 9.9s 65 FAILED MED (g)
detect-secrets 0 0 0 0 0 0 932ms 0 PASSED MED (g)
grype 0 0 0 0 0 0 41.5s 0 PASSED MED (g)
npm-audit 0 0 0 0 0 0 175ms 0 PASSED MED (g)
opengrep 0 14 0 0 0 0 26.1s 14 FAILED MED (g)
semgrep 0 14 0 0 0 0 16.8s 14 FAILED MED (g)
syft 0 0 0 0 0 0 1.4s 0 PASSED MED (g)

Detailed Findings

Show 94 actionable findings

Finding 1: B602

  • Severity: HIGH
  • Scanner: bandit
  • Rule ID: B602
  • Location: infra-terraform/test-scripts/test-oauth2-auth.py:88-90

Description:
subprocess call with shell=True identified, security issue.

Code Snippet:

"""Run shell command and return output."""
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return result.stdout.strip(), result.returncode

Finding 2: CKV_AWS_300

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_300
  • Location: infra-terraform/modules/amplify-hosting/main.tf:42-53

Description:
Ensure S3 lifecycle configuration sets period for aborting failed uploads

Code Snippet:

resource "aws_s3_bucket_lifecycle_configuration" "access_logs" {
  bucket = aws_s3_bucket.access_logs.id

  rule {
    id     = "DeleteOldAccessLogs"
    status = "Enabled"

    expiration {
      days = var.access_logs_expiry_days
    }
  }
}

Finding 3: CKV_AWS_300

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_300
  • Location: infra-terraform/modules/amplify-hosting/main.tf:82-93

Description:
Ensure S3 lifecycle configuration sets period for aborting failed uploads

Code Snippet:

resource "aws_s3_bucket_lifecycle_configuration" "staging" {
  bucket = aws_s3_bucket.staging.id

  rule {
    id     = "DeleteOldDeployments"
    status = "Enabled"

    expiration {
      days = var.staging_bucket_expiry_days
    }
  }
}

Finding 4: CKV_AWS_119

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_119
  • Location: infra-terraform/modules/backend/feedback.tf:13-54

Description:
Ensure DynamoDB Tables are encrypted using a KMS Customer Managed CMK

Code Snippet:

resource "aws_dynamodb_table" "feedback" {
  name         = "${var.stack_name_base}-feedback"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "feedbackId"

  attribute {
    name = "feedbackId"
    type = "S"
  }

  attribute {
    name = "feedbackType"
    type = "S"
  }

  attribute {
    name = "timestamp"
    type = "N"
  }

  # GSI for querying by feedbackType with timestamp sorting
  global_secondary_index {
    name            = "feedbackType-timestamp-index"
    hash_key        = "feedbackType"
    range_key       = "timestamp"
    projection_type = "ALL"
  }

  # Deletion protection disabled (allows terraform destroy)
  deletion_protection_enabled = false

  # Point-in-time recovery
  point_in_time_recovery {
    enabled = true
  }

  # Server-side encryption (AWS managed)
  server_side_encryption {
    enabled = true
  }

}

Finding 5: CKV_AWS_338

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_338
  • Location: infra-terraform/modules/backend/feedback.tf:60-64

Description:
Ensure CloudWatch log groups retains logs for at least 1 year

Code Snippet:

resource "aws_cloudwatch_log_group" "feedback_lambda" {
  name              = "/aws/lambda/${var.stack_name_base}-feedback"
  retention_in_days = local.log_retention_days

}

Finding 6: CKV_AWS_158

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_158
  • Location: infra-terraform/modules/backend/feedback.tf:60-64

Description:
Ensure that CloudWatch Log Group is encrypted by KMS

Code Snippet:

resource "aws_cloudwatch_log_group" "feedback_lambda" {
  name              = "/aws/lambda/${var.stack_name_base}-feedback"
  retention_in_days = local.log_retention_days

}

Finding 7: CKV_AWS_50

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_50
  • Location: infra-terraform/modules/backend/feedback.tf:158-182

Description:
X-Ray tracing is enabled for Lambda

Code Snippet:

resource "aws_lambda_function" "feedback" {
  function_name = "${var.stack_name_base}-feedback"
  role          = aws_iam_role.feedback_lambda.arn
  handler       = "index.handler"
  runtime       = "python3.13"
  timeout       = 30
  memory_size   = 256

  filename         = data.archive_file.feedback_lambda.output_path
  source_code_hash = data.archive_file.feedback_lambda.output_base64sha256

  # Lambda Powertools layer
  layers = [local.powertools_layer_arn]

  # Environment variables
  environment {
    variables = {
      TABLE_NAME           = aws_dynamodb_table.feedback.name
      CORS_ALLOWED_ORIGINS = "${var.frontend_url},http://localhost:3000"
    }
  }

  depends_on = [aws_cloudwatch_log_group.feedback_lambda]

}

Finding 8: CKV_AWS_117

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_117
  • Location: infra-terraform/modules/backend/feedback.tf:158-182

Description:
Ensure that AWS Lambda function is configured inside a VPC

Code Snippet:

resource "aws_lambda_function" "feedback" {
  function_name = "${var.stack_name_base}-feedback"
  role          = aws_iam_role.feedback_lambda.arn
  handler       = "index.handler"
  runtime       = "python3.13"
  timeout       = 30
  memory_size   = 256

  filename         = data.archive_file.feedback_lambda.output_path
  source_code_hash = data.archive_file.feedback_lambda.output_base64sha256

  # Lambda Powertools layer
  layers = [local.powertools_layer_arn]

  # Environment variables
  environment {
    variables = {
      TABLE_NAME           = aws_dynamodb_table.feedback.name
      CORS_ALLOWED_ORIGINS = "${var.frontend_url},http://localhost:3000"
    }
  }

  depends_on = [aws_cloudwatch_log_group.feedback_lambda]

}

Finding 9: CKV_AWS_272

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_272
  • Location: infra-terraform/modules/backend/feedback.tf:158-182

Description:
Ensure AWS Lambda function is configured to validate code-signing

Code Snippet:

resource "aws_lambda_function" "feedback" {
  function_name = "${var.stack_name_base}-feedback"
  role          = aws_iam_role.feedback_lambda.arn
  handler       = "index.handler"
  runtime       = "python3.13"
  timeout       = 30
  memory_size   = 256

  filename         = data.archive_file.feedback_lambda.output_path
  source_code_hash = data.archive_file.feedback_lambda.output_base64sha256

  # Lambda Powertools layer
  layers = [local.powertools_layer_arn]

  # Environment variables
  environment {
    variables = {
      TABLE_NAME           = aws_dynamodb_table.feedback.name
      CORS_ALLOWED_ORIGINS = "${var.frontend_url},http://localhost:3000"
    }
  }

  depends_on = [aws_cloudwatch_log_group.feedback_lambda]

}

Finding 10: CKV_AWS_116

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_116
  • Location: infra-terraform/modules/backend/feedback.tf:158-182

Description:
Ensure that AWS Lambda function is configured for a Dead Letter Queue(DLQ)

Code Snippet:

resource "aws_lambda_function" "feedback" {
  function_name = "${var.stack_name_base}-feedback"
  role          = aws_iam_role.feedback_lambda.arn
  handler       = "index.handler"
  runtime       = "python3.13"
  timeout       = 30
  memory_size   = 256

  filename         = data.archive_file.feedback_lambda.output_path
  source_code_hash = data.archive_file.feedback_lambda.output_base64sha256

  # Lambda Powertools layer
  layers = [local.powertools_layer_arn]

  # Environment variables
  environment {
    variables = {
      TABLE_NAME           = aws_dynamodb_table.feedback.name
      CORS_ALLOWED_ORIGINS = "${var.frontend_url},http://localhost:3000"
    }
  }

  depends_on = [aws_cloudwatch_log_group.feedback_lambda]

}

Finding 11: CKV_AWS_173

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_173
  • Location: infra-terraform/modules/backend/feedback.tf:158-182

Description:
Check encryption settings for Lambda environmental variable

Code Snippet:

resource "aws_lambda_function" "feedback" {
  function_name = "${var.stack_name_base}-feedback"
  role          = aws_iam_role.feedback_lambda.arn
  handler       = "index.handler"
  runtime       = "python3.13"
  timeout       = 30
  memory_size   = 256

  filename         = data.archive_file.feedback_lambda.output_path
  source_code_hash = data.archive_file.feedback_lambda.output_base64sha256

  # Lambda Powertools layer
  layers = [local.powertools_layer_arn]

  # Environment variables
  environment {
    variables = {
      TABLE_NAME           = aws_dynamodb_table.feedback.name
      CORS_ALLOWED_ORIGINS = "${var.frontend_url},http://localhost:3000"
    }
  }

  depends_on = [aws_cloudwatch_log_group.feedback_lambda]

}

Finding 12: CKV_AWS_115

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_115
  • Location: infra-terraform/modules/backend/feedback.tf:158-182

Description:
Ensure that AWS Lambda function is configured for function-level concurrent execution limit

Code Snippet:

resource "aws_lambda_function" "feedback" {
  function_name = "${var.stack_name_base}-feedback"
  role          = aws_iam_role.feedback_lambda.arn
  handler       = "index.handler"
  runtime       = "python3.13"
  timeout       = 30
  memory_size   = 256

  filename         = data.archive_file.feedback_lambda.output_path
  source_code_hash = data.archive_file.feedback_lambda.output_base64sha256

  # Lambda Powertools layer
  layers = [local.powertools_layer_arn]

  # Environment variables
  environment {
    variables = {
      TABLE_NAME           = aws_dynamodb_table.feedback.name
      CORS_ALLOWED_ORIGINS = "${var.frontend_url},http://localhost:3000"
    }
  }

  depends_on = [aws_cloudwatch_log_group.feedback_lambda]

}

Finding 13: CKV_AWS_237

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_237
  • Location: infra-terraform/modules/backend/feedback.tf:188-196

Description:
Ensure Create before destroy for API Gateway

Code Snippet:

resource "aws_api_gateway_rest_api" "feedback" {
  name        = "${var.stack_name_base}-feedback-api"
  description = "API Gateway for feedback collection"

  endpoint_configuration {
    types = ["REGIONAL"]
  }

}

Finding 14: CKV_AWS_73

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_73
  • Location: infra-terraform/modules/backend/feedback.tf:340-365

Description:
Ensure API Gateway has X-Ray Tracing enabled

Code Snippet:

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

  # Access logs
  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.api_gateway_access.arn
    format = jsonencode({
      requestId        = "$context.requestId"
      ip               = "$context.identity.sourceIp"
      caller           = "$context.identity.caller"
      user             = "$context.identity.user"
      requestTime      = "$context.requestTime"
      httpMethod       = "$context.httpMethod"
      resourcePath     = "$context.resourcePath"
      status           = "$context.status"
      protocol         = "$context.protocol"
      responseLength   = "$context.responseLength"
      integrationError = "$context.integrationErrorMessage"
    })
  }


  depends_on = [aws_cloudwatch_log_group.api_gateway_access]
}

Finding 15: CKV_AWS_120

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_120
  • Location: infra-terraform/modules/backend/feedback.tf:340-365

Description:
Ensure API Gateway caching is enabled

Code Snippet:

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

  # Access logs
  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.api_gateway_access.arn
    format = jsonencode({
      requestId        = "$context.requestId"
      ip               = "$context.identity.sourceIp"
      caller           = "$context.identity.caller"
      user             = "$context.identity.user"
      requestTime      = "$context.requestTime"
      httpMethod       = "$context.httpMethod"
      resourcePath     = "$context.resourcePath"
      status           = "$context.status"
      protocol         = "$context.protocol"
      responseLength   = "$context.responseLength"
      integrationError = "$context.integrationErrorMessage"
    })
  }


  depends_on = [aws_cloudwatch_log_group.api_gateway_access]
}

Finding 16: CKV_AWS_338

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_338
  • Location: infra-terraform/modules/backend/feedback.tf:368-372

Description:
Ensure CloudWatch log groups retains logs for at least 1 year

Code Snippet:

resource "aws_cloudwatch_log_group" "api_gateway_access" {
  name              = "/aws/apigateway/${var.stack_name_base}-feedback-api/access-logs"
  retention_in_days = local.log_retention_days

}

Finding 17: CKV_AWS_158

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_158
  • Location: infra-terraform/modules/backend/feedback.tf:368-372

Description:
Ensure that CloudWatch Log Group is encrypted by KMS

Code Snippet:

resource "aws_cloudwatch_log_group" "api_gateway_access" {
  name              = "/aws/apigateway/${var.stack_name_base}-feedback-api/access-logs"
  retention_in_days = local.log_retention_days

}

Finding 18: CKV_AWS_308

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_308
  • Location: infra-terraform/modules/backend/feedback.tf:378-392

Description:
Ensure API Gateway method setting caching is set to encrypted

Code Snippet:

resource "aws_api_gateway_method_settings" "all" {
  rest_api_id = aws_api_gateway_rest_api.feedback.id
  stage_name  = aws_api_gateway_stage.prod.stage_name
  method_path = "*/*"

  settings {
    throttling_rate_limit  = local.api_throttling_rate_limit
    throttling_burst_limit = local.api_throttling_burst_limit
    caching_enabled        = true
    cache_ttl_in_seconds   = local.api_cache_ttl_seconds
    logging_level          = "INFO"
    metrics_enabled        = true
    data_trace_enabled     = false
  }
}

Finding 19: CKV_AWS_338

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_338
  • Location: infra-terraform/modules/backend/gateway.tf:13-17

Description:
Ensure CloudWatch log groups retains logs for at least 1 year

Code Snippet:

resource "aws_cloudwatch_log_group" "tool_lambda" {
  name              = "/aws/lambda/${var.stack_name_base}-sample-tool"
  retention_in_days = local.log_retention_days

}

Finding 20: CKV_AWS_158

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_158
  • Location: infra-terraform/modules/backend/gateway.tf:13-17

Description:
Ensure that CloudWatch Log Group is encrypted by KMS

Code Snippet:

resource "aws_cloudwatch_log_group" "tool_lambda" {
  name              = "/aws/lambda/${var.stack_name_base}-sample-tool"
  retention_in_days = local.log_retention_days

}

Finding 21: CKV_AWS_50

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_50
  • Location: infra-terraform/modules/backend/gateway.tf:70-82

Description:
X-Ray tracing is enabled for Lambda

Code Snippet:

resource "aws_lambda_function" "sample_tool" {
  function_name = "${var.stack_name_base}-sample-tool"
  role          = aws_iam_role.tool_lambda.arn
  handler       = "sample_tool_lambda.handler"
  runtime       = "python3.13"
  timeout       = 30

  filename         = data.archive_file.tool_lambda.output_path
  source_code_hash = data.archive_file.tool_lambda.output_base64sha256

  depends_on = [aws_cloudwatch_log_group.tool_lambda]

}

Finding 22: CKV_AWS_117

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_117
  • Location: infra-terraform/modules/backend/gateway.tf:70-82

Description:
Ensure that AWS Lambda function is configured inside a VPC

Code Snippet:

resource "aws_lambda_function" "sample_tool" {
  function_name = "${var.stack_name_base}-sample-tool"
  role          = aws_iam_role.tool_lambda.arn
  handler       = "sample_tool_lambda.handler"
  runtime       = "python3.13"
  timeout       = 30

  filename         = data.archive_file.tool_lambda.output_path
  source_code_hash = data.archive_file.tool_lambda.output_base64sha256

  depends_on = [aws_cloudwatch_log_group.tool_lambda]

}

Finding 23: CKV_AWS_272

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_272
  • Location: infra-terraform/modules/backend/gateway.tf:70-82

Description:
Ensure AWS Lambda function is configured to validate code-signing

Code Snippet:

resource "aws_lambda_function" "sample_tool" {
  function_name = "${var.stack_name_base}-sample-tool"
  role          = aws_iam_role.tool_lambda.arn
  handler       = "sample_tool_lambda.handler"
  runtime       = "python3.13"
  timeout       = 30

  filename         = data.archive_file.tool_lambda.output_path
  source_code_hash = data.archive_file.tool_lambda.output_base64sha256

  depends_on = [aws_cloudwatch_log_group.tool_lambda]

}

Finding 24: CKV_AWS_116

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_116
  • Location: infra-terraform/modules/backend/gateway.tf:70-82

Description:
Ensure that AWS Lambda function is configured for a Dead Letter Queue(DLQ)

Code Snippet:

resource "aws_lambda_function" "sample_tool" {
  function_name = "${var.stack_name_base}-sample-tool"
  role          = aws_iam_role.tool_lambda.arn
  handler       = "sample_tool_lambda.handler"
  runtime       = "python3.13"
  timeout       = 30

  filename         = data.archive_file.tool_lambda.output_path
  source_code_hash = data.archive_file.tool_lambda.output_base64sha256

  depends_on = [aws_cloudwatch_log_group.tool_lambda]

}

Finding 25: CKV_AWS_115

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_115
  • Location: infra-terraform/modules/backend/gateway.tf:70-82

Description:
Ensure that AWS Lambda function is configured for function-level concurrent execution limit

Code Snippet:

resource "aws_lambda_function" "sample_tool" {
  function_name = "${var.stack_name_base}-sample-tool"
  role          = aws_iam_role.tool_lambda.arn
  handler       = "sample_tool_lambda.handler"
  runtime       = "python3.13"
  timeout       = 30

  filename         = data.archive_file.tool_lambda.output_path
  source_code_hash = data.archive_file.tool_lambda.output_base64sha256

  depends_on = [aws_cloudwatch_log_group.tool_lambda]

}

Finding 26: CKV_AWS_338

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_338
  • Location: infra-terraform/modules/backend/oauth2_provider.tf:23-27

Description:
Ensure CloudWatch log groups retains logs for at least 1 year

Code Snippet:

resource "aws_cloudwatch_log_group" "oauth2_provider" {
  name              = "/aws/lambda/${var.stack_name_base}-oauth2-provider"
  retention_in_days = 7

}

Finding 27: CKV_AWS_158

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_158
  • Location: infra-terraform/modules/backend/oauth2_provider.tf:23-27

Description:
Ensure that CloudWatch Log Group is encrypted by KMS

Code Snippet:

resource "aws_cloudwatch_log_group" "oauth2_provider" {
  name              = "/aws/lambda/${var.stack_name_base}-oauth2-provider"
  retention_in_days = 7

}

Finding 28: CKV_AWS_50

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_50
  • Location: infra-terraform/modules/backend/oauth2_provider.tf:144-158

Description:
X-Ray tracing is enabled for Lambda

Code Snippet:

resource "aws_lambda_function" "oauth2_provider" {
  filename         = data.archive_file.oauth2_provider.output_path
  function_name    = "${var.stack_name_base}-oauth2-provider"
  role             = aws_iam_role.oauth2_provider.arn
  handler          = "index.handler"
  source_code_hash = data.archive_file.oauth2_provider.output_base64sha256
  runtime          = "python3.13"
  timeout          = 300 # 5 minutes


  depends_on = [
    aws_cloudwatch_log_group.oauth2_provider,
    aws_iam_role_policy.oauth2_provider
  ]
}

Finding 29: CKV_AWS_117

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_117
  • Location: infra-terraform/modules/backend/oauth2_provider.tf:144-158

Description:
Ensure that AWS Lambda function is configured inside a VPC

Code Snippet:

resource "aws_lambda_function" "oauth2_provider" {
  filename         = data.archive_file.oauth2_provider.output_path
  function_name    = "${var.stack_name_base}-oauth2-provider"
  role             = aws_iam_role.oauth2_provider.arn
  handler          = "index.handler"
  source_code_hash = data.archive_file.oauth2_provider.output_base64sha256
  runtime          = "python3.13"
  timeout          = 300 # 5 minutes


  depends_on = [
    aws_cloudwatch_log_group.oauth2_provider,
    aws_iam_role_policy.oauth2_provider
  ]
}

Finding 30: CKV_AWS_272

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_272
  • Location: infra-terraform/modules/backend/oauth2_provider.tf:144-158

Description:
Ensure AWS Lambda function is configured to validate code-signing

Code Snippet:

resource "aws_lambda_function" "oauth2_provider" {
  filename         = data.archive_file.oauth2_provider.output_path
  function_name    = "${var.stack_name_base}-oauth2-provider"
  role             = aws_iam_role.oauth2_provider.arn
  handler          = "index.handler"
  source_code_hash = data.archive_file.oauth2_provider.output_base64sha256
  runtime          = "python3.13"
  timeout          = 300 # 5 minutes


  depends_on = [
    aws_cloudwatch_log_group.oauth2_provider,
    aws_iam_role_policy.oauth2_provider
  ]
}

Finding 31: CKV_AWS_116

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_116
  • Location: infra-terraform/modules/backend/oauth2_provider.tf:144-158

Description:
Ensure that AWS Lambda function is configured for a Dead Letter Queue(DLQ)

Code Snippet:

resource "aws_lambda_function" "oauth2_provider" {
  filename         = data.archive_file.oauth2_provider.output_path
  function_name    = "${var.stack_name_base}-oauth2-provider"
  role             = aws_iam_role.oauth2_provider.arn
  handler          = "index.handler"
  source_code_hash = data.archive_file.oauth2_provider.output_base64sha256
  runtime          = "python3.13"
  timeout          = 300 # 5 minutes


  depends_on = [
    aws_cloudwatch_log_group.oauth2_provider,
    aws_iam_role_policy.oauth2_provider
  ]
}

Finding 32: CKV_AWS_115

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_115
  • Location: infra-terraform/modules/backend/oauth2_provider.tf:144-158

Description:
Ensure that AWS Lambda function is configured for function-level concurrent execution limit

Code Snippet:

resource "aws_lambda_function" "oauth2_provider" {
  filename         = data.archive_file.oauth2_provider.output_path
  function_name    = "${var.stack_name_base}-oauth2-provider"
  role             = aws_iam_role.oauth2_provider.arn
  handler          = "index.handler"
  source_code_hash = data.archive_file.oauth2_provider.output_base64sha256
  runtime          = "python3.13"
  timeout          = 300 # 5 minutes


  depends_on = [
    aws_cloudwatch_log_group.oauth2_provider,
    aws_iam_role_policy.oauth2_provider
  ]
}

Finding 33: CKV_AWS_149

  • Severity: HIGH
  • Scanner: checkov
  • Rule ID: CKV_AWS_149
  • Location: infra-terraform/modules/backend/ssm.tf:86-90

Description:
Ensure that Secrets Manager secret is encrypted using KMS CMK

Copy link
Contributor

@kaleko kaleko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issues to flag

  1. backend_vpc_subnet_ids validation is a no-op — The validation length(var.backend_vpc_subnet_ids) == 0 || length(var.backend_vpc_subnet_ids) >= 1 is always true. The real validation is in the check block in locals.tf, which is correct, but this variable-level validation should either be removed or fixed to something meaningful.

  2. Missing docs/TERRAFORM_DEPLOYMENT.md — The README references ../docs/TERRAFORM_DEPLOYMENT.md but I don't see it in the diff. Is it in a separate PR, or was it missed?

  3. Can we include some notion of FAST-TF version? I remember discussing that with you but can't find the slack messages. Both CDK and TF should start at 0.4.0

cc: @kunanit

@kunanit kunanit changed the title Open feat(terraform): Add TF parity updates for v0.3.2 Open feat(terraform): Add TF parity updates for v0.4.0 Mar 10, 2026
@kunanit
Copy link
Contributor Author

kunanit commented Mar 10, 2026

  • backend_vpc_subnet_ids validation is a no-op — The validation length(var.backend_vpc_subnet_ids) == 0 || length(var.backend_vpc_subnet_ids) >= 1 is always true. The real validation is in the check block in locals.tf, which is correct, but this variable-level validation should either be removed or fixed to something meaningful.
  • Missing docs/TERRAFORM_DEPLOYMENT.md — The README references ../docs/TERRAFORM_DEPLOYMENT.md but I don't see it in the diff. Is it in a separate PR, or was it missed?
  • Can we include some notion of FAST-TF version? I remember discussing that with you but can't find the slack messages. Both CDK and TF should start at 0.4.0

I've made updates to address feedback:

  1. Remove unused validation
  2. Added missing doc file
  3. Terraform versioning practices are outlined in infra-terraform/TF_VERSION_BUMP_PLAYBOOK.md (Clarification rather than an update). I've updated the PR title/description to reflect the new target version 0.4.0.

@kaleko kaleko merged commit b5d2ccc into awslabs:main Mar 11, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants