IAM Access AnalyzerのアーカイブルールをCLIやCloudFormationで追加した場合はアーカイブルールの適用を忘れずに

マネジメントコンソールでは1ステップできたこともAWS CLIでは複数の手順を踏むことがあることを意識しよう
2023.02.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

アーカイブルールを追加したのにアーカイブされないな

こんにちは、のんピ(@non____97)です。

皆さんはIAM Access Analyzerのアーカイブルールを追加したのにアーカイブされない事象に遭遇したことはありますか? 私はあります。

私が遭遇した場面はいずれもマネジメントコンソールではなく、AWS CLIとCloudFormationでアーカイブルールを作成した時でした。

マネジメントコンソールではアーカイブルール作成のタイミングでアーカイブしてくれていた認識だったので、少し驚きました。

今回、どのような理由でこの事象が発生したのか調査したので紹介します。

いきなりまとめ

  • アーカイブルールの作成(CreateArchiveRule)をしても、既に検出されたものに対してアーカイブルールは適用されない
  • 別途アーカイブルールの適用(ApplyArchiveRule)が必要
  • マネジメントコンソールからアーカイブルールを作成する場合は、既に検出されたものに対してアーカイブルールを適用することが可能
  • CloudFormation StackSetsでIAM Access Analyzer作成後にアーカイブルールを追加する場合は、別途アーカイブルールの適用をする仕組みが必要

ドキュメントを確認する

アーカイブルールがどのような仕様なのかドキュメントを確認します。

すると、以下のような記載がありました。

結果の詳細に示された情報を使用して、ルールの作成時または編集時に使用する特定のリソースや外部エンティティを識別します。アーカイブルールを作成すると、ルールの基準に一致する新しい結果のみが自動的にアーカイブされます。既存の結果は自動的にはアーカイブされません。

アーカイブルール - AWS Identity and Access Management

どうやら、アーカイブルール作成時点で既に検出されていたものについてはアーカイブされないようです。

アーカイブルールの作成をするCreateArchiveRule APIのリクエストシンタックスを確認すると、確かに作成と同時にアーカイブするようなパラメーターはありません。

PUT /analyzer/analyzerName/archive-rule HTTP/1.1
Content-type: application/json

{
   "clientToken": "string",
   "filter": { 
      "string" : { 
         "contains": [ "string" ],
         "eq": [ "string" ],
         "exists": boolean,
         "neq": [ "string" ]
      }
   },
   "ruleName": "string"
}

ただし、マネジメントコンソールではアーカイブルール作成のタイミングで併せてアーカイブできるオプションがあるようです。

基準と値の追加が完了したら、[Create rule (ルールの作成)] を選択して、新しい結果にのみルールを適用します。ルール基準に基づいて新規および既存の結果をアーカイブするには、[Create and archive active findings (アクティブな結果を作成してアーカイブする)] を選択します。[Results (結果)] セクションでは、アーカイブルールが適用されるアクティブな結果のリストを確認できます。

アーカイブルール - AWS Identity and Access Management

実際にマネジメントコンソールを確認すると、アクティブな検出結果を作成およびアーカイブというボタンがありました。

アクティブな検出結果を作成およびアーカイブ

やってみた

AWS CLIでアーカイブルールを作成した場合に既存の検出結果は自動でアーカイブされないことを確認

実際にAWS CLIでアーカイブルールを作成した場合に既存の検出結果は自動でアーカイブされないことを確認します。

IAM Access Analyzerは作成済みです。

いくつかIAMロールについて検出されていることを確認します。

IAMロールが検出されていることを確認

この状態でIAMロールをアーカイブするようなアーカイブルールをAWS CLIで作成します。

$ create_archive_rule_input=$(cat <<EOM
{
    "analyzerName": "cm-access-analyzer",
    "ruleName": "iam-role-archive",
    "filter": {
        "resourceType": {
            "eq": [
                "AWS::IAM::Role"
            ]
        }
    }
}
EOM
)

# アーカイブルールの作成
$ aws accessanalyzer create-archive-rule \
    --cli-input-json "${create_archive_rule_input}"

# 作成したアーカイブルールの確認
$ aws accessanalyzer get-archive-rule \
    --analyzer-name cm-access-analyzer \
    --rule-name iam-role-archive
{
    "archiveRule": {
        "ruleName": "iam-role-archive",
        "filter": {
            "resourceType": {
                "eq": [
                    "AWS::IAM::Role"
                ]
            }
        },
        "createdAt": "2023-02-28T12:23:24+00:00",
        "updatedAt": "2023-02-28T12:23:24+00:00"
    }
}

