I have a PowerShell completion hook that looks like:
$scriptblock = {
param($wordToComplete, $commandAst, $cursorPosition)
# ...
clap-json-test-completions power-shell query $wordToComplete $script:index -- #elements | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_)
}
}
Register-ArgumentCompleter -Native -CommandName clap-json-test -ScriptBlock $scriptblock
The clap-json-test-completions command is a program written in Rust which generates suggestions for the completing the current command line.
The clap-json-test-completions program is designed to talk to a local "server" process on the machine for certain bits of information that take longer to figure out and can be cached by the server process. If the server process isn't running then the clap-json-test-completions program attempts to start it up.
Unfortunately, the PowerShell completions mechanism waits until that server process stops before returning the suggestions to the user. As that server process is designed to not stop, this is an issue.
I have tried spawning the server process using:
.creation_flags(
DETACHED_PROCESS_FLAG
| CREATE_NEW_PROCESS_GROUP_FLAG
)
Based on the Creation Flags documentation in the Windows docs. Unfortunately they don't seem to have to desired effect. Instead of allowing the completion process to return whilst the server is still running, they just seem to make it so that Ctrl-C no longer kills the server from the hung completion prompt.
I've also tried launching the command a "Job" using:
Start-Job -ScriptBlock { clap-json-test-completions power-shell query $wordToComplete $script:index -- #elements } | Receive-Job -AutoRemoveJob -Wait | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_)
}
In the Power-Shell completion script and using CREATE_BREAKAWAY_FROM_JOB_FLAG as an additional creation flag but that doesn't seem to work.
I understand that Windows Services exist for long running processes but we would rather have a situation where the server only starts when the user first tries to do a completion and have a situation where the user has permissions to start the server (as a normal process) rather than requiring extra permissions to interact with the Windows Service system.
The current approach works fine if the clap-json-test-completions binary is run by itself outside of the completions system. It doesn't hang waiting for the server to finish.
Is there an approach to either the PowerShell completion script or the process creation in the Rust code that'll allow the completion system to return whilst the server still runs?
Related
So there are several factors in play with this question, so here they are:
SailPoint 8.2 and IQService 8.2
Windows Server 2016
A service Account(Domain Admin)
An interactive User account (Domain admin)
Powershell 5.1 build 14393 revision 4583
So what we have is SailPoint is executing a rule on its end, sending over some information to IQService, and IQService is executing the PowerShell scripts as the service account. In one of the PowerShell scripts, we have the following command:
LogToFile("calling start job")
$j = Start-Job -ScriptBlock { C:/SailPoint/Scripts/PowershellContainerAfterCreateRetry.ps1 -sAMAccountName $args[0] -company $args[1] } -ArgumentList $sAMAccountName, $company -Name 'PowershellContainerAfterCreateRetry'
LogToFile($j | Select-Object -Property *)
LogToFile("finished start-job")
and this is where things get interesting because this command, as you can note, we can log to file to see what its output is, which is as follows:
calling start job
#{
State=Running; HasMoreData=True;
StatusMessage=;
Location=localhost;
Command= C:/SailPoint/Scripts/PowershellContainerAfterCreateRetry.ps1 -sAMAccountName $args[0] -company $args[1] ;
JobStateInfo=Running;
Finished=System.Threading.ManualResetEvent;
InstanceId=aa889c06-7a8a-402e-807a-880d02465bdd; Id=1;
Name=PowershellContainerAfterCreateRetry;
ChildJobs=System.Collections.Generic.List`1[System.Management.Automation.Job];
PSBeginTime=10/15/2021 21:14:22; PSEndTime=;
PSJobTypeName=BackgroundJob;
Output=System.Management.Automation.PSDataCollection`1[System.Management.Automation.PSObject];
Error=System.Management.Automation.PSDataCollection`1[System.Management.Automation.ErrorRecord];
Progress=System.Management.Automation.PSDataCollection`1[System.Management.Automation.ProgressRecord];
Verbose=System.Management.Automation.PSDataCollection`1[System.Management.Automation.VerboseRecord];
Debug=System.Management.Automation.PSDataCollection`1[System.Management.Automation.DebugRecord];
Warning=System.Management.Automation.PSDataCollection`1[System.Management.Automation.WarningRecord];
Information=System.Management.Automation.PSDataCollection`1[System.Management.Automation.InformationRecord]}
finished start-job
When I execute this command either by itself OR within this script using Windows PowerShell ISE, it completes with no issue and calls the script in question, and everything works perfectly! (whether I am using my interactive account OR the service account)
When this script executes using the IQService, something "else" is happening - I say something "else" because I don't have any log files or errors; it just seems to disappear into the ether. (I have a log write out five lines into the PowerShell script, so one would think I would at least get SOMETHING!?!? I am out of ideas...thoughts?
As a minor note, I ran an experiment that showed me that there is something strange about the setup which should have succeeded without issue - like the above it appears to execute (because I can see the same information above, that shows that the job has started). Still, just like the above, it never actually "appears" to complete or error out. The only thing I can think of is that somehow the primary script closing out is causing this to close out as well - but I would think it would be able to get a couple of log files written to if that was the case? Anyway...thanks for reading!
$doit = {
"test" | Out-File -filepath ("c:\test.txt") -append
}
Start-job -ScriptBlock $doit
i think Start-Job is the problem here, as iqservice will launch a powershell script process and that may not support the background job aspect you are trying to use.
if you need to have something retry or wait and loop, you'll need to use another identityiq/iqservice mechanism (a workflow in iiq perhaps that calls down to AD when conditions are, timer is hit, etc.) beyond start-job inside of an iqservice powershell script.
I'm using Start-Process cmdlet as part of a Powershell script being executed by the new scriptable TFS build system.
My issue is that I'm starting some executables from my Powershell script and once the build step ends, it kills started processes.
I've also also tried to use ProcessStartInfo directly and Start-Job with no luck.
When I run that script alone it ends, but it leaves the started processes opened.
Is there any way to solve this?
You can use start-Job:
Start-Job -ScriptBlock { start C:\Windows\notepad.exe }
After exiting the PS, Notepad is still open
Check this post for more info:
if you start a script using Start-Process, it will survive the shell
termination, but if you started it from a console window then it stays
bound to that window and closing the window will terminate the process
I need to run a powershell script whenever the server is rebooted/shutdown (whether graceful or disgraceful reboot).
The script will stop 4 application services at an interval of 1 minute and then finally reboots the system.(This is a business requirement, don't ask why)
How can I make server to invoke the .ps1 script whenever a reboot or a shutdown is initiated.
My test results:
I tried to create a test script which will generate a text file with current date/time and added it to the scheduled task on the trigger of event log 6006 (which is created whenever a system reboot/shutdown is initiated.)
I checked the box -"Run with highest privileges" but after system restart no text file was generated as it was supposed to, although it generates when ran manually.
Do we have any better approach to implement this?
(My final expectation should look like this-
On a random day a random user initiated reboot after a monthly patch when a command prompt window opens before him with message something like:
Stopping service abc...
Stopped.
Waiting for 60 seconds.
Stopping service xyz...
Stopped
EDIT: I've been successfully able to invoke the .ps1 file by adding it to the gpedit as suggested by Kory and Alroc but the script runs only in background when computer restart is initiated. It doesn't opens a regular cmd window to show the progress.
I'm adding the .ps1 script as well below which stops 2 services(chosen for testing purpose) at an interval of 10 seconds and will show the timer as well, only when ran manually.When invoked by the shutdown command it'll stop services only in the background without showing the progress to the user. Kindly assist to achieve this?
Write-Host "Shutdown script invoked"
stop-service W32Time -force -PassThru
for($i = 10 ; $i -gt 0 ; $i--)
{
Write-Progress -Activity "`n Waiting for" -status "`$i equals $i seconds"
sleep 1
}
stop-service wuauserv -force -PassThru
You can use GPO to configure a shutdown script for systems.
You might be able to to it via a Win32_ComputerShutdownEvent watcher as well.
After deep digging, I've finally figured out how to make the cmd window visible while system shutdown in progress.
Here is the complete steps of performing above mentioned expectation:
Open gpedit.msc
Navigate to Computer Configuration->Windows
Settings->Scripts(Startup/Shutdown)->Shutdown.
Go to Shutdown properties. In the powershell scripts tab add your
script and select 'Run Windows Powershell script first'
Above steps will enable the invoke of script at every system shutdown. Now to make the script visible and show progress:
Navigate to Computer Configuration->Administrative
Templates->System->Scripts
Among the policies showing in the right pane enable below
properties:
Run Windows Powershell scripts first at computer start,shutdown
Run shutdown scripts visible
Current PS script:
Invoke-Command -ComputerName RemoteServer007.FQDN.com -ScriptBlock {
Set-Variable -Name WOWCONFIG -value "d:\ABCs\WOWzers" `
| Start-Process "d:\da-folder\Do-It-NOW-Pleez.cmd"
}
If I log on locally to the server(RemoteServer007.FQDN.com) and execute the cmd file, it runs through all of the lines(commands) within the cmd file.
When I execute it remotely, it gets about 30% of the way through the commands within the cmd file, the PS execution ends without error, but not all of the lines/commands in the cmd file had been executed.
This was discovered by simply configuring each line of the cmd file to output to txt files.
I even tried re-ranging the commands in the cmd file, thinking that perhaps there was a specific command that was causing it to exit, but that is not the case.
I'm wondering if there is some timeout or response that PowerShell is not getting? and just quitting almost immediately after starting?
Any ideas or help would be greatly appreciated.
There are a couple of things you can do here:
You may have a memory issue. Increasing the value of MaxMemoryPerShellMB might help
set-item WSMan:\$target\Shell\MaxMemoryPerShellMB -Value 0 -Force
You'd need to run this once on the remote machine before you execute your commands again.
You can also see possible error logs in the windows event viewer. There are categories for powershell and for Windows Remote Management which you should look at.
Finally, you can just run this process asynchronously, using the task scheduler for instance. I had a similar problem with windows in the past, and running the process from the task scheduler, outside the powershell session, fixed it. There's an example of how we did this in Cloudify here:
https://github.com/CloudifySource/cloudify/blob/master/esc/src/main/resources/clouds/ec2-win/upload/bootstrap-management.ps1#L220
what is the easy way to kill remote job initiated by invoke-command with background processes?
I kick remote PS script to run legacy exe file and need a way to kill that process.
I was going to use stop-job first but as I am reading it works only for local processes.
Then I was going to use Remove-job -force command but if I understood right, it cannot kill running process until it completes (again my remote ps script will start another process to run exe file).
I am reading I can kill a process using wmi terminate command but I do not know how to get PID of remote process (I cannot use name because I can kill processes from other users)
what is the best way to achieve my goal?
my script looks like that:
invoke-command -computername server1,server2,server3..etc -script-block {my.exe} -asjob
loop to wait for all processes to complete and report progress
check for CTRL-C press and kill all remote instances on {my.exe}
ok checked this morning from work and this works fine from a calling script:
get-job | remove-job -force
I was confused by MSDN doc which says:
When you stop a background job, Windows PowerShell **completes** all tasks that are pending in that job queue and then ends the job
Check out this answer, you can add the start process command to your script block and return the pid or save it to a text file to be reference later.
PowerShell - get process ID of called application