LoginSignup
7
0

AWS Lambda+EventBridgeを利用した時間起動の自動処理

Posted at

はじめに

稼働時間外にシステムへアクセスがあったユーザに対して、稼働時間外であることを伝えるページを表示させるため、AWS Lambda+EventBridgeにて自動化してみました。

今回のケースに限らず、他の処理自動化にも流用することができると思います。

環境

  • macOS Sonoma 14.1
  • terraform 1.5.2

目次

  • イメージ図
  • ELBリスナールールの作成
  • Lambdaの設定
    • Lambda関数の作成
    • iamロールの設定
  • EventBridgeの設定
    • EventBridgeの作成
    • targetの設定
    • lambda permissionの設定
  • 補足

イメージ図

対象システムにはELBを配置しているため、ELBリスナールールにて稼働時間外メッセージ表示、管理を行います。
image.png

ELBリスナールールの作成

まずは、稼働時間外メッセージ表示のリスナールールを作成します。

aws_lb_listener_rule.tf
# aws_lb_listener_rule.change_lb_listener_rule_priority_foo_close:
resource "aws_lb_listener_rule" "change_lb_listener_rule_priority_foo_close" {
    listener_arn = aws_lb_listener.foo-https.arn
    priority     = 1000
    tags         = {}
    action {
        order            = 1
        type             = "fixed-response"
        fixed_response {
            content_type = "text/html"
            message_body = <<-EOT
                <!doctype html>
                <head>
                <meta charset="utf-8">
                <title>システム稼働時間外</title>
                </head>
                <body>
                <header>
                <div id="content">
                <section class="row">
                    <div class="col-full">
                    <p>
                        当サイトはシステム稼働時間外のためアクセスできません。<br>
                        稼働時間まで今しばらくお待ちください。<br>
                    </p>
                    <p>
                        This site is currently out of operation hours<br>
                        Please wait a while until the operation time.<br>
                    </p>
                    <h1>稼働時間<br>08:00 〜 19:00</h1>
                    </div>
                </section>
                <section class="row">
                <div class="col-full">
                    <p>
                    <span style="font-weight:bold;">株式会社foo</span><br /> <span style="font-size:70%;">© foo Inc.</span>
                    </p>
                </div>
                </section>
                </div>
                </body>
                </html>
                EOT
            status_code  = "503"
        }
    }
    condition {
        host_header {
            values = [
                "foo.com",
                "bar.com",
            ]
        }
    }
    lifecycle {
        ignore_changes = [
            priority,
            action[0].fixed_response[0].message_body
        ]
    }
}

Lamdbaの設定

Lambda関数の作成

続いて、Lambda関数を作成します。
作成したELBリスナールールのpriority順位を変更することにより、稼働時間外メッセージの表示・非表示を切り替えるための関数になります。

/../../lambda_functions/change_lb_listener_rule_priority/lambda_function.py
import boto3
import json

def lambda_handler(event, context):
    print(json.dumps(event))

    client = boto3.client('elbv2')

    try:
        # event値取得
        listener_rule_arn = event['resources'][0]
        priority_value = int(event['detail']['priority'][0])

        # リスナールールのpriority変更
        response = client.set_rule_priorities(
            RulePriorities=[
                {
                    'RuleArn': listener_rule_arn,
                    'Priority': priority_value
                },
            ]
        )
        return {
            'statusCode': 200,
            'body': f'Successfully updated rule priority for {listener_rule_arn} to {priority_value}'
        }
    except Exception as e:
        raise e

lambda_function.pyはterraformとは別ディレクトリにて管理していたため、data.tf内のpath.moduleにてパスを指定し、参照するにようにしています。

aws_lambda_function.tf
# aws_lambda_function.change_lb_listener_rule_priority:
resource "aws_lambda_function" "change_lb_listener_rule_priority" {
    architectures                  = [
        "x86_64",
    ]
    function_name                  = "change_lb_listener_rule_priority"
    filename                       = data.archive_file.change_lb_listener_rule_priority.output_path
    handler                        = "lambda_function.lambda_handler"
    layers                         = []
    memory_size                    = 128
    package_type                   = "Zip"
    publish                        = false
    source_code_hash               = data.archive_file.change_lb_listener_rule_priority.output_base64sha256
    reserved_concurrent_executions = -1
    role                           = aws_iam_role.LambdaRole_change_lb_listener_rule_priority.arn
    runtime                        = "python3.9"
    tags                           = {}
    timeout                        = 120
    ephemeral_storage {
        size = 512
    }
    tracing_config {
        mode = "PassThrough"
    }
    vpc_config {
        security_group_ids = [
            aws_security_group.lambda.id,
        ]
        subnet_ids         = [
            aws_subnet.protect-1a.id,
            aws_subnet.protect-1c.id,
            aws_subnet.protect-1d.id,
        ]
    }
}
data.tf
data "archive_file" "change_lb_listener_rule_priority" {
    type        = "zip"
    output_path = "${path.module}/../../lambda_functions/change_lb_listener_rule_priority/change_lb_listener_rule_priority.zip"
    source_file = "${path.module}/../../lambda_functions/change_lb_listener_rule_priority/lambda_function.py"
  }

iamロールの設定

Lambda関数に対してELBリスナールールのpriority変更を実施できる権限を付与します。

