-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathupdateasg
executable file
·246 lines (204 loc) · 8.63 KB
/
updateasg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#!/bin/bash
#
# This script snapshots the current instance volume, and updates the ASG from it
##
if [ -z $(which jq) ]; then
echo "You need jq installed to use this script."
fi
# ============== Script Parameters =======================
placement=$(wget -q -O - http://169.254.169.254/latest/meta-data/placement/availability-zone)
region=$(echo ${placement%?})
myid=$(wget -q -O - http://169.254.169.254/latest/meta-data/instance-id)
myvol=$(wget -q -O - http://169.254.169.254/latest/meta-data/block-device-mapping/ami)
volume=$(aws ec2 describe-volumes --filters Name=attachment.instance-id,Values="${myid}" Name=attachment.device,Values="${myvol}" --region $region)
vol_id=$(echo ${volume} | jq '.Volumes[].VolumeId' | tr -d '"')
vol_size=$(echo ${volume} | jq '.Volumes[].Size' | tr -d '"')
instance=$(aws ec2 describe-instances --region $region --instance-ids $myid)
secgrps=$(echo ${instance} | jq '.Reservations[].Instances[].SecurityGroups[].GroupId' | tr -d '"')
instsize=$(echo ${instance} | jq '.Reservations[].Instances[].InstanceType' | tr -d '"')
keyname=$(echo ${instance} | jq '.Reservations[].Instances[].KeyName' | tr -d '"')
iam_arn=$(wget -q -O - http://169.254.169.254/latest/meta-data/iam/info | jq '.InstanceProfileArn' | tr -d '"')
DATE=$(date +"%Y%m%d_%H%M")
Description="Automated GM snapshot of ${vol_id} "${DATE}
# ============== Function Definitions =======================
# Creates snapshot for new AMI
# Parameters: uses global variables
#
function create_snapshot
{
echo -ne "Creating snapshot of $vol_id"
snapid=$(aws ec2 create-snapshot --volume-id $vol_id --description "${Description}" --region $region | jq '.SnapshotId' | tr -d '"')
snapstatus='pending'
while [ $snapstatus = 'pending' ]; do
snapshot=$(aws ec2 describe-snapshots --snapshot-id ${snapid} --region $region)
snapstatus=$(echo $snapshot | jq '.Snapshots[0].State' | tr -d '"')
snappc=$(echo $snapshot | jq '.Snapshots[0].Progress' | tr -d '"')
bs=$(printf '%0.s\b' $(seq 1 ${#snappc}))
echo -ne ".${snappc}"
sleep 5
echo -ne "${bs}"
done
}
# Creates new AMI from snapshot, and new Launch config from generated AMI
# Parameters: uses global variables
#
function create_ami
{
echo -ne ".Creating AMI..."
ami=$(aws ec2 register-image --name "AUTO-WEB-GM_${DATE}" --root-device-name "/dev/sda1" --architecture "x86_64" --virtualization-type "hvm" --region $region --block-device-mappings "{\"DeviceName\": \"/dev/sda1\",\"Ebs\":{\"DeleteOnTermination\": true,\"VolumeType\":\"gp2\",\"VolumeSize\":${vol_size},\"SnapshotId\": \"${snapid}\"}}" | jq '.ImageId' | tr -d '"')
launchid=$(aws autoscaling create-launch-configuration --launch-configuration-name "AUTO-WEB-GM_${DATE}" --image-id ${ami} --instance-type ${instsize} --security-groups ${secgrps} --key-name ${keyname} --iam-instance-profile ${iam_arn} --block-device-mappings "{\"DeviceName\": \"/dev/sda1\",\"Ebs\":{\"DeleteOnTermination\": true,\"VolumeType\":\"gp2\",\"VolumeSize\":${vol_size},\"SnapshotId\": \"$snapid\"}}" --region $region)
echo "Done."
}
# Updates selected ASG with new Launch config
# Parameters: uses global variables
#
function update_asg
{
aws autoscaling update-auto-scaling-group --auto-scaling-group-name ${asg} --launch-configuration-name AUTO-WEB-GM_${DATE} --region ${region}
echo "${asg} updated to 'AUTO-WEB-GM_${DATE}'"
}
# Checks the running state of an instance id
# Parameter: instance-id
#
function get_instance_state
{
local istate=$(aws ec2 describe-instances --instance-ids $1 --region ${region} | jq '.Reservations[].Instances[].State.Name' | tr -d '"')
echo ${istate} | tr -d '\r\n'
}
# Terminates an instance by id
# Parameter: instance-id
#
function terminate_instance
{
local killid=$1
echo -ne "Terminating old ASG instance ${killid}."
local action=$(aws ec2 terminate-instances --instance-ids $1 --region ${region})
instate='pending'
while [ "${instate}" != "terminated" ]; do
echo -ne "."
sleep 5
instate=$(get_instance_state ${killid})
done
chillout "\r\nWaiting for ASG cooldown."
}
function chillout
{
echo -ne "$1"
counter=0
while [ $counter -lt 20 ]; do
echo -ne ".$(( 20 - $counter ))"
sleep 5
echo -ne "\b\b"
(( counter++ ))
done
}
# ============== Script Body =======================
echo -e "Automated ASG Update\r\n"
if [ -z $vol_id ]; then
echo 'Unable to identify root volume.'
exit 1;
fi
create_snapshot
create_ami
# Get a list of Autoscale Groups for user selectable update of launch config
#
asglist=$(aws autoscaling describe-auto-scaling-groups --region $region | jq '.AutoScalingGroups[].AutoScalingGroupName' | tr -d '"')
readarray asgs <<< "${asglist}"
counter=1
for asg in "${asgs[@]}"
do
asgname=$(echo ${asg} | tr -d '\n\r')
echo -e "[${counter}] ${asgname}"
(( counter++ ))
done
# Get ASG to update
#
echo "Select AutoScaleGroup to update. [0] to exit: "
read asgopt
# Exit if user doesn't want to update any Autoscale Groups
#
if [ ${asgopt} -eq 0 ]; then
echo "Update ASG with Launch configuration 'AUTO-WEB-GM_${DATE}'"
exit 0;
fi
# Make sure user really wants to update the selected ASG
#
asg=$(echo ${asgs[ ${asgopt} - 1 ]} | tr -d '\n\r')
echo -ne "Update ${asg} with Launch configuration 'AUTO-WEB-GM_${DATE}'? Type 'yes' to proceed: "
read updateasg
if [ $updateasg != 'yes' ]; then
echo "Update ASG with Launch configuration 'AUTO-WEB-GM_${DATE}'"
exit 0
fi
update_asg
# Get info about the selected ASG so we can optionally churn out existing instances if any are running
#
asginfo=$(aws autoscaling describe-auto-scaling-groups --region $region --auto-scaling-group-name $asg)
asgdesired=$(echo $asginfo | jq '.AutoScalingGroups[0].DesiredCapacity' | tr -d '"')
# Exit if no instances to churn
#
if [ ${asgdesired} -lt 1 ]; then
echo "No web nodes in ${asg} to churn out."
exit 0
fi
# Check user wants to churn out existing instances in ASG
#
echo -ne "${asg} has ${asgdesired} instances to churn. Churn instances? Type 'yes' to proceed: "
read churnasg
# Bail if churn not desired
#
if [ ${churnasg} != 'yes' ]; then
echo "Churn ${asg} instances manually."
exit 0
fi
# Create a two instance buffer for churn unless the ASG only has 2 instances
#
spawnqty=2
if [ ${asgdesired} -lt 2 ]; then
spawnqty=1
fi
# Work out whether maxsize accomodates the extra instances we'll start up for uninterupted churn.
#
required=$((${asgdesired} + ${spawnqty}))
# Get existing instance ids so we can monitor their termination
#
asginstids=$(echo ${asginfo} | jq '.AutoScalingGroups[0].Instances[].InstanceId' | tr -d '"')
echo "Adding ${spawnqty} more instances to ${asg}."
asgmaxsize=$(echo ${asginfo} | jq '.AutoScalingGroups[0].MaxSize' | tr -d '"')
maxsize=${asgmaxsize}
if [ ${asgmaxsize} -lt ${required} ]; then
maxsize=${required}
fi
aws autoscaling update-auto-scaling-group --auto-scaling-group-name ${asg} --max-size ${maxsize} --desired-capacity ${required} --region ${region}
sleep 10
# Wait for new instances to come online
#
curinsts=$(aws autoscaling describe-auto-scaling-groups --region $region --auto-scaling-group-name $asg | jq '.AutoScalingGroups[0].Instances[].InstanceId' | tr -d '"')
readarray ilist <<< "${curinsts}"
online=0
echo -ne "Waiting for ${required} instances to come online."
while [ ${online} -lt ${required} ]; do
for inst in "${ilist[@]}"; do
instid=$(echo $inst | tr -d '\n\r')
instate=$(get_instance_state ${instid})
if [ "${instate}" == "running" ]; then
(( online++ ))
fi
done
echo -ne "\033[K${online} instances running\r"
done
chillout "\r\nWaiting for new instances to come online"
# Terminate old instances except the one we're on one at a time accounting for a short cool-off period before the ASG adds another instance
#
readarray ilist <<< "${asginstids}"
for inst in "${ilist[@]}"; do
instid=$(echo $inst | tr -d '\n\r')
if [ ${instid} != ${myid} ]; then
terminate_instance ${instid}
fi
done
# Resize ASG to previous size
#
echo "Restoring ASG to previous setting of ${asgdesired} Desired and ${asgmaxsize} Max-size"
aws autoscaling update-auto-scaling-group --auto-scaling-group-name ${asg} --max-size ${asgmaxsize} --desired-capacity ${asgdesired} --region ${region}
echo "ASG update completed."