Skip to content

Commit 0c7df14

Browse files
committed
adding new property "group" to run the scheduled task in group context (TASK_LOGON_GROUP)
1 parent ce528f1 commit 0c7df14

File tree

5 files changed

+93
-10
lines changed

5 files changed

+93
-10
lines changed

REFERENCE.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ The basic property that the resource should be in.
6666

6767
Default value: `present`
6868

69+
##### `group`
70+
71+
The group to run the scheduled task as.
72+
73+
Please also note that Puppet must be running as a privileged user
74+
in order to manage `scheduled_task` resources. Running as an
75+
unprivileged user will result in 'access denied' errors.
76+
6977
##### `trigger`
7078

7179
One or more triggers defining when the task should run. A single trigger is
@@ -205,7 +213,8 @@ conditions will fail with a reported error of 'The operation
205213
completed successfully'. It is recommended that you either
206214
choose another user to run the scheduled task, or alter the
207215
security policy to allow v1 scheduled tasks to run as the
208-
'SYSTEM' account. Defaults to 'SYSTEM'.
216+
'SYSTEM' account.
217+
Defaults to 'SYSTEM' unless the group property is set.
209218

210219
Please also note that Puppet must be running as a privileged user
211220
in order to manage `scheduled_task` resources. Running as an
@@ -216,8 +225,6 @@ user does not end with a dollar sign (`$`) signifying a Group
216225
Managed Service Account (gMSA), the task will be created with
217226
`Run only when user is logged on` specified.
218227

219-
Default value: `system`
220-
221228
##### `working_dir`
222229

223230
The full path of the directory in which to start the command.

lib/puppet/provider/scheduled_task/taskscheduler_api2.rb

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ def user
5959
account
6060
end
6161

62+
def group
63+
task.group_information
64+
end
65+
6266
def compatibility
6367
task.compatibility
6468
end
@@ -79,6 +83,14 @@ def user_insync?(current, should)
7983
Puppet::Util::Windows::SID.name_to_sid(current) == Puppet::Util::Windows::SID.name_to_sid(should[0])
8084
end
8185

86+
def group_insync?(current, should)
87+
return false unless current
88+
89+
# By comparing account SIDs we don't have to worry about case
90+
# sensitivity, or canonicalization of the account name.
91+
Puppet::Util::Windows::SID.name_to_sid(current) == Puppet::Util::Windows::SID.name_to_sid(should[0])
92+
end
93+
8294
def trigger_insync?(current, should)
8395
should = [should] unless should.is_a?(Array)
8496
current = [current] unless current.is_a?(Array)
@@ -177,12 +189,18 @@ def user=(value)
177189
end
178190
end
179191

192+
def group=(value)
193+
raise("Invalid group: #{value}") unless Puppet::Util::Windows::SID.name_to_sid(value)
194+
195+
task.set_group_information(value)
196+
end
197+
180198
def create
181199
@triggers = nil
182200
@task = PuppetX::PuppetLabs::ScheduledTask::Task.new(resource[:name])
183201
self.command = resource[:command]
184202

185-
[:arguments, :working_dir, :enabled, :trigger, :user, :compatibility, :description].each do |prop|
203+
[:arguments, :working_dir, :enabled, :trigger, :user, :group, :compatibility, :description].each do |prop|
186204
send("#{prop}=", resource[prop]) if resource[prop]
187205
end
188206
end
@@ -202,7 +220,7 @@ def flush
202220
# this is a Windows security feature with the v1 COM APIs that prevent
203221
# arbitrary reassignment of a task scheduler command to run as SYSTEM
204222
# without the authorization to do so
205-
self.user = resource[:user]
223+
self.user = resource[:user] unless resource[:group]
206224
task.save
207225
@task = nil
208226
end

lib/puppet/provider/scheduled_task/win32_taskscheduler.rb

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ def user
5656
account
5757
end
5858

59+
def group
60+
task.group_information
61+
end
62+
5963
def trigger
6064
@triggers ||= task.triggers.compact # remove nils for unsupported trigger types
6165
end
@@ -72,6 +76,14 @@ def user_insync?(current, should)
7276
Puppet::Util::Windows::SID.name_to_sid(current) == Puppet::Util::Windows::SID.name_to_sid(should[0])
7377
end
7478

79+
def group_insync?(current, should)
80+
return false unless current
81+
82+
# By comparing account SIDs we don't have to worry about case
83+
# sensitivity, or canonicalization of the account name.
84+
Puppet::Util::Windows::SID.name_to_sid(current) == Puppet::Util::Windows::SID.name_to_sid(should[0])
85+
end
86+
7587
def trigger_insync?(current, should)
7688
should = [should] unless should.is_a?(Array)
7789
current = [current] unless current.is_a?(Array)
@@ -153,12 +165,18 @@ def user=(value)
153165
end
154166
end
155167

