Powershell invoke-буйругунан SQL Server агентине маанини кайтаруу

Бир нече MS-SQL серверлеринде камдык көчүрмөлөрдү башкаруу боюнча өзүмдүн методологиямды түзүп жатканда, мен алыскы чалуулар учурунда Powershellде баалуулуктарды өткөрүү механизмин изилдөөгө көп убакыт короттум, ошондуктан бул пайдалуу болсо, өзүмө эскертүү жазып жатам. башка бирөөгө.

Ошентип, жөнөкөй скрипт менен баштайлы жана аны жергиликтүү иштетели:

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

Скрипттерди иштетүү үчүн мен төмөнкү CMD файлын колдоном, мен аны ар дайым киргизбейм:

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

Экранда биз төмөнкүлөрдү көрөбүз:

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


Эми ошол эле сценарийди WSMAN аркылуу иштетели (алыстан):

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

Жана бул жерде жыйынтык:

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

Абдан жакшы, Errorlevel бир жерде жоголуп кетти, бирок биз сценарийден маани алышыбыз керек! Келгиле, төмөнкү дизайнды сынап көрөлү:

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

Бул дагы кызыктуу. Output ичиндеги билдирүү бир жерде жоголуп кетти:

Out to host.
ExitCode: 2
ERRORLEVEL=0

Эми, лирикалык чегинүү катары, эгер Powershell функциясынын ичинде сиз Write-Output же жөн гана туюнтманы кандайдыр бир өзгөрмөгө дайындабастан жазсаңыз (жана бул кыйыр түрдө Output каналына чыгарууну билдирет), анда локалдуу иштеп жатканда да, экранда эч нерсе көрүнбөйт! Бул powershell түтүкчөлөрүнүн архитектурасынын кесепети - ар бир функциянын өзүнүн Чыгуу конвейери бар, ал үчүн массив түзүлөт жана ага кирген бардык нерсе функциянын аткарылышынын натыйжасы болуп эсептелет, Return оператору ошол эле нерсеге кайтаруу маанисин кошот. конвейери акыркы элемент катары жана башкарууну чакыруу функциясына өткөрүп берет. Көрсөтүү үчүн, төмөнкү скриптти жергиликтүү иштетели:

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

Жана бул жерде жыйынтык:

Main: ParameterValue

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

Негизги функциянын (скрипт корпусунун) өзүнүн Output конвейери да бар жана эгерде биз CMDден биринчи скриптти иштетсек, чыгарууну файлга багыттоо,

PowerShell .TestOutput1.ps1 1 > TestOutput1.txt

анда биз экранда көрөбүз

ERRORLEVEL=1

жана файлда

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

эгерде биз powershellден ушундай чакырык жасасак

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

анда ал экранда болот

Out to host.
ExitCode: 1

жана файлда

Out to output.
1

Бул CMD башка нускамалар жок болгон учурда эки жипти (Хост жана Чыгуу) аралаштырган жана CMDге берген powershellди ишке киргизгендиктен болот, ал алган нерселердин бардыгын файлга жөнөтөт, ал эми powershellден ишке киргизген учурда, бул эки жип өз-өзүнчө бар жана символ кайра багыттоо Чыгууга гана таасир этет.

Негизги темага кайтып келсек, powershell ичиндеги .NET объектинин модели толугу менен бир компьютердин ичинде (бир ОС) бар экенин эстейли, кодду WSMAN аркылуу алыстан иштеткенде объекттерди өткөрүп берүү XML сериялаштыруу аркылуу ишке ашат, бул көптөгөн кошумча кызыгууну жаратат. биздин изилдөөбүзгө. Төмөнкү кодду иштетүү менен эксперименттерибизди уланталы:

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

Жана бул бизде экранда:

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

Мыкты натыйжа! Бул Invoke-Command чакырганда түтүктөрдү эки жипке (Хост жана Чыгуу) бөлүү сакталат дегенди билдирет, бул бизге ийгиликке үмүт берет. Чыгуу агымында бир гана маанини калтырууга аракет кылалы, ал үчүн биз алыстан иштеткен эң биринчи сценарийди өзгөртөбүз:

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

Келгиле, аны мындай иштетели:

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

жана... ООБА, бул жеңиш окшойт!

Out to host.
ExitCode: 4

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


ERRORLEVEL=4

Келгиле, эмне болгонун аныктоого аракет кылалы. Биз локалдык түрдө powershell деп атадык, ал өз кезегинде алыскы компьютерде powershell чакырып, скриптибизди ошол жерде аткардык. Алыскы машинадан эки агым (Хост жана Чыгуу) сериялаштырылган жана артка жөнөтүлгөн, ал эми бир санариптик мааниге ээ болгон Чыгуу агымы Int32 түрүнө айландырылган жана кабыл алуучу тарапка өткөн жана кабыл алуучу тарап аны колдонгон. чалуучу powershellдин чыгуу коду катары.

Ал эми акыркы текшерүү катары, SQL серверинде "Операциялык система (cmdexec)" түрү менен төмөнкү текст менен бир кадамдуу жумуш түзөлү:

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

ЖАШАСЫН! Тапшырма ката менен аяктады, журналдагы текст:

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

корутундулар:

  • Write-Output колдонуудан жана дайындоосуз туюнтмаларды көрсөтүүдөн качыңыз. Бул кодду скрипттин башка жерине жылдыруу күтүлбөгөн натыйжаларга алып келиши мүмкүн экенин унутпаңыз.
  • Кол менен ишке киргизүү үчүн эмес, автоматташтыруу механизмдериңизде колдонуу үчүн, айрыкча WINRM аркылуу алыскы чалуулар үчүн арналган скрипттерде Try/Catch аркылуу катаны кол менен чечиңиз жана окуялардын ар кандай өнүгүшүндө бул скрипт так бир примитивдүү типтеги маанини жөнөтөөрүнө кепилдик бериңиз. . Эгер сиз классикалык ката деңгээлин алгыңыз келсе, бул маани сандык болушу керек.

Source: www.habr.com

Комментарий кошуу