zaki work log

作業ログやら生活ログやらなんやら

[Terraform / AWS] EventBridge Schedulerを使ったVMの自動起動と停止

EventBridge Schedulerを使ったVMの自動on/off設定をCloudFormationを使って設定するには以下のClassmethodさんの記事を見れば一通り実装できます。

dev.classmethod.jp

ここではTerraformを使った実装についてのまとめ。
こうしたらうまくいった、という感じの内容なので、コードの解説はほぼ無いです。

※ 実際に使ってるコードをコピペして変数をblog用に更新してるので、辻褄が合ってない箇所がもしあったらゴメンナサイ

EventBridgeスケジューラ用IAMロールの作成

CloudFormationだとこんな感じ(YAML)

  SchedulerEC2StopStartRole:
    # Start/Stop by EventBridge用IAMロール
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - scheduler.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: "/"
      Policies:
        - PolicyName: EC2StopStart
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - ec2:StartInstances
                  - ec2:StopInstances
                Resource:
                  - "*"

これをTerraformで書くとこんな感じ。
使うのはaws_iam_roleリソース。

registry.terraform.io

resource "aws_iam_role" "stopstart_role" {
  name = "ec2-stop-start-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "scheduler.amazonaws.com"
        }
      },
    ]
  })

  path = "/"

  inline_policy {
    name = "start_stop_policy"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Action   = ["ec2:StartInstances", "ec2:StopInstances"]
          Effect   = "Allow"
          Resource = "*"
        },
      ]
    })
  }
}

nameはドキュメントにはOptionalと書かれてて実際無くてもTerraform的にデプロイはできるんだけど、リソースが作られなかったので実質必須。

自動停止のスケジューラ設定

EC2を自動停止するスケジューラ設定。
CloudFormationだとこんな感じ。(YAML)
InstanceIdsに対象EC2を指定。
毎日17時25分に停止するには以下の通り。

  ScheduleEC2Stop:
    # EC2の停止EventBridge
    Type: AWS::Scheduler::Schedule
    Properties:
      Name: ec2-stop
      Description: Stop EC2 Instance
      ScheduleExpression: cron(25 17 * * ? *)
      ScheduleExpressionTimezone: Japan
      FlexibleTimeWindow:
        Mode: "OFF"
      State: ENABLED
      Target:
        Arn: arn:aws:scheduler:::aws-sdk:ec2:stopInstances
        Input: !Sub |-
          {
            "InstanceIds": ["${Server1}", "${Server2}", "${Server3}"]
          }
        RoleArn:
          Fn::GetAtt:
          - SchedulerEC2StopStartRole
          - Arn

これをTerraformで書くとこんな感じ。
使うのはaws_scheduler_scheduleリソース。

registry.terraform.io

resource "aws_scheduler_schedule" "schedule_stop" {
  name       = "ec2-stop"
  group_name = "default"

  flexible_time_window {
    mode = "OFF"
  }

  schedule_expression = "cron(25 17 * * ? *)"
  schedule_expression_timezone = "Japan"
  target {
    arn = "arn:aws:scheduler:::aws-sdk:ec2:stopInstances"
    role_arn = aws_iam_role.stopstart_role.arn
    input = jsonencode({
      InstanceIds = ["${aws_instance.server1.id}",
                     "${aws_instance.server2.id}",
                     "${aws_instance.server3.id}"]
    })
  }
}

自動起動のスケジューラ設定

最後にEC2を自動起動するスケジューラ設定。
CloudFormationだと(ry
毎朝8時45分に起動する設定。

  ScheduleEC2Start:
    # EC2の開始EventBridge
    Type: AWS::Scheduler::Schedule
    Properties:
      Name: ec2-start
      Description: Start EC2 Instance
      ScheduleExpression: cron(45 8 * * ? *)
      ScheduleExpressionTimezone: Japan
      FlexibleTimeWindow:
        Mode: "OFF"
      State: ENABLED
      Target:
        Arn: arn:aws:scheduler:::aws-sdk:ec2:startInstances
        Input: !Sub |-
          {
            "InstanceIds": ["${Server1}", "${Server2}", "${Server3}"]
          }
        RoleArn:
          Fn::GetAtt:
          - SchedulerEC2StopStartRole
          - Arn

これをTerraformで書くとこんな感じ。
使うのは停止と同様にaws_scheduler_scheduleリソース。

resource "aws_scheduler_schedule" "schedule_start" {
  name       = "ec2-start"
  group_name = "default"

  flexible_time_window {
    mode = "OFF"
  }

  schedule_expression = "cron(45 8 * * ? *)"
  schedule_expression_timezone = "Japan"
  target {
    arn = "arn:aws:scheduler:::aws-sdk:ec2:startInstances"
    role_arn = aws_iam_role.stopstart_role.arn
    input = jsonencode({
      InstanceIds = ["${aws_instance.server1.id}",
                     "${aws_instance.server2.id}",
                     "${aws_instance.server3.id}"]
    })
  }
}

(おまけ)cron式で「平日のみ」にする場合

業務で使う場合だと、毎日起動でなく平日だけ起動にしたい場合がほとんどだと思う。
その場合はこのように書けば「月曜から金曜」になる。

cron(45 8 ? * MON-FRI *)

docs.aws.amazon.com

www.tegos.co.jp