mcpField
A
aws-marketplace-metering

aws-marketplace-metering

github.com/awsクラウド・DevOps3.3k1.1k 回インストール1 日前に更新
リポジトリ

aws-marketplace-metering について

AWS MarketplaceのSaaS従量課金製品を対象に、計測連携の構築や調査を支援するスキルです。連携のセットアップと送信内容の分析を扱い、本番課金前のレビューとテストを求めます。

主な機能

  1. 01SaaS計測連携の構成
  2. 02計測送信の検索と分析
  3. 03デプロイと計測問題の調査
  4. 04本番前レビューとテストの必須化

活用シーン

  1. 01SaaS従量課金製品の計測デプロイ計画
  2. 02既存の送信内容や課金履歴の調査
  3. 03リリース前のテンプレートとコードのレビュー

MCP 対応

サンドボックス実行と監査ログのため、AWS MCP serverの利用を推奨しています。AWS MCP経由で読み込む場合、同梱ファイルは`retrieve_skill`で取得します。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/aws/agent-toolkit-for-aws --skill aws-marketplace-metering

インストール後、Claude Code で/aws-marketplace-meteringを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/
requirements.txt
template-events.yaml
template-main.yaml
references/
architecture.md
client-side-guardrails.md
concurrent-agreements.md
deployment-steps.md
dimension-reference.md
existing-sellers.md
iam-credentials.md
query-path.md
query-patterns.md
registration-page.md
scalable-metering-design.md
seller-reports.md
setup-path.md
test-plan.md
troubleshooting.md
scripts/
aggregator.py
cleanup.py
deploy.sh
deprovision_cleanup.py
discoverer.py
expiry.py
metering_core.py
query_metering.py
register.py
submitter.py
subscription.py
test_event_publisher.py

詳細

翻訳後に原文が更新されています。最新の内容は原文をご確認ください。

AWS Marketplace Metering

概要

AWS Marketplace の使用量ベースの計測をライフサイクル全体にわたって管理します。

  • セットアップ(SaaS PAYG) — 新しいサーバーレス計測統合(SAM、ResolveCustomer、BatchMeterUsage、EventBridge)をデプロイします。
  • クエリ — 既存の計測送信を検索、調査、デバッグ、分析します(CloudTrail Event History は最大 90 日間、請求履歴には Seller Reports を使用)。

対象は SaaS の使用量ベース(PAYG)です。SaaS Contract、Contract with Consumption Pricing(CCP)、AMI/Container は対象外です。AWS の公開ドキュメントを案内してください。

推奨: AWS MCP サーバーでは、サンドボックス化された実行と監査ログが利用できます。

⚠️ AI 生成の出力です。本番環境に適用する前に確認してください。このスキルは AI エージェントによって実行されるため、 誤りが含まれる可能性があります。 スキルが生成するすべてのもの(CloudFormation/SAM テンプレート、Lambda ハンドラー、 IAM ポリシー、deploy.sh)と、販売者の AWS アカウントにデプロイするすべてのものは、信頼できる成果物ではなく、 レビューが必要な提案として扱う必要があります。デプロイ済みのスタックで本番の計測トラフィック(実際の請求)を処理する前に、 販売者は必ず次のことを行ってください。

  • スキルが作成または変更したすべてのリソースを徹底的に確認します。テンプレート、Lambda コード、 IAM ロール/ポリシー(最小権限であること、意図しない許可がないこと)、テーブル/インデックス、キュー、アラームなどを 自身のアカウントで確認し、意図および組織の標準に合致していることを確かめてください。計測エラーは収益(過少請求または過剰請求)に 直接影響するため、このレビューは必須です。
  • 生成されたコードを、販売者が通常行っているコードレビューおよび変更管理プロセスに従って確認します (AI の出力をそのまま本番環境にリリースしないでください)。
  • 完全な実地テスト計画(references/test-plan.md)を最初から最後まで実施し、統合を実際の請求に使用する前に、 期待される各観測結果を確認してください。 このスキルは、本番環境への適用前にレビューが必要であることを販売者に明示しなければなりません。また、デプロイが成功しただけで このレビューとテストなしに統合の本番運用が可能であると示唆してはなりません。

このスキルに含まれるリソースの使用

同梱の scripts/、assets/、references/ は、計測スタックの構成方法を示す参照用であり、そのままコピーするための内容ではありません。

  • 販売者固有の値はすべて販売者から取得します(製品コード、リージョン、ステージ名、VMT タグ、アラームのアクション/しきい値、保持期間/KMS)し、TODO: マーカーを埋めてください。必須の値が不明な場合は、推測せずに質問してください。VMT は存在ベースです。販売者が各使用量行の usageAllocations(実際のタグ)を記述し、パイプライン(アグリゲーター)がそれらをマージします。パイプラインがタグを作り出すことはありません(VmtEnabled/VmtTagAttribute パラメーターはありません)。
  • アーキテクチャや awsmp-* の命名は変更しないでください(2 つのスタック構成、テーブル/GSI/イベントの配線は固定です)。販売者がカスタマイズできるのは、マークされた TODO: セクションのみです。アーキテクチャや命名を壊すリクエストには、黙って応じず、制約として説明してください。

ガードレール — このスキルのファイルの保存場所(MCP とローカル)

リファレンスを読む前、またはスクリプトを実行する前に、スキルの読み込み方法を確認してください。

  • AWS MCP retrieve_skill の場合: スキルはローカルファイルシステム上にありません。各リファレンス/スクリプトは file パラメーター(例:file="references/deployment-steps.md")を指定して retrieve_skill で取得してください。これらのパスに対して file_read を実行しないでください。
  • ローカルにインストールされている場合: ローカルのスキルディレクトリから相対パスを使ってファイルを読み取る/実行してください。

これはスキル自体のファイルにのみ適用されます。ユーザーデータ/セッション成果物は常にユーザーの作業ディレクトリにあります。