aws_iam_role.tf
# aws_iam_role.LambdaRole_change_lb_listener_rule_priority:
resource "aws_iam_role" "LambdaRole_change_lb_listener_rule_priority" {
    assume_role_policy    = jsonencode(
        {
            Statement = [
                {
                    Action    = "sts:AssumeRole"
                    Effect    = "Allow"
                    Principal = {
                        Service = "lambda.amazonaws.com"
                    }
                },
            ]
            Version   = "2012-10-17"
        }
    )
    force_detach_policies = false
    managed_policy_arns   = [
        "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
        "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole",
    ]
    max_session_duration  = 3600
    name                  = "LambdaRole_change_lb_listener_rule_priority"
    path                  = "/service-role/"
    tags                  = {}
    inline_policy {
        name   = "change_lb_rule_priority_policy"
        policy = jsonencode(
            {
                Statement = [
                    {
                        Action   = [
                            "elasticloadbalancing:SetRulePriorities"
                        ]
                        Effect   = "Allow"
                        Resource = [
                            "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*/*",
                            "arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*/*"
                        ]
                    },
                ]
                Version   = "2012-10-17"
            }
        )
    }
}

EventBridgeの設定

EventBridgeの作成

続いて、EventBridgeを作成します。
特定時刻にLambda関数を自動実行するようにします。
下記では7:55と19:00に実行するように設定しています。

aws_cloudwatch_event_rule.tf
# aws_cloudwatch_event_rule.change_lb_listener_rule_priority_open_0755JST:
resource "aws_cloudwatch_event_rule" "change_lb_listener_rule_priority_open_0755JST" {
    description         = "システム稼働時間外メッセージを非表示する(LBリスナールールプライオリティ変更)"
    event_bus_name      = "default"
    is_enabled          = true
    name                = "change_lb_listener_rule_priority_open_0755JST"
    schedule_expression = "cron(55 22 ? * SUN-THU *)"
    tags                = {}
}

# aws_cloudwatch_event_rule.change_lb_listener_rule_priority_close_1900JST:
resource "aws_cloudwatch_event_rule" "change_lb_listener_rule_priority_close_1900JST" {
    description         = "システム稼働時間外メッセージを表示する(LBリスナールールプライオリティ変更)"
    event_bus_name      = "default"
    is_enabled          = true
    name                = "change_lb_listener_rule_priority_close_1900JST"
    schedule_expression = "cron(00 10 ? * MON-FRI *)"
    tags                = {}
}

targetの設定

EventBridgeにtarget設定をすることにより、対象のLambda関数を実行します。
この時にEventBridge側からはevent値としてpriorityをLambda関数に渡しています。

7:55ではpriorityの優先度を下げて時間外メッセージを非表示に、19:00ではpriorityの優先度を上げて時間外メッセージを表示するようにします。

aws_cloudwatch_event_target.tf
# aws_cloudwatch_event_target.change_lb_listener_rule_priority_open_0755JST:
resource "aws_cloudwatch_event_target" "change_lb_listener_rule_priority_open_0755JST" {
    arn            = aws_lambda_function.change_lb_listener_rule_priority.arn
    rule           = aws_cloudwatch_event_rule.change_lb_listener_rule_priority_open_0755JST.name
    event_bus_name = "default"
    input          = jsonencode({
        "detail" : {
            "priority" : [
                "1000"
            ]
        },
        "resources" : [
            aws_lb_listener_rule.change_lb_listener_rule_priority_close.arn
        ]
    })
}
# aws_cloudwatch_event_target.change_lb_listener_rule_priority_close_1900JST:
resource "aws_cloudwatch_event_target" "change_lb_listener_rule_priority_close_1900JST" {
    arn            = aws_lambda_function.change_lb_listener_rule_priority.arn
    rule           = aws_cloudwatch_event_rule.change_lb_listener_rule_priority_close_1900JST.name
    event_bus_name = "default"
    input          = jsonencode({
        "detail": {
            "priority": [
                "1"
            ]
        },
        "resources": [
            aws_lb_listener_rule.change_lb_listener_rule_priority_close.arn
        ]
    })
}

lambda permissionの設定

作成したEventBridgeがLambda関数を実行できるようlambda permissionにて実行権限を付与します。
locals.tfにてリスト化し、for_eachでまとめて実行しています。

aws_lambda_permission.tf
# aws_lambda_permission.events:
resource "aws_lambda_permission" "events" {
    for_each      = { for i in local.events_lambda_permission_list : "${i[0].name}-${i[1]}" => i }
    statement_id  = "AllowExecutionFromEventBridge-${each.value[0].name}"
    action        = "lambda:InvokeFunction"
    function_name = each.value[1]
    principal     = "events.amazonaws.com"
    source_arn    = each.value[0].arn
}
locals.tf
# locals:
locals {
  events_lambda_permission_list = [
    [aws_cloudwatch_event_rule.change_lb_listener_rule_priority_open_0755JST,  aws_lambda_function.change_lb_listener_rule_priority.function_name],
    [aws_cloudwatch_event_rule.change_lb_listener_rule_priority_close_1900JST, aws_lambda_function.change_lb_listener_rule_priority.function_name],
]
}

補足

※ディレクトリ構成やterraformリソース管理の関係で一部コードはdata.tf等による追加定義や修正が必要かもしれませんが、ご容赦ください。

7
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
7
0