Skip to content

Commit d4f5ea0

Browse files
authored
Merge pull request #24 from srcecde/dev
lambda-ec2-start-stop-cron
2 parents 2bee94b + 9e6844f commit d4f5ea0

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed

lambda/lambda_ec2_stop_cron.py

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
"""
2+
-*- coding: utf-8 -*-
3+
========================
4+
AWS Lambda
5+
========================
6+
Contributor: Chirag Rathod (Srce Cde)
7+
========================
8+
"""
9+
10+
import os
11+
import sys
12+
import traceback
13+
import logging
14+
import ast
15+
import json
16+
import boto3
17+
18+
logger = logging.getLogger()
19+
logger.setLevel(logging.INFO)
20+
21+
# retrieve list of instance ids from ENV variable to skip stopping
22+
INSTANCE_IDS_TO_IGNORE_STOP = ast.literal_eval(
23+
os.environ.get("INSTANCE_IDS_TO_IGNORE_STOP", "[]")
24+
)
25+
26+
27+
def process_error() -> dict:
28+
ex_type, ex_value, ex_traceback = sys.exc_info()
29+
traceback_string = traceback.format_exception(ex_type, ex_value, ex_traceback)
30+
error_msg = json.dumps(
31+
{
32+
"errorType": ex_type.__name__,
33+
"errorMessage": str(ex_value),
34+
"stackTrace": traceback_string,
35+
}
36+
)
37+
return error_msg
38+
39+
40+
def fetch_regions() -> list:
41+
"""
42+
Helper function to retrieve regions
43+
44+
Returns:
45+
--------
46+
regions: list of AWS regions
47+
"""
48+
ec2_client = boto3.client("ec2")
49+
try:
50+
regions = ec2_client.describe_regions()
51+
except:
52+
error_msg = process_error()
53+
logger.error(error_msg)
54+
return regions["Regions"]
55+
56+
57+
def stop_instances(ec2_client: object, instance_ids: list) -> None:
58+
"""
59+
Helper function to stop the instances
60+
61+
Parameters:
62+
-----------
63+
ec2_client: boto3 region specific object
64+
instance_ids: list of instance ids to stop
65+
"""
66+
try:
67+
response = ec2_client.stop_instances(InstanceIds=instance_ids, Force=True)
68+
except:
69+
error_msg = process_error()
70+
logger.error(error_msg)
71+
72+
73+
def start_instances(ec2_client: object, instance_ids: list) -> None:
74+
"""
75+
Helper function to stop the instances
76+
77+
Parameters:
78+
-----------
79+
ec2_client: boto3 region specific object
80+
instance_ids: list of instance ids to start
81+
"""
82+
try:
83+
response = ec2_client.start_instances(InstanceIds=instance_ids)
84+
except:
85+
error_msg = process_error()
86+
logger.error(error_msg)
87+
88+
89+
def get_instance_ids(response: dict, STOP: bool) -> list:
90+
"""
91+
Parse the instance IDs from response
92+
93+
Parameters:
94+
-----------
95+
response: boto3 describe_instances response
96+
STOP: Flag to decide type of instance Ids to fetch
97+
98+
Returns:
99+
--------
100+
instance_ids: list of instance ids to stop
101+
"""
102+
if STOP:
103+
instance_ids_to_stop = []
104+
for resp in response.get("Reservations"):
105+
for instance in resp.get("Instances"):
106+
if (
107+
instance.get("State").get("Name") in ["pending", "running"]
108+
and instance["InstanceId"] not in INSTANCE_IDS_TO_IGNORE_STOP
109+
):
110+
instance_ids_to_stop.append(instance["InstanceId"])
111+
return instance_ids_to_stop
112+
else:
113+
instance_ids_to_start = []
114+
for resp in response.get("Reservations"):
115+
for instance in resp.get("Instances"):
116+
if instance.get("State").get("Name") in ["stopped", "stopping"]:
117+
instance_ids_to_start.append(instance["InstanceId"])
118+
return instance_ids_to_start
119+
120+
121+
def start_stop_instances_across_region(regions: list, STOP=True) -> None:
122+
"""
123+
Start / Stop the instances across regions
124+
125+
Parameters:
126+
-----------
127+
regions: list of regions to analyze
128+
STOP: Flag to decide whether to start or stop the instances
129+
130+
"""
131+
try:
132+
for region in regions:
133+
ec2_client = boto3.client("ec2", region_name=region["RegionName"])
134+
response = ec2_client.describe_instances()
135+
136+
instance_ids = get_instance_ids(response, STOP)
137+
if instance_ids and STOP:
138+
stop_instances(ec2_client, instance_ids)
139+
140+
if instance_ids and not STOP:
141+
start_instances(ec2_client, instance_ids)
142+
143+
while "NextToken" in response:
144+
response = ec2_client.describe_instances(
145+
NextToken=response["NextToken"]
146+
)
147+
instance_ids = get_instance_ids(response, STOP)
148+
if instance_ids and STOP:
149+
stop_instances(ec2_client, instance_ids)
150+
151+
if instance_ids and not STOP:
152+
start_instances(ec2_client, instance_ids)
153+
154+
except:
155+
error_msg = process_error()
156+
logger.error(error_msg)
157+
158+
159+
def lambda_handler(event, context):
160+
"""
161+
Main handler
162+
"""
163+
logging.info(event)
164+
# retrieve regions
165+
regions = fetch_regions()
166+
167+
try:
168+
rule_type = event["resources"][0].split("/")[-1]
169+
170+
# checking which rule triggered the lambda function
171+
if "ScheduledEC2StopRule" in rule_type:
172+
start_stop_instances_across_region(regions, STOP=True)
173+
174+
if "ScheduledEC2StartRule" in rule_type:
175+
start_stop_instances_across_region(regions, STOP=False)
176+
except:
177+
error_msg = process_error()
178+
logger.error(error_msg)
179+
180+
return {"statusCode": 200, "body": json.dumps("Thanks from Srce Cde (Chirag)!")}

0 commit comments

Comments
 (0)