セキュリティ運用の第一歩:AWS Config 全リージョン有効化&確認自動化ガイド

に公開

AWS Config 全リージョン有効化手順まとめ

目的

  • 全リージョンで AWS Config を有効化
  • config-bucket-<アカウントID> という S3 バケットにログを集約
  • Recorder, Delivery Channel, Recording の有効化を一括自動化
  • 設定確認用スクリプトで整合性をチェック

前提条件(確認)

Configのサービスリンクロール(AWSServiceRoleForConfig)が事前に作成されていること。なければ以下で1回だけ作成可能です。

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

バケットポリシー未設定でも Config は動作可能(ただし、必要に応じて後から設定可能)

🔧 1. 全自動セットアップスクリプト(Config 有効化含む)

以下のスクリプトは、Config 記録のための IAM ロールと S3 バケットがすでに存在する前提で動作します。

bash -c '
ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
S3_BUCKET_NAME="config-bucket-$ACCOUNT_ID"
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)

# S3バケットをap-northeast-1に作成(存在確認付き)
if ! aws s3api head-bucket --bucket "$S3_BUCKET_NAME" 2>/dev/null; then
    echo "[+] Creating S3 bucket: $S3_BUCKET_NAME"
    aws s3api create-bucket \
        --bucket "$S3_BUCKET_NAME" \
        --region ap-northeast-1 \
        --create-bucket-configuration LocationConstraint=ap-northeast-1
else
    echo "[✓] S3 bucket $S3_BUCKET_NAME already exists"
fi

# S3バケットポリシーを設定
echo "[+] Setting S3 bucket policy for AWS Config"
cat > /tmp/config-bucket-policy.json << EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AWSConfigBucketPermissionsCheck",
      "Effect": "Allow",
      "Principal": { "Service": "config.amazonaws.com" },
      "Action": "s3:GetBucketAcl",
      "Resource": "arn:aws:s3:::$S3_BUCKET_NAME",
      "Condition": { "StringEquals": { "AWS:SourceAccount": "$ACCOUNT_ID" } }
    },
    {
      "Sid": "AWSConfigBucketExistenceCheck",
      "Effect": "Allow",
      "Principal": { "Service": "config.amazonaws.com" },
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::$S3_BUCKET_NAME",
      "Condition": { "StringEquals": { "AWS:SourceAccount": "$ACCOUNT_ID" } }
    },
    {
      "Sid": "AWSConfigBucketDelivery",
      "Effect": "Allow",
      "Principal": { "Service": "config.amazonaws.com" },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::$S3_BUCKET_NAME/*",
      "Condition": {
        "StringEquals": {
          "s3:x-amz-acl": "bucket-owner-full-control",
          "AWS:SourceAccount": "$ACCOUNT_ID"
        }
      }
    }
  ]
}
EOF

aws s3api put-bucket-policy \
    --bucket "$S3_BUCKET_NAME" \
    --policy file:///tmp/config-bucket-policy.json

# バケットのパブリックアクセスをブロック
aws s3api put-public-access-block \
    --bucket "$S3_BUCKET_NAME" \
    --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

# 各リージョンでConfigを設定
for REGION in $REGIONS; do
    echo ""
    echo "=== Processing region: $REGION ==="

    # 既存名の列挙
    EXISTING_REC_NAMES=$(aws configservice describe-configuration-recorders --region "$REGION" \
        --query "ConfigurationRecorders[].name" --output text || true)
    EXISTING_CH_NAMES=$(aws configservice describe-delivery-channels --region "$REGION" \
        --query "DeliveryChannels[].name" --output text || true)

    # 1) Recorder停止
    if [ -n "${EXISTING_REC_NAMES:-}" ]; then
        echo "[*] Stopping existing recorders: $EXISTING_REC_NAMES"
        for NAME in $EXISTING_REC_NAMES; do
            aws configservice stop-configuration-recorder \
                --region "$REGION" \
                --configuration-recorder-name "$NAME" || true
        done
    fi

    # 2) Delivery Channel削除
    if [ -n "${EXISTING_CH_NAMES:-}" ]; then
        echo "[*] Deleting existing delivery channels: $EXISTING_CH_NAMES"
        for NAME in $EXISTING_CH_NAMES; do
            aws configservice delete-delivery-channel \
                --region "$REGION" \
                --delivery-channel-name "$NAME" || true
        done
        # 消失待ち
        for i in {1..20}; do
            cnt=$(aws configservice describe-delivery-channels --region "$REGION" \
                --query "length(DeliveryChannels)" --output text)
            [ "$cnt" = "0" ] && break
            sleep 2
        done
    fi

    # 3) Recorder削除
    if [ -n "${EXISTING_REC_NAMES:-}" ]; then
        echo "[*] Deleting existing recorders: $EXISTING_REC_NAMES"
        for NAME in $EXISTING_REC_NAMES; do
            aws configservice delete-configuration-recorder \
                --region "$REGION" \
                --configuration-recorder-name "$NAME" || true
        done
        # 消失待ち
        for i in {1..20}; do
            cnt=$(aws configservice describe-configuration-recorders --region "$REGION" \
                --query "length(ConfigurationRecorders)" --output text)
            [ "$cnt" = "0" ] && break
            sleep 2
        done
    fi

    # 4) includeGlobalResourceTypes は東京だけ true
    if [ "$REGION" = "ap-northeast-1" ]; then
        INCLUDE_GLOBAL=true
    else
        INCLUDE_GLOBAL=false
    fi

    # 5) Recorder作成
    echo "[+] Creating configuration recorder in $REGION"
    aws configservice put-configuration-recorder \
        --region "$REGION" \
        --configuration-recorder name=default,roleARN="$IAM_ROLE" \
        --recording-group allSupported=true,includeGlobalResourceTypes=$INCLUDE_GLOBAL

    # 6) Delivery Channel作成
    echo "[+] Creating delivery channel in $REGION"
    aws configservice put-delivery-channel \
        --region "$REGION" \
        --delivery-channel name=default,s3BucketName="$S3_BUCKET_NAME"

    # 7) Recorder開始
    echo "[+] Starting configuration recorder in $REGION"
    aws configservice start-configuration-recorder \
        --region "$REGION" \
        --configuration-recorder-name default

    echo "[✓] AWS Config setup completed for $REGION"
done

# 一時ファイルを削除
rm -f /tmp/config-bucket-policy.json

echo ""
echo "[✓] AWS Config setup completed for all regions"
'

✅ 2. 確認用スクリプト(Config設定の整合性検証)

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

以下のスクリプトは、各リージョンの AWS 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"

    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

    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")

    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."
'

📌 備考

  • バケットリージョンは ap-northeast-1 固定(日本リージョン)
  • バケットポリシー は必要に応じて追加可能(未設定でも基本的な動作は可能)

Discussion