重要な事実(すべての計測操作に適用)

  • 新しい(Concurrent Agreements)製品では、UsageRecord ごとに CustomerAWSAccountId(CustomerIdentifier ではありません)と LicenseArn を使用します。
  • サブスクリプションイベントには EventBridge(SNS ではありません)を使用します。イベントは us-east-1 でのみ発行されます。
  • ResolveCustomer は、購入者が Marketplace コンソールから SaaS 登録サイトにリダイレクトされたとき、x-amzn-marketplace-token から購入者の LicenseArn、customerAWSAccountId、productCode を解決します。その後、SaaS は追加の登録データを収集して保存します(PII フィールドは リージョン内の customer-profile テーブルに保存され、PII を含まない subscribers テーブルには CA の ID、ステータス、registeredRegions のみが保存されます)。この API はトップレベルの LicenseArn フィールドを返します。CustomerIdentifier は非推奨のレガシーフィールドで、新しい SaaS 統合では null です(使用しないでください。また、その値を LicenseArn として扱わないでください)。これはリージョン別の API ですが、リージョンは販売者が自由に選択できます。購入者がソフトウェアを使用する場所と一致させる必要はなく、従量課金を制限するものでもありません。BatchMeterUsage は、ResolveCustomer を呼び出した場所に関係なく、サポートされている任意のリージョンで呼び出せます。これは us-east-1 専用の API ではありません(us-east-1 専用なのは License Updated などの EventBridge サブスクリプションイベントのみです)。SaaS 製品を公開したアカウントから呼び出す必要があります(そうでない場合、トークンは解決されません)。x-amzn-marketplace-token は一度限りのトークンではありません。有効期限(約 4 時間)までは再利用できます。期限前に同じトークンを再解決すると同じ結果が返るため、登録ページでエラーが発生した購入者は、約 4 時間の有効期間内であれば元のリンクから再試行できます。期限切れ後に ResolveCustomer を呼び出すと ExpiredTokenException が返されます(すでに使用済み、または無効なトークンの場合は InvalidTokenException が返されます)。購入者は「Set up your account」を再度クリックして新しいトークンを取得します(再サブスクリプションは不要です)。数日後に再試行すると、トークンは期限切れになっているため、新しいトークンを取得します(references/troubleshooting.md)。
  • DuplicateRecord は、最初の書き込みが優先され、結果として報告される状態です(Status = DuplicateRecord であり、Success ではありません)。元の数量が引き続き請求されます。数量を変更して再送信しても請求されません(請求不足のリスクがあります)。安全なのは同一内容の再試行だけです。数量を修正する方法として使用してはならず、「無害」と呼んではいけません。
  • CustomerNotSubscribed は実際の失敗であり、再試行してはいけません。原因:(1)識別子が無効、(2)契約が終了または期限切れ、(3)購入者アカウントが停止中。
  • 不正な LicenseArn はリクエストレベルの InvalidLicenseException を引き起こし、BatchMeterUsage バッチ全体を失敗させます。Results 内のレコードごとの Status ではありません(CustomerNotSubscribed とは異なります)。対策:バッチ処理前にクライアント側で LicenseArn の形式を検証し(理由コード InvalidLicenseArn)、不正な ARN がバッチに入らないようにします。また、リクエストレベルの ClientError が発生した場合は、二分探索して再試行し、問題のレコードを特定します。これにより、同じバッチに含まれていた他のレコードは引き続き従量課金できます。
  • BatchMeterUsage(SaaS)はリージョン別ですが、リージョンは販売者が選択します。InvalidEndpointRegionException は発生しません。 使用量が発生したリージョン(販売者が選択。オンプレミス/他クラウドの場合は最寄りのサポート対象リージョン)で呼び出します。従量課金対象のリージョンごとにスタックをデプロイします。重複排除はリージョンごとに行われます。大半の商用リージョンでサポートされていますが、すべてではありません。選択したリージョンで API が利用できない場合、呼び出しは DisabledApiException を返します(代わりにサポート対象リージョンで従量課金してください)。SaaS の BatchMeterUsage で InvalidEndpointRegionException が発生することはありません。販売者がリージョンを選択し、リージョンごとの重複排除によってマルチリージョンの従量課金が正しく行われるため、エラーにはなりません。
    • AMI/Container の MeterUsage と混同しないでください。 従量課金制の AMI/Container 製品は、このスキルの対象外です(公開ドキュメントを案内してください)。これらのリージョンルールは逆です。MeterUsage は実行中の EC2 インスタンス/ECS タスクと同じリージョンで呼び出す必要があります。 インスタンスの実行場所にかかわらず、us-east-1(または他のリージョン)から呼び出すことはできません。リージョンが一致しないと InvalidEndpointRegionException が返されます。このような製品は、IAM インスタンス/タスクロール(EC2 インスタンスプロファイル、ECS タスクロール、または EKS IRSA)で認証します。埋め込み型や長期有効のアクセスキーは使用しません。同一リージョンでの強制と InvalidEndpointRegionException は、AMI/Container の MeterUsage にのみ適用され、SaaS の BatchMeterUsage には適用されません。AMI/Container の従量課金については、AWS の公開ドキュメントを参照してください。
  • ディメンションの有効性はサーバー側で決定されます。 パイプラインにディメンション一覧は設定されず、クライアント側で拒否されることもありません。未定義のディメンションは、リクエストレベルの InvalidUsageDimensionException を発生させて BatchMeterUsage 呼び出しを失敗させます(大文字と小文字を区別し、請求されません)。これは Results[].Status に記録されるレコードごとの結果ではありません。パイプラインは二分探索で該当レコードを分離します(深さは ⌈log2(batch size)⌉ = 5 に制限され、多数の失敗によって送信側に過剰な負荷がかからないようにします)。該当レコードを RejectedClientSide(理由 = 例外名)として記録し、BatchMeterUsageException メトリクスを出力します。カタログのディメンションを追加または名称変更しても、再デプロイは不要です。
  • 拒否理由コードにはクライアント側 ↔ サーバー側の対応関係があります。必ず両方を示してください。 行が拒否される方法は 2 つあり、評価基準ではそれぞれのコード名が求められます。(1)パイプラインのクライアント側ガードレールが API 呼び出し前に行を拒否し、クライアント側の理由コード(MalformedTimestamp、InvalidLicenseArn、TooManyTags、InvalidDimension など)を付与して、RejectedClientSide として記録し、UsageRecordRejected を出力します。(2)行が API に到達して拒否された場合、対応するサーバー側の結果は BatchMeterUsage のリクエストレベルの例外(TimestampOutOfBoundsException、InvalidLicenseException、TooManyTagsException、InvalidUsageDimensionException)です。拒否について説明する場合は、クライアント側の理由コードと、それに対応するサーバー側の例外の両方を挙げてください。どちらか一方だけではいけません。クライアント側の理由コード ↔ サーバー側の例外:MalformedTimestamp ↔ TimestampOutOfBoundsException、InvalidLicenseArn ↔ InvalidLicenseException、TooManyTags ↔ TooManyTagsException、InvalidDimension ↔ InvalidUsageDimensionException。
  • UnprocessedRecords は部分的なバッチ処理の結果であり、「呼び出しがレコードを受け付けた」という意味ではありません。 BatchMeterUsage リクエスト全体が失敗したわけではありません。成功したレコードは Results に、処理に失敗したレコードは UnprocessedRecords に含まれます(請求されず、通常は一時的な失敗です。例:ThrottlingException/InternalServiceException)。従量課金はべき等であるため、これらのレコードは安全に再送信できます(リージョンごとに CustomerAWSAccountId + LicenseArn + dimension + hour で最初の書き込みを優先して重複排除します)。デプロイ済みの送信側はレコードを1 回だけ再試行します。それでも失敗する場合は再処理対象として残され、次回のスケジュール実行(rate(5m))で取得されます。盲目的なクライアント側の再試行ループではありません。永続的なケースは UsageRecordUnprocessed でアラーム通知されます。これは、レコードごとの Results[].Status 判定(Success/CustomerNotSubscribed/DuplicateRecord。最終結果なので、盲目的に再試行してはいけません)や、リクエストレベルの例外(InvalidUsageDimensionException/TimestampOutOfBoundsException/InvalidLicenseException。バッチ全体が失敗するため、二分探索で問題のレコードを分離する必要があります)とは異なります。
  • CloudTrail はリージョン別です。 BatchMeterUsage を呼び出すリージョンと同じリージョンでクエリしてください。配信には約 15 分かかります。保持期間は90 日です(それより古いデータには Seller Reports を推奨します)。CloudTrail の Event History(初期設定で利用可能。セットアップ不要)を使用し、eventSource = metering-marketplace.amazonaws.com で絞り込んでください。従量課金の検索に CloudTrail Lake/create-event-data-store を使用してはいけません。
  • タイムスタンプの許容範囲は 24 時間(1 時間ではありません)で、現在時刻から計算されます。タイムスタンプが 24 時間より古いレコードは、許容範囲外として拒否され、送信されません。具体的には、30 時間前のレコードは 24 時間の境界を超えているため、許容範囲には入りません。サーバー側では TimestampOutOfBoundsException によって呼び出しが失敗し、タイムスタンプが不正なレコードが 1 件あるとバッチ全体が拒否されます。API に到達する前に、パイプラインのクライアント側ガードレールが理由コード MalformedTimestamp で行を拒否するため、古い/許容範囲外のタイムスタンプがバッチに入ることはありません(RejectedClientSide として記録され、請求されません)。24 時間を超える(例:30 時間前の)レコードが「許容範囲内」または「送信されるはず」と販売者に伝えてはいけません。例外は、月初の UTC 06:00 まで前月のレコードを受け付ける 6 時間の月境界猶予のみです。パイプラインが従量課金するのは、完了した時間のみです(now-23h … now-1h。現在進行中の時間は対象外で、プロビジョニング解除時のフラッシュのみが対象です)。また、MeteringLockHours によって制御されます。時間単位のディスカバラーや rate(5m) の送信側がスキップ/失敗しても、その時間が 24 時間の許容範囲内であれば、後続の実行で自動的に復旧します。詳細は references/scalable-metering-design.md を参照してください。
    • ベストプラクティス — 使用量の生成後、約 1 時間以内に送信してください。 送信の遅れは、実環境での大きな失敗要因です。レコードが 24 時間の境界に近づくほど、再試行できる余裕が少なくなり、24 時間を過ぎると完全に失われます(TimestampOutOfBounds が発生し、請求されません)。1 日分の使用量を境界ぎりぎりでまとめて送るのではなく、許容範囲内に十分余裕を持って、使用量を速やかに書き込み、送信してください。
    • 許容期間切れは終端状態であり、保留マーカーをクリアします。 行/集計データが 24 時間の許容範囲を超える前に従量課金できない場合、パイプラインはそのデータの期限を切らします。終端の meteringStatus(集計されなかった生の行には AggregationExpired、集計済みだが未送信の行には SubmissionExpired)を設定し、meteringPending をクリアします。これにより、行は疎な metering_pending GSI から外れ、ディスカバラーの古い順の逆方向走査から返されなくなります(無限に再処理されることを防ぎます)。これらの状態は UsageAggregationExpired/UsageSubmissionExpired アラームを発生させます。これは使用量が気づかれないまま従量課金されなかったことを示すページャー通知なので、書き込み側/パイプラインの遅延を調査してください。
    • タイムスタンプが約 1 時間前であれば、通常 TimestampOutOfBoundsException が発生することはありません。 1 時間は 24 時間の参照範囲内であり、このエラーは通常ではありません。販売者に「HH:05 まで待つだけ」と伝えてはいけません。実際の原因は、ほとんどの場合、(a)1 件の不正なレコードがバッチ全体を失敗させている(同じバッチに含まれる不正な形式/許容範囲外/未定義ディメンションのレコードが、分離されるまで呼び出し内のすべてのレコードを拒否する)、または(b)時計のずれ(同期されていないホストが未来/大幅に過去のタイムスタンプを設定している、または UTC ではなくローカル時刻を使用している)です。対処方法:バッチを調べて不正なレコードを特定し(二分探索で分離してください)、書き込み側の時計が NTP 同期され、秒単位の UTC を設定していることを確認してください。
  • 現在進行中の時間は、通常の処理では決して従量課金されません。 パイプラインが送信するのは完了した時間のみです(now-23h … now-1h)。現在進行中の時間は完了するまで開いたままになり、スキップされます。MeteringLockHours(デフォルトは 1、設定可能な範囲は 1~20)は、時間単位の使用量が遅れて届く可能性を考慮して、その時間が集計/送信されるまで開いたままにする時間数です。デフォルトが 1 の場合、パイプラインは直前に完了した時間を処理します。保留中の時間バケットは古いものから順に処理されるため、使用量は 24 時間の境界に近づいて期限切れ(TimestampOutOfBounds)になる前に送信されます。アグリゲーターは各 (licenseArn, customerAWSAccountId, dimension, hour) グループを、1 時間あたり正確に 1 件の BatchMeterUsage レコードにまとめます(従量課金はグループ/時間単位で行われ、最初の書き込みが優先されます)。現在進行中の時間が従量課金されるのは、プロビジョニング解除時の最終使用量フラッシュのみです(ロックを迂回します。イベントモデルを参照)。したがって、「この時間の使用量がまだ表示されないのはなぜですか?」という場合は、まだ現在進行中の時間であるか、MeteringLockHours の期間内であることが理由です。これは想定どおりであり、バグではありません。
  • BatchMeterUsage の上限は互いに独立した 2 つの制限です。UsageRecords は 25 件以下、リクエストペイロードは 1 MB 以下です。 有効なレコードが 25 件あるバッチでも、シリアル化後のリクエストが 1 MB を超えるとサイズ超過で拒否されることがあります(usageAllocations のタグセットが多い/大きい、ARN が長い場合など)。制限となるのはレコード数ではなくバイト数です。対処方法:1 回の呼び出しに含めるレコード数を減らし、各リクエストが 1 MB を下回るようにします(送信側はすでに 1 回あたり 25 件以下にまとめます。サイズ超過で拒否された場合はレコードが大きすぎるため、さらに分割してください)。
  • VMT(ベンダー従量課金タグ付け)のタグ上限は、UsageRecord あたり usageAllocations タグ 5 個です。 使用量の行に含められる usageAllocations タグは最大 5 個です。5 個を超えると無効です。パイプラインのクライアント側ガードレールは、タグ数が上限を超えた行を API 呼び出し前に理由コード TooManyTags で拒否します(RejectedClientSide として記録され、請求されず、UsageRecordRejected を出力します)。この行が API に到達した場合のサーバー側の対応する例外は TooManyTagsException です。VMT のタグ上限について尋ねられた場合は、5 個という上限と、クライアント側の拒否 TooManyTags の両方を挙げてください(数だけでは不十分です)。VMT はタグの有無に基づきます。タグを提供するのは販売者であり、パイプラインがタグを生成することはありません。
  • VMT はグループごとに全件適用か全件なしのいずれかです。タグ付きの行とタグなしの行を混在させてはいけません**。1 つの (licenseArn, customerAWSAccountId, dimension, hour) グループ内では、ゼロ以外の行はすべて usageAllocations を持つか、どの行も持たないかのどちらかにします。割り当て済みの行と未割り当ての行を、同じグループ内のゼロ以外の行として混在させることはできません。パイプラインは理由コード MixedAllocatedAndUnallocated でクライアント側から拒否します(RejectedClientSide として記録され、請求されません)。これは「部分的な VMT 帰属」ではありません。数量がゼロの行はこの検証では無視され、同じ時間/バッチ内の異なるグループでは、割り当てを含めるかどうかを個別に選択できます。また、グループごとに、各行の AllocatedUsageQuantity の値の合計はその行の quantity と一致し、1 件の UsageRecord あたりの結合後のタグセットは 2500 個以下でなければなりません(それ以外の場合は AllocationSumMismatch/TooManyAllocations)。詳細は references/setup-path.md、references/client-side-guardrails.md を参照してください。
  • 従量課金はべき等です(リージョンごとに CustomerAWSAccountId + LicenseArn + dimension + hour で最初の書き込みを優先して重複排除されます)。また、パイプラインは分離されています。単一の従量課金 Lambda は存在せず、「予約済み同時実行数 = 2」という設定もありません。 送信側(BatchMeterUsage を呼び出す唯一のステージ)は、ReservedConcurrentExecutions=1 で実行される単一の直列送信側です。BatchMeterUsage のリクエストレート上限(アカウント/リージョンあたり毎秒 10 件という文書化されたクォータ。MeterUsage は毎秒 10 件、RegisterUsage は毎秒 5 件。詳細はサービスクォータを参照)を守り、ThrottlingException を避けるため、意図的に直列化されています。ディスカバラーの予約済み同時実行数は 30 です。アグリゲーターとクリーンアップは、SQS のイベントソースマッピングにある MaximumConcurrency によってスケールします。バーストは SQS が吸収します。スループットを高めるには、送信側の同時実行数を上げるのではなく、キューを処理して減らします。販売者がより高い持続的な従量課金 TPS を必要とする場合は、安全なレベルを超えて同時実行数を増やすのではなく、AWS Marketplace Seller Operations に連絡してください。
    • ThrottlingException の処理 — ジッターを加えた指数バックオフを使用します。 ThrottlingException(またはスロットリングされた UnprocessedRecords)が発生した場合は、ジッターを加えた指数バックオフ(ランダムな遅延。ジッターを明示してください)で再試行します。固定または同期された再試行間隔は再衝突を引き起こすため、使用してはいけません。各呼び出しを 25 件以下(かつ 1 MB 以下)に保ち、「追いつく」ためにバッチを大きくしないでください。従量課金はべき等(最初の書き込みが優先)なので、再試行は安全です。すでに請求済みのレコードを再送信すると、元の数量が引き続き請求されたまま、単に DuplicateRecord が返ります。スロットリングされた/未処理のレコードを再試行しない理由として DuplicateRecord を扱ってはいけません。DuplicateRecord は再試行を安全にするべき等性の保証です。唯一の注意点は、同じグループを異なる数量で再送信しないことです。その場合、請求されません。
  • 登録フォームのデータ(PII)は、subscribers テーブルではなく、リージョンごとの customer-profile テーブルに保存されます。 リージョン内の登録 Lambda は、許可リストにある登録/KYB フィールドを、Lambda が実行されるリージョン内の awsmp-<productCode>-customer-profile(PK は licenseArn、SK は customerAWSAccountId、GSIs は販売者の要望に応じて設定)に書き込みます。購入者の PII はリージョン内に保持されます。登録済みの購入者については、追加のフォームフィールドがゼロであっても、プロフィール行は必ず upsert されます(ID をキーとする行は常に書き込まれます)。統合 subscribers テーブル(us-east-1)は PII を含みません。登録 Lambda が書き込むのは、非 PII の productCode と、冪等な registeredRegions の追加(登録フォームデータは書き込みません)に加えて、CA の ID(licenseArn、customerAWSAccountId、agreementId)と 2 つのライフサイクルステータスのみです。そのため、オプトイン型リージョンの製品でも us-east-1 に安全に配置できます。テンプレートでは、登録 Lambda の CUSTOMER_PROFILE_TABLE 環境変数を必ず設定します。未設定の場合は構成ミスであり、ハンドラーは PII の書き込みを黙って省略せず、エラーを明示して(HTTP 500)失敗します。(既存の販売者独自の登録ハンドラーが異なる方法でデータを保存している場合、スキルはそれに適応し、この参照 Lambda とまったく同じ実装を強制することはありません。)
  • レガシー(CA 導入前)の統合では、LicenseArn の代わりに ProductCode と CustomerIdentifier を使用する場合があります。このスキルでは、独自のテンプレート/フォールバックを強制しません(startswith("arn:") による固定のフォールバックをテンプレートとして提示してはいけません)。レガシーの動作については、販売者の既存の統合と AWS の公開ドキュメント(references/existing-sellers.md)に基づいて説明します。
  • 無料トライアル — 動作はモデルによって異なります。「常に従量課金する」と一律に回答せず、まずモデルを確認し、詳細は公開ドキュメント(SaaS の無料トライアル、PLG ガイド)を根拠にしてください。
    • AWS Marketplace SaaS の無料トライアル機能(ネイティブの期間限定トライアル。購入者は別のトライアルオファーをサブスクライブし、終了前にアップグレードする必要があります):期間中は通常どおり従量課金します。使用量を保留してはいけません。AWS がトライアル価格を適用するため、トライアル中の使用量は請求額 0 ドル(推定収益は $0)となり、コンバージョン後も同じディメンションに統合変更なしで請求できます。
    • 組み込みの無料枠/使用量ベースのトライアル(購入者は標準の有料オファーを利用し、課金をいつ開始するかは販売者が決定):従量課金するタイミング/内容は販売者独自の価格設定上の判断であり、このスキルが従量課金の方針を規定することはありません。通常の使用量ベース(PAYG)では、デフォルトとして請求対象の使用量をすべて従量課金し、無料枠は価格設定/オファーで処理します。契約に含まれる数量を超えた分のみを請求するのは **Contract with Consumption(CCP)**であり、このスキルの対象外です(下記を参照)。→ 公開ドキュメント
  • 使用量データの取り込みは販売者の責任です。 販売者は使用量テーブルに行を書き込みます(マルチアカウントの場合はアカウント横断の収集も含みます)。デフォルトのフルモードでは、パイプラインが時間単位で集計します。ダイレクト送信モードでは、販売者が確定済みの時間単位の合計値を提供します(Q9 を参照)。
  • すべてのタイムスタンプは UTC です。 販売者はソートキーのタイムスタンプと meteringPending の両方を UTC で設定します。GSI は UTC の時間バケットでクエリされるため、現地時刻で書き込むと、行が誤った場所に保存され、気づかないまま誤った従量課金が行われます。
  • 監査タイムスタンプ(createdAt/updatedAt) — パイプラインが書き込むすべての行には、createdAt(書き込み時に一度だけ設定し、if_not_exists を使用)と updatedAt(書き込みごとに更新)が含まれます。共有スタンプヘルパーを使用して、ISO-8601 形式の秒単位の UTC タイムスタンプが一律に適用されます。これらは監査専用のメタデータであり、従量課金の判定条件にはなりません。従量課金の判定は、createdAt/updatedAt ではなく、subscriptionStatus/metering_pending GSI に基づきます。

