Skip to content

Bravestudio-Inc-JP/autostoprds-lambda

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AutoStopRDS - AWS Lambda (Go)

An AWS Lambda function written in Go that automatically stops RDS database instances. Designed to counter AWS's behavior of automatically restarting stopped RDS instances after 7 days.

Problem

AWS automatically restarts any stopped RDS instance after 7 days. For databases that are rarely used (e.g., once every two months), this results in unnecessary running costs. This Lambda function listens for the RDS restart event and immediately stops the instance again.

How It Works

RDS auto-restarts after 7 days
  -> RDS emits event RDS-EVENT-0088 ("DB instance has been started")
    -> Amazon EventBridge matches the event pattern
      -> Invokes this Lambda function
        -> Lambda checks instance status via DescribeDBInstances
          -> If status is "available", calls StopDBInstance
            -> Instance stops again automatically

Architecture

  • Runtime: Go (compiled to static binary)
  • Lambda Runtime: provided.al2023 (Amazon Linux 2023)
  • Architecture: ARM64 (AWS Graviton2 - lower cost)
  • AWS SDK: aws-sdk-go-v2
  • Trigger: Amazon EventBridge rule on RDS instance state change

Configuration

Target Instances

By default, the function targets the instance defined in main.go. You can override this via the TARGET_INSTANCES environment variable (comma-separated for multiple instances):

TARGET_INSTANCES=my-db-1,my-db-2,my-db-3

Before deploying, update the default instance identifier in main.go:

return []string{"your-rds-instance-identifier"}

Prerequisites

  • Go 1.23 or later
  • AWS CLI configured (for deployment)
  • An AWS account with permissions to create Lambda functions, IAM roles, and EventBridge rules
  • make and zip utilities

Deployment Guide

Step 1: Create IAM Role

The Lambda function needs an execution role with permissions to stop RDS instances and write CloudWatch logs.

1.1 Create the trust policy

Save as trust-policy.json:

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

1.2 Create the permissions policy

Save as permissions-policy.json:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "rds:DescribeDBInstances",
                "rds:StopDBInstance"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        }
    ]
}

Permission details:

Permission Purpose
rds:DescribeDBInstances Check the current status of the RDS instance
rds:StopDBInstance Send the stop command to the RDS instance
logs:CreateLogGroup Create CloudWatch log group for the function
logs:CreateLogStream Create log streams within the log group
logs:PutLogEvents Write log entries to CloudWatch

1.3 Create the role via AWS Console

  1. Go to IAM -> Roles -> Create role
  2. Trusted entity type: AWS service
  3. Use case: Lambda
  4. Click Next
  5. Click Create policy -> JSON tab -> paste the permissions policy above
  6. Name it (e.g., LambdaRDSStopPolicy) -> Create policy
  7. Back in the role creation, search and select the policy you just created
  8. Name the role (e.g., LambdaRDSStopRole) -> Create role
  9. Note the role ARN (e.g., arn:aws:iam::<ACCOUNT_ID>:role/LambdaRDSStopRole)

1.3 (Alternative) Create the role via AWS CLI

# Create the role
aws iam create-role \
  --role-name LambdaRDSStopRole \
  --assume-role-policy-document file://trust-policy.json

# Create the policy
aws iam create-policy \
  --policy-name LambdaRDSStopPolicy \
  --policy-document file://permissions-policy.json

# Attach the policy to the role (replace <ACCOUNT_ID>)
aws iam attach-role-policy \
  --role-name LambdaRDSStopRole \
  --policy-arn arn:aws:iam::<ACCOUNT_ID>:policy/LambdaRDSStopPolicy

Step 2: Build the Lambda function

# Clone the repository
git clone <repository-url>
cd go-lambda

# Update the target RDS instance identifier in main.go
# Edit the default in getTargetInstances() function

# Download dependencies
go mod tidy

# Build the binary and create the zip
make zip

This produces function.zip containing the bootstrap binary (~5.7MB zipped).

To verify the build:

file bootstrap
# Expected: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked

Note: To build for x86_64 instead of ARM64, edit the Makefile and change GOARCH=arm64 to GOARCH=amd64. You must also select x86_64 architecture when creating the Lambda function.


Step 3: Create the Lambda function

Via AWS Console

  1. Go to Lambda -> Create function
  2. Select Author from scratch
  3. Function name: choose a name (e.g., autostoprds-go)
  4. Runtime: Amazon Linux 2023 (provided.al2023)
  5. Architecture: arm64
  6. Execution role: Use an existing role -> select the role from Step 1
  7. Click Create function
  8. In Code source, click Upload from -> .zip file -> upload function.zip
  9. Go to Runtime settings -> Edit -> set Handler to bootstrap -> Save

