@@ -25,6 +25,7 @@ CREATE_S3_BUCKET="${INPUT_CREATE_S3_BUCKET:-false}" # Wehther to create a S3 buc
25
25
BUCKET_NAME=" ${INPUT_BUCKET_NAME:- } " # Name of the S3 bucket
26
26
INSTANCE_ID=" ${INPUT_INSTANCE_ID:- } " # ID of the created instance
27
27
KEY_NAME_OPT=" " # Option to pass to the AWS CLI to specify the key pair
28
+ AUTO_TERMINATE_HOURS=" ${INPUT_AUTO_TERMINATE_HOURS:- 6} " # Number of hours after which to terminate the instance (0 means no auto-termination)
28
29
[ " $DEBUG " == " true" ] && set -x
29
30
30
31
# get the organization name from the github repo
@@ -106,6 +107,81 @@ prep_create() {
106
107
fi
107
108
}
108
109
110
+ schedule_termination () {
111
+ local instance_id=$1
112
+ local hours=$2
113
+
114
+ if [ " $hours " -gt 0 ]; then
115
+ # Calculate termination time in UTC
116
+ local termination_time
117
+ termination_time=$( date -u -d " +${hours} hours" +" %Y-%m-%dT%H:%M:%SZ" )
118
+
119
+ # Create an AWS CloudWatch event rule to terminate the instance
120
+ local rule_name=" terminate-${instance_id} "
121
+
122
+ # Create CloudWatch event rule
123
+ aws events put-rule \
124
+ --name " $rule_name " \
125
+ --schedule-expression " at(${termination_time} )" \
126
+ --state ENABLED \
127
+ --region " ${REGION} "
128
+
129
+ # Create IAM role for CloudWatch to terminate EC2 instances if it doesn't exist
130
+ local role_name=" AutoTerminateEC2Role"
131
+ if ! aws iam get-role --role-name " $role_name " 2> /dev/null; then
132
+ aws iam create-role \
133
+ --role-name " $role_name " \
134
+ --assume-role-policy-document ' {
135
+ "Version": "2012-10-17",
136
+ "Statement": [{
137
+ "Effect": "Allow",
138
+ "Principal": {
139
+ "Service": "events.amazonaws.com"
140
+ },
141
+ "Action": "sts:AssumeRole"
142
+ }]
143
+ }'
144
+
145
+ # Attach policy to allow terminating EC2 instances
146
+ aws iam put-role-policy \
147
+ --role-name " $role_name " \
148
+ --policy-name " TerminateEC2Policy" \
149
+ --policy-document ' {
150
+ "Version": "2012-10-17",
151
+ "Statement": [{
152
+ "Effect": "Allow",
153
+ "Action": "ec2:TerminateInstances",
154
+ "Resource": "*"
155
+ }]
156
+ }'
157
+ fi
158
+
159
+ # Create target for the rule
160
+ aws events put-targets \
161
+ --rule " $rule_name " \
162
+ --targets " [{
163
+ \" Id\" : \" TerminateInstance\" ,
164
+ \" Arn\" : \" arn:aws:events:${REGION} :${AWS_ACCOUNT_ID} :rule/${rule_name} \" ,
165
+ \" RoleArn\" : \" arn:aws:iam::${AWS_ACCOUNT_ID} :role/${role_name} \" ,
166
+ \" Input\" : \" {\\\" instance_ids\\\" : [\\\" ${instance_id} \\\" ]}\" ,
167
+ \" RunCommandParameters\" : {
168
+ \" RunCommand\" : [
169
+ \" aws\" ,
170
+ \" ec2\" ,
171
+ \" terminate-instances\" ,
172
+ \" --instance-ids\" ,
173
+ \" ${instance_id} \" ,
174
+ \" --region\" ,
175
+ \" ${REGION} \"
176
+ ]
177
+ }
178
+ }]"
179
+
180
+ debug " Instance $instance_id scheduled for termination at $termination_time UTC"
181
+ fi
182
+ }
183
+
184
+
109
185
# create the user data script
110
186
create_uesr_data () {
111
187
# Encode user data so it can be passed as an argument to the AWS CLI
@@ -179,6 +255,35 @@ delete_s3_bucket () {
179
255
}
180
256
181
257
terminate_instance () {
258
+ local rule_name=" terminate-${instance_id} "
259
+ local role_name=" AutoTerminateEC2Role"
260
+
261
+ # Remove CloudWatch Event Rule and Targets if they exist
262
+ if aws events list-rules --name-prefix " $rule_name " --region " ${REGION} " 2> /dev/null | grep -q " $rule_name " ; then
263
+ debug " Removing CloudWatch Event targets for rule $rule_name "
264
+ aws events remove-targets --rule " $rule_name " --ids " TerminateInstance" --region " ${REGION} "
265
+
266
+ debug " Deleting CloudWatch Event rule $rule_name "
267
+ aws events delete-rule --name " $rule_name " --region " ${REGION} "
268
+ fi
269
+
270
+ # Check if the IAM role is being used by other rules before deleting
271
+ if aws iam get-role --role-name " $role_name " 2> /dev/null; then
272
+ # List all rules to check if the role is still in use
273
+ local rules_using_role
274
+ rules_using_role=$( aws events list-rules --region " ${REGION} " --query ' Rules[?RoleArn!=`null`]' --output text)
275
+
276
+ if [ -z " $rules_using_role " ]; then
277
+ debug " Removing IAM role policy"
278
+ aws iam delete-role-policy --role-name " $role_name " --policy-name " TerminateEC2Policy"
279
+
280
+ debug " Deleting IAM role $role_name "
281
+ aws iam delete-role --role-name " $role_name "
282
+ else
283
+ debug " IAM role $role_name is still in use by other rules, skipping deletion"
284
+ fi
285
+ fi
286
+
182
287
# Terminate instance
183
288
aws ec2 terminate-instances --instance-ids " $INSTANCE_ID " --region " ${REGION} "
184
289
# Delete S3 bucket
@@ -230,6 +335,12 @@ create_runner () {
230
335
exit 1
231
336
fi
232
337
fi
338
+
339
+ # Schedule auto-termination if enabled
340
+ if [ " $AUTO_TERMINATE_HOURS " -gt 0 ]; then
341
+ schedule_termination " $INSTANCE_ID " " $AUTO_TERMINATE_HOURS "
342
+ fi
343
+
233
344
rm user_data.sh
234
345
# Wait for instance to become ready
235
346
aws ec2 wait instance-status-ok --instance-ids " $INSTANCE_ID " --region " ${REGION} "
@@ -249,6 +360,9 @@ create_runner () {
249
360
echo " runner_name=$RUNNER_NAME " >> $GITHUB_OUTPUT
250
361
echo " instance_ip=$INSTANCE_IP " >> $GITHUB_OUTPUT
251
362
echo " bucket_name=$BUCKET_NAME " >> $GITHUB_OUTPUT
363
+ if [ " $AUTO_TERMINATE_HOURS " -gt 0 ]; then
364
+ echo " termination_time=$( date -u -d " +${AUTO_TERMINATE_HOURS} hours" +" %Y-%m-%dT%H:%M:%SZ" ) " >> $GITHUB_OUTPUT
365
+ fi
252
366
}
253
367
254
368
list_runner () {
0 commit comments