Create loops that repeat workflow actions or process arrays in Azure Logic Apps

Applies to: Azure Logic Apps (Consumption)

To process an array in your logic app workflow, you can create a For each loop. This loop repeats one or more actions on each item in the array. For the limit on the number of array items that a "For each" loop can process, see Concurrency, looping, and debatching limits.

To repeat actions until a condition gets met or a state changes, you can create an Until loop. Your workflow first runs all the actions inside the loop, and then checks the condition or state. If the condition is met, the loop stops. Otherwise, the loop repeats. For the default and maximum limits on the number of "Until" loops that a workflow can have, see Concurrency, looping, and debatching limits.

Tip

If you have a trigger that receives an array and want to run a workflow for each array item, you can debatch that array with the SplitOn trigger property.

Prerequisites

"For each" loop

A "For each" loop repeats one or more actions on each array item and works only on arrays. Here are some considerations when you use "For each" loops:

  • The "For each" loop can process a limited number of array items. For this limit, see Concurrency, looping, and debatching limits.

  • By default, iterations in a "For each" loop run at the same time, or in parallel. This behavior differs from Power Automate's Apply to each loop where iterations run one at a time, or sequentially. However, you can set up sequential "For each" loop iterations. For example, if you want to pause the next iteration in a "Foreach" loop by using the Delay action, you need to set the loop to run sequentially.

    The exception to the default behavior are nested loops where iterations always run sequentially, not in parallel. To run operations in parallel for items in a nested loop, create and call a child logic app workflow.

  • To get predictable results from operations on variables during each loop iteration, run those loops sequentially. For example, when a concurrently running loop ends, the increment, decrement, and append to variable operations return predictable results. However, during each iteration in the concurrently running loop, these operations might return unpredictable results.

  • Actions in a "For each" loop use the @item() expression to reference and process each item in the array. If you specify data that's not in an array, the logic app workflow fails.

This example logic app workflow sends a daily summary for a website RSS feed. The workflow uses a "For each" loop that sends an email for each new item.

  1. Create this example Consumption logic app workflow with an Outlook.com account or a work or school account.

  2. Between the RSS trigger and send email action, add a "For each" loop.

    1. To add a loop between steps, move your pointer over the arrow between those steps. Select the plus sign (+) that appears, then select Add an action.

      Select "Add an action"

    2. Under the search box, select All. In the search box, enter for each. From the actions list, select the Control action named For each.

      Add "For each" loop

  3. Now build the loop. Under Select an output from previous steps after the Add dynamic content list appears, select the Feed links array, which is output from the RSS trigger.

    Select from dynamic content list

    Note

    You can select only array outputs from the previous step.

    The selected array now appears here:

    Select array

  4. To run an action on each array item, drag the Send an email action into the loop.

    Your workflow might look something like this example:

    Add steps to "Foreach" loop

  5. Save your workflow. To manually test your logic app, on the designer toolbar, select Run Trigger > Run.

"Foreach" loop definition (JSON)

If you're working in code view for your logic app, you can define the Foreach loop in your logic app's JSON definition instead, for example:

"actions": {
   "myForEachLoopName": {
      "type": "Foreach",
      "actions": {
         "Send_an_email": {
            "type": "ApiConnection",
            "inputs": {
               "body": {
                  "Body": "@{item()}",
                  "Subject": "New CNN post @{triggerBody()?['publishDate']}",
                  "To": "me@contoso.com"
               },
               "host": {
                  "api": {
                     "runtimeUrl": "https://logic-apis-westus.azure-apim.net/apim/office365"
                  },
                  "connection": {
                     "name": "@parameters('$connections')['office365']['connectionId']"
                  }
               },
               "method": "post",
               "path": "/Mail"
            },
            "runAfter": {}
         }
      },
      "foreach": "@triggerBody()?['links']",
      "runAfter": {}
   }
}

"Foreach" loop: Sequential

By default, cycles in a "Foreach" loop run in parallel. To run each cycle sequentially, set the loop's Sequential option. "Foreach" loops must run sequentially when you have nested loops or variables inside loops where you expect predictable results.

  1. In the loop's upper right corner, choose ellipses (...) > Settings.

    On "Foreach" loop, choose "..." > "Settings"

  2. Under Concurrency Control, turn the Concurrency Control setting to On. Move the Degree of Parallelism slider to 1, and choose Done.

    Turn on concurrency control

If you're working with your logic app's JSON definition, you can use the Sequential option by adding the operationOptions parameter, for example:

"actions": {
   "myForEachLoopName": {
      "type": "Foreach",
      "actions": {
         "Send_an_email": { }
      },
      "foreach": "@triggerBody()?['links']",
      "runAfter": {},
      "operationOptions": "Sequential"
   }
}

"Until" loop

To run and repeat actions until a condition gets met or a state changes, put those actions in an "Until" loop. Your logic app first runs any and all actions inside the loop, and then checks the condition or state. If the condition is met, the loop stops. Otherwise, the loop repeats. For the default and maximum limits on the number of "Until" loops that a logic app run can have, see Concurrency, looping, and debatching limits.

