-
Notifications
You must be signed in to change notification settings - Fork 28
Add proxy to Azure Maps #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
3bdfa1c
Add Azure Maps integration with APIM, including policies and Bicep co…
anotherRedbeard 3879831
Enhance Azure Maps integration by implementing user-assigned managed …
anotherRedbeard 5feb2c1
Merge branch 'main' into ar/azure-maps
simonkurtz-MSFT a6cf6e3
Fix policy XML read
simonkurtz-MSFT cc146c4
Update Azure Maps sample documentation and enhance async request hand…
anotherRedbeard 7fd2671
Merge branch 'ar/azure-maps' of https://github.com/Azure-Samples/Apim…
anotherRedbeard b191db1
Add Azure Maps integration with APIM, including policies and Bicep co…
anotherRedbeard 816abf8
Enhance Azure Maps integration by implementing user-assigned managed …
anotherRedbeard 8d3c32a
Update Azure Maps sample documentation and enhance async request hand…
anotherRedbeard c62e458
Fix policy XML read
simonkurtz-MSFT 4a5a829
wqMerge branch 'ar/azure-maps' of https://github.com/Azure-Samples/Ap…
anotherRedbeard 79d4d85
Enhance Azure Maps sample notebook and Bicep templates
anotherRedbeard 5a611bd
Remove output logs from Azure Maps sample notebook to streamline exec…
anotherRedbeard 0637718
Add Azure Maps integration with APIM, including policies and Bicep co…
anotherRedbeard 66b93f7
Enhance Azure Maps integration by implementing user-assigned managed …
anotherRedbeard eeb36f8
Update Azure Maps sample documentation and enhance async request hand…
anotherRedbeard fb8c244
Fix policy XML read
simonkurtz-MSFT a3c23ec
Enhance Azure Maps sample notebook and Bicep templates
anotherRedbeard 8c1b9ad
Remove output logs from Azure Maps sample notebook to streamline exec…
anotherRedbeard dba0707
Merge branch 'ar/azure-maps' of https://github.com/Azure-Samples/Apim…
anotherRedbeard 7c026fa
Refactor Azure Maps sample notebook and Bicep templates for improved …
anotherRedbeard 7232be5
Merge branch 'main' into ar/azure-maps
anotherRedbeard b88cacf
Fix function call for Bicep deployment in Azure Maps sample notebook
anotherRedbeard 93ef931
Enhance Azure Maps sample notebook and Bicep templates with improved …
anotherRedbeard 61b4f12
Merge branch 'main' into ar/azure-maps
simonkurtz-MSFT 80e47b8
Update README.md to enhance Azure Maps sample documentation with deta…
anotherRedbeard 2f939c9
Add refactoring changes
simonkurtz-MSFT 26f947f
Update test initialization and add preflight check for Azure Maps sample
anotherRedbeard 5b39ce5
Use the determined endpoint URL in tests
simonkurtz-MSFT File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # Samples: Api Management proxying calls to Azure Maps | ||
|
|
||
| This sample demonstrates how to use Azure API Management (APIM) to proxy calls to Azure Maps service using **three different authentication methods**. This setup allows you to manage, secure, and monitor access to Azure Maps through APIM while showcasing various authentication patterns for different use cases. | ||
|
|
||
| ⚙️ **Supported infrastructures**: All infrastructures | ||
|
|
||
| 👟 **Expected *Run All* runtime (excl. infrastructure prerequisite): ~2 minutes** | ||
|
|
||
| ## 🎯 Objectives | ||
|
|
||
| 1. **Demonstrate three Azure Maps authentication patterns:** | ||
| - **Shared Key Authentication** - Using Azure Maps subscription keys | ||
| - **Azure Entra ID (Managed Identity)** - Recommended approach for production scenarios | ||
| - **SAS Token Authentication** - Dynamic token generation with fine-grained control | ||
| 2. Learn path-to-operation mapping vs. generic proxy patterns in APIM | ||
| 3. Understand how APIM can enable chargeback/cost allocation scenarios for Azure Maps usage | ||
| 4. Show integration with both v1 and v2 Azure Maps API endpoints | ||
|
|
||
| ## 📝 Scenario | ||
|
|
||
| Organizations migrating from services like Bing Maps to Azure Maps often need flexible authentication and billing models. This sample addresses common questions about: | ||
|
|
||
| - **Authentication flexibility**: While Azure Entra ID with Managed Identity is the recommended production approach, some scenarios require shared keys or SAS tokens | ||
| - **Cost allocation**: Using APIM subscription keys to enable chargeback models and usage tracking per department/application | ||
| - **Migration patterns**: Supporting different authentication methods during transition periods | ||
| - **API management**: Centralizing access control, rate limiting, and monitoring for Azure Maps | ||
|
|
||
| ### Authentication Scenarios Demonstrated: | ||
|
|
||
| 1. **🔑 Shared Key (Subscription Key)**: Direct use of Azure Maps primary/secondary keys - simpler but less granular control | ||
| 2. **🛡️ Azure Entra ID (Managed Identity)**: Recommended for production - leverages Azure RBAC and eliminates key management | ||
| 3. **🎫 SAS Token**: Dynamic token generation with configurable expiration, rate limits, and regional restrictions - ideal for fine-grained access control | ||
|
|
||
| > **Note**: In production scenarios, SAS token generation would typically be handled by a separate Azure Function or API service. This sample demonstrates in-policy generation for simplicity and educational purposes. | ||
|
|
||
| ## 🛩️ Lab Components | ||
|
|
||
| This lab sets up: | ||
|
|
||
| - **Azure Maps Account** with Gen2 pricing tier | ||
| - **APIM Managed Identity** with roles: | ||
| - **Azure Maps Data Reader**: Read access to Maps APIs | ||
| - **Azure Maps Contributor**: Ability to generate SAS tokens | ||
| - **User Assigned Managed Identity (UAMI)** for SAS token principal, with: | ||
| - **Azure Maps Data Reader**: Used as the identity for SAS token operations | ||
| - **Three API Operations** demonstrating each authentication method: | ||
| - `/geocode` - Azure Entra ID authentication | ||
| - `/geocode/batch/async` - Shared key authentication | ||
| - `/default/*` - SAS token authentication with caching | ||
|
|
||
| ## ⚙️ Configuration | ||
|
|
||
| 1. Decide which of the [Infrastructure Architectures](../../README.md#infrastructure-architectures) you wish to use. | ||
| 1. If the infrastructure _does not_ yet exist, navigate to the desired [infrastructure](../../infrastructure/) folder and follow its README.md. | ||
| 1. If the infrastructure _does_ exist, adjust the `user-defined parameters` in the _Initialize notebook variables_ below. Please ensure that all parameters match your infrastructure. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| { | ||
| "cells": [ | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### 🛠️ 1. Initialize notebook variables\n", | ||
| "\n", | ||
| "Configures everything that's needed for deployment. \n", | ||
| "\n", | ||
| "**Modify entries under _1) User-defined parameters_ and _3) Define the APIs and their operations and policies_**." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import utils\n", | ||
| "from apimtypes import *\n", | ||
| "\n", | ||
| "# 1) User-defined parameters (change these as needed)\n", | ||
| "rg_location = 'eastus2'\n", | ||
| "index = 1\n", | ||
| "deployment = INFRASTRUCTURE.SIMPLE_APIM\n", | ||
| "tags = ['azure-maps'] # ENTER DESCRIPTIVE TAG(S)\n", | ||
| "api_prefix = 'am-' # OPTIONAL: ENTER A PREFIX FOR THE APIS TO REDUCE COLLISION POTENTIAL WITH OTHER SAMPLES\n", | ||
| "azure_maps_url = 'https://atlas.microsoft.com' # OPTIONAL: ENTER THE AZURE MAPS URL IF DIFFERENT FROM DEFAULT\n", | ||
| "\n", | ||
| "# 2) Service-defined parameters (please do not change these)\n", | ||
| "rg_name = utils.get_infra_rg_name(deployment, index)\n", | ||
| "sample_folder = \"azure-maps\"\n", | ||
| "nb_helper = utils.NotebookHelper(sample_folder, rg_name, rg_location, deployment, [INFRASTRUCTURE.AFD_APIM_PE, INFRASTRUCTURE.APIM_ACA, INFRASTRUCTURE.SIMPLE_APIM])\n", | ||
| "\n", | ||
| "# 3) Define the APIs and their operations and policies\n", | ||
| "\n", | ||
| "# Policies\n", | ||
| "# Named values must be set up a bit differently as they need to have two surrounding curly braces\n", | ||
| "map_async_geocode_batch_v1_keyauth_post_xml = utils.read_policy_xml('map_async_geocode_batch_v1_keyauth_post.xml', sample_name=sample_folder)\n", | ||
| "map_default_route_v2_aad_get_xml = utils.read_policy_xml('map_default_route_v2_aad_get.xml', sample_name=sample_folder)\n", | ||
| "map_geocode_v2_aad_get_xml = utils.read_policy_xml('map_geocode_v2_aad_get.xml', sample_name=sample_folder)\n", | ||
| "\n", | ||
| "# Map API \n", | ||
| "mapApi_v2_default_get = GET_APIOperation2('get-default-route','Get default route','/default/*','This is the default route that will allow all requests to go through to the backend api',map_default_route_v2_aad_get_xml)\n", | ||
| "mapApi_v1_async_post = APIOperation('async-geocode-batch','Async Geocode Batch','/geocode/batch/async',HTTP_VERB.POST, 'Post geocode batch async endpoint',map_async_geocode_batch_v1_keyauth_post_xml)\n", | ||
| "mapApi_v2_geocode_get = GET_APIOperation2('get-geocode','Get Geocode','/geocode','Get geocode endpoint',map_geocode_v2_aad_get_xml)\n", | ||
| "api1 = API('map-api', 'Map API', '/map', 'This is the proxy for Azure Maps', operations=[mapApi_v2_default_get, mapApi_v1_async_post,mapApi_v2_geocode_get], tags = tags, serviceUrl=azure_maps_url)\n", | ||
| "\n", | ||
| "# APIs Array\n", | ||
| "apis: List[API] = [api1]\n", | ||
| "\n", | ||
| "# 4) Set up the named values, for this specific sample, we are using some of the named values in the API policies defined above that can't be known at this point in the process. For those named values, we are setting them in the main.bicep file.\n", | ||
| "nvs: List[NamedValue] = [\n", | ||
| " NamedValue('azure-maps-arm-api-version','2023-06-01')\n", | ||
| "]\n", | ||
| "\n", | ||
| "utils.print_ok('Notebook initialized')" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### 🚀 2. Create deployment using Bicep\n", | ||
| "\n", | ||
| "Creates the bicep deployment into the previously-specified resource group. A bicep parameters file will be created prior to execution." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import utils\n", | ||
| "\n", | ||
| "# 1) Define the Bicep parameters with serialized APIs\n", | ||
| "bicep_parameters = {\n", | ||
| " 'apis': {'value': [api.to_dict() for api in apis]},\n", | ||
| " 'namedValues': {'value': [nv.to_dict() for nv in nvs]}\n", | ||
| "}\n", | ||
| "\n", | ||
| "# 2) Deploy the bicep template\n", | ||
| "output = nb_helper.deploy_bicep(bicep_parameters)\n", | ||
| "\n", | ||
| "if output.json_data:\n", | ||
| " apim_name = output.get('apimServiceName', 'APIM Service Name')\n", | ||
| " apim_gateway_url = output.get('apimResourceGatewayURL', 'APIM API Gateway URL')\n", | ||
| "\n", | ||
| "utils.print_ok('Deployment completed')" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### ✅ 3. Verify API Request Success\n", | ||
| "\n", | ||
| "Assert that the deployment was successful by making simple calls to APIM. \n", | ||
| "\n", | ||
| "❗️ If the infrastructure shields APIM and requires a different ingress (e.g. Azure Front Door), the request to the APIM gateway URl will fail by design. Obtain the Front Door endpoint hostname and try that instead." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import utils\n", | ||
| "from apimtesting import ApimTesting\n", | ||
| "from apimrequests import ApimRequests\n", | ||
| "import json\n", | ||
| "\n", | ||
| "tests = ApimTesting(\"Azure Maps Sample Tests\", sample_folder, deployment)\n", | ||
| "\n", | ||
| "# Preflight: Check if the infrastructure architecture deployment uses Azure Front Door. If so, assume that APIM is not directly accessible and use the Front Door URL instead.\n", | ||
| "endpoint_url = utils.test_url_preflight_check(deployment, rg_name, apim_gateway_url)\n", | ||
| "\n", | ||
| "reqs = ApimRequests(endpoint_url)\n", | ||
| "\n", | ||
| "# 1) Issue a direct request to API Management\n", | ||
|
simonkurtz-MSFT marked this conversation as resolved.
|
||
| "output = reqs.singleGet('/', msg = 'Calling Hello World (Root) API. Expect 200.')\n", | ||
| "tests.verify(output, 'Hello World from API Management!')\n", | ||
| "\n", | ||
| "# 2) Issue requests to API Management with Azure Maps APIs\n", | ||
| "output = reqs.singleGet('/map/default/geocode?query=15127%20NE%2024th%20Street%20Redmond%20WA', msg = 'Calling Default Route API with SAS Token Auth. Expect 200.')\n", | ||
| "tests.verify('address' in output, True)\n", | ||
| "\n", | ||
| "output = reqs.singleGet('/map/geocode?query=15127%20NE%2024th%20Street%20Redmond%20WA', msg = 'Calling Geocode v2 API with AAD Auth. Expect 200.')\n", | ||
| "tests.verify('address' in output, True)\n", | ||
| "\n", | ||
| "output = reqs.singlePostAsync('/map/geocode/batch/async', data={\n", | ||
| " \"batchItems\": [\n", | ||
| " {\"query\": \"?query=400 Broad St, Seattle, WA 98109&limit=3\"},\n", | ||
| " {\"query\": \"?query=One, Microsoft Way, Redmond, WA 98052&limit=3\"},\n", | ||
| " {\"query\": \"?query=350 5th Ave, New York, NY 10118&limit=1\"},\n", | ||
| " {\"query\": \"?query=Pike Pl, Seattle, WA 98101&lat=47.610970&lon=-122.342469&radius=1000\"},\n", | ||
| " {\"query\": \"?query=Champ de Mars, 5 Avenue Anatole France, 75007 Paris, France&limit=1\"}\n", | ||
| " ]\n", | ||
| "}, msg = 'Calling Async Geocode Batch v1 API with Share Key Auth. Expect initial 202, then a 200 on the polling response', timeout=120, poll_interval=3)\n", | ||
| "\n", | ||
| "# confirm the response contains \"summary\": { \"successfulRequests\": 5, \"totalRequests\": 5}\n", | ||
| "tests.verify('summary' in output and 'successfulRequests' in output and json.loads(output)['summary']['successfulRequests'] == 5, True)\n", | ||
| "\n", | ||
| "tests.print_summary()\n", | ||
| "\n", | ||
| "utils.print_ok('All done!')" | ||
| ] | ||
| } | ||
| ], | ||
| "metadata": { | ||
| "kernelspec": { | ||
| "display_name": "APIM Samples Python 3.12", | ||
| "language": "python", | ||
| "name": "apim-samples" | ||
| }, | ||
| "language_info": { | ||
| "codemirror_mode": { | ||
| "name": "ipython", | ||
| "version": 3 | ||
| }, | ||
| "file_extension": ".py", | ||
| "mimetype": "text/x-python", | ||
| "name": "python", | ||
| "nbconvert_exporter": "python", | ||
| "pygments_lexer": "ipython3", | ||
| "version": "3.12.10" | ||
| } | ||
| }, | ||
| "nbformat": 4, | ||
| "nbformat_minor": 2 | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.