Last active
December 10, 2018 04:58
-
-
Save xntrik/ca83b2ad8b855194436a01003ebe04b2 to your computer and use it in GitHub Desktop.
A simple script to start an AWS EC2 instance and update an associated R53 DNS record. The policy files should setup minimum permissions to allow these actions.
This file contains 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
#!/bin/bash | |
# Dependencies: awscli - see https://aws.amazon.com/cli/ | |
# Some of this is pilfered from | |
# https://github.com/awslabs/aws-codedeploy-samples | |
# | |
# How to use: | |
# ./aws-manage.sh <instance id> <dns-record> <dns-type> <up|down> | |
# | |
# For example: | |
# ./aws-manage.sh i-7337 xntrik.wtf A up | |
# | |
DEBUG=true #print messages | |
AWS_CLI="aws" #in case you need more CLI options? | |
WAITER_ATTEMPTS=24 #how many times the wait_for_state tries | |
WAITER_INTERVAL=5 #and how long we wait each try | |
TTL=300 #DNS TTL | |
msg() { | |
local message=$1 | |
$DEBUG && echo $message 1>&2 | |
} | |
error_exit() { | |
local message=$1 | |
echo "[FATAL] $message" 1>&2 | |
exit 1 | |
} | |
instance_status() { | |
local instance_id=$1 | |
local state=$($AWS_CLI ec2 describe-instances --instance-ids $instance_id \ | |
--query 'Reservations[0].Instances[0].State.Name' \ | |
--output text) | |
if [ $? != 0 ]; then | |
return 1 | |
else | |
echo $state | |
return 0 | |
fi | |
} | |
wait_for_state() { | |
local service=$1 | |
local instance_id=$2 | |
local state_name=$3 | |
local instance_state_cmd | |
if [ "$service" == "ec2" ]; then | |
instance_state_cmd="instance_status $instance_id" | |
else | |
msg "Cannot wait for instance state; unkonwn service type, '$service'" | |
return 1 | |
fi | |
msg "Checking $WAITER_ATTEMPTS times, every $WAITER_INTERVAL seconds for instance $instance_id to be in $state_name" | |
local instance_state=$($instance_state_cmd) | |
if [ $? != 0 ]; then | |
msg "Couldn't get initial state" | |
return 1 | |
fi | |
local count=1 | |
while [ "$instance_state" != "$state_name" ]; do | |
if [ $count -ge $WAITER_ATTEMPTS ]; then | |
local timeout=$(($WAITER_ATTEMPTS * $WAITER_INTERVAL)) | |
msg "Instance failed to reach state, $state_name within $timeout" | |
return 1 | |
fi | |
sleep $WAITER_INTERVAL | |
instance_state=$($instance_state_cmd) | |
count=$(($count + 1)) | |
msg "Instance is currently in state: $instance_state" | |
done | |
return 0 | |
} | |
stop_instance() { | |
local instance_id=$1 | |
msg "Checking instance state" | |
local instance_state=$(instance_status $instance_id) | |
if [ $? != 0 ]; then | |
msg "Unable to get instance state" | |
return 1 | |
fi | |
if [ "$instance_state" != "running" ]; then | |
msg "Instance is not running, therefore we can't stop" | |
return 1 | |
fi | |
msg "Instance is ready to stop.." | |
$AWS_CLI ec2 stop-instances --instance-ids $instance_id | |
if [ $? != 0 ]; then | |
msg "Failed to stop instance" | |
return 1 | |
fi | |
msg "Waiting for instance to be stopped" | |
wait_for_state "ec2" $instance_id "stopped" | |
if [ $? != 0 ]; then | |
msg "Instance didn't appear to stop in time" | |
return 1 | |
fi | |
} | |
start_instance() { | |
local instance_id=$1 | |
msg "Checking instance state" | |
local instance_state=$(instance_status $instance_id) | |
if [ $? != 0 ]; then | |
msg "Unable to get instance state" | |
return 1 | |
fi | |
if [ "$instance_state" != "stopped" ]; then | |
msg "Instance is not stopped, therefore we can't start" | |
return 1 | |
fi | |
msg "Instance is ready to start.." | |
$AWS_CLI ec2 start-instances --instance-ids $instance_id | |
if [ $? != 0 ]; then | |
msg "Failed to start instance" | |
return 1 | |
fi | |
msg "Waiting for instance to be started" | |
wait_for_state "ec2" $instance_id "running" | |
if [ $? != 0 ]; then | |
msg "Instance didn't appear to start in time" | |
return 1 | |
fi | |
} | |
get_ipaddress() { | |
local instance_id=$1 | |
local state=$($AWS_CLI ec2 describe-instances --instance-ids $instance_id \ | |
--query 'Reservations[0].Instances[0].PublicIpAddress' \ | |
--output text) | |
if [ $? != 0 ]; then | |
return 1 | |
else | |
echo $state | |
return 0 | |
fi | |
} | |
fetch_hosted_zone() { | |
local dnsname=$1 | |
if [ "${dnsname: -1}" != "." ]; then | |
dnsname="${dnsname}." | |
fi | |
zones=$($AWS_CLI route53 list-hosted-zones --output text \ | |
--query "HostedZones[*].[Id,Name]") | |
while IFS= read -r line; do | |
local testdnsname=$(echo $line|cut -f 2 -d " ") | |
if [ "$testdnsname" == "$dnsname" ]; then | |
local value=$(echo $line|cut -f 1 -d " ") | |
value=$(echo $value|cut -f 3 -d "/") | |
echo $value | |
return 0 | |
fi | |
done <<< "$zones" | |
echo "No $2 DNS Name Found" | |
return 1 | |
} | |
fetch_dns_record() { | |
local hostedzone=$1 | |
local recordtype=$2 | |
records=$($AWS_CLI route53 list-resource-record-sets --hosted-zone-id $1 \ | |
--query "ResourceRecordSets[*].[Type,ResourceRecords[0].Value]" \ | |
--output text) | |
while IFS= read -r line; do | |
local testrecordtype=$(echo $line|cut -f 1 -d " ") | |
if [ "$testrecordtype" == "$recordtype" ]; then | |
local value=$(echo $line|cut -f 2 -d " ") | |
echo $value | |
return 0 | |
fi | |
done <<< "$records" | |
echo "No $2 Record Found" | |
return 1 | |
} | |
update_ipaddress() { | |
local ip=$1 | |
local action=$2 | |
local name=$3 | |
local recordtype=$4 | |
local comment=$5 | |
local hostedzone=$6 | |
local temp_file=$(mktemp) | |
cat >${temp_file} << EOL | |
{ | |
"Comment": "${comment}", | |
"Changes": [ | |
{ | |
"Action": "${action}", | |
"ResourceRecordSet": { | |
"Name": "${name}", | |
"Type": "${recordtype}", | |
"TTL": ${TTL}, | |
"ResourceRecords": [ | |
{ | |
"Value": "${ip}" | |
} | |
] | |
} | |
} | |
] | |
} | |
EOL | |
$AWS_CLI route53 change-resource-record-sets --hosted-zone-id $hostedzone \ | |
--change-batch file://$temp_file | |
rm ${temp_file} | |
return 0 | |
} | |
if [ $# -ne 4 ]; then | |
error_exit "Params: instance-id dns-record dns-type <up|down>" | |
fi | |
INSTANCE_ID=$1 | |
DNSNAME=$2 | |
RECORDTYPE=$3 | |
ACTION=$4 | |
if [ $ACTION == "up" ]; then | |
msg "Starting instance and waiting for it to be in running state" | |
instance_started=$(start_instance $INSTANCE_ID) | |
if [ $? != 0 ]; then | |
error_exit "We couldn't start the instance properly, so we stopped" | |
fi | |
msg "Updating the A record" | |
hz=$(fetch_hosted_zone $DNSNAME) | |
if [ $? != 0 ]; then | |
error_exit "We couldn't get the hosted zone for: $DNSNAME" | |
fi | |
ip=$(get_ipaddress $INSTANCE_ID) | |
if [ $? != 0 ]; then | |
error_exit "We couldn't get the IP address of our instance: $ip" | |
fi | |
msg "Instance has the IP: $ip" | |
updated_ip=$(update_ipaddress $ip "UPSERT" $DNSNAME $RECORDTYPE \ | |
"This is a comment" $hz) | |
fetch_a=$(fetch_dns_record $hz $RECORDTYPE) | |
if [ $? != 0 ]; then | |
error_exit "We didn't update the $RECORDTYPE record properly: $fetch_a" | |
fi | |
msg "The $RECORDTYPE record for $DNSNAME is now: $fetch_a" | |
elif [ $ACTION == "down" ]; then | |
msg "Stopping instance" | |
ip=$(get_ipaddress $INSTANCE_ID) | |
if [ $? != 0 ]; then | |
error_exit "We couldn't get the IP address of our instance: $ip" | |
fi | |
instance_stopped=$(stop_instance $INSTANCE_ID) | |
if [ $? != 0 ]; then | |
error_exit "We couldn't stop the instance properly, so we quit.." | |
fi | |
msg "Removing the $RECORDTYPE record ($ip) from $DNSNAME" | |
hz=$(fetch_hosted_zone $DNSNAME) | |
if [ $? != 0 ]; then | |
error_exit "We couldn't get the hosted zone for: $DNSNAME" | |
fi | |
removed_ip=$(update_ipaddress $ip "DELETE" $DNSNAME $RECORDTYPE \ | |
"Delete" $hz) | |
fi |
This file contains 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
{ | |
"Version": "2012-10-17", | |
"Statement": [ | |
{ | |
"Effect": "Allow", | |
"Action": "ec2:DescribeInstances", | |
"Resource": "*" | |
}, | |
{ | |
"Effect": "Allow", | |
"Action": [ | |
"ec2:StopInstances", | |
"ec2:StartInstances" | |
], | |
"Resource": [ | |
"arn:aws:ec2:<region>:<account id>:instance/<instance id>" | |
] | |
} | |
] | |
} |
This file contains 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
{ | |
"Version": "2012-10-17", | |
"Statement":[ | |
{ | |
"Action":[ | |
"route53:ChangeResourceRecordSets", | |
"route53:GetHostedZone", | |
"route53:ListResourceRecordSets" | |
], | |
"Effect":"Allow", | |
"Resource":[ | |
"arn:aws:route53:::hostedzone/<hosted zone id>" | |
] | |
}, | |
{ | |
"Action":[ | |
"route53:ListHostedZones" | |
], | |
"Effect":"Allow", | |
"Resource":[ | |
"*" | |
] | |
} | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment