將認證支援新增至 PowerShell 函式
備註
本文 原始版本 出現在 @joshduffney撰寫的部落格上。 這篇文章已經過編輯,以便在此網站上發布。 PowerShell 小組感謝 Josh 與我們共用此內容。 請在 duffney.io查看他的部落格。
本文說明如何將認證參數新增至 PowerShell 函式,以及您想要的原因。 認證參數是可讓您以不同的使用者身分執行函式或 Cmdlet。 最常見的用法是將函式或 Cmdlet 以提升許可權的用戶帳戶執行。
例如,cmdlet New-ADUser
具有 Credential 參數,您可以提供網域管理員認證,以在網域中建立帳戶。 假設執行 PowerShell 會話的一般帳戶還沒有該存取權。
建立認證物件
PSCredential 物件代表一組安全性認證,例如使用者名稱和密碼。 可以將物件作為參數傳遞給在該憑證物件中的使用者帳戶下執行的函式。 有幾種方式可以建立認證物件。 建立認證物件的第一種方式是使用 PowerShell Cmdlet Get-Credential
。 當您在沒有參數的情況下執行時,它會提示您輸入使用者名稱和密碼。 或者,您可以使用一些選擇性參數來呼叫 Cmdlet。
若要事先指定網域名稱和用戶名稱,您可以使用 Credential 或 UserName 參數。 當您使用 UserName 參數時,您也必須提供 Message 值。 下列程式代碼示範如何使用 Cmdlet。 您也可以將認證物件儲存在變數中,以便多次使用認證。 在下列範例中,認證物件會儲存在變數 $Cred
中。
$Cred = Get-Credential
$Cred = Get-Credential -Credential domain\user
$Cred = Get-Credential -UserName domain\user -Message 'Enter Password'
有時候,您無法使用互動式方法來建立上一個範例所示的認證物件。 大部分的自動化工具都需要非互動式方法。 若要在沒有使用者互動的情況下建立認證,請建立包含密碼的安全字串。 然後將安全字串和使用者名稱傳遞至 System.Management.Automation.PSCredential()
方法。
使用下列命令來建立包含密碼的安全字串:
ConvertTo-SecureString "MyPlainTextPassword" -AsPlainText -Force
AsPlainText 和 Force 參數都是必要的。 如果沒有這些參數,您會收到訊息警告,指出您不應該將純文本傳遞至安全字串。 PowerShell 會傳回此警告,因為純文本密碼會記錄在各種記錄中。 建立安全字串之後,您必須將它傳遞至 PSCredential()
方法來建立認證物件。 在下列範例中,變數 $password
包含安全字串,$Cred
包含認證物件。
$password = ConvertTo-SecureString "MyPlainTextPassword" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential ("username", $password)
既然您已瞭解如何建立認證物件,您可以將認證參數新增至 PowerShell 函式。
新增認證參數
就像任何其他參數一樣,您一開始是在函式的 param
區塊中新增它。
建議您將參數命名為 $Credential
,因為這是現有PowerShell Cmdlet所使用的名稱。 參數的類型應 [System.Management.Automation.PSCredential]
。
下列範例顯示名為 Get-Something
之函式的參數區塊。 它有兩個參數:$Name
和 $Credential
。
function Get-Something {
param(
$Name,
[System.Management.Automation.PSCredential]$Credential
)
此範例中的程式代碼足以擁有可運作的認證參數,不過您可以新增一些內容,使其更健全。
新增
[ValidateNotNull()]
驗證屬性,檢查傳遞至認證 的值。 如果參數值為 null,這個屬性會防止函式以無效的認證執行。新增
[System.Management.Automation.Credential()]
。 這可讓您以字串的形式傳入用戶名稱,並具有密碼的互動式提示。將
$Credential
參數的預設值設定為[System.Management.Automation.PSCredential]::Empty
。 您的函式可能會將此$Credential
對象傳遞至現有的PowerShell Cmdlet。 將 Null 值提供給函式內呼叫的 Cmdlet 會導致錯誤。 提供空的認證物件可避免此錯誤。
小提示
某些接受認證參數的 cmdlets 並不如預期般支援 [System.Management.Automation.PSCredential]::Empty
。 如需因應措施,請參閱 處理舊版 Cmdlet 一節。
使用認證參數
下列範例示範如何使用認證參數。 這個範例顯示了一個名為 Set-RemoteRegistryValue
的函式,出自 《Pester Book》。 此函式會使用上一節所述的技術來定義認證參數。 函式會使用 函式所建立的 $Credential
變數呼叫 Invoke-Command
。 這可讓您變更執行 Invoke-Command
的使用者。 因為 $Credential
的預設值是空的認證,因此函式可以在不提供認證的情況下執行。
function Set-RemoteRegistryValue {
param(
$ComputerName,
$Path,
$Name,
$Value,
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
$null = Invoke-Command -ComputerName $ComputerName -ScriptBlock {
Set-ItemProperty -Path $Using:Path -Name $Using:Name -Value $Using:Value
} -Credential $Credential
}
下列各節顯示提供認證給 Set-RemoteRegistryValue
的不同方法。
提示輸入認證
執行時在括弧 ()
中使用 Get-Credential
會導致先執行 Get-Credential
。 系統會提示您輸入使用者名稱和密碼。 您可以使用 Credential 或 UserName 參數 Get-Credential
來預先填入使用者名稱和網域。 下列範例使用一種稱為「splatting」的技術,將參數傳遞至 Set-RemoteRegistryValue
函數。 如需有關展開的詳細資訊,請參閱 about_Splatting 文章。
$remoteKeyParams = @{
ComputerName = $Env:COMPUTERNAME
Path = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server'
Name = 'EnableRemoteManagement'
Value = '1'
}
Set-RemoteRegistryValue @remoteKeyParams -Credential (Get-Credential)
獲取憑證
使用 (Get-Credential)
似乎很麻煩。 一般而言,當您只搭配用戶名稱使用 Credential 參數時,Cmdlet 會自動提示輸入密碼。
[System.Management.Automation.Credential()]
屬性會啟用此行為。
$remoteKeyParams = @{
ComputerName = $Env:COMPUTERNAME
Path = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server'
Name = 'EnableRemoteManagement'
Value = '1'
}
Set-RemoteRegistryValue @remoteKeyParams -Credential duffney
備註
若要設定顯示的登錄值,這些範例假設您已安裝 windows Web Server 功能。 視需要執行 Install-WindowsFeature Web-Server
和 Install-WindowsFeature web-mgmt-tools
。
在變數中提供認證
您也可以事先填入認證變數,並將它傳遞給 Set-RemoteRegistryValue
函式的 Credential 參數。 使用此方法搭配持續整合/持續部署 (CI/CD) 工具,例如 Jenkins、TeamCity 和 Octopus Deploy。 如需使用 Jenkins 的範例,請參閱 Hodge 的部落格文章,在 Windows 上使用 Jenkins 和 PowerShell 進行自動化 - 第 2 部分。
這個範例會使用 .NET 方法來建立認證物件和安全字串,以傳入密碼。
$password = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential ("duffney", $password)
$remoteKeyParams = @{
ComputerName = $Env:COMPUTERNAME
Path = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server'
Name = 'EnableRemoteManagement'
Value = '1'
}
Set-RemoteRegistryValue @remoteKeyParams -Credential $Cred
在此範例中,會使用純文本密碼建立安全字串。 所有先前提及的 CI/CD 都有一種安全的方法,可以在執行時提供密碼。 使用這些工具時,請將純文本密碼取代為您所使用的 CI/CD 工具內定義的變數。
不使用認證執行
由於 $Credential
預設為空的認證物件,因此您可以執行不含認證的命令,如下列範例所示:
$remoteKeyParams = @{
ComputerName = $Env:COMPUTERNAME
Path = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server'
Name = 'EnableRemoteManagement'
Value = '1'
}
Set-RemoteRegistryValue @remoteKeyParams
處理舊版 Cmdlet 指令
並非所有 Cmdlet 都支援認證物件,或允許空的認證。 相反地,Cmdlet 想要將使用者名稱和密碼參數當做字串。 有幾種方法可以解決這項限制。
使用 if-else 來處理空的認證
在此案例中,您想要執行的 Cmdlet 不接受空的認證物件。 本範例只有在認證不是空白時,才會將 Credential 參數新增至 Invoke-Command
。 否則,它會執行 Invoke-Command
,而不使用 Credential 參數。
function Set-RemoteRegistryValue {
param(
$ComputerName,
$Path,
$Name,
$Value,
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
if($Credential -ne [System.Management.Automation.PSCredential]::Empty) {
Invoke-Command -ComputerName:$ComputerName -Credential:$Credential {
Set-ItemProperty -Path $Using:Path -Name $Using:Name -Value $Using:Value
}
} else {
Invoke-Command -ComputerName:$ComputerName {
Set-ItemProperty -Path $Using:Path -Name $Using:Name -Value $Using:Value
}
}
}
使用 Splatting 來處理空的認證
此範例使用參數展開來呼叫舊版 Cmdlet。
$Credential
物件會有條件地新增至哈希表以進行展開,並避免需要重複 Invoke-Command
腳本區塊。 若要深入瞭解函式內部的展開運算,請參閱有關進階函式中 展開參數 的部落格文章。
function Set-RemoteRegistryValue {
param(
$ComputerName,
$Path,
$Name,
$Value,
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
$Splat = @{
ComputerName = $ComputerName
}
if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) {
$Splat['Credential'] = $Credential
}
$null = Invoke-Command -ScriptBlock {
Set-ItemProperty -Path $Using:Path -Name $Using:Name -Value $Using:Value
} @splat
}
處理字串型密碼
Invoke-Sqlcmd
Cmdlet 是接受字串做為密碼的 Cmdlet 範例。
Invoke-Sqlcmd
可讓您執行簡單的 SQL 插入、更新和刪除語句。
Invoke-Sqlcmd
需要純文字使用者名稱和密碼,而不是更安全的認證物件。 此範例示範如何從認證物件擷取使用者名稱和密碼。
此範例中的 Get-AllSQLDatabases
函式會呼叫 Invoke-Sqlcmd
Cmdlet 來查詢 SQL Server 的所有資料庫。 函式會使用先前範例中使用的相同屬性,定義 Credential 參數。 由於使用者名稱和密碼存在於 $Credential
變數內,因此您可以擷取這些值以搭配 Invoke-Sqlcmd
使用。
用戶名稱可從 $Credential
變數的 UserName 屬性取得。 若要取得密碼,您必須使用 $Credential
物件的 GetNetworkCredential()
方法。 將值擷取至變數中,然後將這些變數添加到哈希表中,用於展開參數至 Invoke-Sqlcmd
。
function Get-AllSQLDatabases {
param(
$SQLServer,
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
$UserName = $Credential.UserName
$Password = $Credential.GetNetworkCredential().Password
$splat = @{
UserName = $UserName
Password = $Password
ServerInstance = 'SQLServer'
Query = "Select * from Sys.Databases"
}
Invoke-Sqlcmd @splat
}
$credSplat = @{
TypeName = 'System.Management.Automation.PSCredential'
ArgumentList = 'duffney',('P@ssw0rd' | ConvertTo-SecureString -AsPlainText -Force)
}
$Credential = New-Object @credSplat
Get-AllSQLDatabases -SQLServer SQL01 -Credential $Credential
持續學習認證管理
創建並安全地儲存憑證物件可能會很困難。 下列資源可協助您維護PowerShell認證。