带有连字符参数的Powershell Invoke-Command

时间:2015-02-17 23:29:07

标签: powershell msdeploy invoke-command

我需要能够在远程计算机上运行以下cmd命令。

"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" -verb:sync -source:package="\\Server\Share\Package.zip" -dest:auto,computerName=Server1

Powershells Invoke-Command看起来很有希望,但我无法弄清楚如何将参数传递给msdeploy。

我试过

Invoke-Command -ComputerName RemoteServer1 -ScriptBlock { "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" -verb:sync -source:package="\\Server\Share\Package.zip" -dest:auto,computerName=Server1 }

但它说“你必须在' - '运算符的右侧提供一个值表达式。” 所以我猜这个问题是连字符,但我不知道如何逃避它们以使命令工作。

3 个答案:

答案 0 :(得分:3)

第一部分:处理PowerShell命令行细节

使用&调用外部可执行文件。语法:& "[path] command" [arguments]

另请注意,从PowerShell调用时,msdeploy支持alternate way of specifying arguments

  

通过对其通常语法的微小修改,Web Deploy命令可以   从Windows PowerShell提示符运行。为此,请更改冒号   Web的动词,源和dest参数之后的字符(:)   将命令部署到等号(=)。在以下示例中,比较   带有PowerShell版本的Web Deploy命令。

     

Web Deploy命令:

     

command: msdeploy -verb:sync -source:metakey=/lm/w3svc/1 -dest:metakey=/lm/w3svc/2 -verbose

     

PowerShell命令:

     

.\msdeploy.exe -verb=sync -source=metakey=/lm/w3svc/1 -dest=metakey=/lm/w3svc/2 -verbose

请注意,参数包含在数组中:这提示PowerShell如何将它们正确地传递给目标应用程序。

示例:

Invoke-Command -ComputerName RemoteServer1 -ScriptBlock {&"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" @('-verb=sync', '-source=package="\\Server\Share\Package.zip"', '-dest=auto,computerName=Server1')}

第二部分:处理multi-hop authentication

从您的评论我看到,现在PowerShell在远程服务器上成功运行msdeploy,但msdeploy无法访问远程共享:

  

当命令执行时,现在msdeploy它说:

     

“更多信息:无法创建类型'包'和路径'\ Server \ Share \ Package.zip'的对象。请访问:http://go.microsoft.com/fwlink/?LinkId=221672#ERROR_EXCEPTION_WHILE_CREATING_OBJECT了解详情。

     

错误:无法加载Zip包'\ Server \ Share \ Package.zip'。错误:拒绝访问路径'\ Server \ Share \ Package.zip'。错误计数:1。“

     

即使用户有权访问共享(读/写)。

那是因为您实际上尝试建立从计算机A(客户端)到计算机B(服务器)然后从计算机B建立远程会话,您尝试在计算机C(\\Server\Share\Package.zip)上的共享中访问该文件

CredSSP example

Invoke-Command失败,因为远程会话尝试使用计算机凭据而不是用于调用远程会话的凭据来访问文件共享。 There is a way to pass or delegate credentials from the client以便我们可以对文件共享进行身份验证。这就是所谓的多跳认证,PowerShell远程处理可以使用CredSSP实现这一点。

要启用CredSSP,请从提升的提示符运行这些命令:

  • 在您的电脑上:Enable-WSManCredSSP -Role Client -DelegateComputer "TargetServer.FQ.DN"

    DelegateComputer参数用于指定从客户端接收委派凭据的服务器。 DelegateComputer接受通配符(*.FQ.DN)。您还可以指定*来指定网络中的所有计算机。

  • 在目标服务器上:Enable-WSManCredSSP -Role Server

现在,您应该可以使用CredSSP作为身份验证方法运行Invoke-Command并传递凭据:

Invoke-Command -ComputerName RemoteServer1 -Authentication Credssp -Credential Domain\Username -ScriptBlock {&"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" @('-verb=sync', '-source=package="\\Server\Share\Package.zip"', '-dest=auto,computerName=Server1')}

第三部分:使用Invoke-Command

传递参数
  

我如何进行服务器参数化。如果我有一个参数$Server作为服务器名称,我该如何将其作为-dest=auto,computerName=Server1部分的替代品?

要将参数传递给scriptblock,请使用ArgumentList参数:

$Servers = @('Server1', 'Server2', 'Server3')
$Command = {
    Param($Srv)
    &"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" @('-verb=sync', '-source=package="\\Server\Share\Package.zip"', "-dest=auto,computerName=$Srv")
}

$Servers |
    ForEach-Object {
        Invoke-Command -ComputerName RemoteServer1 -Authentication Credssp -Credential Domain\Username -ScriptBlock $Command -ArgumentList $_
    }

进一步:

  

我会如何动态地向命令中添加额外的参数。对于字符串数组中的每个文件夹,我需要将-skip:objectName=dirPath,absolutePath="<folder>"添加到&"C:\Program Files\..."行的参数中。

如果要为所有服务器排除一组文件夹:

