Close Powershell Child Session of my batch file wrapper - powershell

I have a problem to use a powershell script plugin, with my nagios agent (NRPE_NT.exe).
To solve this problem I've create underlying wrap batch file:
#ECHO OFF
SET SCRIPTPATH=%~d0%~p0check_process_mem.ps1
SET ARGS=%*
IF [%ARGS%] NEQ [] GOTO ESCAPE_ARGS
:POWERSHELL
PowerShell.exe -NoProfile -NonInteractive -NoLogo -ExecutionPolicy Unrestricted -Command "& { $ErrorActionPreference = 'Stop'; & '%SCRIPTPATH%' #args; EXIT $LASTEXITCODE }" %ARGS%
EXIT /B %ERRORLEVEL%
:ESCAPE_ARGS
SET ARGS=%ARGS:"=\"%
SET ARGS=%ARGS:`=``%
SET ARGS=%ARGS:'=`'%
SET ARGS=%ARGS:$=`$%
SET ARGS=%ARGS:{=`{%
SET ARGS=%ARGS:}=`}%
SET ARGS=%ARGS:(=`(%
SET ARGS=%ARGS:)=`)%
SET ARGS=%ARGS:,=`,%
SET ARGS=%ARGS:^%=%
GOTO POWERSHELL
Now, if I run it as dialog user, I haven't any problem, but if I run it with SYSTEM user, powershell session doesn't close, and I have to kill it, from tasklist.
What can I do, for solve this problem?
I can't update NRPE_NT.exe agent to NSClient+ (powershell compliance).

i've solve this problem with powershell plugin, without wrap batch file. I've declaire nrpe.cfg in this way:
command[check_X]=cmd /c echo C:\nrpe\libexec\check_process_mem.ps1 $ARG1$ -VM_w "$ARG2$" -VM_c "$ARG3$" ; exit $($LastExitCode) | powershell.exe -NoProfile -NonInteractive -NoLogo -ExecutionPolicy Unrestricted -command -
and now it works correctly. Exit code and output of script is correct.

Related

Call batch file with elevated privileges via PowerShell and retrieve exit code

My Windows Batch shall be started by the user without administrator privileges. At some step, it shall call itself with elevated privileges. I have learned that this is possible using the PowerShell's runas feature (batch.bat ⭢ PowerShell ⭢ batch.bat). This works like a charm.
Unfortunately, I am not able to receive the exit code from the elevated batch execution. I always get 1, although there is not any error message. I have no idea at which return the exit code gets lost, 1st (batch back to PowerShell) or 2nd (PowerShell back to batch).
I believe, I have tried all of the plenty suggested answers from similar questions, but apparently I am unable to get it going. I need advice.
MVE which should indicate that the elevated batch returns 0:
#echo off
echo param=%~1
openfiles /local >nul 2>&1
IF %ERRORLEVEL% EQU 0 (
echo elevated, exit 0
pause
exit 0
) ELSE (
echo not elevated. trying to elevate.
powershell start-process -wait -verb runas '%0' -argumentlist /foo
echo powershell returned %errorlevel%.
)
Nota bene (edited to eliminate misunderstanding): while the non-elevated call (by the user) does not require any parameter, the elevated call introduces an additional parameter '/foo'. This makes things worse for me because I did not find a solution to not lose this parameter. However, this appears to be a rather unusual use case.
To solve the argument problem, you could use
powershell start-process -wait -verb runas '%0' -argumentlist '/additional-arg %*'
The exit code problem:
The first problem is the line
echo powershell returned %errorlevel%.
This can't work, because it's inside a code block and %errorlevel% will be expanded even before powershell will be called and therefore it is always 1 - the result of openfiles /local ...
But even with delayed expansion, I got always 0, probably because it's the exitcode of the successful runas, that it was able to start your batch.
You could use a work around and store the exitcode in a temporary file
#echo off
setlocal EnableDelayedExpansion
echo param=%*
openfiles /local >nul 2>&1
IF %ERRORLEVEL% EQU 0 (
echo elevated, exit 13
pause
echo 13 > "%temp%\_exitcode.tmp"
rem *** using 14 here to show that it doesn't be stored in errorlevel
exit 14
) ELSE (
echo not elevated. trying to elevate.
powershell start-process -wait -verb runas '%0' -argumentlist '/additional-arg %*'
set /p _exitcode= < "%temp%\_exitcode.tmp"
del "%temp%\_exitcode.tmp"
echo powershell returned !_exitcode!, lvl !errorlevel!.
)
You aren't putting the PowerShell commands to execute in quotes, and you would do well to use the full path as well as include any arguments to the script. A generic way to invoke this, so it could be copied across scripts, your PowerShell invocation should look like so:
powershell -c "if([bool]'%*'){ Start-Process -Wait -Verb runas '%~dpnx0' -ArgumentList ('%*' -split '\s+') } else { Start-Process -Wait -Verb runas '%~dpnx0' }"
For your needs above, this could be simplified since you know you have arguments passed into the batch file to process:
powershell -c "Start-Process -Wait -Verb runas '%~dpnx0' -ArgumentList '/foo'
%~dpnx0 - Automatic batch variable, this is the full path to the current script, including the script name
%* - Automatic batch variable, this is all arguments passed into the script.
('%*' -split '\s'): This is a PowerShell expression takes the space-delimited %* variable and splits it on continuous whitespace, returning an array. For simplicity this does have a shortcoming in that it will split on spaces in between double quotes, but the regex can be tuned to account for that if needed.
This answer is worth a read for other automatic batch variables you may find use for in the future.

