SAS トークンを使用してAzure Maps アカウントをセキュリティで保護する

この記事では、Azure Maps REST API の呼び出しに使用できる安全に保存された SAS トークンを使用して Azure Maps アカウントを作成する方法について説明します。

前提条件

  • Azure サブスクリプション。 まだ Azure アカウントを持っていない場合は、無料アカウントを新規登録してください。

  • Azure サブスクリプションに対する所有者ロールのアクセス許可。 以下を行うには、所有者のアクセス許可が必要です。

    • Azure Key Vault でキー コンテナーを作成する。
    • ユーザー割り当てマネージド ID を作成します。
    • マネージド ID にロールを割り当てます。
    • Azure Maps アカウントを作成する。
  • Azure CLI がインストールされてリソースがデプロイされます。

シナリオの例: SAS トークンのセキュリティで保護されたストレージ

SAS トークンの資格情報によって、トークンの有効期限が切れるか、アクセス権が取り消されるまで、その資格情報を保持するすべてのユーザーに、指定されたアクセス レベルが付与されます。 SAS トークン認証を使用するアプリケーションでは、キーを 安全に保管する必要があります。

このシナリオでは、SAS トークンをシークレットとして Key Vault に安全に保管し、トークンをパブリック クライアントに配布します。 アプリケーションのライフサイクル イベントでは、既存のトークンを使用するアクティブな接続を中断せずに、新しい SAS トークンが生成されることがあります。

Key Vault の構成の詳細については、「Azure Key Vault 開発者ガイド」を参照してください。

次のシナリオ例では、2 つの Azure Resource Manager (ARM) テンプレートのデプロイを使用して、次の手順を実行します。

  1. Key Vault を作成します。
  2. ユーザー割り当てマネージド ID を作成します。
  3. Azure ロールベースのアクセス制御 (RBAC) Azure Maps データ閲覧者ロールをユーザー割り当てマネージド ID に割り当てます。
  4. クロス オリジン リソース共有 (CORS) 構成で Azure Maps アカウントを作成し、ユーザー割り当てマネージド ID をアタッチします。
  5. SAS トークンを作成して Azure Key Vault に保存します。
  6. SAS トークン シークレットをキー コンテナーから取得します。
  7. SAS トークンを使用する Azure Maps REST API 要求を作成します。

完了したら、Azure CLI のある PowerShell で Azure Maps Search Address (Non-Batch) REST API の結果が表示されます。 Azure リソースは、Azure Maps アカウントに接続するためのアクセス許可を使用してデプロイされます。 最大レート制限、許可されるリージョン、localhost で構成された CORS ポリシー、Azure RBAC に対する制御があります。

Azure CLI を使用した Azure リソースのデプロイ

