0

Context

On a build server, a PowerShell 7 script script.ps1 will be started and will be running in the background in the remote computer.

What I want

A safenet to ensure that at most 1 instance of the script.ps1 script is running at once on the build server or remote computer, at all times.

What I tried:

I tried meddling with PowerShell 7 background jobs (by executing the script.ps1 as a job inside a wrapper script wrapper.ps1), however that didn't solve the problem as jobs do not carry over (and can't be accessed) in other PowerShell sessions.

What I tried looks like this:

# inside wrapper.ps1

$running_jobs = $(Get-Job -State Running) | Where-Object {$_.Name -eq "ImportantJob"}

if ($running_jobs.count -eq 0) {
    Start-Job .\script.ps1 -Name "ImportantJob" -ArgumentList @($some_variables)
} else {
    Write-Warning "Could not start new job; Existing job detected must be terminated beforehand."
}

To reiterate, the problem with that is that $running_jobs only returns the jobs running in the current session, so this code only limits one job per session, allowing for multiple instances to be ran if multiple sessions were mistakenly opened.

What I also tried:

I tried to look into Get-CimInstance:

 $processes = Get-CimInstance -ClassName Win32_Process | Where-Object {$_.Name -eq "pwsh.exe"}

While this does return the current running PowerShell instances, these elements carry no information on the script that is being executed, as shown after I run:

foreach ($p in $processes) {
     $p | Format-List *
}

I'm therefore lost and I feel like I'm missing something. I appreciate any help or suggestions.

5
  • 3
    Why not just use a file in a well-known location (either user-specific or machine-wide) as the mutex? This is a fairly common technique on Linux. (Some care must be taken that the file gets properly cleaned up even if scripts are interrupted, but this can be done by, for example, writing a PID to it and considering it invalid if the process with that PID no longer exists, or is not PowerShell.) Commented Jun 29, 2022 at 19:25
  • 1
    Yeah, I agree with using a lock file as a flag to indicate that the script is already running. It's very simple logic that ends up being very robust. Commented Jun 29, 2022 at 19:29
  • 1
    Is this for Windows only? In this case creating a file is not necessary, instead a global event object can be used as a mutex. It gets cleaned up automatically by the OS, when the process exits, even when forcefully terminated. Commented Jun 29, 2022 at 20:22
  • Thanks for answering everyone!, @zett42 I ended up going with the file logic because it sounded a little less complex, but thank you for the suggestion. Commented Jun 30, 2022 at 19:57
  • While the file solution may look more familiar, I don't see how it is less complex. More lines of code, more things that can go wrong. Commented Jul 1, 2022 at 7:05

1 Answer 1

1

I like to define a config path in the $env:ProgramData location using a CompanyName\ProjectName scheme so I can put "per system" configuration. You could use a similar scheme with a defined location to store a lock file created when the script run and deleted at the end of it (as suggested already within the comments).

Then, it is up to you to add additional checks if needed (What happen if the script exit prematurely while the lock is still present ?)

Example


# Define default path (Not user specific)
$ConfigLocation = "$Env:ProgramData\CompanyName\ProjectName"
# Create path if it does not exist
New-Item -ItemType Directory -Path $ConfigLocation -EA 0 | Out-Null

$LockFilePath =  "$ConfigLocation\Instance.Lock"

$Locked = $null -ne (Get-Item -Path $LockFilePath -EA 0)
if ($Locked) {Exit}


# Lock
New-Item -Path $LockFilePath

# Do stuff 

# Remove lock
Remove-Item -Path $LockFilePath

Alternatively, on Windows, you could also use a scheduled task without a schedule and with the setting "If the task is already running, then the following rule applies: Do not start a new instance". From there, instead of calling the original script, you call a proxy script that just launch the scheduled task.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you!! This explains the file logic well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.