A Step Functions state machine is a machine identity that calls other AWS services on your behalf, and every payload it passes between states can end up in execution history, logs and events. Securing it therefore comes down to two customer-side questions: what the execution role is allowed to do, and where workflow data is stored and who can read it. AWS Step Functions gives you the controls for both, including scoped execution roles with confused deputy conditions, per-task role assumption for cross-account calls, logging levels with an option to exclude execution data, and customer managed AWS KMS keys for state machines, activities and logs. This guide explains each control, how to roll them out and what to monitor. It is for cloud platform engineers, serverless developers and AWS security teams. Function-level hardening is covered in the AWS Lambda Security Guide and is not repeated here.
This is an independent guide from NHI Mgmt Group, based on AWS's public documentation for Step Functions as of November 2026. It is not sponsored or reviewed by AWS. Product features change, so confirm settings against AWS's current documentation.
Key takeaways
- The execution role is the workflow's identity. Scope its permissions to the exact actions and resource ARNs each Task state needs, and add aws:SourceArn and aws:SourceAccount conditions to its trust policy.
- Control who can pass and start. iam:PassRole on execution roles and states:StartExecution on state machines are the privileged permissions; restrict both to named principals and ARNs.
- Decide deliberately whether logs carry payloads. The LoggingConfiguration field includeExecutionData set to false keeps execution data out of CloudWatch Logs while still recording events.
- Customer managed keys protect history and definitions. A symmetric KMS key encrypts execution history, state machine definitions and activity inputs, and the encryption context lets you bind the key to one state machine.
- Cross-account tasks use a target role. The source execution role assumes a role in the target account, whose trust policy should check sts:ExternalId set to the state machine ARN.
How identity and access work in AWS Step Functions
Two sets of identities matter. The first are the people and pipelines that manage and run workflows through the Step Functions API (CreateStateMachine, UpdateStateMachine, StartExecution, StartSyncExecution, DescribeExecution, GetExecutionHistory, RedriveExecution and so on). The second is the execution role each state machine assumes when it runs, which is what actually calls Lambda, DynamoDB, SQS, ECS, Bedrock or any other integrated service from Task states.
Access to the Step Functions API is controlled with identity-based IAM policies. To limit where calls can come from, AWS notes you can use policies that restrict access to specific VPC endpoints or VPCs, for example with an aws:SourceVpce condition on states:StartExecution. Clients must use TLS 1.2, with TLS 1.3 recommended, and cipher suites with perfect forward secrecy.
| Identity | Used for | Key permissions | Hardening point |
|---|---|---|---|
| Workflow developers and CI/CD roles | Creating and updating state machines | states:CreateStateMachine, states:UpdateStateMachine, iam:PassRole, kms:DescribeKey and kms:GenerateDataKey if a customer managed key is used | Scope iam:PassRole to named execution roles; deploy through pipelines |
| Callers and triggers | Starting executions (applications, EventBridge rules, API Gateway) | states:StartExecution or states:StartSyncExecution on specific state machine ARNs | One caller role per trigger; restrict by VPC endpoint where calls are internal |
| Operators and support | Viewing and redriving executions | states:DescribeExecution, states:GetExecutionHistory, states:RedriveExecution, kms:Decrypt | Execution data is visible to anyone with these; grant them per workflow |
| Execution role | Calling integrated services from Task states | Only the actions each state uses, plus logging and KMS actions | Trust policy limited with aws:SourceArn and aws:SourceAccount |
| Cross-account target role | Calls to resources in another account | Actions on the target resources | Trust only the source execution role, with sts:ExternalId |
| Activity workers | Polling for activity tasks | states:GetActivityTask, states:SendTaskSuccess, states:SendTaskFailure, kms:Decrypt on the activity key | Treat worker credentials as workload secrets |
Execution role design
The execution role trusts the service principal states.amazonaws.com. AWS recommends the aws:SourceArn global condition key with the full state machine ARN as the most effective protection against the confused deputy problem, with aws:SourceAccount when any state machine in the account may use the role, and wildcards only when the full ARN is not known. Roles generated by the console when you create a state machine there are valid for the Region where they were created.
Decision guidance: one execution role per state machine is the clearest model: permissions match the workflow definition, reviews are simple, and a role compromise affects one workflow. A shared role across many state machines saves effort but accumulates the union of every workflow's permissions. Use shared roles only for small groups of near-identical workflows owned by the same team. See the AWS IAM Policies and Roles Guide.
Admin roles and privileged access
In Step Functions, administrative power comes from a short list of permissions that let someone change what a workflow does or which identity it runs as.
- states:UpdateStateMachine together with iam:PassRole: whoever holds both can point a state machine at a more powerful role or change its definition to call new services. Grant this pair to deployment pipelines, not to individuals, and scope iam:PassRole to the specific execution role ARNs.
- Changes to execution role policies: iam:PutRolePolicy or iam:AttachRolePolicy on execution roles is equivalent to changing the workflow's permissions. Restrict it with permissions boundaries or service control policies; see the AWS Organizations and SCPs Guide.
- Read access to execution data: DescribeExecution returns input and output (up to 262,144 bytes each) and GetExecutionHistory returns full history for Standard Workflows. Treat these as data access permissions, not harmless read-only rights.
- KMS key administration: AWS states that if the customer managed key is disabled, deleted or pending deletion, running executions fail and new ones cannot start, and data encrypted under a deleted key is unrecoverable. Keep key deletion and disablement with a separate key administrator role.
- Redrive: states:RedriveExecution restarts failed Standard executions with their original input and role. Grant it to operators for the workflows they support.
Integrations, API tokens and service accounts
Least privilege per Task state
Step Functions calls integrated services directly from Task states, so the execution role's permissions policy should list only the API actions the definition uses, on specific resource ARNs. AWS documents the policies the console generates for each integrated service, which are a good starting point to tighten further. For workflows that use AWS SDK integrations (arn:aws:states:::aws-sdk:service:action), review each SDK action in the definition and grant that action alone, rather than service-wide wildcards.
Cross-account tasks
AWS documents a four-step pattern: create a role in the target account with permissions on the target resources; specify that role in the Task state so it is assumed before the call; update the target role's trust policy to allow the source account's execution role; and allow the source execution role to assume the target role. The AWS example trust policy for .sync calls also checks sts:ExternalId equal to the source state machine ARN, which ties the target role to one workflow.
- A state machine assumes one role at a time but can use roles from several accounts across its states.
- Cross-account delegation works within a single partition, and cross-Region SDK integrations are outside this feature, so design multi-Region workflows per Region.
- .sync patterns poll the target resource and consume your quota; for nested Express Workflows AWS recommends arn:aws:states:::aws-sdk:sfn:startSyncExecution.
- When a role ARN is taken from state input at runtime, validate the input upstream and constrain the source execution role's sts:AssumeRole permission to a list of approved target role ARNs, so callers cannot redirect the workflow to an unexpected role.
See the AWS STS and AssumeRole Guide for trust policy patterns.
Secrets and external credentials
Keep secrets out of state machine definitions and execution input. Pass a reference such as a Secrets Manager secret ARN and let the Lambda function or service that needs the credential retrieve it, so the value never appears in execution history or logs. See the AWS Secrets Manager guide.
Logging and detection
Execution data in logs
Logging is set with the LoggingConfiguration parameter on CreateStateMachine or UpdateStateMachine. It has three fields: level (ALL, ERROR, FATAL or OFF), includeExecutionData (when false, execution data is excluded from the log) and destinations (one CloudWatch Logs log group, required unless the level is OFF).
| Setting | What AWS documents | Recommendation |
|---|---|---|
| level ALL | Logs all event types | Use for Express Workflows and during development |
| level ERROR | Failure, abort and timeout events such as ExecutionFailed, TaskFailed and MapRunFailed | A practical default for production Standard Workflows |
| level FATAL | ExecutionAborted, ExecutionFailed and ExecutionTimedOut only | Minimum for high-volume workflows |
| includeExecutionData false | Execution data excluded from the log | Use wherever payloads can hold personal data, tokens or business-sensitive values |
| Log group prefix /aws/vendedlogs/states | Keeps CloudWatch Logs resource policies under the 5,120-character limit | Use for every state machine log group |
The type of workflow changes what you need. Standard Workflows keep execution history in the service, available through GetExecutionHistory, and console-created Standard Workflows are not configured for logging by default. Express Workflows keep no history in the service, so CloudWatch Logs is the only record; the console enables logging for them by default, while workflows created through the API, CLI or CloudFormation need logging set explicitly. AWS describes Express log delivery as best effort and recommends a data store such as DynamoDB or Standard Workflows where a guaranteed record is needed.
The execution role needs the CloudWatch Logs delivery actions AWS lists (logs:CreateLogDelivery, logs:PutLogEvents, logs:PutResourcePolicy and related actions) on Resource "*", because these actions do not take resource-level restrictions. Keep that statement separate from the workflow's business permissions so it is easy to review.
Reducing data exposure
- Exclude execution data from logs with includeExecutionData false, and pass large or sensitive payloads as S3 object references rather than inline data, as AWS suggests for large Express payloads.
- Use a customer managed key so reading definitions and history needs kms:Decrypt. AWS notes that for CMK-encrypted workflows, execution input, output, error and cause are omitted from EventBridge status change events.
- Use METADATA_ONLY on describe calls for tooling that needs status but not content; AWS documents it as returning a successful response without the encrypted definition.
- Restrict DescribeExecution and GetExecutionHistory to the team that owns the workflow.
What to monitor
| Signal | Source | Why it matters |
|---|---|---|
| CreateStateMachine and UpdateStateMachine | CloudTrail | Definition or role changes outside the pipeline |
| iam:PassRole for execution roles | CloudTrail | Workflows given a different identity |
| Policy changes on execution roles | CloudTrail IAM events | Silent widening of workflow permissions |
| Decrypt and GenerateDataKey invoked by states.amazonaws.com | CloudTrail KMS events with encryption context | Shows which state machine used which key |
| KMS key disable or deletion scheduling | CloudTrail | Executions will fail and data may become unrecoverable |
| ExecutionFailed, ExecutionTimedOut spikes | CloudWatch Logs and metrics | Access denied errors after role changes, or abuse |
See the AWS CloudTrail Guide. Misuse of serverless orchestration maps to MITRE ATT&CK Serverless Execution (T1648) and use of valid cloud accounts (T1078.004); the MITRE ATT&CK T1648 Serverless Execution guide covers detection in depth.
Metrics:
- Percentage of state machines with a dedicated execution role.
- Percentage of execution role trust policies with aws:SourceArn or aws:SourceAccount conditions.
- Percentage of state machines handling sensitive data with includeExecutionData false.
- Percentage of state machines encrypted with a customer managed key, where policy requires one.
- Number of execution roles with service-wide wildcard actions, trending to zero.
- Number of state machine changes not made by the deployment pipeline.
Hardening baseline
Encryption at rest
Step Functions always encrypts data at rest with AWS owned keys and lets you add a symmetric customer managed key through EncryptionConfiguration with Type CUSTOMER_MANAGED_KMS_KEY. The key encrypts execution history and the definition for Standard and Express state machines, and activity inputs for activities (activity encryption cannot be changed later; create a new activity instead). KmsDataKeyReusePeriodSeconds ranges from 60 to 900 seconds, default 300. The execution role needs kms:Decrypt and kms:GenerateDataKey for Standard and asynchronous Express workflows, and kms:Decrypt for synchronous Express. Scope these with kms:EncryptionContext:aws:states:stateMachineArn. If logging is enabled, the key policy must allow delivery.logs.amazonaws.com to use kms:Decrypt. AWS cautions that adding kms:ViaService to an execution role can stop executions, so test that condition first. See the AWS KMS Security Guide.
Phased rollout
- Inventory: list state machines, their execution roles, logging configuration and encryption settings. Flag shared roles and wildcard actions.
- Trust policies: add aws:SourceArn and aws:SourceAccount to every execution role trust policy. Test by starting an execution in a non-production account.
- Permissions: replace wildcard actions with the actions in each definition. Use IAM Access Analyzer policy generation from CloudTrail activity where helpful, and keep the previous policy version for rollback.
- Logging: set level ERROR or ALL with includeExecutionData false for sensitive workflows, with /aws/vendedlogs/states log groups and a retention period.
- Encryption: introduce customer managed keys workflow by workflow, granting KMS permissions to the execution role, the deployment role and operators before switching.
- Caller restrictions: scope states:StartExecution to named callers and add VPC endpoint conditions for internal callers.
- Guardrails: enforce the baseline in infrastructure as code reviews and with AWS Config rules or service control policies.
Small and large organisations: a small team can reach most of the value with dedicated execution roles, trust conditions and includeExecutionData false. Larger estates add customer managed keys per data classification, cross-account target roles with sts:ExternalId, and automated checks in the pipeline.
Framework mapping
| Control | Step Functions setting | NIST SP 800-53 Rev 5 | NIST CSF 2.0 |
|---|---|---|---|
| Least privilege | Scoped execution roles, iam:PassRole limits | AC-6 | PR.AA-05 |
| Access enforcement | StartExecution and DescribeExecution scoping, VPC endpoint conditions | AC-3 | PR.AA-05 |
| Data at rest | Customer managed KMS keys | SC-28, SC-12 | PR.DS-01 |
| Data in transit | TLS 1.2 or later | SC-8 | PR.DS-02 |
| Audit | LoggingConfiguration, CloudTrail | AU-2, AU-12 | DE.CM-01 |
| Configuration | Infrastructure as code baseline | CM-7 | PR.PS-01 |
Common misconfigurations
- One broad role for all workflows: every state machine inherits every permission. Split roles per workflow.
- Trust policies without source conditions: add aws:SourceArn and aws:SourceAccount as AWS recommends.
- Logging level ALL with execution data for sensitive payloads: set includeExecutionData false or pass references instead of values.
- Secrets in execution input: they persist in history and logs. Pass secret ARNs and retrieve the value inside the task.
- Unrestricted iam:PassRole for developers: scope it to named execution roles and route changes through pipelines.
- Disabling a customer managed key without checking dependants: running executions fail. Review KMS encryption context in CloudTrail before key changes.
Practitioner checklist
- Give each state machine its own execution role with only the actions its Task states use.
- Add aws:SourceArn and aws:SourceAccount conditions to every execution role trust policy.
- Scope iam:PassRole and states:UpdateStateMachine to deployment pipelines.
- Scope states:StartExecution to named callers; use aws:SourceVpce for internal callers.
- Set includeExecutionData false for workflows that handle sensitive data.
- Configure logging explicitly for Express Workflows created through APIs or infrastructure as code.
- Use customer managed KMS keys bound with kms:EncryptionContext:aws:states:stateMachineArn where policy requires.
- Use sts:ExternalId with the state machine ARN in cross-account target role trust policies.
- Keep secrets out of definitions and input; pass Secrets Manager ARNs.
- Alert on state machine changes, execution role policy changes and KMS key state changes.
Standards and references
- AWS: Security in AWS Step Functions
- AWS: Creating an IAM role for your state machine
- AWS: Accessing resources in other AWS accounts in Step Functions
- AWS: Logging Step Functions using CloudWatch Logs
- AWS: LoggingConfiguration API reference
- AWS: Data at rest encryption in Step Functions
- AWS: DescribeExecution API reference
Related NHI Mgmt Group resources: AWS Lambda Security Guide · Serverless Security Guide · Amazon EventBridge, SNS and SQS Security Guide · AWS KMS Security Guide