Saturday, January 3, 2015

Remotely Restart Serivices via PowerShell Without Admin Credentials

Scope


Ever wanted to allow a non-admin to remotely restart a service? No? K.

.... well if sometime you feel like it, this article is for you. We'll walk through enabling PowerShell Remoting (part of WinRM), allowing the non-administrators to connect, and allowing the specified user or group to restart the service in question.


(Install and) Enable PowerShell Remoting


On Windows server 2012, this step is already completed by default. On earlier versions of Windows you may need to install software and then enable connection. That is an article in and of itself, but fortunately it has already been published by Ian Farr. Consult that article to determine what software, if any, you need to install.

After installs:

Note: This may not be necessary on 2012 and up but will not hurt none the less.
  1. Remote to the server you wish to remotely manipulate services on
  2. Open an elevated PowerShell prompt


  3. Run the Enable-PSRemoting cmdlet


  4. For each query, type "y" and press enter.


The Enable-PSRemoting cmdlet will run a few different sub cmdlets to enable the proper services and setup initial permissions.


Set up Security Principals and Grant Permissions to use PowerShell Remoting


Caution: This grants access to connect to the PSRemote interface and attempt to issue commands. While this isn't enough to stop services, etc. by itself it should be done with caution.

There are two levels of security you will need to address with this operation: the ability to connect to the PSRemote interface/attempt to issue commands, and the permissions on the service to be restarted. It is important to consider that these two groups may not be the same; there is a lot one can do with PSRemoting outside of restarting a specific service. Thus it is likely you will want to create a larger (in scope and size) group to access PSRemoting and a smaller group to restart this specific service.

If you don't much care and just want to get going you can just make the principals the same... even just a single user. With that said:

  1. Determine what security principals you want to have access to PSRemoting and create them if necessary. I use an Active Directory group named appropriately and populated with the desired users, but you could use anything down to a single user if you like.


  2. Ensure you still have the PowerShell prompt open on your target server; if not open it as in the section above.
  3. Execute Set-PSSessionConfiguration -ShowSecurityDescriptorUI -Name Microsoft.PowerShell which will bring up the Windows security dialog box.


  4. Add your desired security principal (AD Group, Local Group, User, etc) and grant it "Full Control" access.


  5. Click "OK" to close the box and apply the permissions, then answer y + <enter> to restart the service.

Note that on 64-bit systems where you have elected to use the 32-bit version of PowerShell you will also need to execute steps 3-5 using the Set-PSSessionConfiguration -ShowSecurityDescriptorUI -Name Microsoft.PowerShell32 command as well. 

Now you can hit the PSRemote interface with the specified accounts; if you haven't figured it out yet this could be used for many things other than just restarting a service... this is why PSRemoting is so great!


Grant Service Restart Access to the Appropriate Security Principal


In my example my service will be called "MyService". Normally this is an operation that would be somewhat complex with the built-in Windows tools, so I'm going to offer up a simpler way.

  1. Download and install Carbon (more below) on your target server(s). 
  2. Per the install instructions, unblock the zip, extract, and import the module after install with Import-Module Carbon or the import-carbon.ps1 script. (Note: You may need to set-executionpolicy unrestricted to use Carbon; make sure you set-executionpolicy Restricted when you're done)


  3. Execute the Grant-ServiceControlPermission, i.e. Grant-ServiceControlPermission -ServiceName MyService -Identity MyDomain\MyGroup where MyService is the short name of the target service and MyDomain\MyGroup is the target security principal. 


Aaron Jensen has done a great job with the Carbon module and I highly recommend it to anyone operating in a DevOps space on Windows Server. That one command in point #3 could have been an article about SDDLs in and of itself.


Test and Provide Restart Commands to Target User(s)


Rather than having your target users learn the appropriate PowerShell commands, it may be easier to provide them with a script. Here's a sample one that can restart multiple services on multiple servers after executing the steps above on each.



$servers="Server1","Server2"
$services="MyService1","MyService2"

Write-Warning 'What would you like this script to do? '
Write-Warning '1. Stop Services'
Write-Warning '2. Start Services'
Write-Warning '3. Re-start Services'
Write-Warning '4. Quit'
while($ActionType.length -lt 1)
{
 $ActionType=Read-Host "Enter the number to correspond with your desired action. " 
 $ActionType=[string]$ActionType.trim()
}
 
switch ($ActionType)
{
 1 
 {
  $stop=$true
  $start=$false
 }
 2 
 {
  $stop=$false
  $start=$true
 }
 3
 {
  $stop=$true
  $start=$true
 }
 4 {Exit}
 default {throw ("Unexpected input from action type inquriy.")}
}
 
foreach ($server in $servers)
{
 if ($stop)
 {
  foreach ($service in $services)
  {
   Write-Debug "Stopping $service on $Server"
   Invoke-Command -ComputerName $server -ScriptBlock {Stop-Service -Name $args[0]} -ArgumentList $service
   if ($? -eq $false){throw "TERMINATING SCRIPT; UNABLE TO STOP SERVICE. Check permissions and ensure target machine $server is up"}
   Write-Warning "Service $service stopped on $server"
  }
 }
 start-sleep -Milliseconds 500

 if ($start)
 {
  #Start Services
  foreach ($service in $services)
  {
   Write-Debug "Starting $service on $server"
   Invoke-Command -ComputerName $server -ScriptBlock {Start-Service -Name $args[0]} -ArgumentList $service
   if ($? -eq $false){throw "TERMINATING SCRIPT; UNABLE TO START SERVICE. Check permissions and ensure target machine $server is up"}
   Write-Warning "Service $service started on $server"
  }
 }
}
Write-Warning "All actions complete!"

This script is really just a starting point; there are many different directions you could take it based on your needs. For example, you could use the get-credential cmdlet to allow the user to enter alternative credentials. To use it as-is simply replace the server(s) and service(s) names at the top of the script with those you are targeting.

... and that's it! Thanks to carbon and a couple simple commands this operation is pretty easy to achieve. This has been a big help in lower environments for letting users, developers, etc. restart services as needed without having to interrupt their support personnel. Hopefully it helps you as well.

References/Additional Information


TechNet: Enable-PSRemoting
Stack Overflow: WSMan and Basic Authorization
Stack Overflow: PowerShell Remoting Giving Access is Denied Error
TechNet: Introduction to WinRM
Ondrej Sevecek: Enabling remote WMI and PowerShell access over WinRM for non-administrators (Much of the inspiration for this article)
Carbon (PowerShell Dev-Ops module by Aaron Jensen)