全リージョンでAWS Configの記録対象を「すべて」に設定し、IAMロールをサービスリンクロールに一括変更してみた

全リージョンでAWS Configの記録対象を「すべて」に設定し、IAMロールをサービスリンクロールに一括変更してみた

Clock Icon2025.03.13

はじめに

AWS Security Hubのコントロール「Config.1」には、以下の3つのチェック項目があります。

  1. AWS Config(レコーダー)が有効化されているか
  2. 有効化されているすべてのSecurity Hubコントロールに対応するすべてのリソースタイプがConfigレコーダーで記録できているか
  3. AWS Configのサービスリンクロール(AWSServiceRoleForConfig)が設定されているか

https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/config-controls.html

今回は、2点目と3点目のチェックに失敗していると仮定し、全リージョンでAWS Configの記録対象を「すべて」に設定し、IAMロールをサービスリンクロール(AWSServiceRoleForConfig)に一括変更する方法をまとめました。

AWSベストプラクティス

Security Hubのコントロールだけでなく、AWS Configのベストプラクティスにおいても、記録対象は「すべて」に設定することが推奨されています。

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

また、AWSの公式ドキュメントでも、サービスリンクロールAWSServiceRoleForConfigの利用が推奨されています。

推奨: サービスにリンクされたロールを使用する
サービスにリンクされたロールを使用することをお勧めします。サービスにリンクされたロールは、 が期待どおりに実行 AWS Config するために必要なすべてのアクセス許可を追加します。
https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/manual-setup.title.html

AWSServiceRoleForConfigは、AWS Configのサービスリンクロールであり、AWSによって自動的に作成されるIAMロールです。
このロールは、AWSサービスが必要とする権限のみを持ち、ユーザーがIAMポリシーを直接修正することはできません。また、サービスリンクロールは通常、SCP(Service Control Policy)の影響を受けません。

そのため、基本的にはAWSServiceRoleForConfigの利用を検討しましょう。

前提条件

IAMロールを変更するにあたり、事前に確認すべき点がいくつかあります。以下については対応した前提とします。

  • Configの配信チャネルのS3バケットのバケットポリシーで、Configへのアクセスを許可していること

https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/s3-bucket-policy.html#required-permissions-using-servicelinkedrole

  • 配信をAWS KMSキーで暗号化している場合、キーポリシーには、Configへのアクセスを許可していること(デフォルトはSSE-S3にて暗号化されますので、対応不要です。)

https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/s3-kms-key-policy.html

KMSキーを設定しているかどうかは、AWS CLIで確認できます。以下の例では、KMSキーで暗号化されていませんので対応不要です。

$ aws configservice describe-delivery-channels
{
    "DeliveryChannels": [
        {
            "name": "default",
            "s3BucketName": "cm-members-config-xxxx"
        }
    ]
}
  • 配信チャネルにSNSトピックを設定している場合、SNSトピックのアクセスポリシーにConfigへのアクセスを許可していること

https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/sns-topic-policy.html

一括変更する

AWS CloudShellを開き、サービスリンクロールを作成します。すでに作成済みの場合は、この手順を省略できます。

aws iam create-service-linked-role --aws-service-name config.amazonaws.com

以下の一括変更コマンドを実行します。

bash -c 'ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text); \
IAM_ROLE="arn:aws:iam::$ACCOUNT_ID:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig"; \
regions=$(aws ec2 describe-regions --query "Regions[].RegionName" --output text); \
for region in $regions; do \
    echo "Processing region: $region"; \
    if [ "$region" == "ap-northeast-1" ]; then \
        aws configservice put-configuration-recorder \
            --region $region \
            --configuration-recorder name=default,roleARN=$IAM_ROLE \
            --recording-group allSupported=true,includeGlobalResourceTypes=true; \
    else \
        aws configservice put-configuration-recorder \
            --region $region \
            --configuration-recorder name=default,roleARN=$IAM_ROLE \
            --recording-group allSupported=true,includeGlobalResourceTypes=false; \
    fi; \
done'
出力結果例
Processing region: ap-south-1
Processing region: eu-north-1
Processing region: eu-west-3
Processing region: eu-west-2
Processing region: eu-west-1
Processing region: ap-northeast-3
Processing region: ap-northeast-2
Processing region: ap-northeast-1
Processing region: ca-central-1
Processing region: sa-east-1
Processing region: ap-southeast-1
Processing region: ap-southeast-2
Processing region: eu-central-1
Processing region: us-east-1
Processing region: us-east-2
Processing region: us-west-1
Processing region: us-west-2

