In der Organisation, in der ich arbeite, ist Homeoffice prinzipiell verboten. War es zumindest. Bis letzte Woche. Jetzt mussten wir hastig eine Lösung einführen. Von der Geschäftsleitung — Anpassung der Prozesse an das neue Arbeitsformat, von uns — PKI mit PIN-Codes und Tokens, VPN, detaillierte Protokollierung und vieles mehr.
Neben allem anderen habe ich die Infrastruktur für Remote-Desktops aka Terminaldienste eingerichtet. Wir haben mehrere RDS-Bereitstellungen in verschiedenen Rechenzentren. Eine der Aufgaben war es, Kollegen aus angrenzenden IT-Abteilungen die Möglichkeit zu geben, sich interaktiv mit Nutzersitzungen zu verbinden. Wie bekannt ist, gibt es dafür den integrierten Mechanismus RDS Shadow und die einfachste Möglichkeit, ihn zu delegieren — die Rechte des lokalen Administrators auf den RDS-Servern zu vergeben.
Ich respektiere und schätze meine Kollegen, bin aber sehr sparsam bei der Vergabe von Administratorrechten. 🙂 Diejenigen, die mir zustimmen, bitte unter dem Cut.
Nun, die Aufgabe ist klar, jetzt — zur Sache.
Schritt 1
Erstellen wir in Active Directory eine Gruppe für Sicherheitszwecke. RDP_Operators und fügen wir die Konten der Benutzer hinzu, denen wir Rechte delegieren möchten:
$Users = @(
"UserLogin1",
"UserLogin2",
"UserLogin3"
)
$Group = "RDP_Operators"
New-ADGroup -Name $Group -GroupCategory Security -GroupScope DomainLocal
Add-ADGroupMember -Identity $Group -Members $Users
Wenn Sie mehrere AD-Standorte haben, müssen Sie warten, bis dies auf alle Domänencontroller repliziert wurde, bevor Sie zum nächsten Schritt übergehen. In der Regel dauert dies nicht länger als 15 Minuten.
Schritt 2
Geben wir der Gruppe die Berechtigungen zur Verwaltung von Terminalsitzungen auf jedem der RDSH-Server:
Set-RDSPermissions.ps1
$Group = "RDP_Operators"
$Servers = @(
"RDSHost01",
"RDSHost02",
"RDSHost03"
)
ForEach ($Server in $Servers) {
#Recht auf Schatten-Sitzungen delegieren
$WMIHandles = Get-WmiObject `
-Class "Win32_TSPermissionsSetting" `
-Namespace "rootCIMV2terminalservices" `
-ComputerName $Server `
-Authentication PacketPrivacy `
-Impersonation Impersonate
ForEach($WMIHandle in $WMIHandles)
{
If ($WMIHandle.TerminalName -eq "RDP-Tcp")
{
$retVal = $WMIHandle.AddAccount($Group, 2)
$opstatus = "erfolgreich"
If ($retVal.ReturnValue -ne 0) {
$opstatus = "Fehler"
}
Write-Host ("Rechte für die Schattenverbindung an die Gruppe " +
$Group + " auf dem Server " + $Server + ": " + $opstatus + "`r`n")
}
}
}
Schritt 3
Fügen wir die Gruppe zur lokalen Gruppe hinzu. Benutzer der Remote-Desktop auf jedem der RDSH-Server. Wenn Ihre Server in Sitzungs-Sammlungen zusammengeschlossen sind, machen wir das auf der Ebene der Sammlung:
$Group = "RDP_Operators"
$CollectionName = "MyRDSCollection"
[String[]]$CurrentCollectionGroups = @(Get-RDSessionCollectionConfiguration -CollectionName $CollectionName -UserGroup).UserGroup
Set-RDSessionCollectionConfiguration -CollectionName $CollectionName -UserGroup ($CurrentCollectionGroups + $Group)
Für Einzelserver verwenden wir , während wir darauf warten, dass sie auf den Servern angewendet wird. Diejenigen, die nicht warten möchten, können den Prozess mit dem altbekannten gpupdate forcieren, vorzugsweise .
Schritt 4
Wir bereiten für die "Verwalter" folgendes PS-Skript vor:
RDSManagement.ps1
$Servers = @(
"RDSHost01",
"RDSHost02",
"RDSHost03"
)
function Invoke-RDPSessionLogoff {
Param(
[parameter(Mandatory=$True, Position=0)][String]$ComputerName,
[parameter(Mandatory=$true, Position=1)][String]$SessionID
)
$ErrorActionPreference = "Stop"
logoff $SessionID /server:$ComputerName /v 2>&1
}
function Invoke-RDPShadowSession {
Param(
[parameter(Mandatory=$True, Position=0)][String]$ComputerName,
[parameter(Mandatory=$true, Position=1)][String]$SessionID
)
$ErrorActionPreference = "Stop"
mstsc /shadow:$SessionID /v:$ComputerName /control 2>&1
}
Function Get-LoggedOnUser {
Param(
[parameter(Mandatory=$True, Position=0)][String]$ComputerName="localhost"
)
$ErrorActionPreference = "Stop"
Test-Connection $ComputerName -Count 1 | Out-Null
quser /server:$ComputerName 2>&1 | Select-Object -Skip 1 | ForEach-Object {
$CurrentLine = $_.Trim() -Replace "s+"," " -Split "s"
$HashProps = @{
UserName = $CurrentLine[0]
ComputerName = $ComputerName
}
If ($CurrentLine[2] -eq "Disc") {
$HashProps.SessionName = $null
$HashProps.Id = $CurrentLine[1]
$HashProps.State = $CurrentLine[2]
$HashProps.IdleTime = $CurrentLine[3]
$HashProps.LogonTime = $CurrentLine[4..6] -join " "
$HashProps.LogonTime = $CurrentLine[4..($CurrentLine.GetUpperBound(0))] -join " "
}
else {
$HashProps.SessionName = $CurrentLine[1]
$HashProps.Id = $CurrentLine[2]
$HashProps.State = $CurrentLine[3]
$HashProps.IdleTime = $CurrentLine[4]
$HashProps.LogonTime = $CurrentLine[5..($CurrentLine.GetUpperBound(0))] -join " "
}
New-Object -TypeName PSCustomObject -Property $HashProps |
Select-Object -Property UserName, ComputerName, SessionName, Id, State, IdleTime, LogonTime
}
}
$UserLogin = Read-Host -Prompt "Geben Sie den Benutzernamen ein"
Write-Host "Suche nach RDP-Sitzungen des Benutzers auf den Servern..."
$SessionList = @()
ForEach ($Server in $Servers) {
$TargetSession = $null
Write-Host " Abfrage des Servers $Server"
Try {
$TargetSession = Get-LoggedOnUser -ComputerName $Server | Where-Object {$_.UserName -eq $UserLogin}
}
Catch {
Write-Host "Fehler: " $Error[0].Exception.Message -ForegroundColor Red
Continue
}
If ($TargetSession) {
Write-Host " Sitzung mit ID $($TargetSession.ID) auf Server $Server gefunden" -ForegroundColor Yellow
Write-Host " Was möchten Sie tun?"
Write-Host " 1 - Verbindung zur Sitzung herstellen"
Write-Host " 2 - Sitzung beenden"
Write-Host " 0 - nichts"
$Action = Read-Host -Prompt "Geben Sie die Aktion ein"
If ($Action -eq "1") {
Invoke-RDPShadowSession -ComputerName $Server -SessionID $TargetSession.ID
}
ElseIf ($Action -eq "2") {
Invoke-RDPSessionLogoff -ComputerName $Server -SessionID $TargetSession.ID
}
Break
}
Else {
Write-Host " Keine Sitzungen gefunden"
}
}
Um das PS-Skript bequem auszuführen, erstellen wir für es eine Shell in Form einer cmd-Datei mit demselben Namen wie das PS-Skript:
RDSManagement.cmd
@ECHO OFF
powershell -NoLogo -ExecutionPolicy Bypass -File "%~d0%~p0%~n0.ps1" %*
Wir legen beide Dateien in einen Ordner, der für die „Verwalter“ zugänglich ist, und bitten sie, sich erneut anzumelden. Nun können sie durch Ausführung der CMD-Datei auf die Sitzungen anderer Benutzer im RDS Shadow-Modus zugreifen und diese zwangsweise abmelden (was nützlich sein kann, wenn ein Benutzer die „hängende“ Sitzung nicht selbst beenden kann).
So sieht es ungefähr aus:
Für den „Verwalter“
Für den Benutzer
Ein paar abschließende Anmerkungen
Hinweis 1. Wenn die Sitzung des Benutzers, dessen Kontrolle wir übernehmen möchten, gestartet wurde, bevor das Script Set-RDSPermissions.ps1 auf dem Server ausgeführt wurde, erhält der „Verwalter“ einen Zugriffsfehler. Die Lösung liegt auf der Hand: Warten, bis der verwaltete Benutzer sich erneut anmeldet.
Hinweis 2. Nach mehreren Tagen Arbeit mit RDP Shadow haben wir einen interessanten Bug oder ein Feature festgestellt: Nach dem Beenden der Schatten-Sitzung beim Benutzer, mit dem wir verbunden waren, verschwindet die Sprachleiste im Tray, und um sie zurückzubekommen, muss der Benutzer sich erneut anmelden. Wie sich herausstellte, sind wir damit nicht allein: , , .
Das ist alles. Ich wünsche Ihnen und Ihren Servern Gesundheit. Wie immer freue ich mich auf Ihr Feedback in den Kommentaren und bitte Sie, an der kleinen Umfrage unten teilzunehmen.
Quellen
Nur registrierte Benutzer können an der Umfrage teilnehmen. .
Was verwenden Sie?
8,1%AMMYY Admin5
17,7%AnyDesk11
9,7%DameWare6
24,2%Radmin15
14,5%RDS Shadow9
1,6%Schnelle Hilfe / Windows Remote Assistance1
38,7%TeamViewer24
32,3%VNC20
32,3%andere20
3,2%LiteManager2
62 Benutzer haben abgestimmt. 22 Benutzer haben sich enthalten.
Quelle: habr.com
