From 72e7927dd5cf27d5efe9fd904cd5caf57688e961 Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Wed, 15 Mar 2017 15:11:24 +0100 Subject: [PATCH] win_scheduled_task: Added frequency: once and check_mode support (#22611) * win_scheduled_task: Added frequency: once and check_mode support This patch includes: - Renamed `execute:` parameter to `executable:` - Renamed `argument:` parameter to `arguments:` - Implemented `frequency: once` support - Implemented check_mode support - Fix idempotency issue related to empty description - Added integration tests * Improve the integration test structure I think this is a great way to test normal mode and check-mode from the same playbook. * Small fixes after review --- .../modules/windows/win_scheduled_task.ps1 | 135 +++++++++--------- .../modules/windows/win_scheduled_task.py | 23 ++- .../targets/win_scheduled_task/aliases | 1 + .../targets/win_scheduled_task/tasks/main.yml | 46 ++++++ .../win_scheduled_task/tasks/tests.yml | 26 ++++ 5 files changed, 149 insertions(+), 82 deletions(-) create mode 100644 test/integration/targets/win_scheduled_task/aliases create mode 100644 test/integration/targets/win_scheduled_task/tasks/main.yml create mode 100644 test/integration/targets/win_scheduled_task/tasks/tests.yml diff --git a/lib/ansible/modules/windows/win_scheduled_task.ps1 b/lib/ansible/modules/windows/win_scheduled_task.ps1 index 8804028345f..d9495614922 100644 --- a/lib/ansible/modules/windows/win_scheduled_task.ps1 +++ b/lib/ansible/modules/windows/win_scheduled_task.ps1 @@ -22,47 +22,35 @@ $ErrorActionPreference = "Stop" # WANT_JSON # POWERSHELL_COMMON -$params = Parse-Args $args; - -$days_of_week = Get-AnsibleParam $params -name "days_of_week" -$enabled = Get-AnsibleParam $params -name "enabled" -default $true -$enabled = $enabled | ConvertTo-Bool -$description = Get-AnsibleParam $params -name "description" -default " " -$path = Get-AnsibleParam $params -name "path" -type "path" -$argument = Get-AnsibleParam $params -name "argument" - -$result = New-Object PSObject; -Set-Attr $result "changed" $false; - -#Required vars -$name = Get-AnsibleParam -obj $params -name name -failifempty $true -resultobj $result -$state = Get-AnsibleParam -obj $params -name state -failifempty $true -resultobj $result -validateSet "present","absent" - -#Vars conditionally required -$present_args_required = $state -eq "present" -$execute = Get-AnsibleParam -obj $params -name execute -failifempty $present_args_required -resultobj $result -$frequency = Get-AnsibleParam -obj $params -name frequency -failifempty $present_args_required -resultobj $result -$time = Get-AnsibleParam -obj $params -name time -failifempty $present_args_required -resultobj $result -$user = Get-AnsibleParam -obj $params -name user -failifempty $present_args_required -resultobj $result - - -# Mandatory Vars -if ($frequency -eq "weekly") -{ - if (!($days_of_week)) - { - Fail-Json $result "missing required argument: days_of_week" - } +$result = @{ + changed = $false } -if ($path) -{ - $path = "\{0}\" -f $path -} -else -{ - $path = "\" #default -} +$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 +$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" + +# Vars conditionally required +$present = $state -eq "present" +$executable = Get-AnsibleParam -obj $params -name "executable" -type "str" -aliases "execute" -failifempty $present +$frequency = Get-AnsibleParam -obj $params -name "frequency" -type "str" -validateset "once","daily","weekly" -failifempty $present +$time = Get-AnsibleParam -obj $params -name "time" -type "str" -failifempty $present + +# TODO: We should default to the current user +$user = Get-AnsibleParam -obj $params -name "user" -type "str" -failifempty $present + +$weekly = $frequency -eq "weekly" +$days_of_week = Get-AnsibleParam -obj $params -name "days_of_week" -type "str" -failifempty $weekly + try { $task = Get-ScheduledTask -TaskPath "$path" | Where-Object {$_.TaskName -eq "$name"} @@ -85,7 +73,9 @@ try { $exists = $true } elseif ( ($measure.count -eq 0) -and ($state -eq "absent") ){ - Set-Attr $result "msg" "Task does not exist" + # Nothing to do + $result.exists = $false + $result.msg = "Task does not exist" Exit-Json $result } elseif ($measure.count -eq 0){ @@ -96,69 +86,76 @@ try { Fail-Json $result "$($measure.count) scheduled tasks found" } - Set-Attr $result "exists" "$exists" + $result.exists = $exists if ($frequency){ - if ($frequency -eq "daily") { - $trigger = New-ScheduledTaskTrigger -Daily -At $time + if ($frequency -eq "once") { + $trigger = New-ScheduledTaskTrigger -Once -At $time + } + elseif ($frequency -eq "daily") { + $trigger = New-ScheduledTaskTrigger -Daily -At $time } elseif ($frequency -eq "weekly"){ - $trigger = New-ScheduledTaskTrigger -Weekly -At $time -DaysOfWeek $days_of_week + $trigger = New-ScheduledTaskTrigger -Weekly -At $time -DaysOfWeek $days_of_week } else { Fail-Json $result "frequency must be daily or weekly" } } - if ( ($state -eq "absent") -and ($exists -eq $true) ) { - Unregister-ScheduledTask -TaskName $name -Confirm:$false + if ( ($state -eq "absent") -and ($exists) ) { + Unregister-ScheduledTask -TaskName $name -Confirm:$false -WhatIf:$check_mode $result.changed = $true - Set-Attr $result "msg" "Deleted task $name" + $result.msg = "Deleted task $name" Exit-Json $result } - elseif ( ($state -eq "absent") -and ($exists -eq $false) ) { - Set-Attr $result "msg" "Task $name does not exist" + elseif ( ($state -eq "absent") -and (-not $exists) ) { + $result.msg = "Task $name does not exist" Exit-Json $result } $principal = New-ScheduledTaskPrincipal -UserId "$user" -LogonType ServiceAccount - if ($enabled -eq $false){ - $settings = New-ScheduledTaskSettingsSet -Disable - } - else { + if ($enabled){ $settings = New-ScheduledTaskSettingsSet } + else { + $settings = New-ScheduledTaskSettingsSet -Disable + } - if ($argument) { - $action = New-ScheduledTaskAction -Execute $execute -Argument $argument + if ($arguments) { + $action = New-ScheduledTaskAction -Execute $executable -Argument $arguments } else { - $action = New-ScheduledTaskAction -Execute $execute + $action = New-ScheduledTaskAction -Execute $executable } - if ( ($state -eq "present") -and ($exists -eq $false) ){ - Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $name -Description $description -TaskPath $path -Settings $settings -Principal $principal - $task = Get-ScheduledTask -TaskName $name - Set-Attr $result "msg" "Added new task $name" + if ( ($state -eq "present") -and (-not $exists) ){ + 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" } - elseif( ($state -eq "present") -and ($exists -eq $true) ) { - if ($task.Description -eq $description -and $task.TaskName -eq $name -and $task.TaskPath -eq $path -and $task.Actions.Execute -eq $execute -and $taskState -eq $enabled -and $task.Principal.UserId -eq $user) { - #No change in the task - Set-Attr $result "msg" "No change in task $name" + elseif( ($state -eq "present") -and ($exists) ) { + 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 - Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $name -Description $description -TaskPath $path -Settings $settings -Principal $principal - Set-Attr $result "msg" "Updated task $name" + Unregister-ScheduledTask -TaskName $name -Confirm:$false -WhatIf:$check_mode + if (-not $check_mode) { + Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $name -Description $description -TaskPath $path -Settings $settings -Principal $principal + } $result.changed = $true + $result.msg = "Updated task $name" } } - Exit-Json $result; + Exit-Json $result } catch { Fail-Json $result $_.Exception.Message -} \ No newline at end of file +} diff --git a/lib/ansible/modules/windows/win_scheduled_task.py b/lib/ansible/modules/windows/win_scheduled_task.py index c363b9df51c..194cebb0fd7 100644 --- a/lib/ansible/modules/windows/win_scheduled_task.py +++ b/lib/ansible/modules/windows/win_scheduled_task.py @@ -41,7 +41,6 @@ options: description: description: - The description for the scheduled task - required: false enabled: description: - Enable/disable the task @@ -59,30 +58,27 @@ options: user: description: - User to run scheduled task as - required: false - execute: + executable: description: - Command the scheduled task should execute - required: false - argument: + aliases: [ execute ] + arguments: description: - Arguments to provide scheduled task action - required: false + aliases: [ argument ] frequency: description: - The frequency of the command, not idempotent - required: false choices: + - once - daily - weekly time: description: - Time to execute scheduled task, not idempotent - required: false days_of_week: description: - Days of the week to run a weekly task, not idempotent - required: false path: description: - Task folder in which this task will be stored @@ -93,12 +89,13 @@ EXAMPLES = r''' # Create a scheduled task to open a command prompt - win_scheduled_task: name: TaskName - execute: cmd - frequency: daily - time: 9am description: open command prompt + executable: cmd + arguments: -opt1 -opt2 path: example - enable: yes + time: 9am + frequency: daily state: present + enabled: yes user: SYSTEM ''' diff --git a/test/integration/targets/win_scheduled_task/aliases b/test/integration/targets/win_scheduled_task/aliases new file mode 100644 index 00000000000..c6d61981670 --- /dev/null +++ b/test/integration/targets/win_scheduled_task/aliases @@ -0,0 +1 @@ +windows/ci/group3 diff --git a/test/integration/targets/win_scheduled_task/tasks/main.yml b/test/integration/targets/win_scheduled_task/tasks/main.yml new file mode 100644 index 00000000000..ae22b7923bf --- /dev/null +++ b/test/integration/targets/win_scheduled_task/tasks/main.yml @@ -0,0 +1,46 @@ +# NOTE: The win_scheduled_task module only works on Win2012+ + +- name: Test Windows capabilities + raw: Get-Command New-ScheduledTask -ErrorAction SilentlyContinue; $? + failed_when: no + register: new_scheduledtask + +- name: Set boolean for capability + set_fact: + has_new_scheduledtask: '{{ new_scheduledtask.rc == 0 }}' + +- name: Test in normal mode + when: has_new_scheduledtask + block: + - include: tests.yml + + - name: Check the various tasks in normal mode + assert: + that: + - add_scheduled_task.changed == true + - add_scheduled_task.exists == false + - add_scheduled_task_again.changed == false + - add_scheduled_task_again.exists == true + - remove_scheduled_task.changed == true + - remove_scheduled_task.exists == true + - remove_scheduled_task_again.changed == false + - remove_scheduled_task_again.exists == false + + +- name: Test in check-mode + check_mode: yes + when: has_new_scheduledtask + block: + - include: tests.yml + + - name: Check the various tests in check-mode + assert: + that: + - add_scheduled_task.changed == true + - add_scheduled_task.exists == false + - add_scheduled_task_again.changed == true + - add_scheduled_task_again.exists == false + - remove_scheduled_task.changed == false + - remove_scheduled_task.exists == false + - remove_scheduled_task_again.changed == false + - remove_scheduled_task_again.exists == false diff --git a/test/integration/targets/win_scheduled_task/tasks/tests.yml b/test/integration/targets/win_scheduled_task/tasks/tests.yml new file mode 100644 index 00000000000..96f9556ec9f --- /dev/null +++ b/test/integration/targets/win_scheduled_task/tasks/tests.yml @@ -0,0 +1,26 @@ +- name: Remove potentially leftover scheduled task + win_scheduled_task: &wst_absent + name: Test + state: absent + +- name: Add scheduled task + win_scheduled_task: &wst_present + name: Test + executable: dir.exe + arguments: C:\Windows\Temp\ + frequency: once + time: 5pm + user: SYSTEM + register: add_scheduled_task + +- name: Add scheduled task (again) + win_scheduled_task: *wst_present + register: add_scheduled_task_again + +- name: Remove scheduled task + win_scheduled_task: *wst_absent + register: remove_scheduled_task + +- name: Remove scheduled task (again) + win_scheduled_task: *wst_absent + register: remove_scheduled_task_again