コマンドでは、AWS Security Hubのコントロール[Config.1]が是正されるように、以下の3点を実行しています。

  • サービスリンクロールであるAWSServiceRoleForConfigをConfigレコーダーのIAMロールとして指定する
  • 東京リージョンでは、グローバルリソース (IAM リソースなど) を含め、すべてのリソースタイプを記録する
  • 東京リージョン以外のリージョンでは、グローバルリソースを除くすべてのリソースタイプを記録する

確認

Configレコーダー設定の確認

上記のコマンドで一括変更は可能ですが、設定が正しく適用されているかを確認するために、以下のコマンドを実行します。

bash -c '
ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
EXPECTED_IAM_ROLE="arn:aws:iam::$ACCOUNT_ID:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig"
regions=$(aws ec2 describe-regions --query "Regions[].RegionName" --output text)

divider="============================================================"

function print_error() { echo -e "\e[41m\e[97m[ERROR]\e[0m $1"; }
function print_success() { echo -e "\e[42m\e[97m[OK]\e[0m $1"; }

for region in $regions; do
    echo -e "\n$divider"
    echo "Checking Config Recorder in Region: $region"
    echo -e "$divider"

    # Configレコーダーの設定を取得
    recorder_status=$(aws configservice describe-configuration-recorders --region "$region" --query "ConfigurationRecorders[0]" --output json 2>/dev/null)

    if [[ -z "$recorder_status" ]]; then
        print_error "No Config Recorder found in region: $region"
        continue
    fi

    # Configレコーダーの記録戦略とIAMロールを取得
    all_supported=$(echo "$recorder_status" | jq -r ".recordingGroup.allSupported")
    include_global=$(echo "$recorder_status" | jq -r ".recordingGroup.includeGlobalResourceTypes")
    role_arn=$(echo "$recorder_status" | jq -r ".roleARN")

    # Configレコーダーの有効化ステータスを取得
    recorder_status_details=$(aws configservice describe-configuration-recorder-status --region "$region" --query "ConfigurationRecordersStatus[0]" --output json 2>/dev/null)
    recording_status=$(echo "$recorder_status_details" | jq -r ".recording")
    last_status=$(echo "$recorder_status_details" | jq -r ".lastStatus")
    last_status_change_time=$(echo "$recorder_status_details" | jq -r ".lastStatusChangeTime")

    # 出力情報を表示
    echo "IAM Role ARN: $role_arn"
    echo "Recording Status: $recording_status"
    echo "Last Status: $last_status"
    echo "Last Status Change Time: $last_status_change_time"
    echo "All Supported: $all_supported"
    echo "Include Global Resource Types: $include_global"

    # 共通のチェック
    [[ "$recording_status" != "true" ]] && print_error "Recording Status is not enabled."
    [[ "$last_status" != "SUCCESS" ]] && print_error "Last Status is not SUCCESS: $last_status"
    [[ "$all_supported" != "true" ]] && print_error "allSupported is not true"
    [[ "$role_arn" != "$EXPECTED_IAM_ROLE" ]] && print_error "IAM Role ARN does not match expected value"

    # リージョンごとの条件をチェック
    if [[ "$region" == "ap-northeast-1" && "$include_global" != "true" ]]; then
        print_error "includeGlobalResourceTypes is not true for Tokyo region."
    elif [[ "$region" != "ap-northeast-1" && "$include_global" != "false" ]]; then
        print_error "includeGlobalResourceTypes is not false for region: $region."
    fi

    # トータルの成功/失敗判定
    if [[ "$recording_status" == "true" && "$all_supported" == "true" && "$role_arn" == "$EXPECTED_IAM_ROLE" && "$last_status" == "SUCCESS" && 
          (("$region" == "ap-northeast-1" && "$include_global" == "true") || ("$region" != "ap-northeast-1" && "$include_global" == "false")) ]]; then
        print_success "Region $region recording strategy, IAM Role, and recording status are correct."
    else
        print_error "Region $region recording strategy is incorrect."
    fi
