This will be the last article explaining the different states we can use in a step function. We’ll see three simple states like Pass, Fail and Succeed and finally, we’re going to a see a more complex state like Choice. And obviously, we’re going to use the http://serverless.com framework to deploy them.

Pass state

The pass state is a simple state that just passes its input to its output, without performing any work. Apart from the common fields, you can specify two optional fields:

  • Result: The result to pass to the next state, filtered by the ResultPath field.
  • ResultPath: Specifies where (in the input) put the output specified in the Result field.

Fail state

The fail state stops the execution of the Step Function and marks it as a failure. It only allows the Type and Comment fields from the common fields, and you can use a couple of optional fields:

  • Cause: a custom failure string
  • Error: an error name that can be use for error handling.

Succeed state

The succeed state stops the execution of the Step Function successfully. It’s a terminal state, so it doesn’t have a Next or End field. It’s a good target for a choice branch that you just want to stop the execution.

Choice state

The choice state allows you to declare a decision logic into your state machine. You can specify different branches with different logic to access them. In addition to the common fields it adds a couple of fields:

  • Choices (required): an array of choice rules that determines the next state.
  • Default (optional but recommended): the state to transition if no choice rule is satisfied.

Choice rules

Each choice rule contains a comparision and a next field. Comparisions can be composed using And or Or operators. You can check all the operators available here.

When defining a comparision you must specify two fields:

  • Variable: which value are you going to compare. It will be a path of the input value of the function.
  • Operator: the field name will be the operator you want to use and the value will be the value you want to compare with.

Let’s put everything together

So, let’s put all we’ve learned so far in a single step function (without any lambda this time):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
stepFunctions:
    stateMachines:
        testChoiceStepFunction:
            definition:
                StartAt: DoChoice
                States:
                    DoChoice:
                        Type: Choice
                        Choices: 
                        - Variable: "$.value"
                            NumericGreaterThan: 0
                            Next: PositiveNumber
                        - Variable: "$.value"
                            NumericLessThan: 0
                            Next: NegativeNumber
                        Default: Zero
                    PositiveNumber:
                        Type: Pass
                        Result: {"result": "It's a positive number!"}
                        Next: FinalState
                    NegativeNumber:
                        Type: Pass
                        Result: {"result": "It's a negative number!"}
                        Next: FinalState
                    Zero:
                        Type: Fail
                        Cause: "It's a zero!"
                    FinalState:
                        Type: Succeed

What are we defining here is a step function with two choices (number greater than 0 and number less than zero) and a default state. Every choice has a next state. Both PositiveNumber and NegativeNumber are Pass states with a different result, the Zero state is a Fail state and the FinalState is a Succeed state.

step function

Let’s deploy and run the function and see what happens.

1
sls invoke stepf --nam e testChoiceStepFunction --data '{"value": 1}'

choice positive result

1
sls invoke stepf --nam e testChoiceStepFunction --data '{"value": -1}'

choice negative result

1
sls invoke stepf --nam e testChoiceStepFunction --data '{"value": 0}'

choice zero result

Summary

We’ve seen how easy is to set up a choice state in State Functions, allowing us to choose different paths depending on the input of the state.

As we’ve seen in previous articles, Step Functions helps us to orchestrate lambda functions. One of the most important aspects when we’re developing a system, distributed or not, is handling errors and retries. In this articles we’ll see how easy is to do it using Step Functions and the serverless framework.

Catching errors

Coding the lambda

First of all we’re going to catch some errors. Let’s create a new project with one lambda inside it named ErrorLambda with the following code:

1
2
3
4
5
6
7
8
9
10
public class ErrorLambda
{
    public void Error(string input){
        throw new CustomException(input);
    }
}

public class CustomException : Exception{
    public CustomException(string message) : base(message){}
}

Creating the Step Function