Concurrent Agreements(CA)— 登録と計測のモデル(CA の仕組みを尋ねられたときに説明してください)

新しい SaaS 製品はすべて Concurrent Agreements を使用します。ID は CustomerAWSAccountId + LicenseArn です(従来の CustomerIdentifier は使用しません)。CA の登録、イベントの順序付け、アトリビューション、検索について質問された場合は、これらの事実を必ず明示してください。

  • 登録からサブスクライバーのアップサートへの処理は、EventBridge の License Updated イベントによって実行されます。 購入者がサブスクライブすると、AWS Marketplace は License Updated を発行します(us-east-1 のみ)。events スタックのサブスクリプション Lambda がこのイベントを処理し、licenseArn と customerAWSAccountId(および agreementId)をキーにサブスクライバーレコードを UPSERT して、agreementStatus=active および subscriptionStatus=active を設定します。これにより、購入者は使用量の計測対象になります。ブランド登録ページから呼び出される ResolveCustomer は、トークンを LicenseArn、customerAWSAccountId、productCode に解決し、リージョン内の顧客プロファイルテーブルに登録情報/PIIを保存します。ただし、計測対象となるサブスクライバー行を確立するのは License Updated によるアップサート(agreementStatus=active) です。License Updated → agreementStatus=active のアップサートに触れずに、CA 登録について説明しないでください。
  • ResolveCustomer は LicenseArn を返します。答えは「はい」です。 ResolveCustomer は x-amzn-marketplace-token を解決し、customerAWSAccountId および productCode とともに、トップレベルの LicenseArn フィールドを返します。ResolveCustomer は LicenseArn を返さないと言ってはいけません。レスポンスのトップレベルで返します。CustomerIdentifier は非推奨のレガシーフィールドであり、新しい CA 統合では null です。これを使ったり、その値を LicenseArn として扱ったりしないでください。(新しい SaaS 製品では、ResolveCustomer が返す LicenseArn が保存する識別子であり、後で BatchMeterUsage に渡します。)
  • 適格性は、イベントの到着順序とは無関係に BatchMeterUsage で適用されます。 計測対象かどうかは、送信時に BatchMeterUsage が現在の契約/サブスクリプション状態に基づいてサーバー側で確認します。EventBridge イベントの到着順序には依存しません。サブスクライバーが有効になる前(または契約終了後)に使用量を送信すると、そのレコードは CustomerNotSubscribed を返します。License Updated によって購入者が有効になれば、同じレコードが計測されます。したがって、イベントの順序が前後したり、到着が遅れたりしても、請求データは破損しません。判定の基準は BatchMeterUsage であり、24 時間のウィンドウ内であれば、後続の rate(5m) 実行時にその行は自動的に回復します。パイプラインがイベントの順序に基づいて計測を制御すると主張しないでください。制御するのは送信時の適格性です。
  • 計測はアカウント単位ではなくライセンス単位で紐付けられます。使用量テーブルのキー設計を強調してください。生の使用量テーブルは PK licenseArn、SK customerAWSAccountId#dimension#timestamp です。1 つの AWS アカウント(customerAWSAccountId)が複数の契約/ライセンスを保有する場合があり、重複排除と請求は CustomerAWSAccountId + LicenseArn + dimension + hour の単位で行われます。つまり、使用量は licenseArn ごと に集計・計測され、アカウント単位の合計にまとめられることはありません。「アカウント単位とライセンス単位のどちらで計測するのか」と聞かれたら、答えは ライセンス単位 です(パーティションキーは licenseArn)。
  • 計測を実行するのは metering_pending GSI です。サブスクライバーズテーブルはライフサイクル専用です。 計測対象の検出元は、使用量テーブル/集計テーブルにあるスパースな metering_pending GSI のみです(HASH = meteringPending 時間バケット、RANGE = licenseArn、customerAWSAccountId と dimension を射影)。検出処理は GSI を ページネーション します。単一の読み取りではなく、ページごとに LastEvaluatedKey をたどる Query を実行するため、保留中のグループが 1 ページ分を超える時間帯も、すべて処理されます。使用量を計測する対象の検索に、サブスクライバーズテーブルは参照されません。 このテーブルには CA の識別情報と 2 つのライフサイクル状態(agreementStatus/subscriptionStatus)のみを保持し、ライフサイクル/プロビジョニング解除(このライセンスは有効か、プロビジョニング解除時のフラッシュ処理に入ったか)のために使用します。保留中の使用量を探すためにパイプラインがサブスクライバーズテーブルをスキャンまたはクエリすると説明しないでください。それを担うのは GSI です。
    • 統合サブスクライバーズテーブルのスキーマ(us-east-1、PII なし): PK licenseArn、SK customerAWSAccountId。属性は agreementId、agreementStatus、subscriptionStatus、productCode、registeredRegions、deprovisioningExpiry(プロビジョニング解除時のフラッシュ中にのみ設定)、および監査用タイムスタンプの createdAt/updatedAt です。registeredRegions は 冪等に 更新され(ADD で追加し、重複なし)、参照専用であり、計測の条件には決してなりません。登録フォームのデータ/PIIは 一切保持しません(PII はリージョン内の customer-profile テーブルにのみ保存されます)。アカウント単位の検索には customerAWSAccountId GSI を使用します。licenseArn が PK である理由は 3 つすべてです。(1)同時契約がある場合、1 人の購入者が同じ製品の複数のライセンスを同時に保有する可能性があるため、(2)customerAWSAccountId を PK にすると、異なる契約が 1 行に まとめられ(使用量の欠落/二重請求につながる)ため、(3)各ライセンスは独立したライフサイクルと計測の冪等性を持つため(重複排除は CustomerAWSAccountId + LicenseArn + dimension + hour 単位で行われます)、契約ごとにキーを分けることで互いに分離できるためです。licenseArn がパーティションキーである理由を尋ねられたら、3 つすべてを挙げてください。
  • 1 回の BatchMeterUsage 呼び出しにつき製品は 1 つです。1 つのバッチに複数の製品を混在させないでください。 1 つの BatchMeterUsage リクエスト内のすべての UsageRecord(すべての LicenseArn)は、同じ製品に属している必要があります。異なる 2 つの製品の LicenseArn を 1 回の呼び出しに混在させると、リクエスト単位の InvalidLicenseException が返され、リクエスト全体が失敗します(何も計測されません)。製品ごとに別々の BatchMeterUsage 呼び出しを行ってください。1 つの製品だけを含むバッチ内では、異なるディメンションや、その製品の異なる購入者/ライセンスを組み合わせてもかまいません。重複排除は CustomerAWSAccountId + LicenseArn + dimension + hour 単位で行われ、バッチには ≤25 レコードおよび ≤1 MB という上限が適用されます。(デプロイ済みの送信処理はすでに製品ごとにグループ化するため、異なる製品が同じバッチに含まれることはありません。手動でバッチを作成する販売者も同様にしてください。)
  • バッチ上限は ≤25 個の UsageRecord および ≤1 MB(それぞれ独立した 2 つの制限)です。Critical Facts の項目を参照してください。25 レコードのバッチでも、ペイロードサイズが 1 MB を超えると拒否されることがあります。そのため、N 個のライセンス × M 個のディメンション = N·M 件のレコードの場合、⌈N·M/25⌉ 回の逐次呼び出しが必要です(例:50 個のライセンス × 2 個のディメンション = 100 件のレコード ⇒ 4 回の呼び出しを逐次送信)。再試行の動作: UnprocessedRecords に返されたレコード(一時的な ThrottlingException/InternalServiceException が原因で、課金されていないもの)は 再試行されます。デプロイ済みの送信処理は 1 回再試行した後、次回のスケジュール実行時に再度処理します。これは計測が冪等(最初の書き込みが優先)であるためです。これらを破棄しないでください。
  • events スタック(EventBridge とサブスクライバーズテーブル)は us-east-1 のみです。計測(main)スタックは別のリージョンに配置できます。 Marketplace のサブスクリプションイベント(License Updated、License Deprovisioned など)は us-east-1 でのみ発行されるため、共通の events スタックと、その PII を含まないサブスクライバーズテーブルは us-east-1 に固定されます(サブスクリプションを処理する Lambda と同じリージョンに配置)。一方、リージョンごとの main/計測スタック(使用量テーブル、パイプライン Lambda、BatchMeterUsage)は、販売者が計測を行う任意のリージョンにデプロイされます。us-east-1 に固定されるのは events と subscribers のスタックのみです。計測リージョンが us-east-1 と異なる場合、パイプラインはクロスリージョンでサブスクライバーズテーブルを読み取ります。販売者も us-east-1 で計測する場合は、すべてが単一リージョン内に収まります。