How To Pass in or Set Environment Variable for Powershell script?

I have a powershell script as part of Azure DevOps pipeline. I also want to run it from windows command line. How can I set them before calling them in windows? Perhaps in a cmd file? Note - these are temporary for a single execution and don't want to set windows environment variables.
I am accessing the environment variables like this in the powershell script:
$tfsAccessToken = If ($serverMode) {$env:SYSTEM_ACCESSTOKEN} else {"somethingelse"}
$tfsBuildDefinitionId = If ($serverMode) {$env:SYSTEM_DEFINITIONID} else {2}
SET command:
Display, set, or remove CMD environment variables. Changes made
with SET will remain only for the duration of the current CMD
session.
Example.
Supposedly, the _foo environment variable isn't defined in a newly opened CMD prompt:
set _foo
Environment variable _foo not defined
powershell -nologo -noprofile -command "& {$env:_foo ; $null -eq $env:_foo}"
True
However, if defined, the _foo environment variable is visible in the child process powershell:
set "_foo=bar"
powershell -nologo -noprofile -command "& {$env:_foo ; $null -eq $env:_foo}"
bar
False
Another example (asynchronous child process powershell):
set "_foo=bar"
start "SO_57237835" powershell -noexit -nologo -noprofile -command "& {$env:_foo;$null -eq $env:_foo}"

Run script using Task Scheduler with user context complete silent with no popup or cmd flash

I have tried to run the script using command
cmd.exe /c Start /min powershell.exe -windowstyle hidden -file <file>.ps1
But getting a CMD window for a fraction of a second. I need it to run completely hidden.
Configure the scheduled task to run whether the user is logged on or not:
and reduce the commandline to this:
powershell.exe -File "C:\path\to\your.ps1"
This makes the task run in the background with no visible window.
I have had this issue and the only way I could fix it was to call the PowerShell script with a simple VBS wrapper:
https://github.com/gbuktenica/PsRun
http://blog.buktenica.com/run-a-powershell-task-silently/
' SYNOPSIS
' Run a PowerShell script in the user context without a script window
' EXAMPLE
' wscript.exe PsRun.vbs MyPsScript.ps1
' AUTHOR
' Glen Buktenica
Set objShell = CreateObject("Wscript.Shell")
Set args = Wscript.Arguments
For Each arg In args
Dim PSRun
PSRun = "powershell.exe -WindowStyle hidden -ExecutionPolicy bypass -NonInteractive -File " & arg
objShell.Run(PSRun),0

Calling a powershell function using a .bat file

