UNPKG

26 kBMarkdownView Raw
1# AWS Step Functions Construct Library
2<!--BEGIN STABILITY BANNER-->
3
4---
5
6![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge)
7
8![cdk-constructs: Stable](https://img.shields.io/badge/cdk--constructs-stable-success.svg?style=for-the-badge)
9
10---
11
12<!--END STABILITY BANNER-->
13
14The `@aws-cdk/aws-stepfunctions` package contains constructs for building
15serverless workflows using objects. Use this in conjunction with the
16`@aws-cdk/aws-stepfunctions-tasks` package, which contains classes used
17to call other AWS services.
18
19Defining a workflow looks like this (for the [Step Functions Job Poller
20example](https://docs.aws.amazon.com/step-functions/latest/dg/job-status-poller-sample.html)):
21
22## Example
23
24```ts
25import * as sfn from '@aws-cdk/aws-stepfunctions';
26import * as tasks from '@aws-cdk/aws-stepfunctions-tasks';
27import * as lambda from '@aws-cdk/aws-lambda';
28
29const submitLambda = new lambda.Function(this, 'SubmitLambda', { ... });
30const getStatusLambda = new lambda.Function(this, 'CheckLambda', { ... });
31
32const submitJob = new tasks.LambdaInvoke(this, 'Submit Job', {
33 lambdaFunction: submitLambda,
34 // Lambda's result is in the attribute `Payload`
35 outputPath: '$.Payload',
36});
37
38const waitX = new sfn.Wait(this, 'Wait X Seconds', {
39 time: sfn.WaitTime.secondsPath('$.waitSeconds'),
40});
41
42const getStatus = new tasks.LambdaInvoke(this, 'Get Job Status', {
43 lambdaFunction: getStatusLambda,
44 // Pass just the field named "guid" into the Lambda, put the
45 // Lambda's result in a field called "status" in the response
46 inputPath: '$.guid',
47 outputPath: '$.Payload',
48});
49
50const jobFailed = new sfn.Fail(this, 'Job Failed', {
51 cause: 'AWS Batch Job Failed',
52 error: 'DescribeJob returned FAILED',
53});
54
55const finalStatus = new tasks.LambdaInvoke(this, 'Get Final Job Status', {
56 lambdaFunction: getStatusLambda,
57 // Use "guid" field as input
58 inputPath: '$.guid',
59 outputPath: '$.Payload',
60});
61
62const definition = submitJob
63 .next(waitX)
64 .next(getStatus)
65 .next(new sfn.Choice(this, 'Job Complete?')
66 // Look at the "status" field
67 .when(sfn.Condition.stringEquals('$.status', 'FAILED'), jobFailed)
68 .when(sfn.Condition.stringEquals('$.status', 'SUCCEEDED'), finalStatus)
69 .otherwise(waitX));
70
71new sfn.StateMachine(this, 'StateMachine', {
72 definition,
73 timeout: Duration.minutes(5)
74});
75```
76
77You can find more sample snippets and learn more about the service integrations
78in the `@aws-cdk/aws-stepfunctions-tasks` package.
79
80## State Machine
81
82A `stepfunctions.StateMachine` is a resource that takes a state machine
83definition. The definition is specified by its start state, and encompasses
84all states reachable from the start state:
85
86```ts
87const startState = new sfn.Pass(this, 'StartState');
88
89new sfn.StateMachine(this, 'StateMachine', {
90 definition: startState
91});
92```
93
94State machines execute using an IAM Role, which will automatically have all
95permissions added that are required to make all state machine tasks execute
96properly (for example, permissions to invoke any Lambda functions you add to
97your workflow). A role will be created by default, but you can supply an
98existing one as well.
99
100## Amazon States Language
101
102This library comes with a set of classes that model the [Amazon States
103Language](https://states-language.net/spec.html). The following State classes
104are supported:
105
106* [`Task`](#task)
107* [`Pass`](#pass)
108* [`Wait`](#wait)
109* [`Choice`](#choice)
110* [`Parallel`](#parallel)
111* [`Succeed`](#succeed)
112* [`Fail`](#fail)
113* [`Map`](#map)
114* [`Custom State`](#custom-state)
115
116An arbitrary JSON object (specified at execution start) is passed from state to
117state and transformed during the execution of the workflow. For more
118information, see the States Language spec.
119
120### Task
121
122A `Task` represents some work that needs to be done. The exact work to be
123done is determine by a class that implements `IStepFunctionsTask`, a collection
124of which can be found in the `@aws-cdk/aws-stepfunctions-tasks` module.
125
126The tasks in the `@aws-cdk/aws-stepfunctions-tasks` module support the
127[service integration pattern](https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html) that integrates Step Functions with services
128directly in the Amazon States language.
129
130### Pass
131
132A `Pass` state passes its input to its output, without performing work.
133Pass states are useful when constructing and debugging state machines.
134
135The following example injects some fixed data into the state machine through
136the `result` field. The `result` field will be added to the input and the result
137will be passed as the state's output.
138
139```ts
140// Makes the current JSON state { ..., "subObject": { "hello": "world" } }
141const pass = new sfn.Pass(this, 'Add Hello World', {
142 result: sfn.Result.fromObject({ hello: 'world' }),
143 resultPath: '$.subObject',
144});
145
146// Set the next state
147pass.next(nextState);
148```
149
150The `Pass` state also supports passing key-value pairs as input. Values can
151be static, or selected from the input with a path.
152
153The following example filters the `greeting` field from the state input
154and also injects a field called `otherData`.
155
156```ts
157const pass = new sfn.Pass(this, 'Filter input and inject data', {
158 parameters: { // input to the pass state
159 input: sfn.JsonPath.stringAt('$.input.greeting'),
160 otherData: 'some-extra-stuff'
161 },
162});
163```
164
165The object specified in `parameters` will be the input of the `Pass` state.
166Since neither `Result` nor `ResultPath` are supplied, the `Pass` state copies
167its input through to its output.
168
169Learn more about the [Pass state](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-pass-state.html)
170
171### Wait
172
173A `Wait` state waits for a given number of seconds, or until the current time
174hits a particular time. The time to wait may be taken from the execution's JSON
175state.
176
177```ts
178// Wait until it's the time mentioned in the the state object's "triggerTime"
179// field.
180const wait = new sfn.Wait(this, 'Wait For Trigger Time', {
181 time: sfn.WaitTime.timestampPath('$.triggerTime'),
182});
183
184// Set the next state
185wait.next(startTheWork);
186```
187
188### Choice
189
190A `Choice` state can take a different path through the workflow based on the
191values in the execution's JSON state:
192
193```ts
194const choice = new sfn.Choice(this, 'Did it work?');
195
196// Add conditions with .when()
197choice.when(sfn.Condition.stringEquals('$.status', 'SUCCESS'), successState);
198choice.when(sfn.Condition.numberGreaterThan('$.attempts', 5), failureState);
199
200// Use .otherwise() to indicate what should be done if none of the conditions match
201choice.otherwise(tryAgainState);
202```
203
204If you want to temporarily branch your workflow based on a condition, but have
205all branches come together and continuing as one (similar to how an `if ...
206then ... else` works in a programming language), use the `.afterwards()` method:
207
208```ts
209const choice = new sfn.Choice(this, 'What color is it?');
210choice.when(sfn.Condition.stringEquals('$.color', 'BLUE'), handleBlueItem);
211choice.when(sfn.Condition.stringEquals('$.color', 'RED'), handleRedItem);
212choice.otherwise(handleOtherItemColor);
213
214// Use .afterwards() to join all possible paths back together and continue
215choice.afterwards().next(shipTheItem);
216```
217
218If your `Choice` doesn't have an `otherwise()` and none of the conditions match
219the JSON state, a `NoChoiceMatched` error will be thrown. Wrap the state machine
220in a `Parallel` state if you want to catch and recover from this.
221
222#### Available Conditions
223
224see [step function comparison operators](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-choice-state.html#amazon-states-language-choice-state-rules)
225
226* `Condition.isPresent` - matches if a json path is present
227* `Condition.isNotPresent` - matches if a json path is not present
228* `Condition.isString` - matches if a json path contains a string
229* `Condition.isNotString` - matches if a json path is not a string
230* `Condition.isNumeric` - matches if a json path is numeric
231* `Condition.isNotNumeric` - matches if a json path is not numeric
232* `Condition.isBoolean` - matches if a json path is boolean
233* `Condition.isNotBoolean` - matches if a json path is not boolean
234* `Condition.isTimestamp` - matches if a json path is a timestamp
235* `Condition.isNotTimestamp` - matches if a json path is not a timestamp
236* `Condition.isNotNull` - matches if a json path is not null
237* `Condition.isNull` - matches if a json path is null
238* `Condition.booleanEquals` - matches if a boolean field has a given value
239* `Condition.booleanEqualsJsonPath` - matches if a boolean field equals a value in a given mapping path
240* `Condition.stringEqualsJsonPath` - matches if a string field equals a given mapping path
241* `Condition.stringEquals` - matches if a field equals a string value
242* `Condition.stringLessThan` - matches if a string field sorts before a given value
243* `Condition.stringLessThanJsonPath` - matches if a string field sorts before a value at given mapping path
244* `Condition.stringLessThanEquals` - matches if a string field sorts equal to or before a given value
245* `Condition.stringLessThanEqualsJsonPath` - matches if a string field sorts equal to or before a given mapping
246* `Condition.stringGreaterThan` - matches if a string field sorts after a given value
247* `Condition.stringGreaterThanJsonPath` - matches if a string field sorts after a value at a given mapping path
248* `Condition.stringGreaterThanEqualsJsonPath` - matches if a string field sorts after or equal to value at a given mapping path
249* `Condition.stringGreaterThanEquals` - matches if a string field sorts after or equal to a given value
250* `Condition.numberEquals` - matches if a numeric field has the given value
251* `Condition.numberEqualsJsonPath` - matches if a numeric field has the value in a given mapping path
252* `Condition.numberLessThan` - matches if a numeric field is less than the given value
253* `Condition.numberLessThanJsonPath` - matches if a numeric field is less than the value at the given mapping path
254* `Condition.numberLessThanEquals` - matches if a numeric field is less than or equal to the given value
255* `Condition.numberLessThanEqualsJsonPath` - matches if a numeric field is less than or equal to the numeric value at given mapping path
256* `Condition.numberGreaterThan` - matches if a numeric field is greater than the given value
257* `Condition.numberGreaterThanJsonPath` - matches if a numeric field is greater than the value at a given mapping path
258* `Condition.numberGreaterThanEquals` - matches if a numeric field is greater than or equal to the given value
259* `Condition.numberGreaterThanEqualsJsonPath` - matches if a numeric field is greater than or equal to the value at a given mapping path
260* `Condition.timestampEquals` - matches if a timestamp field is the same time as the given timestamp
261* `Condition.timestampEqualsJsonPath` - matches if a timestamp field is the same time as the timestamp at a given mapping path
262* `Condition.timestampLessThan` - matches if a timestamp field is before the given timestamp
263* `Condition.timestampLessThanJsonPath` - matches if a timestamp field is before the timestamp at a given mapping path
264* `Condition.timestampLessThanEquals` - matches if a timestamp field is before or equal to the given timestamp
265* `Condition.timestampLessThanEqualsJsonPath` - matches if a timestamp field is before or equal to the timestamp at a given mapping path
266* `Condition.timestampGreaterThan` - matches if a timestamp field is after the timestamp at a given mapping path
267* `Condition.timestampGreaterThanJsonPath` - matches if a timestamp field is after the timestamp at a given mapping path
268* `Condition.timestampGreaterThanEquals` - matches if a timestamp field is after or equal to the given timestamp
269* `Condition.timestampGreaterThanEqualsJsonPath` - matches if a timestamp field is after or equal to the timestamp at a given mapping path
270* `Condition.stringMatches` - matches if a field matches a string pattern that can contain a wild card (\*) e.g: log-\*.txt or \*LATEST\*. No other characters other than "\*" have any special meaning - \* can be escaped: \\\\*
271
272### Parallel
273
274A `Parallel` state executes one or more subworkflows in parallel. It can also
275be used to catch and recover from errors in subworkflows.
276
277```ts
278const parallel = new sfn.Parallel(this, 'Do the work in parallel');
279
280// Add branches to be executed in parallel
281parallel.branch(shipItem);
282parallel.branch(sendInvoice);
283parallel.branch(restock);
284
285// Retry the whole workflow if something goes wrong
286parallel.addRetry({ maxAttempts: 1 });
287
288// How to recover from errors
289parallel.addCatch(sendFailureNotification);
290
291// What to do in case everything succeeded
292parallel.next(closeOrder);
293```
294
295### Succeed
296
297Reaching a `Succeed` state terminates the state machine execution with a
298succesful status.
299
300```ts
301const success = new sfn.Succeed(this, 'We did it!');
302```
303
304### Fail
305
306Reaching a `Fail` state terminates the state machine execution with a
307failure status. The fail state should report the reason for the failure.
308Failures can be caught by encompassing `Parallel` states.
309
310```ts
311const success = new sfn.Fail(this, 'Fail', {
312 error: 'WorkflowFailure',
313 cause: "Something went wrong"
314});
315```
316
317### Map
318
319A `Map` state can be used to run a set of steps for each element of an input array.
320A `Map` state will execute the same steps for multiple entries of an array in the state input.
321
322While the `Parallel` state executes multiple branches of steps using the same input, a `Map` state will
323execute the same steps for multiple entries of an array in the state input.
324
325```ts
326const map = new sfn.Map(this, 'Map State', {
327 maxConcurrency: 1,
328 itemsPath: sfn.JsonPath.stringAt('$.inputForMap')
329});
330map.iterator(new sfn.Pass(this, 'Pass State'));
331```
332
333### Custom State
334
335It's possible that the high-level constructs for the states or `stepfunctions-tasks` do not have
336the states or service integrations you are looking for. The primary reasons for this lack of
337functionality are:
338
339* A [service integration](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-service-integrations.html) is available through Amazon States Langauge, but not available as construct
340 classes in the CDK.
341* The state or state properties are available through Step Functions, but are not configurable
342 through constructs
343
344If a feature is not available, a `CustomState` can be used to supply any Amazon States Language
345JSON-based object as the state definition.
346
347[Code Snippets](https://docs.aws.amazon.com/step-functions/latest/dg/tutorial-code-snippet.html#tutorial-code-snippet-1) are available and can be plugged in as the state definition.
348
349Custom states can be chained together with any of the other states to create your state machine
350definition. You will also need to provide any permissions that are required to the `role` that
351the State Machine uses.
352
353The following example uses the `DynamoDB` service integration to insert data into a DynamoDB table.
354
355```ts
356import * as ddb from '@aws-cdk/aws-dynamodb';
357import * as cdk from '@aws-cdk/core';
358import * as sfn from '@aws-cdk/aws-stepfunctions';
359
360// create a table
361const table = new ddb.Table(this, 'montable', {
362 partitionKey: {
363 name: 'id',
364 type: ddb.AttributeType.STRING,
365 },
366});
367
368const finalStatus = new sfn.Pass(stack, 'final step');
369
370// States language JSON to put an item into DynamoDB
371// snippet generated from https://docs.aws.amazon.com/step-functions/latest/dg/tutorial-code-snippet.html#tutorial-code-snippet-1
372const stateJson = {
373 Type: 'Task',
374 Resource: 'arn:aws:states:::dynamodb:putItem',
375 Parameters: {
376 TableName: table.tableName,
377 Item: {
378 id: {
379 S: 'MyEntry',
380 },
381 },
382 },
383 ResultPath: null,
384};
385
386// custom state which represents a task to insert data into DynamoDB
387const custom = new sfn.CustomState(this, 'my custom task', {
388 stateJson,
389});
390
391const chain = sfn.Chain.start(custom)
392 .next(finalStatus);
393
394const sm = new sfn.StateMachine(this, 'StateMachine', {
395 definition: chain,
396 timeout: cdk.Duration.seconds(30),
397});
398
399// don't forget permissions. You need to assign them
400table.grantWriteData(sm);
401```
402
403## Task Chaining
404
405To make defining work flows as convenient (and readable in a top-to-bottom way)
406as writing regular programs, it is possible to chain most methods invocations.
407In particular, the `.next()` method can be repeated. The result of a series of
408`.next()` calls is called a **Chain**, and can be used when defining the jump
409targets of `Choice.on` or `Parallel.branch`:
410
411```ts
412const definition = step1
413 .next(step2)
414 .next(choice
415 .when(condition1, step3.next(step4).next(step5))
416 .otherwise(step6)
417 .afterwards())
418 .next(parallel
419 .branch(step7.next(step8))
420 .branch(step9.next(step10)))
421 .next(finish);
422
423new sfn.StateMachine(this, 'StateMachine', {
424 definition,
425});
426```
427
428If you don't like the visual look of starting a chain directly off the first
429step, you can use `Chain.start`:
430
431```ts
432const definition = sfn.Chain
433 .start(step1)
434 .next(step2)
435 .next(step3)
436 // ...
437```
438
439## State Machine Fragments
440
441It is possible to define reusable (or abstracted) mini-state machines by
442defining a construct that implements `IChainable`, which requires you to define
443two fields:
444
445* `startState: State`, representing the entry point into this state machine.
446* `endStates: INextable[]`, representing the (one or more) states that outgoing
447 transitions will be added to if you chain onto the fragment.
448
449Since states will be named after their construct IDs, you may need to prefix the
450IDs of states if you plan to instantiate the same state machine fragment
451multiples times (otherwise all states in every instantiation would have the same
452name).
453
454The class `StateMachineFragment` contains some helper functions (like
455`prefixStates()`) to make it easier for you to do this. If you define your state
456machine as a subclass of this, it will be convenient to use:
457
458```ts
459interface MyJobProps {
460 jobFlavor: string;
461}
462
463class MyJob extends sfn.StateMachineFragment {
464 public readonly startState: sfn.State;
465 public readonly endStates: sfn.INextable[];
466
467 constructor(parent: cdk.Construct, id: string, props: MyJobProps) {
468 super(parent, id);
469
470 const first = new sfn.Task(this, 'First', { ... });
471 // ...
472 const last = new sfn.Task(this, 'Last', { ... });
473
474 this.startState = first;
475 this.endStates = [last];
476 }
477}
478
479// Do 3 different variants of MyJob in parallel
480new sfn.Parallel(this, 'All jobs')
481 .branch(new MyJob(this, 'Quick', { jobFlavor: 'quick' }).prefixStates())
482 .branch(new MyJob(this, 'Medium', { jobFlavor: 'medium' }).prefixStates())
483 .branch(new MyJob(this, 'Slow', { jobFlavor: 'slow' }).prefixStates());
484```
485
486A few utility functions are available to parse state machine fragments.
487
488* `State.findReachableStates`: Retrieve the list of states reachable from a given state.
489* `State.findReachableEndStates`: Retrieve the list of end or terminal states reachable from a given state.
490
491## Activity
492
493**Activities** represent work that is done on some non-Lambda worker pool. The
494Step Functions workflow will submit work to this Activity, and a worker pool
495that you run yourself, probably on EC2, will pull jobs from the Activity and
496submit the results of individual jobs back.
497
498You need the ARN to do so, so if you use Activities be sure to pass the Activity
499ARN into your worker pool:
500
501```ts
502const activity = new sfn.Activity(this, 'Activity');
503
504// Read this CloudFormation Output from your application and use it to poll for work on
505// the activity.
506new cdk.CfnOutput(this, 'ActivityArn', { value: activity.activityArn });
507```
508
509### Activity-Level Permissions
510
511Granting IAM permissions to an activity can be achieved by calling the `grant(principal, actions)` API:
512
513```ts
514const activity = new sfn.Activity(this, 'Activity');
515
516const role = new iam.Role(stack, 'Role', {
517 assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
518});
519
520activity.grant(role, 'states:SendTaskSuccess');
521```
522
523This will grant the IAM principal the specified actions onto the activity.
524
525## Metrics
526
527`Task` object expose various metrics on the execution of that particular task. For example,
528to create an alarm on a particular task failing:
529
530```ts
531new cloudwatch.Alarm(this, 'TaskAlarm', {
532 metric: task.metricFailed(),
533 threshold: 1,
534 evaluationPeriods: 1,
535});
536```
537
538There are also metrics on the complete state machine:
539
540```ts
541new cloudwatch.Alarm(this, 'StateMachineAlarm', {
542 metric: stateMachine.metricFailed(),
543 threshold: 1,
544 evaluationPeriods: 1,
545});
546```
547
548And there are metrics on the capacity of all state machines in your account:
549
550```ts
551new cloudwatch.Alarm(this, 'ThrottledAlarm', {
552 metric: StateTransitionMetrics.metricThrottledEvents(),
553 threshold: 10,
554 evaluationPeriods: 2,
555});
556```
557
558## Logging
559
560Enable logging to CloudWatch by passing a logging configuration with a
561destination LogGroup:
562
563```ts
564const logGroup = new logs.LogGroup(stack, 'MyLogGroup');
565
566new sfn.StateMachine(stack, 'MyStateMachine', {
567 definition: sfn.Chain.start(new sfn.Pass(stack, 'Pass')),
568 logs: {
569 destination: logGroup,
570 level: sfn.LogLevel.ALL,
571 }
572});
573```
574
575## X-Ray tracing
576
577Enable X-Ray tracing for StateMachine:
578
579```ts
580const logGroup = new logs.LogGroup(stack, 'MyLogGroup');
581
582new sfn.StateMachine(stack, 'MyStateMachine', {
583 definition: sfn.Chain.start(new sfn.Pass(stack, 'Pass')),
584 tracingEnabled: true
585});
586```
587
588See [the AWS documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-xray-tracing.html)
589to learn more about AWS Step Functions's X-Ray support.
590
591## State Machine Permission Grants
592
593IAM roles, users, or groups which need to be able to work with a State Machine should be granted IAM permissions.
594
595Any object that implements the `IGrantable` interface (has an associated principal) can be granted permissions by calling:
596
597* `stateMachine.grantStartExecution(principal)` - grants the principal the ability to execute the state machine
598* `stateMachine.grantRead(principal)` - grants the principal read access
599* `stateMachine.grantTaskResponse(principal)` - grants the principal the ability to send task tokens to the state machine
600* `stateMachine.grantExecution(principal, actions)` - grants the principal execution-level permissions for the IAM actions specified
601* `stateMachine.grant(principal, actions)` - grants the principal state-machine-level permissions for the IAM actions specified
602
603### Start Execution Permission
604
605Grant permission to start an execution of a state machine by calling the `grantStartExecution()` API.
606
607```ts
608const role = new iam.Role(stack, 'Role', {
609 assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
610});
611
612const stateMachine = new stepfunction.StateMachine(stack, 'StateMachine', {
613 definition,
614});
615
616// Give role permission to start execution of state machine
617stateMachine.grantStartExecution(role);
618```
619
620The following permission is provided to a service principal by the `grantStartExecution()` API:
621
622* `states:StartExecution` - to state machine
623
624### Read Permissions
625
626Grant `read` access to a state machine by calling the `grantRead()` API.
627
628```ts
629const role = new iam.Role(stack, 'Role', {
630 assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
631});
632
633const stateMachine = new stepfunction.StateMachine(stack, 'StateMachine', {
634 definition,
635});
636
637// Give role read access to state machine
638stateMachine.grantRead(role);
639```
640
641The following read permissions are provided to a service principal by the `grantRead()` API:
642
643* `states:ListExecutions` - to state machine
644* `states:ListStateMachines` - to state machine
645* `states:DescribeExecution` - to executions
646* `states:DescribeStateMachineForExecution` - to executions
647* `states:GetExecutionHistory` - to executions
648* `states:ListActivities` - to `*`
649* `states:DescribeStateMachine` - to `*`
650* `states:DescribeActivity` - to `*`
651
652### Task Response Permissions
653
654Grant permission to allow task responses to a state machine by calling the `grantTaskResponse()` API:
655
656```ts
657const role = new iam.Role(stack, 'Role', {
658 assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
659});
660
661const stateMachine = new stepfunction.StateMachine(stack, 'StateMachine', {
662 definition,
663});
664
665// Give role task response permissions to the state machine
666stateMachine.grantTaskResponse(role);
667```
668
669The following read permissions are provided to a service principal by the `grantRead()` API:
670
671* `states:SendTaskSuccess` - to state machine
672* `states:SendTaskFailure` - to state machine
673* `states:SendTaskHeartbeat` - to state machine
674
675### Execution-level Permissions
676
677Grant execution-level permissions to a state machine by calling the `grantExecution()` API:
678
679```ts
680const role = new iam.Role(stack, 'Role', {
681 assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
682});
683
684const stateMachine = new stepfunction.StateMachine(stack, 'StateMachine', {
685 definition,
686});
687
688// Give role permission to get execution history of ALL executions for the state machine
689stateMachine.grantExecution(role, 'states:GetExecutionHistory');
690```
691
692### Custom Permissions
693
694You can add any set of permissions to a state machine by calling the `grant()` API.
695
696```ts
697const user = new iam.User(stack, 'MyUser');
698
699const stateMachine = new stepfunction.StateMachine(stack, 'StateMachine', {
700 definition,
701});
702
703//give user permission to send task success to the state machine
704stateMachine.grant(user, 'states:SendTaskSuccess');
705```
706
707## Import
708
709Any Step Functions state machine that has been created outside the stack can be imported
710into your CDK stack.
711
712State machines can be imported by their ARN via the `StateMachine.fromStateMachineArn()` API
713
714```ts
715import * as sfn from 'aws-stepfunctions';
716
717const stack = new Stack(app, 'MyStack');
718sfn.StateMachine.fromStateMachineArn(
719 stack,
720 'ImportedStateMachine',
721 'arn:aws:states:us-east-1:123456789012:stateMachine:StateMachine2E01A3A5-N5TJppzoevKQ');
722```