Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Scripting with PSCredential

Posted on April 10, 2012

I see this question often: how can I pass a parameter value for a PSCredential that might be a credential object or it might be a user name? In the past I've used code like this:

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!


begin {
Write-Verbose -Message "Starting $($myinvocation.mycommand)"
write-verbose -Message "Using volume $($volume.toUpper())"
#convert credential to a PSCredential if a string was passed.
if ( $credential -is [system.management.automation.psCredential]) {
Write-Verbose "Using PSCredential for $($credential.username)"
}
ElseIf ($Credential) {
Write-Verbose "Getting PSCredential for $credential"
$Credential=Get-Credential $credential
}
} #Begin

This assumes $Credential is a parameter. But then I realized, why not take advantage of parameter validation? I could use the [ValidateScript()] parameter attribute and insert some code to test the incoming value. If it is already a PSCredential, don't do anything. But if it is a string, call Get-Credential and use the result.


Param (
[Parameter(Position=0)]
[ValidateNotNullorEmpty()]
[string]$Computername=$env:Computername,
[ValidateScript({
if ($_ -is [System.Management.Automation.PSCredential]) {
$True
}
elseif ($_ -is [string]) {
$Script:Credential=Get-Credential -Credential $_
$True
}
else {
Write-Error "You passed an unexpected object type for the credential."
}
})]
[object]$Credential

When using ValidateScript your code has to return True or False. Or you can also Write and error if you want to customize the exception message a bit. That's what I've done here. With this code I can either use -Credential with a value like jdhitsolutions\administrator or a saved PSCredential object. Let me show you a simple script with this in action, plus I'll address another common question about using credentials with WMI-based scripts and functions.


#requires -version 2.0

<# This function demonstrates how you might pass a credential object as a parameter #>

Function Get-OSName {
[cmdletbinding()]

Param (
[Parameter(Position=0)]
[ValidateNotNullorEmpty()]
[string]$Computername=$env:Computername,
[ValidateScript({
if ($_ -is [System.Management.Automation.PSCredential]) {
$True
}
elseif ($_ -is [string]) {
$Script:Credential=Get-Credential -Credential $_
$True
}
else {
Write-Error "You passed an unexpected object type for the credential."
}
})]
[object]$Credential

)

#Never write the same line of code more than once if you can avoid it
$wmiCommand="Get-WmiObject -Class Win32_Operatingsystem -Property Caption,CSName -ComputerName $Computername"
Write-Verbose $wmiCommand

if ($Credential) {
#add the credential to the command string
Write-Verbose "Adding credential"
#escape the $ sign so that the command uses the variable name
$wmiCommand+=" -credential `$Script:Credential"
}

Write-Verbose "Creating a scriptblock from the command string"
$sb=[scriptblock]::Create($wmiCommand)

Try {
Write-Verbose "Invoking the command"
Invoke-Command -ScriptBlock $sb -errorAction Stop |
Select @{Name="Computername";Expression={$_.CSName}},
@{Name="OperatingSystem";Expression={$_.Caption}}
}
Catch {
Write-Warning $_.Exception.Message
}
Finally {
Write-Verbose "Finished"
}

} #close function

So the challenge is if I have a credential I need to use a Get-Wmiobject expression that uses it, otherwise run an expression without it. I'm a big believer in avoiding writing the same line of code more than once so I'll create a command string with my basic WMI command.


$wmiCommand="Get-WmiObject -Class Win32_Operatingsystem -Property Caption,CSName -ComputerName $Computername"

In this example the value for $Computername will be expanded and inserted into the string. If no credential is passed then this is the command I'll run. But if a credential is passed, then all I need to do is append it to my command string.


$wmiCommand+=" -credential `$Script:Credential"

You must pay attention to a very subtle detail: I am escaping the $ sign in the variable name. I do not want PowerShell to expand the variable. I want the command string to use the variable as variable. That is, if using a credential I need the command to be: Get-WmiObject -Class Win32_Operatingsystem -Property Caption,CSName -ComputerName SERVER01 -credential $Script:Credential"

The last step is to turn this command string into a script block so it can be executed.


$sb=[scriptblock]::Create($wmiCommand)

Armed with a scriptblock I can use Invoke-Command.


Invoke-Command -ScriptBlock $sb -errorAction Stop |
Select @{Name="Computername";Expression={$_.CSName}},
@{Name="OperatingSystem";Expression={$_.Caption}}

The end result is a function that I can run with no credentials. If I use a credential value like jdhitsolutions\administrator, I'll get prompted for the password from Get-Credential. Or if I pass a saved credential, the function will use it.

These techniques are by no means the only solution but I find them simple to follow and effective.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

14 thoughts on “Scripting with PSCredential”

  1. Mike Shepard says:
    April 10, 2012 at 11:04 am

    Instead of escaping the $, why not just use single quotes? Great article, btw.

    1. Jeffery Hicks says:
      April 10, 2012 at 11:36 am

      I suppose it is habit. Plus there always the chance I might have another variable to add to the command string that I DO want replaced.

      1. Mike Shepard says:
        April 10, 2012 at 6:02 pm

        Good point. FWIW, I find myself escaping $ often, too.

  2. beefarino says:
    April 10, 2012 at 12:58 pm

    Any reason you don’t use the built-in CredentialAttribute parameter transform attribute?

    function test-credentialparam{
    Param(
    [Parameter()]
    [System.Management.Automation.PSCredential]
    [System.Management.Automation.CredentialAttribute()]
    $Credential
    )
    Process { $credential; }
    }

    test-credentialparam -credential ‘beef’ #should prompt for a password…

    1. Jeffery Hicks says:
      April 10, 2012 at 1:05 pm

      Because I never knew about it but now I do. Thanks.

      1. Bartek Bielawski says:
        April 13, 2012 at 4:19 am

        Actually, because we do not want Credential to be mandatory in this case – we need to go one step further with transformation attribute: initialize it with ::Empty. More in this great article from Joel:
        http://huddledmasses.org/more-custom-attributes-for-powershell-parameters/
        Bits important here start somewhere in the middle (look for PowerShell syntax rather than C# scary parts… 😉

  3. JV says:
    April 10, 2012 at 2:44 pm

    Jeff -good. I haven’t done much with script validations.

    One thing though that always gets me.

    The following will not work:
    [ValidateNotNullorEmpty()]
    [string]$Computername=$env:Computername

    You cannot assign a value to an argument or it will override the NotNullOrEmpty. Try it.

    You will never be prompted for a computername. Remove the default and it will work.

    1. Jeffery Hicks says:
      April 10, 2012 at 3:02 pm

      I guess it’s a belt and suspenders thing with me. But this prevents someone from accidentally using a variable that might be empty.

      myscript -comp $oops

      Don’t confuse this with Mandatory. You can’t make a parameter mandatory and give it a default value.

      1. JV says:
        April 10, 2012 at 3:42 pm

        Ok. I see the difference. Thanks.

  4. Paul Cassidy says:
    April 10, 2012 at 8:40 pm

    Why not use a parameter set one for a credentials object and the other for the username and let the powershell engine do the binding.

    1. Jeffery Hicks says:
      April 10, 2012 at 10:51 pm

      That is certainly another option but the function would still need code to handle the multiple parameter sets.

  5. Serge Nikalaichyk says:
    April 11, 2012 at 7:13 am

    Hi! I use the following approach to pass credentials:

    [CmdletBinding()]
    param (
    [Parameter(
    Position = 0,
    Mandatory = $true,
    ValueFromPipeline = $true
    )]
    [Alias(“CN”)]
    [String]$ComputerName,

    [Parameter(
    Position = 1,
    Mandatory = $false
    )]
    [Alias(“RunAs”)]
    [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty
    )

    process
    {
    Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ComputerName -Credential $Credential
    }

    1. Jeffery Hicks says:
      April 11, 2012 at 8:03 am

      That is a very handy approach that meets all my criteria. Thanks for sharing.

  6. Pingback: Parameter validation « Mario's Blog

Comments are closed.

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d