Search code examples
windowspowershellshellcommand-line

Cannot convert the value of type "System.String" to type "System.Security.SecureString" but only in PS 7.4.6 but not in PS 5.1


I made a script to unlock bitlocker volumes using the password manager autotype feature instead of the clipboard. It uses gsudo and needs some features available only in 7.4.

$DriveLetter = Read-Host "Drive letter"
$DriveLetter = ($DriveLetter + ":")
$EncUserCredential = Read-Host "Enter Password" -AsSecureString | ConvertFrom-SecureString
gsudo pwsh.exe -CommandWithArgs '`$parm1 = ConvertTo-SecureString -String `$args[1]; Unlock-Bitlocker -MountPoint `$args[0] -Password `$parm1' $DriveLetter $EncUserCredential

When I run the script directly in a 7.4.6 shell I get:

Drive letter: w
Enter Password: ********************
Unlock-BitLocker: Cannot process argument transformation on parameter 'Password'. Cannot convert the value of type "System.String" to type "System.Security.SecureString".

Conversely, if I run it in 5.1 (7.4 is invoked later in the script) it works, why? This forces me to keep 5.1 as the default powershell for my terminal, not a big deal but still... Also, the policy for unsigned scripts is only applied to 5.1, it seems to me that this makes the setting somewhat less relevant.


Solution

  • From a PowerShell session, gsudo offers convenient syntax for invoking PowerShell commands directly, using a script block ({ ... }) and optional arguments, which can be passed as an array to the -Args parameter:

    gsudo { 
      $parm1 = ConvertTo-SecureString -String $args[1]
      Unlock-Bitlocker -MountPoint $args[0] -Password $parm1
    } -Args $DriveLetter, $EncUserCredential
    

    Compared to your attempt to call pwsh, the PowerShell (Core) 7 CLI, explicitly, the above is:

    • more convenient
    • avoids escaping headaches
    • supports returning typed data, within the usual constraints of PowerShell's cross-process serialization infrastructure (see this answer for background information)

    As for what you tried:

    • By passing an explicit pwsh CLI call to gsudo, you're complicating the escaping needs even further (compared to passing just a single argument containing PowerShell code as a string) and, for reasons unknown to me, the -Command (-c) parameter behaves differently from the (v7.4+) -CommandWithArgs (-cwa) parameter.

      • E.g., gsudo pwsh -c ' `$HOME + """!"""; Get-Date ' works (but note the obscure escaping requirements),
        whereas the equivalent gsudo pwsh -cwa ' `$HOME + """!"""; Get-Date ' does not (note the missing ! in the output).
    • If you were to pass the command string alone - in which case no extra escaping is required - things would be simpler (e.g., gsudo ' $HOME + "!"; Get-Date '), but then you couldn't pass arguments separately, the way you tried via -CommandWithArgs

    • However, given the simpler and more robust alternative based on script blocks shown at the top, this isn't a problem that needs solving.