Can't run test .ps1 as a service from particular directory

42 views Asked by At

I have admin rights on a computer. I have one-liner .ps1 that I create as a service using nssm.exe, and when this ps1 is in C:\test, it works. (log on as local system account)

When that same .ps1 resides in C:\Program Files\WindowsPowerShell\Modules\test, the service will not run once created. It says it cannot start, but there's nothing in event viewer except that it paused.

Permissions look fine to me. I have full access.

Tried to use nssm.exe to create a service for a .ps1 script, which should let me run the service, but it pauses

1

There are 1 answers

0
mklement0 On

nssm.exe unfortunately doesn't preserve the boundaries between pass-through arguments in its nssm install command, and simply space-concatenates the argument values - possibly stripped of syntactic " characters - when it writes the service definition to the registry.[1]
(By contrast, the argument specifying the target executable that will receive the pass-through arguments is properly recognized as a single argument when enclosed in "..."; PowerShell does the latter automatically when you pass a variable whose value has embedded spaces.)

While avoiding arguments that contain spaces bypasses the problem - such as using the short (8.3) version of a file path - doing so isn't always an option.

For a workaround that does handle arguments with spaces correctly, you must use embedded "-quoting (as part of the argument value) in order to pass arguments with spaces in a way that what should normally be "..." ends up as \"...\" on nssm.exe's process command line, which ultimately preserves them as distinct arguments:

How that is achieved depends on the PowerShell version you're calling nssm.exe from (see this answer for background information):

  • When calling from Windows PowerShell and PowerShell (Core) 7+ up to v7.2x:

    • Embed \"
  • When calling from PowerShell 7.3+:

    • Embed just " - PowerShell will take care of passing \" on the process command line for you.

In the context of your code, building on your own attempt stated in a comment; works in both PowerShell editions and all (non-obsolete) versions:

#requires -RunAsAdministrator

# Get the PowerShell executable's full path.
$Binary = (Get-Process -Id $PID).Path

# Set the *.ps1 file's full path:
$File = 'C:\Program Files\WindowsPowerShell\Modules\PoshBot\0.12.0\Test-service.ps1'

# Install the service and for simplicity pass the 
# the pass-through arguments as a single string, and
# use embedded quoting around arguments that do/may contain spaces.
& $PSScriptRoot\nssm.exe install Poshbot $Binary @"
-ExecutionPolicy Bypass -NoProfile -File $(("\`"$File\`"", "`"$File`"")[[bool] $IsCoreClr])
"@

Note:

  • To make using embedded " characters easier inside the single arguments string, a here-string is used above (@"<newline>...<newline>"@)

  • If you know that you'll be targeting only Windows PowerShell (or PowerShell 7.2-) vs. PowerShell 7.3+, you can simplify
    $(("\`"$File\`"", "`"$File`"")[[bool] $IsCoreClr]) to
    \"$File\" vs. "$File"


[1] Such pass-through arguments are stored in the AppParameters value of the Parameters subkey of the target service definition, which itself is a subkey of HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services