This Powershell script is designed to run on two machines, and to transfer the elastic IP to the running instance should one fail. This script is loosely based on https://aws.amazon.com/articles/2127188135977316 but has several key differences:
- This provides HA for the public elastic IP without need for an elastic load balancer
- This script runs in windows as a scheduled task at startup under the SYSTEM account.
- This script logs errors to the event viewer
Some Caveats:
- The script is designed to run according to your RTO -- I have this script executing every 60 seconds using the task scheduler.
- In order to use the ec2 commands as written, you will need to auto-assign public IP addresses to the HA partners
- This script does not differentiate console errors from console output. I may add that in a later version
- This script does not account for more than one elastic network interface
- This script does not account for more than one private or public IP address
- This script doesn't have an AZ preference, but it should prefer the AZ with the active RDS instance. I may add this later
- This architecture is not disaster proof. Elastic IPs can only exist within a single region, so if the whole region goes out, so will your solution.
- The script has to declare two system variables in order for the SYSTEM account to be able to run these tasks, but they get re-declared every time the script runs, which really isn't necessary.
#Environment Variables required for EC2
setx EC2_HOME "C:\Program Files\ec2-api-tools-1.7.5.1"
setx JAVA_HOME "C:\Program Files (x86)\Java\jre1.8.0_71"
function catchOutput ($test) #Sends either the command line error or the output to the Event Viewer.
{
#Write-EventLog -LogName Application -Source HA_Monitor -EntryType Information -EventID 0 -Message "$test"
return $test
}
function writeMessage($str) #sends a message to the Event Viewer.
{
Write-EventLog -LogName Application -Source HA_Monitor -EntryType Information -EventID 1 -Message "$str"
}
writeMessage("Running Heartbeat script.")
#$EC2_HOME = catchOutput(Get-ChildItem Env:EC2_HOME | out-string -stream)
#$JAVA_HOME = catchOutput(Get-ChildItem Env:JAVA_HOME | out-string -stream)
#Static Confugration Data
$primaryAID = "eipalloc-893690ed"
$partnerIID = "i-4be48693"
#Fetch local data
$instanceID = Invoke-RestMethod -Uri http://169.254.169.254/latest/meta-data/instance-id
$availabilityZone = Invoke-RestMethod -Uri http://169.254.169.254/latest/meta-data/placement/availability-zone
$region = "$availabilityZone" -replace "[a-z]$",""
$MyVIP = Invoke-RestMethod -Uri http://169.254.169.254/latest/meta-data/public-ipv4
$VIP = catchOutput(ec2-describe-addresses $primaryAID --region $region 2>&1) | %{ $_.split("`t")[1]}
$HA_Partner_IP = catchOutput(ec2-describe-instances $partnerIID --region $region 2>&1) | findstr "PRIVATEIPADDRESS" | %{ $_.split("`t")[1]}
if ($myVIP -ne $VIP) {
$pingResult = catchOutput(ping -n 3 -w 3 $HA_Partner_IP 2>&1)
if ($pingResult | select-string -pattern "100`% loss") {
$rval = catchOutput(ec2-associate-address --region $region -a $primaryAID -i $instanceID --allow-reassociation 2>&1)
writeMessage("Partner reports offline. Successfully obtained primary public IP $VIP")
}
elseif ($pingResult | select-string -pattern "0`% loss") {
writeMessage("Partner reports online; no action necessary")
}
else {
writeMessage("Partner state unknown.")
}
}
else {
writeMessage("The local node $instanceID already possesses the primary public IP $VIP")
}
writeMessage("Hearbeat Complete")