Merge pull request #1306 from username-is-already-taken2/digitalgaz/windows_task

Adding windows_task resource
This commit is contained in:
Christoph Hartmann 2016-11-25 11:46:42 -07:00 committed by GitHub
commit a990d20fcd
8 changed files with 281 additions and 1 deletions

View file

@ -0,0 +1,103 @@
---
title: About the windows_task Resource
---
# windows_task
Use the `windows_task` Inspec audit resource to test a scheduled tasks configuration on a Windows platform.
Microsoft and application vendors use scheduled tasks to perform a varity of system maintaince tasks but system administrators can schedule their own.
## Syntax
A `windows_task` resource block declares the name of the task (as its full path) and tests its configuration:
describe windows_task('task name uri' do
its('parameter') { should eq 'value' }
it { should be_enabled }
end
where
* `'parameter'` must be a valid parameter defined within this resource ie `logon_mode`, `last_result`, `task_to_run`, `run_as_user`
* `'value'` will be used to compare the value gather from your chosen parameter
* `'be_enabled'` is an example of a valid matcher that checks the state of a task, other examples are `exist` or `be_disabled`
## Matchers
This InSpec audit resource has the following matchers:
### be
<%= partial "/shared/matcher_be" %>
### cmp
<%= partial "/shared/matcher_cmp" %>
### eq
<%= partial "/shared/matcher_eq" %>
### include
<%= partial "/shared/matcher_include" %>
### match
<%= partial "/shared/matcher_match" %>
## Examples
The following examples show how to use this InSpec resource.
### Test's that a task is enabled
```
describe windows_task('\Microsoft\Windows\Time Synchronization\SynchronizeTime') do
it { should be_enabled }
end
```
### Test's that a task is disabled
```
describe windows_task('\Microsoft\Windows\AppID\PolicyConverter') do
it { should be_disabled }
end
```
### Test's the configuration parameters of a task
```
describe windows_task('\Microsoft\Windows\AppID\PolicyConverter') do
its('logon_mode') { should eq 'Interactive/Background' }
its('last_result') { should eq '1' }
its('task_to_run') { should cmp '%Windir%\system32\appidpolicyconverter.exe' }
its('run_as_user') { should eq 'LOCAL SERVICE' }
end
```
### Test's that a task is defined
```
describe windows_task('\Microsoft\Windows\Defrag\ScheduledDefrag') do
it { should exist }
end
```
## Gathering Tasknames
Rather then use the GUI you can use the `schtasks.exe` to output a full list of tasks available on the system
`schtasks /query /FO list`
rather than use the `list` output you can use `CSV` if it is easier.
Please make sure you use the full TaskName (include the prefix `\`) within your control
```
C:\>schtasks /query /FO list
...
Folder: \Microsoft\Windows\Diagnosis
HostName: XPS15
TaskName: \Microsoft\Windows\Diagnosis\Scheduled
Next Run Time: N/A
Status: Ready
Logon Mode: Interactive/Background
...
```

View file

@ -125,6 +125,7 @@ require 'resources/sys_info'
require 'resources/users'
require 'resources/vbscript'
require 'resources/windows_feature'
require 'resources/windows_task'
require 'resources/xinetd'
require 'resources/wmi'
require 'resources/yum'

View file

@ -0,0 +1,106 @@
# encoding: utf-8
# author: Gary Bright @username-is-already-taken2
# author: Chris Beard @cdbeard2016
module Inspec::Resources
class WindowsTasks < Inspec.resource(1)
name 'windows_task'
desc 'Use the windows_task InSpec audit resource to test task schedules on Microsoft Windows.'
example "
describe windows_task('\\Microsoft\\Windows\\Time Synchronization\\SynchronizeTime') do
it { should be_enabled }
end
describe windows_task('\\Microsoft\\Windows\\AppID\\PolicyConverter') do
it { should be_disabled }
end
describe windows_task('\\Microsoft\\Windows\\Defrag\\ScheduledDefrag') do
it { should exist }
end
describe windows_task('\\Microsoft\\Windows\\AppID\\PolicyConverter') do
its('logon_mode') { should eq 'Interactive/Background' }
its('last_result') { should eq '1' }
its('task_to_run') { should cmp '%Windir%\\system32\\appidpolicyconverter.exe' }
its('run_as_user') { should eq 'LOCAL SERVICE' }
end
"
def initialize(taskuri)
@taskuri = taskuri
@cache = nil
# verify that this resource is only supported on Windows
return skip_resource 'The `windows_task` resource is not supported on your OS.' unless inspec.os.windows?
end
def exists?
return true unless info.nil? || info[:uri].nil?
false
end
# rubocop:disable Style/WordArray
def enabled?
return false if info.nil? || info[:state].nil?
['Ready', 'Running'].include?(info[:state])
end
def disabled?
return false if info.nil? || info[:state].nil?
info[:scheduled_task_state] == 'Disabled' || info[:state] == 'Disabled'
end
def logon_mode
info[:logon_mode]
end
def last_result
info[:last_result]
end
def task_to_run
info[:task_to_run].to_s.strip
end
def run_as_user
info[:run_as_user]
end
def type
info[:type] unless info.nil?
end
def info
return @cache unless @cache.nil?
# PowerShell v5 has Get-ScheduledTask cmdlet,
# _using something with backward support to v3_
# script = "Get-ScheduledTask | ? { $_.URI -eq '#{@taskuri}' } | Select-Object URI,@{N='State';E={$_.State.ToString()}} | ConvertTo-Json"
# Using schtasks as suggested by @modille but aligning property names to match cmdlet to future proof.
script = "schtasks /query /v /fo csv /tn '#{@taskuri}' | ConvertFrom-Csv | Select @{N='URI';E={$_.TaskName}},@{N='State';E={$_.Status.ToString()}},'Logon Mode','Last Result','Task To Run','Run As User','Scheduled Task State' | ConvertTo-Json -Compress"
cmd = inspec.powershell(script)
begin
params = JSON.parse(cmd.stdout)
rescue JSON::ParserError => _e
return nil
end
@cache = {
uri: params['URI'],
state: params['State'],
logon_mode: params['Logon Mode'],
last_result: params['Last Result'],
task_to_run: params['Task To Run'],
run_as_user: params['Run As User'],
scheduled_task_state: params['Scheduled Task State'],
type: 'windows-task',
}
end
def to_s
"Windows Task '#{@taskuri}'"
end
end
end

View file

@ -262,7 +262,11 @@ class MockLoader
'hostname' => cmd.call('hostname'),
# hostname windows
'$env:computername' => cmd.call('$env-computername'),
}
# windows_task doesnt exist
"schtasks /query /v /fo csv /tn 'does-not-exist' | ConvertFrom-Csv | Select @{N='URI';E={$_.TaskName}},@{N='State';E={$_.Status.ToString()}},'Logon Mode','Last Result','Task To Run','Run As User','Scheduled Task State' | ConvertTo-Json -Compress" => cmd.call('schtasks-error'),
# windows_task exist
"schtasks /query /v /fo csv /tn 'WeLovePizza' | ConvertFrom-Csv | Select @{N='URI';E={$_.TaskName}},@{N='State';E={$_.Status.ToString()}},'Logon Mode','Last Result','Task To Run','Run As User','Scheduled Task State' | ConvertTo-Json -Compress" => cmd.call('schtasks-success'),
}
@backend
end

View file

@ -0,0 +1,42 @@
# author: Gary Bright
# author: Chris Beard
return unless os.windows?
if os.release.to_f == 6.3
# describe a task that exists on Windows 2012 R2
# Test the task without the resource
describe powershell('Test-Path $env:windir\System32\schtasks.exe') do
its('stdout') { should match(/^True/) }
end
describe windows_task('\Microsoft\Windows\AppID\PolicyConverter') do
it { should exist }
it { should be_disabled }
its('logon_mode') { should eq 'Interactive/Background' }
its('last_result') { should eq '1' }
its('task_to_run') { should cmp '%Windir%\system32\appidpolicyconverter.exe' }
its('run_as_user') { should eq 'LOCAL SERVICE' }
end
describe windows_task('\Microsoft\Windows\AppID\SmartScreenSpecific') do
it { should be_enabled }
end
# Testing regular expressions
describe windows_task('\Microsoft\Windows\AppID\VerifiedPublisherCertStoreCheck') do
its('task_to_run') { should match(/^[a-zA-Z0-9%]*\\[a-zA-Z0-9]*\\[a-zA-Z0-9\.]*$/) }
end
end
if os.release.to_f == 6.1
# describe a task that exists on Windows 2008 R2
describe windows_task('\Microsoft\Windows\Defrag\ScheduledDefrag') do
it { should exist }
it { should be_enabled }
its('logon_mode') { should eq 'Interactive/Background' }
its('last_result') { should eq '1' }
its('task_to_run') { should cmp '%windir%\system32\defrag.exe -c' }
its('run_as_user') { should eq 'SYSTEM' }
end
end

View file

@ -0,0 +1 @@
ERROR: The system cannot find the path specified.

View file

@ -0,0 +1 @@
{"URI":"\\Microsoft\\Windows\\Time Synchronization\\SynchronizeTime","State":"Ready","Logon Mode":"Interactive/Background","Last Result":"1056","Task To Run":"%windir%\\system32\\sc.exe start w32time task_started","Run As User":"LOCAL SERVICE","Scheduled Task State":"Enabled"}

View file

@ -0,0 +1,22 @@
# author: Gary Bright
# author: Chris Beard
require 'helper'
require 'inspec/resource'
describe 'Inspec::Resources::WindowsTasks' do
it 'verify parsing a windows task that does not exist' do
resource_fail = MockLoader.new(:windows).load_resource('windows_task', 'does-not-exist')
_(resource_fail.exists?).must_equal false
end
it 'verify parsing a windows task which is valid' do
resource = MockLoader.new(:windows).load_resource('windows_task', 'WeLovePizza')
_(resource.enabled?).must_equal true
_(resource.disabled?).must_equal false
_(resource.logon_mode).must_equal 'Interactive/Background'
_(resource.last_result).must_equal '1056'
_(resource.task_to_run).must_equal '%windir%\\system32\\sc.exe start w32time task_started'
_(resource.run_as_user).must_equal 'LOCAL SERVICE'
_(resource.type).must_equal 'windows-task'
_(resource.to_s).must_equal "Windows Task 'WeLovePizza'"
end
end