Closed12

特定タグのEC2インスタンスを特定の時間に停止する

not75743not75743

要件

  • 以下タグのついたEC2インスタンスを停止する
    • タグキー: AutoStop
    • タグバリュー: True
  • 起動はさせなくていい
  • 決まった時間に実行するようにする
  • Terraform化したい
not75743not75743

  • 🆗Lambda + EventBridge Scheduler
    • タグの指定を出来るのはLambdaだけ?
    • そのLambdaをEventBridge Schedulerで起動すればよさげ
  • ❌EventBridge Scheduler
    • タグ指定ができない?。単体では無理
  • ❌Resource Scheduler(Systems Manager)
  • ❌サーバのCronで管理
    • OSの管理したくないので却下
not75743not75743

特定のタグを持ったEC2を停止させるLambda

lambda.tf
import boto3

def lambda_handler(event, context):
    ec2 = boto3.resource('ec2')

    filters = [{
            'Name': 'tag:AutoStop',
            'Values': ['True']
        },
        {
            'Name': 'instance-state-name', 
            'Values': ['running']
        }
    ]

    instances = ec2.instances.filter(Filters=filters)

    RunningInstances = [instance.id for instance in instances]

    if len(RunningInstances) > 0:
        # perform the shutdown
        stoppingInstances = ec2.instances.filter(InstanceIds=RunningInstances).stop()
        print("Stopped instances: " + str(RunningInstances))
    else:
        print("No instances to stop")
  • タグキーが「AutoStop」、タグバリューが「True」であり、状態が「running」であるEC2のインスタンスIDを取得
    • filterの内容はここを見れば良さげ
  • そのインスタンスIDのインスタンスをstopメソッドで停止させる

filterのサンプル
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/migrationec2.html#checking-what-instances-are-running

stopメソッド
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2/instance/stop.html

not75743not75743

ポイント① タイムアウトの設定

Lambdaのタイムアウトはデフォルトで3秒です。
このようなタイムアウトのログが出る場合は設定しましょう。

{"errorMessage":"2023-07-22T11:53:57.896Z 86702c62-5722-4403-9038-c8f433cc13d2 Task timed out after 3.02 seconds"}~

本環境では10秒に設定しました。

resource "aws_lambda_function" "example_lambda" {
  // 略
  timeout          = 10
}

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-function-common.html#configuration-timeout-console

not75743not75743

ポイント② EC2停止用のIAMポリシ適用

resource "aws_iam_policy" "ec2_stop_policy" {
  name        = "ec2_stop_policy"
  path        = "/"
  description = "IAM policy for stopping EC2 instances"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeInstances",
        "ec2:StopInstances"
      ],
      "Resource": "*"
    }
  ]
}
EOF
}
not75743not75743

EventBridge Scheduler設定

IAMポリシー

Lambda関数をInvoke出来るポリシーを作成し、Schedulerに使ってもらいます

resource "aws_iam_role" "schedule_role" {
  name = "schedule-role"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "scheduler.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}


resource "aws_iam_role_policy_attachment" "schedule_policy_attachment" {
  role       = aws_iam_role.schedule_role.name
  policy_arn = aws_iam_policy.schedule_policy.arn
}

resource "aws_iam_policy" "schedule_policy" {
  name        = "schedule_policy"
  path        = "/"
  description = "IAM policy for invoking Lambda Function"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "lambda:InvokeFunction"
      ],
      "Resource": "${aws_lambda_function.example_lambda.arn}"
    }
  ]
}
EOF
}
not75743not75743

スケジュール

resource "aws_scheduler_schedule" "cron" {
  name        = "ec2-stop-schedule"

  flexible_time_window {
    mode = "OFF"
  }

  target {
    arn      = aws_lambda_function.example_lambda.arn
    role_arn = aws_iam_role.schedule_role.arn
  }

  schedule_expression = "cron(50 23 ? * * *)"
  schedule_expression_timezone = "Asia/Tokyo"
}
  • 23:50分にLambda関数を起動する
  • 日本タイムゾーンを指定
  • フレックスタイムウインドウはoff

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule

not75743not75743

ポイント③ aws_lambda_permissionが不要

てっきり必要なものだと思っていたのですが、使ってところなくても動作するようです。
理由はわからないので後日調べてみます。

resource "aws_lambda_permission" "allow_eventbridge-scheduler" {
  statement_id  = "AllowExecutionFromCloudWatch"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.example_lambda.function_name
  principal     = "scheduler.amazonaws.com"
}
not75743not75743

ポイント④ ログ確認

Lambda関数をSchedulerがInvokeした記録を見れればいいのだが、コンソールからは見つけられなかった。
次回の課題

とりあえずLambda関数のロググループから起動時間を確認し、スケジューラの設定時間+20秒前後で実行されていればよし

このスクラップは2023/07/23にクローズされました