done

echo -e "\n$divider"
echo "Config Recorder check completed."
'
一部の出力結果例
============================================================
Checking Config Recorder in Region: ap-northeast-2
============================================================
IAM Role ARN: arn:aws:iam::111111111111:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig
Recording Status: true
Last Status: SUCCESS
Last Status Change Time: 2025-01-20T03:46:44.867000+00:00
All Supported: true
Include Global Resource Types: false
[OK] Region ap-northeast-2 recording strategy, IAM Role, and recording status are correct.

============================================================
Checking Config Recorder in Region: ap-northeast-1
============================================================
IAM Role ARN: arn:aws:iam::111111111111:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig
Recording Status: true
Last Status: SUCCESS
Last Status Change Time: 2025-01-20T02:30:08.824000+00:00
All Supported: true
Include Global Resource Types: true
[OK] Region ap-northeast-1 recording strategy, IAM Role, and recording status are correct.

============================================================
Checking Config Recorder in Region: ca-central-1
============================================================

エラーが出力されなければ、設定は正しく適用されています。

ConfigのファイルがS3バケットに保存されているか確認

Configの設定変更後、約6時間後に設定変更内容がS3バケットに保存されます。

  • S3バケットの保存先プレフィックス例:s3://cm-members-config-111111111111/AWSLogs/111111111111/Config/ap-northeast-2/2024/12/18/ConfigHistory/111111111111_Config_ap-northeast-2_ConfigHistory_AWS::Config::ConfigurationRecorder_20250117T054209Z_20250117T054634Z_1.json.gz

year、month、dayには、実施日を記載します。Configレコーダー変更日を指定してください。例では2025年1月1日を指定しています。
Config配信チャネルであるS3バケット名(cm-members-config-${ACCOUNT_ID})は各自で変更ください。

bash -c '
year="2025"; 
month="1"; 
day="1"; 

ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text); 
bucket_name="cm-members-config-${ACCOUNT_ID}"; 
base_prefix="AWSLogs/${ACCOUNT_ID}/Config"; 
regions=$(aws ec2 describe-regions --query "Regions[].RegionName" --output text); 
divider="============================================================"; 

echo -e "\n$divider"; 
echo "Starting S3 file check for ConfigurationRecorder files..."; 
echo "Account ID: $ACCOUNT_ID"; 
echo -e "$divider"; 

for region in $regions; do 
    prefix="${base_prefix}/${region}/${year}/${month}/${day}/ConfigHistory/"; 
    result=$(aws s3api list-objects-v2 --bucket "$bucket_name" --prefix "$prefix" --query "Contents[].Key" --output text 2>/dev/null | tr "\t" "\n" | grep "AWS::Config::ConfigurationRecorder"); 

    echo -e "$divider"; 
    if [[ -z "$result" ]]; then 
        echo -e "\e[41m\e[97m[ERROR]\e[0m No ConfigurationRecorder file found in region: $region"; 
        echo "Prefix: $prefix"; 
    else 
        echo -e "\e[42m\e[97m[OK]\e[0m ConfigurationRecorder file(s) found in region: $region"; 
        echo "Prefix: $prefix"; 
        echo "File(s):"; 
        echo "$result" | while read -r file; do 
            echo "  - $(basename "$file")"; 
        done; 
    fi; 
done; 

