FlowLibs
Browse Cloud FlowsConnectorsAI ToolsDev ToolsPricingAboutContact
Dev Tools/PAD

Power Automate Desktop Patterns

A working reference for Power Automate Desktop (PAD) — the RPA engine for Windows. Variables and the % notation, data types and structures, control flow, error handling, UI/browser/Excel automation, cloud orchestration, plus the FlowLibs copy-paste designer syntax that pairs with the PAD skills.

Power Automate Desktop28 min read·Updated 2026-06-16
Power Automate DesktopPADRPADesktop FlowsUI AutomationSelectorsExcel AutomationError HandlingSubflowsUnattendedCopy-Paste Syntax

On this page

  • Fundamentals
  • Variables & the % notation
  • Data types
  • Lists, datatables & custom objects
  • Datatype properties
  • Conditionals
  • Loops
  • Flow control & organisation
  • Error handling
  • UI automation & selectors
  • Browser & web automation
  • Excel automation
  • Files, folders & text
  • Scripting & Power Fx
  • Cloud orchestration
  • PAD copy-paste designer syntax
  • Reusable patterns
  • Best practices & ALM
  • Reserved keywords

Fundamentals

Power Automate Desktop (PAD) is the robotic process automation engine in Power Automate — it drives Windows desktop apps, browsers, files, and legacy systems the way a person would. You build desktop flows in the flow designer by sequencing actions; the console lists your flows and launches runs. Every flow has a Main subflow that runs first, and you call other subflows with Run subflow.

  • Actions are the building blocks — each is a configured step (launch Excel, click a button, set a variable). Rename them; the name becomes how you reference outputs.
  • Subflows group actions for reuse and readability. Main always exists and always runs first; add more from the subflow tabs.
  • Variable scope. Global subflows expose variables across the whole flow; local subflows keep variables private — reference a global from a local subflow with the global. prefix.
  • Machines & connections. A desktop flow runs on a registered machine (or machine group); a desktop flow connection lets cloud flows sign in and trigger it. Gateways are retired — use direct connectivity.
  • Attended vs unattended. Attended runs share your signed-in session; unattended runs spin up their own locked Windows session (no users signed in) and need the Power Automate Process plan.

Author solution-aware from day one

Build inside a Power Platform solution so the desktop flow, its connection references, and environment variables move cleanly across DEV → TEST → PROD. See Best practices & ALM below, and the Naming & ALM guide.

Variables & the % notation

Every variable reference is wrapped in percentage signs: %VariableName%. Anything between % is *evaluated* — a variable, a property, or a whole expression. To print a literal percent character, escape it by doubling: %%.

  • Hard-coded text goes in quotes inside an expression: %'this is ' + 'text'% → this is text.
  • Property access uses dot notation inside the percents: %Files.Count%, %CurrentItem.FullName%, %varDate.Year%.
  • Blank / null. Any variable can hold a Blank value; test it with the Is blank / Is not blank options on conditional actions.
  • Names matter. A variable name can’t be a reserved keyword (see the table at the end) — that includes everyday words like action, step, error, and next.
9/9
ExpressionResultNotes
%5 * 3%15Arithmetic: + - * /
%4 / Var%4 ÷ value of VarNumeric result
%'Order ' + 5%Order 5+ concatenates; number coerced to text
%Index = 1 OR Index = 2%True / FalseLogical OR; also AND, NOT, XOR
%NOT(4 <> 4)%TrueComparisons: = <> < <= > >=
%Contains(Name, 'SQL', True)%True / Falsearg3 = ignore case
%StartsWith(s, 'AVR', False)%True / FalseAlso EndsWith / NotStartsWith
%IsEmpty(s)% / %IsNotEmpty(s)%True / FalseEmpty-string test
%(A + B) * C%grouped mathParentheses set precedence

Comparisons need matching types

PAD only compares values of the same type — comparing a number to text throws. Convert first (Text.ConvertTextToNumber, the Value() expression, or read Excel cells as Typed values) before you compare.

Data types

PAD infers a variable’s type from its content. Simple types hold one value; advanced types are collections; instances are live handles to an app or connection that you pass between actions.