I would like to know how to call a powershell function using a .Bat file.
I have this simple function:
(Script.ps1)
function testfunction
{
Write-Log "Calling from Bat file"
}
and I would like to call the function testfunction within the .Bat File.
powershell .\Script.ps1 ...
I noticed that there is a switch -ExecutionPolicy ByPass which allows the batch file to run using Powershell. This works here, taken from the answer by vonPryz.
#echo off
:: Create a test script file
echo function test { write-host "Called from %~f0" } > s.ps1
powershell -ExecutionPolicy ByPass -command ". "%cd%\s.ps1"; test;"
del s.ps1
pause
Start Powershell with -command switch, include the script and call the function. You need to dot source the script before its function can be called.
:: Create a test script file
C:\temp>echo function test { write-host "Called from .bat" } > c:\temp\s.ps1
C:\temp>powershell -command ". c:\temp\s.ps1; test;"
:: Output
Called from .bat
Also, take a look at some samples.
I know this is very old but I was recently trying to do this same thing and it took me a long time to find out what to do. The answer is amazingly simple
In BatchFile (RunMe)
powershell -command ". Script.ps1; Set-SomeFunction %1 %2 %3 %4 %5"
Then you can call the batch like
RunMe -SomeParm val -SomeParm2 val -SomeSwitch -SomeBool $true
also, to run as admin
powershell -command "Start-Process -verb runas powershell" "'-noexit -command ". Script.ps1; Set-SomeFunction %1 %2 %3 %4 %5 %6 %7 %8 %9'"
I usually do it like that:
powershell {Set-ExecutionPolicy unrestricted}
powershell.exe -executionpolicy ByPass ./script/powerhsell-script.ps1 %par1% %par2%
powershell {Set-ExecutionPolicy Restricted}
Hope you need that :)
I've done it using
powershell.exe -ExecutionPolicy Bypass -file ".\Write_date.ps1"
Seems similar to what others have written but I'm not sure what vonPryz meant by needing the dot source the script before calling the script. The above worked on a Win7 with executionpolicy set to restricted.

Calling Powershell with WshShell.Exec() method hangs the script

I am trying to call Powershell with the Exec method of the WshShell object. I am writing the script in JScript, but I have reproduced the problem in VBScript as well. Both of the following short test scripts will cause WSH to hang indefinitely:
test.js
var shell = new ActiveXObject("WScript.Shell");
WScript.Echo(shell.exec("powershell -Command $Host.Version; Exit").StdOut.ReadAll());
test.vbs
dim shell
set shell = CreateObject("WScript.Shell")
WScript.Echo shell.exec("powershell -Command $Host.Version; Exit").StdOut.ReadAll
Am I doing something wrong, or am I running into or a limitation/incompatibility? The Run method works very well, but I need to capture output, which it's not capable of doing.
Edit: I forgot to mention that my platform is Windows 7 Pro, 64-bit with PowerShell 3. I've tested on Windows XP with PowerShell 1 as well.
Edit 2: I've updated the test scripts that I'm running to fit with x0n's answer. Unfortunately, I'm still having trouble. Here are my current tests:
test.js:
var shell = new ActiveXObject("WScript.Shell");
WScript.Echo(shell.exec('powershell -noninteractive -noprofile -Command "& { echo Hello_World ; Exit }"').StdOut.ReadAll());
test.vbs:
dim shell
set shell = CreateObject("WScript.Shell")
WScript.Echo shell.exec("powershell -noninteractive -noprofile -Command ""& { echo Hello_World ; Exit }""").StdOut.ReadAll
You have to close StdIn:
var shell = new ActiveXObject("WScript.Shell");
var exec = shell.Exec('powershell -noninteractive -noprofile -Command "& { echo Hello_World ; Exit }"');
exec.StdIn.Close();
WScript.Echo(exec.StdOut.ReadAll());
Microsoft said:
StdIn is still open so PowerShell is waiting for input. (This is an
"implementation consideration" that we're hoping to fix in V2. The
PowerShell executable gathers all input before processing.) So
objexec.StdIn.Close() needs to be added.
Use:
powershell.exe -noninteractive -noprofile -command $host.version
as your string. For more complicated groups of commands, use this syntax:
powershell.exe -noninteractive -noprofile -command "& { $host.version; $host.version }"