When you want to submit sensitive information to a PowerShell function, you typically use the SecureString type. This type makes sure a user gets prompted with a masked dialog box, and the input is protected from anyone “looking over the shoulder”.
Since SecureString can always be decrypted to plain text by the person who created the SecureString, you can take advantage of masked input boxes but still work with the entered plain text:
function Test-Password { [CmdletBinding()] param ( [Parameter(Mandatory, Position=0)] [System.Security.SecureString] $Password ) # take a SecureString and get the entered plain text password # we are using a SecureString only to get a masked input box $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password) $plain = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) "You entered: $plain" }
When you run the code and then run Test-Password, you get prompted with a masked input. Inside the function, the submitted SecureString is decrypted to a plain text.
However, this approach has one significant drawback: if you plan to submit the information via a parameter, you now have to submit a SecureString. You can no longer submit plain text:
# fails:
PS> Test-Password -Password test
Test-Password : Cannot process argument transformation on parameter 'Password'. Cannot convert the "test" value of type "System.String" to type "System.Security.SecureString".
# works
PS> Test-Password -Password ("test" | ConvertTo-SecureString -AsPlainText -Force)
You entered: test
With a custom attribute, you can add the automatic capability to any parameter to auto-convert plain text to SecureString, though:
# create a transform attribute that transforms plain text to a SecureString class SecureStringTransformAttribute : System.Management.Automation.ArgumentTransformationAttribute { [object] Transform([System.Management.Automation.EngineIntrinsics]$engineIntrinsics, [object] $inputData) { if ($inputData -is [SecureString]) { return $inputData } elseif ($inputData -is [string]) { return $inputData | ConvertTo-SecureString -AsPlainText -Force } throw "Unexpected Error." } } function Test-Password { [CmdletBinding()] param ( [Parameter(Mandatory, Position=0)] [System.Security.SecureString] [SecureStringTransform()] $Password ) # take a SecureString and get the entered plain text password # we are using a SecureString only to get a masked input box $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password) $plain = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) "You entered: $plain" }
Now the user can run Test-Password without a parameter and gets prompted with a masked dialog. The user can also submit a plain text input directly:
# use built-in masked input PS> Test-Password cmdlet Test-Password at command pipeline position 1 Supply values for the following parameters: Password: ****** You entered: secret # use text-to-SecureString transformation attribute PS> Test-Password -Password secret You entered: secret
If you’d like to understand how transformation attributes work, here are the details: https://powershell.one/powershell-internals/attributes/transformation