はじめに
LINEからLambda関数の指定時刻実行を予約したかったのでLambda関数を作成しました。
つくるもの
イメージ
- LINEから受け取ったメッセージを基にLambdaでboto3を実行し、指定のLambda関数を実行予約するEventBridgeルールを作成します。
- 定期実行ではないため実行予約1つにつき1つのEventBridgeルールを作成します。
- ルール名は<Lambda関数名-実行時間>とし、この関数から作成された事を示すためのTagと実行時刻(UTC)Tagを付与します。
- Tagを参照しこの関数から作成されたEventBridgeルールの中で、実行済みのEventBridgeルールをチャットから一括削除出来るようにします。
- 実行対象に取れる関数のリージョンは1つのみです(メッセージでリージョンを指定させる事も可能ですが、基本東京リージョンでしかLambda関数を使用していないため)。
使用技術
- Python3.8
- AWS SDK for Python (Boto3)
- AWS Lambda
- Amazon EventBridge
実装
AWS Lambda関数の作成
AWSマネジメントコンソールにて関数を作成します。
関数名「Make_LambdaEvent」、ランタイム「Python 3.8」、その他はデフォルト設定です。
トリガーの追加
作成したLambda関数にトリガーを追加します。
作成した関数の画面を開き、「設定」タブの「トリガー」を開き、トリガーを追加します。
API Gatewayを選択、チャットツールのWebhookに生成されたAPI エンドポイントを設定してください。
IAMロールのポリシー編集
Lambda関数にアタッチされているロールにEventBridgeルール操作に必要な権限を追加します。
「Make_LambdaEvent」関数の画面を開き、「設定」タブの「アクセス権限」から実行ロールを押下し、関数にアタッチされているIAMロールを開きます。
開いた画面でポリシー編集ボタンを押下します。
「さらにアクセス許可を追加する」を選択、サービス「EventBridge」を選択し以下の権限を追加し、変更を保存します。
- ListRules: ルールのリスト表示
- PutRule: ルール作成
- PutTargets: ルールのターゲット設定
- TagResource: ルールのTag追加
- ListTargetsByRule: ルールのターゲットのリスト表示
- RemoveTargets: ルールのターゲット削除(ターゲットがあるルールは削除出来ない)
- DeleteRule: ルール削除
Tag操作に必要な権限を追加します。「さらにアクセス許可を追加する」を選択、サービス「Resource Group Tagging」を選択し以下の権限を追加し、変更を保存します。
環境変数設定
EventBridgeルールのターゲット指定時にLambda関数のarnが必要なため、環境変数にリージョン及びアカウントIDを設定します。
「Make_LambdaEvent」関数の画面を開き、「設定」タブの「環境変数」から編集ボタンを押下します。
「環境変数の追加」ボタンを押下し、以下の変数を追加し保存します。
- region: 実効予約をする関数のリージョン(東京の場合はap-northeast-1)
- accountid: 実効予約をする関数があるAWSアカウントのID(12桁の数字)
パーミッション付与
実効対象のLambda関数にて「設定」タブ「アクセス権限」の「ポリシーステートメント」にて、EventBridgeルールからの関数実行を許可します。
コード
「Make_LambdaEvent」関数の「コード」画面にて以下を貼り付けてDeployボタンを押下します。
import json
import boto3
import os
import datetime
#Lambda関数の実効を予約
def make_rule(lambda_name,exec_date):
#日付をdatetimeに変換、JSTをUTCに、cron式に変換
dt_exec_date = datetime.datetime.strptime(exec_date, '%Y/%m/%d %H:%M')
dt_exec_date = dt_exec_date - datetime.timedelta(hours=9)
cron_exec_date = f"cron({dt_exec_date.minute} {dt_exec_date.hour} {dt_exec_date.day} {dt_exec_date.month} ? {dt_exec_date.year})"
#EventBridgeルール名<Lambda関数名-実行時間>
rule_name = lambda_name + '-' + exec_date.replace('/', '').replace(' ', '').replace(':', '')
#EventBridgeルール作成
event_client = boto3.client('events')
response = event_client.put_rule(
Name = rule_name,
ScheduleExpression = cron_exec_date,
EventPattern = '',
State = 'ENABLED',
Description = 'Made by Make_LambdaEventFunction.'
)
rule_arn = response['RuleArn']
#環境変数からTargetに指定するLambda関数のarnを作成
lambda_arn = 'arn:aws:lambda:' + os.environ['region'] + ':' + os.environ['accountid'] + ':function:' + lambda_name
#EventBridgeルールのTargetを指定
response_t = event_client.put_targets(
Rule = rule_name,
Targets = [{'Id': lambda_name,'Arn': lambda_arn}]
)
#作成したEventBridgeルールにTagを追加
tag_client = boto3.client('resourcegroupstaggingapi')
response = tag_client.tag_resources(
ResourceARNList = [rule_arn],
Tags = {
'DELETE_RULE': 'ON',
'EXEC_DATE': exec_date
}
)
#実効済みEventBridgeルールを削除
def delete_rule():
#EventBridgeルールの中でDELETE_RULEキーがONのルールの中で予約時間から1日以上経過しているルールを配列に格納
deleteon_resource_arr = []
tag_client = boto3.client('resourcegroupstaggingapi')
response = tag_client.get_resources(
TagFilters = [{'Key': 'DELETE_RULE','Values': ['ON']}],
ResourceTypeFilters = ['events']
)
for deleteon_resource in response['ResourceTagMappingList']:
for tag in deleteon_resource['Tags']:
if tag['Key'] == 'EXEC_DATE':
if datetime.datetime.now() > (datetime.datetime.strptime(tag['Value'], '%Y/%m/%d %H:%M') - datetime.timedelta(hours=9)):
deleteon_resource_arr.append(deleteon_resource['ResourceARN'])
while response['PaginationToken'] != '':
response = tag_client.get_resources(
PaginationToken = response['PaginationToken'],
TagFilters = [{'Key': 'DELETE_RULE','Values': ['ON']}],
ResourceTypeFilters = ['events']
)
for deleteon_resource in response['ResourceTagMappingList']:
for tag in deleteon_resource['Tags']:
if tag['Key'] == 'EXEC_DATE':
if datetime.datetime.now() > (datetime.datetime.strptime(tag['Value'], '%Y/%m/%d %H:%M') - datetime.timedelta(hours=9)):
deleteon_resource_arr.append(deleteon_resource['ResourceARN'])
#配列に格納されたEventBridgeルールを削除
event_client = boto3.client('events')
for deleteon_resource in deleteon_resource_arr:
deleteon_rule_name = (deleteon_resource.split('/'))[-1]
#EventBridgeルールのTargetを削除
response = event_client.list_targets_by_rule(
Rule=deleteon_rule_name
)
for target in response['Targets']:
response = event_client.remove_targets(
Rule = deleteon_rule_name,
Ids = [target['Id']]
)
#EventBridgeルールを削除
response = event_client.delete_rule(
Name = (deleteon_resource.split('/'))[-1]
)
print(deleteon_resource_arr)
def lambda_handler(event, context):
#LINE以外のメッセージAPIを使用する場合は下の1行=以降を変更してください。
received_text = json.loads(event['body'])['events'][0]['message']['text']
received_text_arr = received_text.split('\n')
if received_text_arr[0] == 'set event':
make_rule(received_text_arr[1],received_text_arr[2])
elif received_text_arr[0] == 'delete event':
delete_rule()
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
使用方法
Lambda関数の実行予約作成
チャットツールで以下形式のメッセージを送信する事でLambda関数をターゲットに取るEventBridgeルールが作成されます。
set event
<実行するLambda関数名>
<YYYY/MM/DD hh:mm(実行する時間)>
作成されたEventBridgeルール
実行済みのEventBridgeルール削除
チャットツールで以下形式のメッセージを送信する事で、本機能で作成され、かつ実行済みのEventBridgeルールが削除されます。
delete event
参考
CloudWatchEvents - Boto 3 Docs 1.9.42 documentaion
ResourceGroupsTaggingAPI - Boto 3 Docs 1.20.46 documentaion