Powershell via Winbatch not working.

Started by kenatureg, July 16, 2020, 08:11:59 AM

Previous topic - Next topic

kenatureg

I am attempting to learn how to run Powershell from Winbatch as I think that could be a very good combination. Here is the ps1 file with some entries that are just for debugging:

$fnd = "no"
$drives = Get-PSDrive
ForEach($drive in $drives) {
  If ($drive.name -eq 'DellSmbios') {
    $fnd = "yes"
    Break
  }
}
$fnd | Out-File c:\temp\fndtest1.txt
If ($fnd -eq "no") {
  "got here 1" | Out-File c:\temp\ghere1.txt
  Import-Module DellBIOSProvider -Force -Verbose
}
$dellb = Get-Module DellBIOSProvider
$dellb | Out-File c:\temp\dellb.txt
$acpwr = Get-Item -Path DellSmbios:\PowerManagement\AcPwrRcvry
$acpwr | Out-File c:\temp\acpwr.txt
Set-Item -Path DellSmbios:\PowerManagement\AcPwrRcvry "On" -Password "xxxxxxxxxx"
$gep = Get-ExecutionPolicy -List
$gep | Out-File c:\temp\gepout.txt


Here is the Winbatch code to invoke the script.

psfile = FileGet("c:\temp\pshelltst.ps1", "")
ObjectClrOption("useany", "System.Management.Automation")
objAutoPs = ObjectClrNew("System.Management.Automation.PowerShell")
oPshell = objAutoPs.Create()
oPshell.AddScript(psfile)
objAsync = oPshell.BeginInvoke()
rslt = oPShell.EndInvoke(objAsync)

oPShell.Dispose()
oPShell=0


Note that I have used a scope setting of TRUE in the AddScript line as well but it doesn't seem to make any difference.

Not sure if this matters but when I create and compile Winbatch code it is done as a standard user. Usually the program is then run as an administrator.

I have a separate Winbatch program that sets the execution policy for localmachine to Bypass by modifying the registry. Note that the default execution policy for our computers is Restricted. If a Powershell prompt is opened after this program is run it shows localmachine = Bypass.
The ps1 file when run from inside Winbatch, which is run as administrator, shows the execution policy as localmachine = RemoteSigned. I don't know where this is coming from.
Note that if the ps1 file is run in a Powershell prompt when localmachine = RemoteSigned it runs fine. When the ps1 file runs from within Winbatch with localmachine = RemoteSigned it does not run fine. I believe that the Import-Module step is not working because the dellb.txt file and the acpwr.txt file are both empty. The Import-Module step runs a script. Somehow I think that script is considered remote (and unsigned) because it is a script within a script within a program. If I could get the execution policy for the ps1 file to be localmachine = Bypass then I believe the Import-Module step would work. Any ideas on where the RemoteSigned is coming from and how it can be changed to Bypass? Another thought is that maybe the ps1 file is not being run as an administrator even though the Winbatch program is run as an administrator.

td

I have no idea if your execution policy hypothesis is correct and causing your problem but according to MSFT documentation, the  "RemoteSigned" execution policy only applies to PS scripts downloaded from "the internet which includes email and instant messaging programs."  Otherwise, it has no effect.  "RemoteSigned" is the default on Windows Servers.  Also, note that MSFT documentation states that "localMachine" policy has the lowest priority so any other policy will override it.

There are multiple PowerShell acolytes that inhabit this forum so perhaps one will respond with better advice.

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

ChuckC

All references to "Downloaded from the Internet..." simply mean that there's a Zone Identifier stream attached to the file.  Remove the named stream from the file by whatever means is available to you, such as the DEL command in a CMD.EXE console window, or via the Windows Explorer where you can do so from the properties panel for the script file.

That's all that is used to flag the file as downloaded.  Once it is removed and as long as the execution policy allows the execution of local scripts, then things should work OK, barring any other subsequent impediments being encountered...

Also, execution policy can be set dynamically [and non-persistently] for the current powershell process as well as persistently in the registry under the HCU and HKLM registry hives.  Furthermore, you can specify command line switches for PowerShell.exe that override the persistent execution policy values stored in the registry.  Given that when WinBatch is invoking PowerShell it is doing it via a COM interface and it is invoked as part of the current WinBatch script process, that can result in different behavior from starting PowerShell.exe in its own process with command line switches setting the execution policy.