次のステップでは、SAS トークン認証を使用して Azure Maps アカウントを作成して構成する方法について説明します。 この例では、Azure CLI は PowerShell インスタンスで実行されます。

  1. az login で Azure サブスクリプションにサインインします。

  2. サブスクリプションに関する Key Vault、マネージド ID、Azure Maps を登録します。

    az provider register --namespace Microsoft.KeyVault
    az provider register --namespace Microsoft.ManagedIdentity
    az provider register --namespace Microsoft.Maps
    
  3. Microsoft Entra オブジェクト ID を取得します。

    $id = $(az rest --method GET --url 'https://graph.microsoft.com/v1.0/me?$select=id' --headers 'Content-Type=application/json' --query "id")
    
  4. 次の内容で prereq.azuredeploy.json という名前のテンプレート ファイルを作成します。

    {
        "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
            "location": {
                "type": "string",
                "defaultValue": "[resourceGroup().location]",
                "metadata": {
                    "description": "Specifies the location for all the resources."
                }
            },
            "keyVaultName": {
                "type": "string",
                "defaultValue": "[concat('vault', uniqueString(resourceGroup().id))]",
                "metadata": {
                    "description": "Specifies the name of the key vault."
                }
            },
            "userAssignedIdentityName": {
                "type": "string",
                "defaultValue": "[concat('identity', uniqueString(resourceGroup().id))]",
                "metadata": {
                    "description": "The name for your managed identity resource."
                }
            },
            "objectId": {
                "type": "string",
                "metadata": {
                    "description": "Specifies the object ID of a user, service principal, or security group in the Azure AD tenant for the vault. The object ID must be unique for the set of access policies. Get it by using Get-AzADUser or Get-AzADServicePrincipal cmdlets."
                }
            },
            "secretsPermissions": {
                "type": "array",
                "defaultValue": [
                    "list",
                    "get",
                    "set"
                ],
                "metadata": {
                    "description": "Specifies the permissions to secrets in the vault. Valid values are: all, get, list, set, delete, backup, restore, recover, and purge."
                }
            }
        },
        "resources": [
            {
                "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
                "name": "[parameters('userAssignedIdentityName')]",
                "apiVersion": "2018-11-30",
                "location": "[parameters('location')]"
            },
            {
                "apiVersion": "2021-04-01-preview",
                "type": "Microsoft.KeyVault/vaults",
                "name": "[parameters('keyVaultName')]",
                "location": "[parameters('location')]",
                "properties": {
                    "tenantId": "[subscription().tenantId]",
                    "sku": {
                        "name": "Standard",
                        "family": "A"
                    },
                    "enabledForTemplateDeployment": true,
                    "accessPolicies": [
                        {
                            "objectId": "[parameters('objectId')]",
                            "tenantId": "[subscription().tenantId]",
                            "permissions": {
                                "secrets": "[parameters('secretsPermissions')]"
                            }
                        }
                    ]
                }
            }
        ],
        "outputs": {
            "userIdentityResourceId": {
                "type": "string",
                "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]"
            },
            "userAssignedIdentityPrincipalId": {
                "type": "string",
                "value": "[reference(parameters('userAssignedIdentityName')).principalId]"
            },
            "keyVaultName": {
                "type": "string",
                "value": "[parameters('keyVaultName')]"
            }
        }
    }
    
    
  5. 前のステップで作成した前提条件リソースをデプロイします。 <group-name> には独自の値を指定してください。 必ず Azure Maps アカウントと同じ location を使用してください。

    az group create --name <group-name> --location "East US"
    $outputs = $(az deployment group create --name ExampleDeployment --resource-group <group-name> --template-file "./prereq.azuredeploy.json" --parameters objectId=$id --query "[properties.outputs.keyVaultName.value, properties.outputs.userAssignedIdentityPrincipalId.value, properties.outputs.userIdentityResourceId.value]" --output tsv)
    
  6. Azure Maps アカウント、ロールの割り当て、SAS トークンをプロビジョニングするテンプレート ファイル azuredeploy.json を作成します。

    Note

    Azure Maps Gen1 価格レベルの廃止

    Gen1 価格レベルは非推奨となり、2026 年 9 月 15 日に廃止されます。 Gen2 価格レベルが Gen1 (S0 と S1 の両方) 価格レベルに取って代わります。 Azure Maps アカウントに Gen1 価格レベルが選択されている場合、廃止前に Gen2 価格レベルに切り替えることができます。切り替えない場合、自動的に更新されます。 詳細については、「Azure Maps アカウントの価格レベルを管理する」を参照してください。

    {
        "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
            "location": {
                "type": "string",
                "defaultValue": "[resourceGroup().location]",
                "metadata": {
                    "description": "Specifies the location for all the resources."
                }
            },
            "keyVaultName": {
                "type": "string",
                "metadata": {
                    "description": "Specifies the resourceId of the key vault."
                }
            },
            "accountName": {
                "type": "string",
                "defaultValue": "[concat('map', uniqueString(resourceGroup().id))]",
                "metadata": {
                    "description": "The name for your Azure Maps account."
                }
            },
            "userAssignedIdentityResourceId": {
                "type": "string",
                "metadata": {
                    "description": "Specifies the resourceId for the user assigned managed identity resource."
                }
            },
            "userAssignedIdentityPrincipalId": {
                "type": "string",
                "metadata": {
                    "description": "Specifies the resourceId for the user assigned managed identity resource."
                }
            },
            "pricingTier": {
                "type": "string",
                "allowedValues": [
                    "S0",
                    "S1",
                    "G2"
                ],
                "defaultValue": "G2",
                "metadata": {
                    "description": "The pricing tier for the account. Use S0 for small-scale development. Use S1 or G2 for large-scale applications."
                }
            },
            "kind": {
                "type": "string",
                "allowedValues": [
                    "Gen1",
                    "Gen2"
                ],
                "defaultValue": "Gen2",
                "metadata": {
                    "description": "The pricing tier for the account. Use Gen1 for small-scale development. Use Gen2 for large-scale applications."
                }
            },
            "guid": {
                "type": "string",
                "defaultValue": "[guid(resourceGroup().id)]",
                "metadata": {
                    "description": "Input string for new GUID associated with assigning built in role types."
                }
            },
            "startDateTime": {
                "type": "string",
                "defaultValue": "[utcNow('u')]",
                "metadata": {
                    "description": "Current Universal DateTime in ISO 8601 'u' format to use as the start of the SAS token."
                }
            },
            "duration" : {
                "type": "string",
                "defaultValue": "P1Y",
                "metadata": {
                    "description": "The duration of the SAS token. P1Y is maximum, ISO 8601 format is expected."
                }
            },
            "maxRatePerSecond": {
                "type": "int",
                "defaultValue": 500,
                "minValue": 1,
                "maxValue": 500,
                "metadata": {
                    "description": "The approximate maximum rate per second the SAS token can be used."
                }
            },
            "signingKey": {
                "type": "string",
                "defaultValue": "primaryKey",
                "allowedValues": [
                    "primaryKey",
                    "seconaryKey"
                ],
                "metadata": {
                    "description": "The specified signing key which will be used to create the SAS token."
                }
            },
            "allowedOrigins": {
                "type": "array",
                "defaultValue": [],
                "maxLength": 10,
                "metadata": {
                    "description": "The specified application's web host header origins (example: https://www.azure.com) which the Azure Maps account allows for CORS."
                }
            }, 
            "allowedRegions": {
                "type": "array",
                "defaultValue": [],
                "metadata": {
                    "description": "The specified SAS token allowed locations where the token may be used."
                }
            }
        },
        "variables": {
            "accountId": "[resourceId('Microsoft.Maps/accounts', parameters('accountName'))]",
            "Azure Maps Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '423170ca-a8f6-4b0f-8487-9e4eb8f49bfa')]",
            "sasParameters": {
                "signingKey": "[parameters('signingKey')]",
                "principalId": "[parameters('userAssignedIdentityPrincipalId')]",
                "maxRatePerSecond": "[parameters('maxRatePerSecond')]",
                "start": "[parameters('startDateTime')]",
                "expiry": "[dateTimeAdd(parameters('startDateTime'), parameters('duration'))]",
                "regions": "[parameters('allowedRegions')]"
            }
        },
        "resources": [
            {
                "name": "[parameters('accountName')]",
                "type": "Microsoft.Maps/accounts",
                "apiVersion": "2023-06-01",
                "location": "[parameters('location')]",
                "sku": {
                    "name": "[parameters('pricingTier')]"
                },
                "kind": "[parameters('kind')]",
                "properties": {
                    "cors": {
                        "corsRules": [
                            {
                                "allowedOrigins": "[parameters('allowedOrigins')]"
                            }
                        ]
                    }
                },
                "identity": {
                    "type": "UserAssigned",
                    "userAssignedIdentities": {
                        "[parameters('userAssignedIdentityResourceId')]": {}
                    }
                }
            },
            {
                "apiVersion": "2020-04-01-preview",
                "name": "[concat(parameters('accountName'), '/Microsoft.Authorization/', parameters('guid'))]",
                "type": "Microsoft.Maps/accounts/providers/roleAssignments",
                "dependsOn": [
                    "[parameters('accountName')]"
                ],
                "properties": {
                    "roleDefinitionId": "[variables('Azure Maps Data Reader')]",
                    "principalId": "[parameters('userAssignedIdentityPrincipalId')]",
                    "principalType": "ServicePrincipal"
                }
            },
            {
                "apiVersion": "2021-04-01-preview",
                "type": "Microsoft.KeyVault/vaults/secrets",
                "name": "[concat(parameters('keyVaultName'), '/', parameters('accountName'))]",
                "dependsOn": [
                    "[variables('accountId')]"
                ],
                "tags": {
                    "signingKey": "[variables('sasParameters').signingKey]",
                    "start" : "[variables('sasParameters').start]",
                    "expiry" : "[variables('sasParameters').expiry]"
                },
                "properties": {
                    "value": "[listSas(variables('accountId'), '2023-06-01', variables('sasParameters')).accountSasToken]"
                }
            }
        ]
    }
    
  7. 前のステップで作成した Key Vault とマネージド ID のリソースの ID パラメーターを使用してテンプレートをデプロイします。 <group-name> には独自の値を指定してください。 SAS トークンを作成する場合は、allowedRegions パラメーターを eastuswestus2westcentralus に設定します。 これらの場所を使用して us.atlas.microsoft.com エンドポイントに対して HTTP 要求を行うことができます。

    重要

    資格情報が Azure デプロイ ログに表示されないように、SAS トークンを Key Vault に保存します。 SAS トークン シークレットの tags には、SAS トークンの有効期限を示す開始日、有効期限、署名キー名も含まれます。

     az deployment group create --name ExampleDeployment --resource-group <group-name> --template-file "./azuredeploy.json" --parameters keyVaultName="$($outputs[0])" userAssignedIdentityPrincipalId="$($outputs[1])" userAssignedIdentityResourceId="$($outputs[2])" allowedOrigins="['http://localhost']" allowedRegions="['eastus', 'westus2', 'westcentralus']" maxRatePerSecond="10"
    
  8. Key Vault から 1 つの SAS トークン シークレットのコピーを検索して保存します。

    $secretId = $(az keyvault secret list --vault-name $outputs[0] --query "[? contains(name,'map')].id" --output tsv)
    $sasToken = $(az keyvault secret show --id "$secretId" --query "value" --output tsv)
    
  9. Azure Maps エンドポイントに対して要求を行って SAS トークンをテストします。 この例では、要求が米国の地域にルーティングされるように us.atlas.microsoft.com を指定します。 お使いの SAS トークンでは、米国地域内のリージョンが許可されます。

    az rest --method GET --url 'https://us.atlas.microsoft.com/search/address/json?api-version=1.0&query=1 Microsoft Way, Redmond, WA 98052' --headers "Authorization=jwt-sas $($sasToken)" --query "results[].address"
    

