powershell loop for periodical checking running process - powershell

I'm trying to create a PowerShell script (loop) for periodical checking if some other PowerShell script with specific command is running. If not I start it.
This is what I have:
$processInfo = Get-WmiObject Win32_Process -Filter "name = 'powershell.exe'" | select CommandLine | Out-String -width 200
while($true) {
if ($processInfo -NotLike '*specific_path*') {
Write-Host 'process not running'
Start-Process powershell -argument '-f X:\specific_path\other_script.ps1'
}
else { Write-Host 'process is running' }
Start-Sleep -Seconds 60
}
The script correctly detects running or not running process when started. but when situation changes during the script run, it does not detect it.
Also, when script starts, detects that the other script is not running, it starts it correctly but then it does not see it already running and starts it again and again.
So the only problem I have (I believe) is how to get "fresh" data about running processes. Any ideas? Many thanks!

Related

How do you log the results of remotely invoked commands on the system running the PowerShell session?

I have a script that loops through a list of my servers, and runs various commands on them.
I am attempting to log the commands and their results for both the computer running the PowerShell script, and the remote computer running the commands.
Right now, I am trying to use the PSLogging module with the Start-Log cmdlet. I am able to log the commands run on the system running the Powershell script, but things run on the remote computer are not showing up.
My code looks like this.
Import-Module -Name "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\PSLogging\2.5.2\PSLogging.psm1"
$Date = Get-Date -Format "MM-dd-yyyy hh-mm tt"
$LogFileOutput = "Output Log"+" $Date"+".log"
$LogFilePlusPath = "\\files\Logs\"+"$LogFileOutput"
Start-Log -LogPath "\\files\Logs\" -LogName "LogFileOutput" -ScriptVersion "1.0"
Servers = #("Server1","Server2")
ForEach ($Server in $Servers)
{
Write-Host "Running script on $Server"
param([string]$Server)
Invoke-Command -Computer $Server -ScriptBlock {
Write-Host "Stopping IIS App Pool on $Server."
c:\windows\system32\inetsrv\appcmd.exe stop apppool "IISAppPool"
Write-Host "Switching Service to manual on $Server and stopping it."
Stop-Service Service -Force
Set-Service Service-StartupType Manual
} -args $Server
}
Write-Host "Script complete at $(Get-Date -Format "hh:mm tt MM/dd/yyyy") Logs available at \\files\Logs"
Stop-Log
The script works, and the services and App pools perform their operation as desired. The results just aren't being logged.
The only thing in the log would be:
Running script on Server1
Running script on Server2
Script complete at 12:20 PM 09/19/2020 Logs available at \files\Logs
Any suggestions on the best way to capture everything?
Thank you!

Task Scheduler can't kill a process properly using PowerShell script

I want to kill a process using Task Scheduler and PowerShell script.
When a specific process starts, task scheduler triggers the PS script. The script gets the process Id and tries to stop it.
My issue is, the script can't kill the process until the process finishes its job. However, I want to kill the process as soon as the script triggers, without waitingfor anything. As a note, the process I want to kill also runs with Admin privileges, and runs in window mode(not in background)
Scheduled Task settings: running as SYSTEM with highest privileges. I also used executionPloicyBypass parameter as below:
powershell -ExecutionPolicy Bypass -File C:\Scripts\KillProcess.ps1.
In the script, I have the following code basically
$process = Get-Process -Id $pid
$process.PriorityClass = 'High'
$events=Get-WinEvent -FilterHashtable #{LogName="Security"; Id = <eventId>; StartTime = [datetime]::Now.AddMinutes(-5)} |Where-Object -Property Message -Match '<string to search for>' | Where-Object -Property Message -Match ('<string to search for>') -ErrorAction Stop
if (!$events[0].message) {
Exit
}
else {
$processes = #()
#for each event, get process Id and kill it.
#this is because the process can spawn multiple process.
foreach ($event in $events) {
#parse the process Id.-*
$processId=[int][regex]::Match($event.message,'Process\sID\:\s+(0x.+)\s').captures.groups[1].Value
$processes += $processId
}
$processes = $processes | Select -Unique
foreach ($proc in $processes) {
Stop-Process -Id $proc -Force -ErrorAction SilentlyContinue
}
}
When I run PowerShell ISE as Admin and run the script there manually, it immediately kills the process. However, it waits for the process to finish its job when task scheduler triggers the script. Am i doing something wrong with the Task scheduler?
I don't know what was the issue with Stop-Process but I changed it to process.Kill() method by getting the process object using Get-Process -Id $proc first. Now it works without issue.

Powershell build step, fire and forget?