Let’s create a Step Function that catches errors. As always, create the serverless.yml and copy the following definition of the Step Function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
stepFunctions:
    stateMachines:
        testErrorStepFunction:
            definition:
                StartAt: Error
                States:
                    Error:
                        Type: Task
                        Resource: arn:aws:lambda:${opt:region}:${self:custom.accountId}:function:${self:custom.errorService}-${opt:stage}
                        Catch:
                            - ErrorEquals: ["CustomException"]
                                Next: CustomErrorFallback
                            - ErrorEquals: ["States.TaskFailed"]
                                Next: "ReservedTypeFallback"
                            - ErrorEquals: ["States.ALL"]
                                Next: "CatchAllFallback"
                        End: true
                    CustomErrorFallback:
                        Type: Pass
                        Result: "This is a fallback from a custom Lambda function exception"
                        End: true
                    ReservedTypeFallback:
                        Type: Pass
                        Result: "This is a fallback from a reserved error code"
                        End: true
                    CatchAllFallback:
                        Type: "Pass"
                        Result: "This is a fallback from any error code"
                        End: true

We can split what we’re doing here in two parts. At the end of the definition we’re defining the steps that we’ll run after we catch an error. In this case, we’ll define three types of errors and three different next states.

First, we defined our catchers. In this case we defined three different catchers. The first one is to catch exceptions that we throw from our Lambda. The string that help us to filter the error is the name of the class of the exception we want to catch. In the second and third catchers, we’re using predefined error codes. We know that they are predefined error codes because they start with States. The possible values for a predifined error codes are States.ALL, States.Timeout, States.TaskFailed, States.Permissions. You can read more about them here

If we now deploy the step function, run it, and we go to the AWS console we’ll see the representation of it

retries

As you can see, is similar to a parallel step, but in this case we just run one of the branches.

If we click in one of the catchers and inspect the input, we’ll see the type of error and the stack trace:

1
2
3
4
5
6
7
8
9
10
11
{
    "Error":"CustomException",
    "Cause":{
        "errorType": "CustomException",
        "errorMessage": "an error",
        "stackTrace": [
            "at StepFunctionsHandleErrors.ErrorLambda.Error(String input) in /StepFunctionsHandleErrors/ErrorLambda/ErrorLambda.cs:line 12",
                "at lambda_method(Closure , Stream , Stream , ContextInfo )"
                ]
    }
}

The string in the errorType field is the one that you have to use in the definition of the catcher.

Retrying

It’s possible that when you detect an error (or an specific type of error) you want to retry a lambda to see if the error was a transient one. Configuring retries is a very simple step in Step Functions, you just need to add the following code just before the Catch element:

1
2
3
4
5
6
7
Retry:
    - ErrorEquals: [ "CustomException" ]
      IntervalSeconds: 1
      BackoffRate: 2.0
      MaxAttempts: 4
    - ErrorEquals: [ "States.ALL" ]
      IntervalSeconds: 5

In this case we’re specifying that, if we get a CustomException error we’re going to retry 4 times at most (default 3), with an initial interval of 1 second (default 1) and a back-off rate of 2.0 (default 2.0).

If we want, we can specify a list of errors in the ErrorEquals field. If we want to specify a States.ALL retrier, it must appear alone and as the last retrier.

Summary

We’ve seen how easy is to deal with errors and retries using Step Functions and, as always, how the serverless framework help us in deploying them.

In the last article we’ve seen how to the parallel state in a State function. In this article we’ll see how we can use the Wait state using the serverless framework.

The wait state delays the execution of the state function for a certain amount of time. By default, it returns the same object that it receives.

What are we going to code

We are going to code the following state function step function with parallel state

As you can see we’re going to have an initial function that creates a result with a field called DelaySeconds. Then, we’ll have the wait state and finally a result state that will format the output.

Coding the lambdas

Following the steps of the following article, create two lambdas called InitLambda and ResultLambda with the following code:

InitLambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class InitLambda
{
    public InitResult Init(string input){
        return new InitResult(int.Parse(input));
    }
}
    
public class InitResult{

    public InitResult(int delay)
    {
        DelaySeconds = delay;
    }
    public int DelaySeconds {get;set;}
}

ResultLambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ResultLambda
{
    public string Result(InitResult input){
        return $"The seconds delayed are {input.DelaySeconds}";
    }
}

public class InitResult{

    public InitResult(int delay)
    {
        DelaySeconds = delay;
    }
    public int DelaySeconds {get;set;}
}

// You can have the result class in a shared library

Creating the step function