Here are some common scenarios where you can use an "Until" loop:

  • Call an endpoint until you get the response you want.

  • Create a record in a database. Wait until a specific field in that record gets approved. Continue processing.

Starting at 8:00 AM each day, this example logic app increments a variable until the variable's value equals 10. The logic app then sends an email that confirms the current value.

Note

These steps use Office 365 Outlook, but you can use any email provider that Logic Apps supports. Check the connectors list here. If you use another email account, the general steps stay the same, but your UI might look slightly different.

  1. Create a blank logic app. In Logic App Designer, under the search box, choose All. Search for "recurrence". From the triggers list, select this trigger: Recurrence - Schedule

    Add "Recurrence - Schedule" trigger

  2. Specify when the trigger fires by setting the interval, frequency, and hour of the day. To set the hour, choose Show advanced options.

    Set up recurrence schedule

    Property Value
    Interval 1
    Frequency Day
    At these hours 8
  3. Under the trigger, choose New step. Search for "variables", and select this action: Initialize variable - Variables

    Add "Initialize variable - Variables" action

  4. Set up your variable with these values:

    Set variable properties

    Property Value Description
    Name Limit Your variable's name
    Type Integer Your variable's data type
    Value 0 Your variable's starting value
  5. Under the Initialize variable action, choose New step.

  6. Under the search box, choose All. Search for "until", and select this action: Until - Control

    Add "Until" loop

  7. Build the loop's exit condition by selecting the Limit variable and the is equal operator. Enter 10 as the comparison value.

    Build exit condition for stopping loop

  8. Inside the loop, choose Add an action.

  9. Under the search box, choose All. Search for "variables", and select this action: Increment variable - Variables

    Add action for incrementing variable

  10. For Name, select the Limit variable. For Value, enter "1".

    Increment "Limit" by 1

  11. Outside and under the loop, choose New step.

  12. Under the search box, choose All. Find and add an action that sends email, for example:

    Add action that sends email

  13. If prompted, sign in to your email account.

  14. Set the email action's properties. Add the Limit variable to the subject. That way, you can confirm the variable's current value meets your specified condition, for example:

    Set up email properties

    Property Value Description
    To <email-address@domain> The recipient's email address. For testing, use your own email address.
    Subject Current value for "Limit" is Limit Specify the email subject. For this example, make sure that you include the Limit variable.
    Body <email-content> Specify the email message content you want to send. For this example, enter whatever text you like.
  15. Save your logic app. To manually test your logic app, on the designer toolbar, choose Run.

    After your logic starts running, you get an email with the content that you specified:

    Received email

Prevent endless loops

The "Until" loop stops execution based on these properties, so make sure that you set their values accordingly:

  • Count: This value is the highest number of loops that run before the loop exits. For the default and maximum limits on the number of "Until" loops that a logic app run can have, see Concurrency, looping, and debatching limits.

  • Timeout: This value is the most amount of time that the "Until" action, including all the loops, runs before exiting and is specified in ISO 8601 format. For the default and maximum limits on the Timeout value, see Concurrency, looping, and debatching limits.

    The timeout value is evaluated for each loop cycle. If any action in the loop takes longer than the timeout limit, the current cycle doesn't stop. However, the next cycle doesn't start because the limit condition isn't met.

To change these limits, in the loop action, select Change limits.

"Until" definition (JSON)

If you're working in code view for your logic app, you can define an Until loop in your logic app's JSON definition instead, for example:

"actions": {
   "Initialize_variable": {
      // Definition for initialize variable action
   },
   "Send_an_email": {
      // Definition for send email action
   },
   "Until": {
      "type": "Until",
      "actions": {
         "Increment_variable": {
            "type": "IncrementVariable",
            "inputs": {
               "name": "Limit",
               "value": 1
            },
            "runAfter": {}
         }
      },
      "expression": "@equals(variables('Limit'), 10)",
      // To prevent endless loops, an "Until" loop 
      // includes these default limits that stop the loop. 
      "limit": { 
         "count": 60,
         "timeout": "PT1H"
      },
      "runAfter": {
         "Initialize_variable": [
            "Succeeded"
         ]
      }
   }
}

This example "Until" loop calls an HTTP endpoint, which creates a resource. The loop stops when the HTTP response body returns with Completed status. To prevent endless loops, the loop also stops if any of these conditions happen:

  • The loop ran 10 times as specified by the count attribute. The default is 60 times.

  • The loop ran for two hours as specified by the timeout attribute in ISO 8601 format. The default is one hour.

"actions": {
   "myUntilLoopName": {
      "type": "Until",
      "actions": {
         "Create_new_resource": {
            "type": "Http",
            "inputs": {
               "body": {
                  "resourceId": "@triggerBody()"
               },
               "url": "https://domain.com/provisionResource/create-resource",
               "body": {
                  "resourceId": "@triggerBody()"
               }
            },
            "runAfter": {},
            "type": "ApiConnection"
         }
      },
      "expression": "@equals(triggerBody(), 'Completed')",
      "limit": {
         "count": 10,
         "timeout": "PT2H"
      },
      "runAfter": {}
   }
}

Get support

Next steps