アーカイブルール作成後、マネジメントコンソールからアーカイブされているか確認します。

アーカイブされていないことを確認

アーカイブされていませんね。

AWS CLIからも確認します。

$ aws accessanalyzer list-findings \
    --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<AWSアカウントID>:analyzer/cm-access-analyzer \
    --query 'findings[?status==`ACTIVE`]'
[
    {
        "id": "16f467a0-1d46-4750-b59b-df68677c5efd",
        "principal": {
            "AWS": "<AWSアカウントID>"
        },
        "action": [
            "sts:AssumeRole"
        "resource": "arn:aws:iam::<AWSアカウントID>:role/<IAMロール名>",
        "isPublic": false,
        "resourceType": "AWS::IAM::Role",
        "condition": {},
        "createdAt": "2022-05-27T17:11:54.527000+00:00",
        "analyzedAt": "2023-02-28T09:47:45.342000+00:00",
        "updatedAt": "2022-05-27T17:11:54.527000+00:00",
        "status": "ACTIVE",
        "resourceOwnerAccount": "<AWSアカウントID>"
    },
    {
        "id": "cc9a7607-e3e6-4f9a-b7fd-27bfa8c85c02",
        "principal": {
            "AWS": "arn:aws:iam::<AWSアカウントID>:user/<iamロール名>"
        },
        "action": [
            "sts:AssumeRole"
        ],
        "resource": "arn:aws:iam::<AWSアカウントID>:role/<IAMロール名>",
        "isPublic": false,
        "resourceType": "AWS::IAM::Role",
        "condition": {},
        "createdAt": "2022-05-27T17:11:54.527000+00:00",
        "analyzedAt": "2023-02-28T09:47:45.342000+00:00",
        "updatedAt": "2022-05-27T17:11:54.527000+00:00",
        "status": "ACTIVE",
        "resourceOwnerAccount": "<AWSアカウントID>"
    },
    {
        "id": "a0ce674f-09d7-48bf-a26d-f8e81810c55c",
        "principal": {
            "AWS": "arn:aws:iam::<AWSアカウントID>:user/<iamロール名>"
        },
        "action": [
            "sts:AssumeRole"
        ],
        "resource": "arn:aws:iam::<AWSアカウントID>:role/<iamロール名>",
        "isPublic": false,
        "resourceType": "AWS::IAM::Role",
        "condition": {},
        "createdAt": "2022-05-27T17:11:54.527000+00:00",
        "analyzedAt": "2023-02-28T09:47:45.342000+00:00",
        "updatedAt": "2022-05-27T17:11:54.527000+00:00",
        "status": "ACTIVE",
        "resourceOwnerAccount": "<AWSアカウントID>"
    },
    {
        "id": "d4ecae43-7cb6-498e-b10b-36e8e986c157",
        "principal": {
            "AWS": "arn:aws:iam::<AWSアカウントID>:user/<iamロール名>"
        },
        "action": [
            "sts:AssumeRole"
        ],
        "resource": "arn:aws:iam::<AWSアカウントID>:role/<iamロール名>",
        "isPublic": false,
        "resourceType": "AWS::IAM::Role",
        "condition": {},
        "createdAt": "2022-05-27T17:11:54.527000+00:00",
        "analyzedAt": "2023-02-28T09:47:45.342000+00:00",
        "updatedAt": "2022-05-27T17:11:54.527000+00:00",
        "status": "ACTIVE",
        "resourceOwnerAccount": "<AWSアカウントID>"
    }
]

マネジメントコンソールと同じく、アーカイブされていないアクティブな検出が4件見つかりました。

改めてAWS CLIでアーカイブルールを作成した場合は、既存の検出結果は自動でアーカイブされないことが確認できました。

なお、マネジメントコンソールで作成したアーカイブルールを選択して編集をクリックすると、アーカイブルールを適用するとアーカイブされる検出結果が表示されます。

アーカイブルールを編集しようとすると出てくる

既存の検出結果に対してアーカイブルールの適用をする

それでは、既存の検出結果に対してアーカイブルールの適用をしてみます。

アーカイブルールの適用はApplyArchiveRule APIです。

今回はAWS CLIでアーカイブルールの適用をするので、apply-archive-ruleを叩きます。

$ aws accessanalyzer apply-archive-rule \
    --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<AWSアカウントID>:analyzer/cm-access-analyzer \
    --rule-name iam-role-archive

アーカイブルール適用後、マネジメントコンソールからアーカイブされたことを確認します。

アーカイブされたことを確認

全てアーカイブされ、検出結果はありませんと表示されるようになりました。

AWS CLIからもアーカイブされたことを確認します。

$ aws accessanalyzer list-findings \
    --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<AWSアカウントID>:analyzer/cm-access-analyzer \
    --query 'findings[?status==`ACTIVE`]'
[]

ステータスがACTIVEとなっている検出結果はないようですね。

CloudFormation StackSetsで複数アカウントや複数リージョンにアーカイブルールを追加した場合

AWS CLIでアーカイブルールを作成した場合は、セットでアーカイブルールの適用を行う必要があることをお伝えしました。

ここで気になるのが、CloudFormationでアーカイブルールを作成した場合です。

EventBridgeルールでアーカイブルールの作成や更新がされたタイミングでアーカイブルールを適用するLambda関数 or Step Functionsのステートマシンを実行するような実装をすれば対応できるかもしれません。

しかし、CloudFormation StackSetsで複数アカウントや複数リージョンにアーカイブルールを追加する環境では、各AWSアカウントとリージョンにEventBridgeルールとLambda関数 or ステートマシンをデプロイするのがちょっと手間です。(それこそCloudFormation StackSetsでデプロイすれば良いかもしれませんが)

そのような場合は、Organizationsの管理アカウントから全アカウントにAssume Roleして、全リージョンのIAM Access Analyzerのアーカイブルールを適用するような処理を行えば良いのではと考えました。

Organizationsで作成したアカウントはデフォルトで、管理アカウントからのAssume Roleを許可するAdministratorAccessのIAMロールが作成されています。

実際のPythonスクリプトは以下の通りです。

import logging
import boto3
import botocore

logger = logging.getLogger()
logger.setLevel(logging.INFO)

assume_role_name = "<Assume Role先のIAMロールの名前>"
analyzer_name = "<アーカイブルールがあるアナライザーの名前>"
archive_rule_name = "<適用したいアーカイブルールの名前>"

# 全てのリージョンを取得
ec2_client = boto3.client("ec2")
regions = list(map(lambda x: x["RegionName"], ec2_client.describe_regions()["Regions"]))

# Organizationsに属するアカウントを取得
organizations_client = boto3.client("organizations")
accountIds = [d["Id"] for d in organizations_client.list_accounts()["Accounts"]]

# sts
sts_client = boto3.client("sts")
config = botocore.config.Config(
    connect_timeout=5, retries={"max_attempts": 5, "mode": "standard"}
)


print(f"Analyzer Name : {analyzer_name}")
print(f"Archive Rule Name : {archive_rule_name}")


for accountId in accountIds:
    print("=====================================================")
    print(f"Account ID : {accountId}")

    # 対象アカウントにAssume Role
    assumed_role_object = sts_client.assume_role(
        RoleArn=f"arn:aws:iam::{accountId}:role/{assume_role_name}",
        RoleSessionName=assume_role_name,
    )
    credentials = assumed_role_object["Credentials"]

    for region in regions:
        print("-----------------------------------------------------")
        print(f"Region Name : {region}")

        accessanalyzer_client = boto3.client(
            "accessanalyzer",
            aws_access_key_id=credentials["AccessKeyId"],
            aws_secret_access_key=credentials["SecretAccessKey"],
            aws_session_token=credentials["SessionToken"],
            config=config,
            region_name=region,
        )

        # アーカイブルールの適用
        try:
            print(f"Analyzer Name : {analyzer_name}")
            print(f"Archive Rule Name : {archive_rule_name}")

            accessanalyzer_client.apply_archive_rule(
                analyzerArn=f"arn:aws:access-analyzer:{region}:{accountId}:analyzer/{analyzer_name}",
                ruleName=archive_rule_name,
            )

        except botocore.exceptions.ClientError as error:
            logger.error(error)
            continue

これにより各アカウントとリージョンに新規リソースを作成することなく、アーカイブルールを適用することができます。

マネジメントコンソールでは1ステップできたこともAWS CLIでは複数の手順を踏むことがあることを意識しよう

アーカイブルールの作成(CreateArchiveRule)をしても、既に検出されたものに対してアーカイブルールは適用されないことを紹介しました。

マネジメントコンソールでは1ステップできたこともAWS CLIでは複数の手順を踏むことがあることを意識する必要があることを再確認しました。今後も何かスクリプトを作成するときに気をつけようと思います。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!