Now it’s time to create the step function. The code is very similar to our original code, but we’re now introducing a new kind of state. Let’s put here the interesting bits:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
stepFunctions:
    stateMachines:
        testParallelStepFunction:
            definition:
                StartAt: Init
                States:
                    Init:
                        Type: Task
                        Resource: arn:aws:lambda:${opt:region}:${self:custom.accountId}:function:${self:custom.initService}-${opt:stage}-Init
                        Next: WaitSeconds
                    WaitSeconds:
                        Type: Wait
                        Seconds: 10
                        Next: Result
                    Result:
                        Type: Task
                        Resource: arn:aws:lambda:${opt:region}:${self:custom.accountId}:function:${self:custom.resultService}-${opt:stage}-Result
                        End: true

As you can see we have a new task state called WaitSeconds which is of type Wait. In this first case we are specifying that we want to wait 10 seconds. Let’s run the step function from the UI and see if it waits the desired time.

wait 10 seconds

It works!

Let’s see which other alternatives do we have.

Specifying a timestamp

It can be possible that we need a step to be executed at a certain time. If we want that, we can specify the timestamp field:

1
2
3
4
WaitSeconds:
    Type: Wait
    Timestamp: "2017-06-20T20:58:00Z"
    Next: Result

The timestamp, as the documentation says, must conform to the RFC3339 profile of ISO 8601, with the further restrictions that an uppercase T must separate the date and time portions, and an uppercase Z must denote that a numeric time zone offset is not present. In our case, we’re saying that we want to wait until 2017/06/20 20:58 UTC.

Let’s deploy and execute the step function from the UI to see if it works:

wait timestamp

Non-hardcoded duration

We don’t need to always hardcode the value of the duration or the timestamp. We can use a path from the state’s input data to specify. If we want to do that, we need to specify the state in this way:

1
2
3
4
WaitSeconds:
    Type: Wait
    SecondsPath: "$.DelaySeconds"
    Next: Result

In our case, we’re going to use the field DelaySeconds of the input data to read the amount of seconds we want to wait. We can do the same thing with the timestamp using the field TimestampPath.

Summary

We’ve seen another possible state that you can use when defining a State Function: the wait step. We’ve seen how we configure this kind of step in four different ways.

In the last article we’ve seen how to create a very basic step function using .Net Core and the serverless framework. Today we’ll see how to create one of the more useful states in a Step Function: the parallel state.

The parallel state allows you to create parallel branches of execution in your state machine. Using it, you’ll be able to run several tasks in parallel and then collect the results in another task, that will be executed only if all the parallel tasks finish correctly.

The task that collects the results of the parallel tasks will receive an array with all the results. The only limitation we have using .Net Core is that the results must be of the same type, so we can have an array of that type. I imagine you can do fancier things using Javascript.

What are we going to code

We are going to code the following state function step function with parallel state

As you can see we’re going to have an initial function that makes a simple transformation of the input, two parallel functions and a function that collects the results.

Coding the lambdas

Following the steps of the previous article, create four lambdas called InitLambda, ParallelOneLambda, ParallelTwoLambda and ResultLambda with the following code:

InitLambda

1
2
3
4
5
6
public class InitLambda
{
    public string Init(string input){
        return $"Start processing {input}";
    }
}

ParallelOneLambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ParallelOneLambda
{
   public ParallelResult ParallelOne(string request)
   {
       return new ParallelResult(DateTime.UtcNow, $"Parallel one output: {request}");
   }
}

public class ParallelResult
{
    public ParallelResult(DateTime dateTime, string result)
    {
        DateTime = dateTime;
        Result = result;
    }

    public DateTime DateTime {get; set;}
    public string Result {get;set;}
}

ParallelTwoLambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ParallelTwoLambda
{
   public ParallelResult ParallelTwo(string request)
   {
       return new ParallelResult(DateTime.UtcNow, $"Parallel two output: {request}");
   }
}

public class ParallelResult
{
    public ParallelResult(DateTime dateTime, string result)
    {
        DateTime = dateTime;
        Result = result;
    }

    public DateTime DateTime {get; set;}
    public string Result {get;set;}
}

// You can have the result class in a shared library

ResultLambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ResultLambda
{
    public string Result(ParallelResult[] input){
        return $"The result of the first task is {input[0].Result} and the result of the second task is {input[1].Result}";
    }
}

