Powershell invoke-command-dan SQL Server agentinə dəyərin qaytarılması

Çoxsaylı MS-SQL serverlərində ehtiyat nüsxələri idarə etmək üçün öz metodologiyamı yaratarkən, uzaqdan zənglər zamanı Powershell-də dəyərlərin ötürülməsi mexanizmini öyrənməyə çox vaxt sərf etdim, ona görə də faydalı olacağı təqdirdə özümə xatırlatma yazıram. başqasına.

Beləliklə, sadə bir skriptlə başlayaq və onu yerli olaraq işə salaq:

$exitcode = $args[0]
Write-Host 'Out to host.'
Write-Output 'Out to output.'
Write-Host ('ExitCode: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)

Skriptləri işə salmaq üçün aşağıdakı CMD faylından istifadə edəcəyəm, onu hər dəfə daxil etməyəcəyəm:

@Echo OFF
PowerShell .TestOutput1.ps1 1
ECHO ERRORLEVEL=%ERRORLEVEL%

Ekranda aşağıdakıları görəcəyik:

Out to host.
Out to output.
ExitCode: 1
1
ERRORLEVEL=1


İndi eyni skripti WSMAN (uzaqdan) vasitəsilə işə salaq:

Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]

Və nəticə budur:

Out to host.
Out to output.
ExitCode: 2
2
ERRORLEVEL=0

Əla, Errorlevel haradasa itdi, lakin biz skriptdən dəyəri almalıyıq! Aşağıdakı tikintini sınayaq:

$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]

Bu daha maraqlıdır. Çıxışdakı mesaj haradasa itdi:

Out to host.
ExitCode: 2
ERRORLEVEL=0

İndi, lirik bir təxribat kimi qeyd edəcəm ki, əgər Powershell funksiyası daxilində hər hansı bir dəyişənə təyin etmədən Yazma-Çıxış və ya sadəcə ifadə yazırsınızsa (və bu, Çıxış kanalına çıxışı nəzərdə tutur), hətta yerli olaraq işləyərkən belə, ekranda heç bir şey görünməyəcək! Bu, powershell boru kəməri arxitekturasının nəticəsidir - hər bir funksiyanın öz Çıxış kəməri var, onun üçün bir massiv yaradılır və ona daxil olan hər şey funksiyanın icrasının nəticəsi hesab olunur, Return operatoru qaytarma dəyərini eyni yerə əlavə edir. boru kəməri son element kimi və nəzarəti çağıran funksiyaya ötürür. Nümunə etmək üçün aşağıdakı skripti yerli olaraq işə salaq:

Function Write-Log {
  Param( [Parameter(Mandatory=$false, ValueFromPipeline=$true)] [String[]] $OutString = "`r`n" )
  Write-Output ("Function: "+$OutString)
  Return "ReturnValue"
}
Write-Output ("Main: "+"ParameterValue")
$res = Write-Log "ParameterValue"
$res.GetType()
$res.Length
$res | Foreach-Object { Write-Host ("Main: "+$_) }

Və nəticə budur:

Main: ParameterValue

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array
2
Main: Function: ParameterValue
Main: ReturnValue

Əsas funksiyanın (skript gövdəsi) də öz Çıxış kəməri var və ilk skripti CMD-dən işə salsaq, çıxışı fayla yönləndiririk,

PowerShell .TestOutput1.ps1 1 > TestOutput1.txt

sonra ekranda görəcəyik

ERRORLEVEL=1

və faylda

Out to host.
Out to output.
ExitCode: 1
1

powershell-dən oxşar zəng etsək

PS D:sqlagent> .TestOutput1.ps1 1 > TestOutput1.txt

sonra ekranda olacaq

Out to host.
ExitCode: 1

və faylda

Out to output.
1

Bunun səbəbi, CMD digər təlimatlar olmadıqda, iki mövzunu (Host və Çıxış) qarışdıran və qəbul etdiyi hər şeyi fayla göndərən CMD-yə verən powershell-i işə salır və powershell-dən işə salındıqda, bu iki mövzu ayrı-ayrılıqda mövcuddur və simvol yönləndirməsi yalnız Çıxışa təsir edir.

