En la organización donde trabajo, el teletrabajo estaba prohibido en principio. Lo estuvo. Hasta la semana pasada. Ahora tuvimos que implementar una solución de manera urgente. Desde el negocio, la adaptación de los procesos al nuevo formato de trabajo, de nuestra parte, PKI con códigos PIN y tokens, VPN, registro detallado y mucho más.
Además de todo esto, estuve configurando la infraestructura de escritorios remotos, es decir, el servicio de Terminales. Tenemos varias implementaciones de RDS en diferentes centros de datos. Una de las tareas era permitir que los colegas de departamentos adyacentes de TI se conectaran a sesiones de usuario en modo interactivo. Como se sabe, para esto existe el mecanismo RDS Shadow y la forma más sencilla de delegar es dar derechos de administrador local en los servidores RDS.
Respeto y aprecio a mis colegas, pero soy muy reacio a otorgar derechos de administrador. 🙂 A quienes estén de acuerdo conmigo, les pido pasar a la siguiente sección.
Bueno, la tarea está clara, ahora — a la acción.
Compra una suscripción
Crearemos un grupo de seguridad en Active Directory RDP_Operators y añadiremos a este grupo las cuentas de los usuarios a quienes deseamos delegar derechos:
$Users = @(
"UserLogin1",
"UserLogin2",
"UserLogin3"
)
$Group = "RDP_Operators"
New-ADGroup -Name $Group -GroupCategory Security -GroupScope DomainLocal
Add-ADGroupMember -Identity $Group -Members $Users
Si tiene varios sitios de AD, antes de pasar al siguiente paso, debe esperar a que se replique en todos los controladores de dominio. Normalmente, esto no toma más de 15 minutos.
Paso 2
Daremos al grupo derechos para gestionar sesiones de terminal en cada uno de los servidores RDSH:
Set-RDSPermissions.ps1
$Group = "RDP_Operators"
$Servers = @(
"RDSHost01",
"RDSHost02",
"RDSHost03"
)
ForEach ($Server in $Servers) {
#Delegamos el derecho a sesiones 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 = "exitoso"
If ($retVal.ReturnValue -ne 0) {
$opstatus = "error"
}
Write-Host ("Delegación de derechos para conexión shadow al grupo " +
$Group + " en el servidor " + $Server + ": " + $opstatus + "`r`n")
}
}
}
Paso 3
Añadiremos el grupo al grupo local Usuarios de escritorio remoto en cada uno de los servidores RDSH. Si sus servidores están combinados en colecciones de sesiones, entonces hacemos esto a nivel de colección:
$Group = "RDP_Operators"
$CollectionName = "MyRDSCollection"
[String[]]$CurrentCollectionGroups = @(Get-RDSessionCollectionConfiguration -CollectionName $CollectionName -UserGroup).UserGroup
Set-RDSessionCollectionConfiguration -CollectionName $CollectionName -UserGroup ($CurrentCollectionGroups + $Group)
Para servidores individuales utilizamos , esperando a que se aplique en los servidores. Aquellos que no quieren esperar pueden forzar el proceso con el viejo y querido gpupdate, preferiblemente .
Paso 4
Prepararemos para los "administradores" el siguiente 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 "Ingrese el nombre de usuario"
Write-Host "Buscando sesiones RDP del usuario en los servidores..."
$SessionList = @()
ForEach ($Server in $Servers) {
$TargetSession = $null
Write-Host " Consultando el servidor $Server"
Try {
$TargetSession = Get-LoggedOnUser -ComputerName $Server | Where-Object {$_.UserName -eq $UserLogin}
}
Catch {
Write-Host "Error: " $Error[0].Exception.Message -ForegroundColor Red
Continue
}
If ($TargetSession) {
Write-Host " Sesión encontrada con ID $($TargetSession.ID) en el servidor $Server" -ForegroundColor Yellow
Write-Host " ¿Qué vamos a hacer?"
Write-Host " 1 - conectarse a la sesión"
Write-Host " 2 - cerrar la sesión"
Write-Host " 0 - nada"
$Action = Read-Host -Prompt "Ingrese la acción"
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 " No se encontraron sesiones"
}
}
Para facilitar la ejecución del script de PS, crearemos un envoltorio en forma de archivo cmd con el mismo nombre que el script de PS:
RDSManagement.cmd
@ECHO OFF
powershell -NoLogo -ExecutionPolicy Bypass -File "%~d0%~p0%~n0.ps1" %*
Colocamos ambos archivos en una carpeta que será accesible para los "administradores" y les pedimos que vuelvan a iniciar sesión. Ahora, al ejecutar el archivo cmd, podrán conectarse a las sesiones de otros usuarios en modo RDS Shadow y forzarlos a cerrar sesión (lo cual es útil cuando el usuario no puede cerrar la sesión "congelada" por sí mismo).
Esto se ve aproximadamente así:
Para el "administrador"
Para el usuario
Algunas observaciones finales
Matiz 1. Si la sesión del usuario a la que intentamos acceder fue iniciada antes de que el script Set-RDSPermissions.ps1 se ejecutara en el servidor, el "administrador" recibirá un error de acceso. La solución aquí es obvia: esperar a que el usuario administrado vuelva a iniciar sesión.
Matiz 2. Después de varios días trabajando con RDP Shadow, notamos un interesante bug o característica: después de finalizar la sesión sombra del usuario al que nos conectamos, la barra de idioma en la bandeja del sistema desaparece y para recuperarla, el usuario debe volver a iniciar sesión. Resulta que no estamos solos: , , .
Eso es todo. Les deseo salud a ustedes y a sus servidores. Como siempre, espero sus comentarios y les pido que participen en una breve encuesta a continuación.
Fuentes
Solo los usuarios registrados pueden participar en la encuesta. , por favor.
¿Qué está utilizando?
8,1%AMMYY Admin5
17,7%AnyDesk11
9,7%DameWare6
24,2%Radmin15
14,5%RDS Shadow9
1,6%Asistencia rápida / Windows Remote Assistance1
38,7%TeamViewer24
32,3%VNC20
32,3%otro20
3,2%LiteManager2
Votaron 62 usuarios. 22 usuarios se abstuvieron.
Fuente: habr.com
