マルチアカウント環境でのCloudTrailの一括設定

## 概要
AWSを利用するにあたって操作情報を記録するCloudTrailは必須と言っても良い機能です。
そのため、複数のAWSアカウントを利用するようなマルチアカウント環境では、複数のAWSアカウントのCloudTrailを一括で設定したい・ログ集約に特化したアカウントに全アカウントのCloudTrailログを集約したい、という要件はよくあると思われます。

こういった要件に対して、AWSではOrganizations・Control Towerの機能を活用することでほぼ自動で設定を行うことが出来ます。
一方、要件によってはこの設定がマッチしない場合もあります。
例えば以下のようなシチュエーションです。
・同一Organizationsに開発・本番など複数の環境が存在しており、環境毎にログ出力先を変更したい
・環境毎にCloudTrailのログ出力設定を変更したい

そこで、本記事ではOrganizations・Control Towerを利用するパターンと比較しながら、Control Towerを利用せずに複数アカウントへCloudTrailを実装するパターンを紹介します。


## Organizations・Control Towerを利用するパターン
Organiations・Control Towerを利用する場合はLanding Zoneの設定でCloudTrailの設定を有効にすることで設定可能です。無効にすることも可能ですが、有効にすることを強く推奨されています。
CloudTrailのログは以下のようにログアーカイブアカウントのS3バケットに保管されます。
![control_tower_setting_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662709/db206b2d-d129-27e5-ee99-f6803e06bcb7.png)
取得するイベントの設定は以下の通りです。(Control Tower v3.1時点)
管理イベントは取得されますが、データイベント・Insightsイベントは取得されません。
![control_tower_setting_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662709/d9058b83-dedb-a9e2-7a3d-c25f3c7ccf3c.png)

詳細な仕様については以下のAWS公式ドキュメントをご確認ください。

https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/creating-trail-organization.html

https://docs.aws.amazon.com/ja_jp/controltower/latest/userguide/cloudtrail.html


## Organizations・Control Towerを利用しないパターン
Organizations・Control Towerを利用しない場合、Control Towerで自動実装する設定を自前で設定する必要があります。
具体的には以下の設定が必要です。
| # | 設定内容 | 利用サービス |
| --- | --- | --- |
| ① | アカウントが作成(OUに追加)されたときに自動でCloudTrailを有効化する | CloudFormation Stacksets |
| ② | 取得したいイベントの情報を収集するようCloudTrailを設定する | CloudFormation |
| ③ | 特定のS3バケットにログを集約する | CloudFormation, S3 |
| ④ | メンバーアカウントからの証跡設定の変更を禁止する | SCP |
| ⑤ |(要件に応じて)独自のCMKでログを暗号化する | CloudFormation, KMS |

### ①アカウントが作成(OUに追加)されたときに自動でCloudTrailを有効化する
この設定はCloudFormationのStackSetsの機能の一つである自動デプロイを利用することで実現できます。
自動デプロイを有効にするとOUにアカウントが追加されたときに自動でCloudFormationのスタックが作成され、リソースがデプロイされます。逆にOUからアカウントが離脱した際は自動でスタックが削除されます。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/stacksets-getting-started-create.html#stacksets-orgs-associate-stackset-with-org

StackSetsはOU単位で適用出来ますので、StackSetsで利用するCloudFormation Templateを変更することで以下の②,③の設定をOU単位で別の設定にすることが出来ます。

