メインコンテンツまでスキップ
翻訳ページ

このページは AI の支援を受けて英語から翻訳されています。内容が異なる場合は英語版が正となります。

AWS グローバルインベントリ

このガイドでは、StackQL を使用して、特定の AWS アカウント内の すべてのリージョン にある すべてのサービス の すべてのリソース について、包括的なインベントリを生成する方法を示します。StackQL は、SQL 構文を使用してクラウドインフラストラクチャとリソースのクエリとデプロイを行える強力な開発ツールです。

動作確認済み 組み込み SQL バックエンド postgres SQL バックエンド macos linux powershell

仕組み​

StackQL は、複数のクエリを非同期に処理できる SQL エンジンを実装しています。たとえば、次のようなクエリです。

SELECT region, COUNT(*) as num_functions
FROM aws.lambda.functions
WHERE region IN (
'us-east-1','us-east-2','us-west-1','us-west-2',
'ap-south-1','ap-northeast-3','ap-northeast-2',
'ap-southeast-1','ap-southeast-2','ap-northeast-1',
'ca-central-1','eu-central-1','eu-west-1',
'eu-west-2','eu-west-3','eu-north-1','sa-east-1')
GROUP BY region;

上記のクエリは、実際には並列に実行される 17 個のクエリ(リージョンごとに 1 つのクエリ)であり、その結果が集約されてレポートとして表示されます。StackQL の Python ラッパーである pystackql を使用すると、該当するサービスがサポートする すべてのリージョン について、すべてのサービス の すべてのリソース を反復処理できます。その結果、次の例のように、各リージョンに各リソースのインスタンスがいくつデプロイされているかを示すレポートが得られます。

AWS インベントリの例

前提条件​

  • pip install pystackql でインストールできる pystackql
  • ReadOnlyAccess ポリシー以上がアタッチされた AWS ユーザーまたはロール(ロールは sts assume-role で引き受け可能)
  • AWS_ACCESS_KEY_ID および AWS_SECRET_ACCESS_KEY 環境変数の設定(AWS Cloud Shell を使用すると自動的に設定可能)

コード​

次のコードは、以下の処理を実行します。

  1. すべての AWS リージョンのリスト(regions)と、サービスごとにサポートされるリージョンのリスト(supported_regions)を定義する(supported_regions の導出方法の詳細は AWS Service Support by Region at a Glance を参照)
  2. aws および aws StackQL プロバイダーがまだインストールされていない場合は、それらをプルする。
  3. 各サービスを反復処理し(SHOW SERVICES を使用)、サービスごとに各リソースを反復処理する(SHOW RESOURCES を使用)
  4. すべてのリージョンにわたる各リソースのすべてのインスタンスを非同期に SELECT し、データをリージョンごとにグループ化して、リージョンごとに各リソースのインスタンス数をカウントする

コードは次のとおりです。

from regions import supported_regions, regions
from resources import excluded_resources, resource_filters
import time
from pystackql import StackQL
import pandas as pd

stackql = StackQL(output='pandas', page_limit=-1, execution_concurrency_limit=-1)

def pull_stackql_providers():
providers_df = stackql.execute("SHOW PROVIDERS")
installed = providers_df['name'].values if not providers_df.empty else []
for provider in ('aws', 'awscc'):
if provider not in installed:
print(f"{provider} not installed. Installing {provider}")
res = stackql.executeStmt(f"REGISTRY PULL {provider}")
print(res['message'])

def get_s3_buckets():
# special case for s3 buckets - use the awscc provider's list view, which
# returns each bucket alongside its region in a single query across all regions
regions_in = ", ".join([f"'{region}'" for region in regions])
buckets_df = stackql.execute(f"""
SELECT region, bucket_name
FROM awscc.s3.buckets_list_only
WHERE region IN ({regions_in});
""")
# Group by region and count the total resources (buckets) in each region
grouped = buckets_df.groupby('region').size().reset_index(name='total_resources')
grouped['svc'] = 's3'
grouped['res'] = 'buckets'
output_df = grouped[['svc', 'res', 'region', 'total_resources']]
return output_df

def main():
start_time = time.time()
pull_stackql_providers()
results_df = pd.DataFrame(columns=['svc', 'res', 'region', 'total_resources'])
# get all enumerable aws services
services_df = stackql.execute("SHOW SERVICES IN aws")
for svcIx, svcRow in services_df.iterrows():
service = svcRow['name']
if service in supported_regions:
# check all enumerable aws resources in the service against supported regions
resources_df = stackql.execute(f"SHOW RESOURCES IN aws.{service}")
plural_resources = [resource for resource in resources_df['name'].tolist() if resource.endswith('s')]
if plural_resources:
for resource in plural_resources:
print(f"Checking {resource} in {service}...")
if service == 's3' and resource == 'buckets':
print(f"Checking location for s3 buckets...")
resource_df = get_s3_buckets()
else:
# should we skip this service?
if service in excluded_resources and resource in excluded_resources[service]:
continue
# check if the resource is global or regional
if 'global' in supported_regions[service]:
regions_in = ", ".join([f"'{region}'" for region in regions])
else:
regions_in = ", ".join([f"'{region}'" for region in supported_regions[service]])

# check if there are additional where clauses for this resource
where_clause = resource_filters.get(service, {}).get(resource, '')

resource_df = stackql.execute(f"""
SELECT '{service}' as svc, '{resource}' as res, region, COUNT(*) as total_resources
FROM aws.{service}.{resource}
WHERE region IN ({regions_in}){where_clause}
GROUP BY svc, res, region
""")

if not resource_df.empty:
# Append resource_df to the results_df
if not 'error' in resource_df.columns:
results_df = pd.concat([results_df, resource_df], ignore_index=True)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"Elapsed time: {elapsed_time:.2f} seconds")
results_df.to_csv('all_aws_resources.csv', index=False)

if __name__ == "__main__":
main()