summaryrefslogtreecommitdiff
path: root/lib/ansible/modules/windows/win_wait_for_process.ps1
blob: 5b18e30eaaf0401c826bd5a366dbe54af566c744 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#!powershell

# Copyright: (c) 2017, Ansible Project
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

#Requires -Module Ansible.ModuleUtils.Legacy
#Requires -Module Ansible.ModuleUtils.SID

$ErrorActionPreference = "Stop"

$params = Parse-Args -arguments $args -supports_check_mode $true

$process_name_exact = Get-AnsibleParam -obj $params -name "process_name_exact" -type "list"
$process_name_pattern = Get-AnsibleParam -obj $params -name "process_name_pattern" -type "str"
$process_id = Get-AnsibleParam -obj $params -name "pid" -type "int" -default 0  # pid is a reserved variable in PowerShell, using process_id instead.
$owner = Get-AnsibleParam -obj $params -name "owner" -type "str"
$sleep = Get-AnsibleParam -obj $params -name "sleep" -type "int" -default 1
$pre_wait_delay = Get-AnsibleParam -obj $params -name "pre_wait_delay" -type "int" -default 0
$post_wait_delay = Get-AnsibleParam -obj $params -name "post_wait_delay" -type "int" -default 0
$process_min_count = Get-AnsibleParam -obj $params -name "process_min_count" -type "int" -default 1
$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent","present"
$timeout = Get-AnsibleParam -obj $params -name "timeout" -type "int" -default 300

$result = @{
    changed = $false
    elapsed = 0
    matched_processes = @()
}

# Validate the input
if ($state -eq "absent" -and $sleep -ne 1) {
    Add-Warning -obj $result -message "Parameter 'sleep' has no effect when waiting for a process to stop."
}

if ($state -eq "absent" -and $process_min_count -ne 1) {
    Add-Warning -obj $result -message "Parameter 'process_min_count' has no effect when waiting for a process to stop."
}

if (($process_name_exact -or $process_name_pattern) -and $process_id) {
    Fail-Json -obj $result -message "Parameter 'pid' may not be used with process_name_exact or process_name_pattern."
}
if ($process_name_exact -and $process_name_pattern) {
    Fail-Json -obj $result -message "Parameter 'process_name_exact' and 'process_name_pattern' may not be used at the same time."
}

if (-not ($process_name_exact -or $process_name_pattern -or $process_id -or $owner)) {
    Fail-Json -obj $result -message "At least one of 'process_name_exact', 'process_name_pattern', 'pid' or 'owner' must be supplied."
}

if ($owner -and ("IncludeUserName" -notin (Get-Command -Name Get-Process).Parameters.Keys)) {
    Fail-Json -obj $result -message "This version of Powershell does not support filtering processes by 'owner'."
}

Function Get-FilteredProcesses {
    [cmdletbinding()]
    Param(
        [String]
        $Owner,
        $ProcessNameExact,
        $ProcessNamePattern,
        [int]
        $ProcessId
    )

    $FilteredProcesses = @()

    try {
        $Processes = Get-Process -IncludeUserName
        $SupportsUserNames = $true
    } catch [System.Management.Automation.ParameterBindingException] {
        $Processes = Get-Process
        $SupportsUserNames = $false
    }

    foreach ($Process in $Processes) {

        # If a process name was specified in the filter, validate that here.
        if ($ProcessNamePattern) {
            if ($Process.ProcessName -notmatch $ProcessNamePattern) {
                continue
            }
        }

        # If a process name was specified in the filter, validate that here.
        if ($ProcessNameExact -is [Array]) {
            if ($ProcessNameExact -notcontains $Process.ProcessName) {
                continue
            }
        } elseif ($ProcessNameExact) {
            if ($ProcessNameExact -ne $Process.ProcessName) {
                continue
            }
        }

        # If a PID was specified in the filter, validate that here.
        if ($ProcessId -and $ProcessId -ne 0) {
            if ($ProcessId -ne $Process.Id) {
                continue
            }
        }

        # If an owner was specified in the filter, validate that here.
        if ($Owner) {
            if (-not $Process.UserName) {
                continue
            } elseif ((Convert-ToSID($Owner)) -ne (Convert-ToSID($Process.UserName))) {  # NOTE: This is rather expensive
                continue
            }
        }

        if ($SupportsUserNames -eq $true) {
            $FilteredProcesses += @{ name = $Process.ProcessName; pid = $Process.Id; owner = $Process.UserName }
        } else {
            $FilteredProcesses += @{ name = $Process.ProcessName; pid = $Process.Id }
        }
    }

    return ,$FilteredProcesses
}

$module_start = Get-Date
Start-Sleep -Seconds $pre_wait_delay

if ($state -eq "present" ) {

    # Wait for a process to start
    do {

        $Processes = Get-FilteredProcesses -Owner $owner -ProcessNameExact $process_name_exact -ProcessNamePattern $process_name_pattern -ProcessId $process_id
        $result.matched_processes = $Processes

        if ($Processes.count -ge $process_min_count) {
            break
        }

        if (((Get-Date) - $module_start).TotalSeconds -gt $timeout) {
            $result.elapsed = ((Get-Date) - $module_start).TotalSeconds
            Fail-Json -obj $result -message "Timed out while waiting for process(es) to start"
        }

        Start-Sleep -Seconds $sleep

    } while ($true)

} elseif ($state -eq "absent") {

    # Wait for a process to stop
    $Processes = Get-FilteredProcesses -Owner $owner -ProcessNameExact $process_name_exact -ProcessNamePattern $process_name_pattern -ProcessId $process_id
    $result.matched_processes = $Processes

    if ($Processes.count -gt 0 ) {
        try {
            # This may randomly fail when used on specially protected processes (think: svchost)
            Wait-Process -Id $Processes.pid -Timeout $timeout
        } catch [System.TimeoutException] {
            $result.elapsed = ((Get-Date) - $module_start).TotalSeconds
            Fail-Json -obj $result -message "Timeout while waiting for process(es) to stop"
        }
    }

}

Start-Sleep -Seconds $post_wait_delay
$result.elapsed = ((Get-Date) - $module_start).TotalSeconds

Exit-Json -obj $result