Look specifically under both the HKCU and HKLM hives for a value named "ExecutionPolicy" under the key "\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell".  You might find missing [default] or different explicitly assigned values there that don't match up with your expectations.

Also, when attempting to persistently set the execution policy from within WinBatch, make sure that you are running PowerShell with the appropriate user account and elevation under UAC to ensure that you have the necessary privileges required to modify the registry under the HKLM hive.

kenatureg

Thanks for the responses. I have checked the registry and the only entry is Bypass in HKLM. There is nothing in the HKCU for the admin user. The other entries in the execution policy list are set to Undefined so the local machine policy should be the one used. The COM interface angle is probably worth looking into. Also I think I will put some code in the ps1 file to indicate whether it is being run as an admin or not.

stanl

The good thing is you can test the .ps1 by running from the command line or the ISE.

td

Quote from: kenatureg on July 16, 2020, 03:23:38 PM
Thanks for the responses. I have checked the registry and the only entry is Bypass in HKLM. There is nothing in the HKCU for the admin user. The other entries in the execution policy list are set to Undefined so the local machine policy should be the one used. The COM interface angle is probably worth looking into. Also I think I will put some code in the ps1 file to indicate whether it is being run as an admin or not.

The change in execution policy is likely just a default used by the CLR when the System.Management.Automation.PowerShell class object is loaded by CLR hosting. But I don't think that you have demonstrated that the execution policy is causing your problem.  You would need to change it via your PS script to show that.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Powershell has an $Error variable - $Error[0].Exception.StackTrace might help.

kenatureg

I added #Requires -RunAsAdministrator to the beginning of the script. The script still runs. I added Set-ExecutionPolicy -Bypass -Force to the script. That seems to have run as well but the script still will not work properly. I then added  a couple of steps to add a registry entry to HKLM. Those do not work even though they work if done via the Powershell prompt. It appears that anything in the script that requires administrator does not work even though it appears that the script is running as an administrator.
I will try to do some error recording as suggested by stanl.

td

What indicator are you using to judge that your script appears to be running as an elevated administrator?
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

kenatureg

The line #Requires -RunAsAdministrator means it must be elevated or the script won't run. At least according to the following. We are at Powershell version 5.1.
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_requires?view=powershell-7

td

If your script is required to run with elevated rights and it is executing but not able to perform tasks that require elevated rights then either #Requires -RunAsAdministrator is not functioning as documented in this case or permissions/rights are not the issue.  That is why I asked how you are verifying that the PS is actually running as an elevated admin.  If you can confirm that it is by another method like using Systeminternals Process Explorer, you can narrow down the cause of your problem.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

kenatureg

Well I am feeling slightly less than brilliant at the moment. :-[
I put try/catch around the import-module to see if it showed a problem. Indeed it did. The module I was trying to import requires a 64 bit environment and the program was compiled as 32 bit (as are most of our programs). This explains why the script ran in the Powershell prompt but not from within the program. The program was re-compiled as 64 bit and the ps1 file ran fine. I will take this as a learning experience and move on with my life.
Thanks for the responses.

td

64-bit vs 32-bit related problems are one of the more common gotchas for WinBatch users.  I should have suggested that as a possible cause. At least you found the solution despite the rabbit trails.  You can pat your self on the back for figuring it out.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

ChuckC

And the other lesson to be retained is that the multitude of environments in which PowerShell code can be executed have varying degrees of informativeness w/respect to the display of exceptions.  Between PowerShell.exe, PowerShell_ISE.exe, the .Net Core based pwsh.exe and the COM-based method of creating a captive instance of PowerShell, you sometimes get more information and sometimes get less information.  The lack of a console to which exceptions can be logged when using PowerShell via COM definitely reduces the visibility of such exceptions in a script.

td

WinBatch catches many CLR based exceptions.  That is the text you see when you click on the More Error Info button of the WinBatch (actually WIL) error message box. Whether or not PowerShell passes exception up the stack to the CLR and therefore WIL, I couldn't say.  If you are perchance referring to passing information to stdout or stderr, obviously WinBatch doesn't know about them because it is a GUI application.  Of course, WinBatch Console does but it doesn't like sharing.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade