How to Get AWS Step Functions to Act on a Lambda Variable’s Value

In my previous post I explained the basics of how to pass variable values from one state machine Lambda function to another. But what if you want to have AWS Step Functions take some sort of action based on the value of one of those variables? Perhaps you want the Step Function to pause until an instance is finished shutting down, or a snapshot has finished copying, etc. If you’re not a moderately experienced programmer you may not be familiar with this, and since Amazon’s documentation on these sort of “basics” is rather sparse, I thought I’d explain it here.

For the sake of argument, let’s say you have a Lambda function in your Step Function that does some stuff to an EC2 instance and then shuts it down. You don’t want the Step Function to do anything else to this instance (e.g., snapshot it) until it has completely stopped, so you may write some Python 3 code that looks something like this:

    print("Checking to see if instance has stopped yet...")
    available = 0
    ec2 = boto3.client('ec2', region_name=region)
    image = ec2.describe_instances(InstanceIds=[instid])
    print("Current state is: ", str(image['Reservations'][0]['Instances'][0]['State']['Name']))
    if image['Reservations'][0]['Instances'][0]['State']['Name'] == 'stopped':
        available = 1
        if available == 1:
            print("The instance has stopped.")

    # Pass along variables as an array to what runs this function next:
    nextstatevars = [currenttime, instid, region, nametag, application, stack, available]
    return nextstatevars

At the end of the second-to-last line you can see I’m returning the “available” variable to the Step Function. It starts out with a value of 0 until the instance’s status changes, when it then sets the value to 1.

On the Step Function side, when we create it in our CloudFormation template the relevant code will look like this:

                        "\"StartAt\": \"Step1-StopEC2\",\n",
                         "\"States\" : {\n",
                         "    \"Step1-StopEC2\" : {\n",
                         "        \"Type\" : \"Task\", \n",
                         "        \"Resource\" : \"",{ "Fn::GetAtt": ["LambdaStep1", "Arn"] },"\", \n",
                         "        \"Next\" : \"Step1b-CheckEC2\"\n",
                         "    },\n",
                         "    \"Step1b-CheckEC2\" : {\n",
                         "        \"Type\" : \"Task\", \n",
                         "        \"Resource\" : \"",{ "Fn::GetAtt": ["LambdaStep1b", "Arn"] },"\", \n",
                         "        \"Next\" : \"Is_Instance_Stopped_Yet?\"\n",			 
                         "    },\n",
                         "    \"Is_Instance_Stopped_Yet?\": {\n",
                         "      \"Type\" : \"Choice\",\n",
                         "      \"Choices\": [\n",
                         "        {\n",
                         "         \"Variable\": \"$[6]\",\n",
                         "          \"NumericEquals\": 0,\n",
                         "          \"Next\": \"Step1b-wait_10_secs\"\n",
                         "        },\n",
                         "        {\n",
                         "          \"Variable\": \"$[6]\",\n",
                         "          \"NumericEquals\": 1,\n",
                         "          \"Next\": \"Step2-CreateSnap\"\n",
                         "        }\n",
                         "      ],\n",
                         "      \"Default\": \"Step1b-wait_10_secs\"\n",
                         "    },\n",
                         "    \"Step1b-wait_10_secs\": {\n",
                         "      \"Type\": \"Wait\",\n",
                         "      \"Seconds\": 10,\n",
                         "      \"Next\": \"Step1b-CheckEC2\"\n",
                         "    },\n",

The above code tells the Step Function to run a Lambda function that stops the EC2 instance, then it runs the function that checks the instance’s state (mentioned above), then it introduces a wait condition which says that if the value of the 6th variable returned from the previous function equals 0, then wait 10 more seconds and try again. If it is equal to 1, then run the next Lambda function. It looks at the 6th variable because the list of variables are an array, and arrays begin counting at 0, so the 7th variable I returned is #6 in the array.

I felt the need to write this post because I too was scratching my head on this one at first. Even though there’s a really helpful page on Amazon States Language that talks about more elaborate versions of this, like using paths & reference paths, etc., I had a hard time finding something documenting this simple case. Luckily I figured it out myself, but since there’s not a lot of info about these more simple cases out there, and not everyone knows something about coding, hopefully this post will help you out if you’re searching for the same thing I was. Step Functions should be something anyone can take advantage of, even if you’re not a developer! :)