Python cant get output from PowerShell - powershell

Im trying to use this code to get a list of Actions in the SCCM pushed into python, but all I get in retun is an empty bytes string.
count = r"""
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
$arguments = "& '" +$myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break
}
$CMGR = New-Object -ComObject CPApplet.CPAppletMgr
$ClientActions = Out-String ($CMGR.GetClientActions() | Select Name).count
$ClientActions
"""
def runPS(command):
import subprocess
process=subprocess.run(["powershell", "-ExecutionPolicy", "Bypass", command],stdout=subprocess.PIPE);
print(process.stdout)
All this outputs is b''
What am I doing wrong???
EDIT: I figured part of it out. Its because it's tecnically running another powershell within this one (Python >> Powershell >> Admin Powershell), and the second one doesn't return its value to the first. (Python <> Powershell >> Admin Powershell)

I would suggest using check_output() instead of run() here. That allows you to detect a non-zero exit code from the child process. More information here. The Powershell might be failing for some reason, but you're not capturing stderr or checking the return code.
Something like this might be more robust:
def runPS(command):
import subprocess
output = subprocess.check_output(["powershell", "-ExecutionPolicy", "Bypass", command])
return output
EDIT: I figured part of it out. Its because it's tecnically running another powershell within this one (Python >> Powershell >> Admin Powershell), and the second one doesn't return its value to the first. (Python <> Powershell >> Admin Powershell)
Hmm. Sorry, I don't know powershell, so I can't help you with that half of it.

Related

Trying to run a headless executable command through Powershell that works on cmd line

I am trying to run an executable through powershell to run headless, to install a program onto a VM/LocalHost machine. I can get the wizard to open, but for whatever reason I cannot get it to run headless. Here is the cmd line that I run that works:
start /WAIT setup.exe /clone_wait /S /v" /qn"
This is my attempts in powershell
Start-Process .\setup.exe /S -Wait -PassThru
Start-Process .\setup.exe /S /v /qn -Wait -PassThru
Start-Process setup.exe -ArgumentList '/clone_wait /S /v /qn' -Wait
In the cmd line instance the application installs without issue - in the powershell instance the wizard opens and is on the first "Next" prompt. Any help would be appreciated!
I also attempted to add the additional parameters "/v" and "/qn" which return an error : Start-Process : A positional parameter cannot be found that accepts argument '/v'
The bottom attempt runs but it's not waiting for the installation to complete
You may be overthinking it. Remember that PowerShell is a shell. One of the purposes of a shell is to run commands that you type.
Thus: You don't need Start-Process. Just type the command to run and press Enter.
PS C:\> .\setup.exe /clone_wait /S /v /qn
Now if the executable (or script) you want to run contains spaces in the path or name, then use the call/invocation operator (&) and specify the quotes; for example:
PS C:\> & "\package files\setup.exe" /clone_wait /S /v /qn
(This behavior is the same no matter whether you are at the PowerShell prompt or if you put the command in a script.)
This worked for me. You need to quote the whole argumentlist, plus embed double quotes to pass what you want to /v.
start-process -wait SetupStata16.exe -ArgumentList '/s /v"/qb ADDLOCAL=core,StataMP64"'
Running the command normally and then using wait-process after might be a simpler alternative, if you're sure there's only one process with that name:
notepad
wait-process notepad
To follow-up to all that you have been given thus far. Running executables via PowerShell is a well-documented use case.
PowerShell: Running Executables
Solve Problems with External Command Lines in PowerShell
Top 5 tips for running external commands in Powershell
Using Windows PowerShell to run old command-line tools (and their
weirdest parameters)
So, from the first link provides more validation of what you've been given.
5. The Call Operator &
Why: Used to treat a string as a SINGLE command. Useful for dealing with spaces.
In PowerShell V2.0, if you are running 7z.exe (7-Zip.exe) or another command that starts with a number, you have to use the command invocation operator &.
The PowerShell V3.0 parser do it now smarter, in this case you don’t need the & anymore.
Details: Runs a command, script, or script block. The call operator, also known as the "invocation operator," lets you run commands that are stored in variables and represented by strings. Because the call operator does not parse the command, it cannot interpret command parameters
Example:
& 'C:\Program Files\Windows Media Player\wmplayer.exe' "c:\videos\my home video.avi" /fullscreen
Things can get tricky when an external command has a lot of parameters or there are spaces in the arguments or paths!
With spaces you have to nest Quotation marks and the result it is not always clear!
In this case it is better to separate everything like so:
$CMD = 'SuperApp.exe'
$arg1 = 'filename1'
$arg2 = '-someswitch'
$arg3 = 'C:\documents and settings\user\desktop\some other file.txt'
$arg4 = '-yetanotherswitch'
& $CMD $arg1 $arg2 $arg3 $arg4
# or same like that:
$AllArgs = #('filename1', '-someswitch', 'C:\documents and settings\user\desktop\some other file.txt', '-yetanotherswitch')
& 'SuperApp.exe' $AllArgs
6. cmd /c - Using the old cmd shell
** This method should no longer be used with V3
Why: Bypasses PowerShell and runs the command from a cmd shell. Often times used with a DIR which runs faster in the cmd shell than in PowerShell (NOTE: This was an issue with PowerShell v2 and its use of .Net 2.0, this is not an issue with V3).
Details: Opens a CMD prompt from within powershell and then executes the command and returns the text of that command. The /c tells CMD that it should terminate after the command has completed. There is little to no reason to use this with V3.
Example:
#runs DIR from a cmd shell, DIR in PowerShell is an alias to GCI. This will return the directory listing as a string but returns much faster than a GCI
cmd /c dir c:\windows
7. Start-Process (start/saps)
Why: Starts a process and returns the .Net process object Jump if -PassThru is provided. It also allows you to control the environment in which the process is started (user profile, output redirection etc). You can also use the Verb parameter (right click on a file, that list of actions) so that you can, for example, play a wav file.
Details: Executes a program returning the process object of the application. Allows you to control the action on a file (verb mentioned above) and control the environment in which the app is run. You also have the ability to wait on the process to end. You can also subscribe to the processes Exited event.
Example:
#starts a process, waits for it to finish and then checks the exit code.
$p = Start-Process ping -ArgumentList "invalidhost" -wait -NoNewWindow -PassThru
$p.HasExited
$p.ExitCode
#to find available Verbs use the following code.
$startExe = new-object System.Diagnostics.ProcessStartInfo -args PowerShell.exe
$startExe.verbs

