Um schneller in den Arbeitstag starten zu können, sollten automatisiert lokal auf einem Server bereits die RemoteApps für verschiedene Benutzer gestartet werden. Damit entfällt die Wartezeit, bis mehrere Programme vollständig geladen sind.

Das Vorhaben war letztlich aufwendiger als zuerst angenommen. Es gibt ein paar Punkte in diesem Szenario zu beachten, wie sich im Laufe der Realisierung zeigte. Im mehrfachen Sinne lehrreich war es auf jeden Fall.

Es gibt einen (imho) schnelleren bzw. einfacheren Weg, dennoch ist die eine oder andere Hintergrund-Info aus diesem Beitrag interessant. Zum “Kurzen Weg” geht’s hier lang:

Windows: RemoteApps für verschiedene Benutzer per Aufgabe automatisch starten und trennen – Der kurze Weg

Geplanter Ablauf

Vor Arbeitsbeginn, also mit genügend zeitlichem Vorlauf, sollen auf einem Terminalserver automatisch für diverse Benutzer die RemoteApps gestartet werden. Da man in Windows nur einen Satz Anmeldedaten pro Server hinterlegen kann, muss der Anmeldedialog der Remotedesktopverbindung automatisch mit verschiedenen Benutzername/Kennwort-Kombinationen ausgefüllt werden. Nach dem Start und Anmelden für den ersten Benutzer muss die Sitzung getrennt werden, da sonst keine weitere Anmeldung mit einem anderen Benutzer möglich ist, und dann die nächsten RemoteApps für den nächsten Benutzer gestartet bzw. angemeldet werden.

Das Ganze geschieht übrigens in der Konsolensitzung, da der Administrator automatisch angemeldet (und gleich gesperrt) wird. Nebenbei bemerkt: Die automatische Anmeldung vom Administrator erfolgt aus mehreren Gründen (div. abhängige Anwendungen) und wird in diesem Zusammenhang lediglich um die automatische Anmeldung/Trennung der RemoteApps erweitert.

Windows-Automatisierung: Unterschiede ob eine Sitzung entsperrt/verbunden, gesperrt oder getrennt ist

Mit AutoIt Interaktionen unter Windows zu automatisieren ist relativ einfach. So lassen sich schnell Skripte erstellen, mit dessen Hilfe Aktionen mit Fenstern, Eingaben etc. lösen lassen.

Im ersten Moment und wenn man sich damit bislang nicht auseinder gesetzt hat, wundert man sich, warum so gewohnte Befehlt wie “Send()” im gesperrten (Lokal oder RDP) oder getrennten Zustand (RDP) dann allerdings nicht funktionieren. Hierzu lieferte die AutoIt-FAQ die Erklärung:

“Why doesn’t my script work on a locked workstation?
On a locked station any window will never be active (active is only dialog with text “Press Ctrl+Alt+Del”). In Windows locked state applications run hidden (behind that visible dialog) and do not have focus and active status. So generally don’t use Send() MouseClick() WinActivate() WinWaitActive() WinActive() etc. Instead use ControlSend() ControlSetText() ControlClick() WinWait() WinExists() WinMenuSelectItem() etc. Doing so allows you to interact with an application regardless of whether it is active or not. It’s possible to run such a script from scheduler on locked Windows stations.”

Quelle: AutoIt – Wiki – FAQ – Why doesn’t my script work on a locked workstation?

Mit anderen Worten: Im gesperrten oder getrennten Zustand gibt es keine aktiven Fenster, es funktioniert dann auch nicht, einem Fenster den Fokus zu geben. Man muss also mit anderen Befehlen (s.o.) arbeiten und ggf. etwas tricksen. Dazu gleich mehr.

Windows-Sicherheit automatisch ausfüllen lassen

Das Anmeldefenster der Remotedesktopverbindung mit dem Titel “Windows-Sicherheit” automatisch ausfüllen zu lassen war relativ spannend. Im angemeldeten Zustand klappte das mit Send() ohne Probleme. Im gesperrten und getrennter Zustand musste dann ControlSend() herhalten. Das klappte nach einigen probieren dann endlich wie erwartet.

