Granular access via PowerShell Remoting

Broad access definitions can be a pain.  Let’s say we have an important service, where we can provide support access to all administrative commands, or none.  Which is better:

  • Providing your Support staff access to tools they need to do their job
  • Locking down access to tools which allow a single support employee to compromise the integrity or availability of an entire service

I would like both.  Thanks to PowerShell remoting, we can achieve this through delegated, constrained endpoints.  High level details from an example:

  • Some technology might not let me provide access to a single command, or control which parameters are passed to the command.  I can only pick between ‘admin’ and ‘not admin’, and I can’t control the scope of these.
  • Install Windows Management Framework 3 and enable PowerShell remoting on the applicable server
  • Create a service account with the permissions to accomplish the task at hand.
  • Create a startup script that restricts a session to whitelist the specific commands we want.  Perhaps wrap these commands so that certain parameters are controlled via ValidateSet.  Perhaps add logging.
  • Create a PS Session Configuration that uses the service account and startup script we created, that only accepts connections from a security group your support staff belong to
  • If desired, provide support with a GUI, PowerShell function, or instructions on using the endpoint
  • Document everything.

With the setup above, support and only support has access to a PowerShell endpoint that runs with the necessary privileges that support staff do not have themselves, and that only has access to the specific commands we defined.  In other words, we created granular access to a service that does not provide this on its own.

Setting this up involves taking a few steps, but once you understand what is going on, it is very easy to repeat and extend this functionality to other use cases.

Please reference the following guides for more details on PowerShell remoting in general, as I glance over this topic:

Let’s walk through the steps.

Prerequisites – Windows Management Framework 3, PSRemoting, a RunAs account

  • If we want to use a delegated endpoint (one that always runs with a pre-specified credential), we need to use Windows Management Framework 3.  If you are running Server 2008 or Server 2008 R2, this means you.  Server 2012 and later include this functionality out of the box.
  • If you are running Server 2008 or Server 2008 R2, enable PS Remoting.  Generally, run Enable-PSRemoting –confirm:$false –force.  If you want to dive deeper into using SSL, setting this up automatically with Group Policy, or other details, consult the guides or Internet.  Again, Server 2012 and later include this functionality out of the box.
  • If you want to use a delegated endpoint, create or identify the account the session will always run as.

Creating a startup script

There are several ways we can constrain a session.  I will be using a startup script given the flexibility this provides.  You could also use New-PSSessionConfigurationFile to control LanguageMode, visibility to cmdlets/functions/aliases, add function definitions and modules to import, and make other customizations.

Much of this script is thanks to the fantastic Windows PowerShell in Action 2nd edition.  It’s a little out of date, but still very relevant and helpful.  If you have any coding or scripting experience, I would recommend this book over most others.

Please download the SpoolService.ps1 demo script from the Technet Script Gallery.  This is a demo startup script and includes brief notes on implementation.

A few quick notes!

  • You might not need to create these endpoints on each and every server.  Perhaps you could designate one or a handful of servers to host these endpoints.
  • You could create multiple endpoints, or consolidate startup scripts and get creative; use the Active Directory module to check for security groups of the connected user ($PSSenderInfo.connectedUser) and customize the startup script and function definitions based on those groups.
  • You don’t need to worry about the double hop problem for the first hop to a delegated session, so feel free to store startup scripts on a network share – just don’t let your end users modify the contents!
  • I have a few functions in the startup script that work together to provide rudimentary logging: Start-Log, Get-HistoryLast, Write-Log, Get-MyCommand, and Check-InvokeCommand.  If you have any suggestions on improving this please let me know! EDIT: Use real logging solutions, not these : )

Register the session configuration

The hard work is done!  We just need to register the configuration and provide details on how to use it.  We will use Register-PSSessionConfiguration:

Register-PSSessionConfiguration -name "SpoolService" -StartupScript "\\path\to\SpoolService.ps1" -RunAsCredential $(Get-Credential) –ShowSecurityDescriptorUI

Details on these arguments:

  • Name – This is the name you will refer to when connecting to the endpoint.  I try to keep the name and the startupScript filename consistent and meaningful.
  • StartupScript – Path to the startup script that will run, which you can use to lock down the session.  Note that this doesn’t suck in and make the script permanent.  You can modify the script and the changes will take effect the next time someone connects to this endpoint.
  • RunAsCredential – Always run these sessions with credentials for this account.
  • ShowSecurityDescriptorUI – Pop up a GUI to control access to this endpoint.  Provide the security group that needs to connect to this group with Execute(Invoke) permissions.  You might want to reconsider access for the local Administrators group.  For the ambitious, feel free to use the SecurityDescriptorSddl parameter.

Test the endpoint and inform the end users

At this point, we can test the endpoint to confirm everything is working as desired.

Invoke-Command -computername $server -configurationname SpoolService -scriptblock { Restart-Service -name AppIDSVC }

Check the contents of the log file that was created in $logPath (C:\temp until you change it).  There is no simple way to log things that run via Invoke-Command that I could find.  If you have ideas or better luck with this, please let me know!

You could now create a GUI that uses this endpoint to your end users, write them a function that runs against this endpoint, or provide them details on how to access this endpoint with invoke-command, enter-pssession, or some other method.

Get creative!

That’s about it – for any technology exposed via PowerShell or the command line, you should now have the ability to roll your own granular access controls.

I provided a very rudimentary startup script.  You could get very creative with this.  Be sure to keep things documented.  I would recommend tracking the following for every endpoint:

  • Endpoint name
  • Server(s) hosting the endpoint
  • RunAs account for the endpoint
  • Startup Script for the endpoint
  • Security groups with access to the endpoint
  • Path to logging for the endpoint, if applicable
  • Other details as necessary


A number of resources and solutions have been published since this post. Be sure to check them out!

  • Boe Prox’ series on remote endpoints
  • Microsoft’s JitJea keep in mind this is currently limited to local accounts. Ping Microsoft on this if you think it would be worth including domain accounts : )


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s