How about doing this a different way?
I had a similar issue where I had application developers who needed to restart their home grown services on test/dev servers. I didn't want to give them admin access, and the biggest problem that I had was that sometimes their code was in a hung state. So they not only needed to stop the service, we needed to kill the hung process too.
My solution was to build a .Net web site to allow users to manage their own "stuff". This is not a trivial task, so I'm just sharing this as "food for thought". The web site ran as the system account, so it had full access to the server. I authenticated the user and displayed options based on what their account was allowed to do. Once I got the first site working it was fairly easy to modify it to do other things.
For your requirements, a simple solution would be to use a file share where the user drops a flag file into. This indicates a stop or start request. It gets processed by a scheduled task that executes a Powershell script. The task runs as the system account, so there is no "access denied" or remote call issues.
You need to create a folder on the target machine and share it out. Grant the user access so that they can create a start.flag or stop.flag file. It should be easy to build them a .bat file to use as their "button" to create the appropriate flag file. If you put the log file in the same folder, they can review it to see the current status.
Create a scheduled task that executes this Powershell script. Add a trigger to run at system startup, and set the account as the system account. The task will always show as running, and that's ok.
$folder = "C:\temp\ZZZZ\" # the folder to watch
$logfile = "C:\temp\ZZZZ\Log.txt" # our log file,
$ServiceName = "w3svc" # the service to start/stop
function LogIt($msg){
Write-Host $msg # seems to only work in ISE
$msg | Out-File $logfile -Append
}
try { # clear out watcher when testing with ISE
$FileSystemWatcher.EnableRaisingEvents = $false
$FileSystemWatcher.Dispose()
} catch {}
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path = $folder
$FileSystemWatcher.EnableRaisingEvents = $True
$FileSystemWatcher.Filter = '*.flag'
$action = {
$msg = '{0} was {1} at {2}' -f $Event.SourceEventArgs.FullPath,$Event.SourceEventArgs.ChangeType, $Event.TimeGenerated
LogIt $msg
if ($Event.SourceEventArgs.ChangeType -eq "Created") {
if ($Event.SourceEventArgs.FullPath -match 'start.flag') {
Logit "We have a start request."
try {
start-service -Name $ServiceName -ErrorAction Stop
logit "The service was started."
} catch {
logit $_
}
}
if ($Event.SourceEventArgs.FullPath -match 'stop.flag') {
Logit "We have a stop request."
try {
stop-service -Name $ServiceName -ErrorAction Stop
logit "The service was stopped."
# If the service won't stop, or some other error occurs, kill the application process here.
} catch {
logit $_
# or kill it here.
}
}
Logit "Service status is: $((get-service -Name $ServiceName).status)"
Remove-Item -Path $folder -Filter *.flag -force -recurse # clean out all flag files
}
}
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $action
LogIt "Running."
while($True){
Start-Sleep -Seconds 1 #keep powershell.exe running
}