Invoke-Expression Not Running Command - powershell

I am pulling in a text file that was previously created that simply lists the KB numbers for all currently installed Windows Updates. I am trying to pull this file in and call wusa.exe and pass it the KB number to perform an uninstallation.
$a = Get-Content c:\hotfixid.txt
foreach ($kb in $a) {
$command = 'cmd /c wusa.exe /uninstall /kb:' + $kb.Trim().Substring(2) + ' /quiet /norestart'
#Write-Host $command
Write-Host "Currently uninstalling $kb"
Invoke-Expression -Command:$command
}
If I use
Write-Host $command
and copy that directly to a run dialog box in Windows, it completes successfully.
What happens when I run it in a PowerShell script is that it only outputs the Write-Host portions one after the other in about 2 seconds. I do not see any command windows opening and I don't see it actually DOING anything. I am running the PowerShell script 'As Administrator' with an unrestricted execution policy. I have also tried adding 'runas' to the $command to call the CMD window each time with administrative privileges and it made no difference. Calling it via Invoke-Command as well makes no difference.

PowerShell can run most commands directly w/o too much trouble.
Using Invoke-Expression just complicates matters, as does Invoke-Command or Start-Process, because you need to get the quoting right, and pass the arguments in a slightly unnatural way.
You can even skip running cmd.exe most of the time.
Try the following:
wusa.exe /uninstall "/kb:$($kb.Trim().Substring(2))" /quiet /norestart

Related

Invoke-Expression to Launch .ps1 scripts

I am trying to make a service that scans a folder. When it detects certain files it renames them and then creates variables for the rest of the script. This all works fine, but I am trying to find a way to run this script so that the subsequent windows that it launches are minimized. I've tried the below, but the 2 scripts still launch a window each that flashes on the screen.
I have tried converting the file into an EXE and a service but then it refuses to work. Hoping for a quick one-liner if anyone can help?
Set-Location -Path C:\scripts\ICVT
while ($true) {
invoke-expression 'cmd /c start powershell -WindowStyle Minimized -Command {.\file_checker.ps1}';
Start-Sleep 5
invoke-expression 'cmd /c start powershell -WindowStyle Minimized -Command {.\kill_checker.ps1}';
Start-Sleep 5
}
Sounds like you need to use Start-Job to run the script in the background. Simply replace the Invoke-Expression with Start-Job:
Set-Location -Path C:\scripts\ICVT
while ($true) {
Start-Job -FilePath '.\file_checker.ps1}';
Start-Sleep 5
Start-Job -FilePath '.\kill_checker.ps1}';
Start-Sleep 5
}
It will also accept script blocks as well. See about_jobs for more information on running and managing background jobs.

Automating vmrun with PowerShell

I have a PowerShell 2.0 script that I use to clone a target vmware workstation guest using vmrun in a windows host environment.
The code to clone a virtual machine executes correctly.
I am now trying to expand on this script to automate more of the process, for example, to check to see if a virtual machine is running, vmrun list, and to stop the virtual machine, vmrun stop [pathToVMXfile] if running.
With a windows command prompt, when I run vmrun list, it returns the following:
Total running VMs: 2
D:\[path]\[name].vmx
E:\[path]\[name].vmx
When I attempt the following in PowerShell, nothing returns. Here is what I have attempted so far. I am expecting an array to return with the values I see when I run from a command prompt.
$vmwareRun = "C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe"
#Original test, also tried with single quotes and no quotes,
#also tried quoting the -filePath
Start-Process $vmwareRun -ArgumentList "list"
#This gives a handle to the process but no return value
$myProcess = Start-Process $vmwareRun -ArgumentList "list" -PassThru
#$t is empty
Start-Process $vmwareRun -ArgumentList "list" -OutVariable $t
#$t is empty, clone command requires the -T ws parameters
Start-Process $vmwareRun -ArgumentList "-T ws list" -OutVariable $t
#RedirectStandardOutput creates a file with the expected output, $t is empty
Start-Process -FilePath "$vmwareRun" -ArgumentList $argList -OutVariable $t -RedirectStandardError "C:\testError.log" -RedirectStandardOutput C:\testOut.log"
No matter what I attempt, I do not get any output. What am I missing?
Note: Vmrun command line documentation is found here: "https://www.vmware.com/support/developer/vix-api/vix112_vmrun_command.pdf"
Thanks.
I found a similar post here: Capturing standard out and error with Start-Process
For whatever reason the powershell solution doesn't send results of the process to the -OutVariable as I expected.
So instead I followed the steps on the page indicated to create a new $pinfo = New-Object System.Diagnostics.ProcessStartInfo.
I set the corresponding properties and then started the process using System.Diagnostics.Process and it allows me to output the results to a string variable.

