Como faço para passar os parâmetros nomeados com o Invoke-Command?
-
26-09-2019 - |
Pergunta
Eu tenho um script que posso correr remotamente via invocar-command
Invoke-Command -ComputerName (Get-Content C:\Scripts\Servers.txt) `
-FilePath C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1
Enquanto eu usar parâmetros padrão, ele funciona bem. No entanto, o script possui 2 parâmetros nomeados [switch] (-debug e -clear)
Como posso passar os parâmetros comutados via Invoke-Command? Eu tentei o -AgumentList, mas estou recebendo erros, então devo ter a sintaxe errada ou algo assim. Qualquer ajuda é muito apreciada.
Solução
-ArgumentList
é baseado no uso com Scriptblock comandos, como:
Invoke-Command -Cn (gc Servers.txt) {param($Debug=$False, $Clear=$False) C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 } -ArgumentList $False,$True
Quando você o chama com um -File
Ele ainda passa os parâmetros como uma matriz splatted idiota. Eu enviei um solicitação de recurso Para ter isso adicionado ao comando (por favor, vote isso).
Então, você tem duas opções:
Se você tem um script que parecia assim, em um local de rede acessível da máquina remota (observe que -Debug
está implícito porque quando eu uso o Parameter
atributo, o script fica implicitamente em cmdletbinding e, portanto, todos os parâmetros comuns):
param(
[Parameter(Position=0)]
$one
,
[Parameter(Position=1)]
$two
,
[Parameter()]
[Switch]$Clear
)
"The test is for '$one' and '$two' ... and we $(if($DebugPreference -ne 'SilentlyContinue'){"will"}else{"won't"}) run in debug mode, and we $(if($Clear){"will"}else{"won't"}) clear the logs after."
Sem ficar pendurado no significado de $Clear
... se você quisesse invocar, poderia usar qualquer um dos seguintes Invoke-Command
Sintaxe:
icm -cn (gc Servers.txt) {
param($one,$two,$Debug=$False,$Clear=$False)
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 @PSBoundParameters
} -ArgumentList "uno", "dos", $false, $true
Nesse caso, estou duplicando todos os parâmetros que me preocupo no Scriptblock para que eu possa passar valores. Se eu posso codificá-los (que é o que eu realmente fiz), não há necessidade de fazer isso e usar PSBoundParameters
, Eu posso simplesmente passar os que eu preciso. No segundo exemplo abaixo, vou passar o $ CLEAR ONE, apenas para demonstrar como passar os parâmetros do interruptor:
icm -cn $Env:ComputerName {
param([bool]$Clear)
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $(Test-Path $Profile)
A outra opção
Se o script estiver na sua máquina local e você não quiser alterar os parâmetros para serem posicionais, ou você deseja especificar parâmetros que são parâmetros comuns (para que você não possa controlá -los), você desejará obter o conteúdo de aquele script e incorporá -lo em seu Scriptblock:
$script = [scriptblock]::create( @"
param(`$one,`$two,`$Debug=`$False,`$Clear=`$False)
&{ $(Get-Content C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -delimiter ([char]0)) } @PSBoundParameters
"@ )
Invoke-Command -Script $script -Args "uno", "dos", $false, $true
PostScript:
Se você realmente precisar passar em uma variável para o nome do script, o que você fará dependerá se a variável é definida local ou remotamente. Em geral, se você tiver uma variável $Script
ou uma variável de ambiente $Env:Script
Com o nome de um script, você pode executá -lo com o operador de chamada (&): &$Script
ou &$Env:Script
Se é uma variável de ambiente que já está definida no computador remoto, isso é tudo o que existe. Se for um local Variável, então você terá que passar para o bloco de script remoto:
Invoke-Command -cn $Env:ComputerName {
param([String]$Script, [bool]$Clear)
&$Script "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $ScriptPath, $(Test-Path $Profile)
Outras dicas
Minha solução para isso foi escrever o bloco de script dinamicamente com [scriptblock]:Create
:
# Or build a complex local script with MARKERS here, and do substitutions
# I was sending install scripts to the remote along with MSI packages
# ...for things like Backup and AV protection etc.
$p1 = "good stuff"; $p2 = "better stuff"; $p3 = "best stuff"; $etc = "!"
$script = [scriptblock]::Create("MyScriptOnRemoteServer.ps1 $p1 $p2 $etc")
#strings get interpolated/expanded while a direct scriptblock does not
# the $parms are now expanded in the script block itself
# ...so just call it:
$result = invoke-command $computer -script $script
Argumentos passantes foram muito frustrantes, tentando vários métodos, por exemplo,
-arguments
, $using:p1
, etc. e isso funcionou como desejado sem problemas.
Desde que eu controlo o conteúdo e a expansão variável da string que cria o [scriptblock]
(ou arquivo de script) Dessa forma, não há problema real com o encantamento "Invoke-Command".
(Não deveria ser tão difícil. :))
Suspeito que seja um novo recurso desde que este post foi criado - PASS parâmetros para o bloco de script usando $ usando: var. Então é um simples Mater para passar parâmetros, desde que o script já esteja na máquina ou em um local de rede conhecido em relação à máquina
Tomando o exemplo principal, seria:
icm -cn $Env:ComputerName {
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -one "uno" -two "dos" -Debug -Clear $Using:Clear
}
Eu precisava de algo para chamar scripts com parâmetros nomeados. Temos uma política de não usar o posicionamento ordinal de parâmetros e exigir o nome do parâmetro.
Minha abordagem é semelhante aos acima, mas obtém o conteúdo do arquivo de script que você deseja ligar e envia um bloco de parâmetros que contém os parâmetros e valores.
Uma das vantagens disso é que você pode opcionalmente escolher quais parâmetros enviar para o arquivo de script, permitindo parâmetros não obrigatórios com padrões.
Supondo que haja um script chamado "myscript.ps1" no caminho temporário que possui o seguinte bloco de parâmetros:
[CmdletBinding(PositionalBinding = $False)]
param
(
[Parameter(Mandatory = $True)] [String] $MyNamedParameter1,
[Parameter(Mandatory = $True)] [String] $MyNamedParameter2,
[Parameter(Mandatory = $False)] [String] $MyNamedParameter3 = "some default value"
)
É assim que eu chamaria esse script de outro script:
$params = @{
MyNamedParameter1 = $SomeValue
MyNamedParameter2 = $SomeOtherValue
}
If ($SomeCondition)
{
$params['MyNamedParameter3'] = $YetAnotherValue
}
$pathToScript = Join-Path -Path $env:Temp -ChildPath MyScript.ps1
$sb = [scriptblock]::create(".{$(Get-Content -Path $pathToScript -Raw)} $(&{
$args
} @params)")
Invoke-Command -ScriptBlock $sb
Eu usei isso em muitos cenários e funciona muito bem. Uma coisa que você ocasionalmente precisa fazer é colocar cotações em torno do bloco de atribuição de valor do parâmetro. Este é sempre o caso quando há espaços no valor.
Por exemplo, este bloco de parâmetros é usado para chamar um script que copia vários módulos no local padrão usado pelo PowerShell C:\Program Files\WindowsPowerShell\Modules
que contém um personagem espacial.
$params = @{
SourcePath = "$WorkingDirectory\Modules"
DestinationPath = "'$(Join-Path -Path $([System.Environment]::GetFolderPath('ProgramFiles')) -ChildPath 'WindowsPowershell\Modules')'"
}
Espero que isto ajude!