Jamie O'Connell
Jamie O'Connell

Reputation: 172

Powershell ConvertTo-SecureString not recognised if run script inline from cmd

When running from cmd.exe

powershell .\scripts\scriptname.ps1

Which has a line to convert plain text to secure string

[securestring]$secString = ConvertTo-SecureString $SampleString -AsPlainText -Force

I get error:

ConvertTo-SecureString : The 'ConvertTo-SecureString' command was found in the module 'Microsoft.PowerShell.Security', but the module could not be loaded. For more information, run 'Import-Module Microsoft.PowerShell.Security'.

However if I open / run the script direct in powershell it runs fine?

If I try to import the module manually I get:

Import-Module : The following error occurred while loading the extended type data file: Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member AuditToString is already present.
Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member AccessToString is already present.
Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member Sddl is already present.
Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member Access is already present.
Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member Group is already present.
Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member Owner is already present.
Error in TypeData "System.Security.AccessControl.ObjectSecurity": The member Path is already present.

Powershell version is

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      22621  963

Upvotes: 8

Views: 5743

Answers (3)

MAng-01
MAng-01

Reputation: 11

In case this error occurs in the context of Azure Devops on a self-hosted runner, it is most likely due to the runner being run in a Powershell Core command line.

Using the suggested workarounds or running it in a Windows Powershell instead should fix the issue.

Upvotes: 1

mklement0
mklement0

Reputation: 440556

The symptom of your call to powershell.exe, the Windows PowerShell CLI, from cmd.exe (or a batch file) implies that you've launched cmd.exe from PowerShell (Core) 7+.

In other words: the chain of calls is:

  • PowerShell (Core) (pwsh.exe) -> cmd.exe -> Windows PowerShell (powershell.exe)

This indirect call from PowerShell (Core) to Windows PowerShell is the problem: It makes the latter use the former's definition of the $env:PSModulePath and therefore results in attempts to load - incompatible - PowerShell (Core) modules.

Such an indirect call bypasses the logic that is built into PowerShell (Core) when directly invoking powershell.exe (dynamic removal of PowerShell (Core)-specific entries from $env:PSModulePath) and therefore requires a workaround:

Per GitHub issue #18530, the solution is to (temporarily) reset the PSModulePath environment variable:

:: From cmd.exe (a batch file).
set "PSModulePath="
powershell .\scripts\scriptname.ps1

Note:

  • As js2010 points out, when a process-specific value for PSModulePath is defined, powershell.exe uses this value as-is, which is problematic: unless the value includes the standard locations, required modules may not be discoverable.

  • By contrast, pwsh, the PowerShell (Core) 7+ CLI, is smarter about this: It automatically ensures the presence of all standard locations, even if a process-level value doesn't include them.

  • The exact rules for how the effective $env:PSModulePath value is determined are complex; they are documented as part of the conceptual about_PSModulePath help topic.

  • Note that even the default $env:PSModulePath values place the standard current-user module location first, making it possible to override standard cmdlets.

    • In both editions it is possible to place additional locations first:

      • implicitly, in Windows PowerShell, by defining a process-level value that determines the effective value as-is, as discussed.

      • explicitly, in PowerShell 7+, by including the current-user location in the process-level value, at the end.

Upvotes: 13

Heather Van De Sande
Heather Van De Sande

Reputation: 11

To expand on the answer above, I needed to do the following. I was calling a PS 5 script from a PS 7 script and the called script contained ConvertTo-SecureString:

Start-Process -FilePath "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoNewWindow -ArgumentList "-File $script", $Parm1, $Parm2 -Environment @{PSModulePath  = 'C:\Program Files\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules;C:\Program Files (x86)\ShareGate\'} -Wait

Upvotes: 1

Related Questions