定义接受 PSCredential 对象的 Powershell 函数,但如果它为空则不会提示?
Posted
技术标签:
【中文标题】定义接受 PSCredential 对象的 Powershell 函数,但如果它为空则不会提示?【英文标题】:Define Powershell function that takes a PSCredential object, but won't prompt if it's null? 【发布时间】:2016-05-04 17:53:51 【问题描述】:我有一个接受 PSCredential 对象的 Powershell 函数。如果 PSCredential 为空,我想自己在函数中专门处理它。然而,最终发生的是我得到一个 PSCredential 提示(与您运行 Get-Credential
时的提示相同)。
在下面的脚本中,我为Credential
参数集设置了所需的参数,并且我想用[AllowNull()]
使其可以为空,但这并没有阻止提示出现。
这是我的功能:
<#
.description
Create a new Connection String Builder object
.parameter existingCSB
Use the passed CSB as the base, but allow it to be overridden.
.parameter property
Hashtable containing named properties for a connection string builder.
Will override values on -existingCSB.
.parameter credential
Use these credentials. Will override values on -existingCSB and any values
passed as -property.
NOTE: If this parameter is null, or if EITHER the username OR password in
the credential is an empty string, use a trusted connection aka
integrated security instead
.parameter username
Use this username. Will override values on -existingCSB and any values
passed as -property
NOTE: If EITHER the username OR password is null or an empty string, use a
trusted connection / integrated security instead
.parameter password
Use this password. Can be a string or a SecureString object. Will override
values on -existingCSB and any values passed as -property
NOTE: If EITHER the username OR password is null or an empty string, use a
trusted connection / integrated security instead
#>
function New-DbConnectionStringBuilder
[cmdletbinding(DefaultParameterSetName="NoCredential")] param(
[System.Data.Common.DbConnectionStringBuilder] $existingCSB,
[Hashtable] $property,
[Parameter(Mandatory=$true, ParameterSetName="Credential")]
[AllowNull()] [System.Management.Automation.PSCredential] $credential,
[Parameter(Mandatory=$true, ParameterSetName="UserPass")]
[AllowEmptyString()] [string] $username,
[Parameter(Mandatory=$true, ParameterSetName="UserPass")]
[AllowNull()] $password
)
$newDbCSB = New-Object System.Data.Common.DbConnectionStringBuilder $true
if ($existingCSB.Keys.Count -gt 0)
$existingCSB.Keys |% $newDbCSB[$_] = [String]::Copy($existingCSB[$_])
if ($property.Keys.Count -gt 0)
$property.Keys |% $newDbCSB[$_] = [String]::Copy($property[$_])
if ($PsCmdlet.ParameterSetName -notmatch "NoCredential")
if ($credential)
$username = $credential.UserName
$password = $credential.Password
if ($password -and $password.GetType().FullName -eq "System.Security.SecureString")
$password = Decrypt-SecureString $password
if ($username -and $password)
$newDbCSB["Uid"] = $username
$newDbCSB["Pwd"] = $password
$newDbCSB.Remove("Trusted_Connection") | Out-Null
else
$newDbCSB["Uid"] = $null
$newDbCSB["Pwd"] = $null
$newDbCSB["Trusted_Connection"] = "yes"
return $newDbCSB
这里有一些代码来调用它:
$csb = New-DbConnectionStringBuilder -credential $null
Write-Host "UID: $($csb['Uid'])"
Write-Host "Trusted_Connection: $($csb['Trusted_Connection'])"
如果您有兴趣,我这样做的原因是为了与现有代码兼容。我们有一些脚本可以从签入 Git 的加密文件中获取凭据。如果凭证不存在,现有代码将返回 $null 作为应使用集成安全性的指示。
总结起来,我想要的行为是:如果根本没有传递凭据,则不要将凭据信息添加到 CSB;如果通过了凭据但 $null,则使用集成安全性(也称为“可信连接”);如果凭据已通过且不为空,则在 CSB 中使用它们。
【问题讨论】:
您可以在参数声明中添加一个 [AllowNull] 属性,以使 PowerShell 知道允许将 $Null 作为值。 @oɔɯǝɹ 我已经做到了 - 请参阅我的代码。它没有失败,就像我将 $null 传递给未使用 [AllowNull()] 注释的必需参数时那样......但它仍然给我截屏的 PSCredential 弹出窗口。 这可能是使用多个参数集和从多个集指定参数的问题? 您应该接受解决您问题的任何答案(包括您自己的答案),这样该问题将被标记为已回答并已关闭。 【参考方案1】:没有什么叫做空值PSCredential
-参数。它会强制您输入某种类型的凭据。最简单的解决方案是检查您要传递给-Credential
的输入,如果它是$null
,则将其替换为[pscredential]::Empty
。例如。
$creds = $null
if($creds -eq $null) $creds = ([System.Management.Automation.PSCredential]::Empty)
$csb = New-DbConnectionStringBuilder -credential $creds
另外,您可以使用以下方法简化密码部分:
if ($credential)
$username = $credential.UserName
$password = $credential.GetNetworkCredential().Password
修改方案:
function New-DbConnectionStringBuilder
[cmdletbinding(DefaultParameterSetName="NoCredential")] param(
[System.Data.Common.DbConnectionStringBuilder] $existingCSB,
[Hashtable] $property,
[Parameter(Mandatory=$true, ParameterSetName="Credential")]
[System.Management.Automation.PSCredential] $credential,
[Parameter(Mandatory=$true, ParameterSetName="UserPass")]
[string] $username,
[Parameter(Mandatory=$true, ParameterSetName="UserPass")]
$password
)
$newDbCSB = New-Object System.Data.Common.DbConnectionStringBuilder $true
if ($existingCSB.Keys.Count -gt 0)
$existingCSB.Keys |% $newDbCSB[$_] = [String]::Copy($existingCSB[$_])
if ($property.Keys.Count -gt 0)
$property.Keys |% $newDbCSB[$_] = [String]::Copy($property[$_])
if($PsCmdlet.ParameterSetName -ne "NoCredential")
if ($PsCmdlet.ParameterSetName -eq "Credential" -and $credential -ne [System.Management.Automation.PSCredential]::Empty)
$username = $credential.UserName.Split("\")[-1]
$password = $credential.GetNetworkCredential().Password
if ($username -and $password)
$newDbCSB["Uid"] = $username
$newDbCSB["Pwd"] = $password
$newDbCSB.Remove("Trusted_Connection") | Out-Null
else
$newDbCSB["Uid"] = $null
$newDbCSB["Pwd"] = $null
$newDbCSB["Trusted_Connection"] = "yes"
$newDbCSB
演示:
Write-host
Write-host Null creds
Write-host
$creds = $null
if($creds -eq $null) $creds = ([System.Management.Automation.PSCredential]::Empty)
$csb = New-DbConnectionStringBuilder -credential $creds
Write-Host "UID: $($csb["Uid"])"
write-host "pass: $($csb["pwd"])"
Write-Host "Trusted_Connection: $($csb["Trusted_Connection"])"
Write-host
Write-host Creds
Write-host
$creds = New-Object System.Management.Automation.PSCredential "frode", (ConvertTo-SecureString -String "lol" -AsPlainText -Force)
if($creds -eq $null) $creds = ([System.Management.Automation.PSCredential]::Empty)
$csb = New-DbConnectionStringBuilder -credential $creds
Write-Host "UID: $($csb["Uid"])"
write-host "pass: $($csb["pwd"])"
Write-Host "Trusted_Connection: $($csb["Trusted_Connection"])"
Write-host
Write-host Username and password
Write-host
$csb = New-DbConnectionStringBuilder -username frode -password pass
Write-Host "UID: $($csb["Uid"])"
write-host "pass: $($csb["pwd"])"
Write-Host "Trusted_Connection: $($csb["Trusted_Connection"])"
Write-host
Write-host Nothing
Write-host
$csb = New-DbConnectionStringBuilder
Write-Host "UID: $($csb["Uid"])"
write-host "pass: $($csb["pwd"])"
Write-Host "Trusted_Connection: $($csb["Trusted_Connection"])"
输出:
Null creds
UID:
pass:
Trusted_Connection: yes
Creds
UID: frode
pass: lol
Trusted_Connection:
Username and password
UID: frode
pass: pass
Trusted_Connection:
Nothing
UID:
pass:
Trusted_Connection:
编辑:实际上您可以使用[object]
参数来接受空值凭据并验证它是脚本中的PSCredential
-object,但是您将失去以下好处写New-DbConnectionStringBuilder -Credential "frode"
时提示输入密码。
我更喜欢上面的解决方案并验证调用者脚本中的输入(修复空值)。这样,该功能仍然是“最佳实践”,用户(您)将有责任像您应该做的那样清理/验证输入。
function New-DbConnectionStringBuilder
[cmdletbinding(DefaultParameterSetName="NoCredential")] param(
[System.Data.Common.DbConnectionStringBuilder] $existingCSB,
[Hashtable] $property,
[Parameter(ParameterSetName="Credential")]
[object] $credential,
[Parameter(Mandatory=$true, ParameterSetName="UserPass")]
[string] $username,
[Parameter(Mandatory=$true, ParameterSetName="UserPass")]
$password
)
#Validate credential-type
if($credential -ne $null -and $credential -isnot [System.Management.Automation.PSCredential]) Write-Error -Message "credential parameter is not null or PSCredential" -Category InvalidArgument -ErrorAction Stop
$newDbCSB = New-Object System.Data.Common.DbConnectionStringBuilder $true
if ($existingCSB.Keys.Count -gt 0)
$existingCSB.Keys |% $newDbCSB[$_] = [String]::Copy($existingCSB[$_])
if ($property.Keys.Count -gt 0)
$property.Keys |% $newDbCSB[$_] = [String]::Copy($property[$_])
Write-Host ($credential -eq [System.Management.Automation.PSCredential]::Empty)
if($PsCmdlet.ParameterSetName -ne "NoCredential")
if ($credential -ne $null -and $credential -ne [System.Management.Automation.PSCredential]::Empty)
$username = $credential.UserName.Split("\")[-1]
$password = $credential.GetNetworkCredential().Password
if ($username -and $password)
$newDbCSB["Uid"] = $username
$newDbCSB["Pwd"] = $password
$newDbCSB.Remove("Trusted_Connection") | Out-Null
else
$newDbCSB["Uid"] = $null
$newDbCSB["Pwd"] = $null
$newDbCSB["Trusted_Connection"] = "yes"
$newDbCSB
$creds = $null
$csb = New-DbConnectionStringBuilder -credential $creds
Write-Host "UID: $($csb["Uid"])"
write-host "pass: $($csb["pwd"])"
Write-Host "Trusted_Connection: $($csb["Trusted_Connection"])"
【讨论】:
我不小心删除了NoCredential
支持。现在添加。 :-)
有趣。当您说“没有空 PSCredential 这样的东西”时,是否有一些解决方案可以像我正在做的那样允许空字符串对象? String 对象不能直接为空;也就是说,[string]$null
只是一个空字符串,并且在类型注释中,否则你将放置 [AllowNull()]
,你必须放置 [AllowEmptyString()]
。是否有注释可以理解我希望-credential $null
变成[PSCredential]::Empty
,就像[string]$null]
导致空字符串一样?
据我所知没有。如果 credential 是一个字符串参数,则将真实凭据传递给它会遇到问题。正如您所注意到的,pscredential-parameter 将在参数被“处理”而不是在实际使用时验证并要求凭据。在执行的那个阶段,我们只能验证值,不能修改它。因此,最好的解决方案是使用 validate 并将其替换为脚本中的空凭据,或者创建一个简单的包装函数来为您完成它(并调用真正的函数)
嘿,这几乎是我在下面自己的回答中得出的结论
我可以看到,但如前所述,我不推荐这种方法。您已经有某种调用此函数的脚本,因此最好的解决方案是在添加 1 行逻辑的脚本中清理您的输入。这样,该功能对于交互式使用(提示输入密码)仍然是安全的,它可重复使用且易于使用。您永远不会要求 Microsoft 修改 .NET Framework 方法来处理 您的 错误输入数据。 :-)【参考方案2】:
通过简单地从参数中省略类型注释,我能够足够接近我想要的。当我这样做时,Powershell 不会启动 PSCredential 弹出窗口,我可以处理函数内的所有内容。
也许不是最优雅的解决方案,但它确实有效。
这是我的代码:
<#
.description
Create a new Connection String Builder object
.parameter existingCSB
Use the passed CSB as the base, but allow it to be overridden.
.parameter property
Hashtable containing named properties for a connection string builder. Will override values on -existingCSB.
.parameter credential
Use these credentials. Will override values on -existingCSB and any values passed as -property.
NOTE: If this parameter is null, or if EITHER the username OR password in the credential is an empty string, use a trusted connection / integrated security instead
.parameter username
Use this username. Will override values on -existingCSB and any values passed as -property
NOTE: If EITHER the username OR password is null or an empty string, use a trusted connection / integrated security instead
.parameter password
Use this password. Can be a string or a SecureString object. Will override values on -existingCSB and any values passed as -property
NOTE: If EITHER the username OR password is null or an empty string, use a trusted connection / integrated security instead
#>
function New-DbConnectionStringBuilder
[cmdletbinding(DefaultParameterSetName="NoCredential")] param(
[System.Data.Common.DbConnectionStringBuilder] $existingCSB,
[Hashtable] $property,
# Do not give a type, so that this may be $null or a PSCredential object
# NOTE that there is no such thing as a null PSCredential object - the closest thing is [PSCredential]::Empty
[Parameter(Mandatory=$true, ParameterSetName="Credential")]
# [System.Management.Automation.PSCredential]
[AllowNull()] $credential,
[Parameter(Mandatory=$true, ParameterSetName="UserPass")]
[AllowEmptyString()] [string] $username,
# Do not give a type, so that this might be a string or a SecureString
[Parameter(Mandatory=$true, ParameterSetName="UserPass")]
[AllowNull()] $password
)
$newDbCSB = New-Object System.Data.Common.DbConnectionStringBuilder $true
if ($existingCSB.Keys.Count -gt 0)
$existingCSB.Keys |% $newDbCSB[$_] = [String]::Copy($existingCSB[$_])
if ($property.Keys.Count -gt 0)
$property.Keys |% $newDbCSB[$_] = [String]::Copy($property[$_])
if ($PsCmdlet.ParameterSetName -eq "Credential")
if ($credential)
# Note that we assume this is a PSCredential object, but it could be anything with a string UserName property and a string or SecureString Password property
$tmpUser = $credential.UserName
$tmpPass = $credential.Password
else
$tmpUser = $tmpPass = $null
elseif ($PsCmdlet.ParameterSetName -eq "UserPass")
$tmpUser = $username
$tmpPass = $password
if ($PsCmdlet.ParameterSetName -notmatch "NoCredential")
if ($tmpPass -and $tmpPass.GetType().FullName -eq "System.Security.SecureString")
$tmpPass = Decrypt-SecureString $tmpPass
if ($tmpUser -and $tmpPass)
$newDbCSB["Uid"] = $tmpUser
$newDbCSB["Pwd"] = $tmpPass
$newDbCSB.Remove("Trusted_Connection") | Out-Null
else
$newDbCSB["Uid"] = $null
$newDbCSB["Pwd"] = $null
$newDbCSB["Trusted_Connection"] = "yes"
return $newDbCSB
【讨论】:
以上是关于定义接受 PSCredential 对象的 Powershell 函数,但如果它为空则不会提示?的主要内容,如果未能解决你的问题,请参考以下文章
是否可以以某种方式将 WindowsIdentity 类型转换或转换为 PSCredential 类型?
PSCredential ToSecureString在哪里?
作为另一个用户的Runspace Powershell命令的WSManConnectionInfo和PSCredential