Väärtuse tagastamine Powershell'i invoke-command'i kaudu SQL-Serveri agentidele

Oma MS-SQL varundamisstrategia loomisel kulutasin ma palju aega PowerShelli väärtuste edastamise mehhanismi õppimisele kaugkutsede puhul, seega kirjutan endale märkmeid, äkki läheb see kellelegi muulegi vaja.

Alustame kõige lihtsamast skriptist ja käivitame selle kohalikult:

$exitcode = $args[0]
Write-Host 'Väljund hostile.'
Write-Output 'Väljund väljundisse.'
Write-Host ('Lahkumiskood: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)

Skriptide käivitamiseks kasutan ma järgmist CMD-faili, millest ma iga kord ei räägi:

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

Kraanil näeme järgmist:

Väljund hostile.
Väljund väljundisse.
Lahkumiskood: 1
1
ERRORLEVEL=1


Nüüd käivitame sama skripti WSMAN kaudu (kaugelt):

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

Ja siin on tulemus:

Väljund hostile.
Väljund väljundisse.
Lahkumiskood: 2
2
ERRORLEVEL=0

Imeline, Errorlevel on kuhugi kadunud, aga me peame ju skriptist väärtust saama! Proovime järgmist konstruktsiooni:

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

Siin on veel huvitavam. Kõik väljund väljundisse on kuhugi kadunud:

Väljund hostile.
Lahkumiskood: 2
ERRORLEVEL=0

Nüüd, kui lühiülevaateks märkida, et kui Powershelli funktsiooni sees kirjutate Write-Output või lihtsalt väljendi, millele te ei määranud mingit muutujat (mis eeldab, et see suunatakse väljundkanalisse), siis ei kuvata isegi kohalikult käivitamisel ekraanile midagi! See tuleneb powershelli torustiku arhitektuurist - igal funktsioonil on oma väljundkanal, mille jaoks luuakse massiiv, ja kõik, mis sinna satub, loetakse funktsiooni täitmise tulemuseks; Return-lause lisab tagastatava väärtuse sellesse kanali viimase elemendina ja edastab kontrolli kutsuvasse funktsiooni. Illustratsiooniks käivitage järgmine skript kohalikult:

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: " + $_) }

Ja siin on tema tulemus:

Main: ParameterValue

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

Peamine funktsioon (skripti keha) omab ka oma väljundi toru, ja kui me käivitame esimese skripti CMD-st, suunates väljundi faili,

PowerShell .TestOutput1.ps1 1 > TestOutput1.txt

siis näeme ekraanil

ERRORLEVEL=1

ja failis

Väljund hosdile.
Väljund väljundisse.
Väljundkood: 1
1

kui me teeme sarnase väljakutse powershellist

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

siis ekraanil kuvatakse

Väljund hosdile.
Väljundkood: 1

ja failis

Väljund väljundisse.
1

See juhtub, kuna CMD käivitab powershelli, mis segab kahte voogu (Host ja Output) ilma muude juhisteta ja edastab need CMD-le, mis saadab faili kõik, mis ta sai, kuid powershellist käivitamisel on need kaks voogu eraldi ning suunamissümbol mõjutab ainult Outputi.

Tagasi põhipunkti juurde, meenutame, et .NET objekti mudel powershellis eksisteerib täielikult ühe arvuti (ühe OS) ulatuses; kaugkoodi käivitamisel WSMAN-i kaudu toimub objektide edastamine XML-serialiseerimise kaudu, mis toob meie uurimisse palju lisahuvi. Jätkame katsetuste tegemist järgmise koodiga:

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

Ja siin on, mida me ekraanil näeme:

Väline hostimise.

Väljutuskood: 3

IsPublic IsSerial Nimi                                     Baastüüp
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array
Argumenti "exitCode" väärtuse "System.Object[]" teisendamine "SetShouldExit" tüübiks "System.Int32" ebaõnnestus: "Ei ole võimalik teisendada väärtust "System.Object[]" tüübist "System.Object[]" tüübiks "System.Int32".
D:sqlagentTestOutput3.ps1:3 rida:1
+ $host.SetShouldExit($res)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
    + KategooriaInfo       : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

ERRORLEVEL=0

Suurepärane tulemus! See tähistab, et Invoke-Commandi kutsumisel jaguneb torustik kaheks vooluks (Host ja Output), mis annab meile lootust eduks. Proovime jätta Output voolu ainult ühe väärtuse, muutes kõigepealt skripti, mida käivitasime kaugjuhtimise teel:

$exitcode = $args[0]
Write-Host 'Väline hostimise.'
#Write-Output 'Väline väljund.'
Write-Host ('Väljutuskood: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)

Käivitame selle nii:

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

ja… JAH, tundub, et see on võit!

Väline hostimise.
Väljutuskood: 4

IsPublic IsSerial Nimi                                     Baastüüp
-------- -------- ----                                     --------
True     True     Int32                                    System.ValueType


ERRORLEVEL=4

Püüame välja selgitada, mis meiega juhtus. Käivitasime kohalikult PowerShelli, mis omakorda kutsus esile PowerShelli kaugarvutis ja exécutis seal meie skripti. Kaks voogu (Host ja Output) kaugarvutitest serialiseeriti ja edastati tagasi, kusjuures Outputi voog, kui see sisaldas ühte numbrilist väärtust, muudeti tyybiks Int32 ja saadeti vastuvõtjale, kes kasutas seda, et lõpukoodina PowerShelli jaoks.

Ja viimase kontrollina loome serveril SQL ülesande ühe sammu jaotisega, mille tüüp on «Operatsioonisüsteem (cmdexec)» ja mille tekst on:

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

TÄHELEPANU! Ülesanne lõpetati veaga, logis tekst:

Käib kasutaja nimel: DOMAINagentuser. Väljund hostile. ExitCode: 6. Protsessi lõpetamiskood 6. Samm lõppes veaga.

Järeldused:

  • Vältige Write-Outputi kasutamist ja väljendite määramist ilma omistamiseta. Pidage meeles, et selle koodi teisaldamine teise kohta skripti võib põhjustada ettenägematuid tulemusi.
  • Automatiseerimismehhanismides, mitte käsitsi käivitamiseks mõeldud skriptides, eriti WINRM kaudu kaugkutsumiseks, tehke vigade käsitlemine läbi Try/Catch ning veenduge, et igas olukorras saadetakse Output voogu täpselt üks primitiivne tüübi väärtus. Kui soovite klassikalist Errorleveli, peab see väärtus olema numbriline.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster