community.general/lib/ansible/modules/windows/win_wait_for_process.ps1
Charles d5d2c80779 add win_wait_for_process (#40404)
* add win_wait_for_process

* try working on additional filter options and fix derps

* improve process name matching: support arrays. support pre and post wait

* use CIM instead of WIM.  requre exact contain process binary extension

* updates to metadata

* fix errors in waiting for process to start

* validate process min count for absent state.  fix typo

* fix bug if only one processes is detected

* address GitHub comments
2018-08-28 19:44:30 -04:00

162 lines
No EOL
6.1 KiB
PowerShell

#!powershell
# This file is part of Ansible
# Copyright (c) 2017 Ansible Project
# 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.FileUtil
$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. use 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 "present","absent"
$timeout = Get-AnsibleParam -obj $params -name "timeout" -type "int" -default 300
$result = @{
changed = $false
}
# validate the input
if ($state -eq "absent" -and $sleep -ne 1)
{
Add-Warning $result "sleep parameter has no effect when waiting for a process to stop."
}
if ($state -eq "absent" -and $process_min_count -ne 1)
{
Add-Warning $result "process_min_count parameter has no effect when waiting for a process to stop."
}
if (($process_name_exact -or $process_name_pattern) -and $process_id)
{
Fail-json $result "process_id may not be used with process_name_exact or process_name_pattern."
}
if ($process_name_exact -and $process_name_pattern)
{
Fail-json $result "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 $result "at least one of: process_name_exact, process_name_pattern, process_id, or owner must be supplied."
}
$module_start = Get-Date
#Get-Process doesn't actually return a UserName value, so get it from WMI.
Function Get-ProcessMatchesFilter {
[cmdletbinding()]
Param(
[String]
$Owner,
$ProcessNameExact,
$ProcessNamePattern,
[int]
$ProcessId
)
$CIMProcesses = Get-CimInstance Win32_Process
foreach ($CIMProcess in $CIMProcesses)
{
$include = $true
if(-not [String]::IsNullOrEmpty($ProcessNamePattern))
{
#if a process name was specified in the filter, validate that here.
$include = $include -and ($CIMProcess.ProcessName -match $ProcessNamePattern)
}
if($ProcessNameExact -is [Array] -or (-not [String]::IsNullOrEmpty($ProcessNameExact)))
{
#if a process name was specified in the filter, validate that here.
if ($ProcessNameExact -is [Array] )
{
$include = $include -and ($ProcessNameExact -contains $CIMProcess.ProcessName)
}
else {
$include = $include -and ($ProcessNameExact -eq $CIMProcess.ProcessName)
}
}
if ($ProcessId -and $ProcessId -ne 0)
{
# if a PID was specified in the filger, validate that here.
$include = $include -and ($CIMProcess.ProcessId -eq $ProcessId)
}
if (-not [String]::IsNullOrEmpty($Owner) )
{
# if an owner was specified in the filter, validate that here.
$include = $include -and ($($(Invoke-CimMethod -InputObject $CIMProcess -MethodName GetOwner).User) -eq $Owner)
}
if ($include)
{
$CIMProcess | Select-Object -Property ProcessId, ProcessName, @{name="Owner";Expression={$($(Invoke-CimMethod -InputObject $CIMProcess -MethodName GetOwner).User)}}
}
}
}
Start-Sleep -Seconds $pre_wait_delay
if ($state -eq "present" ) {
#wait for a process to start
$Processes = @()
$attempts = 0
Do {
if (((Get-Date) - $module_start).TotalSeconds -gt $timeout)
{
$result.elapsed = ((Get-Date) - $module_start).TotalSeconds
Fail-Json $result "timeout while waiting for $process_name to start. waited $timeout seconds"
}
$Processes = Get-ProcessMatchesFilter -Owner $owner -ProcessNameExact $process_name_exact -ProcessNamePattern $process_name_pattern -ProcessId $process_id
Start-Sleep -Seconds $sleep
$attempts ++
$ProcessCount = $null
if ($Processes -is [array]) {
$ProcessCount = $Processes.count
}
elseif ($null -ne $Processes) {
$ProcessCount = 1
}
else {
$ProcessCount = 0
}
} While ($ProcessCount -lt $process_min_count)
if ($attempts -gt 0)
{
$result.changed = $true
}
$result.matched_processess = $Processes
}
elseif ($state -eq "absent") {
#wait for a process to stop
$Processes = Get-ProcessMatchesFilter -Owner $owner -ProcessNameExact $process_name_exact -ProcessNamePattern $process_name_pattern -ProcessId $process_id
$result.matched_processes = $Processes
$ProcessCount = $(if ($Processes -is [array]) { $Processes.count } elseif ($Processes){ 1 } else {0})
if ($ProcessCount -gt 0 )
{
try {
Wait-Process -Id $($Processes | Select-Object -ExpandProperty ProcessId) -Timeout $timeout -ErrorAction Stop
$result.changed = $true
}
catch {
$result.elapsed = ((Get-Date) - $module_start).TotalSeconds
Fail-Json $result "$($_.Exception.Message). timeout while waiting for $process_name to stop. waited $timeout seconds"
}
}
else{
$result.changed = $false
}
}
Start-Sleep -Seconds $post_wait_delay
$result.elapsed = ((Get-Date) - $module_start).TotalSeconds
Exit-Json $result