回答ルール — 必須

  1. まず対象外かどうかを確認してください — 対象外の場合は、1文で対応できない旨を伝えてください。先へ進まないでください。
  2. ナレッジに関する質問(方法、X の仕組み、データソースなど)には、このスキルに基づいて回答してください。AWS API やツールは呼び出さないでください。
  3. アクションに関する質問(今すぐデプロイ、今すぐクエリを実行、これを実行など)では、AWS コマンドを実行してください — 長時間かかる操作には、MCP use_aws より shell ツールと AWS CLI を優先してください。
  4. 複数リージョン/複数製品に関する質問はナレッジに関する質問です — アーキテクチャを説明してください。明示的に依頼されない限り、デプロイしないでください。
  5. 「X の見つけ方を教えて」はナレッジに関する質問です — 方法を説明してください。「見せて」/「実行して」と言われた場合にのみ実行してください。

対象外 — 直ちに対応を断る

"This is outside the scope of AWS Marketplace metering" + 誘導先を1行だけ返答してください。ツールを呼び出したり、リファレンスを読み込んだりしないでください。

  • 物理的なフルフィルメント / FBA / 小売店舗 → 「Try Amazon Seller Central」
  • 個人の AWS 請求 / コスト管理 → 「Try AWS Budgets or Cost Explorer」
  • 商品リスティング/オファーの作成、Private Offers、オファー/割引の管理 → 「Try AWS Marketplace Management Portal」
  • 価格設定、契約、EULA の管理 → 「Try Marketplace Catalog API docs」
  • SaaS Contract / 従量課金制契約(CCP)— 含まれるユニット、超過課金、AND 契約ライフサイクル(契約期間、更新、失効) — この従量課金制(PAYG)スキルの対象外です。これには両方が含まれます:(a) N ユニット/期間を含み、それを超える使用量に課金する契約(例:「月あたり 500 API コール込み、購入者が 750 使用 → 250 の超過」)。これは CCP / SaaS Contract + GetEntitlement の後に BatchMeterUsage で超過分を処理する形です。AND (b) 契約期間や更新に関するあらゆる質問(例:「顧客の年間契約が来週更新されるのですが、従量制の設定を変更すべきですか?」)。契約期間/更新は SaaS Contract(エンタイトルメント)の関心事であり、PAYG 従量制の関心事ではありません。これらを従量制の知識から回答しないでください。特に、更新によって License Updated が再度発火するだけである、従量制の変更は不要であると販売者に伝えたり、CCP の超過フローを説明したり、エンタイトルメントチェックについて説明したりしないでください。このスキルは契約/エンタイトルメントの動作を所有しておらず、契約の更新が従量制イベントにどのように対応するかを断言してはなりません。次のリダイレクトのみを返してください:「SaaS Contract / 従量課金制契約 — 契約期間、更新、含まれるユニット、超過を含む — は、この従量課金制(PAYG)従量制スキルの対象外です。公開されている AWS ドキュメントをご利用ください:契約価格設定、従量課金制を伴う契約価格設定(CCP)、SaaS 契約の統合、GetEntitlements によるエンタイトルメントと更新。」
  • 使用量の従量制を伴わない定額制 / SaaS Subscription 製品 — 顧客がまだアクティブ/エンタイトルメントを保持しているかの確認(例:「定額の月額サブスクリプションで、従量制はありません。顧客がまだアクティブかどうかを確認するにはどうすればよいですか?」):これはサブスクリプションのエンタイトルメント管理(SaaS Subscriptions + GetEntitlements / エンタイトルメントの EventBridge イベント)であり、従量課金制(PAYG)の従量制ではありません。これは対象外です — アクティブな顧客のチェックを設計したり、GetEntitlements/エンタイトルメントイベントの処理を説明したり、このスキルの従量制サブスクライバーテーブルを転用したりしないでください。次のリダイレクトのみを返してください:「使用量の従量制を伴わない定額制の SaaS Subscription は、この従量課金制(PAYG)従量制スキルの対象外です。アクティブ/エンタイトルメントを持つ顧客の確認は、AWS Marketplace の SaaS エンタイトルメントを通じて行われます。公開されている AWS ドキュメントをご覧ください:SaaS サブスクリプション、GetEntitlements によるエンタイトルメントの確認、EventBridge によるサブスクリプションのライフサイクルイベント。」
  • 将来の請求額や使用量の予測/予想(例:「CloudTrail の従量制トレンドから来月の顧客請求額を予測する」、トレンド予測、収益予測):対象外です — このスキルは履歴の送信データ(90 日以内の CloudTrail)と請求済み履歴(Seller Reports)のみをクエリします。予測は行いません → 「将来の請求額/使用量の予測は対象外です。このスキルは履歴の従量制送信データのみをクエリします。予測には、取得した CloudTrail/Seller-Reports のデータをホストエージェント/モデルに渡して分析させるか、AWS コストの予測や Seller Reports に対する独自の分析をご利用ください。」
  • AWS アカウントの請求に関する紛争 → 「Contact AWS Support」
  • BatchMeterUsage、ResolveCustomer、従量制レコード、または SaaS/ソフトウェアの従量課金制請求に関与しないものすべて。

セットアップ手順 — 新しい計測連携のデプロイ

完全な手順:references/setup-path.md + references/deployment-steps.md。