echo -e "$divider"; 
echo "S3 file check completed."; 
'
一部の出力結果例
============================================================
[OK] ConfigurationRecorder file(s) found in region: ap-northeast-2
Prefix: AWSLogs/111111111111/Config/ap-northeast-2/2025/1/17/ConfigHistory/
File(s):
  - 111111111111_Config_ap-northeast-2_ConfigHistory_AWS::Config::ConfigurationRecorder_20250120T002706Z_20250120T012642Z_1.json.gz
============================================================
[OK] ConfigurationRecorder file(s) found in region: ap-northeast-1
Prefix: AWSLogs/111111111111/Config/ap-northeast-1/2025/1/17/ConfigHistory/
File(s):
  - 111111111111_Config_ap-northeast-1_ConfigHistory_AWS::Config::ConfigurationRecorder_20250120T002706Z_20250120T022952Z_1.json.gz
============================================================
[OK] ConfigurationRecorder file(s) found in region: ca-central-1

エラーが出力されず、リージョンごとにファイルを確認できれば、保存が成功していることになります。

Configレコーダーの記録の失敗やS3バケットへの保存を失敗していないか確認

Configレコーダーの記録やS3バケットへの保存が失敗していないかを確認するために、以下のメトリクスをチェックします。

  • ConfigHistoryExportFailed:S3バケットへの保存が失敗していないか
  • ConfigurationRecorderInsufficientPermissionsFailure:レコーダーのIAMロールが原因で失敗したアクセス許可の試行回数

https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/viewing-the-aws-config-dashboard.html

AWS CloudShellで以下のコマンドを実行し、エラーが発生していないことを確認します。
start_timeend_timeregionsは、各自の環境に合わせて変更してください。

bash -c '
metric_names=("ConfigurationRecorderInsufficientPermissionsFailure" "ConfigHistoryExportFailed"); 
namespace="AWS/Config";

# 時間指定か前日か
start_time=$(date -u -d "1 day ago" +"%Y-%m-%dT%H:%M:%SZ"); 
# start_time="2025-01-01T00:00:00Z"; 

# 時間指定か現在時刻か
end_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ"); 
# end_time="2025-01-02T00:00:00Z"; 

# 全リージョンか特定のリージョン
regions=($(aws ec2 describe-regions --query "Regions[].RegionName" --output text));
# regions=("us-east-1" "ap-northeast-1");

for region in "${regions[@]}"; do 
    echo "Checking metrics in region: $region"; 
    for metric_name in "${metric_names[@]}"; do 
        echo "  Metric: $metric_name"; 

        # ディメンションの指定(ConfigHistoryExportFailed には不要)
        if [[ "$metric_name" == "ConfigurationRecorderInsufficientPermissionsFailure" ]]; then 
            dimensions="--dimensions Name=ResourceType,Value=All"
        else 
            dimensions=""
        fi

        result=$(aws cloudwatch get-metric-statistics \
            --namespace "$namespace" \
            --metric-name "$metric_name" \
            $dimensions \
            --start-time "$start_time" \
            --end-time "$end_time" \
            --period 86400 \
            --statistics Sum \
            --region "$region" \
            | jq ".Datapoints[] | select(.Sum > 0)"); 

        if [[ -z "$result" ]]; then 
            echo -e "    \e[42m\e[97m[OK]\e[0m No failures since $start_time"; 
        else 
            echo -e "    \e[41m\e[97m[ERROR]\e[0m Failures detected since $start_time"; 
            echo "    Details: $result"; 
        fi; 
    done; 
done
'
一部の出力結果例
Checking metrics in region: us-east-1
  Metric: ConfigurationRecorderInsufficientPermissionsFailure
    [OK] No failures since 2025-01-19T07:22:36Z
  Metric: ConfigHistoryExportFailed
    [OK] No failures since 2025-01-19T07:22:36Z
Checking metrics in region: us-east-2
  Metric: ConfigurationRecorderInsufficientPermissionsFailure
    [OK] No failures since 2025-01-19T07:22:36Z
  Metric: ConfigHistoryExportFailed
    [OK] No failures since 2025-01-19T07:22:36Z

エラーが出力されなければ、失敗はしていないことになります。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.