Delegera hantering av RDP-sessioner

Delegera hantering av RDP-sessioner
I den organisation där jag arbetar är distansarbete i princip förbjudet. Var. Tills förra veckan. Nu var vi tvungna att snarast implementera en lösning. Från affärer - anpassning av processer till ett nytt arbetsformat, från oss - PKI med PIN-koder och tokens, VPN, detaljerad loggning och mycket mer.
Bland annat satte jag upp Remote Desktop Infrastructure aka Terminal Services. Vi har flera RDS-distributioner i olika datacenter. Ett av målen var att göra det möjligt för kollegor från relaterade IT-avdelningar att ansluta till användarsessioner interaktivt. Som ni vet finns det en standard RDS Shadow-mekanism för detta, och det enklaste sättet att delegera den är att ge lokala administratörsrättigheter på RDS-servrar.
Jag respekterar och värdesätter mina kollegor, men jag är väldigt girig när det kommer till att dela ut administratörsrättigheter. 🙂 För de som håller med mig, följ klippet.

Nåväl, uppgiften är klar, låt oss nu börja.

Steg 1

Låt oss skapa en säkerhetsgrupp i Active Directory RDP_Operatorer och inkludera kontona för de användare som vi vill delegera rättigheter till:

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

Om du har flera AD-webbplatser måste du vänta tills det replikeras till alla domänkontrollanter innan du går vidare till nästa steg. Detta tar vanligtvis inte mer än 15 minuter.

Steg 2

Låt oss ge gruppen rättigheter att hantera terminalsessioner på var och en av RDSH-servrarna:

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")
        }
    }
}

Steg 3

Lägg till gruppen i den lokala gruppen Användare av fjärrskrivbord på var och en av RDSH-servrarna. Om dina servrar kombineras till sessionssamlingar gör vi detta på samlingsnivå:

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

För enstaka servrar använder vi gruppolicy, väntar på att den ska tillämpas på servrarna. De som är för lata för att vänta kan påskynda processen med att använda gamla goda gpupdate, helst centralt.

Steg 4

Låt oss förbereda följande PS-manus för "chefer":

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 "    сессий не найдено"
    }
}

För att göra PS-skriptet bekvämt att köra kommer vi att skapa ett skal för det i form av en cmd-fil med samma namn som PS-skriptet:

RDSManagement.cmd

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

Vi lägger båda filerna i en mapp som kommer att vara tillgänglig för "hanterare" och ber dem att logga in igen. Nu, genom att köra cmd-filen, kommer de att kunna ansluta till andra användares sessioner i RDS Shadow-läge och tvinga dem att logga ut (detta kan vara användbart när användaren inte självständigt kan avsluta en "hängande" session).

Det ser ut så här:

För "chefen"Delegera hantering av RDP-sessioner

För användarenDelegera hantering av RDP-sessioner

Några sista kommentarer

Nyans 1. Om användarsessionen som vi försöker få kontroll till startades innan Set-RDSPermissions.ps1-skriptet kördes på servern, kommer "hanteraren" att få ett åtkomstfel. Lösningen här är uppenbar: vänta tills den hanterade användaren loggar in.

Nyans 2. Efter flera dagars arbete med RDP Shadow märkte vi en intressant bugg eller funktion: efter slutet av skuggsessionen försvinner språkfältet i facket för användaren som är ansluten till, och för att få tillbaka det måste användaren åter -logga in. Som det visar sig är vi inte ensamma: tid, два, tre.

Det är allt. Jag önskar dig och dina servrar god hälsa. Som alltid ser jag fram emot din feedback i kommentarerna och ber dig att svara på den korta enkäten nedan.

källor

Endast registrerade användare kan delta i undersökningen. Logga in, Snälla du.

Vad använder du?

  • 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%andra 20

  • 3,2%LiteManager2

62 användare röstade. 22 användare avstod från att rösta.

Källa: will.com

Lägg en kommentar