Home About

Published

- 3 min read

AWS Lambda to Send Email via SNS when Jumpbox Exceeds Uptime Threshold

aws lambda python
img of AWS Lambda to Send Email via SNS when Jumpbox Exceeds Uptime Threshold

In my previous post I talked about how I’ve written my own Python script to auto start/stop my AWS EC2 jumpbox. Since I needed to manually execute the script to stop the instance, there may come a time where I forget to do so and this will incur unwanted additional charges on my AWS account.

For this reason, I’ve also implemented a Lambda function (using Python) that runs hourly to check the instance uptime and it will send me an email notification via SNS when the instance uptime is above my defined threshold.

How the script works

Using CloudWatch, it will trigger my Lambda Function every hour and will do the following:

  • If the jumpbox instance is running for more than the “NOTIFICATION_THRESHOLD”, then send out a notification via SNS.
  • If the jumpbox instance is running for more than the “UPTIME_THRESHOLD”, then stop the instance and send out a notification via SNS.

Note: The NOTIFICATION_THRESHOLD and UPTIME_THRESHOLD are environmental variables defined in the Lambda function itself.

Example email notifications:

The script

For those that are interested in the script itself, you can find the script in my GitHub repository.

Deploy using AWS S3, GitHub Actions and Terraform

In this section, I will cover how I’ve used GitHub Actions to automatically push the code over to AWS S3 and then use Terraform to create the Lambda function + IAM role/policies for me.

S3 Bucket with Object Versioning

Before we begin, an AWS S3 bucket with versioning is required. I created this resource by using Terraform.

   resource "aws_s3_bucket" "lambda_s3_bucket" {
  bucket = var.lambda_s3_bucket_name
  acl    = "private"

  versioning {
    enabled = true
  }

    lifecycle_rule {
    enabled = true

    abort_incomplete_multipart_upload_days = 1

    noncurrent_version_expiration {
      days = 3
    }

    expiration {
      expired_object_delete_marker = true
    }
  }
}

resource "aws_s3_bucket_public_access_block" "lambda_s3_bucket_block_public" {
  bucket = aws_s3_bucket.lambda_s3_bucket.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

GitHub Docker Container Actions

The default runner by GitHub is very limited and does not provide the tooling required to talk to AWS such as the AWSCLI. In my previous blog post here, I talk about how I created this GitHub Action. I recommend you to take a look to get a better understand of how this all works.

Using Terraform to create the Lambda function

By default Terraform will not detect any changes if S3 object versioning is not used. This is because Terraform have not detected any changes to the template itself.

Now thanks to S3 object versioning, Terraform will detect a different version_id and it will update the Lambda function for me whenever a new piece of code is uploaded to S3 via GitHub Actions.

Example of the Terraform resource:

   data "aws_s3_bucket_object" "lambda_zip" {
  bucket  = var.s3_bucket_name
  key     = var.s3_lambda_zip
}

resource "aws_lambda_function" "lambda_function" {
  function_name = var.lambda_func_name
  description   = var.lambda_func_description
  role          = aws_iam_role.iam_for_lambda.arn
  handler       = "main.handler"

  s3_bucket         = data.aws_s3_bucket_object.lambda_zip.bucket
  s3_key            = data.aws_s3_bucket_object.lambda_zip.key
  s3_object_version = data.aws_s3_bucket_object.lambda_zip.version_id  # <------------

  runtime = "python3.8"

  environment {
    variables = var.lambda_environment_variables
  }
}

Conclusion

Now I can sit back and relax and if I ever forget to stop my jumpbox instance, I will be notified automatically.