How to make powershell wait on event log entry? - powershell

I need to create a component in a larger pipeline that starts vpn service and waits for a connection to be established before proceeding. I'd like to do this with Powershell if possible. I imagine the logic flow being something like this, but the multithreading aspect is vexing me.
create an event log handler
start a service
wait for a specific event log entry
exit

PowerShell v2:
Register-WmiEvent -Query "Select * from __InstanceCreationEvent Where TargetInstance ISA 'Win32_NTLogEvent'" -Action { [console]::beep() }
The script in the action block runs every time there is an event written to the eveng log. Expect a lot of beeps :)

Related

Alternative to Thread. Sleep with different hardware

I am writing a autologin script in Powershell. With main purpose of doing autologon with keystrokes on remote clients in our environment after installation, with the desired AD and password entered.
Works fine on my i9. But most people using Tablets and Elitebooks so using
Thread Sleep
Works bad since i would need to have custom timing on Every hardware, or very high default numbers for lower end clients using my script
Is there any way adding an "wait for row above to completed" Before continuation to next.
I don't have enough on your current code to produce a more accurate answer but the idea, in all cases, remains the same.
You should periodically wake up the thread to check whether or not the machine is in the state you want it in and from there, you either go back to sleep or exit the loop and continue.
The delay is up to you but you want to find a sweet spot to have great performance and reactivity.
Example (based on your description)
$IsLoggedIn = $false
while (! $IsLoggedIn) {
$IsLoggedIn = 'Custom Logic returning $true if the user is logged in'
if ($IsLoggedIn) { break }
Start-Sleep -Milliseconds 100
}
You just need to figure out the thing you want to use as the check to validate the computer is in the correct state you need it in before proceeding further.

(Laravel 5) Monitor and optionally cancel an ALREADY RUNNING job on queue

I need to achieve the ability to monitor and be able to cancel an ALREADY RUNNING job on queue.
There's a lot of answers about deleting QUEUED jobs, but not on an already running one.
This is the situation: I have a "job", which consists of HUNDREDS OF THOUSANDS rows on a database, that need to be queried ONE BY ONE against a web service.
Every row needs to be picked up, queried against a web service, stored the response and its status updated.
I had that already working as a Command (launching from / outputting to console), but now I need to implement queues in order to allow piling up more jobs from more users.
So far I've seen Horizon (which doesn't runs on Windows due to missing process control libs). However, in some demos seen around it lacks (I believe) a couple things I need:
Dynamically configurable timeout (the whole job may take more than 12 hours, depending on the number of rows to process on the selected job)
Ability to CANCEL an ALREADY RUNNING job.
I also considered the option to generate EACH REQUEST as a new job instead of seeing a "job" as the whole collection of rows (this would overcome the timeout thing), but that would give me a Horizon "pending jobs" list of hundreds of thousands of records per job, and that would kill the browser (I know Redis can handle this without itching at all). Further, I guess is not possible to cancel "all jobs belonging to X tag".
I've been thinking about hitting an API route, fire the job and decouple it from the app, but I'm seeing that this requires forking processes.
For the ability to cancel, I would implement a database with job_id, and when the user hits an API to cancel a job, I'd mark it as "halted". On every loop I would check its status and if it finds "halted" then kill itself.
If I've missed any aspect just holler and I'll add it or clarify about it.
So I'm asking for an advice here since I'm new to Laravel: how could I achieve this?
So I finally came up with this (a bit clunky) solution:
In Controller:
public function cancelJob()
{
$jobs = DB::table('jobs')->get();
# I could use a specific ID and user owner filter, etc.
foreach ($jobs as $job) {
DB::table('jobs')->delete($job->id);
}
# This is a file that... well, it's self explaining
touch(base_path(config('files.halt_process_signal')));
return "Job cancelled - It will stop soon";
}
In job class (inside model::chunk() function)
# CHECK FOR HALT SIGNAL AND [OPTIONALLY] STOP THE PROCESS
if ($this->service->shouldHaltProcess()) {
# build stats, do some cleanup, log, etc...
$this->halted = true;
$this->service->stopProcess();
# This FALSE is what it makes the chunk() method to stop looping
return false;
}
In service class:
/**
* Checks the existence of the 'Halt Process Signal' file
*
* #return bool
*/
public function shouldHaltProcess() :bool
{
return file_exists($this->config['files.halt_process_signal']);
}
/**
* Stop the batch process
*
* #return void
*/
public function stopProcess() :void
{
logger()->info("=== HALT PROCESS SIGNAL FOUND - STOPPING THE PROCESS ===");
$this->deleteHaltProcessSignalFile();
return ;
}
It doesn't looks quite elegant, but it works.
I've surfed the whole web and many goes for Horizon or other tools that doesn't fit my case.
If anyone has a better way to achieve this, it's welcome to share.
Laravel queue have 3 important config:
1. retry_after
2. timeout
3. tries
See more: https://laravel.com/docs/5.8/queues
Dynamically configurable timeout (the whole job may take more than 12
hours, depending on the number of rows to process on the selected job)
I think you can config timeout + retry_after about 24h.
Ability to CANCEL an ALREADY RUNNING job.
Delete job in jobs table
Delete process by process id in your server
Hope it help you :)

How to stop a powershell block after a timeout for powershell script

