Nell'organizzazione in cui lavoro, il lavoro remoto è completamente vietato. Lo era. Fino alla settimana scorsa. Ora abbiamo dovuto implementare una soluzione con urgenza. Dal lato aziendale — adattamento dei processi al nuovo formato di lavoro, da parte nostra — PKI con PIN e token, VPN, registrazione dettagliata e molto altro.
Tra le altre cose, mi sono occupato della configurazione dell'infrastruttura dei desktop remoti, ovvero del servizio terminali. Abbiamo diverse distribuzioni RDS in vari data center. Uno degli obiettivi era consentire ai colleghi di reparti IT correlati di connettersi alle sessioni utente in modalità interattiva. Come si sa, per questo esiste un meccanismo nativo di RDS Shadow e il modo più semplice per delegarlo è fornire i diritti di amministratore locale sui server RDS.
Rispetto e apprezzo i miei colleghi, ma sono molto avido nel concedere diritti di amministratore. 🙂 Chi è d'accordo con me, vi invito a proseguire.
Beh, il compito è chiaro, ora passiamo all'azione.
Passo 1
Creiamo un gruppo di sicurezza in Active Directory RDP_Operators e includiamo in esso gli account degli utenti ai quali vogliamo delegare i diritti:
$Users = @(
"UserLogin1",
"UserLogin2",
"UserLogin3"
)
$Group = "RDP_Operators"
New-ADGroup -Name $Group -GroupCategory Security -GroupScope DomainLocal
Add-ADGroupMember -Identity $Group -Members $Users
Se hai più siti AD, prima di passare al passaggio successivo, devi attendere che venga replicato su tutti i controllori di dominio. Di solito, ci vuole non più di 15 minuti.
Passo 2
Diamo al gruppo i diritti per gestire le sessioni terminali su ciascuno dei server RDSH:
Set-RDSPermissions.ps1
$Group = "RDP_Operators"
$Servers = @(
"RDSHost01",
"RDSHost02",
"RDSHost03"
)
ForEach ($Server in $Servers) {
#Delega il diritto sulle sessioni shadow
$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 = "riuscito"
If ($retVal.ReturnValue -ne 0) {
$opstatus = "errore"
}
Write-Host ("Delegazione dei diritti per la connessione shadow al gruppo " +
$Group + " sul server " + $Server + ": " + $opstatus + "`r`n")
}
}
}
Passo 3
Aggiungiamo il gruppo al gruppo locale Utenti desktop remoto su ciascuno dei server RDSH. Se i vostri server sono raggruppati in collezioni di sessioni, allora lo facciamo a livello di collezione:
$Group = "RDP_Operators"
$CollectionName = "MyRDSCollection"
[String[]]$CurrentCollectionGroups = @(Get-RDSessionCollectionConfiguration -CollectionName $CollectionName -UserGroup).UserGroup
Set-RDSessionCollectionConfiguration -CollectionName $CollectionName -UserGroup ($CurrentCollectionGroups + $Group)
Per i server singoli utilizziamo , aspettando che venga applicata sui server. Coloro che non vogliono aspettare possono forzare il processo utilizzando il buon vecchio gpupdate, preferibilmente .
Passo 4
Prepariamo per i «gestori» questo script PS:
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 "Inserisci il nome utente"
Write-Host "Ricerca delle sessioni RDP dell'utente sui server..."
$SessionList = @()
ForEach ($Server in $Servers) {
$TargetSession = $null
Write-Host " Interrogazione del server $Server"
Try {
$TargetSession = Get-LoggedOnUser -ComputerName $Server | Where-Object {$_.UserName -eq $UserLogin}
}
Catch {
Write-Host "Errore: " $Error[0].Exception.Message -ForegroundColor Red
Continue
}
If ($TargetSession) {
Write-Host " Trovata sessione con ID $($TargetSession.ID) sul server $Server" -ForegroundColor Yellow
Write-Host " Cosa desideri fare?"
Write-Host " 1 - collegarsi alla sessione"
Write-Host " 2 - terminare la sessione"
Write-Host " 0 - nulla"
$Action = Read-Host -Prompt "Inserisci l'azione"
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 " Nessuna sessione trovata"
}
}
Per rendere il PS-script più facile da eseguire, creeremo un wrapper sotto forma di file cmd con lo stesso nome dello script PS:
RDSManagement.cmd
@ECHO OFF
powershell -NoLogo -ExecutionPolicy Bypass -File "%~d0%~p0%~n0.ps1" %*
Mettiamo entrambi i file in una cartella accessibile ai "gestori" e chiediamo loro di effettuare nuovamente il login. Ora, eseguendo il file cmd, potranno connettersi alle sessioni di altri utenti in modalità RDS Shadow e forzarne il logout (utile quando un utente non riesce a chiudere autonomamente una sessione "bloccata").
Questo è come appare:
Per il "gestore"
Per l'utente
Alcuni appunti finali
Dettaglio 1. Se la sessione dell'utente a cui stiamo cercando di accedere è stata avviata prima che lo script Set-RDSPermissions.ps1 fosse eseguito sul server, il "gestore" riceverà un errore di accesso. La soluzione è chiara: attendere che l'utente gestito effettui nuovamente il login.
Dettaglio 2. Dopo qualche giorno di lavoro con RDP Shadow, abbiamo notato un'interessante anomalia: dopo la chiusura della sessione shadow dell'utente a cui ci si connetteva, la barra della lingua scompare dalla tray e per ripristinarla, l'utente deve effettuare nuovamente il login. Sembra che non siamo soli: , , .
Questo è tutto. Vi auguro salute e ai vostri server. Come sempre, attendo feedback nei commenti e vi chiedo di partecipare a un breve sondaggio qui sotto.
Fonti
Solo gli utenti registrati possono partecipare al sondaggio. , per favore.
Cosa usate?
8,1%AMMYY Admin5
17,7%AnyDesk11
9,7%DameWare6
24,2%Radmin15
14,5%RDS Shadow9
1,6%Quick Assist / Windows Remote Assistance1
38,7%TeamViewer24
32,3%VNC20
32,3%altro20
3,2%LiteManager2
Hanno votato 62 utenti. 22 utenti si sono astenuti.
Fonte: habr.com
