Step Functions Lab

AWS Step Functions makes it easy to coordinate the components of distributed applications and microservices using visual workflows.

In this lab, let's build a state machine to orchestrate a tax microservice.

Step 1 - Console

Let's now take this for a quick spin

{
  "Who": "Big Daddy!"
}

We have now successfully created and executed our very first (albeit, very simple) State Machine using AWS Step Functions and Lambda.

Step 2 - Create Lambda Functions

Let's take this a bit further and explore more complex state machines and how we'd go about building them. Before we go ahead though, we'll need a few Lambda functions to execute at various stages in the State machine. So let's create a few additional Lambda functions (these will serve as our rudimentary mico-services).

Once we're done creating our Lambda functions, we will create a state machine using Step Functions to orchestrate them.

Step 2.1 - Simple Tax Calculator

You can use the previously created SimpleTaxCalculator Lambda function here, no need to create a new one. But I'm adding the code here anyway.

Ensure that you pick the one that's yours: SimpleTaxCalculator-[YOUR_ORG_ID]

'use strict';

console.log('Loading tax calculator function...');

exports.handler = (event, context, callback) => {
    console.log('Received event: ', JSON.stringify(event, null, 2));
    
    try {
        console.log(`Product price: $${event.productPrice}`);
        console.log(`Tax rate: ${event.taxRate}%`);
        console.log(`Surcharge rate: ${event.surchargeRate}%`);
        
        let tax = event.productPrice * (event.taxRate / 100.00);
        let surcharge = event.productPrice * (event.surchargeRate / 100.00);
        let finalPrice = event.productPrice + tax + surcharge;

        console.log(`Final price with tax: $${finalPrice}`);
        
        let returnValue = {
            price: finalPrice
        }

        // On success, invoke the callback like so (2 arguments)
        // first one being null.
        callback(null, returnValue); // Return calculated tax
    }
    catch(e) {
        console.log(e);
        
        // On failure, invoke the callback like so (a single argument)
        // with a helpful error message.
        callback('ERROR: Something went wrong!');
    }
    
    console.log("Done calculating tax.");
};

Step 2.2 - California Tax Calculator

Follow the same steps as before to create a brand new Lambda function. Let's name this CaliforniaTaxCalculator-[YOUR_ORG_ID]. Just ensure that you add the following values to each of the fields in the Create Lambda screen.

Name: CaliforniaTaxCalculator
Runtime*: Node.js 6.10
Role*: Choose an existing role
Existing Role*: lambda_execution_role_YOUR_ORG_ID

Once created, edit the code inline and add this:

'use strict';

console.log('Loading California tax calculator function...');

const CA_STATE_TAX_RATE = 20;

exports.handler = (event, context, callback) => {
    console.log('Received event: ', JSON.stringify(event, null, 2));
    
    try {
        console.log(`Product price: $${event.price}`);
        console.log(`Calculating California state taxes at ${CA_STATE_TAX_RATE} rate`)

        let stateTax = event.price * (CA_STATE_TAX_RATE / 100.00);
        let finalPrice = event.price + stateTax;

        console.log(`Final price with tax: $${finalPrice}`);

        // On success, invoke the callback like so (2 arguments)
        // first one being null.
        callback(null, finalPrice); // Return calculated tax
    }
    catch(e) {
        console.log(e);
        
        // On failure, invoke the callback like so (a single argument)
        // with a helpful error message.
        callback('ERROR: Something went wrong!');
    }
    
    console.log("Done calculating California tax.");
};
{
  "price": 20
}

Step 2.3 - Wisconsin Tax Calculator

Follow the same steps and add a WisconsinTaxCalculator-[YOUR_ORG_ID].

Use the same execution role as before. All of our Lambda functions will use the same execution role when running.

Edit the code inline and update it to the below:

'use strict';

console.log('Loading Wisconsin tax calculator function...');

const WI_STATE_TAX_RATE = 10; // cheaper than California

