NodeJS - Child Process - Exec VS Powershell ISE | why are the results different?? why can't the PSCredential be created?

76 views Asked by At

MY PS Script:

$admin = "TEST"
$Password =  ConvertTo-SecureString "Test12345" -AsPlainText -Force
$Credential = [PSCredential]::new( $admin, $Password)
if ($Credential -is [System.Management.Automation.PSCredential]) {
return "PS Credentials - true"
} else {
return "PS Credentials - false"
}

RESULT in PS ISE: enter image description here

MY NODE JS Script:

"use strict";
const { exec } = require("child_process");

exec(
    "powershell.exe -File C:\\Users\\fschiller\\Desktop\\Projekt_Node\\Libary\\TEST.ps1",
    (error, stdout, stderr) => {
      console.log(stdout)
    }
)

RESULT in VSCode from Node Script: enter image description here

I Dont know how to Fix it. Can Someone Help ? or know the Problem and how to work around it?

tried ExecFile, other ways to creat PSCredential Object, "terminal.integrated.shellArgs.windows": ["-ExecutionPolicy", "Bypass"]

2

There are 2 answers

1
Bratwurstpilot On

It works if I specify the module to the full path -

Import-Module -Name “C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Security\Microsoft.PowerShell.Security.psd1"
0
mklement0 On

The problem isn't specific to any particular PowerShell host program (console vs. ISE vs. integrated terminal in Visual Studio Code), it comes down to indirectly launching powershell.exe, the Windows PowerShell CLI from PowerShell (Core) 7+:

  • This causes Windows PowerShell to inherit an inappropriate PSModulePath environment-variable value ($env:PSModulePath), with entries that pertain to PowerShell 7+ only, which results in attempts to load incompatible modules - which is what you saw.

  • It is only if you call powershell.exe directly from PowerShell 7+ that the latter anticipates the problem and itself applies the solution described below.

Specifically, the symptoms with respect to the Microsoft.PowerShell.Security module that contains the ConvertTo-SecureString cmdlet in your case are:

  • The first time you call ConvertTo-SecureString in a given session, its module is auto-loaded - but because an attempt is made to load the incompatible PowerShell 7+ version of the module, auto-loading fails with the following 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'.
    At line:1 char:1
    + ConvertTo-SecureString -?
    + ~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ObjectNotFound: (ConvertTo-SecureString:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CouldNotAutoloadMatchingModule
    
  • Running Import-Module Microsoft.PowerShell.Security then yields the following error - its technical details aren't important, and unfortunately, it doesn't provide much of a clue as to what the problem is:

    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.
    # ... (similar lines omitted)
    At line:1 char:1
    + Import-Module Microsoft.PowerShell.Security
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (:) [Import-Module], RuntimeException
        + FullyQualifiedErrorId : FormatXmlUpdateException,Microsoft.PowerShell.Commands.ImportModuleCommand
    

The solution, per a comment from a PowerShell team member in GitHub issue #18530, is to undefine the PSModulePath environment variable for the child process that indirectly calls powershell.exe:

Note:

  • delete, as shown below, must be used to undefine (remove) a variable from process.env - assigning '' (or null or undefined) to process.env.PSModulePath does not work.

  • So as not to modify the caller's environment, a copy of the process.env is created, from which the variable is then removed, and passed via the env: property of an object as the options argument to child_process.exec()

  • A simplified call to powershell.exe is used below, which simply prints the value of $env:PSModulePath as seen by the child process, which should then contain only the usual, Windows PowerShell-only entries.

"use strict"
const { exec } = require("child_process")

// Create a copy of the current environment block
// and remove the 'PSModulePath' variable from it.
const customEnv = Object.assign({}, process.env)
delete customEnv.PSModulePath

exec(
    "powershell.exe -NoProfile -Command $env:PSModulePath",
    { env: customEnv }, // Pass the modified environment.
    (error, stdout, stderr) => {
      console.log(stdout)
      console.error(stderr)
    }
)