AWS_ENDPOINT_URL環境変数およびプロファイル設定で使うサービス名について

2023.07.11

しばたです。

前の記事でAWS各サービスのエンドポイント設定を上書きするAWS_ENDPOINT_URL環境変数と共有設定ファイル(~/.aws/config)の設定が増えたことを解説しました。

この機能では、例えばAWS_ENDPOINT_URL_S3の様にAWS_ENDPOINT_URL_"サービス名"の形式で特定サービス専用のエンドポイント設定が可能となっています。

既にAWSには200以上にわたる大量のサービスがあり、単純にサービス名といってもどの様な名称を指定すべきか困ってしまいます。
というわけで指定可能なサービス名がどの様なルールになっているのか調査してみました。

botocoreの実装

この機能はbotocoreに実装されており、具体的にはConfiguredEndpointProviderクラスで対象となる環境変数および共有設定ファイルの設定を探索しています。

探索順序は

  1. サービス毎の環境変数 (AWS_ENDPOINT_URL_"サービス名")
  2. グローバルの環境変数 (AWS_ENDPOINT_URL)
  3. サービス毎のファイル設定 (~/.aws/configservicesセクション)
  4. グローバルのファイル設定 (~/.aws/configendpoint_urlキー)

となっていました。

環境変数の名前

探索する環境変数の名前は_get_service_env_var_nameメソッドで取得しており、以下の様な実装になっています。

configprovider.py

    def _get_service_env_var_name(self):
        transformed_service_id_env = self._transformed_service_id.upper()
        return f'AWS_ENDPOINT_URL_{transformed_service_id_env}'

ここに出てくる_transformed_service_id_get_snake_case_service_idメソッドからその値を取得しており、

configprovider.py

        self._transformed_service_id = self._get_snake_case_service_id(
            self._client_name
        )

_get_snake_case_service_idメソッドの定義はこんな感じになっています。

configprovider.py

    def _get_snake_case_service_id(self, client_name):
        # Get the service ID without loading the service data file, accounting
        # for any aliases and standardizing the names with hyphens.
        client_name = utils.SERVICE_NAME_ALIASES.get(client_name, client_name)
        hyphenized_service_id = (
            utils.CLIENT_NAME_TO_HYPHENIZED_SERVICE_ID_OVERRIDES.get(
                client_name, client_name
            )
        )
        return hyphenized_service_id.replace('-', '_')

実装をざっくり説明すると、このメソッドで返される値はAWSのサービス名(boto3やAWS CLIで使うサービス名)をベースにし、特定のサービスに関しては一定の変換をかけています。
変換用のDictonaryとしてSERVICE_NAME_ALIASESCLIENT_NAME_TO_HYPHENIZED_SERVICE_ID_OVERRIDESがグローバルに定義されています。

SERVICE_NAME_ALIASESの内容はこんな感じ。

utils.py

SERVICE_NAME_ALIASES = {'runtime.sagemaker': 'sagemaker-runtime'}

詳細は把握できていないのですが、どうやらSageMakerはSDKで使うサービス名が一度変わっている様でそのためのエイリアスのようです。現時点ではsagemaker-runtimeを使う様なのであまり気にしないで良いでしょう。

続けてCLIENT_NAME_TO_HYPHENIZED_SERVICE_ID_OVERRIDESの定義はこんな感じです。

utils.py

