diff --git a/lib/ansible/modules/windows/win_scheduled_task.ps1 b/lib/ansible/modules/windows/win_scheduled_task.ps1 index d949561492..06cfb3c38b 100644 --- a/lib/ansible/modules/windows/win_scheduled_task.ps1 +++ b/lib/ansible/modules/windows/win_scheduled_task.ps1 @@ -26,13 +26,55 @@ $result = @{ changed = $false } +function Invoke-TaskPathCheck { + [CmdletBinding(SupportsShouldProcess=$true)] + param($Path, [Switch]$Remove) + + $pathResults = @{ + PathExists = $null + RemovedPath = $false + } + + if ($path -ne "\") { + $trimmedPath = $Path.TrimEnd("\") + } + else { + $trimmedPath = $Path + } + + $scheduleObject = New-Object -ComObject Schedule.Service + $scheduleObject.Connect() + + try { + $targetFolder = $scheduleObject.GetFolder($trimmedPath) + $pathResults.PathExists = $true + } + catch { + $pathResults.PathExists = $false + } + + if ($Remove -and $pathResults.PathExists) { + $childFolders = $targetFolder.GetFolders($null) + $childTasks = $targetFolder.GetTasks($null) + + if ($childFolders.Count -eq 0 -and $childTasks.Count -eq 0) { + if ($PSCmdlet.ShouldProcess($trimmedPath, "Remove task path")) { + $rootFolder = $scheduleObject.GetFolder("\") + $rootFolder.DeleteFolder($trimmedPath, $null) + } + $pathResults.RemovedPath = $true + } + } + + return $pathResults +} + $params = Parse-Args $args -supports_check_mode $true $check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false $arguments = Get-AnsibleParam -obj $params -name "arguments" -type "str" -aliases "argument" $description = Get-AnsibleParam -obj $params -name "description" -type "str" -default "No description." $enabled = Get-AnsibleParam -obj $params -name "enabled" -type "bool" -default $true -# TODO: We do not create the TaskPath if missing $path = Get-AnsibleParam -obj $params -name "path" -type "str" -default '\' # Required vars @@ -53,7 +95,7 @@ $days_of_week = Get-AnsibleParam -obj $params -name "days_of_week" -type "str" - try { - $task = Get-ScheduledTask -TaskPath "$path" | Where-Object {$_.TaskName -eq "$name"} + $task = Get-ScheduledTask | Where-Object {$_.TaskName -eq $name -and $_.TaskPath -eq $path} # Correlate task state to enable variable, used to calculate if state needs to be changed $taskState = if ($task) { $task.State } else { $null } @@ -76,6 +118,7 @@ try { # Nothing to do $result.exists = $false $result.msg = "Task does not exist" + Exit-Json $result } elseif ($measure.count -eq 0){ @@ -107,6 +150,14 @@ try { Unregister-ScheduledTask -TaskName $name -Confirm:$false -WhatIf:$check_mode $result.changed = $true $result.msg = "Deleted task $name" + + # Remove task path if it exists + $pathResults = Invoke-TaskPathCheck -Path $path -Remove -WhatIf:$check_mode + + if ($pathResults.RemovedPath) { + $result.msg += " and task path $path removed" + } + Exit-Json $result } elseif ( ($state -eq "absent") -and (-not $exists) ) { @@ -131,25 +182,41 @@ try { } if ( ($state -eq "present") -and (-not $exists) ){ + # Check task path prior to registering + $pathResults = Invoke-TaskPathCheck -Path $path + if (-not $check_mode) { Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $name -Description $description -TaskPath $path -Settings $settings -Principal $principal -# $task = Get-ScheduledTask -TaskName $name } + $result.changed = $true $result.msg = "Added new task $name" + + if (!$pathResults.PathExists) { + $result.msg += " and task path $path created" + } } elseif( ($state -eq "present") -and ($exists) ) { + # Check task path prior to registering + $pathResults = Invoke-TaskPathCheck -Path $path + if ($task.Description -eq $description -and $task.TaskName -eq $name -and $task.TaskPath -eq $path -and $task.Actions.Execute -eq $executable -and $taskState -eq $enabled -and $task.Principal.UserId -eq $user) { # No change in the task $result.msg = "No change in task $name" } else { Unregister-ScheduledTask -TaskName $name -Confirm:$false -WhatIf:$check_mode + if (-not $check_mode) { + $oldPathResults = Invoke-TaskPathCheck -Path $task.TaskPath -Remove Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $name -Description $description -TaskPath $path -Settings $settings -Principal $principal } $result.changed = $true $result.msg = "Updated task $name" + + if (!$pathResults.PathExists) { + $result.msg += " and task path $path created" + } } } diff --git a/lib/ansible/modules/windows/win_scheduled_task.py b/lib/ansible/modules/windows/win_scheduled_task.py index 194cebb0fd..82c9038bda 100644 --- a/lib/ansible/modules/windows/win_scheduled_task.py +++ b/lib/ansible/modules/windows/win_scheduled_task.py @@ -81,7 +81,8 @@ options: - Days of the week to run a weekly task, not idempotent path: description: - - Task folder in which this task will be stored + - Task folder in which this task will be stored - creates a non-existent path when C(state) is C(present), + and removes an empty path when C(state) is C(absent) default: '\' ''' @@ -92,7 +93,7 @@ EXAMPLES = r''' description: open command prompt executable: cmd arguments: -opt1 -opt2 - path: example + path: \example time: 9am frequency: daily state: present diff --git a/test/integration/targets/win_scheduled_task/tasks/tests.yml b/test/integration/targets/win_scheduled_task/tasks/tests.yml index af28c04f57..15e1cfd1ba 100644 --- a/test/integration/targets/win_scheduled_task/tasks/tests.yml +++ b/test/integration/targets/win_scheduled_task/tasks/tests.yml @@ -123,3 +123,91 @@ that: - remove_scheduled_task_again.changed == false - remove_scheduled_task_again.exists == false + + +# Test scheduled task path creation and removal + +- name: Remove potentially leftover new path task 1 + win_scheduled_task: &wstp1_absent + name: Ansible Test New Path 1 + path: \non_existent_path\ + state: absent + +- name: Remove potentially leftover new path task 2 + win_scheduled_task: &wstp2_absent + name: Ansible Test New Path 2 + path: \non_existent_path\ + state: absent + + +- name: Add scheduled task new path 1 + win_scheduled_task: &wstp1_present + name: Ansible Test New Path 1 + description: A test of functionality + executable: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe + arguments: -ExecutionPolicy Unrestricted -NonInteractive -File C:\TestDir\Test.ps1 + time: "9:45pm" + path: \non_existent_path\ + frequency: once + state: present + enabled: yes + user: SYSTEM + register: add_scheduled_task_new_path_1 + +- name: Test add_scheduled_task_new_path_1 + assert: + that: + - add_scheduled_task_new_path_1.msg == 'Added new task Ansible Test New Path 1 and task path \\non_existent_path\\ created' + + +- name: Add scheduled task new path 2 + win_scheduled_task: &wstp2_present + <<: *wstp1_present + name: Ansible Test New Path 2 + register: add_scheduled_task_new_path_2 + +- name: Test add_scheduled_task_new_path_2 (normal mode) + assert: + that: + - add_scheduled_task_new_path_2.msg == 'Added new task Ansible Test New Path 2' + when: not in_check_mode + +- name: Test add_scheduled_task_new_path_2 (check-mode) + assert: + that: + - add_scheduled_task_new_path_2.msg == 'Added new task Ansible Test New Path 2 and task path \\non_existent_path\\ created' + when: in_check_mode + + +- name: Remove scheduled task new path 2 + win_scheduled_task: *wstp2_absent + register: remove_scheduled_task_new_path_2 + +- name: Test remove_scheduled_task_new_path_2 (normal mode) + assert: + that: + - remove_scheduled_task_new_path_2.msg == 'Deleted task Ansible Test New Path 2' + when: not in_check_mode + +- name: Test remove_scheduled_task_new_path_2 (check-mode) + assert: + that: + - remove_scheduled_task_new_path_2.msg == 'Task does not exist' + when: in_check_mode + + +- name: Remove scheduled task new path 1 + win_scheduled_task: *wstp1_absent + register: remove_scheduled_task_new_path_1 + +- name: Test remove_scheduled_task_new_path_1 (normal mode) + assert: + that: + - remove_scheduled_task_new_path_1.msg == 'Deleted task Ansible Test New Path 1 and task path \\non_existent_path\\ removed' + when: not in_check_mode + +- name: Test remove_scheduled_task_new_path_1 (check-mode) + assert: + that: + - remove_scheduled_task_new_path_1.msg == 'Task does not exist' + when: in_check_mode