はじめに
「AWS 上でなにか event が発生したときに検知 -> 特定の動作を実行する」
といったことをやりたいときありますよね。(よく AWS の資格試験とかで問題に出てきてるやつです)
そんなときに役立つのが AWS EventBridge です。
今まで、EventBridge は定期実行にしか使ってなかったというのもあり新鮮だったので記事に書き起こしました。
あと、意外に event を拾うという Eventbridge の使い方記事に転がってないので誰かの参考になればと思います。
サマリー
- AWS RDS の autoscaling の event を EventBridge で検知する
- コストタグを付ける
環境
- aws-cdk : 2.107.0
内容
AWS Aurora では autoscaling を設定でき、任意の時間・CPU 使用率に応じてリードレプリカを増やしたりすることができますよね。
この機能とても便利なのですが、使うにあたって一つ問題が発生しました。
「そう、tag を autoscaling 時に自動で付与する設定がない」
ということです。
ですので、Eventbridge で auto scaling event を検知したらタグを付けるための lambda を走らせて付ける形の仕組みを作りました。
早速ですがフォルダ構成とコードはこちら ↓
classにしてるので良い感じにimportして使ってください
フォルダ構成(lambda スクリプトを cdk の asset 使ってデプロイしているためわかりやすいようにフォルダを分けている)
lib --- lambda --- main.py
|
|- event.ts
|
|- main.ts
cdk スクリプト
import * as iam from 'aws-cdk-lib/aws-iam'
import * as events from "aws-cdk-lib/aws-events"
import * as lambda from 'aws-cdk-lib/aws-lambda'
import logs from 'aws-cdk-lib/aws-logs'
import * as assets from 'aws-cdk-lib/aws-s3-assets'
import type { Construct } from 'constructs'
import * as path from 'node:path'
export class Lambda {
private region = 'ap-northeast-1'
private accoutId: string
private readonly rdsTag: string
constructor(region: string, accoutId: string, rdsTag: string) {
this.region = region
this.accoutId = accoutId
this.rdsTag = rdsTag
}
public createResource(scope: Construct): void {
const rdsAddTagLogGroup = new logs.CfnLogGroup(scope, 'RdsAddTagLogGroup', {
logGroupName: '/lambda/rds-add-tag',
retentionInDays: 120
})
const assumeRolePolicy = {
Statement: [
{
Action: 'sts:AssumeRole',
Effect: 'Allow',
Principal: {
Service: 'lambda.amazonaws.com'
}
}
],
Version: '2012-10-17'
}
const baseLambdaPolicy = new iam.PolicyStatement({
sid: 'AllowBaseLambda',
effect: iam.Effect.ALLOW,
actions: [
'logs:CreateLogGroup',
'logs:CreateLogStream',
'logs:PutLogEvents'
],
resources: [rdsAddTagLogGroup.attrArn]
})
const cfnRdsAddTagLambdaRole = new iam.CfnRole(
scope,
'RdsAddTagLambdaRole',
{
roleName: 'rds-add-tag-lambda-role',
assumeRolePolicyDocument: assumeRolePolicy,
policies: [
{
policyDocument: new iam.PolicyDocument({
statements: [
baseLambdaPolicy,
new iam.PolicyStatement({
sid: 'AllowRdsTag',
effect: iam.Effect.ALLOW,
actions: ['rds:AddTagsToResource', 'rds:ListTagsForResource'],
resources: [
`arn:aws:rds:${this.region}:${this.accoutId}:db:application-autoscaling-*`
],
conditions: {
StringEquals: {
'rds:db-tag/application-autoscaling:resourceId':
this.rdsTag
}
}
})
]
}),
policyName: 'rds-add-tag-lambda-policy'
}
]
}
)
// aurora RDSのインスタンスが作成されたときに、発火するLambda関数
const rdsAddTagLambdaCodeDir = path.join(__dirname, './lambda/rds-tag')
const rdsAddTagLambdaCodeConfig = new assets.Asset(
scope,
'RdsAddTagLambdaCodeConfig',
{
path: rdsAddTagLambdaCodeDir
}
)
const rdsAddTagLambda = new lambda.CfnFunction(scope, 'RdsAddTagLambda', {
functionName: 'rds-add-tag-lambda',
code: {
s3Bucket: rdsAddTagLambdaCodeConfig.s3BucketName,
s3Key: rdsAddTagLambdaCodeConfig.s3ObjectKey
},
handler: 'main.lambda_handler',
role: cfnRdsAddTagLambdaRole.attrArn,
runtime: lambda.Runtime.PYTHON_3_12.toString(),
timeout: 60,
memorySize: 256,
description: 'rdsがauto scalingで増えたときにtagをつけるlambda',
loggingConfig: {
logGroup: rdsAddTagLogGroup.logGroupName
},
environment: {
variables: {
TZ: 'Asia/Tokyo',
LOG_LEVEL: 'INFO'
}
}
}).addDependency(rdsAddTagLogGroup)
const rdsAddTagRule = new events.CfnRule(scope , "RdsAddTagRule", {
name: "RdsAddTagRule",
description: "RdsAddTagRule",
eventPattern: {
source: ["aws.rds"],
"detail-type": ["RDS DB Instance Event"],
detail: {
Message: ["DB instance created"]
}
},
targets: [
{
arn: rdsAddTagLambda.attrArn,
id: rdsAddTagLambda.functionName,
}
]
})
new lambda.CfnPermission(scope, "RdsAddTagLambdaPermission", {
action: "lambda:InvokeFunction",
functionName: rdsAddTagLambda.attrArn,
principal: "events.amazonaws.com",
sourceArn: rdsAddTagRule.attrArn
}).addDependency(rdsAddTagLambda)
}
}
lambda スクリプト
動作概略:rdsインスタンスが作成されたイベントをキャッチして、autoscalingにて作成されたインスタンスのみにタグを付ける動作
import boto3
import os
import logging
logger = logging.getLogger()
logger.setLevel(os.getenv("LOG_LEVEL", logging.INFO))
def lambda_handler(event, context):
client = boto3.client("rds")
arn = event["detail"]["SourceArn"]
add_key = os.getenv("KEY", "null")
add_value = os.getenv("VALUE", "null")
if add_key == "null" or add_value == "null":
logger.error(f"TAG_KEY: {add_key} or TAG_VALUE: {add_value} is not set")
exit(1)
try:
instance_tags = client.list_tags_for_resource(ResourceName=arn)
# tag_listはdictの配列
tag_list = instance_tags["TagList"]
for tag in tag_list:
key, value = tag["Key"], tag["Value"]
# keyがapplication-autoscaling:resourceIdであるかつ、valueにhogeが含まれる場合
if key == "application-autoscaling:resourceId" and "hoge" in value:
autoscaling_key = key
break
# application-autoscaling:resourceIdが存在しない場合はautoscaling以外のRDSインスタンスが作成された場合なので特に処理をせず終了
if autoscaling_key != "application-autoscaling:resourceId":
logger.info(f"application-autoscaling:resourceId is not found : {arn}")
else:
response = client.add_tags_to_resource(
ResourceName=arn, Tags=[{"Key": add_key, "Value": add_value}]
)
logger.info(f"Add key:{add_key}, value:{add_value} to RDS instance: {arn}")
except Exception as e:
logger.error(f"Error: {e}")
raise e