Encrypting passwords in a PowerShell script remains a bit of a hot and tricky topic. There are various ways to parse a password. The golden rule is that you do not want anyone to be able to read passwords in your scripts. This means that you should refrain from using plaintext passwords!
Change Log 12.11.2023: Updated this article. The PowerShell Credentials Manager script is no longer available on the Microsoft website. 03.07.2018: I discovered a mega stupid error in the script to create a 256-bit AES key and password file. This has now been corrected. My apologies to anyone who had an issue with it. Thanks to my colleague and PowerShell guru Markus Kausl for his assistance! 05.12.2018: Additional comment added concerning the usage of plaintext passwords. |
Table of Contents |
Using plaintext passwords (not recommended!)
In case you do want to use a plaintext password (for testing purposes for example), use the following PowerShell command to create the PSCredential object (called $DatabaseCredentials in the example below):
1 2 3 |
$Account = "MyDomain\MyAccount" $AccountPassword = "123456" | ConvertTo-SecureString -AsPlainText -Force $DatabaseCredentials = New-Object System.Management.Automation.PSCredential($Account,$AccountPassword) |
The PSCredential object is a combination of the user account and the password. In the last line in the example above, you can see that the PowerShell variables $Account and $AccountPassword are both required to populate the variable $DatabaseCredentials.
TRY TO REFRAIN FROM USING PLAINTEXT PASSWORDS IN SCRIPTS.
There, I said it. My apologies for “shouting”, but plaintext passwords can get you in all sorts of trouble.
Conclusion:
- Biggest advantages: none*
- Biggest drawback: 100% INSECURE!!!
*As was pointed out to me in one of the comments below, technically speaking using plain text passwords does have some advantages. However, looking at the overall system as a whole, taking, among other things, security into consideration, plain text passwords should be avoided as much as possible. From a security perspective, they do not hold any advantages.
Now let’s get on with the good stuff. The remainder of this article will show you a couple of ways to securely use passwords in a PowerShell script.
Enter the password interactively (Read-Host)
This is the easiest method of them all, but this method is only suitable for scripts that run interactively.
In the plaintext example above, we entered the password directly in the script. We only need to replace the second line in the script to achieve our goal of entering the password interactively:
1 2 3 |
$Account = "MyDomain\MyAccount" $AccountPassword = Read-Host -AsSecureString $DatabaseCredentials = New-Object System.Management.Automation.PSCredential($Account,$AccountPassword) |
The second line contains the Read-Host cmdlet. When you run this PowerShell script interactively, you are required to enter the password in the PowerShell window.
Note: how to run a PowerShell script interactively?
|
The AsSecureString parameter turns your string into a secure string. The PSCredential object only accepts secure strings. You can try it yourself. Simply remove the AsSecureString parameter, execute the script once again, enter your password and see what happens. The PSCredential object will throw an error.
Conclusion:
- Biggest advantages: easy to implement and 100% secure.
- The biggest drawback: can only be used when running the PowerShell script interactively.
Using a 256-bit AES key file and a password file
When you use this method you will generate two files: a 256-bit AES key file and a password file. These files can then be used in your PowerShell scripts on local and remote computers.
How does this work?
The idea is that you create a 256-bit AES key file and one file which contains your password. The password in the password file is encrypted by the 256-bit AES key file. Confused? Don’t be. All will be explained in a moment.
Here is an example of the key file and the password file.
- “C:\AES_KEY_FILE.key”
- “C:\AES_PASSWORD_FILE.txt”
You can open these files in an editor such as Notepad, but you will only see gibberish. Once you have created the two files you can keep them on the local computer or copy them to a network share. Copying these files on a network drive means that you can use the same files on multiple computers.
Note: Please be aware that anyone with access to the key file can retrieve the password stored in the password file (the file AES_PASSWORD_FILE.txt in our example). Therefore, make sure that you configure NTFS permissions to prevent unauthorized access to this share! |
So how do you use these files once you create them?
The following code snippet shows you how the PSCredential object retrieves the content from the password file (C:\AES_PASSWORD_FILE.txt), using the AES key in the key file (C:\AES_KEY_FILE.key).
1 2 3 |
$Account = "MyDomain\MyAccount" $Key = Get-Content "\\MyServer\MyShare\AES_KEY_FILE.key" $DatabaseCredentials = New-Object System.Management.Automation.PSCredential($Account,(Get-Content "\\MyServer\MyShare\AES_PASSWORD_FILE.txt" | ConvertTo-SecureString -Key $Key)) |
This is all great, but how to create these files?
I have prepared a PowerShell script that you can use for this purpose. It is quite simple:
- First, save the PowerShell script below in a PS1 file (e.g. C:\CreateSecurePassword.ps1). You can change the directory path and file names in lines 17 to 19. By default the default directory is C:\. The file names are AES_KEY_FILE.key and AES_PASSWORD_FILE.txt.
- Secondly, run the PowerShell script interactively. The following window is displayed:
- Enter your password and click ENTER.
- The files will now be created. In case of an error, the error description is displayed in the PowerShell window.
Note: how to run a PowerShell script interactively?
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#========================================================================== # # CREATE SECURE PASSWORD FILES # # AUTHOR: Dennis Span (https://dennisspan.com) # DATE : 05.04.2017 # # COMMENT: # -This script generates a 256-bit AES key file and a password file # -In order to use this PowerShell script, start it interactively (select this file # in Windows Explorer. With a right-mouse click select 'Run with PowerShell') # #========================================================================== # Define variables $Directory = "C:\" $KeyFile = Join-Path $Directory "AES_KEY_FILE.key" $PasswordFile = Join-Path $Directory "AES_PASSWORD_FILE.txt" # Text for the console Write-Host "CREATE SECURE PASSWORD FILE" Write-Host "" Write-Host "Comments:" Write-Host "This script creates a 256-bit AES key file and a password file" Write-Host "containing the password you enter below." Write-Host "" Write-Host "Two files will be generated in the directory $($Directory):" Write-Host "-$($KeyFile)" Write-Host "-$($PasswordFile)" Write-Host "" Write-Host "Enter password and press ENTER:" $Password = Read-Host -AsSecureString Write-Host "" # Create the AES key file try { $Key = New-Object Byte[] 32 [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($Key) $Key | out-file $KeyFile $KeyFileCreated = $True Write-Host "The key file $KeyFile was created successfully" } catch { write-Host "An error occurred trying to create the key file $KeyFile (error: $($Error[0])" } Start-Sleep 2 # Add the plaintext password to the password file (and encrypt it based on the AES key file) If ( $KeyFileCreated -eq $True ) { try { $Key = Get-Content $KeyFile $Password | ConvertFrom-SecureString -key $Key | Out-File $PasswordFile Write-Host "The key file $PasswordFile was created successfully" } catch { write-Host "An error occurred trying to create the password file $PasswordFile (error: $($Error[0])" } } Write-Host "" write-Host "End of script (press any key to quit)" $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") |
I wrote this paragraph based on the following excellent article by Luca Dell’Oca:
https://www.virtualtothecore.com/encrypt-passwords-in-powershell-scripts/.
This method works fine when you want to parse a secure password to a PSCredential object. However, in some rare cases, you may need to parse a plain-text password. Not to worry, you can decrypt a secure password to a plain-text password in memory.
The key/password file method described in this section ensures that you do not need to enter any plain-text password directly in your script. After creating both files you copy them to a directory on the local server or a network share. After that, you only need four additional lines of PowerShell code to read the secure password from the password file and to decrypt it to a normal readable plaint-text string.
1 2 3 4 5 6 7 8 9 10 |
# Define variables $Directory = "C:\" $KeyFile = Join-Path $Directory "AES_KEY_FILE.key" $PasswordFile = Join-Path $Directory "AES_PASSWORD_FILE.txt" # Read the secure password from a password file and decrypt it to a normal readable string $SecurePassword = ( (Get-Content $PasswordFile) | ConvertTo-SecureString -Key (Get-Content $KeyFile) ) # Convert the standard encrypted password stored in the password file to a secure string using the AES key file $SecurePasswordInMemory = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword); # Write the secure password to unmanaged memory (specifically to a binary or basic string) $PasswordAsString = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($SecurePasswordInMemory); # Read the plain-text password from memory and store it in a variable [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($SecurePasswordInMemory); # Delete the password from the unmanaged memory (for security reasons) |
In the above code, we use the Marshal class to write the secure password to unmanaged memory. We then read this unmanaged memory and store the password in the variable $PasswordAsString. You can test if this variable contains the correct password by adding a simple write-host command:
1 |
Write-Host "Password is: $PasswordAsString" |
Conclusion:
- Biggest advantage: can be used in multiple scripts on remote computers.
- The biggest drawback: is not 100% secure. The AES key file can be used to decrypt the password and therefore requires additional (NTFS) protection from unauthorized access.
Using an SCCM collection variable
For those of you who use PowerShell scripts in SCCM applications and packages, a good way of dealing with passwords is to use a collection variable.
When creating a collection variable, make sure that the tick box Do not display this value in the Configuration Manager console is marked. This makes the variable “secure”. Secure in this case means that the variable is stored in the SCCM SQL database.
You can use the following code snippet in an SCCM package to retrieve the MyPassword collection variable:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Load the Microsoft.SMS.TSEnvironment COM object $SCCMTS = New-Object -COMObject Microsoft.SMS.TSEnvironment # Get all (built-in) task sequence and collection variables $AllTSVariables = ($SCCMTS.GetVariables() | sort-object) # Loop through all variables and retrieve the value for the password variable foreach ($Value in $variableNames){ If ( $Value -eq "MyPassword" ) { $AccountPassword = $($SCCMTS.Value($Value)) } } # Create the PSCredential object $Account = "MyDomain\MyAccount" $AccountPassword = $AccountPassword | ConvertTo-SecureString -AsPlainText -Force $DatabaseCredentials = New-Object System.Management.Automation.PSCredential($Account,$AccountPassword) |
The first step is to connect to the Microsoft.SMS.TSEnvironment COM object. Please be aware that the SCCM package runs as a 32-bit process. However, the PowerShell script needs to run as a 64-bit process, otherwise loading the Microsoft.SMS.TSEnvironment COM object ends in an error. The command in your SCCM program should be something like this:
%WinDir%\Sysnative\WindowsPowershell\v1.0\powershell.exe -executionpolicy unrestricted -file MyScript.ps1
The variable sysnative will redirect the 32-bit process to the 64-bit subsystem and, in our example, will start the 64-bit PowerShell executable (instead of the 32-bit one).
The next step in the script is to retrieve all task sequence and collection variables. When the variable name is MyPassword, its value is read and stored in the PowerShell variable $AccountPassword.
After that, the PSCredential object is created using the user name stored in the variable $Account and the password stored in the collection variable MyPassword.
For more information on how to create a PowerShell script in an SCCM package please see the following two articles:
One last tip; make sure to watch out when writing the variables to a log file. You do not want to write your password to your log file in plaintext!
Conclusion:
- Biggest advantage: relatively secure (but not 100%!).
- Biggest drawbacks: the SCCM collection variable cannot be shared among other collections. The password variable needs to be set on each collection that requires it. Also, retrieving the password using the Microsoft.SMS.TSEnvironment COM object is relatively complex.
PowerShell Credentials Manager
Finally, I want to present to you another approach that I found very interesting, namely using the local Windows Credentials Manager to store your passwords.
The PowerShell script can be found on Github:
GitHub – davotronic5000/PowerShell_Credential_Manager: PowerShell Module to Read and Write Credentials from the Windows Credential Manager
Please be aware that this script has not been tested on newer operating systems such as Windows 10 and Windows Server 2016. Also, this script only works on the local server. The Win32 API CredMan cannot connect to remote computers.
Conclusion:
- Biggest advantage: 100% secure.
- Biggest drawback: can only be used locally and the automation is complex.
All-in-all, none of the methods presented in this article are perfect. There simply is no 100% perfect solution. Each method has some advantages and some drawbacks, but these methods are all that exist (at least to my knowledge). You need to choose the one that is best suited for your current situation.
I hope this article gave you some insights into the world of passwords and PowerShell. Happy scripting!
Dennis Span works as a Lead Account Technology Strategist at Cloud Software Group in Vienna, Austria. He holds multiple Citrix certifications (CCE-V). Dennis has been a Citrix Technology Advocate (CTA) since 2017 (+ one year as Citrix Technology Professional, CTP). Besides his interest in virtualization technologies and blogging, he loves spending time with his family as well as snowboarding, playing basketball and rowing. He is fluent in Dutch, English, German and Slovak and speaks some Spanish.
Pingback: Citrix Delivery Controller unattended installation with PowerShell and SCCM - Dennisspan.com
Pingback: Citrix App Layering Agent unattended installation - Dennis Span
“The following code snippet show you how the PSCredential object retrieves the content from the password file (C:\AES_KEY_FILE.key), using the AES key in the key file (C:\AES_PASSWORD_FILE.txt).”
It appears you accidentally flipped the file names (stating that the password file is entitled “key” and that the key file is entitled “password”).
Thanks a lot Steve! And yes, you are correct; I did flip the file names. I just corrected it. Thanks!
Pingback: Citrix Application Probe Agent unattended installation - Dennis Span
Thank you for the excellent breakdown of options for creating secure credentials for use in PS scripts. I am new to PS and trying to understand the best way to apply these stored credentials to an exe file in a PS script that is using switches to supply username and password to the exe. There seems to be a lot of posts out there about using stored credentials with PS cmdlts but little to nothing about exe. I am trying to convert a batch file with clear text credentials to a more secure powershell script.
Hi Gary, I am happy if the article was of some help to you. I am unsure if I should interpret what you wrote as a question or as a general comment. Sorry. 🙂
For plain text, you listed biggest advantages as “none”. This is incorrect. There are advantages, which is why so many people do it. I’m not saying it is *right*, I’m just saying there are advantages. For example: 1) easy, 2) works with any scripting method, 3) may be acceptable for some passwords (such as an initial temporary password that will be forced to change at first use)
Hi Peter,
Thank you for your comments. And yes, I understand your point. I made a note of your remarks in the main text. Thanks once again for your input.
Kind regards,
Dennis
Pingback: Creating an ODBC Connection With PowerShell Using a Specific Account – Mark Roberts
Hi Dennis, scripts works great but is there a way to decrypt the password?
Hi Gert, as explained in the article you can decrypt the password using the command Write-Host “Password is: $PasswordAsString”, but you need both the key file and the password file.
Pingback: password protect powershell script - infoai.net