$Servers = @('Server1', 'Server2', 'Server3')
$SkipPaths = @('C:\folder\to\skip1', 'C:\folder\to\skip2', 'C:\folder\to\skip3')

$SkipCmd = $SkipPaths | ForEach-Object {"-skip=objectName=dirPath,absolutePath=$_"}

$Command = {
    Param($Srv, $Skp)
    &"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" $(@('-verb=sync', '-source=package="\\Server\Share\Package.zip"', "-dest=auto,computerName=$Srv") + $Skp)
}

$Servers |
    ForEach-Object {
        Invoke-Command -ComputerName RemoteServer1 -Authentication Credssp -Credential Domain\Username -ScriptBlock $Command -ArgumentList ($_, $SkipCmd)
    }

如果每个服务器都有不同的文件夹集:

$Servers = @{
    Server1 = @('C:\folder\to\skip_1', 'C:\folder\to\skip_2', 'C:\folder\to\skip_3')
    Server2 = @('C:\folder\to\skip_A', 'C:\folder\to\skip_B', 'C:\folder\to\skip_C')
    Server3 = @('C:\folder\to\skip_X', 'C:\folder\to\skip_Y', 'C:\folder\to\skip_Z')
}

$Command = {
    Param($Srv, $Skp)
    &"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" $(@('-verb=sync', '-source=package="\\Server\Share\Package.zip"', "-dest=auto,computerName=$Srv") + $Skp)
}

$Servers.GetEnumerator() |
    ForEach-Object {
        $SkipCmd = $_.Value | ForEach-Object {"-skip=objectName=dirPath,absolutePath=$_"}
        Invoke-Command -ComputerName RemoteServer1 -Authentication Credssp -Credential Domain\Username -ScriptBlock $Command -ArgumentList ($_.Key, $SkipCmd)
    }

答案 1 :(得分:1)

您可以使用Powershell stop parsing operator--%):

Invoke-Command -ComputerName RemoteServer1 -ScriptBlock { 
    "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" --% -verb:sync -source:package="\\Server\Share\Package.zip" -dest:auto,computerName=Server1 
}

但是,您需要在其中添加新行,以停止解析并关闭脚本块。另一个缺点是,显然,您不能在操作符之后放置变量,并期望它们扩展到它们的值。

答案 2 :(得分:0)

注意:我错过了在远程计算机上执行此操作所需的部分。在这种情况下,下面的答案并没有帮助,但它仍然适用于其他人,所以我会离开它。

在PowerShell中调用.exe有许多不同的方法,每个方法都有自己的缺点。从我尝试的所有方法中,以下方法最适合调用msdeploy.exe。

您可以在https://gist.github.com/sayedihashimi/1390cd6c97f25eefabdc找到我的要点中的最新资料。我已将其粘贴在下方。

function Execute-CommandString{
    [cmdletbinding()]
    param(
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]
        [string[]]$command,

        [switch]
        $ignoreExitCode
    )
    process{
        foreach($cmdToExec in $command){
            'Executing command [{0}]' -f $cmdToExec | Write-Verbose
            cmd.exe /D /C $cmdToExec

            if(-not $ignoreExitCode -and ($LASTEXITCODE -ne 0)){
                $msg = ('The command [{0}] exited with code [{1}]' -f $cmdToExec, $LASTEXITCODE)
                throw $msg
            }
        }
    }
}

# in this case there is no space in any argument and the call to msdeploy.exe succeeds
$msdeployExe = 'C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe'
$packOutput = 'C:\temp\publish\01'
$pubOut = 'c:\temp\publish-no-space\'

$publishArgs = @()
$publishArgs += ('-source:contentPath=''{0}''' -f "$packOutput")
$publishArgs += ('-dest:contentPath=''{0}''' -f "$pubOut")
$publishArgs += '-verb:sync'
$publishArgs += '-disablerule:BackupRule'

'Calling msdeploy to publish to file system with the command: [{0} {1}]' -f $msdeployExe,($publishArgs -join ' ') | Write-Output
$command = '"{0}" {1}' -f $msdeployExe,($publishArgs -join ' ')
Execute-CommandString -command $command

# in this case the call to msdeploy.exe fails with an argument error 
$pubOut2 = 'c:\temp\publish with space\'
$publishArgs2 = @()
$publishArgs2 += ('-source:contentPath=''{0}''' -f "$packOutput")
$publishArgs2 += ('-dest:contentPath=''{0}''' -f "$pubOut2")
$publishArgs2 += '-verb:sync'
$publishArgs2 += '-disablerule:BackupRule'

'Calling msdeploy to publish to file system with the command: [{0} {1}]' -f $msdeployExe,($publishArgs -join ' ') | Write-Output

$command = '"{0}" {1}' -f $msdeployExe,($publishArgs2 -join ' ')
Execute-CommandString -command $command

这里我引用带有"的msdeploy.exe的路径,并使用带有参数的'来转义任何路径。

我拥有Visual Studio Web发布体验,而对于ASP.NET 5,我们将从PowerShell调用msdeploy.exe。这是我们目前正在使用的技术。