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")