PowerShell Great Debate: Credentials

Credentials suck.

You obviously don't want to hardcode domain credentials into a script - and PowerShell actually makes it a bit difficult to do so, for good reason. On the other hand, you sometimes need a script to do something using alternate credentials, and you don't necessarily want the runner of the script to know those credentials.

So how do you deal with it?

Let's be clear: This is not a wish list. Comments like, "I wish PowerShell could do ____" aren't valid. What do you do using the technology as it exists today? Do you prompt for a credential and assume the script user will have it? Do you try to hardcode it? Do you set up a constrained endpoint? What?

[boilerplate greatdebate]

About the Author

Don Jones

Don Jones is a Windows PowerShell MVP, author of several Windows PowerShell books (and other IT books), Co-founder and President/CEO of PowerShell.org, PowerShell columnist for Microsoft TechNet Magazine, PowerShell educator, and designer/author of several Windows PowerShell courses (including Microsoft’s). Power to the shell!

9 Comments

  1. For most instances where alternate credentials are needed, I run the script as a scheduled task and provide the credential information within the scheduled task.

  2. I manage about 20 forests and I need domain admin credentials in most of those domains. I have sync'd my user name and password across all domains I access. So I save my password in an encrypted file. When my profile loads it creates a hash table of all my domain names (key) with the PSCredential as the value.

    Here is how my process works:

    When my password expires, I manually create a new password file like this:

    convertfrom-securestring (read-host -assecurestring) > passwordfile.txt

    That file is encrypted. The only way to read it is to be logged on to the same computer with the same account (you can't login to a different computer with the same account and you can't log on to the same computer with a different account - it won't be able to decrypt it ). I find this encryption is sufficient. I keep my password file on a secure network share so it isn't stored locally on my computer or laptop.

    In my profile I call a function I created that uses System.Management.Automation.PSCredential to create the PSCredential.

    function Get-CredWithSecurePass($username,$password){
    return New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username,$password
    }

    The nice thing about that object is that it requires a securestring for the password and low and behold the result of decrypting my password file is a securestring.

    So I set a variable with my password as a secure string:

    $admpwd = ConvertTo-SecureString (gc passfile.txt) - because I'm logged into the same computer with the same credential that command is able to convert the encrypted file into a securestring and assign the securestring to a variable.

    Then I get a list of all the netbios names of the domains I login to and call the above function to set the PSCredential like this:

    gc domains.txt | foreach{ $admincreds += @{$_ = Get-CredWithSecurePass "$_\UserName" $admpwd}}

    I end up with a hash variable that has the netbios names as the keys and the PSCredential for the domain as the value.

    To use it I just do something like this:

    get-qaduser samaccountname -service domain -credential $admincreds.domain

    The nice thing is that I can automate connecting to various domains like this.

    csv file contains:
    username,domain
    usera,domaina
    usera,domainb

    so now I can do this:

    import-csv csvfile.csv | foreach{get-qaduser $_.username -service $_.domain -credential $admincreds.($_.domain)}

    Isn't PowerShell just so beautiful? I love it!!!

  3. My rule is:
    a) if you trust the account running the script with the credentials, use DPAPI to encrypt the credentials so they are not available to other accounts on the same machine.
    b) if you don't trust the account running the script with the credentials, use a constrained endpoint.
    c) if it is possible to prompt the interactive user for the credentials when the script runs, you haven't finished automating 😉

  4. It depends on what we're doing and why, but one of the techniques I've used is to compile the script with PrimalScript and use their embedded RunAs feature. Only real downside to this method is you have to recompile if the password changes, and you need to know the account info in order to recompile it (say if you add a new feature).

  5. Jeff Hicks gave an interesting talk on this, at the PowerShell summit. In general we are steering towards constrained, delegated endpoints, although there are many options to chose from depending on your risk appetite and regulatory/legislative landscape.

    In the past we have tried the RunAs feature from tools like PrimalForms. There were a few issues IMHO - how secure is that solution? how often do you change the password? Does it work consistently (no, for us)? Is the troubleshooting worth the effort? Is a GUI worth the extra effort across various layers?

    What we've ended up with is something like Rich Prescott's Arposh tool, which hits constrained, delegated endpoints or runs with the current user's credentials, depending on what it is doing.

    Side note: recently posted a demo for setting up one of these endpoints. Haven't had time to look into the new options with New-PSSessionConfigurationFile or other settings, but this seems to work : ) http://gallery.technet.microsoft.com/scriptcenter/Granular-access-via-8d396436

  6. I have to deal with credentials a notch more than most, as almost everything I build in PowerShell nowadays has some component interacting with a web service. About 2 years ago, I wrote a series of cmdlets for this purpose: Add-SecureSetting, Get-SecureSetting, and Remove-SecureSetting. These work like a charm for managing credentials in scripts. You can download them as part of PowerShell Pipeworks (http://powershellpipeworks.com/), or as part of IsePackV2 (http://powershellise.com)

    This YouTube video shows how to use them:

    http://www.youtube.com/watch?v=0haXavQU_nY

    Hope this helps,

    James

  7. I run into problem when connecting to machines on a completely untrusted domain. I also have built a few scripts for interacting with various web service APIs that usually required some form of credentials.

    After using Get-Credential so many times I finally started Googling for solutions and found a script for getting credentials from the Windows Credential Manager. Like using an EFS protected file, the credentials in Windows Credential Manager are protected by your logon account.

    Details are here: http://www.automatedops.com/blog/2013/06/07/get-storedcredentials-module/

  8. If powershell script is run by a service which has been provided credentials using 'Log on' Tab, wouldn't it work?

    I think following should work, without passing credential object.

    Enter-PSSession -ComputerName "MACHINE-NAME"

    This is what I am trying to do, but does not work. Any thoughts !