Created
July 15, 2015 20:10
-
-
Save ezeeetm/913f3f06c4429c6f4ed5 to your computer and use it in GitHub Desktop.
AWS Instance Scheduler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
######################################################################################################################### | |
# Script name: instanceScheduler.ps1 | |
# Version: v0.1 | |
# Date: 08Nov2014 | |
# Author: Eric Miller | |
# Purpose: start/stop ec2 instances on a schedule, with retries and SNS notifications on WARNING/FATAL events | |
# Usage: ./instanceScheduler.ps1 | |
# Dependencies: aws.ps1, logging.ps1 | |
# Notes: | |
# In order to give the retries sufficient time to process, | |
# do not schedule starts/stops closer together than $maxRetries * $retryIntervals seconds | |
# http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-ItemType-InstanceStateType.html | |
# Valid values: 0 (pending) | 16 (running) | 32 (shutting-down) | 48 (terminated) | 64 (stopping) | 80 (stopped) | |
######################################################################################################################### | |
# Initialize logging and AWS session, see dependencies above | |
# aws.ps1 uses specific IAM role for this script, redact access/secret keys or omit this file in commits | |
. .\logging.ps1 | |
. .\aws.ps1 | |
# config | |
$scheduleFile = '.\schedule.txt' | |
$debug = $false #toggle $true/$false to turn console debugging on/off | |
$skipDays = @("Saturday","Sunday") | |
$chkInterval = 15 #secs | |
$retryInterval = 60 #secs | |
$maxRetries = 5 | |
$pendingChanges = @() | |
# get instances/schedules from $scheduleFile | |
function getSchedule | |
{ | |
$schedule = Get-Content $scheduleFile | |
$instances = @() | |
foreach ($line in $schedule) | |
{ | |
if ($line.StartsWith("#") -or ($line.length -eq 0)){continue} # skip comments and blank lines | |
try | |
{ | |
$line = $line.split(",") | |
$instance = New-Object PSObject | |
Add-Member -InputObject $instance -MemberType NoteProperty -Name id -Value $line[0].Trim().ToLower() | |
Add-Member -InputObject $instance -MemberType NoteProperty -Name startTime -Value $line[1].Trim() | |
Add-Member -InputObject $instance -MemberType NoteProperty -Name stopTime -Value $line[2].Trim() | |
Add-Member -InputObject $instance -MemberType NoteProperty -Name currState -Value $null | |
Add-Member -InputObject $instance -MemberType NoteProperty -Name desState -Value $null | |
Add-Member -InputObject $instance -MemberType NoteProperty -Name retryCredits -Value $null | |
$instances += $instance | |
} | |
catch | |
{ | |
log FATAL "check the schedule file for correct syntax, script terminating" | |
exit | |
} | |
} | |
return $instances | |
} | |
# main loop | |
while ($true) | |
{ | |
# load new schedule whenever script starts or schedule file is changed | |
if(($((gci $scheduleFile).LastWriteTime) -gt $schedLastWrite) -or (!$schedLastWrite)) | |
{ | |
$instanceList = getSchedule | |
$schedLastWrite = (gci $scheduleFile).LastWriteTime | |
log INFO "script started or schedule modified, reading new schedule" | |
if ($debug) | |
{ | |
write-host "DEBUG: schedLastWrite- $schedLastWrite`n" -foregroundcolor yellow | |
write-host "DEBUG: instances from getSchedule function:" -foregroundcolor yellow | |
foreach ($instance in $instanceList){write-host $instance -foregroundcolor yellow} | |
} | |
} | |
# if today is not a skip day, rock on | |
$dow = (Get-Date).DayOfWeek.ToString() | |
if (!$skipDays.contains($dow)) | |
{ | |
# add instance objects with a start or stop scheduled for this minute to $pendingChanges array | |
$now = Get-Date -format "HHmm" | |
foreach($instance in $instanceList) | |
{ | |
if($instance.startTime -eq $now) | |
{ | |
log INFO "added to pending: $($instance.id) / desired state *running* @ $($instance.startTime) / $maxRetries retries" | |
$instance.desState = "running" | |
$instance.retryCredits = $maxRetries | |
$pendingChanges += $instance | |
} | |
if($instance.stopTime -eq $now) | |
{ | |
log INFO "added to pending: $($instance.id) / desired state *stopped* @ $($instance.stopTime) / $maxRetries retries" | |
$instance.desState = "stopped" | |
$instance.retryCredits = $maxRetries | |
$pendingChanges += $instance | |
} | |
} | |
} | |
# refresh instance states and remove any whose current state matches desired state | |
if ($pendingChanges) | |
{ | |
foreach ($instance in $pendingChanges) | |
{ | |
$currInstance = Get-EC2Instance $instance.id | |
$instance.currState = $currInstance.Instances.state.name.Value | |
if ($instance.currState -eq $instance.desState) | |
{ | |
log INFO "removed from pending: $($instance.id), desired state *$($instance.desState)* achieved" | |
$pendingChanges = $pendingChanges -ne $instance | |
} | |
} | |
} | |
# if $pendingChanges still isn't empty, do stuff | |
if ($pendingChanges) | |
{ | |
foreach($instance in $pendingChanges) | |
{ | |
if(($instance.desState -eq "stopped") -and ($instance.currState -eq "running")) #do nothing for pending, shutting-down, terminated, or stopping | |
{ | |
log INFO "stopping $($instance.id)" | |
try {$r = stop-ec2instance $instance.id} catch{log WARNING "problem with API call to stop $($instance.id)"} | |
log INFO "ec2 response: instance = $($r.InstanceId), currentState = $($r.CurrentState.Name.Value), previousState = $($r.PreviousState.Name.Value)" | |
} | |
if(($instance.desState -eq "running") -and ($instance.currState -eq "stopped")) #do nothing for pending, shutting-down, terminated, or stopping | |
{ | |
log INFO "starting $($instance.id)" | |
try {$r = start-ec2instance $instance.id} catch {log WARNING "problem with API call to start $($instance.id)"} | |
log INFO "ec2 response: instance = $($r.InstanceId), currentState = $($r.CurrentState.Name.Value), previousState = $($r.PreviousState.Name.Value)" | |
} | |
# decrement $retryCredits. | |
# If instances hit $maxRetries, drop and send SNS notification (in logging) | |
$instance.retryCredits -= 1 | |
if ($instance.retryCredits -eq 0) | |
{ | |
log WARNING "removed from pending: ($instance.id), $maxRetries failed retries" | |
$pendingChanges = $pendingChanges -ne $instance | |
} | |
} | |
start-sleep -s $retryInterval | |
} | |
start-sleep -s $chkInterval | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment