win_nssm: refactor to fix issues, support check mode and add more features (#45693)

* win_nssm: rename cmdlets to use approved verbs, rename service name parameters

* win_nssm: improve code style and cmdlets ordering

* win_nssm: always escape all command line parameters with Argv-ToString

fix error when the service name contains quotes

* win_nssm: use Fail-Json instead of exceptions and remove global try/catch

* win_nssm: small refactoring, inline some functions

* win_nssm: refactoring - add a generic cmdlet to idempotently set any nssm service parameter

* win_nssm: refactoring - inline some functions

To make the code more malleable for future changes

* win_nssm: change application, stdout_file and stderr_file options type to path

* win_nssm: deprecates app_parameters, rename app_parameters_free_form to arguments, and add support for list of parameters

* win_nssm: add support of check mode

* win_nssm: add working_directory option

* win_nssm: add display_name and description options

* win_nssm: minor changes

* win_nssm: remove some sanity exclusions

* win_nssm: avoid using aliases and minor style fixes

* win_nssm: doc and ui improvements

* win_nssm: remove sanity exclusions

* win_nssm: minor revision

* win_nssm: deprecates dependencies, start_mode, user and password parameters and some choices of state in favor of win_service

* win_nssm: fix style

* win_nssm: add executable option to specify the location of the NSSM utility

* win_nssm: add missing parameter types

* win_nssm: add diff mode support

* win_nssm: avoid displaying depreciation warning if default value is assigned

* win_nssm: fix variable scope

* win_nssm: use the explicit -LiteralPath parameter name instead of -Path

* win_nssm: fix documentation

* win_nssm: add porting guide entries

* win_nssm: add changelog fragment
pull/54695/head
Kevin Subileau 5 years ago committed by Jordan Borean
parent eff1f8851c
commit 09979e899f

@ -0,0 +1,13 @@
minor_changes:
- win_nssm - Add the ``executable`` option to specify the location of the NSSM utility.
- win_nssm - Add the ``working_directory``, ``display_name`` and ``description`` options.
- win_nssm - Add support for check and diff modes.
- win_nssm - Change default value for ``state`` from ``start`` to ``present``.
bugfixes:
- win_nssm - Fix several escaping and quoting issues of paths and parameters.
deprecated_features:
- win_nssm - Deprecate ``dependencies``, ``start_mode``, ``user``, and ``password`` options, in favor of using the ``win_service`` module.
- win_nssm - Deprecate ``start``, ``stop``, and ``restart`` values for ``state`` option, in favor of using the ``win_service`` module.
- win_nssm - Deprecate ``app_parameters`` option in favor of ``arguments``.

@ -312,6 +312,24 @@ Noteworthy module changes
* The ``win_psexec`` has deprecated the undocumented ``extra_opts`` module option. This will be removed in Ansible 2.10.
* The ``win_nssm`` module has deprecated the following options in favor of using the ``win_service`` module to configure the service after installing it with ``win_nssm``:
* ``dependencies``, use ``dependencies`` of ``win_service`` instead
* ``start_mode``, use ``start_mode`` of ``win_service`` instead
* ``user``, use ``username`` of ``win_service`` instead
* ``password``, use ``password`` of ``win_service`` instead
These options will be removed in Ansible 2.12.
* The ``win_nssm`` module has also deprecated the ``start``, ``stop``, and ``restart`` values of the ``status`` option.
You should use the ``win_service`` module to control the running state of the service. This will be removed in Ansible 2.12.
* The ``status`` module option for ``win_nssm`` has changed its default value to ``present``. Before, the default was ``start``.
Consequently, the service is no longer started by default after creation with ``win_nssm``, and you should use
the ``win_service`` module to start it if needed.
* The ``app_parameters`` module option for ``win_nssm`` has been deprecated; use ``argument`` instead. This will be removed in Ansible 2.12.
* The ``app_parameters_free_form`` module option for ``win_nssm`` has been aliased to the new ``arguments`` option.
* The ``win_dsc`` module will now validate the input options for a DSC resource. In previous versions invalid options
would be ignored but are now not.

@ -3,6 +3,7 @@
# Copyright: (c) 2015, George Frank <george@georgefrank.net>
# Copyright: (c) 2015, Adam Keech <akeech@chathamfinancial.com>
# Copyright: (c) 2015, Hans-Joachim Kliemeck <git@kliemeck.de>
# Copyright: (c) 2019, Kevin Subileau (@ksubileau)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#Requires -Module Ansible.ModuleUtils.Legacy
@ -11,671 +12,472 @@
$ErrorActionPreference = "Stop"
$params = Parse-Args $args
$result = @{
changed = $false
$start_modes_map = @{
"auto" = "SERVICE_AUTO_START"
"delayed" = "SERVICE_DELAYED_AUTO_START"
"manual" = "SERVICE_DEMAND_START"
"disabled" = "SERVICE_DISABLED"
}
$params = Parse-Args -arguments $args -supports_check_mode $true
$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
$diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false
$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true
$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent","started","stopped","restarted" -resultobj $result
$display_name = Get-AnsibleParam -obj $params -name 'display_name' -type 'str'
$description = Get-AnsibleParam -obj $params -name 'description' -type 'str'
$application = Get-AnsibleParam -obj $params -name "application" -type "str"
$application = Get-AnsibleParam -obj $params -name "application" -type "path"
$appDirectory = Get-AnsibleParam -obj $params -name "working_directory" -aliases "app_directory","chdir" -type "path"
$appParameters = Get-AnsibleParam -obj $params -name "app_parameters"
$appParametersFree = Get-AnsibleParam -obj $params -name "app_parameters_free_form" -type "str"
$startMode = Get-AnsibleParam -obj $params -name "start_mode" -type "str" -default "auto" -validateset "auto","delayed","manual","disabled" -resultobj $result
$appArguments = Get-AnsibleParam -obj $params -name "arguments" -aliases "app_parameters_free_form"
$stdoutFile = Get-AnsibleParam -obj $params -name "stdout_file" -type "str"
$stderrFile = Get-AnsibleParam -obj $params -name "stderr_file" -type "str"
$dependencies = Get-AnsibleParam -obj $params -name "dependencies" -type "list"
$stdoutFile = Get-AnsibleParam -obj $params -name "stdout_file" -type "path"
$stderrFile = Get-AnsibleParam -obj $params -name "stderr_file" -type "path"
$executable = Get-AnsibleParam -obj $params -name "executable" -type "path" -default "nssm.exe"
# Deprecated options since 2.8. Remove in 2.12
$startMode = Get-AnsibleParam -obj $params -name "start_mode" -type "str" -default "auto" -validateset $start_modes_map.Keys -resultobj $result
$dependencies = Get-AnsibleParam -obj $params -name "dependencies" -type "list"
$user = Get-AnsibleParam -obj $params -name "user" -type "str"
$password = Get-AnsibleParam -obj $params -name "password" -type "str"
if (($appParameters -ne $null) -and ($appParametersFree -ne $null))
{
Fail-Json $result "Use either app_parameters or app_parameteres_free_form, but not both"
}
if (($appParameters -ne $null) -and ($appParameters -isnot [string])) {
Fail-Json -obj $result -message "The app_parameters parameter must be a string representing a dictionary."
$result = @{
changed = $false
}
$diff_text = $null
Function Nssm-Invoke
{
function Invoke-NssmCommand {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$cmd
[Parameter(Mandatory=$true,ValueFromRemainingArguments=$true)]
[string[]]$arguments
)
$nssm_result = Run-Command -command "nssm $cmd"
$command = Argv-ToString -arguments (@($executable) + $arguments)
$result = Run-Command -command $command
return $nssm_result
}
$result.arguments = $command
Function Service-Exists
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$name
)
return [bool](Get-Service "$name" -ErrorAction SilentlyContinue)
return $result
}
Function Nssm-Remove
{
function Get-NssmServiceStatus {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$name
[string]$service
)
if (Service-Exists -name $name)
{
if ((Get-Service -Name $name).Status -ne "Stopped") {
$cmd = "stop ""$name"""
$nssm_result = Nssm-Invoke $cmd
}
$cmd = "remove ""$name"" confirm"
$nssm_result = Nssm-Invoke $cmd
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error removing service ""$name"""
}
$result.changed_by = "remove_service"
$result.changed = $true
}
return Invoke-NssmCommand -arguments @("status", $service)
}
Function Nssm-Install
{
function Get-NssmServiceParameter {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$name,
[string]$service,
[Parameter(Mandatory=$true)]
[AllowEmptyString()]
[string]$application
[Alias("param")]
[string]$parameter,
[Parameter(Mandatory=$false)]
[string]$subparameter
)
if (!$application)
{
Throw "Error installing service ""$name"". No application was supplied."
$arguments = @("get", $service, $parameter)
if($subparameter -ne "") {
$arguments += $subparameter
}
If (-Not (Test-Path -Path $application -PathType Leaf)) {
Throw "$application does not exist on the host"
}
if (!(Service-Exists -name $name))
{
$nssm_result = Nssm-Invoke "install ""$name"" ""$application"""
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error installing service ""$name"""
}
$result.changed_by = "install_service"
$result.changed = $true
} else {
$nssm_result = Nssm-Invoke "get ""$name"" Application"
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error installing service ""$name"""
}
if ($nssm_result.stdout.split("`n`r")[0] -ne $application)
{
$cmd = "set ""$name"" Application ""$application"""
$nssm_result = Nssm-Invoke $cmd
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error installing service ""$name"""
}
$result.application = "$application"
$result.changed_by = "reinstall_service"
$result.changed = $true
}
}
if ($result.changed)
{
$applicationPath = (Get-Item $application).DirectoryName
$cmd = "set ""$name"" AppDirectory ""$applicationPath"""
$nssm_result = Nssm-Invoke $cmd
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error installing service ""$name"""
}
}
return Invoke-NssmCommand -arguments $arguments
}
Function ParseAppParameters()
{
[CmdletBinding()]
function Set-NssmServiceParameter {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[AllowEmptyString()]
[string]$appParameters
[string]$service,
[Parameter(Mandatory=$true)]
[string]$parameter,
[Parameter(Mandatory=$true,ValueFromRemainingArguments=$true)]
[Alias("value")]
[string[]]$arguments
)
$escapedAppParameters = $appParameters.TrimStart("@").TrimStart("{").TrimEnd("}").Replace("; ","`n").Replace("\","\\")
return ConvertFrom-StringData -StringData $escapedAppParameters
return Invoke-NssmCommand -arguments (@("set", $service, $parameter) + $arguments)
}
Function Nssm-Update-AppParameters
{
function Reset-NssmServiceParameter {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$name,
$appParameters,
[string]$appParametersFree
[string]$service,
[Parameter(Mandatory=$true)]
[Alias("param")]
[string]$parameter
)
$cmd = "get ""$name"" AppParameters"
$nssm_result = Nssm-Invoke $cmd
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error updating AppParameters for service ""$name"""
}
$appParamKeys = @()
$appParamVals = @()
$singleLineParams = ""
if ($null -ne $appParameters)
{
$appParametersHash = ParseAppParameters -appParameters $appParameters
$appParamsArray = @()
$appParametersHash.GetEnumerator() | foreach {
$key = $($_.Name)
$val = $($_.Value)
$appParamKeys += $key
$appParamVals += $val
if ($key -ne "_") {
$appParamsArray += $key
}
$appParamsArray += $val
}
$result.nssm_app_parameters_keys = $appParamKeys
$result.nssm_app_parameters_vals = $appParamVals
$singleLineParams = Argv-ToString -arguments $appParamsArray
}
elseif ($null -ne $appParametersFree) {
$result.nssm_app_parameters_free_form = $appParametersFree
$singleLineParams = $appParametersFree
}
$result.nssm_app_parameters = $appParameters
$result.nssm_single_line_app_parameters = $singleLineParams
if ($nssm_result.stdout.split("`n`r")[0] -ne $singleLineParams)
{
# Escape argument line to preserve possible quotes and spaces
$singleLineParams = Escape-Argument -argument $singleLineParams
$cmd = "set ""$name"" AppParameters $singleLineParams"
$nssm_result = Nssm-Invoke $cmd
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error updating AppParameters for service ""$name"""
}
$result.changed_by = "update_app_parameters"
$result.changed = $true
}
return Invoke-NssmCommand -arguments @("reset", $service, $parameter)
}
Function Nssm-Set-Output-Files
{
[CmdletBinding()]
function Update-NssmServiceParameter {
<#
.SYNOPSIS
A generic cmdlet to idempotently set a nssm service parameter.
.PARAMETER service
[String] The service name
.PARAMETER parameter
[String] The name of the nssm parameter to set.
.PARAMETER arguments
[String[]] Target value (or list of value) or array of arguments to pass to the 'nssm set' command.
.PARAMETER compare
[scriptblock] An optionnal idempotency check scriptblock that must return true when
the current value is equal to the desired value. Usefull when 'nssm get' doesn't return
the same value as 'nssm set' takes in argument, like for the ObjectName parameter.
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[Parameter(Mandatory=$true)]
[string]$name,
[string]$stdout,
[string]$stderr
)
$cmd = "get ""$name"" AppStdout"
$nssm_result = Nssm-Invoke $cmd
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error retrieving existing stdout file for service ""$name"""
}
[string]$service,
if ($nssm_result.stdout.split("`n`r")[0] -ne $stdout)
{
if (!$stdout)
{
$cmd = "reset ""$name"" AppStdout"
} else {
$cmd = "set ""$name"" AppStdout $stdout"
}
[Parameter(Mandatory=$true)]
[string]$parameter,
$nssm_result = Nssm-Invoke $cmd
[Parameter(Mandatory=$true,ValueFromRemainingArguments=$true)]
[AllowEmptyString()]
[AllowNull()]
[Alias("value")]
[string[]]$arguments,
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error setting stdout file for service ""$name"""
}
[Parameter()]
[scriptblock]$compare = {param($actual,$expected) @(Compare-Object -ReferenceObject $actual -DifferenceObject $expected).Length -eq 0}
)
$result.changed_by = "set_stdout"
$result.changed = $true
}
if($null -eq $arguments) { return }
$arguments = @($arguments | Where-Object { $_ -ne '' })
$cmd = "get ""$name"" AppStderr"
$nssm_result = Nssm-Invoke $cmd
$nssm_result = Get-NssmServiceParameter -service $service -parameter $parameter
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
if ($nssm_result.rc -ne 0) {
$result.nssm_error_cmd = $nssm_result.arguments
$result.nssm_error_log = $nssm_result.stderr
Throw "Error retrieving existing stderr file for service ""$name"""
Fail-Json -obj $result -message "Error retrieving $parameter for service ""$service"""
}
if ($nssm_result.stdout.split("`n`r")[0] -ne $stderr)
{
if (!$stderr)
{
$cmd = "reset ""$name"" AppStderr"
$nssm_result = Nssm-Invoke $cmd
$current_values = @($nssm_result.stdout.split("`n`r") | Where-Object { $_ -ne '' })
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error clearing stderr file setting for service ""$name"""
if (-not $compare.Invoke($current_values,$arguments)) {
if ($PSCmdlet.ShouldProcess($service, "Update '$parameter' parameter")) {
if($arguments.Count -gt 0) {
$nssm_result = Set-NssmServiceParameter -service $service -parameter $parameter -arguments $arguments
}
else {
$nssm_result = Reset-NssmServiceParameter -service $service -parameter $parameter
}
} else {
$cmd = "set ""$name"" AppStderr $stderr"
$nssm_result = Nssm-Invoke $cmd
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
if ($nssm_result.rc -ne 0) {
$result.nssm_error_cmd = $nssm_result.arguments
$result.nssm_error_log = $nssm_result.stderr
Throw "Error setting stderr file for service ""$name"""
Fail-Json -obj $result -message "Error setting $parameter for service ""$service"""
}
}
$result.changed_by = "set_stderr"
$script:diff_text += "-$parameter = $($current_values -join ', ')`n+$parameter = $($arguments -join ', ')`n"
$result.changed_by = $parameter
$result.changed = $true
}
}
###
# Setup file rotation so we don't accidentally consume too much disk
###
#set files to overwrite
$cmd = "set ""$name"" AppStdoutCreationDisposition 2"
$nssm_result = Nssm-Invoke $cmd
$cmd = "set ""$name"" AppStderrCreationDisposition 2"
$nssm_result = Nssm-Invoke $cmd
#enable file rotation
$cmd = "set ""$name"" AppRotateFiles 1"
$nssm_result = Nssm-Invoke $cmd
#don't rotate until the service restarts
$cmd = "set ""$name"" AppRotateOnline 0"
$nssm_result = Nssm-Invoke $cmd
#both of the below conditions must be met before rotation will happen
#minimum age before rotating
$cmd = "set ""$name"" AppRotateSeconds 86400"
$nssm_result = Nssm-Invoke $cmd
function Test-NssmServiceExists {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$service
)
#minimum size before rotating
$cmd = "set ""$name"" AppRotateBytes 104858"
$nssm_result = Nssm-Invoke $cmd
return [bool](Get-Service -Name $service -ErrorAction SilentlyContinue)
}
Function Nssm-Update-Credentials
{
function Invoke-NssmStart {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$name,
[Parameter(Mandatory=$false)]
[string]$user,
[Parameter(Mandatory=$false)]
[string]$password
[string]$service
)
$cmd = "get ""$name"" ObjectName"
$nssm_result = Nssm-Invoke $cmd
$nssm_result = Invoke-NssmCommand -arguments @("start", $service)
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
if ($nssm_result.rc -ne 0) {
$result.nssm_error_cmd = $nssm_result.arguments
$result.nssm_error_log = $nssm_result.stderr
Throw "Error updating credentials for service ""$name"""
Fail-Json -obj $result -message "Error starting service ""$service"""
}
}
if ($user) {
if (!$password) {
Throw "User without password is informed for service ""$name"""
}
else {
$fullUser = $user
If (-Not($user.contains("@")) -And ($user.Split("\").count -eq 1)) {
$fullUser = ".\" + $user
}
If ($nssm_result.stdout.split("`n`r")[0] -ne $fullUser) {
$cmd = Argv-ToString @("set", $name, "ObjectName", $fullUser, $password)
$nssm_result = Nssm-Invoke $cmd
function Invoke-NssmStop {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$service
)
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error updating credentials for service ""$name"""
}
$nssm_result = Invoke-NssmCommand -arguments @("stop", $service)
$result.changed_by = "update_credentials"
$result.changed = $true
}
}
if ($nssm_result.rc -ne 0) {
$result.nssm_error_cmd = $nssm_result.arguments
$result.nssm_error_log = $nssm_result.stderr
Fail-Json -obj $result -message "Error stopping service ""$service"""
}
}
Function Nssm-Update-Dependencies
{
[CmdletBinding()]
function Start-NssmService {
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[Parameter(Mandatory=$true)]
[string]$name,
[Parameter(Mandatory=$false)]
$dependencies
[string]$service
)
if($null -eq $dependencies) {
# Don't make any change to dependencies if the parameter is omitted
return
}
$cmd = "get ""$name"" DependOnService"
$nssm_result = Nssm-Invoke $cmd
$currentStatus = Get-NssmServiceStatus -service $service
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error updating dependencies for service ""$name"""
if ($currentStatus.rc -ne 0) {
$result.nssm_error_cmd = $currentStatus.arguments
$result.nssm_error_log = $currentStatus.stderr
Fail-Json -obj $result -message "Error starting service ""$service"""
}
$current_dependencies = @($nssm_result.stdout.split("`n`r") | where { $_ -ne '' })
If (@(Compare-Object -ReferenceObject $current_dependencies -DifferenceObject $dependencies).Length -ne 0) {
$dependencies_str = Argv-ToString -arguments $dependencies
$cmd = "set ""$name"" DependOnService $dependencies_str"
$nssm_result = Nssm-Invoke $cmd
if ($currentStatus.stdout -notlike "*SERVICE_RUNNING*") {
if ($PSCmdlet.ShouldProcess($service, "Start service")) {
switch -wildcard ($currentStatus.stdout) {
"*SERVICE_STOPPED*" { Invoke-NssmStart -service $service }
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error updating dependencies for service ""$name"""
"*SERVICE_CONTINUE_PENDING*" { Invoke-NssmStop -service $service; Invoke-NssmStart -service $service }
"*SERVICE_PAUSE_PENDING*" { Invoke-NssmStop -service $service; Invoke-NssmStart -service $service }
"*SERVICE_PAUSED*" { Invoke-NssmStop -service $service; Invoke-NssmStart -service $service }
"*SERVICE_START_PENDING*" { Invoke-NssmStop -service $service; Invoke-NssmStart -service $service }
"*SERVICE_STOP_PENDING*" { Invoke-NssmStop -service $service; Invoke-NssmStart -service $service }
}
}
$result.changed_by = "update-dependencies"
$result.changed_by = "start_service"
$result.changed = $true
}
}
Function Nssm-Update-StartMode
{
[CmdletBinding()]
function Stop-NssmService {
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[Parameter(Mandatory=$true)]
[string]$name,
[Parameter(Mandatory=$true)]
[string]$mode
[string]$service
)
$cmd = "get ""$name"" Start"
$nssm_result = Nssm-Invoke $cmd
$currentStatus = Get-NssmServiceStatus -service $service
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error updating start mode for service ""$name"""
if ($currentStatus.rc -ne 0) {
$result.nssm_error_cmd = $currentStatus.arguments
$result.nssm_error_log = $currentStatus.stderr
Fail-Json -obj $result -message "Error stopping service ""$service"""
}
$modes=@{"auto" = "SERVICE_AUTO_START"; "delayed" = "SERVICE_DELAYED_AUTO_START"; "manual" = "SERVICE_DEMAND_START"; "disabled" = "SERVICE_DISABLED"}
$mappedMode = $modes.$mode
if ($nssm_result.stdout -notlike "*$mappedMode*") {
$cmd = "set ""$name"" Start $mappedMode"
$nssm_result = Nssm-Invoke $cmd
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error updating start mode for service ""$name"""
if ($currentStatus.stdout -notlike "*SERVICE_STOPPED*") {
if ($PSCmdlet.ShouldProcess($service, "Stop service")) {
Invoke-NssmStop -service $service
}
$result.changed_by = "start_mode"
$result.changed_by = "stop_service"
$result.changed = $true
}
}
Function Nssm-Get-Status
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$name
)
$cmd = "status ""$name"""
$nssm_result = Nssm-Invoke $cmd
return $nssm_result
if (($null -ne $appParameters) -and ($null -ne $appArguments)) {
Fail-Json $result "'app_parameters' and 'arguments' are mutually exclusive but have both been set."
}
Function Nssm-Start
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$name
)
$currentStatus = Nssm-Get-Status -name $name
# Backward compatibility for old parameters style. Remove the block bellow in 2.12
if ($null -ne $appParameters) {
Add-DeprecationWarning -obj $result -message "The parameter 'app_parameters' will be removed soon, use 'arguments' instead" -version 2.12
if ($currentStatus.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $currentStatus.stderr
Throw "Error starting service ""$name"""
if ($appParameters -isnot [string]) {
Fail-Json -obj $result -message "The app_parameters parameter must be a string representing a dictionary."
}
switch -wildcard ($currentStatus.stdout)
{
"*SERVICE_RUNNING*" { <# Nothing to do #> }
"*SERVICE_STOPPED*" { Nssm-Start-Service-Command -name $name }
# Convert dict-as-string form to list
$escapedAppParameters = $appParameters.TrimStart("@").TrimStart("{").TrimEnd("}").Replace("; ","`n").Replace("\","\\")
$appParametersHash = ConvertFrom-StringData -StringData $escapedAppParameters
"*SERVICE_CONTINUE_PENDING*" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name }
"*SERVICE_PAUSE_PENDING*" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name }
"*SERVICE_PAUSED*" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name }
"*SERVICE_START_PENDING*" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name }
"*SERVICE_STOP_PENDING*" { Nssm-Stop-Service-Command -name $name; Nssm-Start-Service-Command -name $name }
$appParamsArray = @()
$appParametersHash.GetEnumerator() | Foreach-Object {
if ($_.Name -ne "_") {
$appParamsArray += $_.Name
}
$appParamsArray += $_.Value
}
$appArguments = @($appParamsArray)
# The rest of the code should use only the new $appArguments variable
}
Function Nssm-Start-Service-Command
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$name
)
if ($state -in @("started","stopped","restarted")) {
Add-DeprecationWarning -obj $result -message "The values 'started', 'stopped', and 'restarted' for 'state' will be removed soon, use the win_service module to start or stop the service instead" -version 2.12
}
if ($params.ContainsKey('start_mode')) {
Add-DeprecationWarning -obj $result -message "The parameter 'start_mode' will be removed soon, use the win_service module instead" -version 2.12
}
if ($null -ne $dependencies) {
Add-DeprecationWarning -obj $result -message "The parameter 'dependencies' will be removed soon, use the win_service module instead" -version 2.12
}
if ($null -ne $user) {
Add-DeprecationWarning -obj $result -message "The parameter 'user' will be removed soon, use the win_service module instead" -version 2.12
}
if ($null -ne $password) {
Add-DeprecationWarning -obj $result -message "The parameter 'password' will be removed soon, use the win_service module instead" -version 2.12
}
$cmd = "start ""$name"""
if ($state -ne 'absent') {
if ($null -eq $application) {
Fail-Json -obj $result -message "The application parameter must be defined when the state is not absent."
}
$nssm_result = Nssm-Invoke $cmd
if (-not (Test-Path -LiteralPath $application -PathType Leaf)) {
Fail-Json -obj $result -message "The application specified ""$application"" does not exist on the host."
}
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error starting service ""$name"""
if($null -eq $appDirectory) {
$appDirectory = (Get-Item -LiteralPath $application).DirectoryName
}
$result.changed_by = "start_service"
$result.changed = $true
if ($user -and -not $password) {
Fail-Json -obj $result -message "User without password is informed for service ""$name"""
}
}
Function Nssm-Stop-Service-Command
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$name
)
$cmd = "stop ""$name"""
$service_exists = Test-NssmServiceExists -service $name
$nssm_result = Nssm-Invoke $cmd
if ($state -eq 'absent') {
if ($service_exists) {
if(-not $check_mode) {
if ((Get-Service -Name $name).Status -ne "Stopped") {
$nssm_result = Invoke-NssmStop -service $name
}
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error stopping service ""$name"""
$nssm_result = Invoke-NssmCommand -arguments @("remove", $name, "confirm")
if ($nssm_result.rc -ne 0) {
$result.nssm_error_cmd = $nssm_result.arguments
$result.nssm_error_log = $nssm_result.stderr
Fail-Json -obj $result -message "Error removing service ""$name"""
}
}
$diff_text += "-[$name]"
$result.changed_by = "remove_service"
$result.changed = $true
}
} else {
$diff_text_added_prefix = ''
if (-not $service_exists) {
if(-not $check_mode) {
$nssm_result = Invoke-NssmCommand -arguments @("install", $name, $application)
if ($nssm_result.rc -ne 0) {
$result.nssm_error_cmd = $nssm_result.arguments
$result.nssm_error_log = $nssm_result.stderr
Fail-Json -obj $result -message "Error installing service ""$name"""
}
$service_exists = $true
}
$result.changed_by = "stop_service_command"
$result.changed = $true
}
$diff_text_added_prefix = '+'
$result.changed_by = "install_service"
$result.changed = $true
}
Function Nssm-Stop
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$name
)
$diff_text += "$diff_text_added_prefix[$name]`n"
$currentStatus = Nssm-Get-Status -name $name
# We cannot configure a service that was created above in check mode as it won't actually exist
if ($service_exists) {
$common_params = @{
service = $name
WhatIf = $check_mode
}
if ($currentStatus.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $currentStatus.stderr
Throw "Error stopping service ""$name"""
}
Update-NssmServiceParameter -parameter "Application" -value $application @common_params
Update-NssmServiceParameter -parameter "DisplayName" -value $display_name @common_params
Update-NssmServiceParameter -parameter "Description" -value $description @common_params
Update-NssmServiceParameter -parameter "AppDirectory" -value $appDirectory @common_params
if ($currentStatus.stdout -notlike "*SERVICE_STOPPED*")
{
$cmd = "stop ""$name"""
$nssm_result = Nssm-Invoke $cmd
if ($null -ne $appArguments) {
$singleLineParams = ""
if ($appArguments -is [array]) {
$singleLineParams = Argv-ToString -arguments $appArguments
} else {
$singleLineParams = $appArguments.ToString()
}
$result.nssm_app_parameters = $appArguments
$result.nssm_single_line_app_parameters = $singleLineParams
if ($nssm_result.rc -ne 0)
{
$result.nssm_error_cmd = $cmd
$result.nssm_error_log = $nssm_result.stderr
Throw "Error stopping service ""$name"""
Update-NssmServiceParameter -parameter "AppParameters" -value $singleLineParams @common_params
}
$result.changed_by = "stop_service"
$result.changed = $true
}
}
Function Nssm-Restart
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$name
)
Update-NssmServiceParameter -parameter "AppStdout" -value $stdoutFile @common_params
Update-NssmServiceParameter -parameter "AppStderr" -value $stderrFile @common_params
Nssm-Stop-Service-Command -name $name
Nssm-Start-Service-Command -name $name
}
###
# Setup file rotation so we don't accidentally consume too much disk
###
Function NssmProcedure
{
Nssm-Install -name $name -application $application
Nssm-Update-AppParameters -name $name -appParameters $appParameters -appParametersFree $appParametersFree
Nssm-Set-Output-Files -name $name -stdout $stdoutFile -stderr $stderrFile
Nssm-Update-Dependencies -name $name -dependencies $dependencies
Nssm-Update-Credentials -name $name -user $user -password $password
Nssm-Update-StartMode -name $name -mode $startMode
}
#set files to overwrite
Update-NssmServiceParameter -parameter "AppStdoutCreationDisposition" -value 2 @common_params
Update-NssmServiceParameter -parameter "AppStderrCreationDisposition" -value 2 @common_params
Try
{
switch ($state)
{
"absent" {
Nssm-Remove -name $name
}
"present" {
NssmProcedure
}
"started" {
NssmProcedure
Nssm-Start -name $name
#enable file rotation
Update-NssmServiceParameter -parameter "AppRotateFiles" -value 1 @common_params
#don't rotate until the service restarts
Update-NssmServiceParameter -parameter "AppRotateOnline" -value 0 @common_params
#both of the below conditions must be met before rotation will happen
#minimum age before rotating
Update-NssmServiceParameter -parameter "AppRotateSeconds" -value 86400 @common_params
#minimum size before rotating
Update-NssmServiceParameter -parameter "AppRotateBytes" -value 104858 @common_params
############## DEPRECATED block since 2.8. Remove in 2.12 ##############
Update-NssmServiceParameter -parameter "DependOnService" -arguments $dependencies @common_params
if ($user) {
$fullUser = $user
if (-Not($user.contains("@")) -And ($user.Split("\").count -eq 1)) {
$fullUser = ".\" + $user
}
# Use custom compare callback to test only the username (and not the password)
Update-NssmServiceParameter -parameter "ObjectName" -arguments @($fullUser, $password) -compare {param($actual,$expected) $actual[0] -eq $expected[0]} @common_params
}
"stopped" {
NssmProcedure
Nssm-Stop -name $name
$mappedMode = $start_modes_map.$startMode
Update-NssmServiceParameter -parameter "Start" -value $mappedMode @common_params
if ($state -in "stopped","restarted") {
Stop-NssmService @common_params
}
"restarted" {
NssmProcedure
Nssm-Restart -name $name
if($state -in "started","restarted") {
Start-NssmService @common_params
}
}
########################################################################
Exit-Json $result
}
}
Catch
{
Fail-Json $result $_.Exception.Message
if ($diff_mode -and $result.changed -eq $true) {
$result.diff = @{
prepared = $diff_text
}
}
Exit-Json $result

@ -15,9 +15,10 @@ DOCUMENTATION = r'''
---
module: win_nssm
version_added: "2.0"
short_description: NSSM - the Non-Sucking Service Manager
short_description: Install a service using NSSM
description:
- nssm is a service helper which doesn't suck. See U(https://nssm.cc/) for more information.
- Install a Windows service using the NSSM wrapper.
- NSSM is a service helper which doesn't suck. See U(https://nssm.cc/) for more information.
requirements:
- "nssm >= 2.24.0 # (install via M(win_chocolatey)) C(win_chocolatey: name=nssm)"
options:
@ -29,50 +30,75 @@ options:
state:
description:
- State of the service on the system.
- Note that NSSM actions like "pause", "continue", "rotate" do not fit the declarative style of ansible, so these should be implemented via the
ansible command module.
- Values C(started), C(stopped), and C(restarted) are deprecated since v2.8,
please use the M(win_service) module instead to start, stop or restart the service.
type: str
choices: [ absent, present, started, stopped, restarted ]
default: started
default: present
application:
description:
- The application binary to run as a service
- "Specify this whenever the service may need to be installed (state: present, started, stopped, restarted)"
- "Note that the application name must look like the following, if the directory includes spaces:"
- 'nssm install service "C:\\Program Files\\app.exe\\" "C:\\Path with spaces\\"'
- >
See commit 0b386fc1984ab74ee59b7bed14b7e8f57212c22b in the nssm.git project for more info:
U(https://git.nssm.cc/?p=nssm.git;a=commit;h=0b386fc1984ab74ee59b7bed14b7e8f57212c22b)
- Required when I(state) is C(present), C(started), C(stopped), or C(restarted).
type: path
executable:
description:
- The location of the NSSM utility (in case it is not located in your PATH).
type: path
default: nssm.exe
version_added: "2.8.0"
description:
description:
- The description to set for the service.
type: str
version_added: "2.8.0"
display_name:
description:
- The display name to set for the service.
type: str
version_added: "2.8.0"
working_directory:
version_added: "2.8.0"
description:
- The working directory to run the service executable from (defaults to the directory containing the application binary)
type: path
aliases: [ app_directory, chdir ]
stdout_file:
description:
- Path to receive output.
type: str
type: path
stderr_file:
description:
- Path to receive error output.
type: str
type: path
app_parameters:
description:
- A string representing a dictionary of parameters to be passed to the application when it starts.
- Use either this or C(app_parameters_free_form), not both.
- DEPRECATED since v2.8, please use I(arguments) instead.
- This is mutually exclusive with I(arguments).
type: str
app_parameters_free_form:
arguments:
description:
- Single string of parameters to be passed to the service.
- Use either this or C(app_parameters), not both.
- Parameters to be passed to the application when it starts.
- This can be either a simple string or a list.
- This parameter was renamed from I(app_parameters_free_form) in 2.8.
- This is mutually exclusive with I(app_parameters).
aliases: [ app_parameters_free_form ]
type: str
version_added: "2.3"
dependencies:
description:
- Service dependencies that has to be started to trigger startup, separated by comma.
- DEPRECATED since v2.8, please use the M(win_service) module instead.
type: list
user:
description:
- User to be used for service startup.
- DEPRECATED since v2.8, please use the M(win_service) module instead.
type: str
password:
description:
- Password to be used for service startup.
- DEPRECATED since v2.8, please use the M(win_service) module instead.
type: str
start_mode:
description:
@ -80,84 +106,66 @@ options:
- C(delayed) causes a delayed but automatic start after boot (added in version 2.5).
- C(manual) means that the service will start only when another service needs it.
- C(disabled) means that the service will stay off, regardless if it is needed or not.
- DEPRECATED since v2.8, please use the M(win_service) module instead.
type: str
choices: [ auto, delayed, disabled, manual ]
default: auto
seealso:
- module: win_service
- module: win_service
notes:
- The service will NOT be started after its creation when C(state=present).
- Once the service is created, you can use the M(win_service) module to start it or configure
some additionals properties, such as its startup type, dependencies, service account, and so on.
author:
- Adam Keech (@smadam813)
- George Frank (@georgefrank)
- Hans-Joachim Kliemeck (@h0nIg)
- Michael Wild (@themiwi)
- Kevin Subileau (@ksubileau)
'''
EXAMPLES = r'''
# Install and start the foo service
- win_nssm:
name: foo
application: C:\windows\foo.exe
# Install and start the foo service with a key-value pair argument
# This will yield the following command: C:\windows\foo.exe -bar true
- win_nssm:
name: foo
application: C:\windows\foo.exe
app_parameters: -bar=true
# Install and start the foo service with a single parameter
# This will yield the following command: C:\windows\\foo.exe bar
- win_nssm:
- name: Install the foo service
win_nssm:
name: foo
application: C:\windows\foo.exe
app_parameters: _=bar
# Install and start the foo service with a mix of single params, and key value pairs
# This will yield the following command: C:\windows\\foo.exe bar -file output.bat -foo false
- win_nssm:
name: foo
application: C:\windows\foo.exe
app_parameters: _=bar; -file=output.bat; -foo=false
# This will yield the following command: C:\windows\foo.exe bar "true"
- name: Install the Consul service with a list of parameters
win_nssm:
name: Consul
application: C:\consul\consul.exe
arguments:
- agent
- -config-dir=C:\consul\config
# Use the single line parameters option to specify an arbitrary string of parameters
# for the service executable
- name: Make sure the Consul service runs
# This is strictly equivalent to the previous example
- name: Install the Consul service with an arbitrary string of parameters
win_nssm:
name: consul
name: Consul
application: C:\consul\consul.exe
app_parameters_free_form: agent -config-dir=C:\consul\config
stdout_file: C:\consul\log.txt
stderr_file: C:\consul\error.txt
arguments: agent -config-dir=C:\consul\config
# Install and start the foo service, redirecting stdout and stderr to the same file
- win_nssm:
# Install the foo service, an then configure and start it with win_service
- name: Install the foo service, redirecting stdout and stderr to the same file
win_nssm:
name: foo
application: C:\windows\foo.exe
stdout_file: C:\windows\foo.log
stderr_file: C:\windows\foo.log
# Install and start the foo service, but wait for dependencies tcpip and adf
- win_nssm:
name: foo
application: C:\windows\foo.exe
dependencies: 'adf,tcpip'
# Install and start the foo service with dedicated user
- win_nssm:
- name: Configure and start the foo service using win_service
win_service:
name: foo
application: C:\windows\foo.exe
dependencies: [ adf, tcpip ]
user: foouser
password: secret
# Install the foo service but do not start it automatically
- win_nssm:
name: foo
application: C:\windows\foo.exe
state: present
start_mode: manual
state: started
# Remove the foo service
- win_nssm:
- name: Remove the foo service
win_nssm:
name: foo
state: absent
'''

@ -3,11 +3,11 @@
set_fact:
test_service_cmd: |
$res = @{}
$srvobj = Get-WmiObject Win32_Service -Filter "Name=""$service""" | Select Name,PathName,StartMode,StartName,State
$srvobj = Get-WmiObject Win32_Service -Filter "Name=""$service""" | Select Name,DisplayName,Description,PathName,StartMode,StartName,State
if ($srvobj) {
$srvobj | Get-Member -MemberType *Property | % { $res.($_.name) = $srvobj.($_.name) }
$res.Exists = $true
$res.Dependencies = Get-WmiObject -Query "Associators of {Win32_Service.Name=""$service""} Where AssocClass=Win32_DependentService" | select -ExpandProperty Name
$res.Dependencies = @(Get-WmiObject -Query "Associators of {Win32_Service.Name=""$service""} Where AssocClass=Win32_DependentService" | select -ExpandProperty Name)
$res.Parameters = @{}
$srvkey = "HKLM:\SYSTEM\CurrentControlSet\Services\$service\Parameters"
Get-Item "$srvkey" | Select-Object -ExpandProperty property | % { $res.Parameters.$_ = (Get-ItemProperty -Path "$srvkey" -Name $_).$_}
@ -16,6 +16,24 @@
}
ConvertTo-Json -InputObject $res -Compress
- name: install service (check mode)
win_nssm:
name: '{{ test_service_name }}'
application: C:\Windows\System32\cmd.exe
state: present
register: install_service_check
check_mode: yes
- name: get result of install service (check mode)
win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}'
register: install_service_check_actual
- name: assert results of install service (check mode)
assert:
that:
- install_service_check.changed == true
- (install_service_check_actual.stdout|from_json).Exists == false
- name: install service
win_nssm:
name: '{{ test_service_name }}'
@ -35,6 +53,7 @@
- (install_service_actual.stdout|from_json).State == 'Stopped'
- (install_service_actual.stdout|from_json).StartMode == 'Auto'
- (install_service_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe"
- (install_service_actual.stdout|from_json).Parameters.AppDirectory == "C:\Windows\System32"
- name: test install service (idempotent)
win_nssm:
@ -55,6 +74,7 @@
- (install_service_again_actual.stdout|from_json).State == 'Stopped'
- (install_service_again_actual.stdout|from_json).StartMode == 'Auto'
- (install_service_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe"
- (install_service_again_actual.stdout|from_json).Parameters.AppDirectory == "C:\Windows\System32"
- name: install and start service
win_nssm:
@ -75,12 +95,52 @@
- (install_start_service_actual.stdout|from_json).State == 'Running'
- (install_start_service_actual.stdout|from_json).StartMode == 'Auto'
- (install_start_service_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe"
- (install_start_service_actual.stdout|from_json).Parameters.AppDirectory == "C:\Windows\System32"
- name: install and start service with more parameters (check mode)
win_nssm:
name: '{{ test_service_name }}'
display_name: Ansible testing
description: win_nssm test service
application: C:\Windows\System32\cmd.exe
start_mode: manual
working_directory: '{{ test_win_nssm_path }}'
dependencies: 'tcpip,dnscache'
user: '{{ test_win_nssm_username }}'
password: '{{ test_win_nssm_password }}'
stdout_file: '{{ test_win_nssm_path }}\log.txt'
stderr_file: '{{ test_win_nssm_path }}\error.txt'
state: started
register: install_service_complex_check
check_mode: yes
- name: get result of install and start service with more parameters (check mode)
win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}'
register: install_service_complex_check_actual
- name: assert results of install and start service with more parameters (check mode)
assert:
that:
- install_service_complex_check.changed == true
- (install_service_complex_check_actual.stdout|from_json).Exists == true
- (install_service_complex_check_actual.stdout|from_json).DisplayName == '{{ test_service_name }}'
- (install_service_complex_check_actual.stdout|from_json).Description is none
- (install_service_complex_check_actual.stdout|from_json).StartMode != 'Manual'
- (install_service_complex_check_actual.stdout|from_json).StartName != '.\\' + test_win_nssm_username
- (install_service_complex_check_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe"
- (install_service_complex_check_actual.stdout|from_json).Parameters.AppDirectory == "C:\Windows\System32"
- '"AppStdout" not in (install_service_complex_check_actual.stdout|from_json).Parameters'
- '"AppStderr" not in (install_service_complex_check_actual.stdout|from_json).Parameters'
- (install_service_complex_check_actual.stdout|from_json).Dependencies|length == 0
- name: install and start service with more parameters
win_nssm:
name: '{{ test_service_name }}'
display_name: Ansible testing
description: win_nssm test service
application: C:\Windows\System32\cmd.exe
start_mode: manual
working_directory: '{{ test_win_nssm_path }}'
dependencies: 'tcpip,dnscache'
user: '{{ test_win_nssm_username }}'
password: '{{ test_win_nssm_password }}'
@ -98,10 +158,13 @@
that:
- install_service_complex.changed == true
- (install_service_complex_actual.stdout|from_json).Exists == true
- (install_service_complex_actual.stdout|from_json).DisplayName == 'Ansible testing'
- (install_service_complex_actual.stdout|from_json).Description == 'win_nssm test service'
- (install_service_complex_actual.stdout|from_json).State == 'Running'
- (install_service_complex_actual.stdout|from_json).StartMode == 'Manual'
- (install_service_complex_actual.stdout|from_json).StartName == '.\\' + test_win_nssm_username
- (install_service_complex_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe"
- (install_service_complex_actual.stdout|from_json).Parameters.AppDirectory == test_win_nssm_path
- (install_service_complex_actual.stdout|from_json).Parameters.AppStdout == test_win_nssm_path + '\\log.txt'
- (install_service_complex_actual.stdout|from_json).Parameters.AppStderr == test_win_nssm_path + '\\error.txt'
- (install_service_complex_actual.stdout|from_json).Dependencies|length == 2
@ -111,8 +174,11 @@
- name: install and start service with more parameters (idempotent)
win_nssm:
name: '{{ test_service_name }}'
display_name: Ansible testing
description: win_nssm test service
application: C:\Windows\System32\cmd.exe
start_mode: manual
working_directory: '{{ test_win_nssm_path }}'
# Dependencies order should not trigger a change
dependencies: 'dnscache,tcpip'
user: '{{ test_win_nssm_username }}'
@ -131,70 +197,76 @@
that:
- install_service_complex_again.changed == false
- (install_service_complex_again_actual.stdout|from_json).Exists == true
- (install_service_complex_again_actual.stdout|from_json).DisplayName == 'Ansible testing'
- (install_service_complex_again_actual.stdout|from_json).Description == 'win_nssm test service'
- (install_service_complex_again_actual.stdout|from_json).State == 'Running'
- (install_service_complex_again_actual.stdout|from_json).StartMode == 'Manual'
- (install_service_complex_again_actual.stdout|from_json).StartName == '.\\' + test_win_nssm_username
- (install_service_complex_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe"
- (install_service_complex_again_actual.stdout|from_json).Parameters.AppDirectory == test_win_nssm_path
- (install_service_complex_again_actual.stdout|from_json).Parameters.AppStdout == test_win_nssm_path + '\\log.txt'
- (install_service_complex_again_actual.stdout|from_json).Parameters.AppStderr == test_win_nssm_path + '\\error.txt'
- (install_service_complex_again_actual.stdout|from_json).Dependencies|length == 2
- '"Tcpip" in (install_service_complex_again_actual.stdout|from_json).Dependencies'
- '"Dnscache" in (install_service_complex_again_actual.stdout|from_json).Dependencies'
- name: install service with free form parameters
- name: install service with string form parameters
win_nssm:
name: '{{ test_service_name }}'
application: C:\Windows\System32\cmd.exe
app_parameters_free_form: '-v -Dcom.test.string=value "C:\with space\\"'
arguments: '-v -Dtest.str=value "C:\with space\\"'
state: present
register: free_params
register: str_params
- name: get result of install service with free form parameters
- name: get result of install service with string form parameters
win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}'
register: free_params_actual
register: str_params_actual
- name: assert results of install service with free form parameters
- name: assert results of install service with string form parameters
assert:
that:
- free_params.changed == true
- (free_params_actual.stdout|from_json).Exists == true
- (free_params_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe"
# Expected value: -v -Dcom.test.string=value "C:\with space\\" (backslashes doubled for jinja)
- (free_params_actual.stdout|from_json).Parameters.AppParameters == '-v -Dcom.test.string=value "C:\\with space\\\\"'
- str_params.changed == true
- (str_params_actual.stdout|from_json).Exists == true
- (str_params_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe"
# Expected value: -v -Dtest.str=value "C:\with space\\" (backslashes doubled for jinja)
- (str_params_actual.stdout|from_json).Parameters.AppParameters == '-v -Dtest.str=value "C:\\with space\\\\"'
- name: install service with free form parameters (idempotent)
- name: install service with string form parameters (idempotent)
win_nssm:
name: '{{ test_service_name }}'
application: C:\Windows\System32\cmd.exe
app_parameters_free_form: '-v -Dcom.test.string=value "C:\with space\\"'
arguments: '-v -Dtest.str=value "C:\with space\\"'
state: present
register: free_params_again
register: str_params_again
- name: get result of install service with free form parameters (idempotent)
- name: get result of install service with string form parameters (idempotent)
win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}'
register: free_params_again_actual
register: str_params_again_actual
- name: assert results of install service with free form parameters (idempotent)
- name: assert results of install service with string form parameters (idempotent)
assert:
that:
- free_params_again.changed == false
- (free_params_again_actual.stdout|from_json).Exists == true
- (free_params_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe"
# Expected value: -v -Dcom.test.string=value "C:\with space\\" (backslashes doubled for jinja)
- (free_params_again_actual.stdout|from_json).Parameters.AppParameters == '-v -Dcom.test.string=value "C:\\with space\\\\"'
- name: install service with dict parameters
- str_params_again.changed == false
- (str_params_again_actual.stdout|from_json).Exists == true
- (str_params_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe"
# Expected value: -v -Dtest.str=value "C:\with space\\" (backslashes doubled for jinja)
- (str_params_again_actual.stdout|from_json).Parameters.AppParameters == '-v -Dtest.str=value "C:\\with space\\\\"'
# deprecated in 2.12
- name: install service with dict-as-string parameters
win_nssm:
name: '{{ test_service_name }}'
application: C:\Windows\System32\cmd.exe
app_parameters: foo=true; -file.out=output.bat; -path=C:\with space\; -str=test"quotes; _=bar
register: mixed_params
- name: get result of install service with dict parameters
# deprecated in 2.12
- name: get result of install service with dict-as-string parameters
win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}'
register: mixed_params_actual
- name: assert results of install service with dict parameters
# deprecated in 2.12
- name: assert results of install service with dict-as-string parameters
assert:
that:
- mixed_params.changed == true
@ -203,18 +275,21 @@
# Expected value: bar -file.out output.bat -str "test\"quotes" foo true -path "C:\with space\\" (backslashes doubled for jinja)
- (mixed_params_actual.stdout|from_json).Parameters.AppParameters == 'bar -file.out output.bat -str "test\\"quotes" foo true -path "C:\\with space\\\\"'
- name: install service with dict parameters (idempotent)
# deprecated in 2.12
- name: install service with dict-as-string parameters (idempotent)
win_nssm:
name: '{{ test_service_name }}'
application: C:\Windows\System32\cmd.exe
app_parameters: foo=true; -file.out=output.bat; -path=C:\with space\; -str=test"quotes; _=bar
register: mixed_params_again
- name: get result of install service with dict parameters (idempotent)
# deprecated in 2.12
- name: get result of install service with dict-as-string parameters (idempotent)
win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}'
register: mixed_params_again_actual
- name: assert results of install service with dict parameters (idempotent)
# deprecated in 2.12
- name: assert results of install service with dict-as-string parameters (idempotent)
assert:
that:
- mixed_params_again.changed == false
@ -223,6 +298,81 @@
# Expected value: bar -file.out output.bat -str "test\"quotes" foo true -path "C:\with space\\" (backslashes doubled for jinja)
- (mixed_params_again_actual.stdout|from_json).Parameters.AppParameters == 'bar -file.out output.bat -str "test\\"quotes" foo true -path "C:\\with space\\\\"'
- name: install service with list of parameters
win_nssm:
name: '{{ test_service_name }}'
application: C:\Windows\System32\cmd.exe
arguments:
- -foo=bar
- -day
# Test non-string value
- 14
# Test if dot is not interpreted as separator (see #44079)
- -file.out
# Test if spaces are escaped
- C:\with space\output.bat
- -str
# Test if quotes and backslashes are escaped
- test"quotes\
register: list_params
- name: get result of install service with list of parameters
win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}'
register: list_params_actual
- name: assert results of install service with list of parameters
assert:
that:
- list_params.changed == true
- (list_params_actual.stdout|from_json).Exists == true
- (list_params_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe"
# Expected value: -foo=bar -day 14 -file.out "C:\with space\output.bat" -str "test\"quotes\\" (backslashes doubled for jinja)
- (list_params_actual.stdout|from_json).Parameters.AppParameters == '-foo=bar -day 14 -file.out "C:\\with space\\output.bat" -str "test\\"quotes\\\\"'
- name: install service with list of parameters (idempotent)
win_nssm:
name: '{{ test_service_name }}'
application: C:\Windows\System32\cmd.exe
arguments:
- -foo=bar
- -day
- 14
- -file.out
- C:\with space\output.bat
- -str
- test"quotes\
register: list_params_again
- name: get result of install service with list of parameters (idempotent)
win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}'
register: list_params_again_actual
- name: assert results of install service with list of parameters (idempotent)
assert:
that:
- list_params_again.changed == false
- (list_params_again_actual.stdout|from_json).Exists == true
- (list_params_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe"
# Expected value: -foo=bar -day 14 -file.out "C:\with space\output.bat" -str "test\"quotes\\" (backslashes doubled for jinja)
- (list_params_again_actual.stdout|from_json).Parameters.AppParameters == '-foo=bar -day 14 -file.out "C:\\with space\\output.bat" -str "test\\"quotes\\\\"'
- name: remove service (check mode)
win_nssm:
name: '{{ test_service_name }}'
state: absent
register: remove_service_check
check_mode: yes
- name: get result of remove service (check mode)
win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}'
register: remove_service_check_actual
- name: assert results of remove service (check mode)
assert:
that:
- remove_service_check.changed == true
- (remove_service_check_actual.stdout|from_json).Exists == true
- name: remove service
win_nssm:
name: '{{ test_service_name }}'

@ -91,9 +91,6 @@ lib/ansible/modules/windows/win_iis_website.ps1 PSUseDeclaredVarsMoreThanAssignm
lib/ansible/modules/windows/win_lineinfile.ps1 PSCustomUseLiteralPath
lib/ansible/modules/windows/win_mapped_drive.ps1 PSCustomUseLiteralPath
lib/ansible/modules/windows/win_msg.ps1 PSAvoidTrailingWhitespace
lib/ansible/modules/windows/win_nssm.ps1 PSAvoidUsingCmdletAliases
lib/ansible/modules/windows/win_nssm.ps1 PSCustomUseLiteralPath
lib/ansible/modules/windows/win_nssm.ps1 PSUseApprovedVerbs
lib/ansible/modules/windows/win_package.ps1 PSAvoidTrailingWhitespace
lib/ansible/modules/windows/win_package.ps1 PSCustomUseLiteralPath
lib/ansible/modules/windows/win_package.ps1 PSUseApprovedVerbs

Loading…
Cancel
Save