Skip to content

Step Functions state machines should have logging enabled

Step Functions orchestrate distributed workflows across Lambda functions, ECS tasks, and other AWS services. Without logging, execution failures, timeout conditions, and unexpected state transitions vanish silently. Debugging a multi-step workflow without execution logs means replaying traffic in a lower environment and hoping you can reproduce the issue.

Logging at ALL or ERROR sends structured execution data to CloudWatch Logs, enabling post-incident analysis and automated alerting on failed executions. For workflows that process sensitive data or financial transactions, these logs also produce the audit trail regulators expect.

Retrofit consideration

Enabling logging requires an IAM role with permission to write to CloudWatch Logs. Existing state machines may use a role that lacks logs:CreateLogDelivery, logs:GetLogDelivery, logs:UpdateLogDelivery, and related actions, requiring a policy update.

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 "step_functions" {
  source  = "pcidss.compliance.tf/terraform-aws-modules/step-functions/aws"
  version = ">=5.0.0"

  definition = "${jsonencode({ "Comment" : "Pofix compliance test", "StartAt" : "Pass", "States" : { "Pass" : { "Type" : "Pass", "End" : true } } })}"
  name       = "pofix-abc123"
  type       = "STANDARD"
}

module "step_functions" {
  source  = "acscessentialeight.compliance.tf/terraform-aws-modules/step-functions/aws"
  version = ">=5.0.0"

  definition = "${jsonencode({ "Comment" : "Pofix compliance test", "StartAt" : "Pass", "States" : { "Pass" : { "Type" : "Pass", "End" : true } } })}"
  name       = "pofix-abc123"
  type       = "STANDARD"
}

module "step_functions" {
  source  = "eugmpannex11.compliance.tf/terraform-aws-modules/step-functions/aws"
  version = ">=5.0.0"

  definition = "${jsonencode({ "Comment" : "Pofix compliance test", "StartAt" : "Pass", "States" : { "Pass" : { "Type" : "Pass", "End" : true } } })}"
  name       = "pofix-abc123"
  type       = "STANDARD"
}

module "step_functions" {
  source  = "pcidssv321.compliance.tf/terraform-aws-modules/step-functions/aws"
  version = ">=5.0.0"

  definition = "${jsonencode({ "Comment" : "Pofix compliance test", "StartAt" : "Pass", "States" : { "Pass" : { "Type" : "Pass", "End" : true } } })}"
  name       = "pofix-abc123"
  type       = "STANDARD"
}

If you use terraform-aws-modules/step-functions/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 "step_functions" {
  source  = "terraform-aws-modules/step-functions/aws"
  version = ">=5.0.0"

  definition = "${jsonencode({ "Comment" : "Pofix compliance test", "StartAt" : "Pass", "States" : { "Pass" : { "Type" : "Pass", "End" : true } } })}"
  name       = "pofix-abc123"
  type       = "STANDARD"
}

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

resource "aws_sfn_state_machine" "this" {
  definition = jsonencode({ Comment = "A minimal state machine", StartAt = "Pass", States = { Pass = { Type = "Pass", End = true } } })

  logging_configuration {
    include_execution_data = true
    level                  = "ALL"
    log_destination        = "arn:aws:logs:us-east-1:123456789012:log-group:example-log-group:*"
  }

  name     = "pofix-abc123"
  role_arn = "arn:aws:iam::123456789012:role/example-role"
}

What this control checks

To pass, the aws_sfn_state_machine resource must include a logging_configuration block with level set to anything other than "OFF". Valid passing values are "ALL" or "ERROR". The log_destination argument must point to a CloudWatch Logs log group ARN suffixed with :*. Setting include_execution_data = true captures full input/output payloads.

The state machine's IAM role, set via role_arn, must carry CloudWatch Logs delivery permissions: logs:CreateLogDelivery, logs:GetLogDelivery, logs:UpdateLogDelivery, logs:DeleteLogDelivery, logs:ListLogDeliveries, and logs:PutResourcePolicy. It fails when no logging_configuration block exists or when level is "OFF".

Common pitfalls

  • Missing :* suffix on log group ARN

    The log_destination argument requires the CloudWatch log group ARN with a :* suffix. Using aws_cloudwatch_log_group.example.arn alone produces an ARN without the wildcard, and Step Functions will either fail to deliver logs or error out during state machine creation. Append it explicitly: "${aws_cloudwatch_log_group.example.arn}:*".

  • IAM role missing CloudWatch Logs delivery permissions

    Log delivery silently fails when the execution role referenced by role_arn lacks the full set of CloudWatch Logs delivery permissions: logs:CreateLogDelivery, logs:UpdateLogDelivery, logs:DeleteLogDelivery, logs:GetLogDelivery, logs:ListLogDeliveries, logs:PutResourcePolicy, logs:DescribeResourcePolicies, and logs:DescribeLogGroups. A role scoped only to invoke Lambda or other downstream services won't have these, and the state machine will apply without error while producing no logs.

  • Express vs Standard workflow logging differences

    Express workflows (type = "EXPRESS") have no long-term execution history in the console or API. CloudWatch Logs is the only durable record of what happened, making logging_configuration non-negotiable for troubleshooting and retention. Standard workflows have a 90-day execution history fallback, but the control applies to both types and logging should be configured regardless.

  • Log level mismatch with control parameter

    If the control is configured with a custom logLevel parameter requiring ALL, a state machine set to level = "ERROR" fails even though logging is technically on. Check the expected level in your compliance policy before assuming any non-OFF value passes. Teams that default to ERROR-only often get tripped up when a policy requires ALL.

Audit evidence

Config rule evaluation results showing all AWS::StepFunctions::StateMachine resources as COMPLIANT are the primary artifact. To confirm the configuration, aws stepfunctions describe-state-machine returns the loggingConfiguration object; the level field should show something other than OFF and destinations should contain a valid log group ARN. The same information is visible in the Step Functions console under the Logging tab.

CloudWatch Logs Insights queries against the destination log group, showing actual entries from recent executions, confirm data is flowing. CloudTrail records for UpdateStateMachine establish when logging was enabled or last changed.

Framework-specific interpretation

PCI DSS v4.0: Requirements 10.2 and 10.3 call for capturing and protecting audit log content across all system components in the cardholder data environment. Step Functions workflows that route, process, or transform payment data are in scope, and execution logs at ERROR or ALL provide the event record those requirements ask for, covering both log content and the integrity protections that Requirement 10.3 adds.

Tool mappings

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

  • Compliance.tf Control: sfn_state_machine_logging_enabled

  • AWS Config Managed Rule: STEP_FUNCTIONS_STATE_MACHINE_LOGGING_ENABLED

  • Checkov Check: CKV_AWS_285

  • Powerpipe Control: aws_compliance.control.sfn_state_machine_logging_enabled

  • Prowler Check: stepfunctions_statemachine_logging_enabled

  • AWS Security Hub Control: StepFunctions.1

  • Trivy Check: AWS-0119

Last reviewed: 2026-03-09