AWS Developer Tools Blog

Scripting your EC2 Windows fleet using Windows PowerShell and Windows Remote Management

Today we have a guest post by one of our AWS Solutions Architects, James Saull, discussing how to take advantage of Windows PowerShell and Windows Remote Management (WinRM) to script your Windows fleet.

One of the advantages of using AWS is on-demand access to an elastic fleet of machines—continuously adjusting in response to demand and ranging, potentially, from zero machines to thousands. This presents a couple of challenges: within your infrastructure, how might you identify and run your script against a large and varying number of machines at the same time? In this post, we take a look at how to use EC2 tags for targeting and Windows Remote Management to simultaneously run PowerShell scripts.

Launching an AWS EC2 Windows instance from the console and connecting via RDP is a simple matter. You can even do it directly from within Visual Studio as recently documented here. From the RDP session, you might perform tasks such as updating the assets of an ASP.Net web application. If you had a second machine, you could open a second RDP session and repeat those tasks. Alternatively, if you are running in AWS VPC, you could avoid opening additional RDP sessions and just use PowerShell’s Enter-PSSession to the second machine. This does require that all instances are members of security groups that will allow Windows Remote Management traffic.

Below is an example of connecting to another host in a VPC and issuing a simple command (notice the date time stamps are different on the second host):

However, as the number of machines grows, you will quickly want the ability to issue a command once and have it run against the whole fleet simultaneously. To do this, we can use PowerShell’s Invoke-Command. Let’s take a look at how we might instruct a fleet of Windows EC2 hosts to all download the latest version of my web application assets from Amazon S3.

First, using EC2 tags, we will identify which machines are web servers, as only they should be downloading these files. The example below uses the cmdlets Get-EC2Instance and Read-S3Object, which are part of the AWS Tools for Windows PowerShell and are installed by default on AWS Windows Machine Images:

$privateIp = ((Get-EC2Instance -Region eu-west-1).RunningInstance `
            | Where-Object {
                $_.Tag.Count –gt 0 `
                –and $_.Tag.Key -eq  "Role" `
                -and $_.Tag.Value -match "WebServer"}).PrivateIpAddress 

Establish a session with each of the web servers:

$s = New-PSSession -ComputerName $privateIp 

Invoke the command that will now simultaneously run on each of the web servers:

Invoke-Command -Session $s -ScriptBlock {
    Read-S3Object   -BucketName mysourcebucket `
                    -KeyPrefix /path/towebassets/ `
                    -Directory z:webassets `
                    -Region eu-west-1 } 

This works well, but what if I want to run something that is individualized to the instance? There are many possible ways, but here is one example:

$scriptBlock = {
 param (
            [int] $clusterPosition , [int] $numberOfWebServers
        )
        "I am Web Server $clusterPosition out of $numberOfWebServers" | Out-File z:afile.txt
}

$position = 1
foreach($machine in $privateIp)
{
    Invoke-Command  -ComputerName $machine `
                    -ScriptBlock $scriptBlock `
                    -ArgumentList $position , ($PrivateIp.Length) `
                    -AsJob -JobName DoSomethingDifferent
    $position++
} 

Summary

This post showed how using EC2 tags can make scripting a fleet of instances via Windows Remote Management very convenient. We hope you find these tips helpful, and as always, let us know what other .NET or PowerShell information would be most valuable to you.