How do I get a Powershell process that was opened by another Powershell process? - powershell

I am running multiple PowerShell scripts at once. I would like to be able to wait on certain ones to finish before opening new scripts. Basically, I was thinking if I could find the command line option that ran it something like "powershell.exe -Path "<script dir>" that would do it.
I tried doing a Get-Process | gm to find any parameters that I could call to get that information and I didn't see any (doesn't mean they aren't there) I tried looking through Task Manager to see if I could view something through the gui that I could link to but that didn't help either.
I hope I can get something like
Start-Process -FilePath ".\<script>.ps1" -ArgumentList "<args>"
do
{
sleep 10
}
until ((Get-Process -ProcessName "PowerShell" | where "<paramater>" -EQ ".\<script>")
I need to wait until that process is done but I don't want to put a wait at the end of the Start-Process because after that Start-Process kicks off I need some other items to go to while my .\ is running. I just need it to wait before another section of script kicks off.

Have a look at the "Job" cmdlets https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_jobs?view=powershell-6
And the $PID automatic variable, this will give the process ID of the current PowerShell session.

Related

Labeling a process to determine its execution status

I have a script D:\Script.ps1 that runs using a shortcut:
# The shortcut is located in the startup folder $Env:AppData\Microsoft\Windows\Start Menu\Programs\Startup\Script.lnk
# The object in this shortcut description
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File D:\Script.ps1
How can I see in the console that the process Script is working? How can I mark this process in script or in shortcut description so that I can see if it is working or has already completed the task and completed the work? I could track the process using an ID (Get-Process).ID, but I do not know the ID of my process when starting OS. I can’t Get it's Name easy and Set, as, for example, in Start-Job. I can not distinguish it from other running processes powershell via (Get-Process).ProcessName, they all have the ProcessName of powershell. What can I do to distinguish my powershell process from other powershell running ones? Thanks
If you can switch from using a batch file as a wrapper or can tolerate another PowerShell script to launch the real script. You can use Start-Process with the -PassThru parameter combined with Wait-Process to tell you when it's complete.
Something like:
$Process = Start-Process -FilePath C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe -ArgumentList "-command .{read-host} -WindowStyle Normal" -passthru
$Process | Wait-Process
Write-Host "The process has completed."
However, if you need to observe the process completely apart from it there are a couple of other approaches I can think of.
Get-Process doesn't return a command line property but the WMI Win32_Process class does. You can match some aspect of the CommandLine to differentiate & isolate the process running the script of interest. Something like:
Get-CimInstance win32_Process -Filter "CommandLine LIKE '%d:\\script.ps1'"
You can use this directly or to get the PID or any other characteristic to further observe with. You can certainly get more elaborate with the filter.
Note: Typical best practice is to move the filter criteria to the left, but in this case you might think about using a PowerShell | Where-Object{}, as performance probably wouldn't be a concern. It might make it somewhat easier to filter based on CommandLine. I'd only do that if the existing filter was troublesome.
You can also add the command line column in Task Manager to watch just the same, but without scripting etc...
These aren't exactly eloquent solutions, but let me know if it's helpful.

PowerShell Start-Job not creating or amending files

I am new to PowerShell and this is the first time I am attempting to use a job. I am running into an issue where I have a part of a script that looks for a file, creates it if it doesn't exist and then amends the file, and when I run the script (not as a job) it executes correctly, but when I put it in a job, it doesn't amend the file.
A much simplified example of what I have is this:
Start-job -Name HostCheck -ScriptBlock {
ForEach ($Host in (Get-Content -Path .\HostFile.txt) {
Add-Content .\somefile.txt "`nWrite something on a new line for $Host"
} | Out-Null
}
# Removes job once it is finished
Get-Job -Name HostCheck | Wait-Job | Remove-Job
Now I have tried adding | Receive-Job after the | Out-Null, but that didn't seem to change anything.
I've seen people write the entire script-block to a variable and just use the variable instead, so I am curious if that is a requirement (but I wouldn't think so).
Also, this might matter, I open the script with a .bat file that escalates the PowerShell console to admin as well as setting the execution policy of the process to Bypass. Now it seems that everything that runs in that console session or is kicked off by that console session (several scripts get ran, this is just part of one of them) seems to inherit those settings, but being new with jobs, I don't know if it would also inherit those settings, or how I would force it to (if not).
I discovered the problem:
-Your current working directory is lost when starting a job so my relative path .\somefile.txt would default to C:\Users\[Username]\Documents instead of the location where the .\somefile.txt resides.
I can get around this by using an absolute path, or I think there is a way to pass arguments to a job, but if anyone knows a better way to do this, please feel free to comment.
Here's a workaround, cd to the current dir of the caller.
start-job { cd $using:pwd; pwd } | Receive-Job -wait -auto

Launch PowerShell script into new window while passing variables

I've been using the site for a while, searching through the questions and answers, trying to map them to my scenario, but I'm either missing something, or what I'm looking to do isn't possible (at least the way I'm trying to do it), hence I'm hoping for a push in the right direction. Thanks in advance for reading.
I've been working on a fairly sizeable automation project. My main script performs a number of tasks, and generally works well, and reliably. At one stage of the script, I execute another PowerShell script, which was written by another team. I call the script as follows:
.\DeployMySQLProvider.ps1 -AzCredential $asdkCreds `
-VMLocalCredential $vmLocalAdminCreds `
-CloudAdminCredential $cloudAdminCreds
-PrivilegedEndpoint $ERCSip `
-DefaultSSLCertificatePassword $secureVMpwd -AcceptLicense
When I call it this way, from my main script, it works fine, however, this script uses and registers a DLL file during it's deployment, and locks it until the PowerShell window and session is closed. At the end of my main script, I have a cleanup phase, which can't complete it's job because of this locked DLL.
My thoughts therefore, were to launch the 2nd script into a new PowerShell window and session, either using Start-Process or Invoke-Expression, but I just can't seem to get either right. Most of the variables I'm passing through to the 2nd script aren't just strings, which is probably where I'm falling over. They are a mix of usernames and passwords (secure strings) along with $ERCSip which is a string.
Should I be looking at Start-Process / Invoke-Expression, or something else entirely? When I was testing with Start-Process, I had the following defined, but couldn't get the ArgumentList side working correcly for me (blank below):
Start-Process "$pshome\powershell.exe" -PassThru -Wait `
-Verb RunAs -ErrorAction Stop -ArgumentList ""
Any pointers in the right direction would be much appreciated.
Thanks!
I've used something similar to this in my scripting:
$scriptpath="c:\pathto\deploysqlProvider"
$a = "$scriptpath\DeployMySQLProvider.ps1 -AzCredential $asdkCreds `
-VMLocalCredential $vmLocalAdminCreds `
-CloudAdminCredential $cloudAdminCreds
-PrivilegedEndpoint $ERCSip ` "
-DefaultSSLCertificatePassword $secureVMpwd -AcceptLicense
Start-Process -Verb runas -FilePath powershell.exe -ArgumentList $a -wait -PassThru ;
Not sure if you need it to runas admin or not (-verb runas).
I'd suggest you then look for the Powershell process and path. So that if you have to kill this separate process you can.

Powershell Start and stop additional powershell instance foreach loop

I have a powershell script I am spawning additional powershell instances to run cmdlets.
I need to be able to Start-Process , a powershell instance, from a nested foreach loop and allow the process to run through and then stop that process when its complete.
foreach($thing in $things)
foreach($stuff in $stuffs)
start-process powershell.exe -nonewwindow | get-cmdichooseasanewcmd | export-csv -path stuff
stop-process same instantiated process
How do I stop the process that is specifically tied to my foreach loop when it is complete with its task?
If you're not doing anything requiring credentials you can use jobs.
$job = Start-Job { # Do stuff }
$job.StopJob()
Anything involving credentials invokes the wrath of double hopping but I don't think it works the way you're running it in your question either so shouldn't be a problem.
Alternatively you can use runspaces but that's a big can of worms. See this link if you want to go there.

How to pause a Powershell script until some external event has occurred?

I am using a Powershell 1 script to kick off a Python program, and I want to then pause the Powershell until the Python has completed its work. (I have complete ownership of the Python file.) What is the best way to go about this?
Look at the help on the Wait-Process cmdlet:
man Wait-Process -full
start-process notepad -PassThru | Wait-Process
you can get the Process object or ID with the get-process commandlet to get the process object or you can get it like I show below either will work.
you can use the PID and call
$proc = [System.Diagnostics.Process]::GetProcessById($PID)
while(-not $proc.HasExited) {[System.Threading.thread]::sleep(500) }