販売元への質問事項(コードを生成する前にすべて質問してください)
  • Q1 プロダクトコード(またはプロダクト ID)。 どちらでも受け付けます。検証時に Catalog API(describe-entity)を使用してプロダクト ID をコードに解決します。ディメンション一覧はデプロイ入力ではありません(ディメンションの妥当性はサーバー側が正となります)。セラーの参考情報としてのみディメンションを列挙してかまいません。セラーが使用量行に組み込む前に、Marketplace Catalog API の DescribeEntity(us-east-1)を使用してディメンション名(NAMES)をカタログに対して検証することを推奨します — 返されるエンティティ詳細には、そのプロダクトに登録されたディメンションの API 名(大文字と小文字を区別)が列挙されています。セットアップ時にセラーのディメンション文字列がカタログと完全に一致することを確認することが、実行時の InvalidUsageDimensionException(未定義・スペルミス・大文字小文字の誤りのディメンションは、BatchMeterUsage 呼び出し全体を失敗させます)を回避する方法です。これはあくまで検証の補助であり、ディメンション一覧をデプロイ入力にするものではありません。
  • Q2 あなたは ISV/セラーですか? 統合できるのはセラー(パブリッシャー)または認可された 3PI のみです。3PI は対象外です — 辞退してください。パブリッシングアカウントへのアクセスを確認してください。
  • Q3 どのリージョンから計測しますか? Events スタックは常に us-east-1 です。使用量が発生する各リージョンにメインスタックをデプロイし、そのリージョン内で計測してください。重複排除は CustomerAWSAccountId + LicenseArn + dimension + hour ごとで、送信先リージョンにスコープされます — すべての使用量を 1 つのリージョンに集約しないでください。リージョンサポート。
  • Q4 ステージ名(必須)。 API Gateway のステージ(例: v1/live/prod — セラーの選択)。デフォルトはなく、prod へのフォールバックもありません。空の場合はデプロイが即座に失敗します。STAGE_NAME で渡します。
  • Q5 このプロダクト用のスタックはすでに存在しますか? はいの場合 → 変更/拡張します。何かを変更する前に、コンテキストを収集しセラーの既存スタック(任意の命名が使われている可能性があります)を調査してください — references/existing-sellers.md を参照してください。
  • Q6 計測ロック時間(METERING_LOCK_HOURS → MeteringLockHours、デフォルト 1、1..20 で検証)。遅延使用量のために、ある時間帯が集計/送信される前にどれだけ開いたままになるかを示します(デフォルト 1 = ちょうど完了した時間帯を処理)。上限では、ある時間帯は age 20 で最初に送信され、24 時間の課金可能ウィンドウにリトライ/再ドライブ用に 4 時間以上が残ります。月を考慮: 前月の時間帯は 1 日 03:00 UTC までロックを維持し、その後 06:00 UTC のカットオフまでロックなし(各実行で送信)となり、それまで age out されません。03:00 以降に書き込まれる前月のレコードは、すでに事前集計されている必要があります(そうでない場合は DuplicateRecord)。
  • Q7 TTL/保持(テーブルごと、deploy.sh の環境変数、0 で無効化):
    • 生使用量テーブル USAGE_TABLE_TTL_DAYS(デフォルト 365) — 有効化を推奨(高ボリュームテーブル)。セラーのライターが ttl epoch 属性を設定し、テンプレートは DynamoDB にそれを尊重させるだけです。下限は 2 日以上で、MeteringLockHours+24h を超える必要があり、行が計測前に期限切れにならないようにします。
    • 集計テーブル AGGREGATED_USAGE_TTL_DAYS(デフォルト 0 = 保持) — 省略/大きな値を推奨(小規模な課金監査証跡)。0 より大きい場合、サブミッターは Success 行にのみ ttl を設定します。
    • TTL を有効化するにはデプロイヤーの dynamodb:UpdateTimeToLive が必要です(すでにポリシーに含まれています)。
  • Q8 ヘルスダッシュボードを作成しますか?(CREATE_DASHBOARD、デフォルト true)。プロダクトごとのメインスタックダッシュボード(Lambda ごとの Errors/Throttles/Invocations/Duration、キュー/DLQ の深さ + 最古 age、EMF の理由別 UsageRecordRejected、UsageRecordUnprocessed、CustomerNotSubscribed、例外別 BatchMeterUsageException、UsageAggregationExpired、UsageSubmissionExpired、DuplicateRecord)+ 共有 events スタックダッシュボード。ヘルス専用で、デプロイされたモード(Q9)を反映します。セラーはオプトアウト/カスタマイズできます。
  • Q9 フルパイプライン vs 直接送信(DEPLOYMENT_MODE、デフォルト full) — セラーが納得して選択できるよう、両方を説明してください:
    • full — セラーは秒単位の RAW イベントを出力します。このスキルは生の usage テーブル + discoverer→aggregator→cleanup→submitter→submission-expiry をデプロイし、パイプラインが各 (licenseArn, account, dimension, hour) グループを 1 時間あたり 1 つの BatchMeterUsage レコードに集計します。セラーがすでに集計していない場合や、プラットフォーム所有の集計 + クライアント側の拒否可視性を求める場合に最適です。
    • direct-submit — セラーがすでに確定した時間別合計を生成しています。このスキルは aggregated_usage + submitter + submission-expiry + register/subscription のみをデプロイします(より小さく安価)。セラーは確定レコードを aggregated_usage に直接書き込みます(PK licenseArn、SK customerAWSAccountId#dimension#hour、quantity、meteringPending=hour バケット)。
    • 挿入前の確定(direct-submit、重要): aggregated_usage 内の任意のレコードは、次の 5 分実行で送信される可能性があります — そのグループに対して確定(FINAL)したときのみレコードを書き込んでください。2 回目の書き込みは DuplicateRecord を返し、後の quantity は課金されないためです。ここでは上流の集計/冪等性はセラーの責任です。
    • 一方通行ではありません: セラーは後でモードを切り替えられます(モード固有のリソースを追加/削除するスタック UPDATE。生の usage テーブルは Retain)。稼働中のスタックを切り替える前に、影響範囲(ブラスト半径)を明示してください。
  • Q10 登録フィールド + プロファイル参照(ALLOWED_REGISTRATION_FIELDS) — まず説明してから質問します。リージョン内の register Lambda は、これらの許可リストされたフィールドのみ(それぞれ長さに制限あり。トークンは決して保存されません)を、リージョン内の customer-profile テーブル(PK licenseArn、SK customerAWSAccountId)上の registrationData マップとして永続化します — 購入者の PII はリージョン内に留まり、PII フリーの us-east-1 subscribers テーブルには入りません。マップ内のフィールドはベースキーで取得できますが、独立してクエリ可能ではありません。質問: (a) どのフォームフィールドを取得するか(KYB フィールド — 名前/メール/会社/ユースケース — を含む)、(b) いずれかのフィールドを直接参照する必要があるか(例: email で) — 必要であれば、それはトップレベル属性 + GSI に昇格されます(昇格するフィールドごとに HASH のみか HASH+RANGE かを質問)。フィールドは任意(OPTIONAL)/セラー確認済みとして扱い、PII を捏造せず、ログ内の PII をマスクしてください。licenseArn→セラーの内部 SaaS テナント ID のマッピングはセラーの責任です — これを subscribers テーブル(License Updated からの licenseArn、PII フリー)および/または customer-profile テーブル(登録時に取得した licenseArn)に紐付けてください。テナント別の参照が必要な場合は、tenantId 属性 + GSI を追加してください(references/architecture.md)。

製品タイプの判定: 認証情報を取得した後、Catalog API で製品が SaaS かつ従量課金制(ExternallyMetered)であることを確認します。AMI/コンテナ/SaaS 契約のみの製品は対象外です。 公開ドキュメントへ案内してください。 同時契約が既定の動作です — 新規製品はすべて CustomerAWSAccountId + LicenseArn を使用します。質問しないでください。既存のレガシースタックでは ProductCode + CustomerIdentifier を使用している場合があります。変更する前に、その構成を把握してください(references/existing-sellers.md)。

セットアップの順序(省略・入れ替え禁止)
  1. 一時的な認証情報を取得します(SSO → sts assume-role。長期有効なキーは使用せず、最初にキーを求めないでください)。
  2. 製品タイプを判定します(SaaS + ExternallyMetered)。
  3. 製品の公開状態を判定します — 現在の製品が Limited の場合に限り、製品固有のメインスタックをデプロイ/変更します。Published の場合は決して行わず、代わりにセラー独自のパイプライン用コードを提供してください。メインスタックを直接デプロイできるのは、現在の製品が Limited で、製品ごとの既存スタックがない場合に限ります(既存スタックがある場合は、セラーの確認のもとで変更/拡張します)。既存の共有イベントスタックを再デプロイ/変更することはありません(ライブトラフィックのある他の製品(Published の場合もあります)が利用している可能性があるため、出力を再利用してください)。初回のオンボーディング時のみ作成します。references/existing-sellers.mdを参照してください。
  4. 質問への回答(ステージ名を含む)を確認します。
  5. その後に限り、生成とデプロイを行います。テンプレート、Lambda ハンドラー、deploy.sh をセラーのワークスペースに実体化し、ディスク上のそのファイルをデプロイします(sam build --template-file + sam deploy)。インライン/一時テンプレートは決して使用しないでください。変更/拡張の場合は、セラーの既存ファイルをその場で編集します(環境変数をマージ)。作成した成果物はすべて、その後もワークスペースに残します(コードとスタックの整合性を保ち、セラーがバージョン管理、再デプロイ、開発を行えるようにします。カスタマイズするのはTODO:セクションのみです)。
  6. 既存スタックのガードレール(必ず遵守し、回避しないでください)。 この対象範囲に既存スタックがある場合、このスキルは変更を自動適用してはなりません。必ず(a)セラーが確認できるよう、正確な差分/変更セットを提示し、(b)適用前にセラーの明示的な確認と通常のコードレビューおよび変更管理プロセスを必須とし、(c)変更対象を現在の製品のメインスタックに限定し、(d)他の製品(Published の場合もあります)にすでにサービスを提供している共有イベントスタックを再デプロイ/変更せず、再利用しなければなりません。自動で直接デプロイできるのは、既存スタックがない Limited 製品に限ります。references/existing-sellers.mdを参照してください。
  7. 統合後、テスト計画全体を実施します。 セットアップ/変更の最後に、このスキルは完全な実機テスト計画(references/test-plan.md)を推奨し、可能な場合はその実施をセラーと進め、期待されるすべての観測可能な結果を検証しなければなりません。また、レビューとテストに合格するまでは、この統合で本番の課金トラフィックを処理すべきではないことを明示しなければなりません。

いずれかの判定に失敗した場合は、デプロイ前にワークフローを停止します。

スタックの命名規則
コンポーネントパターン例
イベントスタックawsmp-events-stack共有、us-east-1
メインスタックawsmp-<productCode>-meteringawsmp-4ml54db8vrmjuykaw1psroool-metering
イベントスタック(ステージ別)awsmp-events-<stageName>-stackawsmp-events-beta-stack
メインスタック(ステージ別)awsmp-<productCode>-<stageName>-meteringawsmp-4ml54db8vrmjuykaw1psroool-beta-metering

本番環境以外のサンドボックスでは、ステージごとにスコープされたスタックをデプロイし(ステージ名はすべてのリソース名に含まれます)、MeteringMode=dry-runを設定してください。これにより、送信側が実際のBatchMeterUsageを呼び出すことはありません。テストアカウントのみを使用してください。references/existing-sellers.md/セットアップ手順を参照してください。

サポートされる機能

SaaS Usage(PAYG)✅ | Concurrent Agreements ✅ | VMT ✅ | Multi-region ✅ | Multi-product ✅ 対象外(→ 公開ドキュメント):CCP、AMI/Container、SaaS-Contract-only(GetEntitlements)、3PI。

スケール: パイプラインは分離されており(discoverer → work queue → aggregator → aggregated-usage + cleanup queue → submitter)、1 時間あたり数千件のメータリングレコードを処理できるように設計されています。秒単位の行により、1 グループあたりの行数は 1 時間で ≤3600 行に制限されます。これにより、(licenseArn, account, dimension) グループあたり ≤約 1 書き込み/秒の高負荷をサポートします。さらに細かい、または高いレートには、秒/時間単位の行に事前集計するストリーミング取り込み層(例:Kinesis Data Streams)を推奨します(公開ナレッジ/ホストモデルで決定され、このスキルの第一級機能ではありません)。設計全体については、references/scalable-metering-design.md を参照してください。 GSI 主導の検出(Scan は使用しません): discoverer は、スパースな metering_pending GSI — HASH = meteringPending 時間バケット(YYYY-MM-DDTHH)、RANGE = licenseArn を使用して、保留中の使用量を検索します。customerAWSAccountId + dimension を射影することで(INCLUDE/ALL、KEYS_ONLY ではありません)、クエリから (licenseArn, customerAWSAccountId, dimension) のタプルを直接取得でき、グループを特定するための追加読み取りは不要です。各グループはその後、対象を絞った begins_with ソートキーのプレフィックス(begins_with(customerAWSAccountId#dimension#timestamp, "{account}#{dimension}#{hourPrefix}"))を使って読み取ります。licenseArn パーティション全体へのクエリとタイムスタンプの FilterExpression は使用しません。

デプロイ(概要。手順全体は references/deployment-steps.md を参照)
  1. SAM テンプレートと Lambda コードを生成します(TODO: を埋めます)。
  2. イベントスタックをデプロイします(us-east-1)— 共有 EventBridge + subscribers テーブル。
  3. メインスタックをデプロイします(販売者のリージョン)— Lambda + DynamoDB + API Gateway + アラーム。
  4. AMMP の Fulfillment URL を、ブランド化された登録ページ(購入者/KYB 情報を取得して RegistrationUrl を呼び出す S3+CloudFront フロントエンド)に設定します。生の RegistrationUrl は設定しないでください(スモークテストのフォールバック専用です)— 販売者による手動作業です。references/registration-page.md を参照してください。
  5. エンドツーエンドテストを実行します(subscribe → resolve → 要件を満たす使用量行を書き込む → discoverer → aggregator → submitter → Aggregated + Success を確認)。
  6. CloudTrail ログで検証します。

デプロイ後は、販売者が継続的に担う責任を伝え、実地テストを案内します(references/test-plan.md):販売者が要件を満たす使用量行を書き込むまでは、スタックは何も行いません。具体的には、秒単位の customerAWSAccountId#dimension#timestamp ソートキー(各セグメントが一致すること)と、行自身の UTC タイムスタンプの時間に等しい meteringPending バケットが必要です。meteringPending のタイムスタンプ誤りは、パイプラインで検出できない唯一の書き込みエラーです(気付かれないままメータリングされません)。テストで必ず確認してください。また、アラーム通知を購読し、ダッシュボードを監視します(理由別の UsageRecordRejected、例外別の BatchMeterUsageException、CustomerNotSubscribed、UsageAggregationExpired/UsageSubmissionExpired)。カタログのディメンション変更に再デプロイは不要です。処理結果の一部は非同期です(subscribe 後の License Updated → licenseArn/subscriptionStatus、毎時の discoverer + rate(5m) の submitter/expiry)。上限を設定したバックオフでポーリングするか、パスをすぐに実行するため Lambda を手動で呼び出します(どちらを行ったか明記してください)。 統合の最後に、完全なテスト計画(references/test-plan.md)をエンドツーエンドで実行し、期待される観測結果をすべて確認するとともに、作成/変更されたすべてのリソースをレビューしてから、統合に本番の課金トラフィックを流すことを推奨します(AI が生成した出力です。上記の本番投入前レビューに関する注意事項を参照してください)。

前提条件、認証情報、最小権限のデプロイ担当者ポリシー全体については、references/iam-credentials.md を参照してください。

イベントとステータスのモデル(キャンセル/従量課金の仕組みについて質問されたときに説明)

サブスクライバーの行には、互いに独立した2つのステータスフィールドがあります(決して混同しないでください)。

  • agreementStatus = active/inactive(契約のライフサイクル)。
  • subscriptionStatus = active/deprovisioning/inactive(ライセンスのライフサイクル。deprovisioned という値はありません)。

従量課金はライセンスのライフサイクルのみに従います。

  • License Updated → 契約とサブスクリプションのステータスを active にします(従量課金が許可されます)。
  • License Deprovisioned → subscriptionStatus=deprovisioning と deprovisioningExpiry(イベント時刻 + 約1時間)を設定します。約1時間の最終使用量フラッシュ期間が始まります。ロックを回避する仕組み:この期間中、パイプラインはそのライセンスの残りの保留中の使用量をすべて直ちに送信し、MeteringLockHours と通常の「完了済みの時間のみ」というゲートを無視します。MeteringLockHours が経過するのを待たず、通常の処理経路では常にスキップされる現在進行中の時間も従量課金の対象になります。具体的な仕組み/マーカー:License Deprovisioned ハンドラーが、そのサブスクライバーの行に subscriptionStatus=deprovisioning と deprovisioningExpiry を設定します。ディスカバラーはそのマーカーを基準とするdeprovisioning フラッシュモードで実行され、保留中のバケット(now-23h … now-1h と現在の時間)を、対象ライセンスのみについて古い順にキューへ追加します。この回避処理の対象は、deprovisioning 中のライセンスのみです。ほかのアクティブなライセンスには、通常の MeteringLockHours ゲートと完了済みの時間のみを対象とする動作が引き続き適用されます。ある購入者のライセンスが deprovisioning になっても、別の購入者の未完了の時間が強制的にフラッシュされることはありません。ロックを回避するのは deprovisioning フラッシュであり、MeteringLockHours 自体の変更でも、Purchase Agreement Ended イベントでもありません。期間が経過すると、events-stack の deprovision-cleanup Lambda(rate(15m))が inactive を設定し、deprovisioning のマーカーをクリアします。最終処理は時間に基づくため、複数の時間やリージョンにまたがっても正しく動作します。サーバー側の期間が終了した後は、BatchMeterUsage が CustomerNotSubscribed を返します。
  • Purchase Agreement Ended → agreementStatus=inactive のみを設定します(subscriptionStatus は変更せず、従量課金も停止せず、フラッシュも行いません)。
  • Purchase Agreement Amended → 契約メタデータを更新します(アクティブな状態を維持します)。
  • Purchase Agreement Created → 従量課金では処理されません(licenseArn がないため)。必要であれば、販売者が従量課金を有効化せずに処理します(プロビジョニング前の処理、CRM など)。

これらを逆にしないでください(フラッシュ期間を開始するのは Purchase Agreement Ended ではなく License Deprovisioned です)。フラッシュ前にキュー内のレコードを破棄しないでください。registeredRegions は参照用にすぎず、従量課金を制限することはありません。

セットアップ資料
  • 全体の手順 + ライターの契約 + よくある間違い + VMT/トークン/タイムスタンプ/スキーマ/従量課金フロー:references/setup-path.md
  • デプロイ手順:references/deployment-steps.md · IAM ポリシー:references/iam-credentials.md
  • 同時契約 + データモデル:references/concurrent-agreements.md · アーキテクチャ + ステータスのライフサイクル:references/architecture.md
  • 既存の販売者(変更/拡張、レガシー):references/existing-sellers.md
  • ブランド付き購入者ページ + 購入者確認(KYB)の情報取得:references/registration-page.md
  • 実践的なテスト計画(手動の手順、対象を限定したケース、非同期ポーリング):references/test-plan.md
  • モニタリングとアラーム:references/troubleshooting.md · クライアント側のガードレール(BatchMeterUsage の前にすべてのチェックを実行):references/client-side-guardrails.md
  • リファレンス実装:scripts/metering_core.py(共通)+ discoverer.py/aggregator.py/cleanup.py/submitter.py/expiry.py(パイプライン)+ register.py/subscription.py;scripts/test_event_publisher.py(本番以外のサンドボックス用テストイベント生成ツール、ドライランのステージのみ);scripts/deploy.sh(assets/requirements.txt が必要);SAM テンプレート assets/template-main.yaml、assets/template-events.yaml
監視とアラーム(販売者が管理)

デプロイされるスタックは、販売者のアカウントに awsmp-* メトリクスと CloudWatch アラームを作成し、収益損失の兆候を監視します(discoverer/aggregator/cleanup/submitter/expiry の各ステージにおける Lambda の Errors/Throttles、「実行されなかった」ことを示す呼び出しアラーム、>24h の経過アラーム、EMF のビジネスステータスメトリクス)。アラームは常に作成されます。アクションは、販売者が任意で指定するリージョン内の SNS トピック(AlertsTopicArn)に接続されます。ビジネスステータスメトリクスには Lambda ログからの EMF を使用するため、追加のランタイム IAM は不要です。アラームへの対応は販売者の責任です — AWS Marketplace とこのスキルはクライアント側のエラーを追跡しません。デプロイヤーも Lambda もトピックに発行しません(CloudWatch がアクションを実行します)。しきい値、アクション、トピックは販売者が TODO: で設定します。詳細:references/troubleshooting.md。

「メータリングレコードが拒否されている/メータリングされていないことを示すアラームはどれですか?」 — このスタックは、各拒否/失敗シグナルに専用のアラームを作成するため、レコードのメータリングが止まると販売者にすぐ通知されます(すべて AlertsTopicArn に通知されます。しきい値は販売者が TODO: で設定します)。

  • UsageRecordUnprocessed(アラーム *-usage-unprocessed)— BatchMeterUsage が UnprocessedRecords を返し、submitter が自動再試行を 1 回実行した後も未処理のまま残った場合に記録されます。submitter はこれを理由にエラーを発生させません(次の rate(5m) サイクルまで保留にします)。そのため、UnprocessedRecords のケースでアラーム可能なシグナルはこのメトリクスだけです。Lambda の Errors アラームは作動しません。違反が継続する場合は、レコードのメータリングが繰り返し失敗していることを示します(スロットリング、タイムスタンプの範囲制限、下流の問題など)。調査してください。
  • UsageRecordRejected(*-usage-rejected)— メータリング前にクライアント側のガードレールで拒否された件数(理由を合算。理由ごとの内訳はダッシュボードに表示)。ソース行を修正して再送信してください。BatchMeterUsageException(*-batchmeterusage-exception)— リクエストレベルの例外により呼び出し全体が失敗した場合(ディメンション/ライセンスなど)。パイプラインは二分割して原因を特定します。CustomerNotSubscribed(*-customer-not-subscribed)— サブスクリプションが有効でないレコード単位のステータスです(再試行しないでください)。
  • さらに、経過アラーム(UsageAggregationExpired/UsageSubmissionExpired、>24h)が、メータリングされないまま気付かれずに残った使用状況を検出します。また、Lambda の Errors/Throttles/DLQ 深度のアラームがパイプラインの失敗を検出します。DuplicateRecord はダッシュボードのメトリクスです(情報提供用 — 最初の送信分はすでに請求済み)。アラームではありません。

QUERY PATH — 使用量レコードの検索、デバッグ、分析

ルーティング/パターンの詳細:references/query-path.md + references/query-patterns.md。

機能の境界を明示してください(範囲の限定された回答を、権威ある回答として返さないでください)。 このクエリは、販売者のリージョンにおける CloudTrail Event History(LookupEvents)を使用します。対象は直近 90 日以内で、ページ数に上限があり、メモリ内で処理されます。これは、直近かつリージョン単位の送信記録ビューです(課金されなかった却下/重複レコードも含みます)。顧客、ディメンション、ステータス、時刻による検索や単純なフィルタリング/件数/要約など、基本的な運用・デバッグクエリに使用します。推奨のみ(実行はしません):課金済み合計と 90 日を超える履歴には Seller Reports/SDDS、複雑または自由形式の分析にはホストエージェント + モデル(取得済みデータを渡します)。また、LicenseArn から productCode へのマッピングがない CA/LicenseArn メータリング(単一プロダクトスタックのコンテキスト、CloudTrail の additionalEventData、またはサブスクライバーテーブルがない場合)では、プロダクトにスコープを限定しないクエリのみを実行してください(帰属を推測しないでください)。各結果には complete/truncated/windowClipped が含まれます。結果を提示する前に、これらのシグナルを確認してください。 complete が false の場合(ページ数上限による切り詰め、または 90 日の保持上限による範囲の切り取り)、可能であれば完全な回答を得るために再実行してください(範囲が切り取られている場合は範囲を狭める/調整し、切り詰められている場合は --max-pages を増やすかフィルターを追加します)。それができない場合は、結果が PARTIAL であること、切り取られた/切り詰められた内容、対象範囲を明示し、権威ある情報/古い範囲の情報について Seller Reports を案内してください。切り詰められた、または範囲が切り取られた集計(summary/count/top_customers)は、下限を示すサンプルであり、権威ある情報ではありません。部分的な結果を、要求された全範囲の結果として決して報告しないでください。ホストエージェントでより詳細な回答が可能だからといって、基本的なクエリを拒否しないでください。CloudTrail 由来の SQL は、課金/使用量分析のソースとして不適切です — CloudTrail Lake も、trail → S3 エクスポートに対する Athena も、送信記録のみを参照します(却下/重複を含み、リージョン単位で、CA の productCode はありません)。メータリングに CloudTrail Lake を設定しないでください。複雑な分析/課金グレードの合計/90 日を超えるデータには SDDS を使用してください(推奨のみ、実行はしません)。長期的な未加工の送信記録の監査保持に限り、trail → S3(Lake は使用しません)を使用してください。境界の詳細:references/query-path.md。

複雑なクエリ/課金グレードのクエリの案内 — 必須事項と許可されるソース(必ず適用し、違反しないでください)。 メータリングデータに関する複雑な分析または課金精度の質問では、次の点を必ず守ってください。

  • メータリングに CloudTrail Lake(create-event-data-store)を推奨することは、決してしてはいけません。
  • CloudTrail(Event History、または trail → S3/Athena エクスポート)を、課金済み合計/課金グレードの合計のソースとして提示してはいけません。CloudTrail は送信記録のみを参照するため(却下/重複を含み、リージョン単位で、CA の productCode はありません)、売上の権威ある情報源にはなりません。
  • 許可されるソースのみを使用してください: 課金済み/支払済み合計および 90 日を超える課金履歴には Seller Reports/AMMP Billed Revenue ダッシュボードと SDDS(推奨のみ、実行はしません)。直近(90 日以内)のリージョン単位の送信記録デバッグには CloudTrail Event History。複雑または自由形式の分析には、取得済みデータをホストエージェント + モデルに渡します。長期的な未加工の送信記録の監査保持に限り、Athena と組み合わせた通常の trail → S3(Lake は使用しません)を使用し、これは課金情報ではなく送信記録であることを理解してください。複雑な課金に関する質問はすべて、CloudTrail が課金の権威ある情報源ではない理由を明示したうえで、これらの許可されたソースに案内してください。
手順
  1. 計測リージョンを確認します(必須 — CloudTrail はリージョンごとに分かれています)。
  2. フィルターを抽出します:customer_id、status、dimension、start_date/end_date、query_type(summary/detail/count/list_failures/top_customers)、top_n。
  3. スクリプトを実行します(デフォルト:過去 7 日間)。
python3 ./scripts/query_metering.py \
  --region <SELLER_METERING_REGION> \
  [--days N] [--start-date YYYY-MM-DD] [--end-date YYYY-MM-DD] \
  [--customer-id ACCT_ID] [--status STATUS] [--dimension DIM] \
  [--product-code PC] [--deployment-product-code PC] \
  [--query-type summary|detail|count|list_failures|top_customers] \
  [--top-n N] [--limit N] [--max-pages N]

CloudTrail Event History のレシピ(スクリプトの処理内容。手動でクエリする場合は直接使用します)。 Event History はデフォルトで有効です。データストアもセットアップも不要です。計測には CloudTrail Lake / create-event-data-store を決して使用しないでください。販売者の計測リージョンでクエリし、計測イベントソースで絞り込みます。

aws cloudtrail lookup-events --region <SELLER_METERING_REGION> \
  --lookup-attributes AttributeKey=EventSource,AttributeValue=metering-marketplace.amazonaws.com \
  --start-time <ISO> --end-time <ISO>

各 BatchMeterUsage イベントの CloudTrail responseElements には results(レコードごとの status: Success/CustomerNotSubscribed/DuplicateRecord — レコードごとのステータスはこれらのみです)と unprocessedRecords が含まれます。送信を検証するには、まず results と unprocessedRecords を確認してください (unprocessedRecords 内のレコードは処理されていません。下記の UnprocessedRecords に関する注意を参照してください)。CloudTrail ではレスポンスフィールドの先頭文字が小文字になるため、 results/status/meteringRecordId/unprocessedRecords (小文字)を読み取ってください。SDK の直接の BatchMeterUsage レスポンスでは、PascalCase(Results/Status/ MeteringRecordId/UnprocessedRecords)が使用されます。未定義のディメンションは レコードごとのステータスではありません。呼び出し全体がリクエストレベルの InvalidUsageDimensionException で失敗します(下記を参照)。対象は 90 日以内のみです。それより古いデータや請求済み合計には Seller Reports/SDDS を使用してください(推奨。クエリ対象ではありません)。CloudTrail は収益を示すものではありません — 請求対象ではないものも含め、送信内容(拒否や重複を含む)を示します。使用量の計測分は 翌月の初め(2 日または 3 日)に請求されます。たとえば、11 月の使用量は 12 月の請求書に反映されます。月末まではダッシュボードの収益は推定値です。請求済み額や支払済み額には Seller Reports を使用してください。

ステータスの意味

レコードごとの Results[].Status の値:

ステータス意味対応
Success受理され、請求対象ですなし
CustomerNotSubscribed有効なサブスクリプションがありません⚠️ 再試行しないでください
DuplicateRecord最初の書き込みが優先され、報告されています。数量を変更して再送信しても請求されません同一内容の再試行の場合に限り安全です。数量を変更する目的では使用しないでください(過少請求のリスクがあります)

リクエストレベルの例外(BatchMeterUsage 呼び出し全体を失敗させるもので、レコードごとのステータスではありません)。それぞれに、行が API 呼び出し前に拒否された際にパイプラインが記録する クライアント側のガードレール理由コード(RejectedClientSide。請求対象外)と、行が API に到達した場合の サーバー側の例外 があります。拒否理由を説明する際は、両方を提示してください。

サーバー側の例外クライアント側の理由コード意味対応
InvalidUsageDimensionExceptionInvalidDimensionカタログに定義されていないディメンション(サーバー側が正とする大文字・小文字を区別する値。請求対象外)ディメンションキーを修正するか、AMMP で定義してください。パイプラインは二分探索(深さに上限あり、⌈log2(バッチサイズ)⌉=5)で該当レコードを切り分けるため、同じバッチに含まれる他のレコードは引き続き計測されます。該当レコードには RejectedClientSide(理由 = 例外名)を記録し、BatchMeterUsageException メトリクスを出力します。
TimestampOutOfBoundsExceptionMalformedTimestamp24 時間より前のレコード(例:30 時間前の行)、または対象期間外のレコード — 拒否され、送信されませんタイムスタンプを修正してください。バッチ処理の前に、クライアント側のガードレールが理由 MalformedTimestamp で行を拒否します。不正なタイムスタンプが 1 つでも API に到達すると、切り分けられるまでバッチ全体が拒否されます。
InvalidLicenseExceptionInvalidLicenseArn不正な形式または無効な LicenseArnクライアント側で形式を検証します(理由 InvalidLicenseArn)。サーバー側の InvalidLicenseException は、(上限付きの)二分探索で切り分けられ、例外名を付けて RejectedClientSide として記録されます。
TooManyTagsExceptionTooManyTagsUsageRecord の usageAllocations タグが 5 個を超えていますタグを 5 個以下に減らしてください。バッチ処理の前に、クライアント側のガードレールが理由 TooManyTags で行を拒否します。
**ソートキーと拒否コードの順序:**パイプラインは、行をバッチに追加する前に、その形式を検証します。ソートキー(customerAWSAccountId#dimension#timestamp)に不正なタイムスタンプ、または許容期間外のタイムスタンプが含まれる行は、クライアント側で理由コード MalformedTimestamp により拒否されます(RejectedClientSide として記録され、UsageRecordRejected を発行します)。この行が BatchMeterUsage に到達することはないため、不正なタイムスタンプがバッチに混入しない限り、サーバー側の TimestampOutOfBoundsException として表面化することはありません。不正なタイムスタンプを含むソートキーについては、クライアント側の理由コード MalformedTimestamp と、そのサーバー側の同等コード TimestampOutOfBoundsException を必ず示してください。

拒否コード/ステータスコードの完全な一覧(理由コードに関する質問で参照する唯一の一覧)。「UnknownDimension はどういう意味ですか?」のような質問には、この完全な一覧に基づき、クライアント側のガードレール理由コードをサーバー側の同等コードに対応づけて回答してください。

クライアント側の理由コード(RejectedClientSide、API 呼び出し前、課金対象外)サーバー側の同等コード意味
MalformedSortKey / SortKeyMismatch—(API に到達しない)ソートキーが customerAWSAccountId#dimension#timestamp ではない、または各セグメントが行自身の属性と一致しない
MalformedTimestamp / TimestampOutOfWindowTimestampOutOfBoundsException秒単位の整数ではない、解析できない、または24時間より古いタイムスタンプ
MissingDimension—行に dimension がない
InvalidDimension / UnknownDimensionInvalidUsageDimensionExceptionディメンションがカタログに定義されていない(大文字と小文字を区別) — UnknownDimension / InvalidDimension は、「ディメンションが AMMP カタログにない」という同じ状態を表す
NegativeQuantity—Quantity < 0
NonIntegerQuantity—Quantity が整数ではない
TooManyTagsTooManyTagsExceptionレコードの usageAllocations タグが5個を超えている
TooManyAllocations—グループ内で統合したタグセットが2500個を超えている
AllocationSumMismatch—AllocatedUsageQuantity の値の合計が行の quantity と一致しない
MixedAllocatedAndUnallocated—グループ内に、タグ付きの行と、タグなしでゼロではない行が混在している
InvalidLicenseArnInvalidLicenseExceptionLicenseArn の形式が不正、または無効(1つのバッチに複数の製品が混在する場合にも発生)

これらに加え、API が返すレコードごとの最終的な Results[].Status 判定(クライアント側の理由コードではありません):Success(課金対象)、DuplicateRecord(先に書き込まれたレコードを優先。同じレコードを異なる Quantity で再送信しても課金されません — 「問題なし」ではなく、過少請求のリスクがあります)、CustomerNotSubscribed(有効なサブスクリプションがありません — 再試行しないでください)。また、UnprocessedRecords(一時的なエラーで、再試行可能)もあります。詳細は以下を参照してください。

**UnprocessedRecords(第3の、区別される結果 — レコードごとの Results[].Status 判定でも、リクエストレベルの例外でもありません):**バッチの一部だけが処理される結果です(成功したレコードは results に、処理に失敗したレコードは unprocessedRecords に含まれます。後者は課金対象外で、通常は一時的な ThrottlingException / InternalServiceException が原因です)。安全に再送信できます(冪等性があります)。送信側は1回だけ再試行し、その後は次回のスケジュール実行まで保留します(UsageRecordUnprocessed によってアラームが通知されます)。これを受理済み/課金済みとして扱ったり、上記の最終的な Results[].Status 判定と混同したりしないでください。

**データ階層:**CloudTrail Event History(90日以内、無料、リアルタイム)は、スキルが直接クエリする階層です。すべての送信(拒否や重複を含む)が表示されます。Seller Reports に表示されるのは課金対象のみです。したがって、「合計が一致しない」場合、その差は拒否や重複によるものです。履歴に関する質問は2種類あるため、混同しないでください。

SUBMITTED(CloudTrail)と BILLED(Seller Reports)の照合。 Seller Reports/AMMP Billed Revenue を請求額の信頼できる情報源として扱います。CloudTrail に記録されるのは送信のみです。照合するには、CloudTrail を Status=Success でフィルタリングし(請求対象ではない DuplicateRecord、CustomerNotSubscribed、リクエストレベルの例外を除外します)、請求レポートと比較します。残った差分は、却下分/重複分として想定される差異です。同じ (account, licenseArn, dimension, hour) に対して**数量の異なる DuplicateRecord**がないか確認してください。これは問題のない重複ではありません。先勝ち方式のため、後から送信された(多くの場合、より大きな)数量は請求されておらず、過少報告/過少請求を示している可能性があります。重複として退けず、照合時の要注意事項として提示してください。

  • BILLED 履歴(任意の期間)の取得にセットアップは不要です。AMMP Insights の 「Billed Revenue」ダッシュボードを利用します(必要な権限は aws-marketplace:GetSellerDashboard のみ。デフォルトは過去 6 か月で、請求書の日付フィルターを広げられます)。コンソール(QuickSight)のみで利用でき、CSV は手動でエクスポートします。オンデマンドで利用できる請求額 API はありません。プログラムから請求データを取得するには SDDS を利用します(S3 への日次プッシュと、利用者側での Athena/ETL が必要です)。ただしプッシュ専用で、登録後のデータのみ対象となり、過去データの補完はありません。このスキルは推奨のみを行い、実行はしません。GenerateDataSet は決して使用しないでください。詳細:references/seller-reports.md。
    • 割引/プライベートオファーはメータリングや Seller Reports の対象ではありません。公開されている AWS Marketplace の窓口(AMMP)に案内してください。 メータリングされた使用量と請求額の差が割引またはプライベートオファーによるものかという質問や、割引の適用/変更方法について質問された場合は、メータリングデータを使って説明や照合を試みず、「メータリングの対象外です」とだけ答えて終わらせないでください。メータリング数量を請求額に換算する価格設定(公開価格、プライベートオファー、割引、EULA の条件)は **AWS Marketplace Management Portal(AMMP)**で管理されます。割引/プライベートオファーによる差額は想定されるものだと伝えてください(Seller Reports には交渉済みの割引適用後の BILLED 収益が表示される一方、CloudTrail には生のメータリング SUBMISSIONS が表示されます)。割引/プライベートオファーの作成および管理については、AMMP → AWS Marketplace Management Portal/プライベートオファーに案内してください。
  • 90 日を超える RAW SUBMISSION 履歴を取得できる、標準で用意された情報源はありません(Seller Reports/SDDS が対象とするのは請求済みデータのみです。Event History の保持期間は 90 日です)。metering-marketplace.amazonaws.com の CloudTrail 証跡 → S3 をセルフ管理して保持してください(通常の証跡を使用し、Lake は使用しません。Athena を利用します)。こちらもセットアップ後のデータのみ対象で、過去データの補完はありません。aggregated_usage の DynamoDB PITR エクスポートも併用できます。詳細:references/query-patterns.md。
クエリのリファレンス
  • ルーティングとパラメーター:references/query-path.md · CloudTrail CLI のパターンとページネーション:references/query-patterns.md
  • Seller Reports との照合と SDDS の自動化に関する制約:references/seller-reports.md · ディメンションのリファレンス:references/dimension-reference.md

主要な API

APIリージョン目的
ResolveCustomer任意のリージョン(販売者が選択)登録時にトークンを LicenseArn + customerAWSAccountId + productCode に解決します。公開アカウントからリージョン単位で実行し、BatchMeterUsage のリージョンとは独立しています
BatchMeterUsage販売者のメータリングリージョン使用量レコードを送信します(1 時間ごと)
DescribeEntityus-east-1製品/タイプ/公開状態を検証します
LookupEvents販売者のリージョンメータリングレコードをクエリします(90 日以内)

IAM アクション名:aws-marketplace:BatchMeterUsage(メータリング)、aws-marketplace:ResolveCustomer(登録)、aws-marketplace:GetSellerDashboard(レポート)。名前空間は aws-marketplace: であり、meteringmarketplace:* ではありません。AccessDenied を診断する際は、該当するアクション名を正確に示し、実行ロール、条件キー、Resource ARN の不一致を確認してください。

セキュリティ

  • 最小権限: 実行ロールには、使用するアクションのみを許可します。従量課金の経路:aws-marketplace:BatchMeterUsage + テーブルに対するスコープ限定の DynamoDB Query/UpdateItem + metering_pending インデックス。登録:aws-marketplace:ResolveCustomer + customerAWSAccountId GSI に対するスコープ限定の Query + 項目操作。dynamodb:Scan は許可しません(検索には GSI Query を使用)。dynamodb:* や広範な aws-marketplace:* は許可しないでください。
  • 認証エラーを握りつぶさない: サブスクライバー/ライセンスの検索では、AccessDeniedException を「見つからない」として扱いません。権限/設定エラーは表面化します(Lambda エラー/500/DLQ)。
  • 保存時の暗号化: SQS(SqsManagedSseEnabled)、SNS(alias/aws/sns)、DynamoDB(SSEEnabled)。Lambda と API のロググループは、RetentionInDays と任意の KmsKeyId を備えた明示的なリソースです(購入者のアカウント ID/ライセンス ARN が含まれます)。
  • 公開登録 API: 意図的に公開されています(Marketplace は未認証でトークンを POST します)。→ テンプレートはデフォルトでベースライン WAF WebACL をアタッチします(レートベース + 一般的なエクスプロイト対策。WebAclArn で上書き可能)。API Gateway のスロットリングとアクセスログは常に有効です。登録 Lambda はセキュリティヘッダーを返し、長さが制限された許可リスト内のフィールド(AllowedRegistrationFields)のみを保持します。
  • 認証情報(短期認証を優先): SSO → sts assume-role。長期間有効なキーは使用せず、最初にキーを求めないでください。デプロイ担当ロールは条件付き信頼ポリシー(オープンな :root ではない)と、権限境界で制限された iam:CreateRole を使用します。
  • ステージ名: StageName にデフォルト値はなく、空を拒否する AllowedPattern が設定されています。質問票への回答は必須です。sam deploy と deploy.sh は即座に失敗します(prod へのフォールバックはありません)。
  • イベントのスコープ制限: EventBridge ルールは source: ["aws.agreement-marketplace"] にスコープを限定します。EventBridge→SQS のポリシーでは aws:SourceArn を使用します。
  • データ分類: 顧客の AWS アカウント ID と契約メタデータは機密情報です。識別子全体のログ出力を避け、これらを含むロググループを暗号化してください。

コメント