E Wäert vum Powershell Invoke-Command op SQL Server Agent zréckginn

Wann ech meng eege Methodik erstellen fir Backups op multiple MS-SQL Serveren ze managen, hunn ech vill Zäit verbruecht fir de Mechanismus ze studéieren fir Wäerter an Powershell während Remote Calls ze vermëttelen, also schreiwen ech eng Erënnerung fir mech am Fall wou et nëtzlech ass zu engem aneren.

Also, loosst eis mat engem einfachen Skript ufänken an et lokal ausféieren:

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

Fir Scripten auszeféieren, benotzen ech déi folgend CMD Datei, ech wäert et net all Kéier enthalen:

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

Um Écran wäerte mir déi folgend gesinn:

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


Loosst eis deeselwechte Skript iwwer WSMAN lafen (Fern):

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

An hei ass d'Resultat:

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

Super, Feelerlevel ass iergendwou verschwonnen, awer mir mussen de Wäert vum Skript kréien! Loosst eis de folgenden Design probéieren:

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

Dëst ass nach méi interessant. De Message am Output ass iergendwou verschwonnen:

Out to host.
ExitCode: 2
ERRORLEVEL=0

Elo, als lyresch Digression, wäert ech feststellen datt wann Dir an enger Powershell Funktioun Write-Output schreift oder just en Ausdrock ouni et un eng Variabel ze zouzeweisen (an dat implizit implizit Ausgang op den Output Channel), dann och wann Dir lokal leeft, näischt gëtt um Écran ugewisen! Dëst ass eng Konsequenz vun der Powershell Pipeline Architektur - all Funktioun huet seng eege Output Pipeline, eng Array gëtt dofir erstallt, an alles wat an et geet gëtt als Resultat vun der Funktiounsausféierung ugesinn, de Return Bedreiwer füügt de Retourwäert un datselwecht Pipeline als lescht Element an iwwerdroen d'Kontroll op d'Urufffunktioun. Fir ze illustréieren, loosst eis de folgende Skript lokal ausféieren:

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

An hei ass d'Resultat:

Main: ParameterValue

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

D'Haaptfunktioun (Skript Kierper) huet och seng eege Output Pipeline, a wa mir den éischte Skript aus CMD lafen, d'Output op eng Datei redirectéieren,

PowerShell .TestOutput1.ps1 1 > TestOutput1.txt

da wäerte mir um Écran gesinn

ERRORLEVEL=1

an am Dossier

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

wa mir en ähnlechen Uruff vu Powershell maachen

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

da gëtt et um Écran

Out to host.
ExitCode: 1

an am Dossier

Out to output.
1

Dëst geschitt well d'CMD Powershell lancéiert, déi, an der Verontreiung vun aneren Instruktiounen, zwee Threads (Host an Output) vermëscht a se un d'CMD gëtt, déi alles wat se an eng Datei schéckt, an am Fall vu Powershell starten, dës zwee thread existéieren separat, an d'Symbolviruleedungen beaflossen nëmmen d'Output.

Zréck op d'Haaptthema, loosst eis drun erënneren datt den .NET Objektmodell bannent Powershell komplett an engem Computer existéiert (een OS), wann Dir Code op afstand iwwer WSMAN leeft, geschitt den Transfer vun Objeten duerch XML Serialiséierung, wat vill zousätzlech Interessi bréngt fir eis Fuerschung. Loosst eis weider experimentéieren andeems Dir de folgende Code leeft:

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

An dat ass wat mir um Écran hunn:

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

Flott Resultat! Et heescht datt wann Dir Invoke-Command rufft, d'Divisioun vun de Pipelines an zwee Threads (Host an Output) erhale bleift, wat eis Hoffnung op Erfolleg gëtt. Loosst eis probéieren nëmmen ee Wäert am Output Stream ze loossen, fir dee mir dat éischt Skript änneren, dee mir op afstand lafen:

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

Loosst eis et esou lafen:

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

an ... JO, et gesäit aus wéi eng Victoire!

Out to host.
ExitCode: 4

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


ERRORLEVEL=4

Loosst eis probéieren erauszefannen wat geschitt ass. Mir hunn Powershell lokal genannt, wat am Tour Powershell um Ferncomputer genannt huet an eise Skript do ausgefouert huet. Zwee Streams (Host an Output) vun der Fernmaschinn goufen serialiséiert an zréckginn, während den Output Stream, mat engem eenzegen digitale Wäert dran, an den Typ Int32 ëmgewandelt gouf an als solch op d'Empfangssäit weiderginn, an d'Empfangssäit huet et benotzt. als Ausgangscode vum Uruffer Powershell.

An als Schlussprüfung, loosst eis eng Een-Schrëtt Aarbecht um SQL Server mam Typ "Betriebssystem (cmdexec)" mam folgenden Text erstellen:

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

HOURAY! D'Aufgab fäerdeg mat engem Feeler, Text am Logbuch:

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

Konklusiounen:

  • Vermeit Schreifweis ze benotzen an Ausdréck ze spezifizéieren ouni Aufgab. Sidd bewosst datt dëse Code anzwousch am Skript verschécken kann onerwaart Resultater produzéieren.
  • A Skripte virgesinn net fir manuell Start, awer fir an Ären Automatisatiounsmechanismen ze benotzen, besonnesch fir Remote Appellen iwwer WINRM, maacht manuell Fehlerhandhabung iwwer Try/Catch, a suergt dofir datt, an all Entwécklung vun Eventer, dëst Skript genau ee primitiven Typwäert schéckt . Wann Dir de klassesche Feelerlevel wëllt kréien, muss dëse Wäert numeresch sinn.

Source: will.com

Setzt e Commentaire