asp.net-core-6.0windows-server-2019powershell-7.0windowsdomainaccount

Managing Windows Services with Remote powershell - Windows Server 2019 Datacenter Edition


I want to remotely stop / start / restart SQlBrowser service from a .net core 6 application running on IIS that uses the Powershell 7 library.

The application pool of the .net core service runs under a domain user, called domain.local\service-mgr-svc.

If I make domain.local\service-mgr-svc local administrator on the remote SQL Server, that I want to control the sqlbrowser service on, the remote invoke command works:

Invoke-Command -ComputerName sql-svr.domain.local -Scriptblock { restart-service -Name "sqlbrowser" }

When I take the user out of local administrator group, I get the following error

Cannot find any service with the service name "SQL Browser"

If I try just "get-service", I get response about "might need elevated permissions"

My question is, what are those permissions? I've been down the rabbit-hole on this.

What I have tried:

GPO to give the user on the remote server;

Giving the user the following permissions on the Root Namespace in the WMI config;

Alas, it does not work. What can I try next?


Solution

  • (Posting the solution on behalf of the question author in order to move it to the answer space).

    So I had a look at this SubInACL.exe stuff and that did the job!

    I noticed that running cmd on the remote server and trying to stop / start SQLBrowser, I got access denied.

    So did a

    dir>.\SubInACL.exe /service SQLBrowser /grant=domain\user=PTOIS
    

    And it works!

    Further update: to automate this into a .ps1 startup script, I actually ended up using sc.exe. Its a bit tricky as you need to persist the current value and append your required SID and permissions SDDL. So I did the below, where the $default_svc_sddl is the current SDDL of a given service, and the $util_sddl is the SDDL of the user + permissions that I wanted to add.

    # Give utilityrunner-svc SQLBrowser service permissions
    
    $SQLServices= "SQLBrowser"
    
    foreach ($service in $SQLServices) {
        $this_svc_sddl = sc.exe sdshow $service
        if ($this_svc_sddl -eq $default_svc_sddl ) {
            $sddl_string = [regex]::Split($this_svc_sddl, 'S:')
            $split_sddl_string = $sddl_string[0]
            $new_sddl = $split_sddl_string+$util_sddl
            sc.exe sdset $service $new_sddl
        } else {
            write-host "Permission for utilityrunner-svc already set for $service"
        }
    }
    
    
    # Give utilityrunner-svc ServiceControlManager permissions
    
    $scm="SCManager"
    
    $scm_sddl = sc.exe sdshow $scm
    if ( scm_sddl -eq $default_scm_sddl ) {
        $scm_sddl_string = [regex]::Split($scm_sddl, 'S:')
        $split_scm_sddl_string = $scm_sddl_string[0]
        $new_scm_sddl = $split_scm_sddl_string+$util_sddl
        sc.exe sdset $scm $new_scm_sddl
    } else {
        write-host "$scm - Permission for utilityrunner-svc already set."
    }
    

    Found also needed SCManager permissions also, hence the permissions changes for that.

    I realise the logic isn't completely solid here, in that, if the default_svc_sddl doesn't match and the user isn't permissioned, it wont run, but it works for us.

    You could probably do something a bit more advanced using "ConvertFrom-SddlString" cmdlet in PS to query all the SDDLs against a service and accurately determine whether or not that the user in question, "domain.local\utilityrunner-svc" in my instance, has permissions applied or not.