CloudWatch用のKey Management Service (KMS)をTerraformで構築

# 前書き
CloudWatch Logsの暗号化に使うKMSキーをTerraformで構築する手順を、備忘録も兼ねてまとめました。KMSの基本設定に加え、CloudWatchと連携するためのポリシー設定や注意点も整理しています。


# CloudWatch用の概要
AWS 上のリソースやアプリケーションの監視・可視化を行うフルマネージド型サービスである。メトリクス（CPU使用率、メモリ、ディスクI/Oなど）やログ、イベントデータを収集・分析し、しきい値に基づくアラーム通知や自動アクションを設定できる。インフラの状態やパフォーマンスをリアルタイムで把握し、障害検知や運用の自動化に貢献する。ダッシュボードでの視覚化や、Lambda、SNSなど他サービスとの連携も可能であり、AWS 環境の運用には不可欠な監視基盤である。

# Key Management Service (KMS)概要
データの暗号化と鍵管理を安全かつスケーラブルに行うフルマネージド型サービスである。対称鍵および非対称鍵の生成・保管・ローテーションを統一的に管理でき、AWSサービスや独自アプリケーションからAPI経由で利用可能。KMSキーはCloudTrailでの操作履歴記録に対応し、監査性も高い。IAMやポリシーを通じた厳格なアクセス制御が可能で、S3、EBS、RDSなど主要なAWSサービスとの連携によって、透過的なデータ保護を実現する。セキュリティ要件に応じた鍵管理を支える中核的な機能である。


# ディレクトリー構成
```sh
.
├── terraform/
│   └── # terraformに必要なファイル、ディレクトリが自動生成される
├── .terraform.lock.hcl　# 自動生成
├── providers.tf
├── aws_authentication.tf
├── variables.tf
├── terraform.tfvars
├── .gitignore
├── README.md
└── modules/
    ├── key_management_service/
    │   ├── kms.tf
    │   ├── output.tf
    │   └── variables.tf
    └── cloudwatch/
        ├── cloudwatch_logs.tf
        └── variables.tf
```


# 構築手順
## 初期設定

### .gitignore
:::note alert
注意
- AWSの認証情報がある為、`Git`で管理する場合は必ず`.gitignore`を作成し、下記の設定を記述する
:::
```.gitignore
**/.terraform/*
.terraform.lock.hcl

# .tfstate files
*.tfstate
*.tfstate.backup
*.tfstate.*

# .tfstate files
*.tfvars
*.tfvars.*

crash.log

override.tf
override.tf.json
*_override.tf
*_override.tf.json
```

### providers
- `Terraform`で使用するプロバイダ(provider)の設定を記述
```providers.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.95.0"
    }
  }
}
```
**プロバイダ(provider)**
- `Terraform`が特定のインフラやサービスを作成、管理する為、`API`を提供するプラグイン

### AWSの認証情報の設定

```aws_authentication.tf
provider "aws" {
  region  = "${var.region}"
  profile = "${var.profile}"
  access_key = "${var.aws_access_key}"
  secret_key = "${var.aws_secret_key}"
}
```

```variables.tf
# 変数の設定
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "region"{}
variable "profile"{}
```

```terraform.tfvars
aws_access_key="********************" // 
aws_secret_key="****************************************" //
region="**************" // 
profile="*********" // 設定していれば記述する
```

### variables.tf
```variables.tf
# アカウント情報の取得
data "aws_caller_identity" "current" {}
locals {
  aws_account_id = data.aws_caller_identity.current.account_id
}


locals {
  # 東京リージョン
  tokyo_region = "ap-northeast-1"
}
```

### main.tf

```main.tf
# KMS
module "key_management_service" {
  source = "./modules/key_management_service"

  aws_account_id = local.aws_account_id
  tokyo_region   = local.tokyo_region
}

# cloudwatch
module "cloudwatch" {
  source = "./modules/cloudwatch"

  # KMS
  aws_kms_key_ecs_cloudwatch_log_arn = module.kms.cloudwatch_log_kms_key_arn
  retention_in_days = 30 # ログの保管日数
}
```


## KMSの構築

```./modules/key_management_service/kms.tf
# CloudWatch Logs用の暗号化キー（KMSキー本体）を作成する
resource "aws_kms_key" "cloudwatch_log_kms_key" {
  
  description = "key for application's cloudwatch_log"
  # キー削除リクエスト後に完全削除されるまでの猶予日数
  deletion_window_in_days = 10
  # 年1回の自動キーのローテーションを有効化する
  enable_key_rotation     = true

  # ポリシーの設定
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        "Sid" : "Enable IAM User Permissions",
        "Effect" : "Allow",
        "Principal" : {
          "AWS" : "arn:aws:iam::${var.aws_account_id}:root"
        },
        "Action" : "kms:*",
        "Resource" : "*"
      },
      {
        "Effect" : "Allow",
        "Principal" : {
          "Service" : "logs.${var.tokyo_region}.amazonaws.com"
        },
        "Action" : [
          "kms:Encrypt*",
          "kms:Decrypt*",
          "kms:ReEncrypt*",
          "kms:GenerateDataKey*",
          "kms:Describe*"
        ],
        "Resource" : "*",
        "Condition" : {
          "ArnLike" : {
            "kms:EncryptionContext:aws:logs:arn" : "arn:aws:logs:${var.tokyo_region}:${var.aws_account_id}:*"
          }
        }
      }
    ]
  })
}

# KMSキーにわかりやすいエイリアス名を付けて参照しやすくする
resource "aws_kms_alias" "cloudwatch_log_kms_key_alias" {
  name          = # 任意のエイリアス名
  target_key_id = aws_kms_key.cloudwatch_log_kms_key.key_id # 適用するKMSを指定する
}
```

```./modules/key_management_service/variables.tf
# 共通
variable "aws_account_id" { type = number }
variable "tokyo_region" { type = string }
```

```./modules/key_management_service/output.tf
output "cloudwatch_log_kms_key_arn" {
  value = aws_kms_key.cloudwatch_log_kms_key.arn
}
```


## CloudWatchの構築
```./modules/cloudwatch/cloudwatch_logs.tf
resource "aws_cloudwatch_log_group" "cloudwatch_log" {
  name              = # 任意の名前
  retention_in_days = var.retention_in_days # ログの保持日数
  kms_key_id        = var.kms_cloudwatch_log_arn # KMSのarnを設定
}
```

```./modules/cloudwatch/variables.tf
variable "retention_in_days" { type = number }
variable "kms_cloudwatch_log_arn" { type = string }
```

# 参考記事

- [[AWS 公式] Encrypt log data in CloudWatch Logs using AWS Key Management Service](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html)
- [[AWS 公式] AWS Key Management Service](https://docs.aws.amazon.com/ja_jp/kms/latest/developerguide/overview.html)
- [[AWS 公式] What is Amazon CloudWatch?](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html)
- [[hashicorp 公式] Resource: aws_cloudwatch_log_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group)
- [[hashicorp 公式] Resource: aws_kms_alias](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias)
- [[hashicorp 公式] Resource: aws_kms_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key)


# 感想
今回の構築を通して、改めてCloudWatchとKMSの知識が学べました。AWSで学ぶべき事は無限にあるので、1つずつ確実に学んでいきたいと思いました。