9/9
TypeHoldsHow to create
Text valueAny stringSet variable with text, no notation
Numeric valueNumbers (only type usable in math)Set variable with a number
Boolean valueTrue / FalseSet variable to %True% or %False%
DatetimeDate and/or timeGet current date and time; convert from text
ListSingle-dimension arrayCreate new list; or actions that output lists
DatatableRows × columns (2-D)Create data table; Read from Excel; SQL; Extract web data
DatarowOne row of a datatableThe current item when looping a datatable
Custom objectKey/value pairs (JSON-like)%{ }% empty, or %{ 'k': 'v' }%
InstanceExcel / browser / window / SQL handleLaunch Excel, Launch browser, etc.

Instances are handles, not data

An Excel instance or browser instance identifies *which* app a later action talks to — Launch Excel produces %ExcelInstance%, and every following Excel action takes that instance. Open it, operate, then close it to avoid orphaned processes.

Lists, datatables & custom objects

9/9
GoalNotationExample
List item (0-based)%List[i]%%Files[0]%
List slice%List[start:stop]%%List[2:4]% → 3rd & 4th items
List slice to end%List[start:]%%List[2:]%
List slice from start%List[:stop]%%List[:4]%
Datatable cell by index%DT[row][col]%%ExcelData[0][1]%
Datatable cell by column%DT[row]['Col']%%ExcelData[0]['Email']%
Datatable row slice%DT[start:stop]%%ExcelData[2:5]%
Datarow item / column%Row[i]% or %Row['Col']%%CurrentItem['Status']%
Custom object property%Obj.Prop%%JsonAsObject.access_token%

Build a datatable inline

text
%{ ^['Product', 'Price'], ['Product1', '10 USD'], ['Product2', '20 USD'] }%
Array notation in a Set variable action — prefix the header row with ^ to name columns.

Indexes are 0-based; loop items are datarows

The first row/column/item is index 0. When you iterate a datatable with For each, the loop variable is a datarow — reach a field with %Row['ColumnName']% (the column names come from the datatable that produced it).

Datatype properties

Many types carry read-only properties you access with %Variable.Property% — no extra action required.

13/13
TypePropertyReturns
Text.LengthCharacter count
Text.ToUpper / .ToLowerRe-cased copy
Text.Trimmed / .isEmptyTrimmed copy / empty test
Datetime.Year .Month .DayDate parts
Datetime.DayOfWeek .DayOfYearWeekday name / day number
Datetime.Hour .Minute .SecondTime parts
List.CountNumber of items
Datatable.RowsCountNumber of rows
Datatable.ColumnsList of column names
Datatable.ColumnHeadersRowDatarow of headers
Datatable.IsEmptyTrue if no rows
Datarow.ColumnsCount / .ColumnsNamesColumn count / header list
Browser instance.IsAliveTrue if the window is still open

Conditionals

Use If / Else if / Else to branch on any condition, and Switch / Case / Default case to match one value against many. Conditions can be a simple operand-operator-operand comparison or a full %…% logical expression.

6/6
OperatorMeaningAvailable in
= / <>Equal / not equalIf, Case, Loop condition
< <= > >=Numeric / date compareIf, Case, Loop condition
Contains / Does not containSubstring testIf, Case
Starts with / Ends withPrefix / suffix testIf, Case
Is empty / Is not emptyEmpty-string testIf, Case
Is blank / Is not blankNull testIf, Case

One If with a logical expression beats nested Ifs

text
First operand:  %A = 10 AND B = 5%
Operator:       Equal to (=)
Second operand: %True%
Set First operand to the expression, Operator to “Equals to”, Second operand to True.

Switch isn’t available in Power Fx flows

In standard desktop flows, Switch / Case / Default case work as expected. If you author a flow in Power Fx mode, Switch/Case/Default case aren’t supported — branch with nested If instead.

Loops

5/5
ActionRepeatsNotes
LoopA fixed number of timesStart / increment / end; auto loop-index variable
Loop conditionWhile a condition is trueTwo operands + operator; watch for endless loops
For eachOnce per itemIterates a list, datatable, or datarow
Exit loop—Break out early
Next loop—Skip to the next iteration

For each over a list (designer copy-paste)

