Mendelegasikan Manajemen Sesi RDP


Di organisasi tempat saya bekerja, udalenka pada prinsipnya dilarang. Dulu. Sampai minggu lalu. Sekarang saya harus segera mengimplementasikan solusinya. Dari bisnis - adaptasi proses ke format pekerjaan baru, dari kami - PKI dengan kode pin dan token, VPN, pencatatan terperinci dan banyak lagi.
Antara lain, saya terlibat dalam mengkonfigurasi Terminal Services Remote Desktop Infrastructure. Kami memiliki beberapa penyebaran RDS di berbagai pusat data. Salah satu tugas adalah untuk memungkinkan kolega dari departemen TI terkait untuk terhubung ke sesi pengguna secara interaktif. Seperti yang Anda ketahui, ada mekanisme RDS Shadow reguler untuk ini dan cara termudah untuk mendelegasikannya adalah dengan memberikan hak administrator lokal pada server RDS.
Saya menghormati dan menghargai kolega saya, tetapi sangat rakus untuk distribusi hak admin. :) Mereka yang setuju dengan saya, tolong, di bawah kucing.

Nah, tugasnya jelas, sekarang to the point.

Langkah 1


Mari kita buat grup keamanan RDP_Operators di Active Directory dan sertakan akun pengguna yang ingin kami delegasikan haknya:

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

Jika Anda memiliki beberapa situs AD, maka sebelum melanjutkan ke langkah berikutnya, Anda harus menunggu sampai direplikasi ke semua pengontrol domain. Biasanya tidak lebih dari 15 menit.

Langkah 2


Kami memberi grup hak untuk mengelola sesi terminal di setiap server RDSH:

Set-RDSPermissions.ps1
$Group = "RDP_Operators"
$Servers = @(
    "RDSHost01",
    "RDSHost02",
    "RDSHost03"
)
ForEach ($Server in $Servers) {
    #    
    $WMIHandles = Get-WmiObject `
        -Class "Win32_TSPermissionsSetting" `
        -Namespace "root\CIMV2\terminalservices" `
        -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")
        }
    }
}


Langkah 3


Tambahkan grup ke grup Pengguna Desktop Jarak Jauh lokal di setiap server RDSH. Jika server Anda digabungkan dalam kumpulan sesi, maka kami melakukan ini di tingkat pengumpulan:

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

Untuk server tunggal kami menggunakan kebijakan grup , menunggu hingga diterapkan di server. Mereka yang terlalu malas untuk menunggu dapat memaksa proses menggunakan gpupdate lama yang baik, lebih disukai secara terpusat .

Langkah 4


Kami akan menyiapkan skrip PS berikut untuk "manajer":

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


Untuk membuat skrip PS nyaman dijalankan, kami akan membuat cangkang untuk itu dalam bentuk file cmd dengan nama yang sama dengan skrip PS:

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


Kami meletakkan kedua file dalam folder yang akan tersedia untuk "manajer" dan meminta mereka untuk masuk. Sekarang, setelah meluncurkan file cmd, mereka akan dapat terhubung ke sesi pengguna lain dalam mode RDS Shadow dan memaksa mereka untuk logout (ini berguna ketika pengguna tidak dapat secara mandiri mengakhiri sesi "hung").

Itu terlihat seperti ini:

Untuk "manajer"


Untuk pengguna


Beberapa komentar pada akhirnya


Nuansa 1 . Jika sesi pengguna yang kami coba kendalikan dimulai sebelum skrip Set-RDSPermissions.ps1 bekerja di server, "manajer" akan menerima kesalahan akses. Solusinya di sini jelas: tunggu sampai pengguna yang dikelola masuk.

Nuansa 2 . Setelah beberapa hari bekerja dengan RDP, Shadow melihat ada bug atau fitur yang menarik: setelah selesainya sesi bayangan, pengguna yang terhubung ke bilah bahasa menghilang di baki dan untuk mengembalikannya, pengguna perlu masuk. Ternyata, kita tidak sendirian: satu , dua , tiga .

Itu saja. Saya berharap kesehatan Anda dan server Anda. Seperti biasa, saya menantikan umpan balik di komentar dan meminta Anda untuk pergi melalui survei kecil di bawah ini.

Sumber



All Articles