Подтвердить что ты не робот

Как вы поддерживаете параметры PowerShell -WhatIf & -Confirm в командлете, который вызывает другие командлеты?

У меня есть командлет PowerShell script, который поддерживает параметры -WhatIf и -Confirm.

Он делает это, вызывая метод $PSCmdlet.ShouldProcess() перед выполнением изменения.
Это работает так, как ожидалось.

Проблема заключается в том, что мой командлет реализован путем вызова других командлетов, а параметры -WhatIf или -Confirm не передаются вместе с командлетами, которые я вызываю.

Как передать значения -WhatIf и -Confirm в командлеты, которые я вызываю из своего Cmdlet?

Например, если мой Cmdlet равен Stop-CompanyXyzServices, и он использует Stop-Service для реализации своего действия.

Если -WhatIf передано в Stop-CompanyXyzServices, я хочу, чтобы он также передавался в Stop-Service.

Возможно ли это?

4b9b3361

Ответ 1

После некоторого googling я придумал хорошее решение для передачи общих параметров по вызываемым командам. Вы можете использовать оператор @splatting для передачи всех параметров, которые были переданы вашей команде. Например, если

Start-Service -Name ServiceAbc @PSBoundParameters

находится в теле вашей script powershell передаст все параметры, которые были переданы вашему script команде Start-Service. Единственная проблема заключается в том, что если ваш script содержит параметр -Name, он также будет передан, а PowerShell будет жаловаться на то, что вы дважды включили параметр -Name. Я написал следующую функцию, чтобы скопировать все общие параметры в новый словарь, а затем я разделил это.

function Select-BoundCommonParameters
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        $BoundParameters
    )
    begin
    {
        $boundCommonParameters = New-Object -TypeName 'System.Collections.Generic.Dictionary[string, [Object]]'
    }
    process
    {
        $BoundParameters.GetEnumerator() |
            Where-Object { $_.Key -match 'Debug|ErrorAction|ErrorVariable|WarningAction|WarningVariable|Verbose' } |
            ForEach-Object { $boundCommonParameters.Add($_.Key, $_.Value) }

        $boundCommonParameters
    }
}

Конечный результат: вы передаете параметры типа -Verbose вместе с командами, названными в вашем script, и они выполняют намерение вызывающих абонентов.

Ответ 2

Передача параметров явно

Вы можете передать параметры -WhatIf и -Confirm с помощью переменных $WhatIfPreference и $ConfirmPreference. В следующем примере это достигается с помощью параметрирования splatting:

if($ConfirmPreference -eq 'Low') {$conf = @{Confirm = $true}}

StopService MyService -WhatIf:([bool]$WhatIfPreference.IsPresent) @conf

$WhatIfPreference.IsPresent будет True, если переключатель -WhatIf используется в содержащей функции. Использование переключателя -Confirm на содержащей функции временно устанавливает $ConfirmPreference в low.

Передача параметров неявно

Так как -Confirm и -WhatIf временно устанавливают переменные $ConfirmPreference и $WhatIfPreference автоматически, нужно ли их передавать?

Рассмотрим пример:

function ShouldTestCallee {
    [cmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Medium')] 
    param($test)

    $PSCmdlet.ShouldProcess($env:COMPUTERNAME,"Confirm?")
}


function ShouldTestCaller {
    [cmdletBinding(SupportsShouldProcess=$true)]
    param($test)

    ShouldTestCallee
}

$ConfirmPreference = 'High'
ShouldTestCaller
ShouldTestCaller -Confirm

ShouldTestCaller выводит True из ShouldProcess()

ShouldTestCaller -Confirm выводит запрос подтверждения, даже если я не прошел этот переключатель.

Edit

Ответ

@manojlds заставил меня понять, что мое решение всегда устанавливало $ConfirmPreference на "Низкий" или "Высокий". Я обновил свой код, чтобы установить только переключатель -Confirm, если для параметра подтверждения выбрано значение "Низкий".

Ответ 3

Вот полное решение на основе ответов @Rynant и @Shay Levy:

function Stop-CompanyXyzServices
{
    [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Medium')]

    Param(
        [Parameter(
            Position=0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true
        )]      
        [string]$Name
    )

    process
    {
        if($PSCmdlet.ShouldProcess($env:COMPUTERNAME,"Stop XYZ services '$Name'")){  
            ActualCmdletProcess
        }
        if([bool]$WhatIfPreference.IsPresent){
            ActualCmdletProcess
        }
    }
}

function ActualCmdletProcess{
# add here the actual logic of your cmdlet, and any call to other cmdlets
Stop-Service $name -WhatIf:([bool]$WhatIfPreference.IsPresent) -Confirm:("Low","Medium" -contains $ConfirmPreference)
}

Мы должны видеть, если -WhatIf передается отдельно, так что whatif может быть передан отдельным командлетам. ActualCmdletProcess - это в основном рефакторинг, поэтому вы не вызываете тот же набор команд снова только для WhatIf. Надеюсь, это поможет кому-то.

Ответ 4

Обновлено за комментарий @manojlds

Ввести $WhatIf и $Подтвердить значение Boolean и передать значения командному командлу:

function Stop-CompanyXyzServices
{
    [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='High')]

    Param(
        [Parameter(
            Position=0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true
        )]      
        [string]$Name
    )


    process
    {
        if($PSCmdlet.ShouldProcess($env:COMPUTERNAME,"Stop service '$Name'"))
        {                   
            Stop-Service $name -WhatIf:([bool]$WhatIf) -Confirm:([bool]$confirm)
        }                       
    }
}