Press "Enter" to skip to content

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

In part 1 we wrote a script that will stop the print spooler process. We found if we ran it with a service account without any rights assigned on the local box via task scheduler we received an error. In part 2 we set permissions on the Service Control Manager. In part 3 of this series we are going to take a look at permissions of a Windows service and how to grant them. This is required if we want to allow a non-admin user to do anything with them, including starting/stopping/pausing services.

Lets do a few more tests with this script to define the scope of the issue, then we’ll go about fixing it. First off we know the script fails to run via task scheduler as our service account. What happens when we run it with an admin user?

From an elevated powershell window (user with local admin rights), we see the script runs without issue. And we can now see that the service was stopped successfully. So we can confirm stopping a service needs additional permissions. Does it need local admin? No, it doesn’t.

There are a few ways we can grant granular access to a Windows service. Unfortunately the properties dialog of the service is not one of them. The good news is there are at least 4 other ways configure this.

  1. Directly via SC.EXE
  2. SubInACL tool
  3. Sysinternals Process Explorer
  4. Security Template and GPO

Now to discuss the pro’s and con’s of these three options. We should also note, there ARE other ways to configure permissions on Windows services. They are outside the scope of this article, for more information here is a helpful resource.

Adding permissions using SC.EXE

We can add permissions via the command prompt and SC.EXE (service controller). To get the existing permissions we can use:

sc.exe sdshow spooler


These are the current permissions assigned in Security Descriptor Definition Language (SDDL) format, pretty gross right? We can definitely add our user using sc.exe by building our permissions in SDDL format and applying them with:

sc.exe sdset spooler "your SDDL permission string here"

But as you can tell, the SDDL format is pretty rough. Sure, you can decode it with the help of a cheat sheet, and you could build your string and apply it using the command above. But what happens when your colleague needs to troubleshoot an issue with this service, or if you need to replace the server. Not only is it not a user friendly string, the change is in no way transparent. And for this reason, I chose not to apply permissions this way.

Adding permissions with SubInACL tool

Using the SubInACL tool, created by Mark Russinovich and now Microsoft we can set these permissions using easy to use commands. Download here and a great blog post for how to use the tool is here.

To read current permissions:
subinacl.exe /service "spooler" /display=dacl

To assign permissions to the print spooler service using SubInAcl use the following:

subinacl.exe /service spooler /grant=domain\yourserviceaccount=PTO

In that command “PTO” are the permissions we are granting the user. Here are the available permissions to use with subinacl:
F : Full Control
R : Generic Read
W : Generic Write
X : Generic eXecute
L : Read controL
Q : Query Service Configuration
S : Query Service Status
E : Enumerate Dependent Services
C : Service Change Configuration
T : Start Service
O : Stop Service
P : Pause/Continue Service
I : Interrogate Service
U : Service User-Defined Control Commands

To revoke permissions to a service:
subinacl /service spooler/revoke=domain\yourserviceaccount

While this the SubInAcl.exe method is a lot more user friendly to add/remove permissions, it still not what I’d call a transparent change. It’s not easy to detect it without explicit documentation and because of this it could cause you issues if the server needed to be rebuilt from scratch. Also it requires a separate download.

You might choose this option however if you don’t have a configuration management system in place. Meaning, you are not using Group Policy to configure your servers, you are not using something like Puppet, Ansible, Chef.

Adding Permissions with Process Explorer

If you would prefer to shy away from the command prompt and use a GUI method of applying service permission, you can use a separate utility called Process Explorer. This utility is offered up as part of the Sysinternals suite by Microsoft (same with SubInAcl tool).

This tool is typically used from monitoring and troubleshooting of a Windows system, however it also has the ability to modify permission at a service level using a very familiar permission dialog. You can get download the SysInternals bundle from Microsoft. Located here.

Once you’ve got the SysInternals bundle installed, run Process Explorer. This will give you a list of running processes. Find spoolsv.exe (or your service), right click -> Properties.

This dialog should look very familiar. We can add permissions very easily to the service. You may have noticed it can be a bit cumbersome to find the process which corresponds to your service however. Also this procedure requires to use of a additional software to apply the permissions. Not all bad however. This procedure might work well for you. Again like using SC.EXE and SubInACL, the change is not exactly transparent, which could cause issues mentioned above.

Security Templates and GPO

The last method I will discuss (and my preferred method) is to use a Security Template and GPO to apply modify permissions. This method has a few steps and requires your to have your workstation/server domain joined. Although you could use the security template on a standalone server if you choose.

