terraformでPatch Manger構築

## ◆はじめに
今迄、Patch Mangerをコンソール上から作成していましたが
今回terraformを使用してコード化しました。

※今回のここに書く構成は、シンプルなもので、
メンテナンスウィンドウを使用して、定期的にパッチをあてるだけのものです。

実際の運用だと、Auto Scalingを適用していた場合、
パッチをあてた後のリブートする行為でEC2が台数が予期しないタイミングで
増えてしまったり、パッチをあてた後のAMIをとってAuto Scalingの起動テンプレート
に反映させないといけいなど考えなければならない事が多々あるのですが....

ちなみに、Auto Scalingを適用した例だと
先ず、Auto Scalingのプロセスを止める　→　パッチをあてる
→　パッチをあてた後のAMIを取得するという流れをStepFunctionを使用して
やる事が出来ます。
<br>


## ◆terraformのコード
自分の備忘録として、コード内にコメントを入れています
もし見えずらかった申し訳ありません。

また、下記のコードには、
EC2のタグ付けする為のコードやS3及びSNS作成のコードを示していません。
ご了承ください (_ _)ﾍﾟｺﾘ

今回のパッチマネージャーのステップとして、

①どのパッチベースラインを適用するか
②メンテナンスウィンドウズを作成
　⑴.メンテナンスウィンドウズ本体作成
　⑵.メンテナンスターゲット作成
　⑶.メンテナンスタスク作成
　⑷.ポリシーの作成 (必要な人だけ)

<br>


```.go
/*---------------------------------------------
 Systems Manager Patch Manager               
---------------------------------------------*/
// Patch Baseline //
/*
・AWS-AmazonLinux2DefaultPatchBaseline
 Patch Baselineは、AWS側でOSごとに事前定義されたベースラインを使用する
 ※カスタムで作成する事も出来ます。
*/

// Patch Groupを作成//
resource "aws_ssm_patch_group" "patchgroup" {
  // 事前に定義されたパッチベースラインにパッチグループを適用
  baseline_id = "arn:aws:ssm:ap-northeast-1:486716784251:patchbaseline/pb-00fda5699d1ae3942"
  patch_group = "hogehoge-pnatch-group"
}


/*---------------------------------------------
 Maintenance Windows                         
---------------------------------------------*/
// Maintenance Window
resource "aws_ssm_maintenance_window" "application" {
  description = "This is a maintenance window "
  enabled     = true
  name        = "maintenance-window-application"
  
　// ここでは、毎月2日の午前2時に適用(時間はutcなので9時間後になります)
　// cron(Minutes Hours Day-of-month Month Day-of-week Year) //
  schedule    = "cron(0 17 2 * ? *)"
  
　duration    = 3
  cutoff      = 1
  
　//下記のtimezoneを入れたい場合はコメントアウトを外す
　//schedule_timezone = "Asia/Tokyo"
  
  // Maintenance Windowに登録されていないターゲットの実行を許可するか
  allow_unassociated_targets = true
}

// Maintenance Windotw Target //
resource "aws_ssm_maintenance_window_target" "target" {
  description   = "This is a maintenance window target"
  window_id     = aws_ssm_maintenance_window.application.id
  name          = "maintenance-window-target"
 
　// タグによってパッチを適用するインスタンスを選択します。
　resource_type = "INSTANCE"

  // Target(となるEC2インスタンス)登録
　// EC2にも下記と同じタグづけをします
  targets {
    key    = "tag:Patch Group"
    values = ["hogehoge-patch-group"]
  }
}

// Maintenance Window Task //
resource "aws_ssm_maintenance_window_task" "task" {
  name            = "hogehoge-patch-task"
  description     = "This is a maintenance window task"
  max_concurrency = 1
  max_errors      = 1
  priority        = 1
  
　// Automation Documentationを指定
  task_arn  = "AWS-RunPatchBaseline"
  task_type = "RUN_COMMAND"
  window_id = aws_ssm_maintenance_window.application.id
  
  // Systems Managerがタスクを実行する際に必要となる IAMロールは自動で作成されアタッチされる為指定不要
  // service_role_arn = 
  
  // 1個前に作成したターゲットを指定
  targets {
    key    = "WindowTargetIds"
    values = [aws_ssm_maintenance_window_target.target.id]
  }

  task_invocation_parameters {
    run_command_parameters {
　　　//パッチ適用の結果をS3に保存
      output_s3_bucket     = aws_s3_bucket.patch_output.bucket
      output_s3_key_prefix = "patch-manger/output"
      service_role_arn     = aws_iam_role.patch_role.arn
      timeout_seconds      = 600

　　　// パッチ適用の結果をSNSで送信
      notification_config {
        notification_arn    = aws_sns_topic.for_cloudwatch_alarm.arn
        notification_events = ["All"]
        notification_type   = "Command"
      }

　　　//このパラメータはinstall か scanのどちらか選択
      parameter {
        name   = "Operation"
        values = ["Install"]
      }

　　　//ここでは、ノンリブートにしてますのでパッチ適用した後リブートさせたい場合は
　　　//[RebootIfNeeded]に変更
      parameter {
        name   = "RebootOption"
        values = ["NoReboot"]
      }
    }
  }
}

/*---------------------------------------------
 IAM Role for Patch Manager                  
---------------------------------------------*/
// SSMにロールを適用
resource "aws_iam_role" "patch_role" {
  name               = "hogehoge-patch-role"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ssm.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

//ポリシーにはS3とSNSに対する許可設定を入れる
//SNSの許可はactionは[sns:Publish]だけで良いんですがなんとなく下記の入れています
resource "aws_iam_role_policy" "patch_policy" {
  name   = "hogehoge-patch-policy"
  role   = aws_iam_role.patch_role.id
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
          "s3:GetBucketAcl",
          "s3:PutObject",
          "sns:*"
        ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}
EOF
}
```