### ②取得したいイベントの情報を収集するようCloudTrailを設定する
これはStackSetsで利用するCloudFormation Templateに設定内容を記載することで実現します。
例えば、Control Towerのデフォルト値に加えてインサイトイベントも取得したい場合は以下のようになります。
```
Resources:
  EnableCloudTrail:
    Type: AWS::CloudTrail::Trail
    Properties:
      IsLogging: True
      S3BucketName: !Ref DestinationS3BucketName
      KMSKeyId: !Ref EncryptionKeyArn
      TrailName: !Ref TrailName
      EnableLogFileValidation: True
      IncludeGlobalServiceEvents: True
      IsMultiRegionTrail: True
      InsightSelectors:
       - InsightType: ApiCallRateInsight
       - InsightType: ApiErrorRateInsight
      EventSelectors:
       - IncludeManagementEvents: true
         ReadWriteType: All 
      Tags:
        - Key: "Key"
          Value: "Value"
```
S3のデータイベントを取得したい場合は以下の通りです。
```
Resources:  
  EnableCloudTrail:
    Type: AWS::CloudTrail::Trail
    Properties:
      IsLogging: True
      S3BucketName: !Ref DestinationS3Bucket
      KMSKeyId: !Ref EncryptionKeyArn
      TrailName: !Ref TrailName
      EnableLogFileValidation: True
      IncludeGlobalServiceEvents: True
      IsMultiRegionTrail: True
      EventSelectors:
      - DataResources:
          - Type: AWS::S3::Object
            Values:
             - arn:aws:s3
        IncludeManagementEvents: false
        ReadWriteType: All 
      Tags:
        - Key: "Key"
          Value: "Value"  
```

### ③特定のS3バケットにログを集約する
②と同様にCloudFormation Templateに集約先バケットを記載します。
環境毎にログアーカイブアカウントを持つような構成の場合は、この設定により環境毎に集約先を変更することで実現出来ます。

### ④メンバーアカウントからの証跡設定の変更を禁止する
メンバーアカウントからの変更を禁止するにはSCPを利用します。
CloudFormationの設定にタグ付けを含めておくことで、タグベースでの制限が利用出来ます。
以下のポリシーでは、`Key:Value`のタグが付与されたCloudTrail証跡に対して、削除・変更・一時停止などの操作を禁止しています。

```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyUpdateAndDeleteCloutTrail",
      "Effect": "Deny",
      "Condition": {
        "ArnNotLike": {
          "aws:PrincipalARN": [
            "arn:aws:iam::*:role/stacksets-exec-*",
            "arn:aws:iam::*:role/admin-role"
        ]},
        "StringEquals": { "aws:ResourceTag/Key": "Value"}
        },
      "Action": [
        "cloudtrail:DeleteTrail",
        "cloudtrail:PutEventSelectors",
        "cloudtrail:StopLogging",
        "cloudtrail:UpdateTrail",
        "cloudtrail:RemoveTags",
        "cloudtrail:ListTags"        
      ],
      "Resource":"*"
    }
  ]
}
```

### ⑤(要件に応じて)独自のCMKでログを暗号化する
組織のポリシー等によりCMKによるログの暗号化を行いたい場合は、③のS3と同様にCloudFormation Templateで暗号化に利用するKeyを指定することで実現出来ます。
このkeyはメンバーアカウントから利用出来る必要がありますので、アカウント数が増大する可能性を想定してキーポリシーを設定しておくことで運用負荷を軽減することが出来ます。
例えば、以下のポリシーでは証跡名が`xxxx-*`"もしくは`yyyy-*`からの利用のみアクションを許可するよう設定し、この規則に従ってメンバーアカウントの証跡を作成することで、アカウントが新規追加された際もキーポリシーを更新せずkeyを利用出来るようにしています。
```
{
    "Version": "2012-10-17",
    "Id": "key-consolepolicy-3",
    "Statement": [
        {
            "Sid": "Allow CloudTrail Logs to use the key",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudtrail.amazonaws.com"
            },
            "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:DescribeKey"
            ],
            "Resource": "*",
            "Condition": {
                "StringLike": {
                    "AWS:SourceArn": [
                        "arn:aws:cloudtrail:ap-northeast-1:*:trail/xxxx-*",
                        "arn:aws:cloudtrail:ap-northeast-1:*:trail/yyyy-*",
                    ]
                }
            }
        }
```
本来は`Principal.Service:cloudtrail.amazonaws.com`＋OrganizationのID(or OUID)による制限が出来るとベストなのですが、残念ながらサービスプリンシパルには組織情報が含まれていないためこの制限は出来ません。

## まとめ
本記事ではOrganizations・Control Towerを利用せずにマルチアカウント環境に対してCloudTrailの一括設定を行う方法を紹介しました。
要件上問題がない場合はOrganizations・Control Towerを利用するパターンが圧倒的に楽ですが、環境制約上Control Towerを利用出来ない場合の実装方式として本記事が参考になれば幸いです。