I am running the following powershell command in a build step using TFS 2018.
Start-Job -ScriptBlock {
Invoke-Command -FilePath \\MyServer\run.ps1 -ComputerName MyServer -ArgumentList arg1, arg2
}
Since I don't want the script to affect the build step it should simply fire and forget the script. Hence I am using Start-Job. But it seems that once the step is done the process is killed. Is there a way to maintain the process lifetime even though the build step is done or the build process is finished?
Additional information... the powershell script should run on the remote server. The script itself triggers an .exe with parameters.
To simply fire and forget, invoke the script with Invoke-Command -AsJob:
Invoke-Command -AsJob -FilePath \\MyServer\run.ps1 -ComputerName MyServer -Args arg1, arg2
Start-Sleep 1 # !! Seemingly, this is necessary, as #doorman has discovered.
This should kick off the script remotely, asynchronously, with a job getting created in the local session to monitor its execution.
Caveat: The use of Start-Sleep - possibly with a longer wait time -
is seemingly necessary in order for the remote process to be created before the calling script exits, but such a solution may not be fully robust, as there is no guaranteed timing.
Since you're not planning to monitor the remote execution, the local session terminating - and along with it the monitoring job - should't matter.
When do you want the script to stop running? You could use a do-while loop and come up with a <condition> that meets your needs.
Start-Job -ScriptBlock {
do{
Invoke-Command -FilePath \\MyServer\run.ps1 -ComputerName MyServer -ArgumentList arg1, arg2
Start-Sleep 2
}while(<condition>)
}
Alternatively, you could use the condition $true so it executes forever. You will have to stop the job later in the script when you no longer need it.
$job = Start-Job -ScriptBlock {
do{
Invoke-Command -FilePath \\MyServer\run.ps1 -ComputerName MyServer -ArgumentList arg1, arg2
Start-Sleep 2
}while($true)
}
Stop-Job $job
Remove-Job $job
I've added a Start-Sleep 2 so it doesn't lock up your CPU as no idea what the script is doing - remove if not required.
Why not something like this:
Invoke-Command -Filepath \\MyServer\Run.ps1 -Computername MyServer -Argumentlist Arg1,Arg2 -AsJob
$JobCount = (get-job).Count
Do
{
Start-Sleep -Seconds 1
$totalJobCompleted = (get-job | Where-Object {$_.state -eq "Completed"} | Where-Object {$_.Command -like "NAMEOFCOMMAND*"}).count
}
Until($totalJobCompleted -ge $JobCount)
#doorman -
PowerShell is natively a single threaded application. In almost all cases, this is a huge benefit. Even forcing multiple threads, you can see the child threads are always dependent on the main thread. If this wasn't the case, it would be very easy to create memory leaks. This is almost always a good thing as when you close the main thread, .Net will clean up all the other threads you may have forgotten about for you. You just happened to run across a case where this behaviour is not beneficial to your situation.
There are a few ways to tackle the issue, but the easiest is probably to use the good ol' command prompt to launch an independent new instance not based at all on your original script. To do this, you can use invoke-expression in conjunction with 'cmd /c'. See Below:
invoke-expression 'cmd /c start powershell -NoProfile -windowstyle hidden -Command {
$i = 0
while ($true) {
if($i -gt 30) {
break
}
else {
$i | Out-File C:\Temp\IndependentSessionTest.txt -Append
Start-Sleep -Seconds 1
$i++
}
}
}
'
This will start a new session, run the script you want, not show a window and not use your powershell profile when the script gets run. You will be able to see that even if you kill the original PowerShell session, this one will keep running. You can verify this by looking at the IndependentSessionTest.txt file after you close the main powershell window and see that the file keeps getting updated numbers.
Hopefully this points you in the right direction.
Here's some source links:
PowerShell launch script in new instance
How to run a PowerShell script without displaying a window?

Task Scheduling and Powershell's Start-Job

Currently, I try to run a PowerShell-Script, which starts a few instances with Start-Job -name $jobname -Scriptblock { ... Code ...};
This PowerShell-Script should be executed every 15 minutes in the WIndows Task Schedule. I run this process with highest priviledges, and the Power-Shell-Script starts perfectly - the problem is:
The Code, which is executed by Start-Job doesn't work.
I think the problem is that the "Start-Job" can not work with the task schedule together.
Do you know how to solve that? Are there any settings to configure?
Thank you, SUT
If no any control at the end of script, Powershell will immediately exit once background job is created. So try to add below control code:
$runningJobs = Get-Job -State Running
while($runningJobs.Count -gt 0){
Start-Sleep -Seconds 1
$runningJobs = Get-Job -State Running
}
Write-Host "Done!" -ForegroundColor Red -BackgroundColor Black

How do I run a Windows installer and get a succeed/fail value in PowerShell?

