Part 2 - Cost effective dev environment

Part 2

In part 1 on the search for a cost-effective dev environment we looked at the VPC setup. In this part we will look at the RDS setup.

There are at least 3 ways to start and stop an RDS instance (in this example I used a simple RDS instance):

  1. Using Lambda
  2. Using SSM State Manager
  3. Using Eventbridge schedules

Lambda

The first option is to use a Lambda function to start and stop the RDS instance. This is the most flexible option, but also the most complex. The Lambda function needs to be able to start and stop the RDS instance and also needs to be scheduled in the end.

This is such a common use case that AWS has prebuild this in other ways. So need to start coding.

SSM State Manager

The second option is to use SSM State Manager. With this option we can schedule an existing SSM document to start and stop an RDS instance namely

  • AWS-StopRdsInstance
  • AWS-StartRdsInstance

To run this an appropriate IAM role needs to be created with at least the following permissions:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1609968892000",
            "Effect": "Allow",
            "Action": [
                "rds:StopDBInstance",
                "rds:StartDBInstance",
                "rds:DescribeDBInstances"
            ],
            "Resource": [
                "arn:aws:rds:eu-central-1:123456789012:db:my-rds-instance"
            ]
        }
    ]
}

And the role needs the following trust relationship:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1609968892000",
      "Effect": "Allow",
      "Principal": {
        "Service": "ssm.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Next we can define a rate or cron schedule for the SSM document to run and select the correct RDS instance.

Note that the CRON schedule does not support a workday schedule like cron(30 6 ? * MON-FRI *)

Don’t know why because other cron schedules on other products do support this. Luckily there is a 3rd options that is the more modern approach.

Eventbridge Schedules

Enter Eventbridge Schedules. This is the most modern approach and also the most flexible. We can use a useful cron schedule to start and stop the RDS instance. One really nice feature of Eventbridge Schedules is the fact that we can call any API from Schedules

So what we can do with is call a services’ API via an ARN: The complete service ARN, including the API operation you want to target, in the following format: arn:aws:scheduler:::aws-sdk:service:apiAction.

For example, for Amazon SQS, the service name you specify is arn:aws:scheduler:::aws-sdk:sqs:sendMessage. Addition parameters are applied via a JSON object.

How cool is that.

We still need a role, so we reuse the one from the SSM State Manager option but change the trust relationship to:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1609968892000",
      "Effect": "Allow",
      "Principal": {
        "Service": "events.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Our target ARN is arn:aws:scheduler:::aws-sdk:rds:startDBInstance and arn:aws:scheduler:::aws-sdk:rds:stopDBInstance.

We can construct a workday cron schedule like cron(30 6 ? * MON-FRI *) and we are good to go.

In Terraform it would look like this:

resource "aws_scheduler_schedule" "rds_start" {
  name  = "StartDBInstance"
  state = "ENABLED"

  flexible_time_window {
    mode = "OFF"
  }

  schedule_expression          = "cron(30 6 ? * MON-FRI *)"
  schedule_expression_timezone = "CET"

  target {
    arn      = "arn:aws:scheduler:::aws-sdk:rds:startDBInstance"
    role_arn = aws_iam_role.rds_control.arn

    input = jsonencode({
      DbInstanceIdentifier = "${module.db.instance_id}"
    })

    retry_policy {
      maximum_event_age_in_seconds = 3600
      maximum_retry_attempts       = 3
    }
  }
}

Conclusion

Of all 3 options given here is the Eventbridge the most versatile to use.

PS

DocumentDB (mongo) uses some of the RDS API’s but not all. So the start and stop a DocumentDB Cluster use the target arns:

  • arn:aws:scheduler:::aws-sdk:rds:startDBCluster
  • arn:aws:scheduler:::aws-sdk:rds:startDBCluster

and the json input is DbClusterIdentifier instead of DBInstanceIdentifier.

Note the capitalization of the input parameter in DB (instance) vs Db (cluster).