定义带有PSCredential对象的Powershell函数,但如果它为空则不会提示?

时间:2016-05-04 17:53:51

标签: powershell

我有一个Powershell函数,它接受一个PSCredential对象。如果PSCredential为null,我想自己在函数中专门处理它。但是,最终发生的事情是我收到了PSCredential提示(与运行Get-Credential时的提示相同)。

PSCredential prompt example

在我的下面的脚本中,我创建了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,则使用集成安全性(又名&#34;可信连接&#34;);如果凭据被传递而不是null,则在CSB中使用它们。

2 个答案:

答案 0 :(得分:3)

没有任何东西叫做空值PSCredential - 参数。它会强制您输入某种类型的凭据。最简单的解决方案是检查要传递到-Credential的输入,并将其替换为[pscredential]::Empty,如果它是$null。实施例

$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 - 对象,但是您将写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"])"

答案 1 :(得分:0)

通过简单地省略参数中的类型注释,我能够达到我想要的程度。当我这样做时,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
}