Interessanterweise gelang es nicht, direkt in die Felder für Benutzername und Kennwort die Daten eintragen zulassen, obwohl mit AutoItInfo mit richtigen IDs ausgelesen und im Skript bei “ControlSend()” angegeben wurden. Hier kommt dann das Tricksen im weitesten Sinne ins Spiel.

Damit in die richtigen Felder Benutzername und Kennwort eingetragen werden, wird ein Tastendruck auf “Down” (Pfeiltaste runter) ausgelöst, dann zunächst der Benutzername und nach einem weiteren “Runter” das Kennwort eingetragen. Das zugehörige AutoIt-Skript sieht wie folgt aus:

; Die Parameter den Variablen zuweisen

$Domain = $CmdLine[1]
$Username = $CmdLine[2]
$Password = $CmdLine[3]

; Dem Fenster "quasi" den Fokus geben/dieses aktivieren

ControlFocus ("Windows-Sicherheit", "", "")

; Kurze Pause, da sonst der folgende "Pfeil runter" nicht funktioniert

sleep (1000)

; Einmal "Pfeil runter" drücken

ControlSend ("Windows-Sicherheit", "", "", "{DOWN}")

; Kurze Pause, da sonst die Eingabe im falschen Feld erfolgt

sleep (500)

; Den Benutzernamen eintragen