完全なスクリプトの例

完全な例を実行するには、次のテンプレート ファイルが現在の PowerShell セッションと同じディレクトリにある必要があります。

  • キー コンテナーとマネージド ID を作成するための prereq.azuredeploy.json。
  • Azure Maps アカウントを作成し、ロールの割り当てとマネージド ID を構成し、SAS トークンをキーコンテナーに格納するための azuredeploy.json。
az login
az provider register --namespace Microsoft.KeyVault
az provider register --namespace Microsoft.ManagedIdentity
az provider register --namespace Microsoft.Maps

$id = $(az rest --method GET --url 'https://graph.microsoft.com/v1.0/me?$select=id' --headers 'Content-Type=application/json' --query "id")
az group create --name <group-name> --location "East US"
$outputs = $(az deployment group create --name ExampleDeployment --resource-group <group-name> --template-file "./prereq.azuredeploy.json" --parameters objectId=$id --query "[properties.outputs.keyVaultName.value, properties.outputs.userAssignedIdentityPrincipalId.value, properties.outputs.userIdentityResourceId.value]" --output tsv)
az deployment group create --name ExampleDeployment --resource-group <group-name> --template-file "./azuredeploy.json" --parameters keyVaultName="$($outputs[0])" userAssignedIdentityPrincipalId="$($outputs[1])" userAssignedIdentityResourceId="$($outputs[2])" allowedOrigins="['http://localhost']" allowedRegions="['eastus', 'westus2', 'westcentralus']" maxRatePerSecond="10"
$secretId = $(az keyvault secret list --vault-name $outputs[0] --query "[? contains(name,'map')].id" --output tsv)
$sasToken = $(az keyvault secret show --id "$secretId" --query "value" --output tsv)