public class ParallelResult
{
    public ParallelResult(DateTime dateTime, string result)
    {
        DateTime = dateTime;
        Result = result;
    }

    public DateTime DateTime {get; set;}
    public string Result {get;set;}
}

// You can have the result class in a shared library

As you can see, the result lambda receives an array or ParallelResult.

Creating the step function

Now it’s time to create the step function. The code is very similar to our original code, but we’re now introducing a new kind of state. Let’s put here the interesting bits:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
stepFunctions:
    stateMachines:
        testParallelStepFunction:
            definition:
                StartAt: Init
                States:
                    Init:
                        Type: Task
                        Resource: arn:aws:lambda:${opt:region}:${self:custom.accountId}:function:${self:custom.initService}-${opt:stage}-Init
                        Next: ParallelProcessing
                    ParallelProcessing:
                        Type: Parallel
                        Branches:
                        - StartAt: ParallelOne
                            States:
                            ParallelOne:
                                Type: Task
                                Resource: arn:aws:lambda:${opt:region}:${self:custom.accountId}:function:${self:custom.parallelOneService}-${opt:stage}-ParallelOne
                                End: true
                        - StartAt: ParallelTwo
                            States:
                            ParallelTwo:
                                Type: Task
                                Resource: arn:aws:lambda:${opt:region}:${self:custom.accountId}:function:${self:custom.parallelTwoService}-${opt:stage}-ParallelTwo
                                End: true
                        Next: Result
                    Result:
                        Type: Task
                        Resource: arn:aws:lambda:${opt:region}:${self:custom.accountId}:function:${self:custom.resultService}-${opt:stage}-Result
                        End: true

As you can see we have a new task state called ParallelProcessing which is of type Parallel and has two branches: ParallelOne and ParallelTwo. The branches follow the state machine definition and, in this case, have only a task inside them.

We can now deploy the lambdas and the step function and invoke it:

1
sls invoke stepf --name testParallelStepFunction --data '"asdf"'

And see that we have the expected result:

step function result

A couple of remarks

A branch can be a state machine by itself

A branch is not limited to have just one task inside it. It can have several tasks. The only limitation is that the last task must have a final one (have

1
End:true
)

branch with several tasks

The results always come in order

The order of the results in the array on the step after a parallel step always come in the same order, and that order is the order that the branches are defined. So, although ParallelOne ends later than ParallelTwo, the result of ParallelOne will come in the first position of the array.

Summary

We’ve seen one of the more powerful states of a State Function, the parallel step. We’ve seen how we can add more than one task inside each of its branches and how to collect the results.

If my good friend Alan Gorton is right

we’d better be prepared. In this article we’ll see how we can develop AWS Lambda Functions using .Net Core and deploy them and Step Functions using the serverless framework.

Installing dependencies

You should be able to follow this tutorial using a Windows machine or a Mac. The first step is to install all the things we’re going to need.

Let’s start with .Net Core. The limitation that AWS imposes is to target netcoreapp1.0,so we can just download the last version of .Net Core. To do that, go to https://www.microsoft.com/net/core and follow the instructions.

The next step is to install the serverless framework. You will need to have NodeJs installed and then follow the instructions they have in their website. It’s very straightforward.

And we’re good to go! Nothing else is needed.

Accessing AWS

To be able to run the serverless framework commands we’ll need an account with its access keys. There are a couple of ways to configure them in your environment as it’s explained in https://serverless.com/framework/docs/providers/aws/guide/credentials/ In my case, I’m going to use a profile. You can use whatever you want.

Folder structure

We’re going to have a root folder that will contain the different folders for each lambda function. The only file that will exist in that folder will be the serverless.yml for the step function. So, go ahead and create the root folder

1
mkdir TestServerlessStepFunctions

We’re now ready to create our first lambda function.

Creating a lambda function

The serverless framework allows you to create a project scaffolding for your lambda project. It has a couple of limitations though:

  • It creates a project that you need to work with it with the version 1.0.0-preview2-003131 of the .Net Core SDK. That’s not the last version, is the version that still uses the project.json file.
  • It doesn’t allow you to create an F# project.

I’ll send a pull request to fix both problems, but it’s not there yet.

