はじめに
本番以外の環境ではスケジュールに応じてDBを起動停止させるなんてのはよくあるシチュエーションで、Lambdaを用いて制御するのがメジャーですが、Systems Managerでも出来るよということでCDKで実装してみました。
今回使うのはSystems Manager Automationの「AWS-StartStopAuroraCluster」というドキュメントをEventBridgeを使って指定した日時に実行させようと思います。
※Systems ManagerをターゲットとしたのEventBridgeのCDK-L2コンストラクタがまだサポートされていなかったのでL1で記述しています。
Systems Manager Automation
Automationは、AWS のサービス(EC2、RDSなど)でのメンテナンスやデプロイ、修復に関するタスクを簡素化するための、AWS Systems Manager の機能の1つ。「AWS-StartStopAuroraCluster」以外にも「AWS-StartEC2Instance」や「AWSSupport-CollectECSInstanceLogs」など様々なドキュメントが標準で用意されています。
CDK
cdk.json
{
"app": "npx ts-node --prefer-ts-exts bin/src.ts",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"**/*.d.ts",
"**/*.js",
"tsconfig.json",
"package*.json",
"yarn.lock",
"node_modules",
"test"
]
},
"context": {
"@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
"@aws-cdk/core:stackRelativeExports": true,
"@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
"@aws-cdk/aws-lambda:recognizeVersionProps": true,
"@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true,
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"prefix": "eventtest",
"env": "stg",
"prod": {
"aurora": {
"instances": 2,
"instanceType": "r5.large"
}
},
"stg": {
"aurora": {
"instances": 2,
"instanceType": "t3.small"
}
},
"dev": {
"aurora": {
"instances": 1,
"instanceType": "t3.small"
}
}
}
}
app
bin/src.ts内の以下の部分でenvがprod以外の場合のみ、eventStackを走らせるようにしてます。
if (`${env}` != 'prod') {
const eventStack = new EventStack(app, `${prefix}-${env}-event`, rdsStack)
eventStack.addDependency(rdsStack);
}
#!/usr/bin/env node
import * as cdk from "aws-cdk-lib";
import { VPCStack } from "../lib/vpc-stack";
import { RDSStack } from "../lib/aurora-stack";
import { EventStack } from "../lib/event-stack";
const app = new cdk.App();
// contest取得
const prefix = app.node.tryGetContext("prefix");
const env = app.node.tryGetContext("env");
const vpcStack = new VPCStack(app, `${prefix}-${env}-vpc`)
const rdsStack = new RDSStack(app, `${prefix}-${env}-aurora`, vpcStack)
// envがprod以外の場合のみ実行
if (`${env}` != 'prod') {
const eventStack = new EventStack(app, `${prefix}-${env}-event`, rdsStack)
eventStack.addDependency(rdsStack);
}
rdsStack.addDependency(vpcStack);
stack
vpc、aurora、eventの3つのstackを用意
envがprodの場合は、event-stackが実行されずにvpcとaruroraの2つが展開されるようになります。
import * as cdk from 'aws-cdk-lib'
import * as ec2 from 'aws-cdk-lib/aws-ec2'
export interface VPCStackProps {
readonly vpc: ec2.Vpc
}
export class VPCStack extends cdk.Stack implements VPCStackProps {
public readonly vpc: ec2.Vpc
// vpc
private createVpc(name: string): ec2.Vpc {
const vpc = new ec2.Vpc(this, `${name}`, {
cidr: "10.0.0.0/16",
maxAzs: 2,
subnetConfiguration: [
{
cidrMask: 24,
name: 'Public',
subnetType: ec2.SubnetType.PUBLIC,
},
{
cidrMask: 24,
name: 'Private',
subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
},
]
})
return vpc;
}
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props)
const prefix = this.node.tryGetContext('prefix')
const env = this.node.tryGetContext('env')
this.vpc = this.createVpc(`${prefix}-${env}-vpc`)
}
}
import * as cdk from 'aws-cdk-lib'
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as rds from 'aws-cdk-lib/aws-rds'
import type { VPCStackProps } from "./vpc-stack"
export interface RDSStackProps {
readonly subnetgroup: rds.SubnetGroup
readonly cluster: rds.DatabaseCluster
}
export class RDSStack extends cdk.Stack implements RDSStackProps {
public readonly subnetgroup: rds.SubnetGroup
public readonly cluster: rds.DatabaseCluster
//SubnetGroup
private creatersubnetGroup(name: string, vpc: ec2.Vpc): rds.SubnetGroup {
const subnetgroup = new rds.SubnetGroup(this, `SubnetGroup`, {
vpc,
description: `${name}`,
subnetGroupName: name,
vpcSubnets: {subnetType: ec2.SubnetType.PRIVATE_WITH_NAT},
removalPolicy: cdk.RemovalPolicy.DESTROY,
})
return subnetgroup;
}
// rds
private createrdsCluster(name: string, vpc: ec2.Vpc, subnetgroup: rds.SubnetGroup): rds.DatabaseCluster {
const env = this.node.tryGetContext('env')
const context = this.node.tryGetContext(env);
const cluster = new rds.DatabaseCluster(this, `Database`, {
engine: rds.DatabaseClusterEngine.AURORA_MYSQL,
clusterIdentifier: `${name}-cluster`,
instanceIdentifierBase: `${name}-instance`,
instances: context.aurora.instances, // contextよりinstance数を取得
subnetGroup: subnetgroup,
instanceProps: {
vpc,
instanceType: context.aurora.instanceType, // contextよりinstanceTypeを取得
autoMinorVersionUpgrade: false,
},
storageEncrypted: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
})
return cluster;
}
constructor(scope: cdk.App, id: string, VPCStack: VPCStackProps, props?: cdk.StackProps) {
super(scope, id, props)
const prefix = this.node.tryGetContext('prefix')
const env = this.node.tryGetContext('env')
this.subnetgroup = this.creatersubnetGroup(`${prefix}-${env}-subnetgroup`, VPCStack.vpc)
this.cluster = this.createrdsCluster(`${prefix}-${env}`, VPCStack.vpc, this.subnetgroup)
}
}
import * as cdk from "aws-cdk-lib";
import * as iam from 'aws-cdk-lib/aws-iam';
import * as rds from 'aws-cdk-lib/aws-rds';
import * as events from "aws-cdk-lib/aws-events";
import type { RDSStackProps } from "./aurora-stack";
export interface EventStackProps {
readonly role: iam.Role;
}
export class EventStack extends cdk.Stack {
public readonly role: iam.Role;
// Role
private createRole(name: string, aurora: rds.DatabaseCluster): iam.Role {
const accountId = cdk.Stack.of(this).account;
const region = cdk.Stack.of(this).region;
const role = new iam.Role(this, `${name}-aurora-startstop`, {
roleName: name,
assumedBy: new iam.ServicePrincipal('events.amazonaws.com'),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonSSMAutomationRole'),
]
});
role.addToPolicy(new iam.PolicyStatement({
sid: `AuroraStartStop`,
effect: iam.Effect.ALLOW,
actions: [
"rds:DescribeDBInstances",
"rds:DescribeDBClusters",
"rds:StartDBCluster",
"rds:StopDBCluster"
],
resources: [
`arn:aws:rds:${region}:${accountId}:cluster:${aurora.clusterIdentifier}`,
`arn:aws:rds:${region}:${accountId}:db:${aurora.instanceIdentifiers[0]}`,
`arn:aws:rds:${region}:${accountId}:db:${aurora.instanceIdentifiers[1]}`
]
}))
return role;
}
// EventBridge Auto Stop
private createStopEvent(name: string, aurora: rds.DatabaseCluster, role: iam.Role): void {
const region = cdk.Stack.of(this).region;
const autoStopRule = new events.CfnRule(this, `${name}-AuroraAutoStopRule`, {
name: `${name}-AuroraAutoStopRule`,
scheduleExpression: 'cron(0 11 ? * MON-FRI *)', // 20:00(JST)に停止
targets: [{
arn: `arn:aws:ssm:${region}::automation-definition/AWS-StartStopAuroraCluster:$DEFAULT`,
id: 'AuroraClusterAutoStop',
input: `{"Action":["Stop"],"ClusterName":["${aurora.clusterIdentifier}"]}`,
roleArn: role.roleArn
}]
})
}
// EventBridge Auto Start
private createStartEvent(name: string, aurora: rds.DatabaseCluster, role: iam.Role): void {
const region = cdk.Stack.of(this).region;
const autoStartRule = new events.CfnRule(this, `${name}-AuroraAutoStartRule`, {
name: `${name}-AuroraAutoStartRule`,
scheduleExpression: 'cron(0 1 ? * MON-FRI *)', // 10:00(JST)に起動
targets: [{
arn: `arn:aws:ssm:${region}::automation-definition/AWS-StartStopAuroraCluster:$DEFAULT`,
id: 'AuroraClusterAutoStart',
input: `{"Action":["Start"],"ClusterName":["${aurora.clusterIdentifier}"]}`,
roleArn: role.roleArn
}]
})
}
constructor(scope: cdk.App, id: string, auroraStack: RDSStackProps, props?: cdk.StackProps) {
super(scope, id, props);
const env = this.node.tryGetContext("env"); // Contextで指定したprefixを取得
const prefix = this.node.tryGetContext("prefix"); // Contextで指定したprefixを取得
this.role = this.createRole(`${prefix}-${env}-event-role`, auroraStack.cluster)
this.createStopEvent(`${prefix}-${env}`, auroraStack.cluster, this.role);
this.createStartEvent(`${prefix}-${env}`, auroraStack.cluster, this.role);
}
}
動作確認
デプロイが完了してVPCなどのNW、AuroraCluster、EventBridge、IAMRoleがそれぞれ作成されました。
Aurora

