## EventBridgeの動作概要

## LambdaでEC2とRDSインスタンスをスケジュール停止/起動する例
Update: [Amazon EventBridge が IAM 実行ロールのサポートをすべてのターゲットに拡大](https://aws.amazon.com/jp/about-aws/whats-new/2025/03/amazon-eventbridge-iam-execution-role-all-targets/)
現在は、ターゲット側のリソースベースポリシーによる制御( AWS::Lambda::Permission )でなく、イベントソース側のロール指定で制御可能となっている。
```
AWSTemplateFormatVersion: 2010-09-09
Description: The template for creating EventBridge Schedule.
# -------------------------
# Metadata
# -------------------------
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Schedule Configuration
Parameters:
- EC2InstanceId
- RDSInstanceId
- ScheduleExpressionForStop
- ScheduleExpressionForStart
# -------------------------
# <<< Parameters
# -------------------------
Parameters:
EC2InstanceId:
Type: String
RDSInstanceId:
Type: String
ScheduleExpressionForStop:
Type: String
Default: cron(0 13 * * ? *) # UTC 0時 → JST 22時
ScheduleExpressionForStart:
Type: String
Default: cron(0 23 * * ? *) # UTC 23時 → JST 8時
Resources:
#==========================
# IAM Role for Lambda
#==========================
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: LambdaSchedulerRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/AmazonEC2FullAccess
- arn:aws:iam::aws:policy/AmazonRDSFullAccess
#==========================
# Lambda Function
#==========================
SchedulerFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: EC2RDS-Scheduler
Runtime: python3.9
Handler: index.lambda_handler
Role: !GetAtt LambdaExecutionRole.Arn
Timeout: 90
Code:
ZipFile: |
import boto3
import os
def lambda_handler(event, context):
ec2_id = os.environ['EC2_ID']
rds_id = os.environ['RDS_ID']
action = event.get('action', 'start') # 初期値(EventBridgeで上書き)
ec2 = boto3.client('ec2')
rds = boto3.client('rds')
if action == 'start':
ec2.start_instances(InstanceIds=[ec2_id])
rds.start_db_instance(DBInstanceIdentifier=rds_id)
elif action == 'stop':
ec2.stop_instances(InstanceIds=[ec2_id])
rds.stop_db_instance(DBInstanceIdentifier=rds_id)
Environment:
Variables:
EC2_ID: !Ref EC2InstanceId
RDS_ID: !Ref RDSInstanceId
#==========================
# EventBridge
#==========================
# for Start
StartSchedule:
Type: AWS::Events::Rule
Properties:
Name: EC2RDSStartSchedule
ScheduleExpression: !Ref ScheduleExpressionForStart
State: ENABLED
Targets:
- Arn: !GetAtt SchedulerFunction.Arn
Id: StartTarget
Input: '{ "action": "start" }'
# for Stop
StopSchedule:
Type: AWS::Events::Rule
Properties:
Name: EC2RDSStopSchedule
ScheduleExpression: !Ref ScheduleExpressionForStop
State: ENABLED
Targets:
- Arn: !GetAtt SchedulerFunction.Arn
Id: StopTarget
Input: '{ "action": "stop" }'
#==========================
# Lambda Permissions
#==========================
# for Start
PermissionForEventsToInvokeLambda:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref SchedulerFunction
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt StartSchedule.Arn
# for Stop
PermissionForEventsToInvokeLambdaStop:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref SchedulerFunction
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt StopSchedule.Arn
```
## SSM AutomationでEC2とRDSインスタンスをスケジュール停止/起動する例
```
AWSTemplateFormatVersion: 2010-09-09
Description: The template for creating EC2 scheduled start/stop resources via EventBridge Scheduler + SSM Automation.
# -------------------------
# <<< Parameters
# -------------------------
Parameters:
Prefix:
Type: String
Client:
Type: String
EC2StartCronExpression:
Description: 'Enter the cron expression for EC2 auto start (Asia/Tokyo). Default: weekdays 08:00 JST.'
Type: String
Default: 'cron(0 8 ? * MON-FRI *)'
EC2StopCronExpression:
Description: 'Enter the cron expression for EC2 auto stop (Asia/Tokyo). Default: weekdays 22:00 JST.'
Type: String
Default: 'cron(0 22 ? * MON-FRI *)'
SchedulerState:
Description: 'Enable or disable the schedule.'
Type: String
Default: ENABLED
AllowedValues:
- ENABLED
- DISABLED
Resources:
#==========================
# IAM Role
#==========================
EC2SchedulerRole:
Type: AWS::IAM::Role
Properties:
Path: "/"
RoleName: !Sub '${Prefix}-${Client}-ec2-scheduler-role'
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- scheduler.amazonaws.com
Action:
- sts:AssumeRole
MaxSessionDuration: 3600
Policies:
- PolicyName: !Sub '${Prefix}-${Client}-ec2-scheduler-policy'
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ssm:StartAutomationExecution
Resource:
- !Sub 'arn:aws:ssm:${AWS::Region}::document/AWS-StartEC2Instance'
- !Sub 'arn:aws:ssm:${AWS::Region}::document/AWS-StopEC2Instance'
- Effect: Allow
Action:
- ssm:GetAutomationExecution
- ssm:StopAutomationExecution
Resource: '*'
- Effect: Allow
Action:
- ec2:StartInstances
- ec2:StopInstances
- ec2:DescribeInstances
- ec2:DescribeInstanceStatus
Resource:
- !Sub
- 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:instance/${TargetInstanceId}'
- TargetInstanceId:
Fn::ImportValue:
!Sub '${Prefix}::${Client}::InstanceId'
#==========================
# EventBridge Scheduler
#==========================
# 起動用スケジュール
EC2StartSchedule:
Type: AWS::Scheduler::Schedule
Properties:
Name: !Sub '${Prefix}-${Client}-ec2-start-schedule'
Description: !Sub 'Start EC2 instance for ${Prefix}-${Client} via SSM Automation.'
State: !Ref SchedulerState
FlexibleTimeWindow:
Mode: 'OFF'
ScheduleExpression: !Ref EC2StartCronExpression
ScheduleExpressionTimezone: 'Asia/Tokyo'
Target:
Arn: 'arn:aws:scheduler:::aws-sdk:ssm:startAutomationExecution'
RoleArn: !GetAtt EC2SchedulerRole.Arn
Input:
Fn::Sub:
- '{"DocumentName":"AWS-StartEC2Instance","Parameters":{"InstanceId":["${TargetInstanceId}"]}}'
- TargetInstanceId:
Fn::ImportValue:
!Sub '${Prefix}::${Client}::InstanceId'
# 停止用スケジュール
EC2StopSchedule:
Type: AWS::Scheduler::Schedule
Properties:
Name: !Sub '${Prefix}-${Client}-ec2-stop-schedule'
Description: !Sub 'Stop EC2 instance for ${Prefix}-${Client} via SSM Automation.'
State: !Ref SchedulerState
FlexibleTimeWindow:
Mode: 'OFF'
ScheduleExpression: !Ref EC2StopCronExpression
ScheduleExpressionTimezone: 'Asia/Tokyo'
Target:
Arn: 'arn:aws:scheduler:::aws-sdk:ssm:startAutomationExecution'
RoleArn: !GetAtt EC2SchedulerRole.Arn
Input:
Fn::Sub:
- '{"DocumentName":"AWS-StopEC2Instance","Parameters":{"InstanceId":["${TargetInstanceId}"]}}'
- TargetInstanceId:
Fn::ImportValue:
!Sub '${Prefix}::${Client}::InstanceId'
```
## Amazon RDSで自動起動したデータベースインスタンスを停止する例
[Stopping an Automatically Started Database Instance with Amazon RDS](https://aws.amazon.com/jp/blogs/architecture/field-notes-stopping-an-automatically-started-database-instance-with-amazon-rds/)
このブログでAWSからはStep Functionsを用いた例が紹介されているが、やや大袈裟なためLambdaで対応
```
AWSTemplateFormatVersion: 2010-09-09
Description: The template for pairing RDS start/stop with EC2 instance state (handles RDS 7-day auto-restart).
# -------------------------
# <<< Parameters
# -------------------------
Parameters:
Prefix:
Type: String
Client:
Type: String
Resources:
#==========================
# Lambda (EC2状態にRDSを追従させる)
#==========================
RDSPairingRole:
Type: AWS::IAM::Role
Properties:
Path: "/"
RoleName: !Sub '${Prefix}-${Client}-rds-pairing-role'
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
MaxSessionDuration: 3600
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: !Sub '${Prefix}-${Client}-rds-pairing-policy'
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ec2:DescribeInstances
Resource: '*'
- Effect: Allow
Action:
- rds:DescribeDBInstances
- rds:StartDBInstance
- rds:StopDBInstance
Resource:
- !Sub
- 'arn:aws:rds:${AWS::Region}:${AWS::AccountId}:db:${TargetDBInstanceId}'
- TargetDBInstanceId:
Fn::ImportValue:
!Sub '${Prefix}::${Client}::DBInstanceIdentifier'
RDSPairingFunction:
Type: AWS::Lambda::Function
DependsOn:
- RDSPairingRole
Properties:
Description: 'Keep RDS start/stop state in sync with the paired EC2 instance.'
FunctionName: !Sub '${Prefix}-${Client}-rds-pairing'
Handler: index.handler
Runtime: python3.12
Role: !GetAtt RDSPairingRole.Arn
Timeout: 900
Environment:
Variables:
EC2_INSTANCE_ID:
Fn::ImportValue:
!Sub '${Prefix}::${Client}::InstanceId'
RDS_INSTANCE_ID:
Fn::ImportValue:
!Sub '${Prefix}::${Client}::DBInstanceIdentifier'
Code:
ZipFile: |
import boto3
import os
import time
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
ec2 = boto3.client('ec2')
rds = boto3.client('rds')
EC2_INSTANCE_ID = os.environ['EC2_INSTANCE_ID']
RDS_INSTANCE_ID = os.environ['RDS_INSTANCE_ID']
# RDSの状態を判定するのは安定ステータスのときだけ。
# 'starting'/'stopping'/'rebooting'/'modifying'等
# 遷移中ステータスをスルーして安定ステータスになるまで待つ。
STABLE_STATUSES = {'available', 'stopped'}
POLL_INTERVAL_SECONDS = 30
MAX_WAIT_SECONDS = 840 # Lambda Timeout(900秒)に対して余裕を持たせる
def get_rds_status():
return rds.describe_db_instances(
DBInstanceIdentifier=RDS_INSTANCE_ID
)['DBInstances'][0]['DBInstanceStatus']
def wait_until_stable(status):
waited = 0
while status not in STABLE_STATUSES and waited < MAX_WAIT_SECONDS:
logger.info(f'RDS status is not stable yet ({status}). Waiting {POLL_INTERVAL_SECONDS}s...')
time.sleep(POLL_INTERVAL_SECONDS)
waited += POLL_INTERVAL_SECONDS
status = get_rds_status()
return status
def handler(event, context):
logger.info(event)
ec2_state = ec2.describe_instances(
InstanceIds=[EC2_INSTANCE_ID]
)['Reservations'][0]['Instances'][0]['State']['Name']
rds_status = wait_until_stable(get_rds_status())
logger.info(f'EC2 state={ec2_state}, RDS status={rds_status}')
if ec2_state == 'running' and rds_status == 'stopped':
logger.info('Starting RDS to match EC2 state.')
rds.start_db_instance(DBInstanceIdentifier=RDS_INSTANCE_ID)
elif ec2_state == 'stopped' and rds_status == 'available':
logger.info('Stopping RDS to match EC2 state.')
rds.stop_db_instance(DBInstanceIdentifier=RDS_INSTANCE_ID)
else:
logger.info('No action needed (already in sync, or RDS status did not stabilize within the wait limit).')
#==========================
# EventBridge Rule (EC2状態変化を検知)
#==========================
EC2StateChangeRule:
Type: AWS::Events::Rule
Properties:
Name: !Sub '${Prefix}-${Client}-ec2-statechange-rule'
Description: !Sub 'Trigger RDS pairing when the EC2 instance for ${Prefix}-${Client} starts or stops.'
EventPattern:
source:
- aws.ec2
detail-type:
- 'EC2 Instance State-change Notification'
detail:
state:
- running
- stopped
instance-id:
- Fn::ImportValue:
!Sub '${Prefix}::${Client}::InstanceId'
State: ENABLED
Targets:
- Id: !Sub '${Prefix}-${Client}-rds-pairing-target'
Arn: !GetAtt RDSPairingFunction.Arn
EC2StateChangeRuleLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref RDSPairingFunction
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt EC2StateChangeRule.Arn
#==========================
# EventBridge Rule (RDSの7日強制自動起動イベントを検知)
#==========================
# RDS-EVENT-0154: 停止上限(7日)超過によりRDSが強制的に自動起動された際にのみ発火するイベント。
# RDS-EVENT-0088(DB instance started全般)は手動起動時にも発火するため対象としない。
RDSStateChangeRule:
Type: AWS::Events::Rule
Properties:
Name: !Sub '${Prefix}-${Client}-rds-statechange-rule'
Description: !Sub 'Trigger RDS pairing only when RDS for ${Prefix}-${Client} is force-restarted after exceeding the 7-day stopped limit.'
EventPattern:
source:
- aws.rds
detail-type:
- 'RDS DB Instance Event'
detail:
SourceType:
- DB_INSTANCE
SourceArn:
- Fn::ImportValue:
!Sub '${Prefix}::${Client}::DBInstanceArn'
EventID:
- RDS-EVENT-0154
State: ENABLED
Targets:
- Id: !Sub '${Prefix}-${Client}-rds-statechange-target'
Arn: !GetAtt RDSPairingFunction.Arn
RDSStateChangeRuleLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref RDSPairingFunction
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt RDSStateChangeRule.Arn
```
- [Amazon RDS イベントカテゴリとイベントメッセージ](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_Events.Messages.html)
- [Amazon EC2 インスタンスの状態変更イベント](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/monitoring-instance-state-changes.html)
0 件のコメント:
コメントを投稿