Using credential param as optional..

This topic contains 4 replies, has 4 voices, and was last updated by Profile photo of Jake Sully Jake Sully 3 years, 4 months ago.

  • Author
    Posts
  • #9407
    Profile photo of stephenmbell
    stephenmbell
    Participant

    So I have a function that I am building to get inventory information from various computers on our network. One of the parameters for this is Credential.

    I am doing four WMI queries (so far) to grab os, bios, system, disk information. I am thinking there has to be a better, cleaner way to do it than go:

    if($credential)
    {
    # run all wmi queries with credential
    }
    else
    }
    # run all wmi queries without credentials
    }

    What is this better way?

    Thanks
    sb

  • #9408
    Profile photo of Don Jones
    Don Jones
    Keymaster

    I don't know if it's "better," but you can certainly do:

    if ($PSBoundParameters.ContainsKey('credential')) {
     # you have one
    } else {
     # you don't
    }
    
  • #9414
    Profile photo of Poshoholic
    Poshoholic
    Member

    There is a better way. 🙂

    When you declare your Credential parameter, declare it like this:

    [Parameter()]
    [ValidateNotNull()]
    [System.Management.Automation.PSCredential]
    [System.Management.Automation.Credential()]
    $Credential = [System.Management.Automation.PSCredential]::Empty
    

    Now you can simply pass the $Credential value through to the Credential parameter of your Get-WmiObject calls, because it will have a default value that is supported by Get-WmiObject, so if credentials are not required it will still work. For example:

    # This works even if the caller of our function didn't pass credentials
    Get-WmiObject Win32_Service -Credential $Credential
    

    Even better, by defining it this way you can pass in a Credential object or a string into your command, just like Get-Credential, and you'll be prompted if you pass in a string for the password.

    Caveat: Some cmdlets that accept a Credential parameter do not support/check for [System.Management.Automation.PSCredential]::Empty like they should. This should be treated as a bug by those cmdlet authors. There is a workaround though, and that is to use splatting. For example, if you want a solution that works for any cmdlet, you could do the following.

    1. In the top of your function, add this logic:

    $credSplat = @{}
    if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) {
        $credSplat['Credential'] = $Credential
    }
    

    2. Now wherever you want to optionally pass in the Credential parameter, you just do this:

    Get-WmiObject Win32_Service @credSplat
    

    If no credentials are passed in, or if the Empty credential object is passed in (or if it is left as default), then you don't pass anything to Get-WmiObject because $credSplat will be an empty hash table. Otherwise, if you have credentials, they go through because $credSplat will contain the credential value. Splatting is an awesome feature made for this very purpose.

  • #12920
    Profile photo of Jake Sully
    Jake Sully
    Participant

    I am trying to do this for a function that also collects inventory from systems. I've tried adding the code above but I am not doing something right as the function won't run once I add this in. I did find another cool function in part of another script, if I could add that in it would be great. There are a few functions in the script that I've found would be good. There's 1 that prompts what type of input you want to run the function against – a file, 1 name, search the domain, then 1 that prompts if you want to specify a different credential.
    Can anyone help me figure out what to add to the function I want? The function is straight forward as it's doing cmdlet binding

    Here's the start of the function I'm trying to use
    [CmdletBinding()]
    PARAM(
    [Parameter(ValueFromPipeline=$true)]
    [String[]]$ComputerName = $Computers,
    [String]$Errors = ".\Failures.log"
    )
    BEGIN {}#PROCESS BEGIN
    PROCESS{
    FOREACH ($Computer in $ComputerName) {

  • #12921
    Profile photo of Jake Sully
    Jake Sully
    Participant

    I am trying to do this for a function that also collects inventory from systems. I've tried adding the code above but I am not doing something right as the function won't run once I add this in. I did find another cool function in part of another script, if I could add that in it would be great. There are a few functions in the script that I've found would be good. There's 1 that prompts what type of input you want to run the function against – a file, 1 name, search the domain, then 1 that prompts if you want to specify a different credential.
    Can anyone help me figure out what to add to the function I want? The function is straight forward as it's doing cmdlet binding

    Here's the start of the function I'm trying to use
    [CmdletBinding()]
    PARAM(
    [Parameter(ValueFromPipeline=$true)]
    [String[]]$ComputerName = $Computers,
    [String]$Errors = ".\Failures.log"
    )
    BEGIN {}#PROCESS BEGIN
    PROCESS{
    FOREACH ($Computer in $ComputerName) {

You must be logged in to reply to this topic.