Dans l'organisation où je travaille, le télétravail était fondamentalement interdit. C'était le cas jusqu'à la semaine dernière. Maintenant, nous avons dû mettre en place une solution en urgence. Du côté de l'entreprise, cela implique l'adaptation des processus au nouveau format de travail, de notre côté — PKI avec codes PIN et jetons, VPN, journalisation détaillée et bien d'autres choses encore.
En plus de cela, je travaillais sur la configuration de l'infrastructure de bureaux à distance, aka le service de terminaux. Nous avons plusieurs déploiements RDS dans différents centres de données. L'une des tâches était de permettre à mes collègues des départements adjacents en informatique de se connecter aux sessions utilisateurs en mode interactif. Comme vous le savez, pour cela, il existe un mécanisme standard de RDS Shadow et la manière la plus simple de le déléguer est de donner des droits d'administrateur local sur les serveurs RDS.
Je respecte et apprécie mes collègues, mais je suis très réticent à accorder des droits d'administrateur. 🙂 Ceux qui partagent mon avis, je vous invite à passer à l'étape suivante.
Eh bien, la tâche est claire, maintenant — au travail.
Étape 1
Créons un groupe de sécurité dans Active Directory RDP_Operators et ajoutons-y les comptes des utilisateurs auxquels nous souhaitons déléguer des droits :
$Users = @(
"UserLogin1",
"UserLogin2",
"UserLogin3"
)
$Group = "RDP_Operators"
New-ADGroup -Name $Group -GroupCategory Security -GroupScope DomainLocal
Add-ADGroupMember -Identity $Group -Members $Users
Si vous avez plusieurs sites AD, il faut attendre que cela soit répliqué sur tous les contrôleurs de domaine avant de passer à l'étape suivante. Cela prend généralement moins de 15 minutes.
Étape 2
Donnerons au groupe les droits de gestion des sessions de terminal sur chacun des serveurs RDSH :
Set-RDSPermissions.ps1
$Group = "RDP_Operators"
$Servers = @(
"RDSHost01",
"RDSHost02",
"RDSHost03"
)
ForEach ($Server in $Servers) {
#Déleguer le droit sur les sessions d'ombre
$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 = "réussi"
If ($retVal.ReturnValue -ne 0) {
$opstatus = "erreur"
}
Write-Host ("Délégation des droits de connexion d'ombre au groupe " +
$Group + " sur le serveur " + $Server + ": " + $opstatus + "`r`n")
}
}
}
Étape 3
Ajoutons le groupe au groupe local Utilisateurs de bureau à distance sur chacun des serveurs RDSH. Si vos serveurs sont regroupés en collections de sessions, faites cela au niveau de la collection :
$Group = "RDP_Operators"
$CollectionName = "MyRDSCollection"
[String[]]$CurrentCollectionGroups = @(Get-RDSessionCollectionConfiguration -CollectionName $CollectionName -UserGroup).UserGroup
Set-RDSessionCollectionConfiguration -CollectionName $CollectionName -UserGroup ($CurrentCollectionGroups + $Group)
Pour les serveurs individuels, nous utilisons , en attendant qu'elle soit appliquée sur les serveurs. Ceux qui n'ont pas envie d'attendre peuvent forcer le processus avec le bon vieux gpupdate, de préférence .
Étape 4
Préparons un script PS pour les « gestionnaires » :
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 "Entrez le nom d'utilisateur"
Write-Host "Recherche des sessions RDP de l'utilisateur sur les serveurs..."
$SessionList = @()
ForEach ($Server in $Servers) {
$TargetSession = $null
Write-Host " Interrogation du serveur $Server"
Try {
$TargetSession = Get-LoggedOnUser -ComputerName $Server | Where-Object {$_.UserName -eq $UserLogin}
}
Catch {
Write-Host "Erreur: " $Error[0].Exception.Message -ForegroundColor Red
Continue
}
If ($TargetSession) {
Write-Host " Session trouvée avec l'ID $($TargetSession.ID) sur le serveur $Server" -ForegroundColor Yellow
Write-Host " Que voulez-vous faire?"
Write-Host " 1 - se connecter à la session"
Write-Host " 2 - terminer la session"
Write-Host " 0 - rien"
$Action = Read-Host -Prompt "Entrez l'action"
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 " Aucune session trouvée"
}
}
Pour faciliter l'exécution du script PS, créons un wrapper sous forme de fichier cmd avec le même nom que le script PS :
RDSManagement.cmd
@ECHO OFF
powershell -NoLogo -ExecutionPolicy Bypass -File "%~d0%~p0%~n0.ps1" %*
Nous plaçons les deux fichiers dans un dossier accessible aux "gestionnaires" et leur demandons de se reconnecter. Maintenant, en lançant le fichier cmd, ils pourront se connecter aux sessions d'autres utilisateurs en mode RDS Shadow et les déconnecter de force (ce qui est utile lorsque l'utilisateur ne peut pas finir lui-même une session "bloquée").
Cela ressemble à peu près à ceci :
Pour le "gestionnaire"
Pour l'utilisateur
Quelques remarques en conclusion
Nuance 1. Si la session de l'utilisateur à laquelle nous tentons d'accéder a été démarrée avant que le script Set-RDSPermissions.ps1 ne soit exécuté sur le serveur, le "gestionnaire" recevra un message d'erreur d'accès. La solution ici est évidente : attendre que l'utilisateur géré se reconnecte.
Nuance 2. Après quelques jours de travail avec RDP Shadow, nous avons remarqué un bug ou une fonctionnalité intéressante : après la fin de la session d'ombre sur l'utilisateur auquel nous étions connectés, la barre de langue disparaît de la zone de notification, et pour la récupérer, l'utilisateur doit se reconnecter. Il s'avère que nous ne sommes pas seuls : , , .
C'est tout. Je vous souhaite santé à vous et à vos serveurs. Comme toujours, j'attends vos retours dans les commentaires et vous prie de participer à un petit sondage ci-dessous.
Sources
Seuls les utilisateurs enregistrés peuvent participer au sondage. , s'il vous plaît.
Quels outils utilisez-vous ?
8,1%AMMYY Admin5
17,7%AnyDesk11
9,7%DameWare6
24,2%Radmin15
14,5%RDS Shadow9
1,6%Assistance rapide / Assistance à distance Windows1
38,7%TeamViewer24
32,3%VNC20
32,3%autre20
3,2%LiteManager2
62 utilisateurs ont voté. 22 utilisateurs se sont abstenus.
Source : habr.com
