

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# AWS Marketplace for Containers Anywhere と License Manager の統合
<a name="container-anywhere-license-manager-integration"></a>

 AWS Marketplace 販売者は、Amazon EKS Anywhere、Amazon ECS Anywhere、Amazon EC2、またはオンプレミスインフラストラクチャ AWS Marketplace 用の Containers Anywhere 製品用の AWS License Manager と統合できます。 Amazon ECS Anywhere 以下のセクションでは、この統合の手順について説明します。

使用可能なライセンスモデルなど AWS Marketplace、License Manager と の統合に関する一般的な情報については、「」を参照してください[を使用したコンテナ製品の契約料金 AWS License Manager](container-license-manager-integration.md)。 AWS License Managerの詳細については、「[AWS License Manager ユーザーガイド](https://docs.aws.amazon.com/license-manager/latest/userguide/license-manager.html)」および「AWS CLI コマンドリファレンス」の「[AWS License Manager](https://docs.aws.amazon.com/cli/latest/reference/license-manager/index.html) セクション」を参照してください。**

**Topics**
+ [AWS Marketplace for Containers Anywhere 製品を License Manager と統合する](#containers-anywhere-integrate-with-LM)
+ [License Manager 統合のローカルテスト](#container-testing-LM-integration-locally)
+ [Amazon EKS での License Manager 統合のテスト](#container-testing-LM-integration-EKS)
+ [License Manager を使用したフローティングライセンス使用権限](#container-LM-floating-license)
+ [オンプレミスデプロイでの License Manager との統合に関するベストプラクティス](#container-LM-best-practices-on-prem)
+ [`LicenseManagerCredentialsProvider` - Java 実装](#container-license-manager-cred-provider-java)
+ [`LicenseManagerCredentialsProvider` - `Golang` 実装](#container-license-manager-cred-provider-golang)

## AWS Marketplace for Containers Anywhere 製品を License Manager と統合する
<a name="containers-anywhere-integrate-with-LM"></a>

以下の手順を使用して、 AWS Marketplace for Containers Anywhere 製品を と統合します AWS License Manager。

**AWS Marketplace for Containers Anywhere 製品を License Manager と統合するには**

1. ウェブブラウザを開き、[AWS Marketplace 管理ポータル](https://aws.amazon.com/marketplace/management/) にサインインします。

1. 以下のステップを実行して、コンテナ製品の製品 ID を作成します。この ID をコンテナイメージで使用して、後のステップでライセンスチェックを行います。

   1. メニューバーから [**Assets (アセット)**] を展開し、[**コンテナ**] を選択します。

   1. 商品に購入者向けの名前を入力し、**[作成]** を選択します。この名前は後で変更できます。

   1. **[プロジェクト ID]** を書き留めます。製品料金の詳細を作成または更新するときに使用します。
**ヒント**  
製品 ID を紛失した場合は、**アセット**メニューから**コンテナ** AWS Marketplace 管理ポータル を選択して で確認できます。**[コンテナ]** ページには、製品のリストとそれに関連する製品 ID が表示されます。

1. 最新のパブリック AWS SDK をダウンロードし、コンテナアプリケーションにインストールします。任意の AWS SDK のインストール手順については、「AWS [で構築するツール](https://aws.amazon.com/tools/)」を参照してください。
**注記**  
Amazon EKS Anywhere または によって提供されていない Kubernetes クラスターから License Manager API オペレーションを呼び出すには AWS、サポートされている AWS SDK を使用する必要があります。サポートされている AWS SDKs[「サポートされている AWS SDK の使用](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts-minimum-sdk.html)」を参照してください。

1. カスタム認証情報プロバイダーを使用して AWS License Manager クライアントを作成し、 AWS およびオンプレミスにデプロイされたコンテナアプリケーションに認証情報を提供できるようにします。カスタム認証情報プロバイダー (`LicenseCredentialProvider`) の完全なソースコードについては、以下のセクションを参照してください。
   + [`LicenseManagerCredentialsProvider` - Java 実装](#container-license-manager-cred-provider-java)
   + [`LicenseManagerCredentialsProvider` - `Golang` 実装](#container-license-manager-cred-provider-golang)

    `LicenseCredentialsProvider` は、 AWS を追加することで、SDK のデフォルトの認証情報プロバイダーチェーンをオンプレミス用に拡張します`LicenseManagerTokenCredentialsProvider`。これにより、オンプレミス環境で License Manager OIDC が発行した ID トークンを使用して認証情報が提供されます。`LicenseCredentialsProvider` のソースコードをアプリケーションのクラスパスに含める必要があります。
**注記**  
を拡張`DefaultCredentialsProvider`すると、同じコンテナアプリケーションが で実行されているとき AWS 、およびオンプレミス環境で実行されているときに認証情報を取得できます。コンテナアプリケーションが既にデフォルトではなくカスタム認証情報プロバイダーチェーンを使用している場合は、`LicenseManagerTokenCredentialsProvider` をカスタムチェーンに追加して拡張することもできます。

   次のコードスニペットは、Java を使用して AWS License Manager クライアントを作成する例です。

   ```
   LicenseManagerClientBuilder clientBuilder = LicenseManagerClient.builder().credentialsProvider(LicenseCredentialsProvider.create());
   ```

1. 製品オファー内の各有料コンテナイメージから `aws license-manager checkout-license` コマンドを使用して、`CheckoutLicense` API オペレーションを呼び出します。これにより、購入者がアプリケーションのライセンスを使用する使用権限があるかどうかが確認されます。購入者にアプリケーションに対する使用権限がある場合は、`CheckoutLicense` は、申請を承諾し、リクエストされた使用権限とその値を返します。購入者にアプリケーションに対する使用権限がない場合は、`CheckoutLicense` は、例外をスローします。

   `CheckoutLicense` API オペレーションを呼び出す際には、以下のパラメータが必要です。
   + `CheckoutType` - 有効な値は `PROVISIONAL` または `PERPETUAL` です。
     + チェックアウトされた使用権限の数がプールからなくなる場合に、`PERPETUAL` を使用します。

       例: 購入者には 500 GB のデータを処理する権限があります。データの処理を続けると、500 GB のプールからその量が引き出され、使い果たされます。
     + 使用権がプールからチェックアウトされ、使用後に返却されるフローティングライセンス使用権に `PROVISIONAL` を使用します。

       例: ユーザーには、アプリケーション内で同時に 500 人のユーザーを使用する権利があります。ユーザーがログインまたはログアウトすると、そのユーザーは引き出されるか、500 人のユーザープールに戻ります。フローティングライセンス使用権限の詳細については、「[License Manager を使用したフローティングライセンス使用権限](#container-LM-floating-license)」を参照してください。
   + `ClientToken` - 大文字と小文字が区別される一意の識別子。固有のリクエストごとにランダムな UUID を使用することをお勧めします。
   + `Entitlements` - チェックアウトする使用権限のリスト。
     + 機能の使用権限の場合は、`Name` プロパティと `Unit` プロパティを次のように指定します。

       ```
       {
         "Name": "<Entitlement_Name>",
         "Unit": "None"
       }
       ```
     + カウント対象使用権限については、以下のように、`Name` プロパティ、`Unit` プロパティ、および `Count` プロパティを指定します。

       ```
       {
         "Name": "<Entitlement_Name>",
         "Unit": "<Entitlement_Unit>",
         "Value": <Desired_Count>
       }
       ```
   + `KeyFingerprint` - AWS Marketplace が発行するライセンスのキーフィンガープリントは `aws:294406891311:AWS/Marketplace:issuer-fingerprint` です。このキーフィンガープリントを使用すると、ライセンスは信頼できないエンティティによって発行 AWS Marketplace されるのではなく、 によって発行されます。
   + `ProductSKU` – 前のステップ AWS Marketplace 管理ポータル で で生成された製品 ID。

   次のスニペットは、 AWS CLIを使用した `CheckoutLicense` API オペレーションによる呼び出しの例です。

   ```
   aws license-manager checkout-license \
   --product-sku "2205b290-19e6-4c76-9eea-377d6bf71a47" \
   --checkout-type "PROVISIONAL" \
   --client-token "79464194dca9429698cc774587a603a1" \
   --entitlements "Name=AWS::Marketplace::Usage/Drawdown/DataConsumption, Value=10, Unit=Gigabytes" \
   --key-fingerprint "aws:294406891311:AWS/Marketplace:issuer-fingerprint"
   ```
**注記**  
ライセンスを確認するには、コンテナアプリケーションが License Manager を使用するためのアウトバウンドネットワークアクセスが必要です。オンプレミスでデプロイされたアプリケーションでは、アウトバウンドネットワークアクセスの信頼性が低くなったり、速度が低下する可能性があります。これらのアプリケーションでは、License Manager を呼び出す際に適切に再試行する必要があります。詳細については、「[オンプレミスデプロイでの License Manager との統合に関するベストプラクティス](#container-LM-best-practices-on-prem)」を参照してください。

1. `CheckoutLicense` API オペレーションを定期的に呼び出して、 AWS Marketplaceで行われた更新、アップグレード、またはキャンセルによって顧客のライセンスが変更されたかどうかを確認してください。ケイデンスはアプリケーションによって異なります。購入者の介入なしに変更が自動的に反映されるように、1 日 1 回ライセンスを確認することをお勧めします。

   オンプレミスにデプロイされたアプリケーションでは、定期的にライセンスをチェックするためのアウトバウンドネットワークアクセスが信頼できない場合があります。このような場合、アプリケーションは十分な耐障害性を保つためにキャッシュされたライセンスを使用する必要があります。詳細については、「[オンプレミスデプロイでの License Manager との統合に関するベストプラクティス](#container-LM-best-practices-on-prem)」を参照してください。

1. `CheckoutLicense` 呼び出しをコンテナアプリケーションと統合したら、変更を加えた新しいバージョンの Docker コンテナイメージを構築します。

1. アプリケーションの Helm チャートを更新して、License Manager API を使用してライセンスにアクセスするための設定を含む Kubernetes シークレットをオプション入力として受け入れます。設定シークレットには、License Manager によって発行された ID トークンと、コンテナアプリケーションがオンプレミスにデプロイされたときに License Manager APIs を呼び出すための AWS 認証情報を取得するために、前述のカスタム認証情報プロバイダーによって使用される AWS Identity and Access Management ロールが含まれます。また、デフォルト値 `us-east-1` を使用して AWS リージョン を入力として追加します。

   オンプレミスでコンテナアプリケーションをデプロイする購入者は、コンテナ製品の AWS Marketplace 購入者エクスペリエンスを通じて Kubernetes シークレットを作成できます。Kubernetes シークレット名を `helm install` コマンドへの入力として指定します。設定シークレットは次の形式で構成されます。

   ```
   apiVersion: v1
   kind: Secret
   metadata:
     name: aws-marketplace-license-config
   type: Opaque
   stringData:
     license_token: <token_value> // License Manager issued JWT token
     iam_role: <role_arn> // AWS Identity and Access Management role to assume with license token
   ```

1. と統合されたコンテナイメージの Helm チャートのアプリケーションデプロイテンプレートを更新 AWS License Manager して、以下を含めます。
   + ポッドのサービスアカウント - Amazon EKS に Helm をデプロイするにはサービスアカウントが必要です。コンテナイメージのサービスアカウントの IAM ロールを設定して、License Manager API オペレーションを呼び出す権限を取得するために使用されます。サービスアカウントの IAM ロールの詳細については、「[サービスアカウントの IAM ロール](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)」を参照してください。
   + オンプレミスデプロイのライセンスアクセス - ライセンス設定シークレットは、オンプレミス環境での Helm デプロイの License Manager API オペレーションを呼び出すための認証情報と適切なアクセス許可を提供するために必要です。購入者は、 AWS Marketplace 購入者エクスペリエンスからライセンスシークレットを生成して Helm に提供します。

   以下のコードスニペットは、サービスアカウント、ライセンス設定、イメージプルシークレットを含むデプロイ仕様のサンプルです。

   ```
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: example-app
   spec:
     replicas: 1
     selector:
       matchLabels:
         app: example-app
     template:
       metadata:
         labels:
           app: example-app
   spec:
         // Service account for pod
         serviceAccountName: {{ .Values.serviceAccountName }}
         containers:
           - name: example-app
             image: example-app
             ports:
               - containerPort: 8001
   // Add the following conditional attributes
   {{ - if .Values.awsmp.licenseConfigSecretName }}
             //Mount the license volume to the container image
             volumeMounts:
               - name: awsmp-product-license
                 mountPath: "/var/run/secrets/product-license"
             //Add following environment variable to container for credential
   provider
             env:
               - name: AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE
                 value: "/var/run/secrets/product-license/license_token"
               - name: AWS_ROLE_ARN
                   valueFrom:
                       secretKeyRef:
                       name: {{ .Values.aws.licenseConfigSecretName }}
                       key: iam_role
         //Mount the license secret as a volume to the pod
         volumes:
           - name: awsmp-product-license
             secret:
               secretName: {{ .Values.aws.licenseConfigSecretName }}
               optional: true
   {{ - end }}
   ```
**注記**  
ライセンス設定シークレットはオプションです。購入者はこの値をオンプレミスデプロイにのみ使用します。 AWS デプロイの場合、デプロイ仕様には License Manager 統合イメージのサービスアカウントが含まれている必要があります。

1. 以下のセクションのステップを実行して、License Manager 統合をローカルと Amazon EKS でテストします。

   1. [License Manager 統合のローカルテスト](#container-testing-LM-integration-locally)

   1. [Amazon EKS での License Manager 統合のテスト](#container-testing-LM-integration-EKS)

1. License Manager の統合をオンプレミス AWS とオンプレミスの両方で正常に検証したら、「」の手順に従ってコンテナ製品リストを作成できます[概要: コンテナ製品を作成する](container-product-getting-started.md#create-container-product)。

## License Manager 統合のローカルテスト
<a name="container-testing-LM-integration-locally"></a>

minikube やその他のセットアップを使用して、ローカルで任意の Kubernetes クラスターで License Manager の統合をテストできます。Kubernetes クラスターに、License Manager API オペレーションを呼び出すためのアウトバウンドインターネットアクセスがあることを確認します。

**License Manager 統合をローカルでテストするには**

1. テスト用販売者アカウントで、希望する使用権限を持つテストライセンスを作成します。テストライセンスを設定するには、「AWS License Manager API リファレンス」の「[CreateLicense](https://docs.aws.amazon.com/license-manager/latest/APIReference/API_CreateLicense.html)」を参照してください。**または、次のスクリプトを使用してテストライセンスを作成し、テスト購入者アカウントにライセンスを付与してライセンスを使用します。次のスクリプトでは、テスト用販売者アカウントの認証情報を使用しています。

   ```
   read -p 'AWS Account for test buyer: ' TEST_BUYER_ACCOUNT_ID
   read -p 'License entitlements: ' ENTITLEMENTS
   
   # TEST_SELLER_ACCOUNT_ID="109876543210"
   # ENTITLEMENTS="{\"Name\": \"ByData\",\"MaxCount\": 1000,\"Overage\":true,\"Unit\": \"Gigabits\",\"AllowCheckIn\": true}"
   
   # Create License
   
   NOW=$(date +"%Y-%m-%dT00:00:00+00:00")
   
   PRODUCT_NAME="My awesome product"
   PRODUCT_SKU="c97b7825-44c4-4f42-b025-12baa4c171e0"
   
   LICENSE_BENEFICIARY=" arn:aws:iam::$TEST_BUYER_ACCOUNT_ID:root "
   LICENSE_ISSUER_NAME="test-seller"
   LICENSE_NAME="test-seller-license"
   
   CLIENT_TOKEN="b3920968-a94f-4547-af07-3dd232319367"
   CONSUMPTION_TTL=180
   CONSUMPTION_RENEW_TYPE="None"
   
   HOME_REGION="us-east-1"
   
   LICENSE_ARN=$(aws license-manager create-license --license-name "$LICENSE_NAME" --product-name "$PRODUCT_NAME" --product-sku "$PRODUCT_SKU" --issuer Name="$LICENSE_ISSUER_NAME" --home-region "$HOME_REGION" --validity Begin="$NOW" --entitlements "$ENTITLEMENTS" --beneficiary "$LICENSE_BENEFICIARY" --consumption-configuration RenewType="$CONSUMPTION_RENEW_TYPE",ProvisionalConfiguration={MaxTimeToLiveInMinutes=$CONSUMPTION_TTL} --client-token "$CLIENT_TOKEN" | jq -r ".LicenseArn" )
   
   echo "License arn: $LICENSE_ARN"
   
   # Create Grant
   
   GRANT_TOKEN="e9a14140-4fca-4219-8230-57511a6ea6"
   GRANT_NAME="test-grant"
   
   GRANT_ARN=$(aws license-manager create-grant --grant-name "$GRANT_NAME" --license-arn "$LICENSE_ARN" --principals "$LICENSE_BENEFICIARY" --home-region "$HOME_REGION" --client-token "$GRANT_TOKEN" --allowed-operations "CheckoutLicense" "CheckInLicense" "ExtendConsumptionLicense" "CreateToken" | jq -r ".GrantArn")
   
   echo "Grant arn: $GRANT_ARN"
   ```

1. 以前に定義したシークレット形式を使用して、ライセンストークンと IAM ロールを使用して Kubernetes シークレットを作成します。License Manager `CreateToken` API オペレーションを使用してライセンストークンを生成します。次に、IAM `CreateRole` API オペレーションを使用して、アクセス許可と信頼ポリシーを含む IAM ロールを作成します。このスクリプトの例を参照してください。次のスクリプトでは、テスト用購入者アカウントの認証情報を使用しています。

   ```
   read -p 'AWS Account for test license: ' TEST_ACCOUNT_ID
   read -p 'License Arn' LICENSE_ARN
   # Create IAM Role
   ROLE_NAME="AWSLicenseManagerConsumptionTestRole"
   ROLE_DESCRIPTION="Role to test AWS License Manager integration on-prem"
   ROLE_POLICY_ARN="arn:aws:iam::aws:policy/service-role/AWSLicenseManagerConsumptionPolicy"
   ROLE_TRUST_POLICY="{\"Version\": \"2012-10-17\",\"Statement\": [{ \"Effect\":\"Allow\", \"Principal\": { \"Federated\": \"openid-license-manager.amazonaws.com\" }, \"Action\": \"sts:AssumeRoleWithWebIdentity\",\"Condition\": { \"ForAnyValue:StringLike\": { \"openid-license-manager.amazonaws.com:amr\": \"aws:license-manager:token-issuer-account-id:${TEST_ACCOUNT_ID}\" }}}]}"
   ROLE_SESSION_DURATION=3600
   
   ROLE_ARN=$(aws iam create-role --role-name "$ROLE_NAME" --description "$ROLE_DESCRIPTION" --assume-role-policy-document "$ROLE_TRUST_POLICY" --max-session-duration $ROLE_SESSION_DURATION | jq ".Role" | jq -r ".Arn")
   
   aws iam attach-role-policy --role-name "$ROLE_NAME" --policy-arn "$ROLE_POLICY_ARN"
   
   echo "Role arn: $ROLE_ARN"
   
   # Create Token
   CLIENT_TOKEN="b3920968-a94f-4547-af07-3dd232319367"
   
   TOKEN=$(aws license-manager create-token --license-arn $LICENSE_ARN --role-arns $ROLE_ARN --client-token $CLIENT_TOKEN | jq '.Token')
   
   echo "License access token: $TOKEN"c
   ```

1. 外部でホストされている Kubernetes クラスターをセットアップします AWS。これを使用して、コンテナアプリケーションが 以外の環境から AWS License Manager API に接続できること、 AWS およびカスタム認証情報プロバイダーがアプリケーションに適切に統合されていることをテストします。

1. 以前に生成したライセンストークンと IAM ロールをローカル Kubernetes クラスターにデプロイします。

   ```
   kubectl create secret generic "awsmp-license-access-config" \
   --from-literal=license_token=${TOKEN} \
   --from-literal=iam_role=${ROLE_ARN}
   ```

1. シークレット名を入力としてアプリケーションを Helm 経由でデプロイし、アプリケーションが License Manager API オペレーションを呼び出して使用権限チェックを実行できることを確認します。Helm とデプロイ仕様の変更については、[AWS Marketplace for Containers Anywhere 製品を License Manager と統合する](#containers-anywhere-integrate-with-LM) の「ステップ 9」を参照してください。

## Amazon EKS での License Manager 統合のテスト
<a name="container-testing-LM-integration-EKS"></a>

Amazon EKS で License Manager の統合をテストすることもできます。ライセンス設定シークレットがなくても、アプリケーションが License Manager API オペレーションを呼び出せることをテストして確認します。また、サービスアカウントを使用してサービスアカウント (IRSA) の IAM ロールを設定し、関連する認証情報をアプリケーションに提供できることも確認してください。

**Amazon EKS で License Manager 統合をテストするには**

1. テスト用販売者アカウントで、希望する使用権限を持つテストライセンスを作成します。「[CreateLicense API リファレンス](https://docs.aws.amazon.com/license-manager/latest/APIReference/API_CreateLicense.html)」を参照してテストライセンスを設定するか、次のスクリプトを使用してライセンスを作成し、テスト購入者アカウントにライセンスを付与してライセンスを消費します。次のスクリプトでは、テスト用販売者アカウントの認証情報を使用しています。

   ```
   read -p 'AWS Account for test buyer: ' TEST_BUYER_ACCOUNT_ID
   read -p 'License entitlements: ' ENTITLEMENTS
   
   # TEST_SELLER_ACCOUNT_ID="109876543210"
   # ENTITLEMENTS="{\"Name\": \"ByData\",\"MaxCount\": 1000,\"Overage\": true,\"Unit\": \"Gigabits\",\"AllowCheckIn\": true}"
   
   # Create License
   
   NOW=$(date +"%Y-%m-%dT00:00:00+00:00")
   
   PRODUCT_NAME="My awesome product"
   PRODUCT_SKU="c97b7825-44c4-4f42-b025-12baa4c171e0"
   
   LICENSE_BENEFICIARY=" arn:aws:iam::$TEST_BUYER_ACCOUNT_ID:root "
   LICENSE_ISSUER_NAME="test-seller"
   LICENSE_NAME="test-seller-license"
   
   CLIENT_TOKEN="b3920968-a94f-4547-af07-3dd232319367"
   CONSUMPTION_TTL=180
   CONSUMPTION_RENEW_TYPE="None"
   
   HOME_REGION="us-east-1"
   
   LICENSE_ARN=$(aws license-manager create-license --license-name "$LICENSE_NAME" --product-name "$PRODUCT_NAME" --product-sku "$PRODUCT_SKU" --issuer Name="$LICENSE_ISSUER_NAME" --home-region "$HOME_REGION" --validity Begin="$NOW" --entitlements "$ENTITLEMENTS" --beneficiary "$LICENSE_BENEFICIARY" --consumption-configuration RenewType="$CONSUMPTION_RENEW_TYPE",ProvisionalConfiguration={MaxTimeToLiveInMinutes=$CONSUMPTION_TTL} --client-token "$CLIENT_TOKEN" | jq -r ".LicenseArn" )
   
   echo "License arn: $LICENSE_ARN"
   
   # Create Grant
   
   GRANT_TOKEN="e9a14140-4fca-4219-8230-57511a6ea6"
   GRANT_NAME="test-grant"
   
   GRANT_ARN=$(aws license-manager create-grant --grant-name "$GRANT_NAME" --license-arn "$LICENSE_ARN" --principals "$LICENSE_BENEFICIARY" --home-region "$HOME_REGION" --client-token "$GRANT_TOKEN" --allowed-operations "CheckoutLicense" "CheckInLicense" "ExtendConsumptionLicense" "CreateToken" | jq -r ".GrantArn")
   
   echo "Grant arn: $GRANT_ARN"
   ```

1. 必要な設定のテスト Amazon EKS クラスターを作成するか、以下のコマンドを実行してデフォルト設定を使用します。

   ```
   aws ec2 create-key-pair --region us-west-2 --key-name eks-key-pair
   ```

   ```
   eksctl create cluster \
   --name awsmp-eks-test-example \
   --region us-west-2 \
   --with-oidc \
   --ssh-access \
   --ssh-public-key eks-key-pair
   ```

1. 既存のクラスターのサービスアカウントを作成し、それを IAM ロールに関連付けます。次のコマンドは、`AWSLicenseManagerConsumptionPolicy` を使用して IAM ロールを作成します。次に、コマンドは、License Manager 統合イメージをデプロイする必要がある Amazon EKS クラスターの `test_sa` サービスアカウントにアタッチします。その結果、サービスアカウントは、License Manager API オペレーションを呼び出すための適切な認証情報を取得できます。

   ```
   eksctl create iamserviceaccount \
   --name test_sa \
   --namespace test_namespace \
   --cluster awsmp-eks-test-example \
   --attach-policy-arn "arn:aws:iam::aws:policy/service-role/AWSLicenseManagerConsumptionPolicy" \
   --approve \
   --override-existing-serviceaccounts
   ```

1. Helm を通じて、前のコマンドで IAM ロールが関連付けられているサービスアカウントにアプリケーションをデプロイします。アプリケーションが License Manager API オペレーションを呼び出して使用権限チェックを実行できることを確認します。

## License Manager を使用したフローティングライセンス使用権限
<a name="container-LM-floating-license"></a>

フローティングライセンスでは、ユーザーがアプリケーションにログインすると、使用可能なライセンスのプールからライセンスが引き出されます。ユーザーがログアウトすると、ライセンスは使用可能なライセンスのプールに再び追加されます。

フローティングライセンスの場合、アプリケーションは `CheckoutLicense` API オペレーションを使用して、リソースが使用されているときに使用権限プールから使用権限をチェックアウトします。`CheckoutLicense` API オペレーションのレスポンスには、チェックアウト時の一意の識別子であるライセンス消費トークンが含まれます。ライセンス消費トークンは、チェックアウトした使用権限をライセンスプールに戻したり、チェックアウトを延長したりするなど、追加のアクションを実行できます。

リソースが使用されなくなると、アプリケーションは `CheckInLicense` API オペレーションを使用して使用権限をプールにチェックインし直します。

```
aws license-manager check-in-license \
--license-consumption-token "f1603b3c1f574b7284db84a9e771ee12"
```

オペレーション中にアプリケーションがクラッシュした場合など、ライセンスをプールにチェックインし直せなかった場合、60 分後に使用権限が自動的にプールにチェックインされます。このため、リソースが 60 分以上使用されている場合は、使用権限をプールからチェックアウトしたままにしておくことがベストプラクティスです。そのためには、リソースが使用されている限り `ExtendLicenseConsumption` API オペレーションを使用してください。

```
aws license-manager extend-license-consumption \
--license-consumption-token "f1603b3c1f574b7284db84a9e771ee12"
```

## オンプレミスデプロイでの License Manager との統合に関するベストプラクティス
<a name="container-LM-best-practices-on-prem"></a>

オンプレミス環境にコンテナアプリケーションをデプロイすると、信頼性の低いアウトバウンドネットワークアクセスが発生する可能性があります。以下のベストプラクティスを活用して回復性を高め、インターネット接続の不備による潜在的な問題による購入者のサービスの中断を回避してください。
+ **適切な再試行** — 一時的なネットワークの問題により、アプリケーションが接続できなくなる可能性があります AWS License Manager。最大で 30 分間の再試行をし、指数関数的にバックオフを行います。これにより、短期的な停止やネットワークの問題を回避できます。
+ **ハードリミットの回避** - 接続されたクラスターにデプロイされたアプリケーションは、定期的にライセンスをチェックして、アップグレードや更新による変更を特定できます。アウトバウンドアクセスの信頼性が低いと、アプリケーションはそれらの変更を識別できない可能性があります。可能な限り、アプリケーションは、License Manager を通じてライセンスを確認できないことによる購入者へのサービスの中断を避ける必要があります。ライセンスの有効期限が切れてライセンスが有効かどうかを確認できなくなると、アプリケーションは無料試用エクスペリエンスまたはオープンソースエクスペリエンスに頼ることができます。
+ **顧客への通知** - キャッシュ型ライセンスを使用する場合、ライセンスへの変更 (更新やアップグレードを含む) は、実行中のワークロードに自動的に反映されません。アプリケーションがキャッシュされたライセンスを更新できるように、アプリケーションへのアウトバウンドアクセスを一時的に再度許可する必要があることを顧客に通知します。例えば、アプリケーション自体またはドキュメントを通じて顧客に通知します。同様に、下位の機能に頼る場合は、その使用権限がなくなったか、ライセンスの有効期限が切れていることを顧客に通知します。その後、顧客はアップグレードまたは更新のどちらかを選択できます。

## `LicenseManagerCredentialsProvider` - Java 実装
<a name="container-license-manager-cred-provider-java"></a>

`LicenseCredentialsProvider` は、 AWS を追加することで、SDK のデフォルトの認証情報プロバイダーチェーンをオンプレミス用に拡張します`LicenseManagerTokenCredentialsProvider`。

**`LicenseCredentialsProvider`**

```
package com.amazon.awsmp.license;

import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.credentials.internal.LazyAwsCredentialsProvider;
import software.amazon.awssdk.utils.SdkAutoCloseable;

public class LicenseCredentialsProvider implements AwsCredentialsProvider, SdkAutoCloseable {
    private static final LicenseCredentialsProvider CREDENTIALS_PROVIDER = new LicenseCredentialsProvider();
    private final LazyAwsCredentialsProvider providerChain;

    private LicenseCredentialsProvider() {
        this.providerChain = createChain();
    }

    public static LicenseCredentialsProvider create() {
        return CREDENTIALS_PROVIDER;
    }

    @Override
    public AwsCredentials resolveCredentials() {
        return this.providerChain.resolveCredentials();
    }

    @Override
    public void close() {
        this.providerChain.close();
    }

    private LazyAwsCredentialsProvider createChain() {
        return LazyAwsCredentialsProvider.create(() -> {
            AwsCredentialsProvider[] credentialsProviders = new AwsCredentialsProvider[]{
                    DefaultCredentialsProvider.create(),
                    LicenseManagerTokenCredentialsProvider.create()};

            return AwsCredentialsProviderChain.builder().reuseLastProviderEnabled(true)
                    .credentialsProviders(credentialsProviders).build();
        });
    }
}
```

**`LicenseManagerTokenCredentialsProvider`**

`LicenseManagerTokenCredentialsProvider` により、オンプレミス環境で License Manager OIDC が発行した ID トークンを使用して認証情報が提供されます。`LicenseCredentialsProvider` のソースコードをアプリケーションのクラスパスに含める必要があります。

```
package com.amazon.awsmp.license;

import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.core.SdkSystemSetting;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
import software.amazon.awssdk.core.retry.RetryPolicyContext;
import software.amazon.awssdk.core.retry.conditions.OrRetryCondition;
import software.amazon.awssdk.core.retry.conditions.RetryCondition;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
import software.amazon.awssdk.services.licensemanager.LicenseManagerClient;
import software.amazon.awssdk.services.licensemanager.model.GetAccessTokenRequest;
import software.amazon.awssdk.services.licensemanager.model.GetAccessTokenResponse;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.auth.StsAssumeRoleWithWebIdentityCredentialsProvider;
import software.amazon.awssdk.services.sts.model.AssumeRoleWithWebIdentityRequest;
import software.amazon.awssdk.services.sts.model.IdpCommunicationErrorException;
import software.amazon.awssdk.utils.IoUtils;
import software.amazon.awssdk.utils.SdkAutoCloseable;
import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.SystemSetting;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.function.Supplier;

public class LicenseManagerTokenCredentialsProvider implements AwsCredentialsProvider, SdkAutoCloseable {

    private final StsAssumeRoleWithWebIdentityCredentialsProvider credentialsProvider;
    private final RuntimeException loadException;

    private Path licenseAccessTokenFile;
    private String roleArn;
    private String roleSessionName;
    private StsClient stsClient;
    private LicenseManagerClient lmClient;

    public static LicenseManagerTokenCredentialsProvider create() {
        return new Builder().build();
    }

    @Override
    public AwsCredentials resolveCredentials() {
        if (this.loadException != null) {
            throw this.loadException;
        }
        return this.credentialsProvider.resolveCredentials();
    }

    @Override
    public void close() {
        IoUtils.closeQuietly(this.credentialsProvider, null);
        IoUtils.closeQuietly(this.stsClient, null);
        IoUtils.closeIfCloseable(this.lmClient, null);
    }

    private LicenseManagerTokenCredentialsProvider(Builder builder) {
        StsAssumeRoleWithWebIdentityCredentialsProvider credentialsProvider = null;
        RuntimeException loadException = null;

        try {
            this.licenseAccessTokenFile = Paths.get(StringUtils.trim(LicenseSystemSetting.AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE.getStringValueOrThrow()));
            this.roleArn = SdkSystemSetting.AWS_ROLE_ARN.getStringValueOrThrow();
            this.roleSessionName = SdkSystemSetting.AWS_ROLE_SESSION_NAME.getStringValue().orElse("aws-sdk-java-" + System.currentTimeMillis());
            this.stsClient = builder.stsClient != null ? builder.stsClient : StsClientFactory.create();
            this.lmClient = builder.lmClient != null ? builder.lmClient : LicenseManagerClientFactory.create();

            AssumeRoleWithWebIdentityRequest request = AssumeRoleWithWebIdentityRequest.builder()
                    .roleArn(this.roleArn).roleSessionName(this.roleSessionName).build();

            Supplier<AssumeRoleWithWebIdentityRequest> supplier = new AssumeRoleRequestSupplier(request,
                    this.licenseAccessTokenFile, this.lmClient);

            credentialsProvider = StsAssumeRoleWithWebIdentityCredentialsProvider.builder()
                    .stsClient(this.stsClient).refreshRequest(supplier).build();
        } catch (RuntimeException ex) {
            loadException = ex;
        }

        this.credentialsProvider = credentialsProvider;
        this.loadException = loadException;
    }

    public static final class Builder {
        private Path licenseAccessTokenFile;
        private String roleArn;
        private String roleSessionName;
        private StsClient stsClient;
        private LicenseManagerClient lmClient;

        public LicenseManagerTokenCredentialsProvider build() {
            return new LicenseManagerTokenCredentialsProvider(this);
        }

        public LicenseManagerTokenCredentialsProvider.Builder licenseAccessTokenFile(Path licenseAccessTokenFile) {
            this.licenseAccessTokenFile = licenseAccessTokenFile;
            return this;
        }

        public LicenseManagerTokenCredentialsProvider.Builder roleArn(String roleArn) {
            this.roleArn = roleArn;
            return this;
        }

        public LicenseManagerTokenCredentialsProvider.Builder roleSessionName(String roleSessionName) {
            this.roleSessionName = roleSessionName;
            return this;
        }

        public LicenseManagerTokenCredentialsProvider.Builder stsClient(StsClient stsClient) {
            this.stsClient = stsClient;
            return this;
        }

        public LicenseManagerTokenCredentialsProvider.Builder lmClient(LicenseManagerClient lmClient) {
            this.lmClient = lmClient;
            return this;
        }
    }

    private static final class AssumeRoleRequestSupplier implements Supplier {
        private final LicenseManagerClient lmClient;
        private final AssumeRoleWithWebIdentityRequest request;
        private final Path webIdentityRefreshTokenFile;

        AssumeRoleRequestSupplier(final AssumeRoleWithWebIdentityRequest request,
                                                 final Path webIdentityRefreshTokenFile,
                                                 final LicenseManagerClient lmClient) {
            this.lmClient = lmClient;
            this.request = request;
            this.webIdentityRefreshTokenFile = webIdentityRefreshTokenFile;
        }

        public AssumeRoleWithWebIdentityRequest get() {
            return this.request.toBuilder()
                    .webIdentityToken(getIdentityToken())
                    .build();
        }

        private String getIdentityToken() {
            return refreshIdToken(readRefreshToken(this.webIdentityRefreshTokenFile));
        }

        private String readRefreshToken(Path file) {
            try (InputStream webIdentityRefreshTokenStream = Files.newInputStream(file)) {
                return IoUtils.toUtf8String(webIdentityRefreshTokenStream);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        private String refreshIdToken(String licenseRefreshToken) {
            final GetAccessTokenRequest request = GetAccessTokenRequest.builder()
                    .token(licenseRefreshToken)
                    .build();

            GetAccessTokenResponse response = this.lmClient.getAccessToken(request);
            return response.accessToken();
        }
    }

    private static final class LicenseManagerClientFactory {
        private static final Duration DEFAULT_API_TIMEOUT = Duration.ofSeconds(30);
        private static final Duration DEFAULT_API_ATTEMPT_TIMEOUT = Duration.ofSeconds(10);

        public static LicenseManagerClient create() {
            return getLicenseManagerClient();
        }

        private static LicenseManagerClient getLicenseManagerClient() {
            ClientOverrideConfiguration configuration = ClientOverrideConfiguration.builder()
                    .apiCallTimeout(DEFAULT_API_TIMEOUT)
                    .apiCallAttemptTimeout(DEFAULT_API_ATTEMPT_TIMEOUT)
                    .build();

            LicenseManagerClient client = LicenseManagerClient.builder()
                    .region(configureLicenseManagerRegion())
                    .credentialsProvider(AnonymousCredentialsProvider.create())
                    .overrideConfiguration(configuration).build();
            return client;
        }

        private static Region configureLicenseManagerRegion() {
            Region defaultRegion = Region.US_EAST_1;

            Region region;
            try {
                region = (new DefaultAwsRegionProviderChain()).getRegion();
            } catch (RuntimeException ex) {
                region = defaultRegion;
            }
            return region;
        }
    }

    private static final class StsClientFactory {
        private static final Duration DEFAULT_API_TIMEOUT = Duration.ofSeconds(30);
        private static final Duration DEFAULT_API_ATTEMPT_TIMEOUT = Duration.ofSeconds(10);

        public static StsClient create() {
            return getStsClient();
        }

        private static StsClient getStsClient() {
            OrRetryCondition retryCondition = OrRetryCondition.create(new StsRetryCondition(),
                    RetryCondition.defaultRetryCondition());

            ClientOverrideConfiguration configuration = ClientOverrideConfiguration.builder()
                    .apiCallTimeout(DEFAULT_API_TIMEOUT)
                    .apiCallAttemptTimeout(DEFAULT_API_ATTEMPT_TIMEOUT)
                    .retryPolicy(r -> r.retryCondition(retryCondition))
                    .build();

            return StsClient.builder()
                    .region(configureStsRegion())
                    .credentialsProvider(AnonymousCredentialsProvider.create())
                    .overrideConfiguration(configuration).build();
        }

        private static Region configureStsRegion() {
            Region defaultRegion = Region.US_EAST_1;
            Region stsRegion;
            try {
                stsRegion = (new DefaultAwsRegionProviderChain()).getRegion();
            } catch (RuntimeException ex) {
                stsRegion = defaultRegion;
            }
            return stsRegion;
        }

        private static final class StsRetryCondition implements RetryCondition {
            public boolean shouldRetry(RetryPolicyContext context) {
                return context.exception() instanceof IdpCommunicationErrorException;
            }
        }
    }

    private enum LicenseSystemSetting implements SystemSetting {
        AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE("aws.webIdentityRefreshTokenFile");

        private String systemProperty;
        private String defaultValue = null;

        LicenseSystemSetting(String systemProperty) {
            this.systemProperty = systemProperty;
        }

        @Override
        public String property() {
            return this.systemProperty;
        }

        @Override
        public String environmentVariable() {
            return this.name();
        }

        @Override
        public String defaultValue() {
            return this.defaultValue;
        }
    }
}
```

## `LicenseManagerCredentialsProvider` - `Golang` 実装
<a name="container-license-manager-cred-provider-golang"></a>

**`LicenseCredentialsProvider`**

`LicenseCredentialsProvider` は、 AWS を追加することで、SDK のデフォルトの認証情報プロバイダーチェーンをオンプレミス用に拡張します`LicenseManagerTokenCredentialsProvider`。

```
package lib

import (
	"context"
	"fmt"
	"sync"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
)

// LicenseCredentialsProvider is the custom credential provider that can retrieve valid temporary aws credentials
type LicenseCredentialsProvider struct {
	fallBackProvider   aws.CredentialsProvider
	mux                sync.RWMutex
	licenseCredentials aws.Credentials
	err                error
}

// NewLicenseCredentialsProvider method will create a LicenseCredentialProvider Object which contains valid temporary aws credentials
func NewLicenseCredentialsProvider() (*LicenseCredentialsProvider, error) {
	licenseCredentialProvider := &LicenseCredentialsProvider{}
	fallBackProvider, err := createCredentialProvider()
	if err != nil {
		return licenseCredentialProvider, fmt.Errorf("failed to create LicenseCredentialsProvider, %w", err)
	}
	licenseCredentialProvider.fallBackProvider = fallBackProvider
	return licenseCredentialProvider, nil
}

// Retrieve method will retrieve temporary aws credentials from the credential provider
func (l *LicenseCredentialsProvider) Retrieve(ctx context.Context) (aws.Credentials, error) {
	l.mux.RLock()
	defer l.mux.RUnlock()
	l.licenseCredentials, l.err = l.fallBackProvider.Retrieve(ctx)
	return l.licenseCredentials, l.err
}

func createCredentialProvider() (aws.CredentialsProvider, error) {
	// LoadDefaultConfig will examine all "default" credential providers
	ctx := context.TODO()
	cfg, err := config.LoadDefaultConfig(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to create FallBackProvider, %w", err)
	}

	var useFallbackProvider bool
	if cfg.Credentials != nil {
		if _, err := cfg.Credentials.Retrieve(ctx); err != nil {
			// If the "default" credentials provider cannot retrieve credentials, enable fallback to customCredentialsProvider.
			useFallbackProvider = true
		}
	} else {
		useFallbackProvider = true
	}

	if useFallbackProvider {
		customProvider, err := newLicenseManagerTokenCredentialsProvider()
		if err != nil {
			return cfg.Credentials, fmt.Errorf("failed to create fallBackProvider, %w", err)
		}
		// wrap up customProvider with CredentialsCache to enable caching
		cfg.Credentials = aws.NewCredentialsCache(customProvider)
	}
	return cfg.Credentials, nil
}
```

**`LicenseManagerTokenCredentialsProvider`**

`LicenseManagerTokenCredentialsProvider` により、オンプレミス環境で License Manager OIDC が発行した ID トークンを使用して認証情報が提供されます。`LicenseCredentialsProvider` のソースコードをアプリケーションのクラスパスに含める必要があります。

```
package lib

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/sts"
)

const awsRefreshTokenFilePathEnvVar = "AWS_LICENSE_ACCESS_FILE"

// licenseManagerTokenCredentialsProvider defines and contains StsAssumeRoleWithWebIdentityProvider
type licenseManagerTokenCredentialsProvider struct {
	stsCredentialProvider *stsAssumeRoleWithWebIdentityProvider
	mux                   sync.RWMutex
	licenseCredentials    aws.Credentials
	err                   error
}

// Retrieve method will retrieve credentials from credential provider.
// Make this method public to make this provider satisfies CredentialProvider interface
func (a *licenseManagerTokenCredentialsProvider) Retrieve(ctx context.Context) (aws.Credentials, error) {
	a.mux.RLock()
	defer a.mux.RUnlock()
	a.licenseCredentials, a.err = a.stsCredentialProvider.Retrieve(ctx)
	return a.licenseCredentials, a.err
}

// newLicenseManagerTokenCredentialsProvider will create and return a LicenseManagerTokenCredentialsProvider Object which wraps up stsAssumeRoleWithWebIdentityProvider
func newLicenseManagerTokenCredentialsProvider() (*licenseManagerTokenCredentialsProvider, error) {
	// 1. Retrieve variables From yaml environment
	envConfig, err := config.NewEnvConfig()
	if err != nil {
		return &licenseManagerTokenCredentialsProvider{}, fmt.Errorf("failed to create LicenseManagerTokenCredentialsProvider, %w", err)
	}
	roleArn := envConfig.RoleARN
	var roleSessionName string
	if envConfig.RoleSessionName == "" {
		roleSessionName = fmt.Sprintf("aws-sdk-go-v2-%v", time.Now().UnixNano())
	} else {
		roleSessionName = envConfig.RoleSessionName
	}
	tokenFilePath := os.Getenv(awsRefreshTokenFilePathEnvVar)
	b, err := ioutil.ReadFile(tokenFilePath)
	if err != nil {
		return &licenseManagerTokenCredentialsProvider{}, fmt.Errorf("failed to create LicenseManagerTokenCredentialsProvider, %w", err)
	}
	refreshToken := aws.String(string(b))

	// 2. Create stsClient
	cfg, err := config.LoadDefaultConfig(context.TODO())
	if err != nil {
		return &licenseManagerTokenCredentialsProvider{}, fmt.Errorf("failed to create LicenseManagerTokenCredentialsProvider, %w", err)
	}
	stsClient := sts.NewFromConfig(cfg, func(o *sts.Options) {
		o.Region = configureStsClientRegion(cfg.Region)
		o.Credentials = aws.AnonymousCredentials{}
	})

	// 3. Configure StsAssumeRoleWithWebIdentityProvider
	stsCredentialProvider := newStsAssumeRoleWithWebIdentityProvider(stsClient, roleArn, roleSessionName, refreshToken)

	// 4. Build and return
	return &licenseManagerTokenCredentialsProvider{
		stsCredentialProvider: stsCredentialProvider,
	}, nil
}

func configureStsClientRegion(configRegion string) string {
	defaultRegion := "us-east-1"
	if configRegion == "" {
		return defaultRegion
	} else {
		return configRegion
	}
}
```