Tuesday, January 23, 2018

Starting and stopping AWS instances via Powershell

On my long-overdue return to this blog, here's a script I wrote recently to solve my usually long process to start a full environment, do some work on it, then stop it.

Objective

I regularly have to run POCs or tests, on somewhat complex environments involving 10+ machines. Since I am a cost-conscious person, I will normally start the environment, do my testing/work, and then stop it at the end. All good with this, except that:

  1. Logging in to AWS console, switching profiles, switching zones takes a good 2-3 minutes
  2. Finding the the instances I need, starting them, takes another 30 seconds
  3. Updating all links to my test instances in Remote Desktop Manager (which I use) takes another good 5 minutes (remember we're talking 10 instances).
So I decided I should automate this process, given that's what APIs are for.

The script below will:
  • Find all instances that use a given AWS Security Group
  • Start all those instances
  • Modify my Remote Desktop Manager xml file that contains the links to the VMs
As usual... the code is written in a very "results oriented way"... some code purists may be annoyed with my lack of code consistency.

Hope this helps anyone out there.

I promise I'll figure out code highlighting on this blog another day.
# Get list of instances 

# Important Variables
$groupName = "NHS-ALL-LOCAL" #Name of security group to search for in instances
$rdgFilePath = "C:\Users\Nuno\OneDrive\Documents\NHS.rdg" #Path to RD Manager group file with links to Machines/RDP
$instanceStartWaitTime = 20 # Time in seconds to wait for EC2 instances to start

function GetName($instance) {
    foreach ($tag in $instance.Tags) {
        if ($tag.Key -eq "Name")
        {return $tag.Value}
        return $null
    }
}

function GetAllInstances() {
    $allAWSInstances = aws ec2 describe-instances | ConvertFrom-Json
    $instances = @{}
    foreach ($instance in $allAWSInstances.Reservations.Instances) {
        foreach ($group in $instance.SecurityGroups) {
            if ($group.GroupName -eq $groupName) {
                $nameTag = GetName($instance)
                $instances.Add($nameTag, $instance)
            }
        }
    }
    return $instances
}

function GetInstanceIds($instances) {
    $listInstances = @()
    foreach ($instance in $instances.GetEnumerator()) {
        $listInstances += $instance.Value.InstanceId
    }
    return $listInstances
}
$instances = GetAllInstances 
Write-Host "Found the following Instances:"
foreach ($instance in $instances.GetEnumerator()) {
    Write-Host $instance.Name " - " $instance.Value.InstanceId
}

# Start all instances
$list = GetInstanceIds($instances)

Write-Host "Starting all instances with security group $groupName..."
aws ec2 start-instances --instance-ids $list
Write-Host "Waiting $instanceStartWaitTime seconds"
for($i=1; $i -le $instanceStartWaitTime; $i++)
{
    $timeRemaining = $instanceStartWaitTime - $i
    Write-Progress -Activity "Waiting $timeRemaining seconds" -PercentComplete (($i/$instanceStartWaitTime)*100)
    Start-Sleep -Seconds 1
}
$instances = GetAllInstances

# Should be updated now.
foreach ($instance in $instances.GetEnumerator()) {
    Write-Host $instance.Value.InstanceId is now $instance.Value.State.Name
}

# Modify C:\Users\Nuno\OneDrive\Documents\NHS.rdg to place all the right URLs
[xml]$rdg = Get-Content -Path $rdgFilePath


foreach ($instance in $instances.GetEnumerator()) {
    $instanceName = $instance.Key
    $publicUrl = $instance.Value.PublicDnsName
    foreach ($server in $rdg.RDCMan.file.server.properties) {
        if ($server.displayName -eq $instanceName) {
            Write-Host "Updating Node: $instanceName with Url $publicUrl"
            $server.name = $publicUrl
        }
    } 
}
$rdg.Save($rdgFilePath)