メインコンテンツにスキップ

Terraformで実現するWindows EC2のWindows Update自動化

タグ: 🏷 AWS ,EC2 ,SSM ,Patch Manager ,Terraform

はじめに

Windows Serverのセキュリティパッチ適用は、サーバー運用における必須のタスクです。しかし、このプロセスを手動で行うのは非効率的であり、更新漏れのリスクも伴います。

AWS Systems Manager (SSM) の Patch Manager を利用すれば、このプロセスを効率的に自動化できます。

実際、運用しているWindowsサーバがあります。検証環境で20分、本番環境で20分を費やしているので、不毛だなーと思ったりしています。

Terraformを使用してSSM Patch Managerの各コンポーネントをコードで定義し、Windows EC2インスタンスの更新プログラム適用を完全に自動化する手順を具体的に解説します。

前提条件

  • EC2インスタンスには、SSM Agentがインストールされ、正常に動作していること。(公式Windows AMI にはプリインストール済みです)
  • EC2インスタンスに AmazonSSMManagedInstanceCore ポリシーを含むIAMインスタンスプロファイルがアタッチされていること。

TerraformによるPatch Managerの構成

Patch Managerの自動化は、以下の3つのリソーを組み合わせて実現します。

  • パッチベースライン
  • パッチグループ
  • メンテナンスウィンドウ

1. パッチベースラインの定義

まず、どのパッチを承認するかを定義する「パッチベースライン」を作成します。ここでは、Windows Server 2025向けに、重要度が「Critical」と「Important」の更新プログラムをリリース7日後に自動承認するルールを定義します。

# windows_patch_baseline.tf

resource "aws_ssm_patch_baseline" "windows_server_baseline" {
  name             = "WindowsServer2025-Critical-And-Important-Updates"
  operating_system = "WINDOWS"
  description      = "Baseline for Windows Server 2025, approves critical and important updates 7 days after release."

  approval_rule {
    approve_after_days = 7
    compliance_level   = "CRITICAL"

    patch_filter {
      key    = "PRODUCT"
      values = ["WindowsServer2025"]
    }

    patch_filter {
      key    = "CLASSIFICATION"
      values = ["CriticalUpdates", "SecurityUpdates"]
    }

    patch_filter {
      key    = "MSRC_SEVERITY"
      values = ["Critical", "Important"]
    }
  }

  tags = {
    ManagedBy = "Terraform"
  }
}

2. パッチグループの作成とEC2への関連付け

次に、作成したパッチベースラインを特定のEC2インスタンス群に適用するための「パッチグループ」を作成します。パッチグループは、EC2インスタンスに特定のタグ (PatchGroup) を付けることで機能します。

まず、パッチグループとベースラインを関連付けます。

# windows_patch_group.tf

resource "aws_ssm_patch_group" "windows_production_group" {
  baseline_id = aws_ssm_patch_baseline.windows_server_baseline.id
  patch_group = "Windows-Production"
}

そして、対象のEC2インスタンスにこのパッチグループ名のタグを付けます。

# ec2.tf (抜粋)

resource "aws_instance" "win_server" {
  # ... (ami, instance_type, iam_instance_profileなど)

  tags = {
    Name       = "Windows-Production-Server"
    PatchGroup = "Windows-Production" # このタグでパッチグループに関連付ける
  }
}

3. メンテナンスウィンドウの作成

パッチ適用を実行する時間帯を「メンテナンスウィンドウ」でスケジュールします。ここでは、毎週日曜日のAM 2:00 (UTC) から3時間、パッチ適用ウィンドウを設けます。

# maintenance_window.tf

resource "aws_ssm_maintenance_window" "weekly_patching" {
  name     = "Weekly-Patching-Window-For-Windows"
  schedule = "cron(0 2 ? * SUN *)" # 毎週日曜 AM 2:00 (UTC)
  duration = 3
  cutoff   = 1

  tags = {
    ManagedBy = "Terraform"
  }
}

4. メンテナンスウィンドウのターゲットとタスクを登録

最後に、メンテナンスウィンドウで「どのインスタンスに」「何を実行するか」を定義します。

まず、先ほど定義したパッチグループをターゲットとして登録します。

# maintenance_window_target.tf

resource "aws_ssm_maintenance_window_target" "patch_target" {
  window_id    = aws_ssm_maintenance_window.weekly_patching.id
  name         = "Windows-Production-Servers"
  resource_type = "INSTANCE"

  targets {
    key    = "tag:PatchGroup"
    values = [aws_ssm_patch_group.windows_production_group.patch_group]
  }
}

次に、AWS-RunPatchBaseline ドキュメントを実行するタスクを登録します。これにより、パッチベースラインに基づいたスキャンとインストールが実行されます。

# maintenance_window_task.tf

resource "aws_ssm_maintenance_window_task" "patch_task" {
  window_id      = aws_ssm_maintenance_window.weekly_patching.id
  target_arn     = aws_ssm_maintenance_window_target.patch_target.id
  task_type      = "RUN_COMMAND"
  task_arn       = "AWS-RunPatchBaseline"
  priority       = 1
  service_role_arn = aws_iam_role.ssm_maintenance_window_role.arn # 事前に作成したIAMロール

  task_invocation_parameters {
    run_command_parameters {
      parameter {
        name   = "Operation"
        values = ["Install"]
      }
    }
  }
}

# メンテナンスウィンドウがタスクを実行するためのIAMロールが別途必要です
resource "aws_iam_role" "ssm_maintenance_window_role" {
  name = "SSMMaintenanceWindowRole"
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = "sts:AssumeRole",
        Effect = "Allow",
        Principal = {
          Service = "ssm.amazonaws.com"
        }
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "ssm_maintenance_window_policy" {
  role       = aws_iam_role.ssm_maintenance_window_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonSSMMaintenanceWindowRole"
}

上記のようにコードで管理することで、以下のような大きなメリットがもたらされます。

  • 再現性の確保: 同じ構成を複数の環境に迅速かつ正確に展開できます。
  • 構成の可視化: インフラの構成がコードで明確に定義されるため、レビューや管理が容易になります。
  • 変更管理の簡素化: パッチ適用のルールやスケジュールの変更も、コードを修正して適用するだけで完了します。