Powershell: Włączenie remotingu z maszyny zdalnej. Niemożliwe? Wcale nie!

31-gru-2015

No dobra. Do włączenia remotingu na serwerze lokalnym służy polecenie

Enable-PSRemoting

Niestety to polecenie nie ma parametru -ComputerName, aby włączyć remoting na systemie zdalnym 🙁

Na szkoleniu Powershell dla administratorów Windows pokazuję, że jeśli chcesz włączyć remoting na maszynie zdalnej, to powinieneś wykorzystać zasady grupy i stosując te zasady do serwerów konfigurować je do włączenia lub wyłączenia remotingu. A co jeśli w firmie konfiguracją domeny zajmuje się ktoś inny!? Nie masz z nim kontaktu, a nawet jeśli masz to nie masz mocy przekonania kolegów o tym, że potrzebujesz włączenia remotingu. Hmmm….

Do zdalneo uruchomienia polecenia można się też posłużyć poleceniem Invoke-WMIMethod. Gdyby tak tej komencie przesłać instrukcję:

powershell.exe -command „{Enable-PSRemoting -Force}”

to cel byłby osiągnięty! Tylko jest jeden problem… Invoke-WMIMethod uruchomi polecenie nie dołączając tokenu administratora (nie jako administrator – elevated). Tymczasem Enable-PSRemoting musi być uruchamiane jako administrator!

Trzeba pokombinować. Jest metoda, która pozwala na sytemie zdalnym uruchmić polecenie jako administrator. Możesz utworzyć zadanie w harmonogramie zadań, do uruchomienia na koncie system i natychmiast to zadanie uruchomić. W zadaniu możesz zdefiniować do uruchomienia właśnie powershella włączającego Remoting!

Takie rozwiązanie proponuje książka Windows Powershell Cookboook. Oto i ono po moich małych przeróbkach Enable-PSRemotingRemotely.txt. A tu parę słów komentarza:

Chcemy pracować z funkcją, a nie skryptem, więc dodajemy nagłówek:

