Using OverThere to control a Windows Server from Java

 03 December 2011  cloud  java  powershell  software-engineering

Screen showing turn sign

XebiaLab’s OverThere project recently appeared on GitHub. I’ve recently been doing some work on jclouds, where it was noted that OverThere could address a gap in jclouds - controlling Windows Server virtual machines. So I decided to dust off my braincells concerned with Windows Server and take a look at OverThere.

The first thing I observed from its README was that none of Windows remote control methods work out-of-the-box - they all need some setup first. The README provides some instructions on how to set up WinRM (“Windows Remote Management”), which takes about a dozen steps to set up an SSL-secured WinRM service. Until this is done, OverThere can’t talk to the server, which puts a couple of speedbumps in the way of having boot up and talk to a Windows Server instance provided from the vanilla Amazon AMIs.

However, the steps needed can be drastically reduced with a bit of scripting, specifically PowerShell scripting. All of the components needed to set up a server ready for OverThere can be handled by PowerShell in a single script. Below I’ve explained how I wrote the script, but if you’re impatient you can download the finished script, and skip to Trying it out below.

Download activate-winrm.ps1

Update 25/2/2012: I’ve tidied up this script and release it onto GitHub - see this post for details. Also, I’ve clarified the licensing for this script in this post: as it incorporates material from Microsoft TechNet and MSDN, you should consider the script covered by the Microsoft Limited Public License.

Installing the WinRM feature

The first thing needed is the WinRM feature itself. Normally, you’d start the Server Manager application, select Add Server Features, and proceed through a wizard. But Server Manager has a set of PowerShell cmdlets which mean this task can simply be completed in two lines:

Import-Module servermanager
Add-WindowsFeature WinRm-IIS-Ext

Basic configuration of WinRM

Next, the OverThere README gives instructions on some configuration options that need to be set. Those are given in the form of winrm command invocations, but there’s also a set of cmdlets that do the same thing in a more PowerShell-ish way of doing things:

Set-WSManInstance WinRM/Config/Service/Auth -ValueSet @{Basic = $true}
Set-WSManInstance WinRM/Config/Service -ValueSet @{AllowUnencrypted = $true}
Set-WSManInstance WinRM/Config/WinRS -ValueSet @{MaxMemoryPerShellMB = 1024}
Set-WSManInstance WinRM/Config/Client -ValueSet @{TrustedHosts="*"}

Caution:** that last command allows anyone to connect to WinRM (although they will still need a valid username and password), so either change that command to be more specific or ensure that you have an alternative to prevent unwanted users connecting (such as an EC2 security group).

Activating SSL

Since OverThere will be using Basic Authentication, we don’t want Administrator-strength credentials passing unencrypted across the network. So we’ll create a WinRM endpoint that uses SSL. To do this, we’ll generate a self-signed certificate, and use this for the WinRM endpoint. The first thing you need when making an SSL certificate is your hostname. Thanks to the EC2 metadata service, this is a one-liner in PowerShell:

$hostname = (New-Object System.Net.WebClient).DownloadString("http://169.254.169.254/2011-01-01/meta-data/public-hostname")

Next we’ll need to generate the self-signed certificate. The OverThere README suggests using the selfssl.exe tool from the IIS6 resource kit; other options are makecert.exe from the Windows Platform SDK. But ideally we want to avoid having to download these kits. Fortunately there’s a way to do this in PowerShell: using the CryptoAPI COM objects, as described in this TechNet blog post. So we’ll snaffle that code - all we need to do is to change the second line to use the hostname we discovered above:

$name.Encode("CN=$hostname", 0)

Having created the certificate, we can now create the WinRM SSL endpoint:

# Get the thumbprints of the SSL certificates that match the hostname
$thumbprints = Get-Childitem -path cert:\LocalMachine\My | Where-Object { $_.Subject -eq "CN=$hostname" } | Select-Object -Property Thumbprint
# PowerShell magic to retrieve the first matching thumbprint (there'll probably only be one anyway)
$thumbprint = @($thumbprints)[0].Thumbprint
# Create a WinRM listener, identifying the SSL certificate by the thumbprint
New-WSManInstance WinRM/Config/Listener -SelectorSet @{Address = "*"; Transport = "HTTPS"} -ValueSet @{Hostname = $hostname; CertificateThumbprint = $thumbprint}

Open the firewall

This will have created a service listening on port 5986. This firewall port will be closed, however; we need to open it. Once again, there’s a nice PowerShell snippet that can arrange this, as described in this MSDN blog post by Tom Hollander. So, we’ll snaffle his handy Add-FirewallRule function, and then use it to open the port:

Add-FirewallRule "Windows Remote Management HTTP/SSL" "5986" $null $null

Running it

So now we’ve got the script, it’s time to try it out. Start up an Amazon EC2 instance using Windows Server 2008 R2. Wait for it to generate its Administrator password, then get logged in by RDP. Now take our PowerShell script and load it on to the new instance (I used RDP’s ability to share your local drive with the remote server, but any mechanism will do.) Start PowerShell.

Now, PowerShell by default will not execute scripts as a security measure, so we’re going to have to relax this restriction slightly. Enter in this command:

set-executionpolicy unrestricted

This means that PowerShell will execute scripts, without checking signatures. It’s not very secure, but we’ll turn it on afterwards (and later on I’ll see if I can provide a signed version of the script.) Next, run the script (changing this path to reflect where you put the script file):

C:\Users\Administrator\Desktop\activate-winrm.ps1

It’ll take a few minutes to run as it installs the necessary components, and will print out various messages as it goes. At the end, if there’s nothing in red text, then it succeeded. So we’ll reset the execution policy to be secure again:

set-executionpolicy restricted

Trying it out

Now we can go to OverThere to try it out. In the examples folder, there’s an example called ExecuteOnWindows. Change the options to look similar to this:

options.set(ADDRESS, "your-instance-public-dns-name.compute.amazonaws.com");
options.set(USERNAME, "Administrator");
options.set(PASSWORD, "thePassword");
options.set(OPERATING_SYSTEM, WINDOWS);
options.set(CONNECTION_TYPE, WINRM_HTTPS);

Run it, and with a bit of luck, it’ll work on the first attempt!

Next steps

Obviously, it’s less than ideal that you still need to remote-desktop into the server to run the script, even if it is far fewer steps that you have to do now. You can however now take an AMI based on this image and know that it will work with OverThere out-of-the-box. I’ll be continuing to see if there’s any way to simplify this further, and am happy to take your suggestions!

Image credit: Ambrozjo

 03 December 2011  cloud  java  powershell  software-engineering

Comments on this article