168+
def group=(value)
169+
raise("Invalid group: #{value}") unless Puppet::Util::Windows::SID.name_to_sid(value)
170+
171+
task.set_group_information(value)
172+
end
173+
156174
def create
157175
@triggers = nil
158176
@task = PuppetX::PuppetLabs::ScheduledTask::Task.new(resource[:name], :v1_compatibility)
159177
self.command = resource[:command]
160178

161-
[:arguments, :working_dir, :enabled, :trigger, :user, :description].each do |prop|
179+
[:arguments, :working_dir, :enabled, :trigger, :user, :group, :description].each do |prop|
162180
send("#{prop}=", resource[prop]) if resource[prop]
163181
end
164182
end
@@ -177,7 +195,7 @@ def flush
177195
# this is a Windows security feature with the v1 COM APIs that prevent
178196
# arbitrary reassignment of a task scheduler command to run as SYSTEM
179197
# without the authorization to do so
180-
self.user = resource[:user]
198+
self.user = resource[:user] unless resource[:group]
181199
task.save
182200
@task = nil
183201
end

lib/puppet/type/scheduled_task.rb

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@
7777
completed successfully'. It is recommended that you either
7878
choose another user to run the scheduled task, or alter the
7979
security policy to allow v1 scheduled tasks to run as the
80-
'SYSTEM' account. Defaults to 'SYSTEM'.
80+
'SYSTEM' account.
81+
Defaults to 'SYSTEM' unless the group property is set.
8182
8283
Please also note that Puppet must be running as a privileged user
8384
in order to manage `scheduled_task` resources. Running as an
@@ -88,7 +89,7 @@
8889
Managed Service Account (gMSA), the task will be created with
8990
`Run only when user is logged on` specified."
9091

91-
defaultto :system
92+
defaultto { :system if @resource[:group].nil? || @resource[:group].empty? }
9293

9394
def insync?(current)
9495
provider.user_insync?(current, @should)
@@ -103,6 +104,18 @@ def insync?(current)
103104
to determine if a scheduled task is in sync or not."
104105
end
105106

107+
newproperty(:group) do
108+
desc "The group to run the scheduled task as.
109+
110+
Please also note that Puppet must be running as a privileged user
111+
in order to manage `scheduled_task` resources. Running as an
112+
unprivileged user will result in 'access denied' errors."
113+
114+
def insync?(current)
115+
provider.group_insync?(current, @should)
116+
end
117+
end
118+
106119
newproperty(:compatibility, required_features: :compatibility) do
107120
desc "The compatibility level associated with the task. May currently be set
108121
to 1 for compatibility with tasks on a Windows XP or Windows Server

lib/puppet_x/puppet_labs/scheduled_task/task.rb

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,15 @@ def initialize(task_name, compatibility_level = nil)
218218
# definition populated when task exists, otherwise new
219219
@task, @definition = self.class.task(@full_task_path)
220220
task_userid = @definition.Principal.UserId || ''
221+
task_groupid = @definition.Principal.GroupId || ''
221222

222223
self.compatibility = TASK_COMPATIBILITY::TASK_COMPATIBILITY_V1 if compatibility_level == :v1_compatibility
223224

224-
set_account_information(task_userid, nil)
225+
if task_groupid == ''
226+
set_group_information(task_groupid)
227+
else
228+
set_account_information(task_userid, nil)
229+
end
225230
end
226231

227232
# API v1 Compatibility list
@@ -320,6 +325,8 @@ def save
320325
task_user = nil
321326
task_password = nil
322327

328+
# user and password should be nil if task should run in
329+
# service account or group context
323330
case @definition.Principal.LogonType
324331
when TASK_LOGON_TYPE::TASK_LOGON_PASSWORD,
325332
TASK_LOGON_TYPE::TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD
@@ -369,6 +376,18 @@ def set_account_information(user, password)
369376
true
370377
end
371378

379+
# Sets the +group+ for the given task if the task should run in a group context.
380+
# If successfull then true is returned.
381+
#
382+
def set_group_information(group)
383+
@definition.Principal.RunLevel = TASK_RUNLEVEL_TYPE::TASK_RUNLEVEL_HIGHEST
384+
385+
@definition.Principal.GroupId = group
386+
@definition.Principal.LogonType = TASK_LOGON_TYPE::TASK_LOGON_GROUP
387+
388+
true
389+
end
390+
372391
# Returns the user associated with the task or nil if no user has yet
373392
# been associated with the task.
374393
#
@@ -377,6 +396,14 @@ def account_information
377396
principal&.UserId
378397
end
379398

399+
# Returns the group associated with the task or nil if no group has yet
400+
# been associated with the task.
401+
#
402+
def group_information
403+
principal = @definition.Principal
404+
principal&.GroupId
405+
end
406+
380407
# Returns the name of the application associated with the task.
381408
#
382409
def application_name

0 commit comments

Comments
 (0)