簡體   English   中英

具有“動態”ConfirmImpact 屬性設置的 Powershell Cmdlet

[英]Powershell Cmdlet with 'dynamic' ConfirmImpact attribute setting

我正在編寫一個支持ShouldProcess的 Powershell cmdlet。 我想要一個取決於傳遞給 cmdlet 的參數值的“動態”值,而不是固定的ConfirmImpact值。 讓我用一個例子來說明。

讓我們假設我是網絡托管服務提供商。 我有很多網站,每個網站都屬於以下類別之一,按重要性排序: ProductionTestDevelopment 作為我的托管管理的一部分,我有一個用於銷毀網站的Remove-WebSite cmdlet。 以下代碼說明了這一點:

Class WebSite {
    [string] $Name
    [string] $Category # Can be one of: Production, Test, Development
}

Function Remove-WebSite {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [WebSite] $WebSite
    )
    Write-Host "$($WebSite.Name) was destroyed"
}

目前網站在未經確認的情況下被銷毀。 雖然這很方便,但有太多實習生錯誤地破壞了生產站點,因此我希望通過利用 Powershell 的ShouldProcess功能在Remove-WebSite cmdlet 上獲得更多安全網。

所以,我的補充SupportsShouldProcessConfirmImpact值給CmdletBinding屬性。 我的 cmdlet 定義變為:

Function Remove-WebSite {
    [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='High')]
    Param(
        [Parameter(Mandatory=$true)]
        [WebSite] $WebSite
    )

    if ($PSCmdlet.ShouldProcess("$($WebSite.Category) site $($WebSite.Name)")) {
        Write-Host "$($WebSite.Name) was destroyed"
    }
}

有了這個定義,任何調用Remote-Website cmdlet 的人現在都會被要求確認他們是否真的想要銷毀該站點。 現在幾乎沒有任何生產站點被錯誤地破壞,除了 Web 開發人員抱怨他們的自動化腳本停止工作。

我真的很喜歡的是, ConfirmImpact該cmdlet值在運行時改變取決於網站的類別的重要性- High的生產基地, Medium的測試現場和Low的發展用地。 以下函數定義說明了這一點:

Function CategoryToImpact([string]$Category) {
    Switch ($Category) {
        'Production' {
            [System.Management.Automation.ConfirmImpact]::High
            break
        }
        'Test' {
            [System.Management.Automation.ConfirmImpact]::Medium
            break
        }
        'Development' {
            [System.Management.Automation.ConfirmImpact]::Low
            break
        }
        default {
            [System.Management.Automation.ConfirmImpact]::None
            break
        }
    }
}