<br>

## ◆ちょこっと解説

#### ◇Patch Baseline
Patch Baselinetとは、もの凄く簡単いうと、
どのようにしてパッチをあてるかを定義したものって感じです。

今回、AWS側で事前定義されている「AWS-AmazonLinux2DefaultPatchBaseline」を
使用しています。これが下記のルールでパッチあてを行います(ドキュメントに載っている
事をそのまま転記しています)

```
分類が「セキュリティ」で、重要度レベルが「非常事態」または「重要」のすべてのオペレーティングシステムパッチを承認
分類が「Bugfix」(バグ修正) のすべてのパッチを承認
→ パッチはリリースから 7日後に自動承認
```

>※上記のようなルールをカスタムルールとして作成する事も出来ます
<br>

#### ◇Patch Group
Patch Groupのリソースを作成する際に、baseline_idを指定していますが、
これはパッチマネージャーコンソール　→　パッチベースラインタブ
→　ベースラインIDをクリックし　→　下記の画像のベースラインIDの所を指定してます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689346/1bce3150-c0b3-dcab-2e55-04b64ac6280c.png)

<br>

#### ◇Maintenance Windotw Target
メンテナンスウィンドウズのターゲットはタグによって、
どのEC2をターゲットにするかを決定します。

・タグのつけ方にはちょっとしたルールがあります。

タグのkeyは必ず「Patch Group」
タグのバリューは、「作成したパッチグループ名」にします。

この例だと下記のようになりますね。
[Patch Group : hogehoge-patch-group]
>※バリューの所は確か....パッチグループ名だったような気がするのですが、
違ってたらすみません....
分かりやすくするという意味では、作成したパッチグループ名にしておくのが
安牌かとは思いますが。

>先に記載しますが、この [Patch Group : hogehoge-patch-group]を
ターゲットとするEC2にも付与します。

<br>

#### ◇Maintenance Windotw Task
メンテナンスウィンドウズのタスクとしては、
今回一番オーソドックスな[RUN_COMMAND]を使用しています。

RUN_COMMANDの中身は、自分でドキュメントを作成して入れるコマンドを作成する事も
出来ますが、今回は、これもAWS側で既に定義していあります、
[AWS-RunPatchBaseline]を使用しています。

<br>

ほんの少しの解説でした。
コード内にも大体のことは記載したつもりなのですが、それでも
意味がよく分からないという方はネット検索を( ｀・∀・´)ﾉﾖﾛｼｸﾃﾞｽ


## ◆最後に
今回は、簡単なパッチマネージャーをterraformで構築しました。
拙い説明でしたが、どこかの誰かのお役に立てれば幸いです。

今度は、Stepfuctionを絡めたものも掲載しようかと思います。