Skip to content

Developer Guide for RDS Remote Applications

This guide outlines the development and integration process for creating custom Remote Applications that support Zero Trust Access through the 12Port Access Broker. It details the execution flow, required application behavior, configuration expectations, and provides a scripting template for implementing automation using AutoIT.

This guide is intended for developers building or adapting Remote Applications for use in RDS environments managed by the Access Broker.


Execution Flow

When a user launches an RDS Remote Application session via the Access Broker, the following execution flow is triggered:

1. Remote Application Launch

* The Access Broker uses the value in the Remote App field of the main asset to determine the name of the Remote Application to launch on the RDS server.

2. Parameter Injection

* The Remote Arguments field defines a comma-separated list of main asset fields to pass to the Remote Application. Host field is hidden by default.
* For example:

Host,User,Password
* The Access Broker retrieves the values of these fields and assembles them into an ENTER-separated list of name=value pairs.

Example Arguments List:

Host=db.host.com
User=db-admin
Password=secret

3. Encoding

* The argument list is then base64-encoded and passed as a single argument to the Remote Application executable.

4. Connection Initialization

* The Access Broker uses the selected Member Asset (from the Session Launcher Credential Type) to connect to the RDS server. It passes the remote application with the base64-encoded argument as an initial remote application to launch.

5. Input Delay

* User input is blocked for the duration specified in the Input Delay field (hidden by default) of the main asset (default: 10 seconds), allowing the Remote Application to initialize without interference.

6. Remote Application Execution

* The Remote Application decodes the base64 string, parses the parameters list, and proceeds to launch the target destination application.
* Depending on implementation, it may populate UI fields (e.g., login forms) using the provided credentials.

7. Input Unlocked

* After the delay, the Access Broker unlocks the session for user interaction.

8. Session Scope

* The session includes only applications launched by the Remote Application on the RDS host.
* The session terminates when the last application is closed.


Remote Application Requirements

A Remote Application must meet the following requirements to function properly within this framework:

  • Must accept a single base64-encoded string as a command-line argument. This string represents an ENTER-separated list of key-value pairs.
  • Must decode and parse this argument to extract parameters used for authentication or connection.
  • Should handle its own logic for launching the target application and applying authentication or configuration.

Example PowerShell Publication

Publish the Remote Application on the RDS host using the following example PowerShell command template or using the Remote Desktop Services GUI:

New-RDRemoteApp `
  -CollectionName "QuickSessionCollection" `
  -Alias "remote-app-name" `
  -DisplayName "remote-app-name" `
  -FilePath "C:\Path\To\remote-app-full-path.exe" `
  -ShowInWebAccess $true `
  -CommandLineSetting "Allow"
  • remote-app-name: Name of the published Remote Application.
  • remote-app-full-path.exe: Full file path to the executable.
  • CommandLineSetting "Allow": This setting must be configured to allow arguments to be passed to the Remote Application.

⚠️ It is critical that the CommandLineSetting is set to "Allow" as this enables the Access Broker to pass the encoded argument to the application. Failure to set this will prevent parameter injection.


Security Considerations

  • The Remote Application executable should be digitally signed, or added as an exception to endpoint protection or antivirus tools on the RDS server.
  • Applications should be hardened and tested for reliability and security, especially when handling sensitive credential injection.

Remote Application Documentation Requirements

Each Remote Application should be packaged with the following documentation:

  • Prerequisites: OS compatibility, dependencies, and any required third-party components.
  • Installation Instructions: Steps to install the application on the RDS server, including download links to binaries.
  • Field Requirements:
    • List of required fields in the RDS Remote Application asset.
    • The value to use in the Remote App field (e.g., ||RemoteAppName).
    • The value to use in the Remote Arguments field (e.g., Host,User,Password).

AutoIT Development Template

Below is a high-level AutoIT script template that can be used to build Remote Applications compatible with the Access Broker. The script should perform the following functions:

Core Logic:

  1. Decode the Arguments
    * Base64-decode the argument string passed to the application.
  2. Parse Parameters
    * Extract and store parameters such as $User, $Password, etc.
  3. Read INI File
    * Load configuration values such as the target executable path from an application.ini file.
  4. Launch Target Application
    * Start the executable defined in the INI file.
  5. Automate Authentication
    * Populate login forms or other UI components using the decoded parameter values.


This template can be used with minor modifications to support most Windows-based fat client applications that require automated login or configuration at startup.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#include <MsgBoxConstants.au3>

