SQL-Serveri agent tsembreerimise kaudu PowerShell invoke-commandi kaudu.

Oma varukoopiate haldamise meetodite loomisel mitu MS-SQL serverit kulutasin ma palju aega PowerShellis kaugkutses väärtuste edastamise mehhanismi õppimisele, seetõttu kirjutan endale märkmed, ehk on kellelegi veel kasu.

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

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

Skripti käitamiseks kasutan järgmise CMD-faili, ma ei hakka seda iga kord kordama:

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

Küljel näeme järgmist:

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


Nüüd jooksutame sama skripti WSMANi kaudu (eemalt):

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

Ja siin on tulemus:

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

Üllatav, Errorlevel on kadunud, kuid meil on vaja skriptist väärtust saada! Proovime järgmist konstruktsiooni:

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

Siin on veel huvitavam. Kogu väljund väljundisse on kuhugi kadunud:

Väljund hostile.
ExitCode: 2
ERRORLEVEL=0

Nüüd märkuseks, et kui kirjutate Powershelli funktsiooni sees Write-Output või lihtsalt väljendi, ilma et see oleks määratud mõnele muutujale (mis eeldab väljundit väljundkanalisse), siis isegi kohaliku käivitamise korral ei kuvata midagi ekraanile! See on PowerShelli torustiku arhitektuuri tagajärg - igal funktsioonil on oma väljundkanal, mille jaoks luuakse massiiv, ja kõik, mis sinna satub, loetakse funktsiooni täitmise tulemuseks, operaatort Return lisab tagastatava väärtuse sellele kanali viimase elemendina ja edastab juhtimise kutsuvasse funktsiooni. Näiteks käivitame kohapeal järgmise skripti:

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

Ja siin on selle tulemus:

Peamine: ParameetriVäärtus

IsPublic IsSerial Nimi                                     PõhiTüüp
-------- -------- ----                                     --------
True     True     Objekt[]                                 System.Array
2
Peamine: Funktsioon: ParameetriVäärtus
Peamine: TagastatavVäärtus

Peamine funktsioon (skripti keha) omab samuti 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

Käib hostile.
Käib väljundisse.
Väljundi kood: 1
1

kui aga teeme sarnase väljakutse PowerShellist

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

siis ekraanil on

Käib hostile.
Väljundi kood: 1

ja failis

Käib väljundisse.
1

See juhtub, kuna CMD käivitab PowerShelli, mis ilma teiste näidisteta segab kahte voogu (Host ja Output) ja annab need edasi CMD-le, mis saadab failisse kõik, mis sai, ja PowerShellist käivitamisel eksisteerivad need kaks voogu eraldi ning suunamis sümbol mõjutab ainult Outputi.

Tagasi põhiteemasse, tuletame meelde, et .NET objekti mudel PowerShellis eksisteerib täielikult ühe arvuti (ühe OS) raames. Koodide kaugkäitamisel WSMAN kaudu toimub objektide edastamine XML-sarjanduse kaudu, mis toob meie uuringutesse palju lisahuvi. Jätkame eksperimente järgmise koode käivitamisega:

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

Ja siin on meile ekraanil:

Käib hostile.

Väljundi kood: 3

IsPublic IsSerial Nimi                                    AlusTüüp
-------- -------- ----                                    --------
True     True     Object[]                                System.Array
Argumenti "exitCode" väärtusega: "System.Object[]" ei saa konverteerida "SetShouldExit" jaoks tüübiks "System.Int32": "System.Object[]" tüübi "System.Object[]" väärtust ei saa konverteerida tüübiks "System.Int32"."
D:sqlagentTestOutput3.ps1:3 märk:1
+ $host.SetShouldExit($res)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
    + KategooriaInfo          : Määratlemata: (:) [], MethodException
    + TäielikultMääratletudVigaId : MethodArgumentConversionInvalidCastArgument

ERRORLEVEL=0

Imeline tulemus! See tähendab, et Invoke-Command’i kutsumisel säilivad konveierite jagunemised kahte voogu (Host ja Output), mis annab meile lootust edule. Proovime jätta väljundis ainult ühe väärtuse, selleks muudame kõige esimest skripti, mida kaugelt käivitame:

$exitcode = $args[0]
Write-Host 'Käib hostile.'
#Write-Output 'Käib väljundisse.'
Write-Host ('Väljundi kood: ' + $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!

Käib hostile.
Väljundi kood: 4

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


ERRORLEVEL=4

Üritame mõista, mis meiega juhtus. Me kutsusime kohalikult esile PowerShell'i, mis omakorda kutsus esile PowerShell'i eemaloleval arvutil ja käivitas seal meie skripti. Kaks voogu (Host ja Output) eemalt arvutist seriaaliti edastati tagasi, samas kui Output voog, kui see sisaldas ühte numbrilist väärtust, muundati Int32 tüübiks ja edastati vastuvõtjale, kes kasutas seda PowerShell'i väljakutsuja väljumiskoodina.

Ja viimase kontrollina loome serveris SQL ülesande ühe sammu tüüpi „Operatsioonisüsteem (cmdexec)“ sellise tekstiga:

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

HURRAA! Ülesanne lõpetati veaga, ajaloos on tekst:

Teostab kasutaja nimega: DOMAINagentuser. Väljastab hostile. ExitCode: 6.  Protsessi väljumiskood on 6.  Samm lõppes veaga.

Kokkuvõtted:

  • Vältige Write-Output kasutamist ja väljendite määramist ilma omistamiseta. Pidage meeles, et selle koodi viimine teise kohta skriptis võib põhjustada ootamatuid tulemusi.
  • Skriptides, mis ei ole ette nähtud käsitsi käivitamiseks, vaid automaatika mehhanismides teie jaoks, eriti kaugkutsed WINRM kaudu, tehke vigade käsitlemine käsitsi läbi Try/Catch ja tagage, et iga olukorra korral saadaks see skript Output voogu täpselt ühe primitiivse tüübi väärtuse. Kui soovite klassikalist Errorlevel'i — peab see väärtus olema numbriline.

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster