マルチアカウントにAWS CLIを実行してみた

2024.01.15

こんにちは。たかやまです。

マルチアカウント環境のリソースを確認するとき皆さんどうされますか?

Organizations環境であればAWS Configを使った高度なクエリでリソースを検索することができます。

ただ、AWS Configの高度なクエリはSQL文を利用する必要があったり、検索できるリソースタイプが限られています。

普段AWS CLIでリソースを確認している場合には、そのままマルチアカウントにAWS CLIでリソースを確認したいという要望もあるかと思います。
(ターミナルのブロードキャストコマンドのようなイメージ)

そこで今回はAWS CLIでマルチアカウントのリソースを確認する方法を紹介します。

検証環境

検証環境の全体構成はこちらです

やってみる

クロスアカウントロールを設定する

まずマルチアカウントにAWS CLIを実行するために、必要な権限を持ったクロスアカウントロールを作成していきます。ここでは、ReadOnly権限を持ったクロスアカウントロールを作成します。

<Your Management Account>には、マルチアカウントの管理アカウントのIDを入力してください。

cross-account-access-role.yaml

AWSTemplateFormatVersion: "2010-09-09"

Resources:
  CrossAccountAccessRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: CrossAccountAccessRole
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              AWS: arn:aws:iam::<Your Management Account>:root # Replace with the your management account ID
            Action: "sts:AssumeRole"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/ReadOnlyAccess"
      Path: "/"
      Description: "IAM role with ReadOnly permissions for cross-account access."

Outputs:
  RoleARN:
    Description: "The ARN of the created IAM Role"
    Value: !GetAtt CrossAccountAccessRole.Arn

Organizations環境であれば、CloudFormation StackSetsを利用して、マルチアカウントにクロスアカウントロールを作成することができます。

yamlファイルをローカルに配置した状態で以下のコマンドを実行することでスタックセットを作成します。

aws cloudformation create-stack-set \
--stack-set-name cross-account-access-role-stackset \
--template-body file://cross-account-access-role.yaml \
--permission-model SERVICE_MANAGED \
--auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false \
--capabilities CAPABILITY_NAMED_IAM

次に、以下のコマンドでスタックインスタンスを作成します。

r-xxxはOrganizationsのRootのUnitIdを入力してください。

aws cloudformation create-stack-instances \
--stack-set-name cross-account-access-role-stackset \
--operation-preferences FailureTolerancePercentage=100,MaxConcurrentPercentage=100,ConcurrencyMode=SOFT_FAILURE_TOLERANCE \
--deployment-targets OrganizationalUnitIds='["r-xxxx"]' \
--regions '["ap-northeast-1"]'

スタックインスタンスが無事作成されたら、AWS CLIをマルチアカウントに実行するための準備は完了です。

マルチアカウントに対してAWS CLIを実行する

以下のスクリプトを利用して、マルチアカウントに対してAWS CLIを実行していきます。

おおまかな処理として、aws organizations list-accountsで取得したアカウントIDに対して、aws sts assume-roleでさきほど作成したクロスアカウントロールの一時クレデンシャルを取得してAWS CLIを実行しています。

(Organizations環境外のアカウントに対してAWS CLIを実行する場合にはコメントアウトしているaccount_ids=(xxxxxxxxxxxx xxxxxxxxxxx)にアカウントIDをハードコードして利用してください)

処理時間短縮のために、コマンドは並列実行しています。アカウント数が多い場合はCPU負荷が高くなるため、高負荷を許容できない場合には直列実行版のスクリプトを利用することをおすすめします。

#!/bin/bash

# Gets the current AWS account ID.
admin_account_id=$(aws sts get-caller-identity --query "Account" --output text)

# Prompts the user for input: AWS command, account ID, and region.
read -rp "Enter the AWS command you want to execute: " command
read -rp "Enter the account ID to execute on (default: all accounts): " specific_account_id
read -rp "Enter the region to execute in (default: all regions): " specific_region
echo

# Gets a list of member account IDs.
if [[ -z "${specific_account_id}" ]]; then
  read -ra account_ids <<<"$(aws organizations list-accounts --query "Accounts[?Status=='ACTIVE'].Id" --output text)"
  # account_ids=(xxxxxxxxxxxx xxxxxxxxxxx)
else
  account_ids=("${specific_account_id}")
fi

# Gets a list of all available regions.
if [[ -z "${specific_region}" ]]; then
  read -ra regions <<<"$(aws ec2 describe-regions --query "Regions[].RegionName" --output text)"
else
  regions=("${specific_region}")
fi

# Creates a directory to store temporary files.
tmp_dir=$(mktemp -d)
trap 'rm -rf "${tmp_dir}"' EXIT

# Executes the command for each account and region in parallel.
for account_id in "${account_ids[@]}"; do
  for region in "${regions[@]}"; do
    tmp_file="${tmp_dir}/output_${account_id}_${region}.txt"
    (
      if [[ "${account_id}" != "${admin_account_id}" ]]; then
        role_arn="arn:aws:iam::${account_id}:role/CrossAccountAccessRole"

        assume_role_output=$(aws sts assume-role --role-arn "${role_arn}" --role-session-name "CrossAccountSession")

        access_key_id=$(echo "${assume_role_output}" | jq -r '.Credentials.AccessKeyId')
        secret_access_key=$(echo "${assume_role_output}" | jq -r '.Credentials.SecretAccessKey')
        session_token=$(echo "${assume_role_output}" | jq -r '.Credentials.SessionToken')

        AWS_ACCESS_KEY_ID=${access_key_id} AWS_SECRET_ACCESS_KEY=${secret_access_key} AWS_SESSION_TOKEN=${session_token} eval "${command} --region ${region}" &>>"${tmp_file}"
      else
        eval "${command} --region ${region}" &>>"${tmp_file}"
      fi
    ) &
  done
done

# Waits for all background processes to complete.
wait

# Outputs the results from the temporary files in order.
current_account_id=""
for account_id in "${account_ids[@]}"; do
  if [[ "${current_account_id}" != "${account_id}" ]]; then
    echo "# Account ID: ${account_id}"
    current_account_id=${account_id}
  fi
  for region in "${regions[@]}"; do
    tmp_file="${tmp_dir}/output_${account_id}_${region}.txt"
    if [[ -f "${tmp_file}" ]]; then
      echo "## Region: ${region}"
      cat "${tmp_file}"
      echo
    fi
  done
done

# Temporary directory deletion is handled by the trap.
echo "All commands have been executed."
直列実行版
#!/bin/bash

# Gets the current AWS account ID.
admin_account_id=$(aws sts get-caller-identity --query "Account" --output text)

# Prompts the user for input: AWS command, account ID, and region.
read -rp "Enter the AWS command you want to execute: " command
read -rp "Enter the account ID to execute on (default: all accounts): " specific_account_id
read -rp "Enter the region to execute in (default: all regions): " specific_region
echo

# Gets a list of member account IDs.
if [[ -z "${specific_account_id}" ]]; then
  read -ra account_ids <<<"$(aws organizations list-accounts --query "Accounts[?Status=='ACTIVE'].Id" --output text)"
  # account_ids=(xxxxxxxxxxxx xxxxxxxxxxx)
else
  account_ids=("${specific_account_id}")
fi

# Gets a list of all available regions.
if [[ -z "${specific_region}" ]]; then
  read -ra regions <<<"$(aws ec2 describe-regions --query "Regions[].RegionName" --output text)"
else
  regions=("${specific_region}")
fi

# Executes the command for each account and region in series.
for account_id in "${account_ids[@]}"; do
  echo "# Account ID: ${account_id}"
  for region in "${regions[@]}"; do
    echo "## Region: ${region}"
    if [[ "${account_id}" != "${admin_account_id}" ]]; then
      role_arn="arn:aws:iam::${account_id}:role/CrossAccountAccessRole"

      assume_role_output=$(aws sts assume-role --role-arn "${role_arn}" --role-session-name "CrossAccountSession")

      access_key_id=$(echo "${assume_role_output}" | jq -r '.Credentials.AccessKeyId')
      secret_access_key=$(echo "${assume_role_output}" | jq -r '.Credentials.SecretAccessKey')
      session_token=$(echo "${assume_role_output}" | jq -r '.Credentials.SessionToken')

      AWS_ACCESS_KEY_ID=${access_key_id} AWS_SECRET_ACCESS_KEY=${secret_access_key} AWS_SESSION_TOKEN=${session_token} eval "${command} --region ${region}"
    else
      eval "${command} --region ${region}"
    fi
    echo
  done
done

echo "All commands have been executed."

スクリプト内で以下3つの入力を受け付けるようにしています。

  • Enter the AWS command you want to execute:
  • Enter the account ID to execute on (default: all accounts):
  • Enter the region to execute in (default: all regions):

実際にこちらのスクリプトを利用して、マルチアカウントのAWS Configの設定を確認してみたいと思います。
パラメータとしては以下のように入力しています。

  • Enter the AWS command you want to execute: aws configservice describe-configuration-recorder-status --query ConfigurationRecordersStatus[].recording --output text
  • Enter the account ID to execute on (default: all accounts):
  • Enter the region to execute in (default: all regions):
Enter the AWS command you want to execute: aws configservice describe-configuration-recorder-status --query ConfigurationRecordersStatus[].recording --output text
Enter the account ID to execute on (default: all accounts): 
Enter the region to execute in (default: all regions): 

# Account ID: 27xxxxxxxx75
## Region: ap-south-1
True

## Region: eu-north-1
True

## Region: eu-west-3
True

## Region: eu-west-2
True

## Region: eu-west-1
True

## Region: ap-northeast-3
True

## Region: ap-northeast-2
True

## Region: ap-northeast-1
True

## Region: ca-central-1
True

## Region: sa-east-1
True

## Region: ap-southeast-1
True

## Region: ap-southeast-2
True

## Region: eu-central-1
True

## Region: us-east-1
True

## Region: us-east-2
True

## Region: us-west-1
True

## Region: us-west-2
True

# Account ID: 80xxxxxxxx71
## Region: ap-south-1
True

## Region: eu-north-1
True

## Region: eu-west-3
True

## Region: eu-west-2
True

## Region: eu-west-1
True

## Region: ap-northeast-3
True

## Region: ap-northeast-2
True

## Region: ap-northeast-1
True

## Region: ca-central-1
True

## Region: sa-east-1
True

## Region: ap-southeast-1
True

## Region: ap-southeast-2
True

## Region: eu-central-1
True

## Region: us-east-1
True

## Region: us-east-2
True

## Region: us-west-1
True

## Region: us-west-2
True

# Account ID: 77xxxxxxxx59
## Region: ap-south-1
True

## Region: eu-north-1
True

## Region: eu-west-3
True

## Region: eu-west-2
True

## Region: eu-west-1
True

## Region: ap-northeast-3
True

## Region: ap-northeast-2
True

## Region: ap-northeast-1
True

## Region: ca-central-1
True

## Region: sa-east-1
True

## Region: ap-southeast-1
True

## Region: ap-southeast-2
True

## Region: eu-central-1
True

## Region: us-east-1
True

## Region: us-east-2
True

## Region: us-west-1
True

## Region: us-west-2
True

# Account ID: 74xxxxxxxx44
## Region: ap-south-1
True

## Region: eu-north-1
True

## Region: eu-west-3
True

## Region: eu-west-2
True

## Region: eu-west-1
True

## Region: ap-northeast-3
True

## Region: ap-northeast-2
True

## Region: ap-northeast-1
True

## Region: ca-central-1
True

## Region: sa-east-1
True

## Region: ap-southeast-1
True

## Region: ap-southeast-2
True

## Region: eu-central-1
True

## Region: us-east-1
True

## Region: us-east-2
True

## Region: us-west-1
True

## Region: us-west-2
True

All commands have been executed.

4アカウントの全リージョンのAWS Configの設定を確認することができました。

以下のように特定アカウントの、特定リージョンのAWS Configの設定を確認することもできます。

  • Enter the AWS command you want to execute: aws configservice describe-configuration-recorder-status --query ConfigurationRecordersStatus[].recording --output text
  • Enter the account ID to execute on (default: all accounts): 27xxxxxxxx75
  • Enter the region to execute in (default: all regions): ap-northeast-1
Enter the AWS command you want to execute: aws configservice describe-configuration-recorder-status --query ConfigurationRecordersStatus[].recording --output text
Enter the account ID to execute on (default: all accounts): 27xxxxxxxx75
Enter the region to execute in (default: all regions): ap-northeast-1

# Account ID: 27xxxxxxxx75
## Region: ap-northeast-1
True

All commands have been executed.

最後に

今回はAWS CLIでマルチアカウントのリソースを確認する方法を紹介しました。

クロスアカウントロールを用意することで、AWS CLIでマルチアカウントのリソースを確認することができるようになります。

多数のアカウントに対してコマンド打ちたい場合などに、ぜひご活用してみてください。

以上、たかやま(@nyan_kotaroo)でした。