; ----------------------------
; Function: ParseBase64Pairs
; ----------------------------
; Accepts: Base64 string
; Returns: Map of name=value pairs (keys are names, values are values)
; On error: returns an empty map and sets @error = 1
;
Func ParseBase64Pairs($sB64)
    Local $mOut[] ; create empty map

    ; Decode Base64 -> text
    Local $sText = _DecodeBase64ToString($sB64)
    If @error Then Return SetError(1, 0, $mOut)

    ; Normalize newlines
    $sText = StringReplace($sText, @CRLF, @LF)
    $sText = StringReplace($sText, @CR, @LF)
    $sText = StringReplace($sText, "\n", @LF)

    ; Split into lines
    Local $aLines = StringSplit($sText, @LF, 2)
    For $i = 0 To UBound($aLines) - 1
        Local $line = StringStripWS($aLines[$i], 3)
        If $line = "" Then ContinueLoop
        Local $p = StringInStr($line, "=", 0, 1)
        If $p = 0 Then ContinueLoop

        Local $name  = StringStripWS(StringLeft($line, $p - 1), 3)
        Local $value = StringStripWS(StringMid($line, $p + 1), 3)

        $mOut[$name] = $value
    Next

    Return $mOut
EndFunc

; ----------------------------
; Base64 decode helper
; ----------------------------
Func _DecodeBase64ToString($sB64)
    Local $b = _Base64Decode($sB64)
    If @error Or Not IsBinary($b) Or BinaryLen($b) = 0 Then Return SetError(1, 0, "")

    ; Assume UTF-8
    Local $s = BinaryToString($b, 4)
    If @error Then $s = BinaryToString($b) ; fallback ANSI
    Return $s
EndFunc

Func _Base64Decode($s)
    ; First call: get buffer size
    Local $a = DllCall("Crypt32.dll", "bool", "CryptStringToBinaryW", _
        "wstr", $s, "dword", 0, "dword", 1, _
        "ptr", 0, "dword*", 0, "dword*", 0, "dword*", 0)
    If @error Or Not $a[0] Then Return SetError(1, 0, Binary(""))

    Local $cb = $a[5]
    Local $tBuf = DllStructCreate("byte[" & $cb & "]")

    ; Second call: actual decode
    $a = DllCall("Crypt32.dll", "bool", "CryptStringToBinaryW", _
        "wstr", $s, "dword", 0, "dword", 1, _
        "ptr", DllStructGetPtr($tBuf), "dword*", $cb, "dword*", 0, "dword*", 0)
    If @error Or Not $a[0] Then Return SetError(2, 0, Binary(""))

    Return Binary(DllStructGetData($tBuf, 1))
EndFunc

Func _ToBool($s)
    ; Accepts true/false/yes/no/on/off/1/0 (case-insensitive)
    If StringRegExp($s, '^(?i)(1|true|yes|on)$') Then Return True
    Return False
EndFunc

If $CmdLine[0] >= 1 Then
    ; --------------------------
    ; Decode the arguments
    ; --------------------------

    Local $pairs = ParseBase64Pairs($CmdLine[1])
    If @error Then
        MsgBox($MB_ICONERROR, "Error", "Failed to parse Base64 string.")
        Exit
    EndIf

    ; --------------------------
    ; Access asset parameters
    ; --------------------------

    Local $user = $pairs["User"]
    Local $password = $pairs["Password"]

    ; ---------------------------------
    ; Applicaiton specific logic: BEGIN
    ; ---------------------------------

    ; --- config ---
    ; INI format expected (same folder as script):
    ; [app]
    ; exe=C:\Path\To\YourApp.exe
    ; trial=true

    ; ===== Script =====
    Opt("SendKeyDelay", 20)
    Opt("SendKeyDownDelay", 5)

    ; --------------------------
    ; Read INI file
    ; --------------------------

    Local Const $gIniPath = @ScriptDir & "\application.ini"
    Local Const $gSection = "app"

    ; Read settings
    Local $exe   = IniRead($gIniPath, $gSection, "exe", "")

    If $exe = "" Then
        MsgBox(16, "Error", "Missing [app]/exe in " & $gIniPath)
        Exit 1
    EndIf

    ; If exe is a relative path, make it relative to the script folder
    If Not FileExists($exe) And FileExists(@ScriptDir & "\" & $exe) Then
        $exe = @ScriptDir & "\" & $exe
    EndIf

    ; --------------------------
    ; Launch the target
    ; --------------------------
    Local $pid = Run('"' & $exe & '"', @ScriptDir)
    If @error Or $pid = 0 Then
        MsgBox(16, "Error", "Failed to start: " & $exe)
        Exit 2
    EndIf

    ; Give the app time to show its first inputable window
    ; (If you know the window title/class, replace Sleep with WinWaitActive)
    Sleep(1200)

    ; --------------------------
    ; Authenticate
    ; --------------------------

    ; Now type credentials: $user <TAB> $password <ENTER>
    ; Ensure $user and $password are defined in your script.
    Send($user)
    Send("{TAB}")
    Send($password)
    Send("{ENTER}")

    ; ---------------------------------
    ; Applicaiton specific logic: END
    ; ---------------------------------
Else
    MsgBox($MB_ICONWARNING, "Usage", 'Run with Base64 string as argument')
EndIf