Function Remove-WebSite {
    [CmdletBinding(SupportsShouldProcess=$true<#,ConfirmImpact="Depends!"#>)]
    Param(
        [Parameter(Mandatory=$true)]
        [WebSite] $WebSite
    )

    # This doesn't work but I hope it illustrates what I'd *like* to do
    #$PSCmdLet.ConfirmImpact = CategoryToImpact($WebSite.Category)

    if ($PSCmdlet.ShouldProcess("$($WebSite.Category) site $($WebSite.Name)")) {
        Write-Host "$($WebSite.Name) was destroyed"
    }
}

假設有可能,如何做到這一點?

這是一個完整的腳本加上一些測試代碼的粘貼: http : //pastebin.com/kuk6HNm6

這並不完全是您所要求的(我認為嚴格來說這是不可能的),但這可能是一種更好的方法。

離開ConfirmImpact孤獨,而是及時與用戶$PSCmdlet.ShouldContinue()

根據從 Cmdlets 請求確認中給出的指導(重點是我的):

對於大多數 cmdlet,您不必明確指定 ConfirmImpact。 而是使用參數的默認設置,即中等。 如果將 ConfirmImpact 設置為 High,則默認情況下會確認操作。 將此設置保留用於高度破壞性的操作,例如重新格式化硬盤卷。

更遠:

大多數 cmdlet 僅使用 ShouldProcess 方法請求確認。 但是,某些情況可能需要額外確認。 對於這些情況,請使用對 ShouldContinue 方法的調用來補充 ShouldProcess 調用。

...

如果 cmdlet 調用 ShouldContinue 方法,則該 cmdlet 還必須提供 Force 開關參數。 如果用戶在調用 cmdlet 時指定 Force,則 cmdlet 仍應調用 ShouldProcess,但應繞過對 ShouldContinue 的調用。

鑒於此指導,我建議進行以下更改:

Function Remove-WebSite {
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
        [Parameter(Mandatory=$true)]
        [WebSite] $WebSite ,
        [Switch] $Force
    )

    if ($PSCmdlet.ShouldProcess("$($WebSite.Category) site $($WebSite.Name)")) {
        $destroy =
            $Force -or
            $WebSite.Category -ne 'Production' -or
            $PSCmdlet.ShouldContinue("Are you sure you want to destroy $($WebSite.Name)?", "Really destroy this?")
        if ($destroy) {
            Write-Host "$($WebSite.Name) was destroyed"
        }
    }
}

最簡單的解決方案是刪除$PSCmdlet.ShouldProcess方法調用,並根據我們自己的標准有條件地調用$PSCmdlet.ShouldContinue方法。 這樣做的問題是我們失去了-WhatIf功能。 作為briantist指出, $PSCmdlet.ShouldContinue應沿着側使用$PSCmdlet.ShouldProcess ,除了這可能會導致多余的確認提示即,用戶在提示時,兩次一次就足夠了。

通過實驗,我發現通過在CmdletBinding屬性聲明中設置CmdletBinding ConfirmImpact='None'ShouldProcess不再顯示提示,但如果指定了-WhatIf ,仍然返回$false 因此, ShouldProcessShouldContinue都可以被調用,並且仍然只向用戶顯示一個提示。 然后我可以使用我自己的邏輯來確定是否調用ShouldContinue

這是一個完整的解決方案:

# Represents a website
Class WebSite {
    # The name of the web site
    [string] $Name

    # The category of the website, which can be one of: Production, Test, Development
    [string] $Category # Can be one of

    <#
        Gets the ConfirmImpact level based on Category, as follows:

            Category     ConfirmImpact
            -----------  -------------
            Production   High
            Test         Medium
            Development  Low
            Default      None
    #>
    [System.Management.Automation.ConfirmImpact] GetImpact() {
        Switch ($this.Category) {
            'Production' {
                return [System.Management.Automation.ConfirmImpact]::High
            }
            'Test' {
                return [System.Management.Automation.ConfirmImpact]::Medium
            }
            'Development' {
                return [System.Management.Automation.ConfirmImpact]::Low
            }
        }
        return [System.Management.Automation.ConfirmImpact]::None
    }

    # String representation of WebSite
    [string] ToString() {
        return "$($this.Category) site $($this.Name)"
    }
}

<#
.SYNOPSIS
Destroys a WebSite

.DESCRIPTION
The Remove-WebSite cmdlet permanently destroys a website so use with care.
To avoid accidental deletion, the caller will be prompted to confirm the
invocation of the command if the value of $ConfirmPreference is less than
or equal to the Impact level of the WebSite. The Impact level is based
upon the category, as follows:

    Category     ConfirmImpact
    -----------  -------------
    Production   High
    Test         Medium
    Development  Low
    Default      None

.PARAMETER Website
The WebSite to destroy.

.PARAMETER Force
Destroys website without prompt

.PARAMETER Confirm
Require confirmation prompt always regardless of $ConfirmPreference

.PARAMETER WhatIf
Show what would happen if the cmdlet was run. The cmdlet is not run.

#>
Function Remove-WebSite {
    # Set ConfirmImpact to 'None' so that ShouldProcess automatically returns
    # true without asking for confirmation, regardless of the value of
    # $ConfirmPreference. 
    [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='None')]
    Param(
        [Parameter(Mandatory=$true)]
        [WebSite] $WebSite,
        [Switch] $Force
    )

    # Returns true without prompt unless -WhatIf is specified when, in which case
    # false is returned without prompt
    if ($PSCmdlet.ShouldProcess($WebSite)) {

        # Determine whether to continue with the command. Only destroy website if...
        $continue = 
            # ...forced to by Force parameter...
            $Force -or

            #...or the Impact level of the Website is less than $ConfirmPreference...
            $WebSite.GetImpact() -lt $ConfirmPreference -or

            #...or the user clicked 'Yes' in ShouldContinue prompt
            $PSCmdlet.ShouldContinue("Are you sure you want to destroy $($WebSite)?", $null)

        if ($continue) {
            Write-Host "$($WebSite.Name) was destroyed"
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM