Using Lambda and EventBridge to automatically stop and start EC2 Instances
Tags: devops, aws, serverless, python
Published: 2022-12-04 23:36:32

## <u>So What's the Big Picture?</u>

When looking at AWS cloud consumption, many companies may have instances that aren't actively utilized compared to others. Wouldn't it be cool, if there was a way to shutdown those instances outside of work hours, and start them back up before people log onto the system?

Well, you're in luck, because today I want to show you how to save money on your monthly AWS bill, by creating an automatic stop and start schedule on your infrequently used ec2 instances.

---

The services involved in making this happen are:

**AWS IAM**

AWS Identity and Access Management (IAM) is a web service that allows you to securely control access to your AWS resources. With IAM, we can govern who can sign in, and authorization to use resources.

For more about IAM, click this [link](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html)

**AWS Lambda**

AWS Lambda is a compute service that allows you to build applications that respond to new information and events.

Visit this [link](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) for more information about Lambda.

**AWS Eventbridge**

EventBridge is a serverless event bus that ingests data from your apps, AWS services and routes that data to targets.

Visit this [link](https://docs.aws.amazon.com/eventbridge/) to learn more about EventBridge

---

Prior to getting started, It's important that you know which ec2 instances you're wanting to test. For the sake of the demonstration, I am conducting the shutdown & start up by collecting the instance IDs.

For the demo, only one region will be used, but if you're interested in running it across multiple regions, you may need to create another lambda function to associate with that specified region.

Now that the formalities are out of the way, lets get to it!

---


### Create IAM Policy

First, search for **IAM** in the top search bar, select create policy, and choose **JSON**. For ease of use, please copy and paste the code provided.

```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:Start*",
        "ec2:Stop*"
      ],
      "Resource": "*"
    }
  ]
}
```
You can name your policy whatever you desire, but for nostalgic purposes, I named my policy **StopNGo** , which was a gas station in Houston. 

After the policy is named, select **create**.


## Developing the IAM Role

Next, we want to create the IAM Role for the Lambda function.

Since we are still in the **IAM console**, select **Roles** and select **Create Roles**.

Select the **AWS Service**, and choose **Lambda** as the common use case.

![Choose-AWS-Service](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oydsv49vfku6f2mu9duf.png)

Select Next, and search for **AWSLambdaBasicExecutionRole** and choose it.

![IAM Role](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ncpyyqir65wk8qb2fz2m.png)

While we are at it, lets ensure to add the policy we previously created. Please be sure to clear the existing filter for Lambda prior to searching.

Select **Next**, and label your role, and click **create role**. I kept it similar and named it StopNGo-N.

![Name IAM Role](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ai5n6s7pyfi3v5vas5r7.png)

## Creating our Lambda Function

At this point, we need to create a lambda function that will end up triggering the shut down and start of our selected ec2 instances.

Let's utilize the search bar at the top, and type **Lambda**, choose it, and to get started choose **Create a function**.

![Lambda Setup-1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vwyr65wknocfepaplpdc.png)

Ensure the **Author from scratch** option is highlighted, and proceed to fill out

Function Name : StopNGo-Stop
Runtime : Python 3.9
Architecture : X86_64

Under the **Permissions** section, expand the **Change default execution role** and choose **Use an existing role**. Be sure to attach the previous IAM role created.

![Lambda Function Setup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/spewbnqc4sjxtchqwdak.png)

Select **Create Function**, and let the real work commence!

---


## Function Code

Under the **code** tab, make sure we are in the **code source** subtask and lets change what we have in the editor.

Feel free to copy and paste the code below into your editor pane, but modify the **region** and **instances** section to reflect your environment.

```
import boto3
region = 'us-east-1'
instances = ['i-0d274fa2a0606dd64', 'i-0081fec0e3aa3883a', 'i-05ac19a60ca8e823d', 'i-053147387fd6e625e']
ec2 = boto3.client('ec2', region_name=region)

def lambda_handler(event, context):
    ec2.stop_instances(InstanceIds=instances)
    print('We stopped your instances: ' + str(instances))
```
**Save** the function and **deploy**

Now we want to create a separate function to kick-start our instances. When you create a lambda function, you can copy and paste the code block below. Please be sure to modify the region and instance ids to fit your use case.

```
import boto3
region = 'us-east-1'
instances = ['i-0d274fa2a0606dd64', 'i-0081fec0e3aa3883a', 'i-05ac19a60ca8e823d', 'i-053147387fd6e625e']
ec2 = boto3.client('ec2', region_name=region)

def lambda_handler(event, context):
    ec2.start_instances(InstanceIds=instances)
    print('We started your instances: ' + str(instances))
```
**Save** the function and **deploy**.

---

## Testing our functions
After creating our functions, we want to ensure they work.

Select our **StopNGo-Stop** function
Choose the **Code** tab
In the **code source** section, click **Test**

![Lambda-test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lavxe5dmh3yjtba3kktc.png)

In the **Configure test** pop-up option, **create a new event**, enter a suitable name, and click **Save**.

![Test-Event](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1pajyhxrvb4pgjuc3ml.png)

![lambda-save](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/arlbwelycc6kivnewyah.png)

**Sidenote**: There is no need to modify the JSON code for our test event, because the function won't use it.

Now, lets press the **Test** button to run the function.

![Success-fail](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ozzr4husmxrkq9e0rmy8.png)

If we analyze the execution results, the function ran properly and stopped the targeted instances. 

So, let's check our EC2 instances. The test should've shut them down.

![Success-Lambda](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7zfhl1itybdxr8ggf4q.png)

Regarding the start function, we will mirror the previous steps used for our stop function. 

Once you have verified its success, we are one step closer to the finish line.

---

## Scheduling the Time Schedule with EventBridge
Here is where the rubber meets the road, we will be creating a rule that stops and starts the targeted instances at a defined time.

Search for **EventBridge**, and open the console.

Select **Create Rule**

![EventBridge-1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hn4kn0hf60fz405xtnyu.png)

Name the Rule
Write the **description** - Optional
Under **Rule Type**, select **Schedule**
Select **Continue to create rule**

![EventBridge-2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ahfu6y089bcmp15yctbh.png)

For the schedule pattern, we are going to utilize the **fine-grained** schedule, as this is a suitable use case for our example.

Next, we are going to define the parameters that our **cron expression** will trigger.

![eventBridge-3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/10n0jg13fe77wq3i4oju.png)

I want to stop my ec2 instances everyday at 7:30pm UTC,so my minutes and hours will be entered, but different wildcard expressions are used in the other fields.

If you're wanting to create your own cron expressions, select this [link](https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html).

Select **Next**

Now, let's define our Target

- Target Types: **AWS Service**
- Target: **Lambda Function**
- Function: **StopNGo-Stop**

![Target-select](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ox0qr6wvddyrad7s3w19.png)

Select Next, skip the tags, and review the rules. 

![start-bridge](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t88lczkuzw1k8stlilkc.png)

Once rules are created for **StopNGo-Stop**, we will create another EventBridge rule for our ec2 instances to start at 6:30am UTC. 

Similar to the stop rule, I will modify the cron expression to fit my use case.

![EventBridge-Start](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/teupr2hjr9sl339bymcg.png)

If you've been able to followed me up to this point, there will be two EventBridge rules that are similar to the photo below. 
![eventbridge-5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zdkpdxq7g160se2ehf9m.png)

Which means a congratulations is in order, because you've successfully created a lambda function, and created a schedule with EventBridge that stops instances at 7:30pm UTC, and starts them at 6:30am UTC.