Apart from some utility files, the framework only adds a file to an standard project, so we can create the project by ourselves using the .Net Core CLI and manually add that file. Let’s do that:

1
dotnet new classlib --framework netcoreapp1.0 --name UppercaseLambda --output UppercaseLambda

This creates a new C# project targeting netcoreapp1.0 called UppercaseLambda in a folder called UppercaseLambda. That’s all we need.

Rename the cs file to UppercaseLambda.cs and copy the following content there:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using Amazon.Lambda.Core;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializerAttribute(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]

namespace TestServerlessStepFunctions
{
    public class UppercaseLambda
    {
        public string Uppercase(string request)
        {
            return request.ToUpperInvariant();
        }
    }
}

In order to be able to build this project, we’ll need to add a package. So, go to the project folder and run:

1
dotnet add package Amazon.Lambda.Serialization.Json

We’re good to go now. Let’s restore the dependencies

1
dotnet restore

dotnet restore

Build the project

1
dotnet build

dotnet build

And publish it

1
dotnet publish

dotnet publish

What we need to be able to publish the lambda function is to create a zip file with all the files needed to execute the function. We’ll need to create the zip in a folder we can reference later, so let’s create the folder and the zip:

1
2
mkdir bin/Debug/netcoreapp1.0/package
zip -Xrj bin/Debug/netcoreapp1.0/package/UpperCaseLambda.zip bin/Debug/netcoreapp1.0/publish/

zip

As you can see, we’re creating a zip with the contents of the publish folder.

We’re just one step away to be able to deploy our Lambda function. If we want to use the serverless framework to do it, we need to create a file called serverless.yml in the package folder. Create the file and copy the following contents:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
service: uppercaseService

provider:
    name: aws
    runtime: dotnetcore1.0
    profile: serverless-admin-vgaltes
    region: us-east-1
    stage: dev

package:
    artifact: bin/Debug/netcoreapp1.0/package/UpperCaseLambda.zip

functions:
    Uppercase:
        handler: UppercaseLambda::TestServerlessStepFunctions.UppercaseLambda::Uppercase

If we use different yaml files for each lambda functions, we needd to specify a different service name for each lambda. In the provider section, we’re telling the framework which provider we’re going to use, runtime, user profile, region and stage. Stages are a good way to manage different environments inside the same AWS account.

In the package section we’re specifing which package we’re going to deploy.

And finally, we’re going to define our functions, in our case just one of them. In the handler, we’re going to specify where the function lives, in our case in the UppercaseLambda file, in a class called UppercaseLambda inside a namespace called TestServerlessStepFunctions and the method is called Uppercase.

We’re prepared to deploy the function. We just need to run the following command:

1
sls deploy -v

sls deploy uppercase

The -v flag is to have a verbose output. If everything is configured well we’re going to see how the lambda is correctly deployed in the account.

We can now try the lambda to see if it’s been correctly deployed. To do that, just run

1
sls invoke -f Uppercase --data "asdf"

We should see “ASDF” as the output.

sls invoke uppercase

A basic step function

Now that we have a lambda function, we can use it inside a Step Function. A Step Function is a nice way to orchestrate Lambda functions. You can learn more here https://aws.amazon.com/step-functions/

To be able to deploy a Step Function using the serverless framework we’ll need to install a plugin. So, lets go to the root folder and type:

1
npm install --save serverless-step-functions

The step function will not have any code associated to it. It will just call the Lambdas we create. So, we just need to create a serverless.yml file in the root folder. Create the file and copy the following contents:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
service: TestStepFunctions

custom:
    accountId: <your_account_id>
    uppercaseService: uppercaseService

provider:
    name: aws
    runtime: dotnetcore1.0
    profile: serverless-admin-vgaltes
    region: us-east-1
    stage: dev

stepFunctions:
    stateMachines:
        testStepFunction:
            definition:
                StartAt: Uppercase
                States:
                    Uppercase:
                        Type: Task
                        Resource: arn:aws:lambda:${opt:region}:${self:custom.accountId}:function:${self:custom.uppercaseService}-${opt:stage}-Uppercase
                        End: true

plugins:
- serverless-step-functions