function Enable-PSRemotingRemotely()
{ 
 param(
 ## The computer on which to enable remoting
 $Computername,
 
 ## The credential to use when connecting
 $Credential = (Get-Credential)
 )

Za chwilę będzie dość złożony tekst z komendami zawierający w sobie kolejne komendy, warto więc włączyć tzw. Strict Mode, który powoduje, że gdy odwołasz się do niezainicjowanej zmiennej to będzie błąd. Bardzo dobry krok w skomplikowanych skryptach!

 Set-StrictMode -Version Latest
 $VerbosePreference = "Continue"

W kolejnych krokach czasami trzeba skorzystać z credentials, czasami z nazwy użytkownika, a czasami z hasła. Przygotowujemy zmienne:

 $credential = Get-Credential $credential
 $username = $credential.Username
 $password = $credential.GetNetworkCredential().Password

Teraz konstruujemy skrypt, który przez Invoke-WMIMethod będzie uruchamiany na zdalnym systemie. Wszystkie pracujące tu komendy będą już działać lokalnie na tamtym systemie.

 $script = @" 
 `$log = Join-Path `$env:TEMP Enable-RemotePsRemoting.output.txt
 `$scriptFile = Join-Path `$env:TEMP tmpScriptFile.ps1
 Remove-Item -Force `$log -ErrorAction SilentlyContinue
 Remove-Item -Force `$scriptFile -ErrorAction SilentlyContinue
 Start-Transcript -Path `$log 
 ## Create a task that will run with full network privileges.
 ## In this task, we call Enable-PsRemoting
 'Enable-PSRemoting -Force' | Out-File `$scriptFile -Force
schtasks /CREATE /TN 'Enable Remoting' /SC WEEKLY /RL HIGHEST /RU SYSTEM /TR "powershell.exe -executionpolicy bypass -command `$scriptFile" /F | Out-String
 schtasks /RUN /TN 'Enable Remoting' | Out-String
 
 `$securePass = ConvertTo-SecureString $password -AsPlainText -Force
 `$credential = New-Object Management.Automation.PsCredential $username,`$securepass 
 ## Wait for the remoting changes to come into effect
 for(`$count = 1; `$count -le 10; `$count++)
 { 
 `$output = Invoke-Command localhost { 1 } -Cred `$credential -ErrorAction SilentlyContinue 
 if(`$output -eq 1) { Write-Host "Done!!!"; break; } 
 Write-Host "Attempt `$count : Not ready yet.`n" 
 Sleep 5
 } 
 ## Delete the temporary task
 schtasks /DELETE /TN 'Enable Remoting' /F | Out-String
 Stop-Transcript 
"@

uff… długie to było a co się tam nie działo:

  • tworzymy zmienne na nazwy plików tymczasowych. Wszystkie będą tworzone w profilu użytkownika (odwołanie do $env:temp)
  • Plik log będzie zawierał log z działania skryptu
  • a plik scriptFile zawiera skrypt, w którym znajdzie się polecenie włączające remoting
  • dwa polecenie SCHTASKS po pierwsze tworzą zadanie w harmonogramie, a po drugie uruchamiają to zadanie. Opis schtasks znajdziesz na msdn
  • Pętla for spowalnia skrypt, czekając aż uda się nawiązać połączenie z localhost przez remoting (tak z localhost, bo polecenia są uruchamiane na systemie lokalnym)
  • Na zakończenie zadanie z harmonogramu będzie usunięte i zatrzymamy logowanie. Plik można przejrzeć w celu debuggownaia.

No dobrze. Skrypt już jest gotowy, teraz chcemy go wysłać i wykonać na systemie zdalnym. Dlatego konwertujemy skrypt do postaci Base64.

$commandBytes = [System.Text.Encoding]::Unicode.GetBytes($script)
 $encoded = [Convert]::ToBase64String($commandBytes)

Teraz wystarczy dokleić zakodowany skrypt do polecenia powershell.exe i uruchomić ten skrypt na maszynie zdalnej przez Invoke-WMIMethod:

Write-Verbose "Configuring $computername"
 $command = "powershell -NoProfile -EncodedCommand $encoded"
 $null = Invoke-WmiMethod -Computer $computername -Credential $credential `
 Win32_Process Create -Args $command

Teraz wszystko w rękach powershella. Na dodatek wykonanie zadania w harmonogramie zadania odbywa się asynchronicznie, tzn naszy skrypt przechodzi do kolejnych kroków, a zadanie jeszcze nie zdążyło się wykonać na serwerze. Dlatego jeszcze raz dodajemy pętelkę for, która będzie sprawdzać, czy remoting już działa. Jeśli wszystko jest ok, to w ostatnim kroku wyświetli informacje o maszynie zdalnej:

## Wait for the remoting changes to come into effect
 for($count = 1; $count -le 10; $count++)
 { 
 $output = Invoke-Command $Computername { 1 } -Cred $credential -ErrorAction SilentlyContinue 
 if($output -eq 1) { Write-Host "Done!!!"; break; } 
 Write-Host "Attempt $count : Not ready yet." 
 Sleep 5
 } 
 
 Write-Verbose "Testing connection"
 Invoke-Command $computername {
 Get-WmiObject Win32_ComputerSystem } -Credential $credential
}

Komentarze:

  1. Szkolenie Powershell po polsku. Informacja dla studentów | Kursy OnlineKursy Online napisał,

    […] Często pojawiające się pytanie, to czy remoting może być włączony zdalnie i to najlepiej przez jakąś komendę w powershellu. Odpowiedź wydaje się oczywista: NIE! Ale czy na pewno? Jak sugeruje tytuł kolejnego wpisu, chyba jednak powinno się udać! Ja używam tej metody produkcyjnie i u mnie działa. Proste to nie jest, wymaga kilku sztuczek, ale o prawie wszystkich mówimy na naszym kursie, więc jeśli już kończysz materiał – nie powinno być z tym problemu. Powershell: Włączenie remotingu z maszyny zdalnej. Niemożliwe? Wcale nie! […]

Autor: Rafał Kraik