To get started we will first need to create the Security Template. Its important to note, if the service is a local Default Windows Service such as the print spooler, the template could be created on any host that has the service running. However, in many cases the service(s) you may be working with could be from a third party or specific to a single or group of computers/server. If you are unsure, it’s best to just create the Security Template on the host where you plan to have the permissions applied, then import them to the GPO.

To get started, open an mmc.exe console and add the Security Templates snap-in as shown below:

Create a New Template, give it a name and description.

Navigate to your new template -> System Services. Then find the service(s) you would like to apply permissions to. Do this one at a time. Double click the service, Check the Define this policy check box, define the service startup mode, in our case we want the print spooler to start automatically with Windows. Then click Edit Security.

This opens a very familiar permissions dialog. We can then go add permissions to our service account. Click Add, find your user or group, then click okay to add. Grant the Read and Start, Stop Pause permissions.

Note: For illustration I’ve been assigning permissions directly to our service account. If you are in a domain environment, I would highly recommend assigning permissions to a domain group which contains your service account. This is the recommended best practice for assigning permissions!

After clicking OK, you may see this dialog box, click Yes.

Now we need to save our template. Right click the template, Save As. This will save it as an INF file for later use. It will be located in the C:\Users\%username%\Documents\Security\Templates folder by default.

If we take a look at the INF file, you can see it creates the permissions using SDDL format as we saw with the sc.exe sdshow spooler command. In theory you could take this SDDL string and apply it with sc.exe manually. But rather than doing that, we’ll proceed with setting this permission with GPO. Lets take a look the two strings and do a diff to see what happened.

[Registry Values]
[Profile Description]
Description=Grant the right to manage specific services
[Service General Setting]

In the SDDL string, if we diff between the current permissions and the permissions assigned from the Security Template, we see there is a section added that includes the Active Directory SID of our service account.


Great! Now that we have our template exported, lets go ahead and create a GPO that will apply the template to our hosts.

Note: Details around how to create and apply GPO’s are outside the scope of this article. Please familiarize yourself with that process before proceeding!

Open up your group policy management console, create a new blank GPO. Navigate to: Computer Configuration -> Windows Settings -> Security Settings -> System Services. Right-click Security Services and select Import Policy.

Select the Security Template that was saved earlier, and click Open. Navigate to Security Settings -> System Services, then search for the service(s) you defined. In our case, the print spooler. Check to see that your settings are defined and the user account is specified as configured in the template as shown.

Once you’ve confirmed your settings are correct. Link the GPO where appropriate and it’s time to test. Once group policy has refreshed on a test host confirm the gpo is applied.

Note: Linking, refreshing and testing GPO’s are outside of the scope of this article.

On my test host, I ran powershell as my service account, then tested stopping the service. As we can see now that the Security Template has been applied via GPO, we can stop the service without getting an error! That’s great!

Now that we have a GPO applied the change is transparent. Anyone who stumbles across this system later, or who may have to rebuild it later can see there is a policy applied that sets permissions on services. Sure, it takes a few more steps, but leaving a trail will save you or your colleagues the headache of troubleshooting issues later.

So were done right? Well, we’re close. We just ran the commands through Powershell, and everything went super smooth. Well that wasn’t the original goal. The goal was to configure this to run via a Scheduled Task. Lets go ahead and test that out.

Confirm your service is running, then rerun your “stop services” task as configured in part one of this article. If you don’t have the task configured, jump back to part one and complete the steps.

Lets take a look at the transcript log once more:

Windows PowerShell transcript start
Start time: 20200117161418
Username: AD\is-svc-serv-test
RunAs User: AD\is-svc-serv-test
Machine: IS-CRAIGS-DEV2 (Microsoft Windows NT 10.0.14393.0)
Host Application: powershell.exe -NonInteractive -NoLogo -NoProfile -ExecutionPolicy Bypass -File C:\temp\demo\stop_services.ps1
Process ID: 8928
PSVersion: 5.1.14393.3053
PSEdition: Desktop
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.14393.3053
BuildVersion: 10.0.14393.3053
CLRVersion: 4.0.30319.42000
WSManStackVersion: 3.0
PSRemotingProtocolVersion: 2.3
Transcript started, output file is c:\temp\demo\transcript.log
Stopping Print Spooler Service
Windows PowerShell transcript end
End time: 20200117161419

No more errors in the log file and checking the service itself we see its now stopped!

If you’ve made it this far, I’d like to thank you for following along. There are a number of steps to complete a seemingly simple task, but the results speak for themselves. This may be a narrow use case but I’m hopeful that some the tools or tricks outlined here will be applicable to other tasks in your day to day. If you have any questions or comments, feel free to leave them below.

Sharing is caring!

Leave a Reply

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

seventy eight + = eighty seven