Danny Nunez

3 minutes read

Creating EBS Volume Snapshots with Lambda and Terraform

In this tutorial, we will cover setting up a Scheduled Event that will trigger a Lambda Function to create an EBS volume snapshot. Amazon CloudWatch Events are near real time events that describe changes with your AWS resources. These events can be used to trigger actions such as running a lambda function or sending an SMS message. These events can also be scheduled to run on a cron like schedule. This tutorial will cover creating an Amazon CloudWatch Event using a cron like schedule to trigger a Lambda Function that will create the EBS volume snapshot.

Dependencies

There are several requirements that need to be installed or configured prior to continuing with this tutorial.

Skills Required:

  • General Understanding on AWS
  • Comfortable with using the command line interface
  • General Understanding of Linux
  • Ability to install applications

Clone the Repository and Create the Base Infrastructure

Clone the AWS-CloudWatch-Scheduled-Events-With-Terraform repository. Once you have cloned the repository open it in your editor of choice and go to the ebs-volumes/ebs-volume-snapshot-incomplete directory and run the following command and follow the prompts to have terraform create the basic infrastructure needed to continue on with this tutorial.

terraform init
terraform apply

Next we will create our lambda function. Open the ebs-volumes/ebs-volume-snapshot-incomplete/ebs-snapshot.tf file and lets start building it.

We will need to create the zip file of the python code that will be executed.

resource "null_resource" "build_lambda_zip" {

  triggers = {
    key = uuid()
  }

  provisioner "local-exec" {
    working_dir = "."

    command = <<EOF
    mkdir ${path.module}/lambda
    mkdir ${path.module}/tmp
    cp    ${path.module}/create_snapshot.py ${path.module}/tmp/create_snapshot.py
EOF
  }
}

data "archive_file" "lambda_zip" {
  type        = "zip"
  source_dir  = "${path.module}/tmp"
  output_path = "${path.module}/lambda/tmp.zip"

  depends_on = [
    null_resource.build_lambda_zip
  ]
}

Next we will create the IAM Role that will be attached to the Lambda function. The IAM Role will grant the Lambda function the permissions it will need to create the ebs volume snapshot. The Permissions for this are not set for production security requirements. This is for educational purposes only.

data "aws_iam_policy_document" "create_ebs_volume_snapshot_assume_role_policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "create_ebs_volume_snapshot" {
  name               = "create-ebs-volume-snapshot"
  assume_role_policy = data.aws_iam_policy_document.create_ebs_volume_snapshot_assume_role_policy.json
}

data "aws_caller_identity" "current" {}

data "aws_iam_policy_document" "create_ebs_volume_snapshot" {
  statement {
    effect = "Allow"

    sid = "CreateEBSVolumeSnapshot"

    actions = [
      "ec2:*",
    ]

    resources = [
      "*",
    ]
  }
}

resource "aws_iam_role_policy" "create_ebs_volume_iam_role_policy_lambda" {
  name   = "create_ebs_volume"
  role   = aws_iam_role.create_ebs_volume_snapshot.id
  policy = data.aws_iam_policy_document.create_ebs_volume_snapshot.json
}

Create the Lambda function.

resource "aws_lambda_function" "create_ebs_volume_snapshot" {
  function_name    = "create-ebs-volume-snapshot"
  filename         = "${path.module}/lambda/tmp.zip"
  source_code_hash = data.archive_file.lambda_zip.output_base64sha256
  role             = aws_iam_role.create_ebs_volume_snapshot.arn
  runtime          = "python3.6"
  handler          = "create_snapshot.lambda_handler"
  timeout          = "60"
  publish          = true

  depends_on = [
    data.archive_file.lambda_zip
  ]

  environment {
    variables = {
      VOLUME_ID = tolist(aws_instance.this.ebs_block_device)[0]["volume_id"]
    }
  }

  tags = {
    Terraform = true
  }
}

Create the the CloudWatch Schedule Event to trigger the Lambda Function.

resource "aws_cloudwatch_event_rule" "ebs_snapshot" {
  name                = "ebs-snapshot"
  description         = "Cronlike scheduled for creating daily ebs volume snapshot."
  schedule_expression = var.cron_expression
}

resource "aws_cloudwatch_event_target" "ebs_backup" {
  rule      = aws_cloudwatch_event_rule.ebs_snapshot.name
  target_id = aws_lambda_function.create_ebs_volume_snapshot.id
  arn       = aws_lambda_function.create_ebs_volume_snapshot.arn
}

resource "aws_lambda_permission" "allow_cloudwatch" {
  statement_id  = "AllowExecutionFromCloudWatchCreateEBSVolumeSnapshot"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.create_ebs_volume_snapshot.arn
  principal     = "events.amazonaws.com"
  source_arn    = aws_cloudwatch_event_rule.ebs_snapshot.arn
}

The Lambda function will create a EBS Volume Snapshot based on the scheduled expression. To view the next scheduled times the Lambda function will be triggered go to AWS Console > CloudWatch > Rules > ebs-snapshot.

CloudWatch Scheduled Event Times

Clean up

Now it is time to destroy our resources to avoid unwanted charges. Go to the ebs-volumes/ebs-volume-snapshot-incomplete directory and run the following command and follow the prompts to remove the AWS Resources created.

terraform destroy

Recent posts

Categories