Accessing the status of Windows services from a scheduled task

A client using a monitoring suite which checks for certain running Windows services once an hour.

Used the Windows scheduled tasks feature to run.

When running as a normal user, it returns no services even though the user is able to list all services in services.msc or on the command line.

Demonstration :


-> returns 157 services on a Windows 8


Create a script to enumerate the services from task scheduler:

'get-service | out-file $env:temp\services.txt' | Set-Content $env:temp\test.ps1


schedule :

schtasks /create /TN test /TR "powershell.exe %temp%\test.ps1" /SC monthly /NP /RU $env:username


run the task:

schtasks /run /tn test


check :

gc $env:temp\services.txt

it is empty. In the pre-Vista days any user would be able to enumerate services. Since Vista the security on what users can do with services has been tightened. While a logged on (interactive) user can still list all services. One that is running as a task can’t see anything. Why ?  Look at the permissions on the Service Control Manager, the process that is responsible in managing Windows services.


sc.exe sdshow scmanager

on Windows 8 :



each set between the brackets describe the permissions for one user(group). The last two letters define the group, AU stands for authenticated users while IU stands for Interactive users. We see the IU has a permission of LC (each two letter between the semicolons define a permission). LC stands for the ‘SERVICE_QUERY_STATUS’ which is what we need. All we have to do is give this permission to authenticated users. To do this, copy the existing security descriptor and add the LC in the first section:

sc.exe sdset scmanager D:(A;;LCCC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)

this command fails in a Power Shell because the brackets have a special meaning to the parser, we also need to run this command elevated.

Start-Process "$psHome\powershell.exe" -Verb Runas

and use quotes around the parameter with brackets:

sc.exe sdset scmanager "D:(A;;LCCC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)"

run test again:

schtasks /run /tn test; start-sleep 3;gc $env:temp\services.txt

we now get a list with 40 services, hmm why not all 157? Lets look at the permissions for a service we get and one we don’t:

sc.exe sdshow Spooler


sc.exe sdshow themes


We see AC has LC access to the spooler service, while no access at all to the themes service.

Adding this:


Now see the themes service as well:

schtasks /run /tn test; start-sleep 3;gc $env:temp\services.txt


If need to change these permissions for many or all services, a script would make sense:


[string]$pattern = ".",
[string]$principal = "AU",
[string]$permission = "LC")

function FixIt ([string]$name,[string]$rawSD,[string]$newSD)
Write-Host $name -ForegroundColor White
if ($fix)
sc.exe sdset $name $newSD
Write-Host $rawSD -ForegroundColor Yellow
Write-Host $newSD -ForegroundColor Cyan

function ProcessService([string]$name,[string]$permission,[string]$group){

$rawSD = & sc.exe sdshow $name

$match = ([regex] »\(A;;[;A-Z]+;;;$group\) »).match($rawSD)


If ($match.Success){
	If ($match.groups[0].value -notmatch $permission){
		# no, so add it
		$newtoken = $match.groups[0].value -replace "\(A;;","(A;;$permission"
		# remove the brackets:
		$newtoken = $newtoken -replace "[()]",""
		#write-host $newtoken -ForegroundColor red
		$newSD = $rawSD -replace $match.groups[0].value,$newtoken
		FixIt $name $rawSD $newSD
	Write-Host $name -ForegroundColor green
	# it has no AC token yet, lets add one
	if ($rawSD -match "D:\("){
		# starts with D:, just replace the beginning
		$newSD = $rawSD -replace "D:\(","D:(A;;$permission;;;$group)"
		FixIt $name $rawSD $newSD
		Write-Host $_.Name -ForegroundColor Red
		Write-Host $rawSD -ForegroundColor Yellow

get-service | Where-Object {$_.Name -match $pattern}| ForEach-Object {
ProcessService $_.Name $permission $principal


ProcessService « scmanager » $permission $principal

Copy to a file Set-ServiceSecurityDescriptors.ps1

When running it, it shows the descriptors for all services and how it would change them. You can limit it to one or certain services

.\Set-ServiceSecurityDescriptors.ps1 -pattern spooler
.\Set-ServiceSecurityDescriptors.ps1 -pattern svc

To change the permission use the -fix switch:

.\Set-ServiceSecurityDescriptors.ps1 -pattern time -fix

or for all:

.\Set-ServiceSecurityDescriptors.ps1 -fix

You will get an access denied errors, because for certain services even an elevated administrator has no permissions to change the permissions. Run our task again:

schtasks /run /tn test; start-sleep 3;gc $env:temp\services.txt


We now get a list of 152 services, the five we can’t see are: DPS, gpsvc, msiserver, wdiservicehost and whisystemhost

To get to these, open a powershell as system user:

psexec.exe -i -s powershell.exe

.\Set-ServiceSecurityDescriptors.ps1 -fix

now get all but msiserver.

codes check:

Thank you for the sources, Peter Hahndorf :

Laisser un commentaire