Mainframes still run the world's most critical business data, but extracting it means navigating green screen terminals most modern tools can't touch. This expert-level lesson teaches you how to connect Power Automate Desktop to TN3270 and TN5250 emulators, navigate menu hierarchies reliably, and parse fixed-width screen data into structured tables for modern system integration.

Somewhere in your organization's data center — possibly humming in a room that smells faintly of the 1980s — there is almost certainly a mainframe. IBM z/OS systems running CICS, IMS, or batch jobs still process an estimated 95% of ATM transactions, 80% of in-person credit card transactions, and the majority of global airline reservations. These systems aren't going away anytime soon, and the data locked inside them is critically valuable. The problem is that extracting it means dealing with green screen terminals: character-mode interfaces that feel utterly alien to anyone raised on graphical UIs, REST APIs, and cloud dashboards.
Your mission, if you're reading this lesson, is to automate interactions with those terminals using Power Automate Desktop — sending keystrokes to navigate menu trees, reading structured data from fixed-width character screens, and piping it into modern systems. This is genuinely difficult automation work. The screens are stateless from your bot's perspective. There's no DOM, no accessibility tree, no JSON response. There are just 24 rows of 80 columns of characters and a protocol that speaks in data streams rather than HTTP. But with the right architecture, you can build flows that are every bit as reliable as anything you'd write against a modern API.
By the end of this lesson, you'll know how to connect Power Automate Desktop to both TN3270 (IBM AS/400 and z/OS descendants) and TN5250 (IBM iSeries) emulators, navigate multi-level menu systems programmatically, use fixed-position screen scraping to extract structured records, handle session errors and timeouts gracefully, and route that data into Excel, databases, or cloud flows. This is expert-level RPA, and we're going to treat it that way.
What you'll learn:
You should come to this lesson with solid Power Automate Desktop fundamentals. If you're new to the platform, work through Getting Started with Power Automate Desktop: Installing, Recording, and Running Your First Desktop Flow before proceeding. You'll also want to be comfortable with data tables and variables — the data extraction sections of this lesson assume that foundation, which is covered thoroughly in Variables, Lists, and Data Tables in Power Automate Desktop: A Complete Practitioner's Guide.
On the infrastructure side, you'll need:
Before you write a single action in your desktop flow, you need to understand what you're connecting to, because the choice of protocol fundamentally shapes how your emulator behaves and which automation techniques apply.
TN3270 is the telnet extension that enables TCP/IP connectivity to IBM mainframe environments — primarily z/OS (formerly OS/390 and MVS), VM/CMS, and VSE. The "3270" refers to the IBM 3270 terminal family introduced in 1971. The protocol is block-mode rather than character-mode: data is buffered locally, the user fills in fields, and the entire screen buffer is sent to the host when the user presses a transmit key (Enter, a PF key, or PA key). This is critically different from a telnet session where every keystroke travels to the server. Your automation must account for this: you send keystrokes, then wait for the host to respond with a complete new screen state before reading anything.
TN5250 serves IBM iSeries (now called IBM i) and AS/400 systems. The protocol is conceptually similar to TN3270 but uses a different data stream format. Screens are defined by Data Description Specifications (DDS), and the emulator renders fields with explicit input-capable regions. One practical difference for automation: TN5250 screens often have named fields in the emulator's programming interface, whereas TN3270 field identification typically relies on cursor position or screen coordinates.
For automation purposes, the most important implication is this: both protocols require you to wait for screen transitions, not just for keypresses to be accepted. Your flow must detect "the screen I expected has arrived" before proceeding. Rush ahead and you'll be typing into the wrong screen or reading stale data.
Key insight
Unlike web or Windows GUI automation where you're interacting with a live, event-driven UI, mainframe terminal sessions are fundamentally a request-response cycle. Every navigation action is a round trip to the host. Design your flows around explicit wait-and-verify patterns, not optimistic sequential actions.
Power Automate Desktop doesn't have built-in mainframe terminal actions the way it has dedicated SAP GUI support. Instead, you're automating the terminal emulator as a Windows application. This gives you two broad strategies:
Both strategies have merit, and the best approach often combines them.
Rumba is the most common enterprise TN3270/TN5250 emulator in Windows environments and exposes a robust COM automation interface called the Rumba Object Framework. Through this API, you can send keystrokes, read screen content by row/column position, check connection status, and even query field attributes programmatically — all without clicking on the screen at all.
To enable COM access, open Rumba, go to Tools → Options → API, and enable "Allow Macro API access." Note the session name, which you'll use in your PowerShell or VBScript automation code.
PCOM also exposes a COM automation interface called the EHLLAPI (Emulator High-Level Language Application Programming Interface) — an ancient but surprisingly capable API that IBM has maintained for decades. PCOM additionally exposes a more modern Object API through PCSAPI.exe and COM classes.
EHLLAPI uses a function-number-based interface: you call SendKeys, CopyPresentation, SearchPresentation, and similar functions by passing integer function codes. It's verbose, but it works reliably.
For environments where commercial licenses aren't available, x3270 (for TN3270) and tn5250 provide command-line and scriptable interfaces. The x3270if utility in the x3270 suite allows script-driven session control. These are excellent for development and testing, though less common in enterprise production environments.
For this lesson, we'll use a hybrid approach that works across emulators: PowerShell scripts invoked from Power Automate Desktop for COM-based interaction (where the emulator supports it), and screen region OCR/text extraction as a fallback. This mirrors real-world production automation where you want programmatic control but need OCR as a safety net for screens that resist structured extraction.
If you're new to running scripts from your flows, Scripting Inside Desktop Flows: Running PowerShell, Python, and VBScript Actions is required reading alongside this lesson.
Start with a clean session launch. Don't assume the emulator is already running — your flow should own the full lifecycle. Use the "Run application" action to launch the emulator executable with the session file path as an argument:
Application path: C:\Program Files\Micro Focus\Rumba\Desktop\RumbaDesktop.exe
Arguments: "C:\SessionFiles\MainframeHost.rss"
Window style: Normal
After application launch: Wait for application to load
After launch, add a "Wait" action of 3–5 seconds to allow the emulator to establish the TCP connection to the host. This is one of those places where a hard wait is actually appropriate: the connection involves a TCP handshake, TN3270/TN5250 negotiation, and a host initialization sequence that doesn't have a predictable UI signal you can wait on.
Warning
Never assume the session is connected just because the emulator window is open. Always verify connection state before sending keystrokes. A disconnected session will silently eat your keystrokes, and your flow will navigate a static screen indefinitely.
Here's a PowerShell snippet that checks Rumba's connection state via COM and returns it as a string your flow can inspect:
$rumba = New-Object -ComObject 'RUMBA.Session'
$sessions = $rumba.Sessions
if ($sessions.Count -eq 0) {
Write-Output "NO_SESSION"
exit 1
}
$session = $sessions.Item(1)
$connected = $session.Display.IsConnected
if ($connected) {
Write-Output "CONNECTED"
} else {
Write-Output "DISCONNECTED"
}
In your desktop flow, use "Run PowerShell script" and capture the output into a variable called SessionStatus. Then add a conditional:
IF SessionStatus = "DISCONNECTED"
Run subflow: ReconnectSession
END IF
For PCOM users, the equivalent check uses the PCOM Object API:
$pcom = New-Object -ComObject 'PCSOBJ.PCSOBJ'
$session = $pcom.OpenSession("A")
$status = $session.autECLSession.Started
Write-Output $status
Most mainframe environments present a login screen after connection. The login screen is a known fixed state — one of the few moments when you can actually rely on screen coordinates because the layout is standardized by the host environment's configuration.
Send the user ID and password using the "Send keys to window" action, targeting the emulator window by title. Before sending credentials, verify you're on the login screen by checking for expected text in a known position — typically the application name or the "VTAM" prompt.
For credential management, avoid hardcoding usernames and passwords in your flow variables. Instead, retrieve them from a secure source at runtime. The lesson on Handling Credentials Securely in Desktop Flows: Sensitive Variables and Azure Key Vault shows you exactly how to pull credentials from Azure Key Vault — essential reading for any production mainframe automation.
Here's the login sequence in pseudocode:
Run PowerShell: GetScreenText → ScreenContent
IF NOT ScreenContent Contains "TSO/E LOGON" THEN
Throw exception: "Unexpected screen after connection"
END IF
Send keys to window: UserID → {Tab}
Send keys to window: Password → {Enter}
Wait: 2 seconds
Run PowerShell: GetScreenText → PostLoginScreen
IF PostLoginScreen Contains "IKJ56421I" THEN
Throw exception: "Login failed - bad credentials"
END IF
The message IKJ56421I is a z/OS TSO error indicating an invalid password. Build a dictionary of these system messages — you'll use them throughout your error handling.
This is where mainframe automation diverges most sharply from everything else you automate in Power Automate Desktop. There's no DOM, no accessibility properties, no table element to extract. There's a 24×80 (or 32×80 for extended displays) grid of characters. Every piece of data extraction depends on knowing the exact row and column where a value lives.
The Rumba Object Framework lets you read text from specific screen positions:
$rumba = New-Object -ComObject 'RUMBA.Session'
$session = $rumba.Sessions.Item(1)
$display = $session.Display
# Read a single cell
$cell = $display.ScreenCellAt(5, 20) # Row 5, Column 20
Write-Output $cell.Text
# Read a row range
$rowText = $display.ScreenCellsAt(10, 1, 10, 80) # Row 10, all 80 columns
$rowString = ($rowText | ForEach-Object { $_.Text }) -join ""
Write-Output $rowString
# Read a rectangular region
$region = $display.ScreenCellsAt(12, 5, 20, 74)
This PowerShell runs inside a "Run PowerShell script" action, and the output gets captured as a variable in your flow. For multi-row data — a list of accounts, transaction records, or inventory items — you'll call a version of this script that reads multiple rows and returns them as a delimited string.
Rather than scattering PowerShell calls throughout your flow, encapsulate screen reading in a reusable subflow. Following the patterns in Subflows and Reusable Logic in Power Automate Desktop, create a GetScreenRegion subflow that accepts StartRow, EndRow, StartCol, EndCol as input variables and returns ScreenText as output.
The PowerShell inside this subflow:
param(
[int]$StartRow,
[int]$EndRow,
[int]$StartCol,
[int]$EndCol
)
$rumba = New-Object -ComObject 'RUMBA.Session'
$session = $rumba.Sessions.Item(1)
$display = $session.Display
$lines = @()
for ($row = $StartRow; $row -le $EndRow; $row++) {
$cells = $display.ScreenCellsAt($row, $StartCol, $row, $EndCol)
$line = ($cells | ForEach-Object { $_.Text }) -join ""
$lines += $line
}
Write-Output ($lines -join "`n")
Pass %StartRow%, %EndRow%, %StartCol%, %EndCol% as PowerShell arguments from your flow variables.
Tip
Always read the entire row (columns 1-80) rather than just the region you think contains data. Screen layouts can shift slightly depending on host configuration, data length, or seasonal screen variants. It's easier to substring a full row in PAD than to recalibrate row/column coordinates when something moves.
For emulators without a COM API, or for portions of the screen with complex rendering (like color attributes that affect text layout), OCR is a viable fallback. Power Automate Desktop's "Extract text with OCR from screen" action can capture a screen region. Configure it with the Windows OCR engine (more reliable for monospace fonts than Tesseract in this context) and set the capture area to the emulator's character display region.
The challenge with OCR on green screen terminals is that monospace fonts + high contrast green-on-black can confuse OCR on certain character combinations (1/I/l, 0/O, S/5). Validate OCR output against expected patterns using regular expressions, and fall back to "Send keys" and re-read if validation fails.
For a deeper treatment of OCR-based extraction, see Extracting Text from PDFs, Images, and Scanned Documents with OCR in Power Automate Desktop — the OCR configuration principles there apply directly to terminal screen capture.
Real mainframe applications rarely expose their data in a single screen. You typically navigate a tree: a top-level menu leads to a subsystem menu, which leads to a function selection, which leads to a parameter input screen, which finally leads to the data display. Each level requires a different key sequence, and you must verify screen transitions at every step.
Build a library of "screen signatures" — short strings that uniquely identify each screen you'll navigate through. For a z/OS TSO/ISPF environment, these might look like:
| Screen | Signature Text | Row | Column |
|---|---|---|---|
| ISPF Primary Menu | "ISPF PRIMARY OPTION MENU" | 1 | 28 |
| ISPF Option 3.4 - DSLIST | "DATA SET LIST UTILITY" | 1 | 30 |
| File-AID Main Menu | "FILE-AID PRIMARY MENU" | 2 | 25 |
| CICS Transaction Screen | "YOUR-TRAN-ID" | 1 | 2 |
Create a IdentifyCurrentScreen subflow that reads text at each signature position and returns a screen identifier string. This becomes the foundation of your navigation logic:
Call subflow: IdentifyCurrentScreen → CurrentScreen
SWITCH CurrentScreen
CASE "ISPF_PRIMARY"
Call subflow: NavigateToOption34
CASE "DSLIST"
Call subflow: EnterDatasetFilter
CASE "UNKNOWN"
Call subflow: HandleUnknownScreen
END SWITCH
Mainframe navigation relies heavily on Program Function (PF) keys. PF1–PF24 are the mainframe equivalent of F1–F24, but with meanings defined by each application. In terminal emulators, these map to keyboard keys, but the mapping varies by emulator configuration. Always document the PF key mapping for the emulator you're using.
In Power Automate Desktop's "Send keys" action:
{F1} through {F12} send PF1–PF12{F13} through {F24} send PF13–PF24 (if your keyboard supports them; otherwise configure the emulator's toolbar){Enter} sends the Enter/transmit key{Escape} or configured PA key sends PA1 (the "attention" key that interrupts processing)For ISPF navigation, the most common sequence is typing an option number followed by Enter:
Focus window: RumbaDesktop (by title)
Send keys: 3{Enter} ← ISPF Option 3 (Utilities)
Wait: 1.5 seconds
Call subflow: IdentifyCurrentScreen → CurrentScreen
IF CurrentScreen ≠ "ISPF_UTILITIES" THEN
Throw exception: "Navigation failed: expected ISPF Utilities"
END IF
Send keys: 4{Enter} ← Option 4 (DSLIST)
Wait: 1.5 seconds
Warning
Do not use fixed waits as your primary synchronization mechanism. They work in development but break in production when host response times vary under load. Always combine a short wait with a screen verification loop. A 5-second fixed wait that works on Tuesday morning may cause failures during month-end batch processing when the host is under heavy load.
Replace blind waits with an active polling loop:
Set variable: MaxWaitSeconds = 30
Set variable: WaitedSeconds = 0
Set variable: TargetScreen = "DSLIST"
Set variable: ScreenFound = FALSE
LOOP WHILE ScreenFound = FALSE
Call subflow: IdentifyCurrentScreen → CurrentScreen
IF CurrentScreen = TargetScreen THEN
Set variable: ScreenFound = TRUE
ELSE IF CurrentScreen = "KEYBOARD_LOCKED" THEN
Wait: 1 second
Set variable: WaitedSeconds = WaitedSeconds + 1
ELSE IF CurrentScreen = "ERROR_MESSAGE" THEN
Call subflow: CaptureAndLogErrorScreen
Throw exception: "Host returned error screen"
ELSE
Wait: 1 second
Set variable: WaitedSeconds = WaitedSeconds + 1
END IF
IF WaitedSeconds > MaxWaitSeconds THEN
Throw exception: "Timeout waiting for screen: " + TargetScreen
END IF
END LOOP
The "KEYBOARD_LOCKED" state deserves special mention. In block-mode terminals, the keyboard locks during host processing — the emulator accepts no input until the host sends a response. Your PowerShell screen-reading code should check for this state:
$oid = $display.OIA # Operator Information Area
$locked = $oid.InputInhibited
if ($locked) { Write-Output "KEYBOARD_LOCKED" }
Many mainframe functions require parameter input: a date range, an account number, a report code. These screens have explicitly positioned input fields. Tab to each field (or position the cursor using the emulator's tab sequence) and type the value.
For ISPF screens, the Tab key advances to the next input field. For CICS screens, the cursor position within the protected/unprotected field map determines where input goes. Always confirm you're in the right field before sending data:
# Check cursor position
$cursor = $display.CursorPos
$row = $cursor.Row
$col = $cursor.Col
Write-Output "$row,$col"
Compare returned row/col against expected values. If the cursor is in the wrong position, your previous Tab or navigation command didn't land where expected — a common failure mode when a prior screen displayed an extra warning message that shifted the layout.
The real payload of most mainframe automation is a list of records: open purchase orders, general ledger transactions, inventory movements, customer account summaries. These appear as a table-like display across multiple rows of the screen, and you may need to page through multiple screens to collect all records.
Mainframe screen data is almost always fixed-width. Column positions are hard-coded in the host application, so Account Number is always in columns 2-12, customer name is always in columns 14-43, balance is always in columns 45-58, and so on. Document these positions from a sample screen — print it on paper, count characters, and record the mapping.
A PowerShell function to extract a fixed-width record from a screen row:
function Parse-TransactionRow {
param([string]$Row)
# Fixed positions (1-based, converted to 0-based for substring)
$acctNum = $Row.Substring(1, 12).Trim() # Cols 2-13
$custName = $Row.Substring(13, 30).Trim() # Cols 14-43
$tranDate = $Row.Substring(43, 8).Trim() # Cols 44-51
$amount = $Row.Substring(51, 14).Trim() # Cols 52-65
$tranCode = $Row.Substring(65, 4).Trim() # Cols 66-69
# Filter out blank rows (list screens often have blank separators)
if ([string]::IsNullOrWhiteSpace($acctNum)) { return $null }
return "$acctNum|$custName|$tranDate|$amount|$tranCode"
}
# Read rows 6 through 22 (the data area on this screen)
$allRows = @()
for ($r = 6; $r -le 22; $r++) {
$cells = $display.ScreenCellsAt($r, 1, $r, 80)
$rowText = ($cells | ForEach-Object { $_.Text }) -join ""
$parsed = Parse-TransactionRow -Row $rowText
if ($parsed) { $allRows += $parsed }
}
Write-Output ($allRows -join "`n")
In your desktop flow, receive this pipe-delimited output and split it into a data table:
Set variable: RawData = [PowerShell output]
Split text: RawData by newline → RowList
Create new data table with columns:
AccountNumber, CustomerName, TransactionDate, Amount, TransactionCode
FOR EACH Row in RowList
IF Row is not empty
Split text: Row by "|" → Fields
Add row to data table:
AccountNumber = Fields[0]
CustomerName = Fields[1]
TransactionDate = Fields[2]
Amount = Fields[3]
TransactionCode = Fields[4]
END IF
END FOR
Most mainframe list screens display 15-20 records at a time and use PF7 (scroll up) and PF8 (scroll down) to page through data. Your extraction loop must detect when more pages exist and continue extracting until it reaches the end.
End-of-data indicators vary by application, but common patterns include:
Build a HasMoreData detection into your extraction loop:
Set variable: AllTransactions = [empty data table]
Set variable: MorePagesExist = TRUE
LOOP WHILE MorePagesExist = TRUE
Call subflow: ExtractCurrentPageRecords → PageRecords
Append rows: PageRecords to AllTransactions
Run PowerShell: CheckBottomOfData → StatusMessage
IF StatusMessage Contains "BOTTOM OF DATA"
OR StatusMessage Contains "+++ END +++"
OR PageRecords.RowCount = 0 THEN
Set variable: MorePagesExist = FALSE
ELSE
Send keys: {F8} ← PF8 = scroll forward
Call subflow: WaitForScreen("SAME_LIST_SCREEN")
END IF
END LOOP
The CheckBottomOfData PowerShell reads the standard message area — typically row 23 or 24 in ISPF, row 1 in some CICS applications — and returns the content.
Note
Some mainframe applications don't use PF8 scrolling. They use command input — you type "NEXT" or a page number in a command field. Document which navigation model applies to each function you're automating. The navigation pattern is part of your screen specification, just like column positions.
If you're extracting thousands of records, pagination through a terminal becomes slow. Each page requires a host round trip (500ms–2 seconds depending on host load), screen reading, and parsing. Ten thousand records across 500+ pages could take 10–30 minutes.
Consider these optimization strategies:
Batch by date or key range: Instead of one extraction for all records, invoke the function multiple times with narrower date ranges and merge results in Excel or a database.
Request host-side reports: Many mainframe applications can generate a flat file or report that you extract via FTP rather than screen scraping. If available, this is always preferable. RPA should be the option of last resort, not the first.
Parallel sessions: If your mainframe license allows multiple concurrent sessions per user ID, run multiple desktop flows against different date ranges simultaneously. See Attended vs Unattended RPA: Choosing a Run Mode and Configuring Machines in Power Automate for the infrastructure considerations.
You've navigated the menus, extracted the records, built the data table. Now you need to put it somewhere useful.
For operational reporting, Excel is the most common destination. Use the "Write to Excel worksheet" action with your extracted data table. If you're doing this regularly, set up a template Excel file with predefined headers, formatting, and any formulas, then write data starting at row 2.
For Excel-heavy post-processing, Automating Excel with Power Automate Desktop: Reading, Writing, and Running Macros covers the full range of write actions, named range targeting, and macro invocation that you'll want after the extraction phase.
For maximum compatibility, write your data table to a CSV file first, then let downstream systems consume it. This decouples the extraction timing from the consumption timing — a mainframe extraction at 6 AM can produce a CSV that's processed by a cloud flow at 7 AM after validation.
Configure the "Write data table to CSV" action with:
| if account names or descriptions might contain commas — they often do on mainframe systems)The most powerful integration pattern wraps your mainframe extraction in a desktop flow that's triggered from a cloud flow, and then returns the extracted data as output for further cloud processing — routing to SharePoint, Dataverse, or an HTTP connector. For production deployments of this pattern, read Triggering Desktop Flows from Cloud Flows: Passing Inputs and Returning Outputs, which covers the input/output variable configuration that enables this integration.
Mainframe sessions fail in ways that are completely unlike web or Windows app automation. The failures are often silent, delayed, or expressed through cryptic system messages. You need a comprehensive error handling strategy that covers all the failure modes.
Session drops: The TCP connection to the host terminates. The emulator shows a "Connection lost" dialog or simply displays a blank/frozen screen. Detect by checking IsConnected before every navigation step, not just at startup.
Keyboard lock without resolution: The keyboard enters a locked state (host processing) and doesn't unlock within a reasonable time. The host may have hung, or a long-running job is blocking the session. After your timeout threshold, send the PA1 key (attention interrupt), wait 2 seconds, and check if the keyboard unlocks. If not, kill and restart the emulator session.
Wrong screen: You sent an option and received an unexpected screen — perhaps because a security message appeared, or a popup warning intercepted the navigation. Your screen identification subflow should catch this and route to a cleanup handler.
Data-level errors: The host accepted your parameters but returned a "RECORD NOT FOUND" or "NO DATA FOR DATE RANGE" message instead of records. These aren't system errors — they're valid business results you need to handle and log.
Session timeout (VTAM timeout): Many z/OS environments disconnect idle sessions after 5-30 minutes. If your extraction takes longer than the VTAM timeout, the session will be killed mid-extraction. Configure the emulator's keep-alive feature (sends a null character periodically to prevent timeout) or ensure your host admin sets an appropriate timeout for the service account.
Wrap every navigation block in an error handler using the "On block error" action. For detailed patterns on structured error handling in PAD, review Error Handling in Desktop Flows: On Block Error, Retry Policies, and Recovery Screenshots.
A mainframe-specific error handler skeleton:
ON BLOCK ERROR
MODE: Continue flow on error
# Take screenshot for diagnostics
Take screenshot: FullScreen → ErrorScreenshot
# Capture current screen text for logging
Run PowerShell: CaptureFullScreen → ErrorScreenText
# Log to error table
Add row to data table: ErrorLog
Timestamp = DateTime.Now
FlowStep = %CurrentStepName%
ScreenContent = %ErrorScreenText%
Screenshot = %ErrorScreenshot%
# Attempt session recovery
Call subflow: RecoverTerminalSession
# If recovery succeeds, retry; otherwise propagate
IF SessionRecovered = TRUE THEN
Retry action (max 3 times)
ELSE
Throw exception: "Unrecoverable mainframe session error"
END IF
END ON ERROR
The RecoverTerminalSession subflow should:
SessionRecovered booleanKey insight
Mainframe sessions are expensive to establish — each new session involves VTAM negotiation, security validation, and ISPF initialization that can take 5-15 seconds. Build your recovery logic to restore existing sessions before creating new ones.
This exercise walks you through building a complete mainframe data extraction flow. You'll need a terminal emulator configured to connect to a mainframe (or a mainframe emulation environment like IBM's Wazi as a Service for development).
Your finance team needs daily extraction of open accounts payable transactions from the mainframe. The data lives in CICS, accessible via the AP inquiry transaction (TRAN ID: APINQ). Each execution should extract all open items for a given vendor code, write them to an Excel file, and email a summary.
Create three subflows:
LaunchAndConnect: Launches emulator, waits for connection, verifies login screen appearsLogin: Sends credentials, waits for main menu, verifies successful authentication Logout: Navigates to the CICS clear command, sends CSSF LOGOFF, closes the emulatorUsing the COM API or OCR, read row 1 columns 1-80 and classify it. Your IdentifyCurrentScreen subflow should return one of: CICS_CLEAR, APINQ_MAIN, APINQ_RESULTS, APINQ_NEXT_PAGE, ERROR_MESSAGE, UNKNOWN.
Main flow sequence:
LaunchAndConnectLoginAPINQ + Enter → navigate to AP inquiryAPINQ_MAIN screenAPINQ_RESULTS or ERROR_MESSAGESet variable: AllAPItems = [new data table]
Set variable: ContinueExtracting = TRUE
LOOP WHILE ContinueExtracting = TRUE
Run PowerShell: ExtractAPResultsPage(6, 22) → RawPageData
Call subflow: ParseAPRows(RawPageData) → PageDataTable
Append PageDataTable to AllAPItems
Run PowerShell: ReadRow(23, 1, 80) → StatusRow
IF StatusRow Contains "NO MORE DATA" THEN
Set ContinueExtracting = FALSE
ELSE
Send keys: {F8}
Call subflow: WaitForScreen("APINQ_NEXT_PAGE", 15)
END IF
END LOOP
AllAPItems data table to Sheet1 starting at row 2Logout subflowValidation: Run the flow with a vendor code that returns 3 known transactions. Verify all three appear in the Excel output with correct values, especially date formatting (mainframe dates are often in YYYYDDD Julian format — build a conversion function in your ParseAPRows subflow).
This is the most common failure mode in screen-scraping-based terminal automation. The fix is to always explicitly set focus to the emulator window before sending keystrokes, using "Set window focus" or "Focus UI element" targeting the emulator's main display area. Additionally, use the COM API for keystrokes instead of OS-level key simulation wherever possible — COM API keystrokes go directly to the session object, not to whatever window has focus.
Usually caused by timing: you're reading the screen before the host has finished writing the response. Add a 500ms wait after the keyboard unlock condition is met, then read. The emulator may signal "keyboard unlocked" (host is done transmitting) fractionally before all characters are rendered in the display buffer.
Check whether the emulator has remapped PF keys. In Rumba, go to File → Session Properties → Keyboard to verify the PF key mappings. Also check whether the application is intercepting PF keys for its own use — some CICS applications remap PF3 to "return to previous menu" rather than the ISPF standard "end."
Some mainframe applications re-display the last page of data rather than showing a blank screen when you PF8 past the end. Your "end of data" detection must check both the status message AND compare the first record of the current page against the first record of the previous page. If they match, you've looped back to the end.
Configure the emulator's keep-alive feature: in Rumba, Session Properties → Connection → Send NOP (No Operation) every N seconds. Set this to 60 seconds for sessions with a 5-minute VTAM timeout. Alternatively, send a benign keystroke (like moving the cursor with an arrow key that stays in a safe position) every 4 minutes as a heartbeat.
The emulator's COM registration may be missing or broken. Run regsvr32 RumbaSession.dll from an elevated command prompt in the Rumba installation directory. If using PCOM, run the PCOM repair installer. This is also a common failure after Windows updates that reset COM registration for 32-bit DLLs on 64-bit systems.
Tip
For debugging screen position issues, add a diagnostic subflow that captures the entire 24×80 screen and writes it to a text file with a timestamp. Run this at each navigation step during development. Reviewing these captures tells you exactly what the screen looked like when your flow made a decision, which is invaluable when a production failure generates an unexpected screen layout.
Before we wrap up, let's talk about how this fits into a production environment.
For extractions that run on a schedule without human involvement, you need an unattended configuration. This means the automation machine must have the terminal emulator session pre-configured (but not necessarily launched — your flow handles that), a service account with mainframe access, and proper credential management.
For unattended runs, the emulator cannot display modal dialogs that require user interaction — license expiry warnings, certificate errors, or "session already exists" prompts will block the flow indefinitely. Suppress all such dialogs in the emulator's configuration, or pre-accept any required certificates.
If you're running multiple concurrent mainframe extraction flows — different vendors, different date ranges, different CICS transactions — each instance needs its own terminal emulator session. You cannot share one emulator window across multiple flows. Size your machine group accordingly: if you need five concurrent extractions, you need either five machines or five separate session files on one machine with a PAD instance that knows which session it owns.
For the machine management infrastructure underlying this, Managing Machines and Machine Groups for Scalable Unattended Automation in Power Automate covers the setup in detail.
Service accounts used for RPA mainframe access should have the minimum RACF (or equivalent) profile needed for the transactions being automated. Specifically:
The mainframe is a high-value target. A compromised service account could access decades of financial data. Treat its credentials with the same seriousness as a production database password.
You've worked through the full stack of mainframe terminal automation: understanding protocol fundamentals that shape your architecture, configuring emulators for programmatic control, navigating menu trees with verified transitions, extracting fixed-width structured data across paginated screens, and building the error handling infrastructure that makes it production-ready.
The key principles to carry forward:
Protocol-first thinking: TN3270 and TN5250 are block-mode, request-response protocols. Design around explicit screen verification, not optimistic sequential actions.
COM API over screen scraping: When your emulator offers a programming interface, use it. Screen scraping is for emulators that don't.
Fixed-position parsing is your friend: Mainframe screens are rigidly structured. Column positions are stable across data volumes, date ranges, and system updates. Document them once and rely on them.
Session resilience is non-negotiable: Network blips, VTAM timeouts, and keyboard locks will happen in production. Build recovery paths into every flow, not as afterthoughts.
Minimize RPA, maximize integration: If the mainframe can generate a report or write a file that you can consume via FTP or SFTP, that's better than screen scraping 10,000 records page by page.
Once your mainframe extraction flows are stable, the natural evolution is integrating them into larger multi-system workflows — combining mainframe data with ERP records, web lookups, or database queries in a single orchestrated flow. The patterns for building these multi-hop integrations are covered in depth in Automating Multi-Application Workflows in Power Automate Desktop: Transferring Data Between Windows Apps, Web Browsers, and Excel in a Single Desktop Flow.
For enterprise-scale deployment where these flows need to run reliably around the clock with automatic error recovery and queue-driven dispatch, the orchestration framework patterns in Building a Resilient Unattended RPA Orchestration Framework in Power Automate Desktop: Queue-Driven Job Dispatch, Machine Load Balancing, and Automated Recovery for 24/7 Production Bot Fleets show you how to scale from a single working flow to a production fleet.
The mainframe isn't going anywhere. Your automation skills are what make the data inside it accessible to the modern systems that depend on it.
Power Automate Desktop & RPA