Powershell Invoke-Expressions pauses

I wrote a Powershell script that uses Steam's command line tool to login and check for updates for a community server I am running. See below:
$steamcmdFolder = 'C:\download\steam'
$steamcmdExec = $steamcmdFolder+"\steamcmd.exe"
$forceinstall = 'force_install_dir'+$steamcmdFolder
$appupdate = 'app_update 258550'
$cmdOutput = "$steamcmdExec +login anonymous"
do {
Write-Host Checking for an update....
Invoke-Expression $cmdOutput
Invoke-expression $forceinstall
Invoke-expression $appupdate
}
while ($Update = 1)
The Invoke-Expression lines are individual command-line statements I want executed in the order I have them. For some reason, the first Invoke-Expression works fine but the others do not -- everything just stops. I can type in the value of $forceinstall on the PowerShell command-line and it works as expected. But why can't I do this using PowerShell? Any suggestions are welcome!
If you convert the other two lines down to what they are, it seems like they are not real commands.
#Invoke-expression $forceinstall
Invoke-Expression "force_install_dirC:\download\steam"
#Invoke-expression $appupdate
Invoke-Expression "app_update 258550"
Looking into the SteamCMD documents, it appears that you might want to change it to be a single line command.
Invoke-Expression "steamcmd +login anonymous +force_install_dir C:\download\steam +app_update 258550 +quit"

parse error in one-line powershell script

I am trying to create a one-line powershell script that just requests an url. The script is working fine when I run it as a ps1 file:
File "test.ps1":
$webclient=New-Object "System.Net.WebClient"
$data=$webclient.DownloadString("https://google.com")
I run this script in PS console like this:
PS C:\test.ps1 -ExecutionPolicy unrestricted
This runs without any problem, but when I try to schedule this script and make it a one-line according to these recommendations i.e. replace "" with '' and separate commands with ; so the result will be:
one-line:
powershell -ExecutionPolicy unrestricted -Command "$webclient=New-Object 'System.Net.WebClient'; $data=$webclient.DownloadString('https://google.com');"
Then I got the following problem:
Error:
The term '=New-Object' is not recognized as the name of a cmdlet,
function, script file, or operable program
I tried another script that also works fine as ps1 file, but not working as one-liner:
$request = [System.Net.WebRequest]::Create("https://google.com")
$request.Method = "GET"
[System.Net.WebResponse]$response = $request.GetResponse()
echo $response
one-line:
powershell -ExecutionPolicy unrestricted -Command "$request = [System.Net.WebRequest]::Create('https://google.com'); $request.Method = 'GET'; [System.Net.WebResponse]$response = $request.GetResponse(); echo $response"
Error:
Invalid assignment expression. The left hand side of an assignment
operator needs to be something that can be assigned to like a variable
or a property. At line:1 char:102
According to get-host command I have powershell v 2.0. What is the problem with one-line scripts above?
Put the statements you want to run in a scriptblock and run that scriptblock via the call operator:
powershell.exe -Command "&{$webclient = ...}"
Note that pasting this commandline into a PowerShell console will produce a misleading error, because PowerShell (the one into which you paste the commandline) expands the (undefined) variables in the string to null values, which are then auto-converted to empty strings. If you want to test a commandline like this, run it from CMD, not PowerShell.
It might also be a good idea to have the scriptblock exit with a status code, e.g.
&{...; exit [int](-not $?)}
or
&{...; $status=$response.StatusCode.value__; if ($status -eq 200) {exit 0} else {exit $status}}

