Returning an error code from CMD to Powershell - powershell

I'm working on a Mssql install script and I want to get the results of a silent mssql installation. In my PowerShell script I run this command:
$result = (start cmd "/c D:\SQL2008R2\SQL2008R2\setup.exe /CONFIGURATIONFILE=sqlconfig.ini && exit 0 || exit 1")
Which should return 0 on fail and 1 on pass. Unfortunately, I don't get any output back. Any ideas?

An alternative to Start-Process is the more syntactically terse call operator &.
& cmd.exe /c 'ping.exe doesnotexist && exit 0 || exit 1'
The exit code will be contained in the built-in variable $LASTEXITCODE so:
Write-Host $LASTEXITCODE
This will contain the exit code of the program run so you don't necessary have to run it with CMD.exe you could just do:
& ping.exe doesnotexist ; Write-Host $LASTEXITCODE
Applied to your command line program:
& cmd.exe /c 'D:\SQL2008R2\SQL2008R2\setup.exe /CONFIGURATIONFILE=sqlconfig.ini && exit 0 || exit 1'
Or just:
& D:\SQL2008R2\SQL2008R2\setup.exe /CONFIGURATIONFILE=sqlconfig.ini
In both cases $LASTEXITCODE should be 0 for success, non-zero otherwise (if the external program was written correctly).

This is how you do it: start is actually an alias Start-Process, so you have to look at it's documentation, which is a lot different than cmd.exe's start. So you can do this:
(Start-Process -FilePath "cmd.exe /c ..." -Wait -Passthru).ExitCode
So easy!

Too late maybe but does this help?
start /WAIT cmd.exe /C "YOUR-COMMAND-HERE" & if errorlevel 1 echo 'error occurred'
you can also explicitly return an error code like this:
start /WAIT cmd.exe /C "YOUR-COMMAND-HERE & exit MY-ERROR-CODE" & if errorlevel 1 echo 'error occurred'

Related

Powershell command run on CMD with if condition

Example 1:
for /F "tokens=3 delims= " %%A in ('manage-bde -status %systemdrive% ^| findstr " Encryption Method:"') do (
if "%%A"=="AES" goto EncryptionCompleted
)
:EncryptionCompleted
Example 2:
for /F %%A in ('wmic /namespace:\\root\cimv2\security\microsofttpm path win32_tpm get IsEnabled_InitialValue ^| findstr "TRUE"') do (
if "%%A"=="TRUE" goto nextcheck
)
:nextcheck
Please help to find the below code as run on .bat to stop script execution.
The command is:
powershell.exe (Get-Tpm | Select -Property TpmReady).TpmReady -eq $False
then goto Failed
:Failed
Since you're only looking to act on a Boolean value, you can communicate that via the PowerShell process' exit code, with 0 corresponding to $true and 1 to $false, given that the widely observed convention is that exit code 0 signals success, whereas any nonzero exit code signals an error condition.
Boolean values in PowerShell can directly be converted to integers, which, however, performs the opposite mapping: [int] $true is 1 and [int] $false is 0.
Therefore, the logic must be reversed with -not before passing the Boolean to PowerShell's exit statement.
On the cmd.exe (batch-file) side, this allows you to act on the exit code with the || operator, which only executes the RHS in case of failure, i.e. if the LHS command reported a nonzero exit code (such as 1).
powershell.exe -noprofile -c "exit -not (Get-Tpm).TpmReady" || goto :FAILED
echo "TPM is ready."
exit /b 0
:FAILED
echo "TPM is NOT ready." >&2
exit /b 1
Note that I've added the following CLI parameters to the PowerShell call: -noprofile to potentially speed up execution, and -c (-Command) to explicitly signal that a command (piece of PowerShell code) is being passed.
A demo of doing everything in powershell.
(get-bitlockervolume $env:systemdrive).encryptionmethod
None
(get-ciminstance -namespace root\cimv2\security\microsofttpm win32_tpm).
IsEnabled_InitialValue
True
(get-tpm).TpmReady
True

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.

Powershell via batch file multiple statements