I want to invoke the command
$myArray = Get-Wmiobject -Class Win32-printer
But, on rare occasions, this command sometimes never fails nor succeed, returning the list of available printer.
I would like to assign $myArray only if the invocation takes less than a few seconds, lets say 5. Otherwise, I want to display a dialog telling that printers are unavailable at this moment.
How to start a powershell block with a timeout?
You can use a job for that:
$job = Start-Job { Get-Wmiobject -Class Win32-printer }
$job | Wait-Job -Timeout 5
if ($job.State -eq 'Running') {
# Job is still running, cancel it
$job.StopJob()
} else {
# Job completed normally, get the results
$myArray = $job | Receive-Job
}
I would say to create your own customized WMI queries using type casting and the native .NET providers. This way the work is still being done in the same opened console and you have physical control on the time outs.
I basically had the same frustration as you did. I would be querying servers all day until I hit the one that had a broken WMI. After so much, I started researching how to create my own WMI function to get around this. That was my solution to the problem. Learned a lot along the way.
Here is an article to help you along your way.
http://stevenmurawski.com/powershell/2009/01/dealing-with-wmi-timeouts/
Just to add to the above- powershell also has a built in stopwatch diagnostic for timeout functionality.
https://technet.microsoft.com/en-us/magazine/2009.03.heyscriptingguy.aspx

powershell - How to wait for user input

I am trying to prevent users from shutting down the computer in certain situations. I am displaying a confirm message to do that. This is how my script looks like:
$sysevent = [microsoft.win32.systemevents]
Register-ObjectEvent -InputObject $sysevent -EventName "SessionEnding" -Action $OnShutdown -SourceIdentifier "ExecuteOnShutdown"
$OnShutdown =
{
Write-Host -ForeGround Green $event.SourceEventArgs.Reason
$OUTPUT= [System.Windows.Forms.MessageBox]::Show("Do you really want to shutdown the computer?." , "confirm" , 4)
Write-Host $OUTPUT
}
This works fine but i dont know how do i suspend the shutdown command till user clicks "yes" or "no". Is there a way to prevent the system shutdown and wait for the user to click "yes" or "no" and then shutdown the server based on the answer?
In your event handler scriptblock there are a number of automatic variables defined one of which is $EventArgs. In this case there will be a Cancel property on this object you can set to $true but the docs warn:
When set to true, this property requests that the session continue to
run. It provides no guarantee that the session will not end.
There is also another variable defined in this context - $Sender. Execute man about_automatic_variables for more info.
Consider deploying your script via group policy or a local policy shutdown / logoff script which should prevent shutdown until your condition is met. You might need to wrap your messagebox call in conditional sleep loop (which is what I did for something similar in VBScript years ago!), maybe not.
If you choose to use this method, you may also want to include a preferred default selection for your messagebox (perhaps after a specified timeout period has elapsed?); a user may not hang around to see your mesaagebox as it will be drawn after the interactive desktop has unloaded.
Here's a link to a Technet article about how to Use Startup, Shutdown, Logon, and Logoff Scripts.
I'm not sure if this answers your question as this won't prevent the shutdown, it just stops it until your condition is met.

Creating batch jobs in PowerShell

Imagine a DOS style .cmd file which is used to launch interdependent windowed applications in the right order.
Example:
1) Launch a server application by calling an exe with parameters.
2) Wait for the server to become initialized (or a fixed amount of time).
3) Launch client application by calling an exe with parameters.
What is the simplest way of accomplishing this kind of batch job in PowerShell?
Remember that PowerShell can access .Net objects. The Start-Sleep as suggested by Blair Conrad can be replaced by a call to WaitForInputIdle of the server process so you know when the server is ready before starting the client.
$sp = get-process server-application
$sp.WaitForInputIdle()
You could also use Process.Start to start the process and have it return the exact Process. Then you don't need the get-process.
$sp = [diagnostics.process]::start("server-application", "params")
$sp.WaitForInputIdle()
$cp = [diagnostics.process]::start("client-application", "params")
#Lars Truijens suggested
Remember that PowerShell can access
.Net objects. The Start-Sleep as
suggested by Blair Conrad can be
replaced by a call to WaitForInputIdle
of the server process so you know when
the server is ready before starting
the client.
This is more elegant than sleeping for a fixed (or supplied via parameter) amount of time. However,
WaitForInputIdle
applies only to processes with a user
interface and, therefore, a message
loop.
so this may not work, depending on the characteristics of launch-server-application. However, as Lars pointed out to me, the question referred to a windowed application (which I missed when I read the question), so his solution is probably best.
To wait 10 seconds between launching the applications, try
launch-server-application serverparam1 serverparam2 ...
Start-Sleep -s 10
launch-client-application clientparam1 clientparam2 clientparam3 ...
If you want to create a script and have the arguments passed in, create a file called runlinkedapps.ps1 (or whatever) with these contents:
launch-server-application $args[0] $args[1]
Start-Sleep -s 10
launch-client-application $args[2] $args[3] $args[4]
Or however you choose to distribute the server and client parameters on the line you use to run runlinkedapps.ps1. If you want, you could even pass in the delay here, instead of hardcoding 10.
Remember, your .ps1 file need to be on your Path, or you'll have to specify its location when you run it. (Oh, and I've assumed that launch-server-application and launch-client-application are on your Path - if not, you'll need to specify the full path to them as well.)