text
LOOP FOREACH CurrentItem IN $fx'=varFiles'
    # ... actions using CurrentItem ...
END
LOOP FOREACH binds each item to a loop variable; END closes the block.

Prefer For each, and filter before you loop

Looping is the slowest thing PAD does. Filter or sort the source first (Filter data table, Get files with a file filter), and use Exit loop / Next loop to skip work instead of wrapping everything in deeper conditionals.

Flow control & organisation

8/8
ActionWhat it doesNotes
Run subflowCalls another subflowSupports dynamic name via expression
RegionGroups actions visuallyNestable; pure organisation
CommentAnnotates the flowExplain the why
Label / Go toJump target / jumpUse sparingly — prefer subflows
WaitPauses N secondsLast resort vs waiting on a UI element
Stop flowEnds the runSuccessfully, or with an error message
ExitTerminates with a codeOptional ErrorMessage to the caller
Run desktop flowCalls another desktop flowChild runs in the same Windows session

Subflows vs child desktop flows

Run subflow calls a subflow inside the *same* flow — free and instant. Run desktop flow calls a *separate* desktop flow (shareable across flows) and can pass input/output variables, but adds overhead. Reach for subflows for internal structure; child desktop flows for genuinely shared automation.

Error handling

By default a desktop flow stops on the first error. Assume every UI click, file op, and web call can fail, and decide per action — or per block — what should happen instead.

  • Per-action On error. Open an action’s On error tab to set a Retry (N times, every M seconds), then Continue flow run (*Go to next action*, *Repeat action*, or *Go to label*), plus rules to Set variable or Run subflow. Use Advanced to handle different errors differently.
  • On block error. Wrap a group of actions in a Block to apply one error policy to all of them — including a Retry policy (None / Fixed / Exponential) and Handle flow terminating errors to also catch logic errors like out-of-bounds indexing or divide-by-zero.
  • Get last error. Retrieves the most recent error into an error variable; enable Clear error so you don’t re-read it later.
  • Throw custom error. Raises your own named error — place it inside an On block error and add a matching + Custom error entry to handle it.
  • Self-healing (preview). For single-element UI/browser actions that hit *Element not found*, an AI fallback can re-find the element at runtime, in the order: Retry → Self-healing → Set variable / Run subflow → Continue / Throw.

FlowLibs Try/Catch pattern (copy-paste)

text
SomeAction Parameter: $fx'value' Output=> Result
    ON ERROR REPEAT 1 TIMES WAIT 2
    ON ERROR
        CALL Subflow_ErrorLogging
        THROW ERROR
    END
Retry once, then log to the error subflow and re-throw so the run history shows the failure.

Block-level handler

text
BLOCK 'Create HTML Table'
ON BLOCK ERROR REPEAT 1 TIMES WAIT 2
ON BLOCK ERROR
    CALL Subflow_ErrorLogging
    THROW ERROR
END
    # ... all actions inside the block ...
END
A retry policy on a block retries from the start of the block; the handler runs only if every retry fails.
5/5
AccessReturns
LastError.MessageHuman-readable error message
LastError.LocationWhere the failure occurred
LastError.ErrorDetailsFull detail string
LastError['AdditionalInfo']Extra info (e.g. cloud-connector body)
LastError['Body']['error']['message']Nested API error message

Throw error so failures stay visible

Swallowing an error keeps the run green while the work silently failed. Log it (Dataverse / file / Teams) and Throw error to mark the run failed — unless you have a deliberate dead-letter path that records the bad item and continues.

UI automation & selectors

UI automation drives Windows desktop apps. Each target is a UI element described by one or more selectors — a hierarchy that uses the > notation, where each level is contained in the one to its left. Window-level selectors start from the :desktop root.

