Press "Enter" to skip to content

Automate Windows service actions using non-admin service accounts, powershell, and task scheduler – Part 1

This week I received a ticket from a user requesting help automating a Windows service state via Powershell and Windows Task Scheduler. The user had wrote a basic script to start and stop selected application services and was attempting to schedule them using Task Scheduler.

Pre-requisites:

  1. Basic understanding of Powershell scripting
  2. Basic understanding of how to task scheduler works
  3. Know how to write/apply/update and test GPOs

Part 1 – The Task Scheduler

For anyone who has tried executing a Powershell script via task scheduler, it can be a bit tricky to figure out whats happening because not all actions are transparent to the user. In this case the task scheduler would run successfully but the services would not start/stop. However, if you ran the script manually via a Powershell console, everything works as expected. In this case I figured I should probably try to figure out if task scheduler was actually running the script.

Lets take a look at the stop_services.ps1 powershell script. Then we will look at the task scheduler configuration. And finally we’ll see what we can do for logging to ensure the script is running.

# $ServiceName accepts wildcards and will search the SERVICE DISPLAYNAME for a match! 
# Service displayname is what you see in the services mmc under the name collumn.
$ServiceName = "*Spooler*"

$servicearray = Get-Service | Where-Object { $_.DisplayName -like "$($ServiceName)" }

for ($i = 0; $i -lt $servicearray.Length; $i++) {
    Write-host "Stopping $($servicearray[$i].DisplayName) Service"
    Stop-Service $servicearray[$i].Name
}

Note: In this example, I am substituting our production services and using the print spooler service. If you’d like to use this code, substitute your service or services using a wildcard, as shown.

Configured task to run as a service account, with no admin rights.
Task will run daily at 10am local time.
Here we configure the action to be performed, in our case run a powershell script
Arguments: -NonInteractive -NoLogo -NoProfile -ExecutionPolicy Bypass -File “C:\temp\demo\stop_services.ps1”

If you’d like to create this task scheduler task using Powershell see the following snippet:

# Configurable Items
$ScriptPath = "C:\temp\demo\stop_services.ps1" # Full path to script you'd like to execute
$UserName = "DOMAIN\your-service-account" # Service account name in which script should be executed as
$TaskName = "Stop Services" # Task display name
$Trigger = New-ScheduledTaskTrigger -At 10:00am –Daily # Specify when and how oftent the task should be ran

# Scheduled task creation after this point
$Action = New-ScheduledTaskAction -Execute "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -Argument "-NonInteractive -NoLogo -NoProfile -ExecutionPolicy Bypass -File ""$($ScriptPath)"""

$Settings = New-ScheduledTaskSettingsSet -DontStopOnIdleEnd -RestartInterval (New-TimeSpan -Minutes 1) -RestartCount 10 -StartWhenAvailable
$Settings.ExecutionTimeLimit = "PT0S"

$Credentials = Get-Credential -UserName "$($UserName)" -Message "Enter password: "
$Password = $Credentials.GetNetworkCredential().Password

$Task = New-ScheduledTask -Action $Action -Trigger $Trigger -Settings $Settings
$Task | Register-ScheduledTask -TaskName "$($TaskName)" -User "$($UserName)" -Password $Password


Once the task is configured, you may notice if you try to run the task it will error with “Task Scheduler failed to start “\Stop Services” task for user “DOMAIN\User”. Additional Data: Error Value: 2147943785.”

We receive this error because the service account does not have permission to run scripts on the host. To grant this privileged we need to give the service account the ability to “Logon as a batch job” by defining it in the local group policy.

Click Start, type “gpedit.msc”, configure the following policy:
[Computer Configuration\Windows Settings\Security Settings\Local Policies\User Rights Assignment]

Select “Logon as a batch job”, then add your service account.

Once you’ve granted the service account permission, re-run your scheduled task. You should see that it now runs, and if we look in the history we can see a successful execution.

This is great! Or is it? We now have the ability to run a script on a host with no admin privileges. We’re done right? Not so fast… Our goal was to stop the services. If we take a look at the Print Spooler service, we can see its still running. So what happened? Did the script not run?

As far as we know, the script ran. Task scheduler logged an event 201 “Action Completed” and the script returned code 0. Since there is no visibility into what the script is doing when it’s ran from Task Scheduler, we have a couple options. We can either write extensive logging into the script to write debug information to disk, or we can use start-transcript. By using this, we can log the entire powershell session to disk, quickly and easily. Lets modify our script to include start transcript (and stop-transcript). Wrap the script with start and stop transcript as shown on line 1 and 14. Modify the file path appropriately, then save.

Start-Transcript -Path "C:\temp\demo\transcript.log"

# $ServiceName accepts wildcards and will search the SERVICE DISPLAYNAME for a match! 
# Service displayname is what you see in the services mmc under the name collumn.
$ServiceName = "*Spooler*"

$servicearray = Get-Service | Where-Object { $_.DisplayName -like "$($ServiceName)" }

for ($i = 0; $i -lt $servicearray.Length; $i++) {
    Write-host "Stopping $($servicearray[$i].DisplayName) Service"
    Stop-Service $servicearray[$i].Name
}

Stop-Transcript

After modifying the script, run your Scheduled Task once more. Check the path you specified for the log file.

**********************
Windows PowerShell transcript start
Start time: 20191227100002
Username: AD\is-svc-serv-test
RunAs User: AD\is-svc-serv-test
Configuration Name: 
Machine: your_host (Microsoft Windows NT 10.0.17763.0)
Host Application: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NonInteractive -NoLogo -NoProfile -ExecutionPolicy Bypass -File C:\temp\demo\stop_services.ps1
Process ID: 12492
PSVersion: 5.1.17763.771
PSEdition: Desktop
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.17763.771
BuildVersion: 10.0.17763.771
CLRVersion: 4.0.30319.42000
WSManStackVersion: 3.0
PSRemotingProtocolVersion: 2.3
SerializationVersion: 1.1.0.1
**********************
Transcript started, output file is C:\temp\demo\transcript.log
PS>TerminatingError(Get-Service): "Cannot open Service Control Manager on computer '.'. This operation might require other privileges."
Get-Service : Cannot open Service Control Manager on computer '.'. This operation might require other privileges.
At C:\temp\demo\stop_services.ps1:7 char:17
+ $servicearray = Get-Service | Where-Object { $_.DisplayName -like "$( ...
+                 ~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Get-Service], InvalidOperationException
    + FullyQualifiedErrorId : System.InvalidOperationException,Microsoft.PowerShell.Commands.GetServiceCommand
Get-Service : Cannot open Service Control Manager on computer '.'. This operation might require
other privileges.
At C:\temp\demo\stop_services.ps1:7 char:17
+ $servicearray = Get-Service | Where-Object { $_.DisplayName -like "$( ...
+                 ~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Get-Service], InvalidOperationException
    + FullyQualifiedErrorId : System.InvalidOperationException,Microsoft.PowerShell.Commands.GetSe
   rviceCommand

**********************
Windows PowerShell transcript end
End time: 20191227100002
**********************

Wow, helpful right? Now we can see what happened and why the service didn’t stop. The script is throwing and error on Get-Service. More specifically:

"Get-Service : Cannot open Service Control Manager on computer '.'. This operation might require other privileges."

This is certainly not what we want, but at least it points us in a general direction. We know we are running this as a limited user, with no admin rights. It’s more than likely this service account doesn’t have permission to user the Service Control Manager. We’ll take a look at that in the next section.

Sharing is caring!

Leave a Reply

Your email address will not be published. Required fields are marked *

three + one =