How to Set Up NSSM Service Using an Idempotent Approach
Testing NSSM removal of service and introduce some consistency when performing mutable deployment
Context
I was attempting to automate a mutable deployment for an application service on a Windows EC2 instance with NSSM. However, I faced difficulties trying to setup an idempotent workflow, whereby the service will be configured and running successfully for each execution.
Issues
I faced issues in removing the NSSM service in a clean manner, which includes issues such as:
The service has been marked for deletion
: This means while NSSM has removed the service, hence out of its scope, Windows still interpret as the service exists as not all processes have been closed.The installation step has been seemingly skipped. This subsequently affects the downstream steps using NSSM to reinstall the service with the same name.
This may be a race condition or side effects for mutable application deployments.
Insights
I read through the NSSM official documentation to understand how the underlying mechanism of removing a service. The "Service shutdown" section caught my attention.
It seems that there are four stages which NSSM can use to shut down the application and by default it will attempt all four in the following order:
Generate Control-C event to application's console
Enumerate all windows created by application and send a
WM_CLOSE
messageEnumerate all threads created by the application and send
WM_QUIT
message.Call
TerminateProcess()
as the last resort to request for the operating system to forcibly terminate the application
The key information that got me thinking is as quoted below:
By default nssm will wait up to 1500 milliseconds for the application to exit after trying each of the methods describe above.
Perhaps, the key to idempotency is to stop the service then remove the service with NSSM.
Testing
Script to be executed as service
For simplicity, I am using a simple PowerShell script, hello-world.ps1
to output some messages at a periodical manner.
Write-Output "Hello World"
Write-Output "Now sleep for 20 seconds"
Start-Sleep 20
Write-Output "Hellow World, I am awake"
PowerShell Script to configure NSSM Serice
Below is the PowerShell script setup-nssm-svc.ps1
, to configure the NSSM service:
# PowerShell script to setup the NSSM service
## To replace the target directory path with the appropriate local file path
$targetDirectory="/PATH/TO/DIR/WITH/SCRIPTFILE"
$scriptFile = "$targetDirectory\hello-world.ps1"
## Defining NSSM parameters
$powershell = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
$arguments = "powershell $scriptFile"
$stdinPath = "$targetDirectory\app.log"
$stdoutPath = "$targetDirectory\app.log"
$stderrPath = "$targetDirectory\app.log"
$serviceName = "test-svc"
## To check if the NSSM service already exists
if ( (Get-CimInstance win32_service | ?{$_.PathName -Like '*nssm*'}).Name ) {
echo "Service already exists. Will stop the service before proceeding to re-installing the service again"
## Stop the service and give it time to process
nssm stop $serviceName
Start-Sleep 10
## Proceed to remove the NSSM service
nssm remove $serviceName confirm
} else {
echo "Service does not exist, will proceed to install the service"
}
## Setting up the target NSSM service
nssm install $serviceName $powershell $arguments
nssm set $serviceName AppDirectory "$targetDirectory"
nssm set $serviceName AppStdin "$stdinPath"
nssm set $serviceName AppStdout "$stdoutPath"
nssm set $serviceName AppStderr "$stderrPath"
echo "[Before] Service status"
nssm status $serviceName
nssm start $serviceName
echo "[After] Service status"
nssm status $serviceName
Results
When running
setup-nssm-svc.ps1
the first time, you can observe from the output thattest-svc
service has been configured successfully.Upon running
setup-nssm-svc.ps1
again, the script detects that the service already exists, it will first stop the service before removing it. You can see that NSSM is able to install the same service again successfully.
Caveats
Considering that the application used is a simple PowerShell script, this workflow may still not work well for more complex applications.
You may still need to introduce
Start-Sleep
to give the Windows processes time to complete. In this case, perhaps the minimum wait duration should be at least 60 seconds for NSSM's 4 stages of shutting down of services.