Əsas mövzuya qayıdaraq xatırlayaq ki, powershell daxilindəki .NET obyekt modeli tam olaraq bir kompüter (bir ƏS) daxilində mövcuddur, kodu uzaqdan WSMAN vasitəsilə işlədərkən obyektlərin ötürülməsi XML serializasiyası vasitəsilə baş verir ki, bu da əlavə maraq doğurur. tədqiqatımıza. Aşağıdakı kodu işlətməklə təcrübələrimizə davam edək:

$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
$res.GetType()
$host.SetShouldExit($res)

Və ekranda gördüyümüz budur:

Out to host.

ExitCode: 3

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array
Не удается преобразовать аргумент "exitCode", со значением: "System.Object[]", для "SetShouldExit" в тип "System.Int32": "Не удается преобразовать значение "System.Object[]" типа "System.Object[]" в тип "System
.Int32"."
D:sqlagentTestOutput3.ps1:3 знак:1
+ $host.SetShouldExit($res)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

ERRORLEVEL=0

Əla nəticə! Bu o deməkdir ki, Invoke-Command çağırarkən boru kəmərlərinin iki mövzuya bölünməsi (Host və Output) saxlanılır və bu, bizə uğur üçün ümid verir. Gəlin Çıxış axınında yalnız bir dəyər buraxmağa çalışaq, bunun üçün uzaqdan işlədiyimiz ilk skripti dəyişdirəcəyik:

$exitcode = $args[0]
Write-Host 'Out to host.'
#Write-Output 'Out to output.'
Write-Host ('ExitCode: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)

Gəlin bunu belə icra edək:

$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
$host.SetShouldExit($res)

və... BƏLİ, zəfər kimi görünür!

Out to host.
ExitCode: 4

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int32                                    System.ValueType


ERRORLEVEL=4

Gəlin nə baş verdiyini anlamağa çalışaq. Biz yerli olaraq powershell çağırdıq, o da öz növbəsində uzaq kompüterdə powershell çağırdı və skriptimizi orada icra etdi. Uzaq maşından iki axın (Host və Çıxış) seriallaşdırıldı və geri ötürüldü, tək rəqəmsal dəyəri olan Çıxış axını Int32 tipinə çevrildi və beləliklə qəbul edən tərəfə keçdi və qəbul edən tərəf ondan istifadə etdi. zəng edənin powershell-in çıxış kodu kimi.

Və son yoxlama olaraq, yaradaq server Aşağıdakı mətnlə birlikdə "Əməliyyat Sistemi (cmdexec)" tipli tək addımlı SQL tapşırığı:

PowerShell -NonInteractive -NoProfile "$res=Invoke-Command -ComputerName BACKUPSERVER -ConfigurationName SQLAgent -ScriptBlock {&'D:sqlagentTestOutput1.ps1' 6}; $host.SetShouldExit($res)"

HAYIR! Tapşırıq xəta ilə tamamlandı, jurnaldakı mətn:

Выполняется от имени пользователя: DOMAINagentuser. Out to host. ExitCode: 6.  Код завершения процесса 6.  Шаг завершился с ошибкой.

Sonuç:

  • Yazma-Çıxışdan istifadə etməkdən və təyinat olmadan ifadələr təyin etməkdən çəkinin. Nəzərə alın ki, bu kodu skriptin başqa yerə köçürmək gözlənilməz nəticələr verə bilər.
  • Əllə işə salmaq üçün deyil, avtomatlaşdırma mexanizmlərinizdə istifadə üçün nəzərdə tutulmuş skriptlərdə, xüsusən də WINRM vasitəsilə uzaqdan zənglər üçün, Sınaq/Yaxala vasitəsilə səhvlərin əllə idarə olunmasını həyata keçirin və hadisələrin hər hansı inkişafında bu skriptin tam olaraq bir primitiv tip dəyəri göndərdiyinə əmin olun. . Klassik Səhv səviyyəsini əldə etmək istəyirsinizsə, bu dəyər rəqəmli olmalıdır.

Mənbə: www.habr.com

DDoS mühafizəsi, VPS VDS serverləri olan saytlar üçün etibarlı hostinq alın 🔥 DDoS qorunması, VPS VDS serverləri ilə etibarlı veb sayt hostinqi alın | ProHoster