1

I'm trying to create a module with a bunch of functions, but I'm stuck with a problem: sometimes I need to run functions with a different from current credentials. But the thing is: I don't want to ask for credentials if I didn't specify a username. Like this:

function MyFunction ($ComputerName='localhost', $UserName) {
    if ($UserName) {
        Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ComputerName -Credential $UserName
    } else {
        Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ComputerName
    }
}

Can I somehow get rid of the if statement? Can I just let the function use current credentials unless I specify -UserName?

If I leave it like this:

Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ComputerName -Credential $UserName

and call a function without specifying -UserName, it asks for credentialls every time. Yes, it uses current if I close the 'get-cred' box, but it's not what I want.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
n01d
  • 1,047
  • 8
  • 22
  • May I ask exactly what's wrong with using the "if"? Since Get-WmiObject is an internal MS cmdlet I'm not sure you'll find any easier way to perform what you're attemping to do :). – cjones26 Jun 09 '15 at 14:41
  • This function is only for example. Not all of them are that simple (none of them actually). Trying to avoid wrapping really big expressions into 'if-else' statements. – n01d Jun 09 '15 at 17:32

3 Answers3

2

You could use splatting for dynamically building a parameter list, but you'd still need an if statement to decide whether or not to add the -Credential parameter:

function MyFunction ($ComputerName='localhost', $UserName) {
    $params = @{
      Class        = 'Win32_OperatingSystem'
      ComputerName = $ComputerName
    }
    if ($UserName) { $params['Credential'] = $UserName }

    Get-WmiObject @params
}
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
0

You can populate a PSCredential object with current credentials using the default credential acquired like in here:

if ($UserName) {
   $cred = get-credential $username # this prompts for credentials
} else {
   $cred=[PSCredential][System.Net.CredentialCache]::DefaultCredentials
}

Then just use -credential $cred whenever you need without building a wall of if's.

EDIT: Apparently, as the current user's PSCredential object has its password as SecureString encrypted by the very same user's private key, if one would be able to get a PSCredential out of default credentials object, he'll be able to decrypt the password into plaintext, so this hole existed but was eventually closed. (Maybe let this answer hang here so that people won't get the asme error as I did) This question has the canonical answer on what arised in here.

Another way to do this might be using a variation of splatting, explained in detail by Ansgar Wiechers and here to construct only credential block in a single if statement and then use that block wherever you need, instead of writing direct -credential parameter.

$creds=@{}
if ($UserName) {
   $cred = get-credential $username # this prompts for credentials
   $creds['Credential']=$cred
}

And then add @creds wherever you require alternate credentials:

Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ComputerName @creds

This way you'll be asked for user's password once if $UserName is supplied, and then the $creds will either be empty or contain valid credentials of $UserName, so all subsequent if's can be replaced by explicit adding of @creds.

Community
  • 1
  • 1
Vesper
  • 18,599
  • 6
  • 39
  • 61
  • Are you sure this is supposed to work? Trying to cast `[PSCredential][System.Net.CredentialCache]::DefaultCredentials` threw an invalid cast exception for me. – Ansgar Wiechers Jun 09 '15 at 17:52
  • I receive the following error on this: `Cannot convert the "System.Net.SystemNetworkCredential" value of type "System.Net.SystemNetworkCredential" to type "System.M anagement.Automation.PSCredential".` – n01d Jun 09 '15 at 19:24
  • @AnsgarWiechers I did it two-step, first into a variable, then into a PSCredential, and yes, it did create a valid PSCredential for me. Powershell 4.0, though. Maybe PS2.0 won't be able to do this. – Vesper Jun 10 '15 at 03:27
  • Okay, I've apparently messed up my local vars with this. Pity. On the other hand, there's [this question](http://stackoverflow.com/questions/3917779/get-current-users-credentials-object-in-powershell-without-prompting) that states you can't get default creds without prompting. Weird tho. I'd rather make those cmdlets accept a `NetworkCredential` object to avoid this case. – Vesper Jun 10 '15 at 06:37
  • Yep I was wrong. See updated answer. This one is based on @AnsgarWiechers one but is a tad better to only create a variable parameter set into hashtable to be splatted, so you don't need to build the whole parameter set each time you need alternate credentials. – Vesper Jun 10 '15 at 07:10
  • Edited answer really works! That's the thing i needed, thanks a lot! – n01d Jun 13 '15 at 21:20
0

Your never really going to lose the IF, but you could create a string on the fly and then invoke it as and expression:

If $UserName is $null the credential parameter will not be added to the expression.

function MyFunction ([String]$ComputerName='localhost', [String]$UserName) {

    Invoke-Expression ("Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ComputerName $(IF($UserName){'-Credential $UserName'})")

}
Richard
  • 6,812
  • 5
  • 45
  • 60
  • Hmmmm, check if supplying an invalid value for `$Username` won't result in Powershell injection into the command. `"Robert"; Set-ExecutionPolicy unrestricted -scope LocalMachine -force; #` for example must not trigger the `Set-ExecutionPolicy` calling. – Vesper Jun 09 '15 at 15:11
  • 1
    @Vesper I have just tried it and it looks like you can tag any command onto the end of the string, if run outside the function. But if you set the parameters of the function to only except strings this stops the injection. – Richard Jun 09 '15 at 15:27
  • Hehe, it's just a reflex, if there's evaluatable expression, check if an injection is possible. This thing is dangerous, because Powershell deals with server management up close and personal, and often runs under administrative privileges (manual mode or scripted tasks), so an injection can literally destroy an organization structure if allowed to happen. – Vesper Jun 09 '15 at 15:32