あなたはAWS Configで損をしている！？ ～ AWS提供サンプルテンプレートの罠 ～

# はじめに

結論から言います。

:::note warn
**AWSが提供している CloudFormation StackSets サンプルテンプレートを用いて、AWS Config を有効化している場合、余分なコストが発生している可能性があります。**
:::

## 対象読者

* AWSが提供している CloudFormation StackSets サンプルテンプレートを用いて、 AWS Config を有効化している方（または、これから有効化しようと考えている方）
* AWS Config をすべてのリージョンで有効化していない方
* AWS Config のベストプラクティスに興味のある方


# CloudFormation StackSets とは

複数のアカウントとリージョンに対して、同じ CloudFormation スタックを作成する機能です。

マルチアカウントの印象が強いかもしれませんが、対象アカウントを一つにすることにより、同一アカウント内の複数リージョンのみを対象とすることも可能です。

本記事では、同一アカウントの複数リージョンを対象とします。
また、CloudFormation StackSets では長いので、以降は **StackSets** と略します。

<img width="600" alt="image.png" src="https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/images/stack_set_conceptual_sv.png">

引用：https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/what-is-cfnstacksets.html


## StackSets の使いどころ

StackSets は複数リージョンに対して、同じリソースを作成したい場合に効果を発揮します。

例えば、**AWS アカウントの初期設定** です。
本記事のテーマである AWS Config だけでなく、 CloudTrail, GuardDuty, Security Hub などのサービスは、すべてのリージョンで有効化することが推奨されています。

しかし、これらのサービスをマネジメントコンソールからすべてのリージョンで有効化するのは大変です。
このような場合に StackSets を使うと、すべてのリージョンで一括設定することが可能です。


# AWSが提供しているサンプルテンプレートの問題点

本題に入りましょう。

AWS は、いくつかの StackSets サンプルテンプレートを提供しています。
この中には **AWS Config を有効にする** テンプレートも含まれています。

<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2443303/162d1057-209e-dcaa-095e-15aeb1c9eb30.png">

引用：https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/stacksets-sampletemplates.html


なお、マネジメントコンソールから StackSet を作成する際にもサンプルテンプレートを選ぶことができます。

CloudFormation > StackSets > StackSet の作成

<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2443303/512cbaf3-cf44-977f-ee58-1e0906657f15.png">


## なにが問題なのか？

問題は、このサンプルテンプレートをそのまま使うと、**すべてのリージョンでグローバルリソースの記録が有効になってしまうこと** です。

グローバルリソースとは IAM などのグローバルサービスのリソースのことです。
AWS Config では、各リージョンの記録にグローバルリソースを含めるか否かを選択することができます。

すべてのリージョンでグローバルリソースの記録が有効にした場合、以下の問題が発生します。


### AWS Config の料金が増える

AWS Config はルールの評価件数に応じて課金されるため、各リージョンでグローバルリソースを重複して記録している場合、余分な料金が発生します。

https://aws.amazon.com/jp/config/pricing/


### グローバルリソースに関する通知が重複する

ルールの評価結果に対して通知を設定している場合、各リージョンでグローバルリソースに関するチェックが実行されるため、通知も重複します。

通知の重複については、以下のブログで検証されています。

https://dev.classmethod.jp/articles/aws-config-rules-multi-region-check/


## AWS Config のベストプラクティス

ここで AWS Config のベストプラクティスを見てみましょう。

https://aws.amazon.com/jp/blogs/news/aws-config-best-practices/

実は、グローバルリソースの記録は1つのリージョンのみで有効としたほうがよい、という話は AWS Config のベストプラクティスにも書かれています。

> **3.1 つのリージョンでのみグローバルリソース (IAM リソースなど) を記録します。**
> これにより、IAM 設定アイテムの冗長コピーをすべてのリージョンで取得することがなくなります。それは費用の節約にもなります。

引用：https://aws.amazon.com/jp/blogs/news/aws-config-best-practices/

しかし、残念ながら提供されているサンプルテンプレートは、このベストプラクティスに準拠していません。


# 改善しよう

問題点ばかり指摘しても仕方がないので、サンプルテンプレートを改善してみましょう。


## サンプルテンプレートの分析