CLIENT_NAME_TO_HYPHENIZED_SERVICE_ID_OVERRIDES = {
    # Actual service name we use -> Allowed computed service name.
    'alexaforbusiness': 'alexa-for-business',
    'apigateway': 'api-gateway',
    'application-autoscaling': 'application-auto-scaling',
    'appmesh': 'app-mesh',
    'autoscaling': 'auto-scaling',
    'autoscaling-plans': 'auto-scaling-plans',
    'ce': 'cost-explorer',
    'cloudhsmv2': 'cloudhsm-v2',
    'cloudsearchdomain': 'cloudsearch-domain',
    'cognito-idp': 'cognito-identity-provider',
    'config': 'config-service',
    'cur': 'cost-and-usage-report-service',
    'datapipeline': 'data-pipeline',
    'directconnect': 'direct-connect',
    'devicefarm': 'device-farm',
    'discovery': 'application-discovery-service',
    'dms': 'database-migration-service',
    'ds': 'directory-service',
    'dynamodbstreams': 'dynamodb-streams',
    'elasticbeanstalk': 'elastic-beanstalk',
    'elastictranscoder': 'elastic-transcoder',
    'elb': 'elastic-load-balancing',
    'elbv2': 'elastic-load-balancing-v2',
    'es': 'elasticsearch-service',
    'events': 'eventbridge',
    'globalaccelerator': 'global-accelerator',
    'iot-data': 'iot-data-plane',
    'iot-jobs-data': 'iot-jobs-data-plane',
    'iot1click-devices': 'iot-1click-devices-service',
    'iot1click-projects': 'iot-1click-projects',
    'iotevents-data': 'iot-events-data',
    'iotevents': 'iot-events',
    'iotwireless': 'iot-wireless',
    'kinesisanalytics': 'kinesis-analytics',
    'kinesisanalyticsv2': 'kinesis-analytics-v2',
    'kinesisvideo': 'kinesis-video',
    'lex-models': 'lex-model-building-service',
    'lexv2-models': 'lex-models-v2',
    'lex-runtime': 'lex-runtime-service',
    'lexv2-runtime': 'lex-runtime-v2',
    'logs': 'cloudwatch-logs',
    'machinelearning': 'machine-learning',
    'marketplacecommerceanalytics': 'marketplace-commerce-analytics',
    'marketplace-entitlement': 'marketplace-entitlement-service',
    'meteringmarketplace': 'marketplace-metering',
    'mgh': 'migration-hub',
    'sms-voice': 'pinpoint-sms-voice',
    'resourcegroupstaggingapi': 'resource-groups-tagging-api',
    'route53': 'route-53',
    'route53domains': 'route-53-domains',
    's3control': 's3-control',
    'sdb': 'simpledb',
    'secretsmanager': 'secrets-manager',
    'serverlessrepo': 'serverlessapplicationrepository',
    'servicecatalog': 'service-catalog',
    'servicecatalog-appregistry': 'service-catalog-appregistry',
    'stepfunctions': 'sfn',
    'storagegateway': 'storage-gateway',
}

こっちが今回のメインで、ここで列挙されているサービスについては独自の変換をした後の名前を使う様になっています。
ほとんどが長い名称をハイフン付きの形にするパターンですが、

  • elbv2elastic-load-balancing-v2
  • stepfunctionssfn
  • serverlessreposerverlessapplicationrepository

の様に略称から長い名称に変換するパターン、逆に略称にするパターン、さらにハイフン無しの名称にしてしまうパターンがあったりと、きれいな統一には至っていない感じです。
(何故こうなったのかは謎です...どうして...)

最終的に環境変数で使うサービス名としてはハイフンをアンダースコアに変換し大文字にしたものを採用しています。
たとえば先述の3サービスであれば、

  • elbv2AWS_ENDPOINT_URL_ELASTIC_LOAD_BALANCING_V2
  • stepfunctionsAWS_ENDPOINT_URL_SFN
  • serverlessrepoAWS_ENDPOINT_URL_SERVERLESSAPPLICATIONREPOSITORY

という環境変数になります。

CLIENT_NAME_TO_HYPHENIZED_SERVICE_ID_OVERRIDESの変換対象になっていないサービスの場合は、boto3のサービス名をそのままアンダースコア形式+大文字にしたものを使えばOKです。 簡単な例を挙げると、

  • s3AWS_ENDPOINT_URL_S3
  • ec2AWS_ENDPOINT_URL_EC2
  • license-managerAWS_ENDPOINT_URL_LICENSE_MANAGER

といった感じになります。

共有設定ファイルのキー名

次に共有設定ファイルの場合は_get_endpoint_url_config_serviceメソッドで設定値を取得しています。

メソッドの定義は以下の通りで環境変数の時と同様に_transformed_service_idの値をベースとし、こちらはアンダースコア形式+小文字にしたキー名を探索する様になっています。

    def _get_endpoint_url_config_service(self):
        snakecase_service_id = self._transformed_service_id.lower()
        return (
            self._get_services_config()
            .get(snakecase_service_id, {})
            .get('endpoint_url')
        )

このため、前節で例示したサービスの場合

  • elbv2elastic_load_balancing_v2
  • stepfunctionssfn
  • serverlessreposerverlessapplicationrepository
  • s3s3
  • ec2ec2
  • license-managerlicense_manager

というキー名を指定する形になります。   

~/.aws/config 設定例

[services custom-endpoint]
elastic_load_balancing_v2 = 
  endpoint_url = http://localhost:4567

[profile dev]
services = custom-endpoint

最後に

簡単ですが以上となります。

独特の変換をしているサービスがあるため「いずれはAWSのドキュメントでも一覧化してくれるのでは?」と期待しています。
困ったときは本記事の内容をベースに最新のbotocoreのソースを探索すると良いでしょう。

追記

本記事を公開した後で気が付いたのですが、一覧、ありました...

昨日は無かったような気がするのですが恐らくは単純に見逃してただけでしょう。