Delegování řízení relací RDP

Delegování řízení relací RDP
V organizaci, kde pracuji, je práce na dálku zásadně zakázána. Byl. Až do minulého týdne. Nyní jsme museli urychleně implementovat řešení. Od byznysu – přizpůsobení procesů novému formátu práce, od nás – PKI s PIN kódy a tokeny, VPN, podrobné protokolování a mnoho dalšího.
Mimo jiné jsem nastavoval infrastrukturu vzdálené plochy alias terminálové služby. Máme několik nasazení RDS v různých datových centrech. Jedním z cílů bylo umožnit kolegům z příbuzných IT oddělení interaktivně se připojovat k uživatelským relacím. Jak víte, existuje pro to standardní mechanismus stínu RDS a nejjednodušší způsob, jak jej delegovat, je udělit místním administrátorským právům na serverech RDS.
Vážím si a vážím si svých kolegů, ale jsem velmi chamtivý, pokud jde o rozdávání administrátorských práv. 🙂 Kdo se mnou souhlasí, prosím sledujte střih.

Úkol je jasný, pojďme na věc.

Krok 1

Vytvořme skupinu zabezpečení v Active Directory RDP_Operátoři a zahrnout do něj účty těch uživatelů, kterým chceme delegovat práva:

$Users = @(
    "UserLogin1",
    "UserLogin2",
    "UserLogin3"
)
$Group = "RDP_Operators"
New-ADGroup -Name $Group -GroupCategory Security -GroupScope DomainLocal
Add-ADGroupMember -Identity $Group -Members $Users

Pokud máte více serverů AD, budete muset počkat, dokud nebude replikován na všechny řadiče domény, než přejdete k dalšímu kroku. To obvykle netrvá déle než 15 minut.

Krok 2

Dejme skupině práva pro správu relací terminálu na každém ze serverů RDSH:

Set-RDSPermissions.ps1

$Group = "RDP_Operators"
$Servers = @(
    "RDSHost01",
    "RDSHost02",
    "RDSHost03"
)
ForEach ($Server in $Servers) {
    #Делегируем право на теневые сессии
    $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 = "успешно"
        If ($retVal.ReturnValue -ne 0) {
            $opstatus = "ошибка"
        }
        Write-Host ("Делегирование прав на теневое подключение группе " +
            $Group + " на сервере " + $Server + ": " + $opstatus + "`r`n")
        }
    }
}

Krok 3

Přidejte skupinu do místní skupiny Uživatelé vzdálené plochy na každém ze serverů RDSH. Pokud jsou vaše servery sloučeny do kolekcí relací, provádíme to na úrovni kolekce:

$Group = "RDP_Operators"
$CollectionName = "MyRDSCollection"
[String[]]$CurrentCollectionGroups = @(Get-RDSessionCollectionConfiguration -CollectionName $CollectionName -UserGroup).UserGroup
Set-RDSessionCollectionConfiguration -CollectionName $CollectionName -UserGroup ($CurrentCollectionGroups + $Group)

Pro jednotlivé servery, které používáme skupinová politika, čeká na jeho uplatnění na serverech. Ti, kteří jsou příliš líní čekat, mohou proces urychlit nejlépe pomocí starého dobrého gpupdate centrálně.

Krok 4

Připravme si následující PS skript pro „manažery“:

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 "Введите логин пользователя"
Write-Host "Поиск RDP-сессий пользователя на серверах..."
$SessionList = @()
ForEach ($Server in $Servers) {
    $TargetSession = $null
    Write-Host "  Опрос сервера $Server"
    Try {
        $TargetSession = Get-LoggedOnUser -ComputerName $Server | Where-Object {$_.UserName -eq $UserLogin}
    }
    Catch {
        Write-Host "Ошибка: " $Error[0].Exception.Message -ForegroundColor Red
        Continue
    }
    If ($TargetSession) {
        Write-Host "    Найдена сессия с ID $($TargetSession.ID) на сервере $Server" -ForegroundColor Yellow
        Write-Host "    Что будем делать?"
        Write-Host "      1 - подключиться к сессии"
        Write-Host "      2 - завершить сессию"
        Write-Host "      0 - ничего"
        $Action = Read-Host -Prompt "Введите действие"
        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 "    сессий не найдено"
    }
}

Aby bylo spouštění skriptu PS pohodlné, vytvoříme pro něj shell ve formě souboru cmd se stejným názvem jako skript PS:

RDSManagement.cmd

@ECHO OFF
powershell -NoLogo -ExecutionPolicy Bypass -File "%~d0%~p0%~n0.ps1" %*

Oba soubory vložíme do složky, která bude přístupná „správcům“ a požádáme je o opětovné přihlášení. Nyní se spuštěním souboru cmd budou moci připojit k relacím jiných uživatelů v režimu stínu RDS a donutit je k odhlášení (to může být užitečné, když uživatel nemůže samostatně ukončit „visící“ relaci).

Vypadá to takto:

pro "manažera"Delegování řízení relací RDP

Pro uživateleDelegování řízení relací RDP

Několik poznámek na závěr

Nuance 1. Pokud byla uživatelská relace, nad kterou se snažíme získat kontrolu, spuštěna před spuštěním skriptu Set-RDSPermissions.ps1 na serveru, pak „správce“ obdrží chybu přístupu. Řešení je zde zřejmé: počkejte, až se spravovaný uživatel přihlásí.

Nuance 2. Po několika dnech práce s RDP Shadow jsme si všimli zajímavé chyby nebo funkce: po skončení stínové relace pro připojeného uživatele zmizí lišta jazyků na liště a aby ji uživatel získal zpět, musí ji znovu -přihlásit se. Jak se ukazuje, nejsme sami: čas, два, tři.

To je vše. Přeji vám i vašim serverům pevné zdraví. Jako vždy se těším na vaši zpětnou vazbu v komentářích a žádám vás o vyplnění krátkého průzkumu níže.

zdroje

Průzkumu se mohou zúčastnit pouze registrovaní uživatelé. Přihlásit se, prosím.

Co používáš?

  • 8,1%AMMYY Admin5

  • 17,7%AnyDesk11

  • 9,7%DameWare6

  • 24,2%Radmin15

  • 14,5%RDS Shadow9

  • 1,6%Rychlá pomoc / Vzdálená pomoc systému Windows1

  • 38,7%TeamViewer24

  • 32,3%VNC20

  • 32,3%ostatní20

  • 3,2%LiteManager2

62 uživatelé hlasovali. 22 uživatelé se zdrželi hlasování.

Zdroj: www.habr.com

Přidat komentář