AWS ConfigルールとSCPを組み合わせてメンバーアカウントに展開する時のTips

この記事は、Supershipグループ Advent Calendar 2023の3日目の記事になります。

# はじめに

私は普段、CCoEとして当社で利用するパブリッククラウドの統制・運用を主な業務としています。  
CCoEの活動については昨年のAdvent Calendarでも触れられていますので、是非御覧ください。
> [CCoEを一から構築する　AWSのセキュリティ・ガバナンス・コスト最適化を実現するためのクラウドジャーニー
](https://qiita.com/Brutus/items/4d83ab3cc77c099ca65b#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB)

今回、統制の１つとしてAWS Configルールを各メンバーアカウントに展開したい、というケースがありました。
展開したConfigルールをメンバーアカウントで操作されないような仕組みを検討した場合、いろいろ考慮する点が多かったので、今回記事にまとめます。

なお、今回のものはすべて AWS CDKで構築した場合に発生するケースになります。  
マネジメントコンソール上で作成した場合は記載する考慮が不要な場合もあります。

また、各AWSサービスの基本的な説明については割愛します。

### 対象とする読者
- AWSをマルチアカウント管理している人
- AWSのサービスについてある程度理解している人

# Configルールの展開方法
まず、AWS Configのルールを各アカウントに展開するには２通りの方法があります。  
- CloudFormation StackSetsで展開する
- Config APIの `PutOrganizationConfigRule` を展開する

それぞれの違いについては以下の記事を参照ください。
> [Organizations環境でConfigルールを展開する方法を確認してみた](https://dev.classmethod.jp/articles/config-setting-in-organizations/)

今回はConfigの自動修復をあわせて設定したかったため、 **CloudFormation StackSetsで展開する** を採用しました。

## Stacksets
Stacksetsで展開する場合の考慮点は、以下のようなものがあります。

### リソースの展開について
Configのルールは、利用可能なリージョンすべてに展開することが望ましいです。  
Configの自動修復には、メンバーアカウント上でIAMロールを用意する必要があります。
Stacksetsでこれらをすべて展開しようとした場合、以下がポイントとなります。

:::note info
Configルールはリージョンサービス、IAMはグローバルサービスです。
そのため、テンプレートを分けるかテンプレート内で特定リージョンでのみIAMリソースを作成するといった条件付をしましょう。
:::

例えばStacksetsで複数リージョンに展開する場合を考えてみます。
ConfigとIAMを同じテンプレートで定義している場合はIAMリソースが重複して作成しようとするため、エラーとなります。
そのため、テンプレートを分割するか、テンプレート内で条件分岐する、といった対応が必要になります。

今回は、今後IAMリソースを追加で展開する場合を考え、シンプルなものにしたかったのでテンプレートを分け、グローバルサービス用のStacksetsと、Config用のStacksetsで分割しました。

# SCP
展開したIAMリソースやConfigルールは、そのままだとメンバーアカウントの利用者が変更・削除することが可能です。利用者が意図せずリソースを変更・削除してしまった場合、展開したConfigルールが正しく機能せず、統制としても正しく機能しない、ということになります。

このようなケースに対して、AWSではいくつかの方法があると思いますが、今回はOrganizationサービスの[SCP（サービスコントロールポリシー）](https://docs.aws.amazon.com/ja_jp/organizations/latest/userguide/orgs_manage_policies_scps.html)を検討しました。
SCPを利用することで、特定のアクション（今回展開したリソースの変更・削除など）を制限することができます。

## Configルールを指定する場合の課題
SCPではIAMポリシーと似た構文で宣言することができます。

例えば、 `Resource句` で特定のリソース名(ARN)に宣言することができます。
Stacksetsで展開する場合はリソース名を固定できるパターンが多いため、有効な手法であると言えます。
また、 `Condition句` で特定タグを持つリソースに限定する、といった手法も有効です。

今回の場合は展開したConfigルールの操作を制限したいのですが、いくつか問題があります。
ConfigルールのARNは、以下のようになっています。
```
arn:aws:config:ap-northeast-1:000000000000:config-rule/config-rule-xxxxxx
```
このように、以下の特徴が含まれます。
- ARNにルール名が含まれない
- ARN末尾に乱数が含まれる

したがって、 `Resource句` で指定することは難しいといえます。そのため、 `Condtion句` のタグで指定することが考えられると思います。

しかし、CloudformationでConfigルールを作成する際は、タグを指定することができません。（CloudformationのPropertyにタグがない。）
> [AWS::Config::ConfigRule](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-config-configrule.html)

手動でルールを作成する場合は、コンソールでタグを付与することは可能です。
今回、AWS CDKで作成しているため、手動での操作はなるべく避けたいと考え、対策をサポートケースに問い合わせながら調べてみました。

## Configルールのタグ付け
Configルールのタグ付けについては、マネジメントコンソールの操作以外では、[AWS Config リソースのタグ付け](https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/tagging.html)にもあるように、Config APIの[TagResource](https://docs.aws.amazon.com/ja_jp/config/latest/APIReference/API_TagResource.html)を実行する必要があります。

:::note info
Cloudformationが対応していないAPIアクションを実行したい場合、[AWS Lambda-backed カスタムリソース](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-custom-resources-lambda.html)が有効です。
:::

Cloudformationテンプレート上でLambdaを定義し、作成したリソース情報をLambdaに引き渡してAPIを実行する、といったことが可能です。
以下、カスタムリソース部分のテンプレートのサンプルになります。
```
Resources:
  ConfigRuleTags:
    Type: Custom::ConfigRuleTags
    Properties:
        ServiceToken: !GetAtt TagsResourcrFunction.Arn
        ConfigRuleArn: !GetAtt ConfigRule.Arn
  TagsResourcrFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: "config-tag-resource"
      Description: "Config rules add tags by ccoe."
      Runtime: python3.9
      Handler: index.handler
      Role: !Sub "arn:aws:iam::${AWS::AccountId}:role/role-name"
      Code:
        ZipFile: !Sub |
          import boto3
          import cfnresponse
          import time
          def handler(event, context):
            rule_arn = event['ResourceProperties']['ConfigRuleArn']
            client = boto3.client('config')
            response = client.tag_resource(
              ResourceArn=rule_arn,
              Tags=[
                {
                    'Key': 'key',
                    'Value': 'value'
                },
              ]
            )

            cfnresponse.send(event, context, cfnresponse.SUCCESS, response)
      Timeout: 900
      Tags:
        - Key: key
          Value: value
```
留意する部分としては、以下のようなものがあります。

- Lambdaで実行するAPIの許可を持ったIAMロールを別途用意する必要がある。
- Lambdaもメンバーアカウント上にデプロイされるため、同様にSCPで制限するなどが必要がある。

管理アカウント上にデプロイしたLambdaを使用することもできるようですが、構成をシンプルにするため今回は採用しませんでした。
> [ある AWS アカウントで作成された Lambda 関数を、別の AWS アカウントの AWS CloudFormation カスタムリソースとともに使用するにはどうすればよいですか?](https://repost.aws/ja/knowledge-center/cloudformation-lambda-custom-resource)

これらを駆使することで、特定のConfigルールへの操作をSCPで制限することができます。
以下、SCPのサンプルになります。
```
{
  "Sid": "DenyActions",
  "Effect": "Deny",
  "Action": [
    "config:PutConfigRule",
    "config:DeleteConfigRule",
  ],
  "Resource": "*",
  "Condition": {
    "StringEquals": {
      "aws:ResourceTag/key": "value"
    },
    "ArnNotLike": {
      "aws:PrincipalARN": [
        "arn:aws:iam::*:role/stacksets-exec-*"
      ]
    }
  }
},

```
上記のポリシーのActionは最低限のもので、展開するIAM・カスタムリソースLambdaなどのActionも制限したい場合は適宜追加が必要です。

`ArnNotLike` は、SCPが効いている際にStacksetsで変更・削除できなくなるため、除外条件として追加が必要です。
このロールはStacksetsの設定で変わる部分ですので、適宜確認ください。

## Configの自動修復について
AWS Configの自動修復設定については、タグを付与することができません。
しかし、ARNがルール名を含むものになるため、 `Resource句` で指定が可能です。
SCPで以下のようなポリシーになります。

```
 {
  "Sid": "DenyRemediationActions",
  "Effect": "Deny",
  "Action": [
    "config:DeleteRemediationConfiguration",
    "config:PutRemediationConfigurations"
  ],
  "Resource": "arn:aws:config:*:*:remediation-configuration/Cfn-StackSets-*",
  "Condition": {
    "ArnNotLike": {
      "aws:PrincipalARN": [
        "arn:aws:iam::*:role/stacksets-exec-*"
      ]
    }
  }
}
```

# おわりに
今回、独自にConfigルールと自動修復を展開するために色々新しいものに触れる機会があり、楽しかったです。
カスタムリソースLambdaなど、CDKで動いているが自身で直接利用したことはなかったので、便利なものだなあと感じました。

今後もCCoEとして適切な統制のため、いろいろチャレンジしていきたいです。

# 宣伝
Supershipではプロダクト開発やサービス開発に関わる人を絶賛募集しております。
ご興味がある方は以下リンクよりご確認ください。

[Supership 採用サイト](https://supership.jp/recruit/)

是非ともよろしくお願いします。