az rest --method GET --url 'https://us.atlas.microsoft.com/search/address/json?api-version=1.0&query=1 Microsoft Way, Redmond, WA 98052' --headers "Authorization=jwt-sas $($sasToken)" --query "results[].address"

実例

C#、Java、または JavaScript など、ほとんどのクライアントから Azure Maps API への要求を実行できます。 Postman を使用すると、選択したほぼすべてのプログラミング言語またはフレームワークで、API 要求が基本的なクライアント コード スニペットに変換されます。 この生成されたコード スニペットは、フロントエンド アプリケーションで使用できます。

次の小さな JavaScript コードの例では、JavaScript Fetch API で SAS トークンを使用して Azure Maps 情報を取得および返す方法が示されています。 この例では、Get Search Address API バージョン 1.0 が使用されています。 <your SAS token> には独自の値を指定してください。

このサンプルを機能させるには、API 呼び出しの allowedOrigins と同じ配信元内から実行してください。 たとえば、API 呼び出しで allowedOrigins として https://contoso.com を指定した場合、JavaScript のスクリプトをホストする HTML ページは https://contoso.com である必要があります。

async function getData(url = 'https://us.atlas.microsoft.com/search/address/json?api-version=1.0&query=1 Microsoft Way, Redmond, WA 98052') {
  const response = await fetch(url, {
    method: 'GET',
    mode: 'cors',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'jwt-sas <your SAS token>',
    }
  });
  return response.json(); // parses JSON response into native JavaScript objects
}

postData('https://us.atlas.microsoft.com/search/address/json?api-version=1.0&query=1 Microsoft Way, Redmond, WA 98052')
  .then(data => {
    console.log(data); // JSON data parsed by `data.json()` call
  });

リソースをクリーンアップする

Azure リソースが不要になったら削除できます。

az group delete --name {group-name}

次のステップ

クイック スタート ARM テンプレートをデプロイして、SAS トークンを使用する Azure Maps アカウントを作成します。

詳細な例については、以下をご覧ください。

Azure Maps アカウントにおける API 使用状況メトリックを確認します。

Microsoft Entra ID と Azure Maps を統合する方法を示すサンプルを確認します。