Powershell Requires UAC, I need help translating this function to a BAT file for PSexec

$i=0;
$pnp = pnputil -e;$matched = [regex]::matches($pnp, ".......................................Lexmark International");
$split = $matched -split (".........inf");
$replace = $split -replace " Driver package provider : Lexmark International","";
$replace1 = $replace -replace " ","`n";
write-output $replace1;
foreach ($i in $replace1){;
$pnpdel = pnputil -f -d $i;$pnpdel;
};
Reg delete "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows x64\Drivers\Version-3\Lexmark Universal v2 XL" /f;
net stop spooler;
net start spooler;
$PrinterPath = "\\officechicprt5\111w-2w-bprn-07";
$net = new-Object -Com WScript.Network;
$net.AddWindowsPrinterConnection($PrinterPath)
I know it's not pretty, but it works every time I have tried it. In case you are curious, in our environment, Lexmark drivers corrupt frequently, which is actually a Microsoft issue. In the registry, Dependent Files is truncated, so the printer will never print, often forcing gibberish to the printer. The only way we have found to fix this is to remove the driver completely, and read our point and print driver. This script does that, but unfortunately requires UAC elevation. I have attempted a bat file to run alongside this:
#ECHO OFF
PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""' -Verb RunAs}"
timeout /t 10
But unfortunately it leaves the user with a confused expression and a UAC prompt. Is it possible to run this somehow through PSexec in a bat file? I do not want to run this by RDP'ing into hundreds of machines (been there, done that). I would prefer a repeatable process, this issue is a pandemic here.
Thanks again
You're overcomplicating things. Don't start PowerShell to start PowerShell with parameters. Just start PowerShell directly with parameters.
powershell.exe -NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1"" -Verb RunAs
If you need to run the PowerShell script with elevated privileges when your users are not members of the administrators group you should rather enable PS Remoting and run it via Invoke-Command on the remote hosts:
Invoke-Command -Computer 'hostA', 'hostB', ... -ScriptBlock {
# your PowerShell code here
}

PowerShell: Start-Process with wait option taking long time to return