text
:desktop > window[Name="%WindowName%"][Process="Notepad"] > menu "Application" > menu item "File"
A selector with a variable in the window name — use %var% inside the selector.
5/5
TechnologyUse forNotes
UIA (default)Modern apps — WPF, WinForms, UWPMost robust; prefer this
MSAALegacy Win32 / VB6 appsWhen UIA exposes nothing usable
UIA3 RawElectron / custom-rendered controlsExposes the full, unfiltered tree
Text-based selectorElements with stable visible textCapture → “Capture based on text”
Image fallbackApps with no usable accessibility treeMatches a captured image region
  • Add multiple selectors to one element — PAD tries them in order, so a fallback survives minor UI changes.
  • Make selectors static. Replace Equals to with Starts with / Ends with / Contains or a regex to strip dynamic bits (e.g. a trailing (2) or a counter in a window title).
  • Test selector and Repair selector from the selector builder before blaming the flow — and recapture on the actual unattended machine, where resolution and DPI differ.
  • Run as administrator when automating an elevated app — PAD must run at the same or higher privilege than its target.

Selectors are the #1 cause of flaky RPA

Most unattended failures are *Element not found*: the app updated, the window resized, or the unattended session’s resolution differs from authoring. Build resilient selectors, add fallbacks + a retry policy, and set a fixed screen resolution for unattended runs.

Browser & web automation

Browser automation targets web pages. Launch a browser instance (Chrome, Edge, Firefox, or the automation browser), then act on web UI elements captured with HTML attribute selectors. Web actions need a *browser* instance — desktop UI actions won’t see web elements and vice-versa.

9/9
ActionWhat it does
Launch new browserOpens Chrome/Edge/Firefox → browser instance
Go to web page / Click linkNavigate or follow a link
Populate text field on web pageType into an input
Press button on web pageClick a button/element
Set drop-down value on web pageSelect an option
Extract data from web pageScrape value / table / list (with paging)
Get details of web page / elementRead title, URL, attribute, text
Run JavaScript function on web pageExecute JS for cases actions can’t reach
Handle web dialogAccept/dismiss JS prompts & auth dialogs

Prefer a connector or API when one exists

Screen-scraping breaks when layouts change. If the site has an API or a Power Automate connector, call that instead — and reserve Extract data from web page for systems with no programmatic interface.

Excel automation

Excel actions work against an Excel instance from Launch Excel (or Attach to running Excel). Read a range into a datatable, manipulate it in memory, and write it back — far faster than clicking cells through UI automation.

7/7
ActionWhat it doesOutput
Launch ExcelOpen blank or a document%ExcelInstance%
Read from Excel worksheetSingle cell / range / selection / all%ExcelData% (datatable)
Write to Excel worksheetWrite value, list, or datatable—
Get first free row/columnFind the next empty row/columnIndex for appends
Set active worksheetSwitch sheet by name or index—
Run Excel macroRun a VBA macro (name;args)—
Close ExcelSave options + release the instance—
text
Excel.ReadFromExcel.ReadAllCells Instance: $fx'=ExcelInstance' GetCellContentsMode: Excel.GetCellContentsMode.TypedValues FirstLineIsHeader: True RangeValue=> ExcelData
Read with headers, then address cells by column name (designer copy-paste).

OneDrive / SharePoint-synced files break the COM bridge

PAD drives Excel through COM, which isn’t reliable on OneDrive/SharePoint-synced paths — you’ll hit *file not found*. Work on a local copy and sync it back, or open via Run application + Attach to running Excel. Reading Typed values (not text) avoids ### and keeps numbers/dates typed.

Files, folders & text

12/12
ActionWhat it does
Get files in folderList files, with filter + sort → list of files
Get special folderResolve Documents/Desktop/etc. paths
If file exists / If folder existsBranch on presence
Read text from fileWhole text or list of lines
Write text to fileCreate/append text
Read from / Write to CSV fileCSV ⇄ datatable (encoding, delimiter, headers)
Move / Copy / Delete fileFile management
Filter data tableMulti-condition AND/OR filtering → new datatable
Retrieve data table column into listPull one column out as a list
Text.ReplaceFind/replace, literal or regex
Crop text / Split textSubstring between flags / split on delimiter
Convert date time to textFormat a datetime with a custom pattern

There is no Text.Length() function

PAD has no Text.Length() expression. To get a character count use the Get text length action (Text.GetTextLength) and read its output; to test for non-empty, use IsNotBlank(varName) in the condition. Windows env vars like %Username% aren’t auto-resolved either — get the path from Get special folder and crop it.

Scripting & Power Fx