Notice that, as we explained previously, we’re using a different service name here. In the custom section we’re defining some custom variable that we’ll use to properly reference the Lambda function. The meat of the file is in the stepFunctions sections. We’re defining there a new state machine called testStepFunction that has a single state, called Uppercase, which is a Task. In the resource, we’re referencing the arn of the lambda function we’ve created previously. To do that, we need to use the custom variables previously declared.

Finally, but very important, we need to tell the framework that we’re using the step functions plugin.

And that’s all! We can now deploy the Step function. Just type:

1
sls deploy -v

sls deploy step function

We should see the step function deployed correctly. It’s time to invoke the Step Function and see it working:

1
sls invoke stepf --name testStepFunction --data '"asdf"'

sls invoke step function

Voilà!!

A bit of FSharp

We’ve seen how to develop a Lambda function using C#. As we’ve seen, at the end we’re compiling the code and publishing it so we can do the same with an F# project. Let’s do it!

First of all, let’s create a new F# project. Go to your root folder and type

1
dotnet new classlib -lang F# --name SayHelloLambda --output SayHelloLambda

Unfortunately you can’t change the targeted framework using the CLI. So, go to the SayHelloLambda.fsproj file and change the target framework to netcoreapp1.0

As we did with the C# project, we need to add the assembly attribute to our module. Let’s start adding the required package:

1
dotnet add package Amazon.Lambda.Serialization.Json

Now you can change the contents of the fs file (you can rename it as well if you want). Copy the following contents:

1
2
3
4
5
6
7
8
9
namespace TestServerlessStepFunctions

module SayHelloLambda =
    open Amazon.Lambda.Core
    [<assembly: LambdaSerializerAttribute(typeof<Amazon.Lambda.Serialization.Json.JsonSerializer>)>]
    do()

    let sayHello name =
        sprintf "Hello %s" name

As you can see we’re composing a new string as a return value of our Lambda. We can now build and deploy the project:

1
2
3
4
5
dotnet restore
dotnet build
dotnet publish
mkdir bin/Debug/netcoreapp1.0/package
zip -Xrj bin/Debug/netcoreapp1.0/package/SayHelloLambda.zip bin/Debug/netcoreapp1.0/publish/

We just need to add the serverless.yml file. Copy the following contents:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
service: sayHelloService

provider:
    name: aws
    runtime: dotnetcore1.0
    profile: serverless-admin-vgaltes
    region: us-east-1
    stage: dev

package:
    artifact: bin/Debug/netcoreapp1.0/package/SayHelloLambda.zip

functions:
    SayHello:
        handler: SayHelloLambda::TestServerlessStepFunctions.SayHelloLambda::sayHello

Nothing new here. We’re ready to deploy now. Let’s do it:

1
sls deploy -v

Our Lambda function developed in F# is deployed now. Let’s try it:

1
sls invoke -f SayHello --data "Vicenç"

sls invoke sayHello

Great! The F# Lambda function is working now! Time to add the lambda to our Step function. Let’s start adding a new custom variable:

1
2
3
4
custom:
    accountId: <your_account_id>
    uppercaseService: uppercaseService
    sayHelloService: sayHelloService

And now add the new Lambda to the Step function and link the execution of the previous one to this one:

1
2
3
4
5
6
7
8
9
10
11
12
testStepFunction:
  definition:
    StartAt: Uppercase
    States:
      Uppercase:
        Type: Task
        Resource: arn:aws:lambda:${opt:region}:${self:custom.accountId}:function:${self:custom.uppercaseService}-${opt:stage}-Uppercase
        Next: SayHello
      SayHello:
        Type: Task
        Resource: arn:aws:lambda:${opt:region}:${self:custom.accountId}:function:${self:custom.sayHelloService}-${opt:stage}-SayHello
        End: true

And that’s all we need. You can now deploy the step function:

1
sls deploy -v

And test it:

1
sls invoke stepf --name testStepFunction --data '"asdf"'

You should see “Hello ASDF” as output.

sls invoke step function

Summary

We’ve seen quite a few things in this article. We’ve discovered Step functions, which are a nice way to orchestrate Lambda functions. We’ve seen as well how we can develop Lambda functions using C# or F# thanks to .Net Core. As a side effect, we’ve seen how we can develop all of this using a Mac laptop and not a Windows machine. Hope you enjoyed it!