Executing powershell.exe from powershell script (run in ISE but not in script)

I'm new to these awesome Power shell world. I have a problem with script and really apreciate your help.
I have a script "cmd4.ps1" that needs to run another script "Transfer.ps1" that needs to receive 3 strings params and it needs to be run as other process thead different to "cmd4.ps1".
cmd4.ps1:
$Script="-File """+$LocalDir+"\Remote\Transfer.ps1"" http://"+$ServerIP+"/Upload/"+$FileName+" "+$ServerIP+" "+$LocalIP
Start-Process powershell.exe -ArgumentList $Script
After ejecution, the $Script cointain a value similar to
-File "c:\temp re\Remote\Transfer.ps1" http://10.1.1.1/Upload/file.txt 10.1.1.1 10.1.1.10
containing the syntax to use -File parameter to run a script of Powershell.exe, and three parameters that Transfer.ps1 needs ("http://10.1.1.1/Upload/file.txt", 10.1.1.1, 10.1.1.10).
When I write these instructions in PowerShell ISE I can see every values are right and PowerShell.exe is executed with right values, everything work fine!, but if I put these instructions inside "cmd4.ps1" script it doesn't work, I mean something is not right with parameters because I can see it start powershell but it never ends.
-ArgumentList is expecting an array of string arguments instead of a single string. Try this instead:
$ScriptArgs = #(
'-File'
"$LocalDir\Remote\Transfer.ps1"
"http://$ServerIP/Upload/$FileName $ServerIP $LocalIP"
)
Start-Process powershell.exe -ArgumentList $ScriptArgs
Note that you can simplify the string construction of the args as shown above.
Why don't you put this in cmd4.ps1?
& "c:\temp re\Remote\Transfer.ps1" "http://10.1.1.1/Upload/file.txt" "10.1.1.1" "10.1.1.10"

Can't redirect PowerShell output when -EncodedCommand used

For my tests I am using 'Start > Run' dialog (not the cmd.exe).
This works fine, and I get 9 in log.txt
powershell -Command 4+5 > c:\log.txt
But this does not work:
powershell -EncodedCommand IAA1ACsANwAgAA== > c:\log.txt
So how can I redirect output in this case?
Experimental code:
function Test
{
$cmd = { 5+7 }
$encodedCommand = EncodeCommand $cmd
StartProcess "powershell -Command $cmd > c:\log.txt"
StartProcess "powershell -EncodedCommand $encodedCommand > c:\log2.txt"
}
function StartProcess($commandLine)
{
Invoke-WMIMethod -path win32_process -name create -argumentList $commandLine
}
function EncodeCommand($expression)
{
$commandBytes = [System.Text.Encoding]::Unicode.GetBytes($expression)
[Convert]::ToBase64String($commandBytes)
}
The "Run" dialog doesn't seem like it provides redirection at all. According to this usenet post you only get redirection if you have a console. I wonder if the redirection parameter is being parsed by powershell.exe, which is choosing to redirect if it's not receiving encoded input? Sounds like a question for Raymond Chen.
Anyway, this works, at the expense of spawning an otherwise useless console:
cmd /c powershell -EncodedCommand IAA1ACsANwAgAA== > c:\ps.txt
The difference between those two commands is that the -Command parameter is greedy. It takes everything on the command line after it, while -EncodedCommand is not greedy. What the first command is really doing is:
powershell -Command "4+5 > c:\log.txt"
So the new PowerShell instance is handling the redirection. However, if you use the -EncodedCommand paramter, the new PowerShell instance does not see the redirection because you did not include it in the encoded command. This can be a bad thing if the environment calling PowerShell does not have redirection (like in a scheduled task).
So, as "crb" showed, you need to either encode the redirection into your command, or call PowerShell from an environment that can handle the redirection (like cmd, or another PowerShell instance).
I had to encode command together with redirection.
function Test
{
$cmd = { 5+7 }
$encodedCommand = EncodeCommand "$cmd > 'c:\log2.log'"
StartProcess "powershell -Command $cmd > c:\log.txt"
StartProcess "powershell -EncodedCommand $encodedCommand"
}
So this will write a sum 5+7 into c:\log2.log
powershell -EncodedCommand IAA1ACsANwAgACAAPgAgACcAYwA6AFwAbABvAGcAMgAuAGwAbwBnACcA
P.S.
crb suggested to use "cmd /c". But in this case the encoded script length will be constrained by the command line limitations
On computers running Microsoft Windows
XP or later, the maximum length of the
string that you can use at the command
prompt is 8191 characters. On
computers running Microsoft Windows
2000 or Windows NT 4.0, the maximum
length of the string that you can use
at the command prompt is 2047
characters.