Via AWS CLI

aws lambda create-function \
  --function-name autostoprds-go \
  --runtime provided.al2023 \
  --handler bootstrap \
  --architectures arm64 \
  --role arn:aws:iam::<ACCOUNT_ID>:role/LambdaRDSStopRole \
  --zip-file fileb://function.zip

(Optional) Set environment variable for custom targets

aws lambda update-function-configuration \
  --function-name autostoprds-go \
  --environment "Variables={TARGET_INSTANCES=<your-rds-instance-id>}"

Step 4: Create EventBridge rule

The EventBridge rule listens for the RDS auto-restart event and triggers the Lambda function.

Via AWS Console

  1. Go to Amazon EventBridge -> Rules -> Create rule
  2. Name: choose a name (e.g., autostoprds-on-start)
  3. Event bus: default
  4. Rule type: Rule with an event pattern
  5. Click Next
  6. Select Custom patterns (JSON editor) and paste:
{
    "source": ["aws.rds"],
    "detail-type": ["RDS DB Instance Event"],
    "detail": {
        "SourceIdentifier": ["<your-rds-instance-identifier>"],
        "EventID": ["RDS-EVENT-0088"]
    }
}
  1. Click Next
  2. Target: AWS service -> Lambda function -> select your Lambda function
  3. Click through Next -> Create rule

Note: To monitor multiple instances, add their identifiers to the SourceIdentifier array:

"SourceIdentifier": ["instance-1", "instance-2", "instance-3"]

Via AWS CLI

# Create the rule
aws events put-rule \
  --name autostoprds-on-start \
  --event-pattern '{
    "source": ["aws.rds"],
    "detail-type": ["RDS DB Instance Event"],
    "detail": {
      "SourceIdentifier": ["<your-rds-instance-identifier>"],
      "EventID": ["RDS-EVENT-0088"]
    }
  }'

# Grant EventBridge permission to invoke the Lambda
aws lambda add-permission \
  --function-name autostoprds-go \
  --statement-id eventbridge-invoke \
  --action lambda:InvokeFunction \
  --principal events.amazonaws.com \
  --source-arn arn:aws:events:<REGION>:<ACCOUNT_ID>:rule/autostoprds-on-start

# Add the Lambda as target
aws events put-targets \
  --rule autostoprds-on-start \
  --targets "Id"="1","Arn"="arn:aws:lambda:<REGION>:<ACCOUNT_ID>:function:autostoprds-go"

Step 5: Verify the trigger is connected

After creating the EventBridge rule, verify it appears in the Lambda console:

  1. Go to Lambda -> your function -> Function overview
  2. EventBridge should appear as a trigger in the diagram
  3. If it doesn't appear, click + Add trigger -> EventBridge -> select your existing rule -> Add

Step 6: Test the setup

  1. Manual test: In the Lambda console, click Test with an empty event {}. Check the execution result — it should show the current status of your RDS instance.

  2. Check CloudWatch Logs: Go to CloudWatch -> Log groups -> /aws/lambda/<your-function-name> to see log output.

  3. Live monitoring: Go to CloudWatch -> Logs -> Live tail -> select your function's log group -> Start to watch logs in real-time when the event fires.

  4. End-to-end test: Start your RDS instance from the console and watch the Live tail. Within a few minutes of the instance reaching available status, the Lambda should fire and stop it.


Updating the Function

After making code changes:

make clean
make zip

Then upload function.zip via the Lambda console or CLI:

aws lambda update-function-code \
  --function-name <your-function-name> \
  --zip-file fileb://function.zip

RDS Event Reference

Event ID Description
RDS-EVENT-0088 The DB instance has been started
RDS-EVENT-0087 The DB instance has been stopped
RDS-EVENT-0154 The DB instance is being started due to exceeding the maximum allowed time being stopped

This function triggers on RDS-EVENT-0088. You can also add RDS-EVENT-0154 to the EventBridge pattern to specifically catch the "7-day auto-restart" event.

Log Messages

The function logs in Japanese:

Log Message Meaning
停止コマンドを送信しました Stop command sent successfully
既に停止しています Instance is already stopped
現在停止処理中です Instance is currently stopping
停止できないステータスです Instance is in a state that cannot be stopped
エラーが発生しました An error occurred

About

ステージング環境のRDSデータベースは、あまり使用していません。 メジャーバージョンアップデートがあるときに、2か月に1回程度使うくらいです。 ただし、データ自体は必要です。 なお、RDSインスタンスを停止しても、7日後に自動的に再起動します。

Resources

Stars

Watchers

Forks

Contributors