This code is very close to being finished, however I cannot get the last statement (psexec \\%PC% -i -d -s "\.exe) that I wish to run, to run only if the device pings.
The aim is, if a device pings then write to host that it pings and run the psexec command else write to host that it hasn't been able to ping.
Code:
#ECHO OFF
cls
Clear-Host
set /p PC=PC no?:
FOR %%i IN (
%PC%
) DO (
PowerShell -NoProfile -Command "If (Test-Connection %%i -Count 1 -Quiet) { Write-Host "%%i - successfully pinged" -F Green } (psexec \\%PC% -i -d -s "\.exe that i wish to run")
else { Write-Host "%%i FAILED" -F Red}"
)
pause
This is like Spanglish or something. Clear-Host isn't a command that cmd understands, and it'd be easier just to ping.
#echo off
setlocal
if "%~1"=="" (
set /P "PC=PC no? "
) else (
set "PC=%~1"
)
ping -n 1 %PC% | find /i "TTL=" >NUL 2>NUL && (
powershell "Write-Host '%PC% passed' -f Green"
psexec \\%PC% -i -d -s "\.exe that i wish to run"
) || (
powershell "Write-Host '%PC% failed' -f Red"
)
For what it's worth, if you don't need the output of the command that psexec runs, I generally find wmic process call create to be faster than psexec.
wmic /node:%PC% process call create "exe that you wish to run"
If you need to supply auth info, add the /user:username and /password:password switches before process call create.
There are several problems with your code above. When executing a PowerShell command within a batch script, you can't break the line very easily. You should also include your psexec command within the if code block, rather than after it as you have now. You can separate commands with your braced code block with semicolons if needed. And finally, you need to be careful mixing double quotes as part of the powershell command token with the double quotes within the command. It could be fixed so that powershell handles the conditional flow, but my solution above works just as well and is more readable I think.

Learn error with wget command using PowerShell & Batch File

I have a power shell script that invokes a .cmd file. Here the architecture looks like
-PowerShell File Code
$arguments='/savecred /profile /user:myDomain\myuser "cmd /c C:\Users\myuser\code.cmd"'
Start-Process cmd.exe $arguments -Wait
.cmd code file
Here the code invokes wget request to download a file
wget .....(command here)
My goal is to learn at PowerShell command prompt (after completing Start-Process command) whether the wget command executed successfully or some error like 401, 404 occurred during the execution. Here I am particular not interested in the type of error, just need to know whether error occurred or not.
Not sure if this is what you are asking but you could test none-zero return code of the last command using $? variable, which will be $false if return code is not zero.
Let's say you have a test.cmd file which simply returns 5:
exit 5
If you run it within PowerShell, you can look at the result of $?
if ($?) {"No error"} else {"some error"}
Here is a link for more info: http://blogs.technet.com/b/heyscriptingguy/archive/2011/05/12/powershell-error-handling-and-why-you-should-care.aspx
Using $? with Start-Process isn't going to work:
C:\PS> Start-Process cmd.exe -arg '/c exit 5'
C:\PS> $?
True
If you want to use Start-Process, you can go this route:
C:\PS> $p = Start-Process cmd.exe -arg '/c exit 5' -PassThru -Wait
C:\PS> $p.ExitCode
5
Or you could just invoke cmd.exe directly:
C:\PS> cmd /c exit 5
C:\PS> $LASTEXITCODE
5
In this last example you could use $? but I prefer $LastExitCode since some brain-damaged console apps return non-zero for success. Regarding calling cmd.exe and using $LASTEXITCODE see this ScriptingGuy blog post.
For a handy CheckLastExitCode function look through this blog post for an implementation of the function.

VBScript, Batch or PowerShell Script?

I'm trying to run various commands using psexec.exe from Windows Sysinternals. What I need is a simple script to read the output of those commands.
For example if everything went OK, then it returns a 0. If something went wrong, then it will spit out an error code.
How can it be done?
In PowerShell, you would use the $LastExitCode variable to test if psexec succeeded or not e.g.:
$results = psexec <some command on remote system>
if ($LastExitCode -ne 0) {
throw "PSExec failed with error code $LastExitCode"
}
return 0
In a batch file, you use the %ERRORLEVEL% variable, or the IF ERRORLEVEL n command. For example:
psexec \\host -i findstr.exe "test" c:\testfile
if errorlevel 1 (
echo A problem occurred
)
IF ERRORLEVEL checks whether the return value is the same or higher than the number you specify.
This is not the same as capturing the output of the command though. If you actually want the output, you need to include redirection to an output file on the command line:
psexec \\host -i cmd.exe /c findstr "test" c:\testfile ^> c:\output.txt
The ^ is necessary to escape the > character, or the redirection would happen locally instead of on the remote machine. The cmd.exe is necessary, because redirection is handled by cmd.