[英]Powershell Cmdlet with 'dynamic' ConfirmImpact attribute setting
我正在編寫一個支持ShouldProcess
的 Powershell cmdlet。 我想要一個取決於傳遞給 cmdlet 的參數值的“動態”值,而不是固定的ConfirmImpact
值。 讓我用一個例子來說明。
讓我們假設我是網絡托管服務提供商。 我有很多網站,每個網站都屬於以下類別之一,按重要性排序: Production
、 Test
和Development
。 作為我的托管管理的一部分,我有一個用於銷毀網站的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 上獲得更多安全網。
所以,我的補充SupportsShouldProcess
並ConfirmImpact
值給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
。 因此, ShouldProcess
和ShouldContinue
都可以被調用,並且仍然只向用戶顯示一個提示。 然后我可以使用我自己的邏輯來確定是否調用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.