ControlSend ("Windows-Sicherheit", "", "", $Domain & "\" & $Username)

; Zum Kennwort-Feld wechseln

ControlSend ("Windows-Sicherheit", "", "", "{DOWN}")

; Das Kennwort eintragen

ControlSend ("Windows-Sicherheit", "", "", $Password)

; ENTER drücken

ControlSend ("Windows-Sicherheit", "", "", "{ENTER}")

Domäne, Benutzername und Kennwort werden als Parameter übergeben. Beim zur *.exe-Datei kompilierten Skript sieht das so aus:

RDPAutoLogon.exe <Domain> <Username> <Password>

Hinweise: Wurde “mstsc” schonmal verwendet, dann wird der letzte verwendete Benutzername pro Ziel-Computer gespeichert. Bei der automatischen Eingabe der Zugangsdaten bei abweichendem Benutzername muss also zunächst “nach unten” gesprungen werden.

By the way: Hinterlegt ist der zuletzt verwendete Benutzername in der Registry unter

HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Servers\<IP der Hostname>

UsernameHint

Bei einer “jungfräulichen” mstsc bzw. wenn erstmals zu einen Zielserver verbunden wird, entfällt das erste “runter” drücken und man kann direkt den Benutzername etc. eintragen. Das gilt aber wirklich nur für das erste Mal!

Das Ganze hier wurde unter Windows Server 2012 R2 realisiert. Unter anderen Windows-Versionen kann der “Windows-Sicherheit”-Dialog varrieren, so das Anpassungen notwendig sind.

Windows-Aufgabenplannung: Aufgabe wird nur ausgeführt, wenn der Benutzer angemeldet und verbunden ist

Die Aufgabe wird also nur ausgeführt, wenn man wirklich angemeldet und aktuell verbunden ist. Im gesperrten oder getrennten Zustand geschieht schlicht nichts oder nicht richtig.

Die Aufgabe unabhängig vom angemeldeten Benutzer auszuführen bringt leider nichts, da dann die “Fenster-Automatisierung”, also das Eintragen der Zugangsdaten nicht greift.

Als Plan B musste eine andere Aufgabenplanung her, alternativ kann man ein Skript schreiben, das regelmässig die Uhrzeit prüft und nur zum festgelegten Zeitpunkt eine Aktion ausführt.

Als alternative Aufgabenplanung für diesen Job dient nun Solway’s Task Scheduler, siehe dazu den Beitrag Windows: Alternativen zur Aufgabenplanung.

Je nach Zustand unterschiedliches Verhalten von AutoIt

Ebenfalls unerwartet war das Verhalten von AutoIt im gesperrten/getrennten Zustand wenn es ums reine Ausführen geht. Während der Skript-Entwicklung wurde mit Hilfe der AutoIt3.exe das jeweilige Skript gestartet. Während es im angemeldeten Zustand einfach mit “AutoIt3.exe <Skript>” klappt, blieb es in den anderen Zuständen bei der Ausführung “hängen”. Gelöst werden konnte das so:

start /d <Ordner> AutoIt3.exe <Skript>

RemoteApp(s) trennen für den nächsten Benutzer

Wie man die RemoteApps bzw. generell RDP-Sitzungen trennen kann, ist im Beitrag Windows: RemoteApps trennen beschrieben. Auf die dortigen Skripte wird für dieses Vorhaben zurückgegriffen.

Das Gesamtkunstwerk

Lange Rede, kurzer Sinn: Das eigentliche Batch-Skript das als Aufgabe ausgeführt wird sieht (auszugsweise) so aus:

@echo off

rem Konfiguration

set SessionDomain=localhost
set SessionUsername=<Benutzername>
set SessionPassword=<Kennwort>

rem Automatische Anmeldung vorbereiten

cd C:\Skripte\RemoteAppAutoLogon

start RDPAutoLogon.exe %SessionDomain% %SessionUsername% %SessionPassword%

rem RemoteApps starten

mstsc "Outlook.rdp"

timeout /t 5 > nul

mstsc "Firefox.rdp"
mstsc "3CXPhone for Windows.rdp"
mstsc "PhoneSuite CTI Client.rdp"
mstsc "JTL-Wawi.rdp"

rem Sitzung trennen

set next=eof
goto rdp-disconnect

rem ------------------------------------------------------
rem Naechster Benutzer
:<username>
rem Benutzername, RemoteApps, etc.

rem ------------------------------------------------------
rem Skript beenden

:eof
 exit

rem ------------------------------------------------------
rem Quasi-Funktion "RDP-Sitzung trennen"
:rdp-disconnect

rem Automatische Bestaetitung der Trennung vorbereiten

 start ClickDiscRAWa-x64.exe
  
rem Pause, damit die RemoteApps vollstaendig starten koennen

 timeout /t 60

rem Aktuelle Sitzungen auflisten

 query session /server:"%WTSServer%" | find "%SessionUsername%" > "%TEMP%\Session.txt"
 set /p Session=<"%TEMP%\Session.txt"
 
rem ID des Benutzers ermitteln

 rem Ab Zeichen 43 in der Zeile,
 rem drei Stellen in die Variable schreiben.

  set SessionID=%Session:~43,3%
 
 rem Ggf. vorhandene Leerstelle(n) entfernen, falls die ID nur ein- oder zweistellig ist.
 
   set "SessionID=%SessionID: =%"
 
rem "Session.txt" entfernen

 del "%TEMP%\Session.txt" /q
 
rem Sitzung trennen
 
 tsdiscon %SessionID% /Server:%WTSServer%
 
rem Pause bevor es weiter geht, damit es zu keinen Schwierigkeiten beim Bestaetigen der Trennung kommt

 timeout /t 15
 
rem Zur naechsten Marke springen
 
 goto %next%

Man beachte die Pause nach dem Start von Outlook, diese dient dazu, das die RemoteApp geladen werden kann und die Abfrage nach dem Benutzernamen und Kennwort erscheint und automatisch ausgefüllt werden kann.

Sinnvoll können je nach zu startender/n Anwendung(en) zudem mehrere Pausen sein. Dies verhindert dann z.B. Lastspitzen bei CPU, Storage, etc.

Dieser Auszug ist nur für einen Benutzer und kann um mehrere erweitert werden. In der Praxis werden damit bereits mehrere Benutzer bedient.

Offene Punkte

Noch nicht berücksichtigt ist das Thema, wenn das Server-Zertifikat erneuert wurde, diese haben meist eine Gültigkeit von einem Jahr, und dann die Abfrage erscheint, ob man diesem Vertraut. Bei einem Schnelltest konnte diese Abfrage bislang nicht abgefangen werden. Vermutlich muss man auch hier etwas Tricksen.

Wie verbindet sich nun der Anwender von seinem Arbeitsplatz aus?

Diese Frage ist einfach zu beanworten: Es genütgt eine RemoteApp zu starten und die zuvor automatisch erstellte Sitzung wird verbunden. Aber Achtung: Manche Anwendung startet dann doppelt! Gut, d.h. ohne doppelten Start, funktioniert das Verbinden wenn man Outlook als RemoteApp aufruft.