@@ -25,6 +25,7 @@ CREATE_S3_BUCKET="${INPUT_CREATE_S3_BUCKET:-false}" # Wehther to create a S3 buc
2525BUCKET_NAME=" ${INPUT_BUCKET_NAME:- } " # Name of the S3 bucket
2626INSTANCE_ID=" ${INPUT_INSTANCE_ID:- } " # ID of the created instance
2727KEY_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)
2829[ " $DEBUG " == " true" ] && set -x
2930
3031# get the organization name from the github repo
@@ -106,6 +107,81 @@ prep_create() {
106107 fi
107108}
108109
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+
109185# create the user data script
110186create_uesr_data () {
111187 # Encode user data so it can be passed as an argument to the AWS CLI
@@ -179,6 +255,35 @@ delete_s3_bucket () {
179255}
180256
181257terminate_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+
182287 # Terminate instance
183288 aws ec2 terminate-instances --instance-ids " $INSTANCE_ID " --region " ${REGION} "
184289 # Delete S3 bucket
@@ -230,6 +335,12 @@ create_runner () {
230335 exit 1
231336 fi
232337 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+
233344 rm user_data.sh
234345 # Wait for instance to become ready
235346 aws ec2 wait instance-status-ok --instance-ids " $INSTANCE_ID " --region " ${REGION} "
@@ -249,6 +360,9 @@ create_runner () {
249360 echo " runner_name=$RUNNER_NAME " >> $GITHUB_OUTPUT
250361 echo " instance_ip=$INSTANCE_IP " >> $GITHUB_OUTPUT
251362 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
252366}
253367
254368list_runner () {
0 commit comments