exports.handler = (event, context, callback) => {
    console.log('Received event: ', JSON.stringify(event, null, 2));
    
    try {
        console.log(`Product price: $${event.price}`);
        console.log(`Calculating Wisconsin state taxes at ${WI_STATE_TAX_RATE} rate`)

        let stateTax = event.price * (WI_STATE_TAX_RATE / 100.00);
        let finalPrice = event.price + stateTax;

        console.log(`Final price with tax: $${finalPrice}`);

        // On success, invoke the callback like so (2 arguments)
        // first one being null.
        callback(null, finalPrice); // Return calculated tax
    }
    catch(e) {
        console.log(e);
        
        // On failure, invoke the callback like so (a single argument)
        // with a helpful error message.
        callback('ERROR: Something went wrong!');
    }
    
    console.log("Done calculating Wisconsin tax.");
};
{
  "price": 20
}

Step 3 - Modify Step Function

Let's modify the new Step Function that we just created in Step 1 to use the Lambda functions that we just created.

{
  "Comment": "A more complicated tax calculator",
  "StartAt": "SimpleTaxCalculator",
  "States": {
    
    "SimpleTaxCalculator": {
      "Type": "Task",
      "Resource": "[MOVE CURSOR HERE TO SEE OPTIONS]",
      "Next": "ChooseState"
    },
    
    "ChooseState": {
      "Type" : "Choice",
      "Choices": [
        {
          "Variable": "$.state",
          "StringEquals": "CA",
          "Next": "CaliforniaTaxCalculator"
        },
        {
          "Variable": "$.state",
          "StringEquals": "WI",
          "Next": "WisconsinTaxCalculator"
        }
      ],
      "Default": "InvalidState"
    },

    "CaliforniaTaxCalculator": {
      "Type" : "Task",
      "Resource": "[MOVE CURSOR HERE TO SEE OPTIONS]",
      "End": true
    },

    "WisconsinTaxCalculator": {
      "Type" : "Task",
      "Resource": "[MOVE CURSOR HERE TO SEE OPTIONS]",
      "End": true
    },

    "InvalidState": {
      "Type": "Fail",
      "Error": "Cannot calculate state tax!",
      "Cause": "Invalid State!"
    }
  }
}
{
  "productPrice": 20,
  "taxRate": 10,
  "surchargeRate": 1
}


You will notice that the Step Function just failed. Can you figure out why?

Expand the result of each state and dig in...

Step 4 - Fix Step Function

Now that we've identified the issue, let's fix this by updating the original code for SimpleTaxCalculator.

    state: "CA"
let returnValue = {
    price: finalPrice,
    state: "CA"
}

Step 5 - Execute State Machine Again

{
  "productPrice": 20,
  "taxRate": 10,
  "surchargeRate": 1
}

This time, notice that the state machine branched off to the correct states and finished executing successfully.

Extra Credits

For this, let's deliberately update the State Machine Code to something invalid and see what kind of errors we get.

To do this, go to the Step Functions console to edit the State Machine. Refer to the instructions in Step 3 for the full monty.

Update the Stat Machine Code to the below:

{
  "Comment": "A more complicated tax calculator",
  "StartAt": "SimpleTaxCalculator",
  "States": {
    
    "SimpleTaxCalculator": {
      "Type": "Task",
      "Resource": "[MOVE CURSOR HERE TO SEE OPTIONS]",
      "Next": "FirstState"
    },
    
    "ChooseState": {
      "Type" : "Choice",
      "Choices": [
        {
          "Variable": "$.state",
          "StringEquals": "CA",
          "Next": "CaliforniaTaxCalculator"
        },
        {
          "Variable": "$.state",
          "StringEquals": "WI",
          "Next": "WisconsinTaxCalculator"
        }
      ],
      "Default": "InvalidState"
    },

    "CaliforniaTaxCalculator": {
      "Type" : "Task",
      "Resource": "[MOVE CURSOR HERE TO SEE OPTIONS]",
      "End": "true"
    },

    "WisconsinTaxCalculator": {
      "Type" : "Task",
      "Resource": "[MOVE CURSOR HERE TO SEE OPTIONS]",
      "End": true
    },

    "InvalidState": {
      "Type": "Fail",
      "Error": "Cannot calculate state tax!",
      "Cause": "Invalid State!"
    }
  }
}


Can you figure out why?

States Language Reference

See here for more information on how to define StepFunctions using Amazon States Language