Powershell 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 }

Но он говорит: «Вы должны указать выражение значения справа от оператора «-». Итак, я предполагаю, что проблема в дефисах, но я не знаю, как их избежать, чтобы команда работала.


person Kyro    schedule 17.02.2015    source источник
comment
Какую версию PowerShell вы используете?   -  person campbell.rw    schedule 19.02.2015
comment
Причина, по которой я спрашиваю, заключается в том, что в PowerShell 4.0 я могу без проблем запустить следующий блок сценария как локально, так и с помощью команды Invoke, и в нем есть дефис. $sb = { svn log C:\src -l5 }; &$sb   -  person campbell.rw    schedule 19.02.2015


Ответы (3)


Часть первая: особенности командной строки PowerShell

Используйте & для вызова внешнего исполняемого файла. Синтаксис: & "[path] command" [arguments].

Также обратите внимание, что msdeploy поддерживает альтернативный способ указания аргументов при вызове из PowerShell:

С небольшим изменением обычного синтаксиса команды веб-развертывания можно запускать из командной строки Windows PowerShell. Для этого измените символ двоеточия (:) после глагола, исходного и целевого аргументов команды Web Deploy на знак равенства (=). В следующем примере сравните команду Web Deploy с ее версией PowerShell.

Команда веб-развертывания:

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')}

Часть вторая: работа с многоэтапная аутентификация

Из ваших комментариев я вижу, что теперь 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

Invoke-Command происходит сбой, поскольку удаленный сеанс пытается получить доступ к общему файловому ресурсу, используя учетные данные компьютера вместо учетных данных, используемых для вызова удаленного сеанса. Существует способ передать или делегировать учетные данные от клиента, чтобы мы могли аутентифицироваться в общей папке. Это то, что называется аутентификацией с несколькими переходами, и удаленное взаимодействие PowerShell позволяет использовать CredSSP.

Чтобы включить CredSSP, запустите эти команды из командной строки с повышенными привилегиями:

  • На вашем ПК: Enable-WSManCredSSP -Role Client -DelegateComputer "TargetServer.FQ.DN"

    Параметр DelegateComputer используется для указания сервера или серверов, которые получают делегированные учетные данные от клиента. DelegateComputer принимает подстановочные знаки (*.FQ.DN). Вы также можете указать *, чтобы указать все компьютеры в сети.

  • На целевом сервере: Enable-WSManCredSSP -Role Server

Теперь вы сможете запустить Invoke-Command с CredSSP в качестве метода аутентификации и передать учетные данные:

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?

Чтобы передать аргументы в скриптовый блок, используйте параметр 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)
    }
person beatcracker    schedule 17.02.2015
comment
Похоже, это не работает. При выполнении в powershell он просто показывает ›› в новой строке. И когда я проверяю место, где должен был быть заполнен msdeploy, оно пусто. - person Kyro; 18.02.2015
comment
Я обновил свой ответ, не могли бы вы попробовать еще раз? На этот раз аргументы для msdeploy завернуты в массив — это еще один способ PS передать хитрые командные строки. Я проверил это на своих серверах сейчас, и это работает. - person beatcracker; 18.02.2015
comment
Пока команда выполняется, теперь msdeploy говорит: Дополнительная информация: объект типа «пакет» и путь «\\Server\Share\Package.zip» не может быть создан. Дополнительные сведения см. на странице go.microsoft.com/fwlink/. Ошибка: Zip-пакет «\\Server\Share\Package.zip» не может быть загружен. Ошибка: доступ к пути «\\Server\Share\Package.zip» запрещен. Количество ошибок: 1. Несмотря на то, что у пользователя есть доступ к общему ресурсу (чтение/запись). - person Kyro; 18.02.2015
comment
Похоже на проблему аутентификации с несколькими переходами, я обновил свой ответ, посмотрим, поможет ли это. - person beatcracker; 19.02.2015
comment
теперь он отлично работает. Еще раз последнее, как мне настроить сервер. если у меня есть параметр $server в качестве имени сервера, как мне поставить его вместо части «-dest=auto,computerName=Server1»? - person Kyro; 19.02.2015
comment
Учитывая, что вы так много помогали раньше, как мне динамически добавлять дополнительные параметры в команду. Мне нужно добавить -skip:objectName=dirPath,absolutePath=‹folder› к аргументам в строке &C:\Program Files\... для каждой папки в массиве строк. - person Kyro; 20.02.2015

Вы можете использовать оператор 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 
}

Однако вам понадобятся новые строки, чтобы остановить синтаксический анализ остановки и закрыть блок сценария. Другим недостатком является то, что, очевидно, вы не можете поместить переменные после оператора и ожидать, что они расширятся до своего значения.

person Bacon Bits    schedule 18.02.2015
comment
Это не работает. Powershell просто выдает следующую ошибку. Вы должны указать выражение значения в правой части оператора «%». - person Kyro; 18.02.2015

Примечание. Я пропустил ту часть, где это требовалось для выполнения на удаленном компьютере. Ответ ниже в этом случае не поможет, но он по-прежнему актуален для других людей, поэтому я его оставлю.

Существует множество различных способов вызова .exe в PowerShell, и каждый из них имеет свои недостатки. Из всех методов, которые я пробовал, для вызова 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, и для ASP.NET 5 мы будем вызывать msdeploy.exe из PowerShell. Это техника, которую мы сейчас используем для этого.

person Sayed Ibrahim Hashimi    schedule 18.02.2015
comment
Это не похоже на выполнение msdeploy на удаленном компьютере. Причина, по которой я пытаюсь выполнить команду на удаленном компьютере, заключается в том, что между мной и удаленным компьютером есть глобальная сеть, из-за чего msdeploy работает очень медленно. - person Kyro; 18.02.2015
comment
Ой, извините, я пропустил эту часть. Я обновил ответ, чтобы указать, что в этом случае он не помогает. Прости за это. - person Sayed Ibrahim Hashimi; 19.02.2015