Update 2017-03-26: I've updated the article below to make it clearer that each separate Logic App execution instance has its own member variable instance, but that variables are shared (currently) between parallel iterations in a loop.
The latest Logic Apps refresh, on March 24th 2017, finally added support for variables.
Microsoft have taken an interesting approach with variables: unlike a BizTalk orchestration, where you define variables separately to your workflow, in Logic Apps they're defined in new Variables actions. This means that they're defined inline, similar to how you would define them in source code. There is also a new @variables('variableName') expression that can be used to obtain the value of a named variable.
There are some limitations to variables though:
- They are currently only global in scope within a Logic App execution (same value is available everywhere within that Logic App instance)
- You can currently only initialize a variable, or increment its value – you can't update the value in any other way
- Only Integer and Float values are supported
In this post, I'm going to look at how you can add variables to your Logic Apps; what you can use them for; and why there is such limited support for variables in this initial release.
How to add variables
You can initialize and increment variables using the two new Variables actions (you can find them by entering “Variable” into the action search box):
As mentioned above, there are currently only two options.
Selecting Initialize variable gives you an action that looks like this:
You can supply the variable name, give it a type (only Integer and Float supported), and give it an initial value.
Selecting Increment variable gives you an action that looks like this:
You can select an existing variable (numeric variable types only – Integer or Float – although those are the only types you can create at the mo!), and enter the amount you want to increment by.
Interestingly, you can put a negative value in here, and it will decrement by that much.
What you can do with variables
Numeric variables that can be incremented/decremented are useful for two main activities: maintaining the count in a loop; and selecting a specific value (by index) from an array.
Here's an example to show you how you could use the current numeric variable support.
Let's imagine we're sent an array of cities, as a JSON message. However, we don't want to process *all* the cities in the array: we just want to process a subset. The request includes start and end position properties that give us the subset of cities to process.
"name": "New York",
(more cities removed)
In the example above, we're going to be supplied 4 cities, but we only want to process cities 1-3.
Without variables, this is tricky to do in Logic Apps, as we have no way of knowing where we are in a loop. Logic Apps only supports 2 types of loops currently: foreach, and do-until.
foreach iterates over all items in an array; do-until will iterate until a condition is reached. Neither of these options provide us a built-in way to iterate over a subset of items in an array.
Note: there are other ways to achieve this e.g. using the @take function, but none so clean as a for loop.
But now we can do this with Variables: by creating an Index variable, setting it to 0, using a foreach loop, and incrementing the index value. Unfortunately, there's no equivalent of a for loop in LogicApps (i.e. where we can start at a given value and continue until a condition is met). So, that means we're still going to iterate over every item in the array, but we'll only process items that fall between the start and end positions.
There's one other thing we need to do: we must set our foreach loop to execute sequentially i.e. not in parallel. By default, foreach loops will execute each iteration of the loop in parallel, up to 20 executions at a time. But trying to decrement a variable in a parallel loop gives us a non-deterministic value each time.
I learnt this the hard way, as I forgot to set my foreach loop to execute in sequence (in the example below), and I couldn't work out why I was getting odd results…
Setting a foreach loop to execute in parallel is as simple as adding "operationOptions": "Sequential" to the foreach definition. However, we currently need to do this from the code view as there's no UI option to do this:
A For-Loop Example
Let's create an example logic app that shows this.
We'll take in the JSON message from above, which gives us a start position, and an end position, and then an array. And then we'll initialize an Index variable to 0:
And then we'll create a foreach loop. Because we don't want to process all the items in the array, just those that are between the start/end positions, we'll use a condition.
The condition we use needs to ensure that the current index is greater than or equal to the start position, and less than or equal to the end position. We'll use the advanced editor and write it manually like this:
@and(greaterOrEquals(variables('index'), triggerBody()?['startPos']),lessorequals(variables('index'), triggerBody()?['endPos']))
Notice the use of the new variables expression.
If we are between start/end then we'll send the city object to a service bus queue; if we're outside the start/end position, then we do nothing. And then we'll use the Increment variable action to increment the index variable by 1.
The for-each action looks like this:
We can test our Logic App by submitting a message to it using Postman, and if things go correctly, we should end up with 3 messages in our Service Bus queue:
Which is exactly what we get! (Note that I passed in the current Index variable value as the SessionId so I could debug.)
How about decrementing?
We can test that by initializing the index variable to the count of array items; and using “-1” as our increment value:
I tested using this message:
"name": "New York",
I expected to get 2 more messages to my Service Bus queue, which is what I saw:
So, there we go, successful first test of using variables.
Why such limited support for variables?
One of my clients asked why we can't yet set variable values i.e. what use are variables if you can only initialize them. And where's support for string variables?
As the Logic Apps team mentioned in their last webcast, support for more variable types, and support setting a variable value, are coming.
But I can appreciate this is a tricky thing to do.
For example, let's say you have a foreach loop, and you want to set a variable value on each iteration. How does that work if your loop is executing in parallel (the default)? Remember that the current variable action defines a variable that is effectively static within loop iterations i.e. all parallel runs that are spawned from a loop will have access to the same variable instance.
I suspect the product group will need to either find a way to scope variables (i.e. making changes to a variable in a particular execution in a loop doesn't affect any other instances of that variable); or they may implement a version of the C# lock() keyword, so that only one thread can update the global variable at a time.
Note that this doesn't mean that separate triggered executions of your Logic App share the same variable value: this only applies to parallel iterations in loops.
We'll have to wait and see.
In any case, I hope this helps with your understanding of how to use variables in a Logic App. I'll update this article, as more variable functionality is released.