1
0

More than 1 year has passed since last update.

チャットからLambda関数の実行を予約する

Last updated at Posted at 2022-01-30

はじめに

LINEからLambda関数の指定時刻実行を予約したかったのでLambda関数を作成しました。

つくるもの

イメージ

訓練時.drawio (1).png

  • 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ロールを開きます。
Lambda_Role.PNG
開いた画面でポリシー編集ボタンを押下します。
ポリシー編集ボタン.PNG
「さらにアクセス許可を追加する」を選択、サービス「EventBridge」を選択し以下の権限を追加し、変更を保存します。

  • ListRules: ルールのリスト表示
  • PutRule: ルール作成
  • PutTargets: ルールのターゲット設定
  • TagResource: ルールのTag追加
  • ListTargetsByRule: ルールのターゲットのリスト表示
  • RemoveTargets: ルールのターゲット削除(ターゲットがあるルールは削除出来ない)
  • DeleteRule: ルール削除 target_list.PNG

Tag操作に必要な権限を追加します。「さらにアクセス許可を追加する」を選択、サービス「Resource Group Tagging」を選択し以下の権限を追加し、変更を保存します。

  • GetResources: Tagからのリソース検索
  • TagResources: Tagの追加 tagu---.PNG

環境変数設定

EventBridgeルールのターゲット指定時にLambda関数のarnが必要なため、環境変数にリージョン及びアカウントIDを設定します。
「Make_LambdaEvent」関数の画面を開き、「設定」タブの「環境変数」から編集ボタンを押下します。
環境変数1.PNG
「環境変数の追加」ボタンを押下し、以下の変数を追加し保存します。
環境変数2.PNG

  • region: 実効予約をする関数のリージョン(東京の場合はap-northeast-1)
  • accountid: 実効予約をする関数があるAWSアカウントのID(12桁の数字)

パーミッション付与

実効対象のLambda関数にて「設定」タブ「アクセス権限」の「ポリシーステートメント」にて、EventBridgeルールからの関数実行を許可します。
パーミッション.PNG

コード

「Make_LambdaEvent」関数の「コード」画面にて以下を貼り付けてDeployボタンを押下します。

lambda_function.py
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(実行する時間)>

set.PNG
作成されたEventBridgeルール
set_result.PNG

指定した時間に実行されます。
実効.PNG

実行済みのEventBridgeルール削除

チャットツールで以下形式のメッセージを送信する事で、本機能で作成され、かつ実行済みのEventBridgeルールが削除されます。
delete event
before.PNG
delete.PNG
after.PNG

参考

CloudWatchEvents - Boto 3 Docs 1.9.42 documentaion
ResourceGroupsTaggingAPI - Boto 3 Docs 1.20.46 documentaion

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0