IamRole
SSMAutoAutomationRoleとAuroraへの権限を付与したポリシーがアタッチされたRoleが作成されます。
AuroraClusterに対するStart/Stop権限を付与したポリシー
EventBridge
AutoStartRuleとAutoStopRuleの2種類が作成
Rule内では指定したcron通りのスケジュールが組まれてます。
Eventのターゲットを見るとSystem Maneger オートメーションの「AWS-StartStopAuroraCluster」が指定され、入力定数のActionにはStartかStop、ClusterNameにAuroraClusterの識別子がそれぞれ指定されています。Roleもちゃんと適用されてますね。

AuroraCluster自動停止起動確認
イベントスケジュールを適当な時刻に合わせて自動停止/起動を検証したところ、問題なく実行されました。

CloudTrail上でも、発信元IPアドレスがevents.amazonaws.comによるStopDBClusterとStartDBClusterがそれぞれ履歴として記録されていますね。

envをprodとdevにそれぞれ指定してdiffを確認
検証ではstgを指定してデプロイしたので、それ以外を指定した場合とで展開されるStackを比較します。
※各リソースの命名にenv区分を設けているのでdiffの結果がそのまま展開されるリソースということになります。
devの場合
devの場合はeventtest-dev-vpc
、eventtest-dev-aurora
とeventtest-dev-event
がstgの差分として出力されています。
% cdk diff -c env=dev ?[logalert]
Stack eventtest-dev-vpc
Parameters
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}
Conditions
[+] Condition CDKMetadata/Condition CDKMetadataAvailable: {"Fn::Or":[{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"af-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-northwest-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}]}
Resources
[+] AWS::EC2::VPC eventtest-dev-vpc eventtestdevvpcF1C58DDB
[+] AWS::EC2::Subnet eventtest-dev-vpc/PublicSubnet1/Subnet eventtestdevvpcPublicSubnet1Subnet3A2F729B
[+] AWS::EC2::RouteTable eventtest-dev-vpc/PublicSubnet1/RouteTable eventtestdevvpcPublicSubnet1RouteTable3CD01344
[+] AWS::EC2::SubnetRouteTableAssociation eventtest-dev-vpc/PublicSubnet1/RouteTableAssociation eventtestdevvpcPublicSubnet1RouteTableAssociationF26CBE1A
[+] AWS::EC2::Route eventtest-dev-vpc/PublicSubnet1/DefaultRoute eventtestdevvpcPublicSubnet1DefaultRoute72DD17B7
[+] AWS::EC2::EIP eventtest-dev-vpc/PublicSubnet1/EIP eventtestdevvpcPublicSubnet1EIPB98AA85A
[+] AWS::EC2::NatGateway eventtest-dev-vpc/PublicSubnet1/NATGateway eventtestdevvpcPublicSubnet1NATGatewayF5E1273E
[+] AWS::EC2::Subnet eventtest-dev-vpc/PublicSubnet2/Subnet eventtestdevvpcPublicSubnet2SubnetD6AD1270
[+] AWS::EC2::RouteTable eventtest-dev-vpc/PublicSubnet2/RouteTable eventtestdevvpcPublicSubnet2RouteTable59DEEA55
[+] AWS::EC2::SubnetRouteTableAssociation eventtest-dev-vpc/PublicSubnet2/RouteTableAssociation eventtestdevvpcPublicSubnet2RouteTableAssociation9286CEF4
[+] AWS::EC2::Route eventtest-dev-vpc/PublicSubnet2/DefaultRoute eventtestdevvpcPublicSubnet2DefaultRoute03E3B042
[+] AWS::EC2::EIP eventtest-dev-vpc/PublicSubnet2/EIP eventtestdevvpcPublicSubnet2EIPEAD4B0BE
[+] AWS::EC2::NatGateway eventtest-dev-vpc/PublicSubnet2/NATGateway eventtestdevvpcPublicSubnet2NATGateway7CAA7494
[+] AWS::EC2::Subnet eventtest-dev-vpc/PrivateSubnet1/Subnet eventtestdevvpcPrivateSubnet1SubnetDA7C0851
[+] AWS::EC2::RouteTable eventtest-dev-vpc/PrivateSubnet1/RouteTable eventtestdevvpcPrivateSubnet1RouteTableB25FFB72
[+] AWS::EC2::SubnetRouteTableAssociation eventtest-dev-vpc/PrivateSubnet1/RouteTableAssociation eventtestdevvpcPrivateSubnet1RouteTableAssociationAD7C5CC1
[+] AWS::EC2::Route eventtest-dev-vpc/PrivateSubnet1/DefaultRoute eventtestdevvpcPrivateSubnet1DefaultRoute32EA15BD
[+] AWS::EC2::Subnet eventtest-dev-vpc/PrivateSubnet2/Subnet eventtestdevvpcPrivateSubnet2SubnetF92CED9F
[+] AWS::EC2::RouteTable eventtest-dev-vpc/PrivateSubnet2/RouteTable eventtestdevvpcPrivateSubnet2RouteTable20C7ABC4
[+] AWS::EC2::SubnetRouteTableAssociation eventtest-dev-vpc/PrivateSubnet2/RouteTableAssociation eventtestdevvpcPrivateSubnet2RouteTableAssociationAD13C64F
[+] AWS::EC2::Route eventtest-dev-vpc/PrivateSubnet2/DefaultRoute eventtestdevvpcPrivateSubnet2DefaultRoute8DB548F6
[+] AWS::EC2::InternetGateway eventtest-dev-vpc/IGW eventtestdevvpcIGW31EDB392
[+] AWS::EC2::VPCGatewayAttachment eventtest-dev-vpc/VPCGW eventtestdevvpcVPCGWB4BD99D3
Outputs
[+] Output Exports/Output{"Ref":"eventtestdevvpcPrivateSubnet1SubnetDA7C0851"} ExportsOutputRefeventtestdevvpcPrivateSubnet1SubnetDA7C0851A86E0B9D: {"Value":{"Ref":"eventtestdevvpcPrivateSubnet1SubnetDA7C0851"},"Export":{"Name":"eventtest-dev-vpc:ExportsOutputRefeventtestdevvpcPrivateSubnet1SubnetDA7C0851A86E0B9D"}}
[+] Output Exports/Output{"Ref":"eventtestdevvpcPrivateSubnet2SubnetF92CED9F"} ExportsOutputRefeventtestdevvpcPrivateSubnet2SubnetF92CED9FCB2379A6: {"Value":{"Ref":"eventtestdevvpcPrivateSubnet2SubnetF92CED9F"},"Export":{"Name":"eventtest-dev-vpc:ExportsOutputRefeventtestdevvpcPrivateSubnet2SubnetF92CED9FCB2379A6"}}
[+] Output Exports/Output{"Ref":"eventtestdevvpcF1C58DDB"} ExportsOutputRefeventtestdevvpcF1C58DDB38C33E3E: {"Value":{"Ref":"eventtestdevvpcF1C58DDB"},"Export":{"Name":"eventtest-dev-vpc:ExportsOutputRefeventtestdevvpcF1C58DDB38C33E3E"}}
Other Changes
[+] Unknown Rules: {"CheckBootstrapVersion":{"Assertions":[{"Assert":{"Fn::Not":[{"Fn::Contains":[["1","2","3","4","5"],{"Ref":"BootstrapVersion"}]}]},"AssertDescription":"CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."}]}}
Stack eventtest-dev-aurora
Security Group Changes
┌───┬───────────────────────────────────┬─────┬────────────┬─────────────────┐
│ │ Group │ Dir │ Protocol │ Peer │
├───┼───────────────────────────────────┼─────┼────────────┼─────────────────┤
│ + │ ${Database/SecurityGroup.GroupId} │ Out │ Everything │ Everyone (IPv4) │
└───┴───────────────────────────────────┴─────┴────────────┴─────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Parameters
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}
Conditions
[+] Condition CDKMetadata/Condition CDKMetadataAvailable: {"Fn::Or":[{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"af-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-northwest-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}]}
Resources
[+] AWS::RDS::DBSubnetGroup SubnetGroup SubnetGroup
[+] AWS::EC2::SecurityGroup Database/SecurityGroup DatabaseSecurityGroup5C91FDCB
[+] AWS::SecretsManager::Secret Database/Secret DatabaseSecret3B817195
[+] AWS::SecretsManager::SecretTargetAttachment Database/Secret/Attachment DatabaseSecretAttachmentE5D1B020
[+] AWS::RDS::DBCluster Database DatabaseB269D8BB
[+] AWS::RDS::DBInstance Database/Instance1 DatabaseInstance1844F58FD
Outputs
[+] Output Exports/Output{"Ref":"DatabaseB269D8BB"} ExportsOutputRefDatabaseB269D8BB88F4B1C4: {"Value":{"Ref":"DatabaseB269D8BB"},"Export":{"Name":"eventtest-dev-aurora:ExportsOutputRefDatabaseB269D8BB88F4B1C4"}}
[+] Output Exports/Output{"Ref":"DatabaseInstance1844F58FD"} ExportsOutputRefDatabaseInstance1844F58FDF162DF73: {"Value":{"Ref":"DatabaseInstance1844F58FD"},"Export":{"Name":"eventtest-dev-aurora:ExportsOutputRefDatabaseInstance1844F58FDF162DF73"}}
Other Changes
[+] Unknown Rules: {"CheckBootstrapVersion":{"Assertions":[{"Assert":{"Fn::Not":[{"Fn::Contains":[["1","2","3","4","5"],{"Ref":"BootstrapVersion"}]}]},"AssertDescription":"CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."}]}}
Stack eventtest-dev-event
IAM Statement Changes
┌───┬──────────────────────┬────────┬──────────────────────┬──────────────────────┬───────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼──────────────────────┼────────┼──────────────────────┼──────────────────────┼───────────┤
│ + │ ${eventtest-dev-even │ Allow │ sts:AssumeRole │ Service:events.amazo │ │
│ │ t-role-aurora-starts │ │ │ naws.com │ │
│ │ top.Arn} │ │ │ │ │
├───┼──────────────────────┼────────┼──────────────────────┼──────────────────────┼───────────┤
│ + │ arn:aws:rds:${AWS::R │ Allow │ rds:DescribeDBCluste │ AWS:${eventtest-dev- │ │
│ │ egion}:${AWS::Accoun │ │ rs │ event-role-aurora-st │ │
│ │ tId}:cluster:{"Fn::I │ │ rds:DescribeDBInstan │ artstop} │ │
│ │ mportValue":"eventte │ │ ces │ │ │
│ │ st-dev-aurora:Export │ │ rds:StartDBCluster │ │ │
│ │ sOutputRefDatabaseB2 │ │ rds:StopDBCluster │ │ │
│ │ 69D8BB88F4B1C4"} │ │ │ │ │
│ │ arn:aws:rds:${AWS::R │ │ │ │ │
│ │ egion}:${AWS::Accoun │ │ │ │ │
│ │ tId}:db:undefined │ │ │ │ │
│ │ arn:aws:rds:${AWS::R │ │ │ │ │
│ │ egion}:${AWS::Accoun │ │ │ │ │
│ │ tId}:db:{"Fn::Import │ │ │ │ │
│ │ Value":"eventtest-de │ │ │ │ │
│ │ v-aurora:ExportsOutp │ │ │ │ │
│ │ utRefDatabaseInstanc │ │ │ │ │
│ │ e1844F58FDF162DF73"} │ │ │ │ │
└───┴──────────────────────┴────────┴──────────────────────┴──────────────────────┴───────────┘
IAM Policy Changes
┌───┬────────────────────────────────────────────┬────────────────────────────────────────────┐
│ │ Resource │ Managed Policy ARN │
├───┼────────────────────────────────────────────┼────────────────────────────────────────────┤
│ + │ ${eventtest-dev-event-role-aurora-startsto │ arn:${AWS::Partition}:iam::aws:policy/serv │
│ │ p} │ ice-role/AmazonSSMAutomationRole │
└───┴────────────────────────────────────────────┴────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Parameters
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}
Conditions
[+] Condition CDKMetadata/Condition CDKMetadataAvailable: {"Fn::Or":[{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"af-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-northwest-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}]}
Resources
[+] AWS::IAM::Role eventtest-dev-event-role-aurora-startstop eventtestdeveventroleaurorastartstop954CBEF0
[+] AWS::IAM::Policy eventtest-dev-event-role-aurora-startstop/DefaultPolicy eventtestdeveventroleaurorastartstopDefaultPolicy79B7D1C5
[+] AWS::Events::Rule eventtest-dev-AuroraAutoStopRule eventtestdevAuroraAutoStopRule
[+] AWS::Events::Rule eventtest-dev-AuroraAutoStartRule eventtestdevAuroraAutoStartRule
Other Changes
[+] Unknown Rules: {"CheckBootstrapVersion":{"Assertions":[{"Assert":{"Fn::Not":[{"Fn::Contains":[["1","2","3","4","5"],{"Ref":"BootstrapVersion"}]}]},"AssertDescription":"CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."}]}}
prodの場合
一方でprodの場合の展開されるであろうstackはeventtest-prod-aurora
とeventtest-prod-aurora
だけであることが分かります。event-stackが実行されないことが確認できましたね。
% cdk diff -c env=prod ?[logalert]
Stack eventtest-prod-vpc
Parameters
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}
Conditions
[+] Condition CDKMetadata/Condition CDKMetadataAvailable: {"Fn::Or":[{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"af-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-northwest-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}]}
Resources
[+] AWS::EC2::VPC eventtest-prod-vpc eventtestprodvpc60BD6525
[+] AWS::EC2::Subnet eventtest-prod-vpc/PublicSubnet1/Subnet eventtestprodvpcPublicSubnet1Subnet933DD4F9
[+] AWS::EC2::RouteTable eventtest-prod-vpc/PublicSubnet1/RouteTable eventtestprodvpcPublicSubnet1RouteTable3AA66A4B
[+] AWS::EC2::SubnetRouteTableAssociation eventtest-prod-vpc/PublicSubnet1/RouteTableAssociation eventtestprodvpcPublicSubnet1RouteTableAssociation8A44C4DD
[+] AWS::EC2::Route eventtest-prod-vpc/PublicSubnet1/DefaultRoute eventtestprodvpcPublicSubnet1DefaultRoute1D5C14B5
[+] AWS::EC2::EIP eventtest-prod-vpc/PublicSubnet1/EIP eventtestprodvpcPublicSubnet1EIPE51ED220
[+] AWS::EC2::NatGateway eventtest-prod-vpc/PublicSubnet1/NATGateway eventtestprodvpcPublicSubnet1NATGateway0D921433
[+] AWS::EC2::Subnet eventtest-prod-vpc/PublicSubnet2/Subnet eventtestprodvpcPublicSubnet2Subnet4D4F7879
[+] AWS::EC2::RouteTable eventtest-prod-vpc/PublicSubnet2/RouteTable eventtestprodvpcPublicSubnet2RouteTable8B22DBC8
[+] AWS::EC2::SubnetRouteTableAssociation eventtest-prod-vpc/PublicSubnet2/RouteTableAssociation eventtestprodvpcPublicSubnet2RouteTableAssociation76235E9E
[+] AWS::EC2::Route eventtest-prod-vpc/PublicSubnet2/DefaultRoute eventtestprodvpcPublicSubnet2DefaultRoute2853AEE3
[+] AWS::EC2::EIP eventtest-prod-vpc/PublicSubnet2/EIP eventtestprodvpcPublicSubnet2EIP0C3871BE
[+] AWS::EC2::NatGateway eventtest-prod-vpc/PublicSubnet2/NATGateway eventtestprodvpcPublicSubnet2NATGateway1B31C15E
[+] AWS::EC2::Subnet eventtest-prod-vpc/PrivateSubnet1/Subnet eventtestprodvpcPrivateSubnet1Subnet5F692D49
[+] AWS::EC2::RouteTable eventtest-prod-vpc/PrivateSubnet1/RouteTable eventtestprodvpcPrivateSubnet1RouteTable2A9B119A
[+] AWS::EC2::SubnetRouteTableAssociation eventtest-prod-vpc/PrivateSubnet1/RouteTableAssociation eventtestprodvpcPrivateSubnet1RouteTableAssociation61E30962
[+] AWS::EC2::Route eventtest-prod-vpc/PrivateSubnet1/DefaultRoute eventtestprodvpcPrivateSubnet1DefaultRouteF60E052F
[+] AWS::EC2::Subnet eventtest-prod-vpc/PrivateSubnet2/Subnet eventtestprodvpcPrivateSubnet2Subnet53F01FAA
[+] AWS::EC2::RouteTable eventtest-prod-vpc/PrivateSubnet2/RouteTable eventtestprodvpcPrivateSubnet2RouteTable2CD271B8
[+] AWS::EC2::SubnetRouteTableAssociation eventtest-prod-vpc/PrivateSubnet2/RouteTableAssociation eventtestprodvpcPrivateSubnet2RouteTableAssociation90AA9E8E
[+] AWS::EC2::Route eventtest-prod-vpc/PrivateSubnet2/DefaultRoute eventtestprodvpcPrivateSubnet2DefaultRoute1FA2B890
[+] AWS::EC2::InternetGateway eventtest-prod-vpc/IGW eventtestprodvpcIGW8A932F50
[+] AWS::EC2::VPCGatewayAttachment eventtest-prod-vpc/VPCGW eventtestprodvpcVPCGW6767810A
Outputs
[+] Output Exports/Output{"Ref":"eventtestprodvpcPrivateSubnet1Subnet5F692D49"} ExportsOutputRefeventtestprodvpcPrivateSubnet1Subnet5F692D49ECB47F1A: {"Value":{"Ref":"eventtestprodvpcPrivateSubnet1Subnet5F692D49"},"Export":{"Name":"eventtest-prod-vpc:ExportsOutputRefeventtestprodvpcPrivateSubnet1Subnet5F692D49ECB47F1A"}}
[+] Output Exports/Output{"Ref":"eventtestprodvpcPrivateSubnet2Subnet53F01FAA"} ExportsOutputRefeventtestprodvpcPrivateSubnet2Subnet53F01FAA21084046: {"Value":{"Ref":"eventtestprodvpcPrivateSubnet2Subnet53F01FAA"},"Export":{"Name":"eventtest-prod-vpc:ExportsOutputRefeventtestprodvpcPrivateSubnet2Subnet53F01FAA21084046"}}
[+] Output Exports/Output{"Ref":"eventtestprodvpc60BD6525"} ExportsOutputRefeventtestprodvpc60BD65256808C09B: {"Value":{"Ref":"eventtestprodvpc60BD6525"},"Export":{"Name":"eventtest-prod-vpc:ExportsOutputRefeventtestprodvpc60BD65256808C09B"}}
Other Changes
[+] Unknown Rules: {"CheckBootstrapVersion":{"Assertions":[{"Assert":{"Fn::Not":[{"Fn::Contains":[["1","2","3","4","5"],{"Ref":"BootstrapVersion"}]}]},"AssertDescription":"CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."}]}}
Stack eventtest-prod-aurora
Security Group Changes
┌───┬───────────────────────────────────┬─────┬────────────┬─────────────────┐
│ │ Group │ Dir │ Protocol │ Peer │
├───┼───────────────────────────────────┼─────┼────────────┼─────────────────┤
│ + │ ${Database/SecurityGroup.GroupId} │ Out │ Everything │ Everyone (IPv4) │
└───┴───────────────────────────────────┴─────┴────────────┴─────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Parameters
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}
Conditions
[+] Condition CDKMetadata/Condition CDKMetadataAvailable: {"Fn::Or":[{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"af-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-northwest-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}]}
Resources
[+] AWS::RDS::DBSubnetGroup SubnetGroup SubnetGroup
[+] AWS::EC2::SecurityGroup Database/SecurityGroup DatabaseSecurityGroup5C91FDCB
[+] AWS::SecretsManager::Secret Database/Secret DatabaseSecret3B817195
[+] AWS::SecretsManager::SecretTargetAttachment Database/Secret/Attachment DatabaseSecretAttachmentE5D1B020
[+] AWS::RDS::DBCluster Database DatabaseB269D8BB
[+] AWS::RDS::DBInstance Database/Instance1 DatabaseInstance1844F58FD
[+] AWS::RDS::DBInstance Database/Instance2 DatabaseInstance2AA380DEE
Other Changes
[+] Unknown Rules: {"CheckBootstrapVersion":{"Assertions":[{"Assert":{"Fn::Not":[{"Fn::Contains":[["1","2","3","4","5"],{"Ref":"BootstrapVersion"}]}]},"AssertDescription":"CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."}]}}
さいごに
今回はEventBridgeとSystem Manger Automationを使ってAuroraClusterの自動停止起動を検証してみました。
コンソール上でも比較的簡単に出来るのは勿論ですが、CDKでも定義出来る事が確認出来たのでシーンによってさっと使えるようにストックしておくのはありですね。
EventBridgeは、AWS内をターゲットとしたのものだけでなくAuth0やDatadog、Shopify、Zendeskなどの外部SaaSとの統合がサポートされており可能性を秘めたサービスなので、機会があったらそちらも試してみたいですね。
参考
https://aws.amazon.com/jp/eventbridge/integrations/
https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-automation.html
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_events-readme.html