Skilar gildi frá powershell invoke-command til SQL Server umboðsmanns

Þegar ég bjó til mína eigin aðferðafræði til að stjórna öryggisafritum á mörgum MS-SQL netþjónum, eyddi ég miklum tíma í að rannsaka aðferðina til að senda gildi í Powershell meðan á fjarsímtölum stendur, svo ég er að skrifa áminningu fyrir sjálfan mig ef það er gagnlegt til einhvers annars.

Svo, við skulum byrja á einföldu handriti og keyra það á staðnum:

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

Til að keyra forskriftir mun ég nota eftirfarandi CMD skrá, ég mun ekki láta hana fylgja í hvert skipti:

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

Á skjánum munum við sjá eftirfarandi:

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


Nú skulum við keyra sama handritið í gegnum WSMAN (fjarlægt):

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

Og hér er niðurstaðan:

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

Frábært, villustigið hefur horfið einhvers staðar, en við þurfum að fá gildið úr handritinu! Prófum eftirfarandi smíði:

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

Þetta er enn áhugaverðara. Skilaboðin í Output hafa horfið einhvers staðar:

Out to host.
ExitCode: 2
ERRORLEVEL=0

Nú, sem ljóðræn útrás, tek ég fram að ef inni í Powershell falli þú skrifar Write-Output eða bara tjáningu án þess að tengja það við neina breytu (og þetta þýðir óbeint úttak á Output rásina), þá jafnvel þegar þú keyrir á staðnum, ekkert mun birtast á skjánum! Þetta er afleiðing af powershell pipeline arkitektúr - hver aðgerð hefur sína eigin Output pipeline, fylki er búið til fyrir hana og allt sem fer í það er talið afleiðing af framkvæmd fallsins, Return operator bætir return gildinu við það sama leiðsla sem síðasta þátturinn og flytur stjórn yfir á kallaaðgerðina. Til að sýna fram á, skulum við keyra eftirfarandi handrit á staðnum:

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

Og hér er niðurstaðan:

Main: ParameterValue

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

Aðalaðgerðin (forskriftarhluti) hefur líka sína eigin úttaksleiðslu, og ef við keyrum fyrsta handritið frá CMD, beinum úttakinu í skrá,

PowerShell .TestOutput1.ps1 1 > TestOutput1.txt

þá munum við sjá á skjánum

ERRORLEVEL=1

og í skránni

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

ef við hringjum svipað frá powershell

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

þá verður það á skjánum

Out to host.
ExitCode: 1

og í skránni

Out to output.
1

Þetta gerist vegna þess að CMD ræsir powershell, sem, í fjarveru annarra leiðbeininga, blandar saman tveimur þráðum (Host og Output) og gefur þeim til CMD, sem sendir allt sem það fékk í skrá, og ef ræst er úr powershell, þessir tveir þræðir eru til í sitt hvoru lagi og tákntilvísanir hafa aðeins áhrif á úttak.

Ef við snúum aftur að aðalefninu, skulum við muna að .NET-hlutalíkanið í powershell er að fullu til í einni tölvu (eitt stýrikerfi), þegar keyrt er fjarstýrt kóða í gegnum WSMAN, þá á flutningur hluta sér stað með XML-raðgerð, sem vekur aukinn áhuga. til rannsókna okkar. Höldum áfram tilraunum okkar með því að keyra eftirfarandi kóða:

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

Og þetta er það sem við höfum á skjánum:

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

Frábær árangur! Það þýðir að þegar hringt er í Invoke-Command er skipting leiðslunnar í tvo þræði (Host og Output) viðhaldið sem gefur okkur von um árangur. Við skulum reyna að skilja aðeins eftir eitt gildi í Output straumnum, fyrir það munum við breyta allra fyrstu skriftu sem við keyrum lítillega:

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

Við skulum keyra þetta svona:

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

og... JÁ, þetta lítur út fyrir að vera sigur!

Out to host.
ExitCode: 4

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


ERRORLEVEL=4

Við skulum reyna að komast að því hvað gerðist. Við kölluðum powershell á staðnum, sem aftur kallaði powershell á ytri tölvunni og keyrðum scriptið okkar þar. Tveir straumar (Host og Output) frá fjartengdu vélinni voru settir í raðnúmer og fluttir til baka, en Output straumurinn, sem hafði eitt stafrænt gildi í sér, var breytt í gerð Int32 og sem slíkur sendur yfir á móttökuhliðina og móttökuhliðin notaði hann sem útgöngukóði fyrir powershell sem hringir.

Og sem lokaathugun skulum við búa til eins skrefs starf á SQL þjóninum með gerðinni „Stýrikerfi (cmdexec)“ með eftirfarandi texta:

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

HÚRRA! Verkefninu lauk með villu, texti í annál:

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

Ályktanir:

  • Forðastu að nota Write-Output og tilgreina tjáningu án úthlutunar. Vertu meðvituð um að færa þennan kóða annað í handritinu getur valdið óvæntum niðurstöðum.
  • Í forskriftum sem ekki eru ætlaðar til handvirkrar ræsingar, heldur til notkunar í sjálfvirknikerfum þínum, sérstaklega fyrir fjarsímtöl í gegnum WINRM, gerðu handvirka villumeðferð í gegnum Try/Catch og tryggðu að í hvaða þróun atburða sem er, sendir þetta handrit nákvæmlega eitt frumstætt tegundargildi . Ef þú vilt fá klassíska villustigið verður þetta gildi að vera tölulegt.

Heimild: www.habr.com

Bæta við athugasemd