Skip to content

Commit 6b4267a

Browse files
authored
[Monitor Query] MetricsClient sovereign cloud support (#35502)
This adds some documentation and samples regarding using MetricsClient with sovereign clouds. This adds a feature that extrapolates the correct audience based on the passed in endpoint. This saves the user from having to pass in extra information via the "audience" kwarg. Signed-off-by: Paul Van Eck <[email protected]>
1 parent 514b03b commit 6b4267a

File tree

9 files changed

+124
-7
lines changed

9 files changed

+124
-7
lines changed

sdk/monitor/azure-monitor-query/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### Features Added
66

7+
- An `audience` keyword argument can now be passed to the `MetricsClient` constructor to specify the audience for the authentication token. This is useful when querying metrics in sovereign clouds. ([#35502](https://github.com/Azure/azure-sdk-for-python/pull/35502))
8+
79
### Breaking Changes
810

911
### Bugs Fixed

sdk/monitor/azure-monitor-query/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,20 @@ async_metrics_client = MetricsClient("https://<regional endpoint>", credential)
6969

7070
#### Configure client for Azure sovereign cloud
7171

72-
By default, `LogsQueryClient` and `MetricsQueryClient` are configured to use the Azure Public Cloud. To use a sovereign cloud instead, provide the correct `endpoint` argument. For example:
72+
By default, all clients are configured to use the Azure public cloud. To use a sovereign cloud, provide the correct `endpoint` argument when using `LogsQueryClient` or `MetricsQueryClient`. For `MetricsClient`, provide the correct `audience` argument instead. For example:
7373

7474
```python
7575
from azure.identity import AzureAuthorityHosts, DefaultAzureCredential
76-
from azure.monitor.query import LogsQueryClient, MetricsQueryClient
76+
from azure.monitor.query import LogsQueryClient, MetricsQueryClient, MetricsClient
7777

7878
# Authority can also be set via the AZURE_AUTHORITY_HOST environment variable.
7979
credential = DefaultAzureCredential(authority=AzureAuthorityHosts.AZURE_GOVERNMENT)
8080

8181
logs_query_client = LogsQueryClient(credential, endpoint="https://api.loganalytics.us/v1")
8282
metrics_query_client = MetricsQueryClient(credential, endpoint="https://management.usgovcloudapi.net")
83+
metrics_client = MetricsClient(
84+
"https://usgovvirginia.metrics.monitor.azure.us", credential, audience="https://metrics.monitor.azure.us"
85+
)
8386
```
8487

8588
**Note**: Currently, `MetricsQueryClient` uses the Azure Resource Manager (ARM) endpoint for querying metrics. You need the corresponding management endpoint for your cloud when using this client. This detail is subject to change in the future.

sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_client.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,26 @@ class MetricsClient: # pylint: disable=client-accepts-api-version-keyword
2828
resources. For global resources, the region should be 'global'. Required.
2929
:param credential: The credential to authenticate the client.
3030
:type credential: ~azure.core.credentials.TokenCredential
31+
:keyword str audience: The audience to use when requesting tokens for Microsoft Entra ID. Defaults to the public
32+
cloud audience (https://metrics.monitor.azure.com).
33+
34+
.. admonition:: Example:
35+
36+
.. literalinclude:: ../samples/sample_authentication.py
37+
:start-after: [START create_metrics_client]
38+
:end-before: [END create_metrics_client]
39+
:language: python
40+
:dedent: 4
41+
:caption: Creating the MetricsClient with a TokenCredential.
42+
43+
.. admonition:: Example:
44+
45+
.. literalinclude:: ../samples/sample_authentication.py
46+
:start-after: [START create_metrics_client_sovereign_cloud]
47+
:end-before: [END create_metrics_client_sovereign_cloud]
48+
:language: python
49+
:dedent: 4
50+
:caption: Creating the MetricsClient for use with a sovereign cloud (i.e. non-public cloud).
3151
"""
3252

3353
def __init__(self, endpoint: str, credential: TokenCredential, **kwargs: Any) -> None:
@@ -59,7 +79,7 @@ def query_resources(
5979
order_by: Optional[str] = None,
6080
filter: Optional[str] = None,
6181
roll_up_by: Optional[str] = None,
62-
**kwargs: Any
82+
**kwargs: Any,
6383
) -> List[MetricsQueryResult]:
6484
"""Lists the metric values for multiple resources.
6585
@@ -154,7 +174,7 @@ def query_resources(
154174
orderby=order_by,
155175
filter=filter,
156176
rollupby=roll_up_by, # cspell:ignore rollupby
157-
**kwargs
177+
**kwargs,
158178
)
159179

160180
# In rare cases, the generated value is a JSON string instead of a dict. This potentially stems from a bug in

sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_client_async.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,26 @@ class MetricsClient: # pylint: disable=client-accepts-api-version-keyword
2929
resources. For global resources, the region should be 'global'. Required.
3030
:param credential: The credential to authenticate the client.
3131
:type credential: ~azure.core.credentials_async.AsyncTokenCredential
32+
:keyword str audience: The audience to use when requesting tokens for Microsoft Entra ID. Defaults to the public
33+
cloud audience (https://metrics.monitor.azure.com).
34+
35+
.. admonition:: Example:
36+
37+
.. literalinclude:: ../samples/async_samples/sample_authentication_async.py
38+
:start-after: [START create_metrics_client_async]
39+
:end-before: [END create_metrics_client_async]
40+
:language: python
41+
:dedent: 4
42+
:caption: Creating the asynchronous MetricsClient with a TokenCredential.
43+
44+
.. admonition:: Example:
45+
46+
.. literalinclude:: ../samples/async_samples/sample_authentication_async.py
47+
:start-after: [START create_metrics_client_sovereign_cloud_async]
48+
:end-before: [END create_metrics_client_sovereign_cloud_async]
49+
:language: python
50+
:dedent: 4
51+
:caption: Creating the MetricsClient for use with a sovereign cloud (i.e. non-public cloud).
3252
"""
3353

3454
def __init__(self, endpoint: str, credential: AsyncTokenCredential, **kwargs: Any) -> None:
@@ -60,7 +80,7 @@ async def query_resources(
6080
order_by: Optional[str] = None,
6181
filter: Optional[str] = None,
6282
roll_up_by: Optional[str] = None,
63-
**kwargs: Any
83+
**kwargs: Any,
6484
) -> List[MetricsQueryResult]:
6585
"""Lists the metric values for multiple resources.
6686
@@ -155,7 +175,7 @@ async def query_resources(
155175
orderby=order_by,
156176
filter=filter,
157177
rollupby=roll_up_by, # cspell:ignore rollupby
158-
**kwargs
178+
**kwargs,
159179
)
160180

161181
# In rare cases, the generated value is a JSON string instead of a dict. This potentially stems from a bug in

sdk/monitor/azure-monitor-query/samples/async_samples/sample_authentication_async.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,38 @@ async def create_metrics_query_client_sovereign_cloud_async():
5656
# [END create_metrics_query_client_sovereign_cloud_async]
5757

5858

59+
async def create_metrics_client_async():
60+
# [START create_metrics_client_async]
61+
from azure.identity.aio import DefaultAzureCredential
62+
from azure.monitor.query.aio import MetricsClient
63+
64+
credential = DefaultAzureCredential()
65+
client = MetricsClient("https://eastus.metrics.monitor.azure.com", credential)
66+
# [END create_metrics_client_async]
67+
68+
69+
async def create_metrics_client_sovereign_cloud_async():
70+
# [START create_metrics_client_sovereign_cloud_async]
71+
from azure.identity import AzureAuthorityHosts
72+
from azure.identity.aio import DefaultAzureCredential
73+
from azure.monitor.query.aio import MetricsClient
74+
75+
credential = DefaultAzureCredential(authority=AzureAuthorityHosts.AZURE_GOVERNMENT)
76+
client = MetricsClient(
77+
"https://usgovvirginia.metrics.monitor.azure.us",
78+
credential,
79+
audience="https://metrics.monitor.azure.us",
80+
)
81+
# [END create_metrics_client_sovereign_cloud_async]
82+
83+
5984
async def main():
6085
await create_logs_query_client_async()
6186
await create_logs_query_client_sovereign_cloud_async()
6287
await create_metrics_query_client_async()
6388
await create_metrics_query_client_sovereign_cloud_async()
89+
await create_metrics_client_async()
90+
await create_metrics_client_sovereign_cloud_async()
6491

6592

6693
if __name__ == '__main__':

sdk/monitor/azure-monitor-query/samples/sample_authentication.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,34 @@ def create_metrics_query_client_sovereign_cloud():
5353
# [END create_metrics_query_client_sovereign_cloud]
5454

5555

56+
def create_metrics_client():
57+
# [START create_metrics_client]
58+
from azure.identity import DefaultAzureCredential
59+
from azure.monitor.query import MetricsClient
60+
61+
credential = DefaultAzureCredential()
62+
client = MetricsClient("https://eastus.metrics.monitor.azure.com", credential)
63+
# [END create_metrics_client]
64+
65+
66+
def create_metrics_client_sovereign_cloud():
67+
# [START create_metrics_client_sovereign_cloud]
68+
from azure.identity import AzureAuthorityHosts, DefaultAzureCredential
69+
from azure.monitor.query import MetricsClient
70+
71+
credential = DefaultAzureCredential(authority=AzureAuthorityHosts.AZURE_GOVERNMENT)
72+
client = MetricsClient(
73+
"https://usgovvirginia.metrics.monitor.azure.us",
74+
credential,
75+
audience="https://metrics.monitor.azure.us",
76+
)
77+
# [END create_metrics_client_sovereign_cloud]
78+
79+
5680
if __name__ == '__main__':
5781
create_logs_query_client()
5882
create_logs_query_client_sovereign_cloud()
5983
create_metrics_query_client()
6084
create_metrics_query_client_sovereign_cloud()
85+
create_metrics_client()
86+
create_metrics_client_sovereign_cloud()

sdk/monitor/azure-monitor-query/tests/base_testcase.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
METRICS_CLIENT_ENVIRONMENT_AUDIENCE_MAP = {
2222
"AzureCloud": "https://metrics.monitor.azure.com",
2323
"AzureChinaCloud": "https://metrics.monitor.azure.cn",
24-
"AzureUSGovernment": "https:/metrics.monitor.azure.us"
24+
"AzureUSGovernment": "https://metrics.monitor.azure.us"
2525
}
2626

2727
TLD_MAP = {

sdk/monitor/azure-monitor-query/tests/test_metrics_client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,12 @@ def test_batch_metrics_granularity(self, recorded_test, monitor_info):
4444
assert metric.timeseries
4545
for t in metric.timeseries:
4646
assert t.metadata_values is not None
47+
48+
def test_client_different_endpoint(self):
49+
credential = self.get_credential(MetricsClient)
50+
endpoint = "https://usgovvirginia.metrics.monitor.azure.us"
51+
audience = "https://metrics.monitor.azure.us"
52+
client = MetricsClient(endpoint, credential, audience=audience)
53+
54+
assert client._endpoint == endpoint
55+
assert f"{audience}/.default" in client._client._config.authentication_policy._scopes

sdk/monitor/azure-monitor-query/tests/test_metrics_client_async.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,13 @@ async def test_batch_metrics_granularity(self, recorded_test, monitor_info):
5252
assert metric.timeseries
5353
for t in metric.timeseries:
5454
assert t.metadata_values is not None
55+
56+
@pytest.mark.asyncio
57+
async def test_client_different_endpoint(self):
58+
credential = self.get_credential(MetricsClient, is_async=True)
59+
endpoint = "https://usgovvirginia.metrics.monitor.azure.us"
60+
audience = "https://metrics.monitor.azure.us"
61+
client = MetricsClient(endpoint, credential, audience=audience)
62+
63+
assert client._endpoint == endpoint
64+
assert f"{audience}/.default" in client._client._config.authentication_policy._scopes

0 commit comments

Comments
 (0)