I would like to install a set of applications: .NET 4, IIS 7 PowerShell snap-ins, ASP.NET MVC 3, etc. How do I get the applications to install and return a value that determines if the installation was successful or not?
These answers all seem either overly complicated or not complete enough. Running an installer in the PowerShell console has a few problems. An MSI is run in the Windows subsystem, so you can't just invoke them (Invoke-Expression or &). Some people claim to get those commands to work by piping to Out-Null or Out-Host, but I have not observed that to work.
The method that works for me is Start-Process with the silent installation parameters to msiexec.
$list =
#(
"/I `"$msi`"", # Install this MSI
"/QN", # Quietly, without a UI
"/L*V `"$ENV:TEMP\$name.log`"" # Verbose output to this log
)
Start-Process -FilePath "msiexec" -ArgumentList $list -Wait
You can get the exit code from the Start-Process command and inspect it for pass/fail values. (and here is the exit code reference)
$p = Start-Process -FilePath "msiexec" -ArgumentList $list -Wait -PassThru
if($p.ExitCode -ne 0)
{
throw "Installation process returned error code: $($p.ExitCode)"
}
Depends. MSIs can be installed using WMI. For exes and other methods, you can use Start-Process and check the Process ExitCode.
msi's can also be installed using msiexec.exe, msu's can be installed using wusa.exe, both have a /quiet switch, /norestart and /forcerestart switches and a /log option for logging (specify the file name).
You can read more about the options if you call them with /?
Note: wusa fails silently when they fail, so you have to check the log file or eventlog to determine success.
I have implemented exactly what you are looking for at my current project. We need to automate deployment and instillation of n number of apps across multiple environments and datacenters. These scripts are slightly modified from the original version for simplicity sake since my complete code is reaching 1000 lines but the core functionality is intact. I hope this does what you are asking for.
This PS function pulls all the apps from the registry (what add/remove programs reads from) and then search's for the supplied app name and display version. In my code (PSM1) I run this function before I install to whether or not it is their and then afterword’s to verify that it got installed…. All this can be wrapped in one master function to manager flow control.
function Confirm-AppInstall{
param($AppName,$AppVersion)
$Apps = Get-ItemProperty Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*|?{$_.DisplayName -ne $Null}|?{$_.DisplayName -ne ""}
$Apps += Get-ItemProperty Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*|?{$_.DisplayName -ne $Null}|?{$_.DisplayName -ne ""}
$Installed = $Apps|?{$_.DisplayName -eq ""}|?{$_.DisplayVersion -eq ""}|select -First 1
if($Installed -ne $null){return $true}else{return $false}
}
This PS function will load a txt file that has the install commands prepopulated (one command per line). and run each line indivaduly and wait for the install to complete before moving on to the next.
function Install-Application{
param($InstallList = "C:\Install_Apps_CMDS.txt")
$list = gc -Path $InstallList
foreach ($Command in $list){
Write-Output ("[{0}]{1}" -f (Get-Date -Format G),$call)
#Make install process wait for exit before continuing.
$p = [diagnostics.process]::Start("powershell.exe","-NoProfile -NoLogo -Command $Command")
$p.WaitForExit()
Start-Sleep -Seconds 2
#Searches for the installer exe or msi that was directly opened by powershell and gets the process id.
$ProcessID = (gwmi -Query ("select ProcessId from Win32_Process WHERE ParentProcessID = {0} AND Name = '{1}'" -f $p.Id,$ParentProcessFile)|select ProcessId).ProcessId
#waits for the exe or msi to finish installing
while ( (Get-Process -Id $ProcessID -ea 0) -ne $null){
Start-Sleep -Seconds 2
$ElapsedTime = [int](New-TimeSpan -Start $P.StartTime -End (Get-Date)|select TotalSeconds).TotalSeconds
#install times out after 1000 seconds so it dosent just sit their forever this can be changed
if(2000 -lt $ElapsedTime){
Write-Output ('[{0}] The application "{1}" timed out during instilation and was forcfully exited after {2} seconds.' -f (Get-Date -Format G),$App.Name,(([int]$App.InstallTimeOut) * 60))
break
}
}
#clean up any old or hung install proccess that should not be running at this point.
Stop-Process -Name $ParentProcessName -ea 0 -Force
Stop-Process -Name msiexec -ea 0 -Force
}
}
The TXT file should be formatted as such... you will need to do you research on how to each app needs to be installed. a good resource is appdeploy.com
C:\Install.exe /q
C:\install.msi /qn TRANSFORMS='C:\transform.mst'
C:\install2.msi /qn /norestart
C:\install3.exe /quiet
Let me know if there are any errors I had to modify my existing code to remove the proprietary values and make this a little more simplistic. I am pulling my values from a custom XML answer sheet. But this code should work as I have supplied it.
If you would like for me to discuss more about my implementation let me know and i can make a more detailed explanation and also add more of the supporting functions that I have implemented.