I have the following script:
$mArgs = #('myProj.vcxproj', '/t:Clean,Build' ,('/p:configuration=DEBUG'+';platform=win32;OutDir=./'))
Start-Process msbuild.exe -ArgumentList $mArgs -RedirectStandardOutput $tempFile -wait
The above successfully builds myProj. However, it takes a really long time to return. When the above line is reached, I see the msbuild windows for about 2 minutes. Then, it closes. After that, it takes another 8 minutes for the process to complete. If I just run the above in a cmd window, it takes about 2 minutes for it to complete.
I tried starting the process cmd.exe and passing msbuild as a parameter, but got the same result.
I also tried Invoke-Expression and also got the same results.
Does anyone have a clue what can be causing this delay ?
Thanks in advance!
I am using PowerShell v4.0 Start-Process for running MSBuild on Win2012R2. Usually build time of my solution is 1 min, but on build server it is 16 min. It takes MSBuild 15 min to close all nodes and finally exit (16 min to print "Finished!!!").
PowerShell code:
$process = Start-Process -FilePath $fileName -ArgumentList $arguments
-WorkingDirectory $workingDir -NoNewWindow -PassThru -Wait
Write-Host "Finished!!!"
$exitCode = $process.ExitCode
MSBuild args:
$commandLine = "/nologo /p:Configuration=Release;Platform=x64 /maxcpucount:2"
+ " "+ $dir + "MySolution.sln"
After adding /nodeReuse:false to MSBuild command line the build time is back to normal (1min).
Here is the working code:
function PsStartProcess([string]$fileName, [array]$arguments, [string]$workingDir)
{
if (!$workingDir)
{
$workingDir = [System.IO.Directory]::GetCurrentDirectory()
}
$process = Start-Process -FilePath $fileName -ArgumentList $arguments -WorkingDirectory $workingDir -NoNewWindow -PassThru -Wait
Write-Host "Finished!!!"
$exitCode = $process.ExitCode
$process.Close()
return $exitCode
}
$exeFileName = "c:\Program Files (x86)\MSBuild\12.0\Bin\MSBuild.exe"
$commandLine = "/nologo /p:Configuration=Release;Platform=x64 /maxcpucount:2 /nodeReuse:false" + " "+ $dir + "MySolution.sln"
PsStartProcess $exeFileName $commandLine $binDir
I had same issue using devenv.exe to compile like this
Start-Process $devenv $args -Wait
That would take forever. I reformatted the code to this
$proc = Start-Process $devenv $args -PassThru
$proc.WaitForExit()
And it is flying. In my case it is compilation of multiple solutions (40+) in the loop, and in perf-mon I don't see many active MSBuild/Devenv processes. There are few of each but all "terminated". And memory is OK. So, good option.
Why are you using Start-Process to run MSBUILD? I run it directly within PowerShell e.g.:
C:\PS> msbuild myproj.vcxproj /t:clean`,build /p:configuration=DEBUG`;platform=win32`;OutDir=. > $tempfile
Just be sure to escape the characters that PowerShell would normally interpret like , and ;.
I ran into this problem today. What I found is that msbuild waits for VBCSCompiler.exe (located in the visual studio installation folder) to close. There is a config to this exe with a keepalive setting which defaults to 600 seconds. Setting this to 1 second will bypass this problem.
Just observed the script and found that the first Line of the script seems to have a missing Single Quote. Please try again with the missing quote and report if that helps?
On Windows 7 the process returns immediately after completion of the build, but on Windows 8 I have the same problem. If possible, I'll try to test with some other environments as well.

PowerShell - Start-Process and Cmdline Switches

I can run this fine:
$msbuild = "C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe"
start-process $msbuild -wait
But when I run this code (below) I get an error:
$msbuild = "C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe /v:q /nologo"
start-process $msbuild -wait
Is there a way I can pass parameters to MSBuild using start-process? I'm open to not using start-process, the only reason I used it was I needed to have the "command" as a variable.
When I have
C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe /v:q /nologo
on a line by itself, how does that get handled in Powershell?
Should I be using some kind of eval() kind of function instead?
you are going to want to separate your arguments into separate parameter
$msbuild = "C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe"
$arguments = "/v:q /nologo"
start-process $msbuild $arguments
Using explicit parameters, it would be:
$msbuild = 'C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe'
start-Process -FilePath $msbuild -ArgumentList '/v:q','/nologo'
EDIT: quotes.
Warning
If you run PowerShell from a cmd.exe window created by Powershell, the 2nd instance no longer waits for jobs to complete.
cmd> PowerShell
PS> Start-Process cmd.exe -Wait
Now from the new cmd window, run PowerShell again and within it start a 2nd cmd window:
cmd2> PowerShell
PS> Start-Process cmd.exe -Wait
PS>
The 2nd instance of PowerShell no longer honors the -Wait request and ALL background process/jobs return 'Completed' status even thou they are still running !
I discovered this when my C# Explorer program is used to open a cmd.exe window and PS is run from that window, it also ignores the -Wait request.
It appears that any PowerShell which is a 'win32 job' of cmd.exe fails to honor the wait request.
I ran into this with PowerShell version 3.0 on windows 7/x64
I've found using cmd works well as an alternative, especially when you need to pipe the output from the called application (espeically when it doesn't have built in logging, unlike msbuild)
cmd /C "$msbuild $args" >> $outputfile
Unless the OP is using PowerShell Community Extensions which does provide a Start-Process cmdlet along with a bunch of others. If this the case then Glennular's solution works a treat since it matches the positional parameters of pscx\start-process : -path (position 1) -arguments (positon 2).