グローバルリソースを記録するか否かは [AWS::Config::ConfigurationRecorder](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-config-configurationrecorder.html) リソースの [**IncludeGlobalResourceTypes**](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-config-configurationrecorder-recordinggroup.html#cfn-config-configurationrecorder-recordinggroup-includeglobalresourcetypes) プロパティで指定します。
サンプルテンプレートでは、このプロパティの値をパラメータで指定する形になっています。

StackSets は、指定したリージョンに対して、同じテンプレートからスタックを作成するため、パラメータで指定した値（True または False）がすべてのリージョンに反映されます。
これが原因です。

```yaml:EnableAWSConfig.yml（オリジナル抜粋）
Parameters:
  IncludeGlobalResourceTypes:
    Type: String
    Default: False
    Description: Indicates whether AWS Config records all supported global resource types.
    AllowedValues:
      - True
      - False

（中略）

Resources:
  ConfigRecorder:
    Type: AWS::Config::ConfigurationRecorder
    DependsOn:
      - ConfigBucketPolicy
    Properties:
      RoleARN: !GetAtt ConfigRecorderRole.Arn
      RecordingGroup:
        AllSupported: !Ref AllSupported
        IncludeGlobalResourceTypes: !Ref IncludeGlobalResourceTypes
        ResourceTypes: !If
          - IsAllSupported
          - !Ref AWS::NoValue
          - !Ref ResourceTypes
```


## 改善版テンプレート

では、どのように改善したらよいか？

答えは、**特定のリージョンの場合に限り IncludeGlobalResourceTypes を True にする** です。

具体的な方法を解説していきます。

1. グローバルリソースの記録を有効にするリージョンをパラメータとして追加します。
  Parameter: **HomeRegion** 

2. 疑似パラメータの AWS::Region を用いて、スタックを作成するリージョンが HomeRegion か否かを判定する Condition を作成します。
  Condition: **IsHomeRegion**

3. 条件式を用いて、IsHomeRegion が真 ＝ **スタックを作成するリージョンが HomeRegion と一致した場合に限り、IncludeGlobalResourceTypes の値を True に設定します。**

4. 元々あった IncludeGlobalResourceTypes パラメータは不要なので、削除します。

```yaml:EnableAWSConfig.yml（改善版抜粋）
Parameters:
  HomeRegion:
    Type: String

（中略）

Conditions:
  IsHomeRegion: !Equals [!Ref AWS::Region, !Ref HomeRegion]

（中略）

Resources:
  ConfigRecorder:
    Type: AWS::Config::ConfigurationRecorder
    DependsOn:
      - ConfigBucketPolicy
    Properties:
      RoleARN: !GetAtt ConfigRecorderRole.Arn
      RecordingGroup:
        AllSupported: !Ref AllSupported
        IncludeGlobalResourceTypes: !If
          - IsHomeRegion
          - True
          - False
        ResourceTypes: !If
          - IsAllSupported
          - !Ref AWS::NoValue
          - !Ref ResourceTypes
```

これで HomeRegion パラメータで指定したリージョンに限り、グローバルリソースを記録することができました。


# 【要検証】2022年2月以降はグローバルリソースの扱いが変わっている！？

ここまでサンプルテンプレートの問題点と改善方法を解説してきましたが、最後に重要なお知らせがあります。

なんと、公式ドキュメントに以下の記載がありました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2443303/21730e0c-7189-6bdf-df3d-a34ec75cfe2d.png)

引用：https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/select-resources.html

> 2022 年 2 月以前にオンボードされたものは変更されず、今後も AWS Config で有効化されているすべてのリージョン内の設定項目を配信します。この変更は、2022 年 2 月以降にオンボードされた新しいグローバルリソースタイプのみに影響します。

この記載によると、2022 年 2 月以降はグローバルリソースの扱いが変わっているそうです。

**ただし、私が 2022/03/21 時点で動作を確認した限りでは、従来動作からの差異は確認できませんでした。**

**■確認手順**
1. 既存の AWS Config 関連リソース（AWS::Config::ConfigurationRecorder および AWS::Config::DeliveryChannel）を削除。
2. 新しい AWS Config 関連リソース（AWS::Config::ConfigurationRecorder および AWS::Config::DeliveryChannel）を作成。
   IncludeGlobalResourceTypes は東京リージョン（ap-northeast-1）のみ True とし、他リージョンは False とした。
3. 新しい IAM リソース（AWS::IAM::Group）を作成。

**■確認結果**
* 東京リージョン（ap-northeast-1）の AWS Config に新たに作成した IAM リソース（AWS::IAM::Group）が表示された。
* バージニア北部リージョン（us-east-1）の AWS Config には新たに作成した IAM リソース（AWS::IAM::Group）が表示されなかった。

また、念のため、API リファレンスも確認してみましたが、動作変更に関する記載はありませんでした。

https://docs.aws.amazon.com/config/latest/APIReference/API_PutConfigurationRecorder.html

https://docs.aws.amazon.com/config/latest/APIReference/API_RecordingGroup.html

時間が取れたらさらに検証してみようと思いますが、AWS サポートに確認した方が確実かつ早いかもしれません。
もし、詳細をご存じの方がいらっしゃいましたら教えてください。


# おわりに

本記事では、サンプルテンプレートの問題点を指摘・改善しましたが、サンプルの提供元を批判する意図は一切ありません。
サンプルの提供元に関わらず、利用者が内容を理解した上で利用することと、ベストプラクティスの理解が重要と考えます。

最後まで読んでいただき、ありがとうございます。
本記事が少しでも役に立てば幸いです。