SAP GUI holds critical enterprise data but predates modern APIs by decades. This lesson shows you how to drive SAP reliably with Power Automate Desktop — navigating transactions with scripting IDs, extracting full ALV grid datasets, and building session recovery logic that keeps unattended flows running without human intervention.

Picture this: every morning, a business analyst at a manufacturing company opens SAP, navigates to transaction ME2M, filters by plant and material group, scrolls through a grid of 400+ purchase order line items, copies them to Excel, then repeats the exercise for three other transaction codes before 9 AM. It takes 45 minutes on a good day. On a bad day — when SAP is slow, a session times out, or the grid has an extra column — it takes longer, and the data is wrong.
This is the kind of work Power Automate Desktop was built to eliminate. SAP GUI is one of the most common targets for RPA precisely because so much critical enterprise data lives inside it, yet the system predates modern APIs by decades. Connecting to it programmatically is either expensive (SAP RFC/BAPI licenses) or complicated (SAP Business Technology Platform). But the GUI is right there, and Power Automate Desktop can drive it with surprising reliability — if you understand how SAP GUI works under the hood and how to handle its quirks.
By the end of this lesson, you'll be able to build a production-ready desktop flow that launches SAP GUI, navigates to any transaction code, interacts with input fields and toolbar buttons, extracts data from ALV grid tables, and recovers gracefully when sessions time out or modal dialogs interrupt your flow.
What you'll learn:
You should already be comfortable with the Power Automate Desktop designer and have built at least a few working flows. Specifically, you'll need a working understanding of:
On the SAP side, you need SAP GUI for Windows (version 7.40 or higher, though 7.60+ is strongly preferred), a user account with appropriate transaction access, and — critically — SAP GUI Scripting must be enabled on both the server and your local client. We'll cover that setup first.
Before a single Power Automate action will work against SAP, you need to turn on SAP GUI Scripting. This is a two-step process involving your SAP Basis team and your local machine.
Server-side (your Basis admin does this):
The profile parameter sapgui/user_scripting must be set to TRUE in your SAP system. Some organizations also set sapgui/user_scripting_disable_recording to FALSE to allow recording. If you're working in a corporate environment, file a request with your Basis team and explain you need it for RPA automation.
Client-side (you do this):
Warning
If you skip the "Notify" checkbox, every time your flow attaches to SAP GUI, a dialog box will appear asking "A script is attempting to attach..." — and your flow will freeze waiting for user input that never comes in an unattended scenario.
Once scripting is enabled, SAP GUI exposes a rich COM automation object (SAPROTWr.ScriptingCtrl.1) that Power Automate Desktop can interact with either through its built-in SAP actions or — in more complex scenarios — through VBScript. We'll use both approaches.
Most Windows applications expose their UI through Microsoft UI Automation (UIA), and Power Automate Desktop is excellent at driving those. SAP GUI does expose UIA, but its accessibility tree is organized very differently from a typical Windows form.
The SAP GUI window hierarchy looks like this:
SAP GUI Main Window
└── GuiApplication
└── GuiConnection
└── GuiSession
└── GuiMainWindow
└── GuiUserArea
├── GuiTextField (input fields)
├── GuiLabel (labels)
├── GuiStatusBar (bottom status bar)
└── GuiGridView (ALV grids/tables)
The key implication: nearly everything in a SAP screen is identified by its technical ID, not by a label or position. A field like "Company Code" has an ID like wnd[0]/usr/ctxtRF02L-BUKRS. These IDs are stable across machines and system refreshes, which is both a blessing and a curse — they don't break when the screen repaints, but you need to know the technical name to target the right element.
Key insight
The SAP technical IDs (like wnd[0]/usr/ctxtRF02L-BUKRS) correspond to the field's ABAP data dictionary name. You can find these by right-clicking any field in SAP GUI (with scripting enabled) and choosing Technical Information. This shows you the exact string you need for scripting.
Power Automate Desktop has a dedicated set of SAP GUI actions in the action library. However, these built-in actions cover the most common operations, not every edge case. For full control — especially table extraction and custom toolbar interactions — you'll supplement them with VBScript actions that invoke the SAP scripting COM object directly.
There are two common patterns: launching SAP fresh from your flow, or attaching to an already-running session. In unattended scenarios, you'll almost always launch fresh.
Use the Run Application action to start SAP:
Run Application
Application Path: C:\Program Files (x86)\SAP\FrontEnd\SapGui\saplogon.exe
Wait For Application: Wait for application to complete loading
After launch, use Attach to Running Process or simply proceed with UI actions targeting the SAP Logon window. For most production setups, you'll want to launch a specific system directly using a connection shortcut (.sap file) to skip the logon pad:
Run Application
Application Path: C:\SAP_Shortcuts\PRD_Client_100.sap
Never hardcode SAP credentials in your flow. Use a sensitive variable sourced from Azure Key Vault or the machine's credential store. The pattern for entering credentials looks like this:
# Set focus on the SAP Logon screen
Click UI Element: SAP_Client_Field
Send Keys: {Client_Number}
Click UI Element: SAP_Username_Field
Send Keys: %SAPUsername% # Variable holding username
Click UI Element: SAP_Password_Field
Send Keys: %SAPPassword% # Sensitive variable - won't appear in logs
Press Key: Enter
For the details on managing credentials safely, see Handling Credentials Securely in Desktop Flows: Sensitive Variables and Azure Key Vault.
Tip
After pressing Enter to log in, always add a Wait for UI Element action that waits for the SAP Easy Access menu to appear before proceeding. SAP login time varies significantly depending on network latency and server load — a fixed Wait/Sleep action will eventually fail you.
The SAP command field (the text box in the top-left of every SAP screen) is your primary navigation tool. You can navigate to any transaction by typing /n followed by the transaction code. For example, /nME23N navigates to the Purchase Order Display transaction.
The command field has the UI element ID wnd[0]/tbar[0]/okcd in SAP's technical naming. You can target it with a standard PAD UI action, but the most reliable approach for transaction navigation is a VBScript action:
' Navigate to a transaction using SAP's scripting API
Dim SapGuiAuto
Dim SapApp
Dim SapConn
Dim SapSession
Set SapGuiAuto = GetObject("SAPGUI")
Set SapApp = SapGuiAuto.GetScriptingEngine
Set SapConn = SapApp.Children(0)
Set SapSession = SapConn.Children(0)
SapSession.StartTransaction "%TransactionCode%"
In this snippet, %TransactionCode% is a Power Automate Desktop variable (e.g., ME2M). The StartTransaction method is the cleanest way to navigate — it handles the /n prefix internally and properly resets the screen state.
If you prefer the UI approach (useful when you need to stay entirely within PAD's visual designer):
# Click the command field
Click UI Element: [SAP Command Field - Technical ID: wnd[0]/tbar[0]/okcd]
# Clear whatever is there and type the transaction
Send Keys: {Ctrl}({A})
Send Keys: /nME2M
Press Key: Enter
Once you're in a transaction, you'll typically need to fill selection screen fields before executing. This is where knowing the technical field names pays off. Use Right-click > Technical Information in SAP to discover them.
Here's a complete example for ME2M (Purchase Orders by Material) — filling the plant and date range, then pressing Execute (F8):
Dim SapGuiAuto, SapApp, SapConn, SapSession
Set SapGuiAuto = GetObject("SAPGUI")
Set SapApp = SapGuiAuto.GetScriptingEngine
Set SapConn = SapApp.Children(0)
Set SapSession = SapConn.Children(0)
' Navigate to ME2M
SapSession.StartTransaction "ME2M"
' Set Plant field
SapSession.findById("wnd[0]/usr/ctxtWERKS-LOW").Text = "%PlantCode%"
' Set Document Date range
SapSession.findById("wnd[0]/usr/ctxtBEDAT-LOW").Text = "%StartDate%"
SapSession.findById("wnd[0]/usr/ctxtBEDAT-HIGH").Text = "%EndDate%"
' Press Execute (F8)
SapSession.findById("wnd[0]/tbar[1]/btn[8]").press()
The pattern SapSession.findById("...") is the core of SAP GUI scripting. Everything — buttons, fields, checkboxes, tabs — has an ID, and findById retrieves it. Toolbar buttons are indexed: btn[8] is always F8 (Execute) on the application toolbar.
Note
SAP dates are locale-specific. If your SAP system is configured for German locale (DD.MM.YYYY), your date variables must match that format. Build this into your variable setup rather than assuming the format — a mismatch will silently clear the field or throw a conversion error that can be very hard to diagnose.
ALV (ABAP List Viewer) grids are SAP's standard display for tabular data — purchase orders, open items, material movements, and hundreds of other reports. Extracting this data reliably is the hardest part of SAP automation, and the part most likely to break if you take shortcuts.
The temptation is to use PAD's Extract Data from Window action or the recorder to capture what's visible. This approach has two fatal problems:
The correct approach is to use the SAP GUI scripting API to read the grid object directly, bypassing the visual rendering entirely.
Here's the complete pattern for extracting all rows from an ALV grid:
Dim SapGuiAuto, SapApp, SapConn, SapSession
Dim oGrid
Dim iRow, iCol
Dim rowCount, colCount
Dim cellValue
Dim outputData
Set SapGuiAuto = GetObject("SAPGUI")
Set SapApp = SapGuiAuto.GetScriptingEngine
Set SapConn = SapApp.Children(0)
Set SapSession = SapConn.Children(0)
' Get the ALV grid object - ID varies by transaction
' Use Technical Information to find yours
Set oGrid = SapSession.findById("wnd[0]/usr/cntlRESULT_LIST/shellcont/shell")
' Get dimensions
rowCount = oGrid.RowCount
colCount = oGrid.ColumnCount
' Build tab-delimited output string
outputData = ""
' First, write column headers
Dim colKey
For iCol = 0 To colCount - 1
colKey = oGrid.ColumnOrder(iCol)
outputData = outputData & oGrid.GetColumnDataType(colKey) & Chr(9)
Next
outputData = outputData & Chr(10)
' Then write data rows
For iRow = 0 To rowCount - 1
For iCol = 0 To colCount - 1
colKey = oGrid.ColumnOrder(iCol)
cellValue = oGrid.GetCellValue(iRow, colKey)
outputData = outputData & cellValue & Chr(9)
Next
outputData = outputData & Chr(10)
Next
' Return the data to PAD
WScript.Echo outputData
Warning
The grid's technical ID varies by transaction. In ME2M it might be wnd[0]/usr/cntlRESULT_LIST/shellcont/shell, while in FB03 it could be wnd[0]/usr/cntlGRID1/shellcont/shell. Always use Technical Information (right-click the grid) or the SAP GUI Script Recorder to discover the correct ID before hardcoding it.
The VBScript action in Power Automate Desktop captures what the script sends to WScript.Echo into a PAD variable. You then parse that tab-delimited string into a proper data table:
Run VBScript: [above script]
VBScript Output: %RawGridData%
# Split into rows
Split Text
Text to Split: %RawGridData%
Delimiter: New Line
Split Into Variable: %GridRows%
# Create an empty DataTable
Set Variable: %ExtractedData% = {New DataTable}
# Loop through rows (skip index 0 which is headers)
For Each %CurrentRow% In %GridRows% Starting From Index 1
# Split each row by tab
Split Text
Text to Split: %CurrentRow%
Delimiter: Tab
Split Into Variable: %RowCells%
# Add row to DataTable
Add Row to DataTable
Table: %ExtractedData%
Row Data: %RowCells%
End
Once you have a proper DataTable, you can write it to Excel, filter it, or pass it as output to a cloud flow. For the Excel write pattern, see Automating Excel with Power Automate Desktop: Reading, Writing, and Running Macros.
Some SAP reports use pagination (Next Page button) rather than a single scrollable grid. In those cases, you need a loop that extracts the current page, clicks Next Page, and repeats until there is no next page.
' Check if Next Page button is enabled
Dim btnNextPage
Dim hasMorePages
On Error Resume Next
Set btnNextPage = SapSession.findById("wnd[0]/tbar[1]/btn[35]")
If Err.Number <> 0 Then
hasMorePages = False
Else
hasMorePages = Not btnNextPage.Enabled = False
End If
On Error GoTo 0
WScript.Echo CStr(hasMorePages)
Use this check inside a PAD loop: extract the current page's data, append it to your DataTable, check for more pages, click Next if available, and repeat. The button ID btn[35] is typically the "Next Page" button in ALV toolbar — verify this for your specific transaction.
SAP is notorious for interrupting your flow with modal dialog boxes. These appear for:
If your automation doesn't handle these, it will freeze — the modal blocks all further interaction with the SAP window, and your actions time out one by one until the flow errors out.
A SAP popup dialog is a separate window in the scripting object model, appearing as wnd[1] (the main window is always wnd[0]). You can check for its existence:
Dim SapGuiAuto, SapApp, SapConn, SapSession
Dim popupExists
Dim popupText
Set SapGuiAuto = GetObject("SAPGUI")
Set SapApp = SapGuiAuto.GetScriptingEngine
Set SapConn = SapApp.Children(0)
Set SapSession = SapConn.Children(0)
popupExists = False
popupText = ""
On Error Resume Next
Dim wnd1
Set wnd1 = SapSession.findById("wnd[1]")
If Err.Number = 0 Then
popupExists = True
' Try to get the message text
Dim msgText
Set msgText = SapSession.findById("wnd[1]/usr/txtMessageTxt")
If Err.Number = 0 Then
popupText = msgText.Text
Else
Err.Clear
' Some popups use a different text field ID
Set msgText = SapSession.findById("wnd[1]/usr/lblMessage")
If Err.Number = 0 Then
popupText = msgText.Text
End If
End If
End If
On Error GoTo 0
WScript.Echo popupExists & "|" & popupText
In PAD, capture this output, split on |, and then branch:
If %PopupExists% = "True"
If %PopupMessage% Contains "No records found"
# Dismiss and handle empty result gracefully
[Run VBScript to press Enter/OK on wnd[1]]
Set Variable: %ResultCount% = 0
Else If %PopupMessage% Contains "not authorized"
# Log the error and alert someone
[Write to log file]
Stop Flow with Error Message
Else
# Unknown popup - take a screenshot and stop
Take Screenshot
Stop Flow with Error Message: "Unexpected SAP dialog: " & %PopupMessage%
End
End
To click the OK or Enter button on a SAP dialog:
' Press Enter on the popup (dismisses most information/warning dialogs)
SapSession.findById("wnd[1]").sendVKey(0)
' Or click a specific button by text
' Common button IDs on SAP popups:
' btn[0] = Yes / OK / Continue
' btn[1] = No / Cancel
SapSession.findById("wnd[1]/tbar[0]/btn[0]").press()
sendVKey(0) sends the Enter key to the window — this dismisses the vast majority of SAP information popups.
SAP sessions time out after a period of inactivity (typically 15-30 minutes, configured by your Basis team). In unattended automation, this is a real problem: if your flow pauses waiting on a previous step (Excel writing, an API call, etc.) and then returns to SAP, the session may be dead.
A timed-out SAP session shows the logon screen again, or presents a "Reconnect" dialog. Your VBScript will either fail to find elements (throwing COM errors) or find unexpected elements (the logon fields instead of your transaction screen).
Build a session health check that runs before any SAP interaction:
Dim SapGuiAuto, SapApp, SapConn, SapSession
Dim sessionStatus
Set SapGuiAuto = GetObject("SAPGUI")
On Error Resume Next
Set SapApp = SapGuiAuto.GetScriptingEngine
If Err.Number <> 0 Then
sessionStatus = "NO_SAP_RUNNING"
WScript.Echo sessionStatus
WScript.Quit
End If
Set SapConn = SapApp.Children(0)
If Err.Number <> 0 Then
sessionStatus = "NO_CONNECTION"
WScript.Echo sessionStatus
WScript.Quit
End If
Set SapSession = SapConn.Children(0)
If Err.Number <> 0 Then
sessionStatus = "NO_SESSION"
WScript.Echo sessionStatus
WScript.Quit
End If
' Check if we're on a login screen (session timed out)
Dim loginField
Set loginField = SapSession.findById("wnd[0]/usr/txtRSYST-BNAME")
If Err.Number = 0 Then
sessionStatus = "TIMED_OUT"
Else
Err.Clear
sessionStatus = "ACTIVE"
End If
On Error GoTo 0
WScript.Echo sessionStatus
Structure your flow so session recovery is a callable subflow. When the health check returns anything other than ACTIVE, the recovery subflow handles it:
[Main Flow]
Call Subflow: CheckSAPSession
If %SessionStatus% = "ACTIVE"
[Continue with normal work]
Else If %SessionStatus% = "TIMED_OUT"
Call Subflow: ReloginToSAP
Call Subflow: NavigateToLastTransaction
[Retry the operation]
Else If %SessionStatus% = "NO_SAP_RUNNING"
Call Subflow: LaunchSAP
Call Subflow: LoginToSAP
Call Subflow: NavigateToLastTransaction
Else
[Log error and stop]
End
Using subflows for this keeps your main logic readable and makes the recovery code reusable across multiple SAP flows. For more on structuring reusable logic, see Subflows and Reusable Logic in Power Automate Desktop.
Key insight
Track your "last known position" in a PAD variable — which transaction you were running and what parameters you had filled in. This way, your recovery subflow can navigate back to exactly where you left off rather than restarting the entire extraction from scratch.
When SAP detects an existing session for your user, it shows a dialog asking how to handle the conflict. In unattended automation, you almost always want to continue with this logon (terminating the old session):
' After entering credentials and pressing Enter,
' check for the "Already Logged On" dialog
Dim wnd1
On Error Resume Next
Set wnd1 = SapSession.findById("wnd[1]")
If Err.Number = 0 Then
' This is the multiple logon dialog
' Option: Continue with this logon (terminate other sessions)
SapSession.findById("wnd[1]/usr/radMULTI_LOGON_OPT1").select()
SapSession.findById("wnd[1]/tbar[0]/btn[0]").press()
End If
On Error GoTo 0
Warning
Automatically terminating existing sessions can cause data loss if someone else is actively using that SAP account — or if a previous flow run is still mid-transaction. In unattended production flows, use a dedicated service account for SAP automation, separate from any human user. This eliminates logon conflicts entirely and makes your automation behavior predictable.
Combine On Block Error handling with your session health checks for a robust execution wrapper. Every significant SAP interaction block should be wrapped:
On Block Error
Number of Retries: 2
Retry Interval: 30 seconds
On Error:
Call Subflow: CaptureErrorDetails
Call Subflow: CheckSAPSession
If %SessionStatus% != "ACTIVE"
Call Subflow: RecoverSAPSession
End
# After recovery, On Block Error will retry the block
# --- Protected block starts ---
[Run VBScript: Navigate to ME2M]
[Run VBScript: Fill selection parameters]
[Run VBScript: Execute F8]
[Run VBScript: Check for popup]
[Run VBScript: Extract grid data]
# --- Protected block ends ---
This pattern means that if SAP throws a COM error mid-extraction (perhaps because a timeout occurred right as you were reading row 237), PAD waits 30 seconds, checks the session, recovers it if needed, and retries the entire block. Most transient SAP errors resolve within one retry.
For deeper patterns on robust error handling strategies, the lesson on Error Handling in Desktop Flows: On Block Error, Retry Policies, and Recovery Screenshots covers this comprehensively.
Let's put everything together. You'll build a flow that:
Create these input variables in your flow (mark the password as sensitive):
SAPSystem — the system ID (e.g., PRD)SAPClient — client number (e.g., 100)SAPUsername — your service account usernameSAPPassword — sensitive variablePlantCode — e.g., 1000StartDate — e.g., 01.01.2024EndDate — e.g., 31.01.2024OutputFolder — e.g., C:\Reports\PurchaseOrders\Build a LaunchAndLoginSAP subflow:
Run Application: saplogon.exe
Wait for Process: saplogon.exe to start
[Wait for UI: SAP Logon window visible]
# Open the target system connection
[Double-click system shortcut in SAP Logon pad
OR use the sapshcut.exe command-line approach for direct connection]
[Wait for UI: SAP Login Screen]
# Enter credentials
Click UI: Client Field → Send Keys: %SAPClient%
Click UI: Username Field → Send Keys: %SAPUsername%
Click UI: Password Field → Send Keys: %SAPPassword%
Press Key: Enter
# Handle "Already Logged On" dialog via VBScript (see earlier section)
Run VBScript: [HandleMultipleLogon.vbs]
# Wait for Easy Access menu
Wait for UI Element: SAP Easy Access menu title
Timeout: 30 seconds
If Timeout: Stop flow with error
# Navigate to ME2M
Run VBScript: StartTransaction("ME2M")
# Wait for selection screen to load
Wait for UI Element: [ME2M plant field]
Timeout: 15 seconds
# Fill parameters
Run VBScript: [FillME2MParameters.vbs with %PlantCode%, %StartDate%, %EndDate%]
# Check for popup before executing
Run VBScript: [CheckForPopup.vbs]
Output: %PopupInfo%
Split Text: %PopupInfo% on "|" → %PopupParts%
If %PopupParts%[0] = "True"
Stop Flow: "Unexpected popup on ME2M selection screen: " & %PopupParts%[1]
End
# Execute (F8)
Run VBScript: [PressF8.vbs]
# Wait for results
Wait for UI Element: [ALV Grid]
Timeout: 60 seconds # Reports can be slow
# Extract grid data
Run VBScript: [ExtractALVGrid.vbs]
Output: %RawGridData%
# Parse into DataTable
[Split, loop, and build DataTable as shown earlier]
# Create Excel file with timestamp
Get Current Date and Time → %Now%
Format DateTime: %Now% as "yyyyMMdd_HHmmss" → %Timestamp%
Set Variable: %OutputFile% = %OutputFolder% & "ME2M_" & %PlantCode% & "_" & %Timestamp% & ".xlsx"
Launch Excel (New Document)
Write DataTable to Excel: %ExtractedData% starting at A1
Include Column Headers: Yes
Save Excel As: %OutputFile%
Close Excel
Wrap steps 3 and 4 inside an On Block Error block with 2 retries and the session recovery subflow as the error handler. Add logging at the end that writes the output file path, row count extracted, and run timestamp to a CSV log file in the output folder.
"Object variable or With block variable not set" in VBScript
This almost always means GetObject("SAPGUI") failed — either SAP isn't running, or scripting isn't enabled. Add an On Error Resume Next check right after GetObject and output the error number. If it's non-zero, trigger your LaunchSAP subflow.
Grid data extraction returns empty strings for all cells The grid ID is wrong. Use SAP GUI's built-in script recorder (Tools > Script > Record and Playback) to capture a mouse click on the grid — this reveals the exact technical ID in the recorded script. Copy it directly into your VBScript.
Dates not being accepted on selection screens
SAP selection screens validate date format strictly. Use the Technical Information popup on the date field to check if it expects MM/DD/YYYY or DD.MM.YYYY, then format your date variables accordingly in PAD before passing them to the VBScript.
Flow times out waiting for the ALV grid after F8 Some ME-series transactions scan millions of records and genuinely take 60-90 seconds. Increase your wait timeout. Better yet, apply more restrictive selection criteria (narrower date ranges, specific material groups) to make the query faster. Never run an extraction without at least one indexed selection field populated.
"Cannot attach to SAP GUI" in unattended mode Unattended flows run in an isolated Windows session. If SAP GUI was started in the interactive user session and your flow runs in the automation session, they can't see each other. Make sure your unattended flow launches SAP itself rather than relying on a pre-existing session. See Attended vs Unattended RPA: Choosing a Run Mode and Configuring Machines in Power Automate for the full context on session isolation.
Modal dialog appears but the dismiss VBScript does nothing
SAP has multiple popup types. If sendVKey(0) doesn't work, try pressing the specific button. Also check whether the dialog is actually at wnd[1] — if there's already a wnd[1] from a previous operation that wasn't dismissed, your new popup could be at wnd[2]. Iterate through all open windows to find it.
Tip
Build a dedicated "SAP Screen Spy" diagnostic flow that simply attaches to SAP, dumps the entire window hierarchy (all children and their IDs) to a text file, and stops. Run this against any SAP screen you're trying to automate to get a complete map of every element and its technical ID before you start writing your production automation.
You now have the full picture for production SAP GUI automation with Power Automate Desktop. The key principles to carry forward:
findById, StartTransaction, GetCellValue) for reliable, position-independent interaction. Don't rely on coordinate-based clicking for anything in SAP.From here, consider these natural extensions:
SAP automation rewards the practitioner who takes the time to understand the system's architecture. The scripting API is powerful, the technical IDs are stable, and once your flow is built with proper error handling, it runs with minimal maintenance — freeing your analysts from that 45-minute morning ritual permanently.
Power Automate Desktop & RPA
Automating Data Entry into Windows Desktop Applications with Power Automate Desktop: Launching Apps, Navigating Forms, and Submitting Records Reliably
Working with Excel Ranges, Sheets, and Named Cells in Power Automate Desktop: Reading Tables, Writing Data, and Switching Worksheets Without Macros