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:
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.
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.
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.
For example:
{
"count": 4,
"startPos": 1,
"endPos": 3,
"cities": [
{
"city": {
"name": "New York",
"lat": 40.71,
"long": -74
}
},
(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:
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.)
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:
{
“count”: 4,
“startPos”: 2,
“endPos”: 3,
“cities”: [
{
“city”: {
“name”: “New York”,
“lat”: 40.71,
“long”: -74
}
},
{
“city”: {
“name”: “London”,
“lat”: 120.82,
“long”: -97.1
}
},
{
“city”: {
“name”: “Auckland”,
“lat”: -127.45,
“long”: 56.78
}
},
{
“city”: {
“name”: “Tokyo”,
“lat”: -12.98,
“long”: 34.66
}
}
]
}
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.
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.