Automagically Secure PowerShell Remote Sessions

In my previous post, PowerShell for Good and Sometimes for Evil, I detailed a few steps that you can take to help secure your systems from malicious use of remote PowerShell sessions. I very quickly realized, as I’m sure many will, that the manual steps are great to understand the concept of what’s being done…but doesn’t really help me in a real life administration setting. I now need to manually configure a bunch of different items on potentially dozens of servers.

That’s no good. If only there was some sort of technology out there to help me script these kinds of tedious administrative tasks to make life easier. Oh wait, isn’t that what this blog is all about?

Right! So to make this configuration a little more real work effective lets put together a script to do this work for me within a deployment. Just a couple of points before I really dive into my first PowerShell script in this blog about how I view PowerShell and writing scripts. Of course it’s an art form and everyone does it differently, so hopefully this helps give you an understanding of my scripting style

  • When I initially write a script I try to make it as simple as possible to make sure it does what I want to do, then layer on usability and extensibility later. It’s a lot easier to start with a simple script and get it working before adding a bunch of error handling, and script parameters. A good chunk of my scripts starting out here will be fairly basic in this sense, I’d prefer to give you something I know will work and give you an understanding of what the script does, and you can add whatever logging, or environment logic to fit your needs.
  • I try to avoid a lot of the short hand and aliasing that exists in PowerShell. Those two things are extremely helpful when you’re a PowerShell guru and you want to fire out scripts that are massive and would otherwise take a lot of time to write. I find a lot of my scripts end up being consumed by people who have a very basic PowerShell knowledge, and using short hand and aliases just makes a script very difficult to follow.
  • I try to always work off the latest and greatest version of PowerShell. This is really a no-brainer, every single release it gets better and easier to use. So if you have bits of script that don’t apply because you’re using PSv2, please upgrade – you won’t regret it, I promise.

Alright, with all that out of the way, lets dive into the script.

With this script I’m assuming that all your servers are part of your domain, and each has a Computer certificate available. In a later iteration of this script we could integrate in a certificate request from the Enterprise CA, but for simplicity let’s assume you’ve already got one. I’m also assuming you have the Remote Server Administration Tools (RSAT) installed, as well as the Group Policy Management feature enabled if you wish to do the bits with the GPO.

First thing we’ll want to do is get some administrator credentials to do all the operations we want. It’s bad practice to hard code usernames and passwords in your insecure scripts, so let’s use Get-Credential to securely collect that

# Get our administrator credentials for doing these operations
$creds = Get-Credential

Now we’ll want to retrieve a list of machines we want to apply this to. We can do this using the Get-ADComputer cmdlet (don’t forget to have the RSAT feature enabled on Windows or you won’t have the cmdlets).

# Get our administrator credentials for doing these operations
$creds = Get-Credential

# Get a list of computers to apply the operation to
$ouSearchBase = "OU=Computers, DC=local613, DC=com"
$serverList = Get-ADComputer -SearchBase $ouSearchBase -Filter *

I keep my servers in a root OU named Servers, so my search base comes out looking like this (of course, replace local613 and com with your domain and domain post-fix).

Great, now I’ve got a list of machines in my $serverList variable, lets get to work by setting up WinRM to accept HTTPS connections. I’ll do this remotely by using the Invoke-Command cmdlet and using the administrator credentials I collected earlier

foreach($server in $serverList)
 $fqdn = $server.DNSHostName
 Write-Host "Performing quick configure on $fqdn"
 Invoke-Command -ComputerName $fqdn -Credential $creds -ScriptBlock { winrm quickconfig -transport:https -force}

I’m using the -force switch on the WinRM command, since there should already be an HTTP listener running on the server, and WinRM will complain there’s an existing configuration and the WinRM service is already running.






Leave a Reply