Learn how to use For Each, Loop Until, and If actions in Power Automate Desktop to build automations that handle real data — iterating over Excel rows, waiting for dynamic conditions, and branching based on field values. This is where desktop flows become genuinely powerful.

Imagine you've been handed a spreadsheet with 200 vendor invoices. Each row has a vendor name, an invoice number, and an amount. Your job is to open your company's accounting application, find each vendor, enter the invoice, and save the record — 200 times. Manually, that's a full day of mind-numbing, error-prone work. With Power Automate Desktop and a solid understanding of loops and conditionals, it's a flow you can build in an afternoon and run in minutes.
That scenario isn't unusual. Most real desktop automation isn't a single action — it's a series of decisions and repetitions. Does the file exist before we try to open it? Is the row we're looking at a header or actual data? Should we skip this record because the amount is zero? These questions get answered by conditionals (if/then logic) and loops (repeat this block of actions). Together, they are the backbone of every non-trivial automation you'll ever build. If you've already worked through getting started with Power Automate Desktop and you understand how variables work, this is the natural next step — the moment where your flows stop being simple scripts and start becoming real programs.
By the end of this lesson, you'll be able to build flows that iterate over lists and data tables, loop until a condition is satisfied, branch logic based on variable values, and combine all three to handle realistic multi-step desktop tasks.
What you'll learn:
Before diving in, you should be comfortable with the Power Automate Desktop designer — you know how to add actions, run a flow, and read error messages. You should also understand variables and data types, especially lists and data tables, since loops are almost always iterating over one of those structures. If you need a refresher, the Variables, Lists, and Data Tables in Power Automate Desktop guide is exactly the right foundation.
Before touching the designer, it's worth building mental clarity on what "flow control" actually means.
A flow without loops or conditionals runs every action exactly once, top to bottom, in a fixed sequence. That works for a narrow set of tasks: launch an app, type a value, click a button, close the app. The moment your task involves more than one record, or requires different behavior depending on what it finds, a linear flow breaks down.
Loops solve the repetition problem. Instead of copying 200 identical blocks of actions, you write the block once and tell Power Automate Desktop to execute it repeatedly — either a fixed number of times, once for each item in a collection, or until some condition becomes true.
Conditionals solve the decision problem. They let your flow examine a value at runtime and choose which path to follow. Is the status field "Paid"? Skip this row. Is the invoice amount greater than $10,000? Flag it for review. Conditionals give your automation the ability to react to what it finds, rather than blindly executing the same steps regardless of context.
Think of a loop as a conveyor belt and a conditional as a quality inspector standing beside it. The conveyor belt keeps feeding items; the inspector looks at each one and decides what to do with it.
The most common loop you'll use is For Each. Its purpose is simple: given a list or data table, execute a block of actions once for each item in that collection. The collection drives the number of iterations — you don't need to know in advance how many items there are.
To add a For Each loop, open the Power Automate Desktop designer and search for "For each" in the Actions panel on the left. Drag it onto the canvas. You'll see two things appear: the For each action and an End marker. Every action you place between these two markers is part of the loop body — the code that runs on every iteration.
The For Each action asks you for two things:
If you're iterating over a simple list — say, a list of file paths — the current item variable holds the path for that iteration. If you're iterating over a data table (which is what you get when you read from Excel), the current item variable holds an entire row object, and you access columns by name or index.
Let's build on the invoice scenario. Suppose you've used the Excel automation actions to read a spreadsheet into a data table variable called InvoiceTable. Each row has columns: VendorName, InvoiceNumber, Amount, and Status.
Here's the logical structure of a For Each loop over that table:
For each CurrentRow in InvoiceTable
Set Variable: VendorName = CurrentRow['VendorName']
Set Variable: InvoiceNum = CurrentRow['InvoiceNumber']
Set Variable: Amount = CurrentRow['Amount']
Set Variable: Status = CurrentRow['Status']
[... actions to enter data into accounting app ...]
End
On the first iteration, CurrentRow holds row 0 of the data table. On the second, row 1 — and so on until every row has been processed. You never need to know the row count; Power Automate Desktop handles the stopping condition automatically.
Tip
Data table rows returned by Power Automate Desktop are zero-indexed. Row 0 is the first data row — but if your spreadsheet had a header row and you used the "Read from Excel worksheet" action with the "First line of range contains column names" option enabled, the header is already stripped out and used as column names. You access values by column name (CurrentRow['Amount']) rather than by position, which makes your flow much easier to read and maintain.
Lists work exactly the same way. If FileList is a list of file paths returned by "Get files in folder," then:
For each FilePath in FileList
Open File: FilePath
[... process the file ...]
End
The current item (FilePath) is simply the text string of the path on each pass. No column access needed.
Sometimes you need to short-circuit a loop. Two actions let you do this:
These are almost always used inside a conditional inside the loop — which is exactly where the power of combining loops and conditionals starts to show up.
For example, if you're searching a list of employee IDs for a specific value, you can stop the moment you find it:
For each EmployeeID in EmployeeList
If EmployeeID = TargetID Then
[... do something with this record ...]
Exit Loop
End
End
Next Loop is useful for skipping bad records. If a row is missing a required value, rather than crashing or entering garbage data, you skip it:
For each CurrentRow in InvoiceTable
If CurrentRow['Amount'] = '' Then
Next Loop
End
[... process valid row ...]
End
Warning
Avoid putting Exit Loop outside of a conditional. If it runs unconditionally, your loop will always terminate after the first iteration — a frustrating bug that can be hard to spot because the flow "completes" without errors.
Power Automate Desktop also offers a plain Loop action, which repeats a block a specific number of times rather than iterating over a collection. You configure it with three values:
The loop exposes a built-in index variable (you name it) that holds the current counter value.
Loop LoopIndex from 1 to 5 step 1
[... this block runs 5 times ...]
[... LoopIndex = 1, then 2, then 3, then 4, then 5 ...]
End
This is useful when you need to retry an action a fixed number of times, or when you're building a row-by-row writer where you control the position explicitly. In practice, though, For Each over a list is more common because most real data comes in collections, not fixed counts.
Loop Until is fundamentally different from For Each and plain Loop. Instead of being driven by a collection or a counter, it keeps running until a condition you specify becomes true. This is the right tool for situations like:
The structure looks like this:
Loop
[... actions to check the condition ...]
Until SomeVariable = 'Ready'
In the Power Automate Desktop designer, you add the Loop action and set its Loop condition option to "Until," then define the condition expression at the bottom of the loop block.
Suppose your legacy reporting system takes an unpredictable amount of time to generate a PDF. It places the file in a known folder when it's done. You can't hard-code a wait time — sometimes it's 10 seconds, sometimes 90. Loop Until is the right solution:
Set Variable: ReportReady = false
Loop
Get files in folder: 'C:\Reports\Output' matching '*.pdf'
Store into: FoundFiles
If Count of FoundFiles > 0 Then
Set Variable: ReportReady = true
End
If ReportReady = false Then
Wait: 5 seconds
End
Until ReportReady = true
Each pass checks for the file. If it's not there, the flow waits 5 seconds before checking again. The moment the file appears, ReportReady becomes true and the loop exits.
Warning
Always include a wait action or some form of delay inside a Loop Until, and always plan for an exit condition that can actually become true. A Loop Until without a delay will execute thousands of times per second, hammering your CPU. A Loop Until with a condition that never resolves will run forever and lock up your flow. Consider combining Loop Until with a counter variable that exits after a maximum number of attempts, and pair this with error handling strategies to handle the timeout gracefully.
The If action evaluates a condition and executes a block of actions only when that condition is true. If the condition is false, the block is skipped entirely. This is the fundamental decision-making structure in any automation.
Power Automate Desktop supports the full conditional structure:
You build this by adding an If action, then right-clicking the End of the If block to insert an Else If or Else branch. In the designer, this looks like nested labeled sections.
If Status = 'Paid' Then
[... skip this record, mark as processed ...]
Else If Status = 'Disputed' Then
[... flag for manual review ...]
Else If Amount > 10000 Then
[... route to manager approval queue ...]
Else
[... process normally ...]
End
The conditions are evaluated in order from top to bottom. The first one that is true wins; the rest are skipped. This order matters — put your most specific conditions first.
The If action supports these comparison operators:
| Operator | Meaning |
|---|---|
= |
Equal to |
<> |
Not equal to |
> |
Greater than |
< |
Less than |
>= |
Greater than or equal to |
<= |
Less than or equal to |
Contains |
Text contains substring |
Does not contain |
Text does not contain substring |
Is empty |
Variable has no value |
Is not empty |
Variable has a value |
You can also combine conditions with AND and OR operators within a single If action.
Key insight
The "Is empty" operator is your best friend when dealing with real-world data. Spreadsheets always have blank cells, web pages sometimes return empty fields, and OCR sometimes misses characters. Checking for empty before you try to use a value prevents a huge class of runtime errors. Always guard your critical variables with an empty check before using them in downstream actions.
Let's bring everything together. You're automating a vendor payment workflow:
Here's the skeletal flow structure:
// Read the spreadsheet
Read from Excel Worksheet
Store result into: InvoiceTable
// Initialize tracking variables
Set Variable: ProcessedCount = 0
Set Variable: SkippedCount = 0
Set Variable: ErrorList = (empty list)
// Loop through every row
For each CurrentRow in InvoiceTable
// Only work on Pending invoices
If CurrentRow['Status'] = 'Pending' Then
// Guard against missing data
If CurrentRow['VendorName'] Is Empty Then
Append CurrentRow['InvoiceNumber'] to ErrorList
Increase SkippedCount by 1
Next Loop
End
If CurrentRow['Amount'] <= 0 Then
Increase SkippedCount by 1
Next Loop
End
// Data is valid — process it
[... launch accounting app actions ...]
[... enter vendor, invoice number, amount ...]
[... save and close record ...]
Increase ProcessedCount by 1
End
End
// Report results
Display Message: 'Done. Processed: %ProcessedCount%, Skipped: %SkippedCount%'
Notice how the structure layers: the For Each handles iteration, the outer If filters to only the records you care about, and the inner If guards against bad data before you touch the application. Each layer has a clear, single responsibility.
Tip
Use Next Loop early in the loop body to filter out records you don't want to process. This "early exit" pattern keeps your main processing logic clean and unindented, and makes the flow easier to read and debug. The pattern is sometimes called a guard clause in traditional programming.
Build a flow that practices everything from this lesson. Here's the scenario: you have a text list of website URLs that a colleague has pasted into a file. Some URLs are valid (start with "https://"), some are just domain names without the protocol, and some lines are blank. You need to process only the valid ones.
Step 1: Create a list variable manually with these values:
https://vendor1.com
vendor2.com
https://vendor3.com
https://vendor4.com
notaurl
Step 2: Add a For Each loop that iterates over this list.
Step 3: Inside the loop, add an If action that checks whether the current item Is empty. If it is, use Next Loop to skip it.
Step 4: Add an Else If that checks whether the current item Does not contain "https://". If so, use a Set Variable action to prepend the protocol: "https://" + CurrentItem, then store it back.
Step 5: After the If block, add a Display Message action that shows the processed URL for each valid iteration.
Step 6: Run the flow and verify that blank lines and protocol-less URLs are handled correctly.
Bonus: Add a counter variable that increments each time a URL is fixed (was missing the protocol), and display the total count at the end of the flow.
The loop runs only once (or not at all) Check that the variable you're iterating over is actually populated before the loop starts. If you're reading from Excel or a file and that action fails silently, the result variable will be empty — and an empty list produces zero iterations. Add a Display Message action immediately after your data-reading action to confirm the row count before the loop.
"End" markers are in the wrong place In the Power Automate Desktop designer, it's easy to accidentally drop an action outside the loop body when you meant to put it inside. Watch the indentation in the canvas carefully. If an action isn't indented under the For Each, it won't run as part of the loop. You can drag actions up or down to reposition them.
Loop Until runs forever This almost always means the condition you're checking never becomes true. Either the variable isn't being updated inside the loop, or the value you're comparing against doesn't match exactly (a trailing space, a case difference, a numeric-vs-text type mismatch). Add a Display Message inside the loop to print the variable value on each pass — you'll quickly see what's happening.
Nested If conditions behave unexpectedly When you have multiple conditions, Power Automate Desktop evaluates them strictly in order. If a record matches the first condition, it never evaluates the second, even if the second would also be true. This is correct behavior, but it means your order of conditions matters. Also double-check that your Else If blocks are attached to the right If — it's possible to accidentally create two separate If blocks instead of one branched If/Else If.
Data table column access fails with an error
Column names in data tables are case-sensitive and must match exactly what's in the spreadsheet header. If your Excel column is "Invoice Number" (with a space), you can't access it as CurrentRow['InvoiceNumber']. Check the exact column name, including spaces and capitalization.
Note
When debugging a loop that processes real application data (like entering records into an accounting system), it's smart to test with a small subset first — maybe 3-5 rows — before running against the full dataset. This lets you catch logic errors without making hundreds of unwanted changes to your application. You can limit iterations by temporarily adding a counter variable and an Exit Loop when it hits 5.
You've now covered the three core flow control structures in Power Automate Desktop:
These aren't just individual features — they're the grammar of automation logic. Every real-world flow you build, whether you're automating Excel worksheets, reading and processing CSV files, or scraping data from a web application, will use these structures as its skeleton.
From here, the natural next topics to explore are:
The jump from "I can record clicks" to "I can write automation logic" is the biggest one in this learning path. You've just made it.