When no action fits, drop to a script; and on newer flows you can author logic in Power Fx instead of the classic %…% expressions.

7/7
MechanismReturn valuesNotes
Run PowerShell scriptWrite-OutputUse $ for vars; %padVar% to inject
Run Python scriptprintInject PAD vars with %notation%
Run VBScript / JavaScriptWScript.EchoJS uses var; both echo to output
Run .NET scriptScript Parameters (In/Out)Typed in/out arguments
Power Fx — Index(list, n)List itemPower Fx flows use functions, not [i]
Power Fx — Index(Index(dt,r),c)Datatable cellRow r, column c
Power Fx — ${ expr }Interpolation in text/selectorsEscape a literal with $${

Keep scripts thin and idempotent

A one-line PowerShell ([guid]::NewGuid().ToString()) is fine; a 200-line script hidden in an action is a maintenance trap. Prefer built-in actions for portability and logging, and pass values in/out explicitly so the step is testable.

Cloud orchestration

Desktop flows usually run as a step inside a cloud flow. Add Run a flow built with Power Automate for desktop, pick the connection and run mode, and pass data through input/output variables.

  • Input variables flow cloud → desktop; output variables flow desktop → cloud. Mark sensitive inputs as sensitive so they’re obfuscated in logs.
  • Run modes. *Attended* runs in your signed-in session; *unattended* creates a locked RDP session (all users must be signed out) and needs the Process license. *Picture-in-picture* is attended-only.
  • Trigger options. Cloud flow step, the console, a desktop shortcut, or a run URL (browser / Run dialog / Task Scheduler).
LimitValue
Desktop-flow input size2 MB (1 MB in China regions)
Runs per minute, per connectionUp to 70
UnattendedProcess plan; no users signed in on Win10/11
Run header output typeResolves at runtime (General at authoring)
Limits worth designing around (verify current values in Microsoft docs).

Log to Dataverse for estate-wide visibility

Per-run history lives on the machine. For monitoring across many flows, write a run header + step logs to a Dataverse logging table (the FlowLibs pattern below), and stamp a correlation id so a cloud→desktop transaction can be stitched back together.

PAD copy-paste designer syntax

PAD actions are plain text you can copy straight into the designer — paste them and they become real actions. Getting the string enclosure right is everything: one wrong quote style and the paste fails silently. There are exactly three.

6/6
EnclosureUse forExample
$'''value'''Fixed text, no runtime varsSET v TO $'''Info'''
%var% inside $'''…'''Inject a var into a literal$'''C:\\Temp\\%varUser%'''
$fx'…${var}…'String interpolation$fx'Processing ${CurrentItem.Name}'
$fx'= expr'Expression evaluation$fx'= CountRows( varFiles )'
Bare referenceDirect var / property / indexSET x TO LastError.Message
Output =>Assign an action’s resultFiles=> varFiles
  • Backslashes double inside `$'''…'''` — C:\\Users\\.... This rule does not apply inside $fx'…'.
  • `%var%` for literal strings, `${var}` for fx strings. %var% substitutes inside triple-quotes; ${var} interpolates inside $fx'…'.
  • Datatable columns: dot notation inside `$fx`, brackets only when bare. $fx'${Row.Name}' is valid; $fx'${Row[''Name'']}' is not — use Row['Name'] as a bare reference or in an IF condition.
  • `=>` marks every output (no spaces in the parameter name): Instance=> ExcelInstance, Response=> RawReturn.
  • `REGION / ENDREGION` group actions (double asterisks); name variables varCamelCase.

The three enclosures side by side

text
SET varDownloads TO $'''C:\\Users\\%varUser%\\Downloads'''
SET varMessage   TO $fx'Found ${varFileCount} files to clean.'
SET varFileCount TO $fx'= CountRows( varFiles )'
SET varErrorMsg  TO LastError.Message
Literal (backslashes doubled, %var% substitution) vs interpolation vs expression vs bare.

This pairs with the FlowLibs PAD skills

The same syntax backs the pad-script generator and pad-code-review skills — so a snippet here pastes cleanly into the designer, and a review reads it the same way.

Reusable patterns

Composite, ready-to-copy building blocks. Adapt the variable names; keep the structure.

Logging call

text
**REGION Logging
SET varCurrentMessage TO $fx'Your message here'
CALL Subflow_Logging
**ENDREGION

Early exit when there is nothing to do

text
SET varFileCount TO $fx'= CountRows( varFiles )'
IF varFileCount = 0 THEN
    **REGION Logging
    SET varCurrentMessage TO $fx'No records found to process'
    CALL Subflow_Logging
    **ENDREGION
    EXIT Code: $fx'=0'
ELSE
    **REGION Logging
    SET varCurrentMessage TO $fx'Found ${varFileCount} records to process.'
    CALL Subflow_Logging
    **ENDREGION
END

Process loop with counter + per-item logging

text
SET varProcessedCount TO $fx'0'
LOOP FOREACH CurrentItem IN $fx'=varFiles'
    Variables.IncreaseVariable Value: $fx'=varProcessedCount' IncrementValue: $fx'1'
    **REGION Logging
    SET varCurrentMessage TO $fx'Processing item ${CurrentItem.FullName}'
    CALL Subflow_Logging
    **ENDREGION
    # ... processing logic ...
END
**REGION Logging
SET varCurrentMessage TO $fx'Processed ${varProcessedCount} items'
CALL Subflow_Logging
**ENDREGION

Action guarded by retry + error logging

text
Folder.GetFiles Folder: $fx'${varPickupFolder}' FileFilter: $fx'*.csv' IncludeSubfolders: False FailOnAccessDenied: True Files=> varFiles
    ON ERROR REPEAT 1 TIMES WAIT 2
    ON ERROR
        CALL Subflow_ErrorLogging
        THROW ERROR
    END

Best practices & ALM

  • Rename actions and use Regions. Get_open_orders beats Get_files_3. Names become references; regions make a long Main readable.
  • Extract reuse into subflows. A logging subflow, an error-logging subflow, and a set-variables subflow keep Main short and consistent.
  • Robust selectors. Multiple selectors + static attributes + a retry policy; recapture on the unattended machine.
  • Error handling everywhere it can fail. File, web, UI, and cloud-connector actions get an ON ERROR (retry → log → throw) or sit inside an On block error.
  • No secrets inline. Use sensitive inputs, environment variables, or Key Vault — never hard-code credentials, and never log them.
  • Solution-aware + versioned. Author in a solution, bump the version each release, and promote DEV → TEST → PROD; keep the Default environment locked down.
  • Test on the target machine. Resolution, DPI, app versions, and permissions differ unattended — validate there, not just in the designer.

Document the flow

Capture purpose, trigger, owner, machine/connection, input/output variables, and dependencies in the flow description and a solution README — future-you (and the next maintainer) will need it. See Best Practices and Naming & ALM.

Reserved keywords

These words are reserved by the PAD engine and can’t be used for variable names, custom-object properties, or custom action names/properties. They’re case-insensitive (ACTION = action).

12/12
A – EF – LM – ST – Z
actionfalsemainthen
andformodthrow
asforeachnexttimes
blockfromnoto
callfunctionnottrue
caseglobalonwait
defaultgotoorwhile
disableifoutputxor
elseimportrepeatyes
endinset—
errorinputstep—
exitlabelswitch—

The varCamelCase prefix dodges the whole list

Prefixing every variable with var (and instances by type, like ExcelInstance) keeps you clear of reserved words and instantly readable — varStep, varNext, varError are all fine where step, next, error are not.

← All Dev ToolsUse these in your AI assistant →
Spotted an error or have a suggestion? Let us know
FlowLibs

A curated library of production-grade Power Automate cloud flow patterns. Packaged as managed solutions, ready to import into your environment.

Library

  • Browse Cloud Flows
  • Approvals
  • Email & Notifications
  • Reporting
  • Security & Compliance

AI

  • AI Tools
  • MCP Server
  • Generate a Token

Resources

  • About
  • FAQ
  • Support
  • Status
  • Contact
  • Power Automate Docs
  • Connector Reference

© 2026 FlowLibs. All rights reserved.

  • Privacy
  • Terms
  • Refunds
  • Cookies
  • Acceptable Use
  • DMCA
Help