Skip to content

Commit

Permalink
Add managed_identity_client_id argument to DefaultAzureCredential (#1…
Browse files Browse the repository at this point in the history
  • Loading branch information
chlowell authored Aug 28, 2020
1 parent 3891c08 commit 902b5e8
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 20 deletions.
3 changes: 3 additions & 0 deletions sdk/identity/azure-identity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
- Application authentication APIs from 1.4.0b7
- `ManagedIdentityCredential` supports the latest version of App Service
([#11346](https://github.com/Azure/azure-sdk-for-python/issues/11346))
- `DefaultAzureCredential` allows specifying the client ID of a user-assigned
managed identity via keyword argument `managed_identity_client_id`
([#12991](https://github.com/Azure/azure-sdk-for-python/issues/12991))

## 1.4.0 (2020-08-10)
### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class DefaultAzureCredential(ChainedTokenCredential):
:keyword str interactive_browser_tenant_id: Tenant ID to use when authenticating a user through
:class:`~azure.identity.InteractiveBrowserCredential`. Defaults to the value of environment variable
AZURE_TENANT_ID, if any. If unspecified, users will authenticate in their home tenants.
:keyword str managed_identity_client_id: The client ID of a user-assigned managed identity. Defaults to the value
of the environment variable AZURE_CLIENT_ID, if any. If not specified, a system-assigned identity will be used.
:keyword str shared_cache_username: Preferred username for :class:`~azure.identity.SharedTokenCacheCredential`.
Defaults to the value of environment variable AZURE_USERNAME, if any.
:keyword str shared_cache_tenant_id: Preferred tenant for :class:`~azure.identity.SharedTokenCacheCredential`.
Expand All @@ -79,6 +81,10 @@ def __init__(self, **kwargs):
"interactive_browser_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
)

managed_identity_client_id = kwargs.pop(
"managed_identity_client_id", os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID)
)

shared_cache_username = kwargs.pop("shared_cache_username", os.environ.get(EnvironmentVariables.AZURE_USERNAME))
shared_cache_tenant_id = kwargs.pop(
"shared_cache_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
Expand All @@ -99,9 +105,7 @@ def __init__(self, **kwargs):
if not exclude_environment_credential:
credentials.append(EnvironmentCredential(authority=authority, **kwargs))
if not exclude_managed_identity_credential:
credentials.append(
ManagedIdentityCredential(client_id=os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID), **kwargs)
)
credentials.append(ManagedIdentityCredential(client_id=managed_identity_client_id, **kwargs))
if not exclude_shared_token_cache_credential and SharedTokenCacheCredential.supported():
try:
# username and/or tenant_id are only required when the cache contains tokens for multiple identities
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class DefaultAzureCredential(ChainedTokenCredential):
Defaults to **False**.
:keyword bool exclude_shared_token_cache_credential: Whether to exclude the shared token cache. Defaults to
**False**.
:keyword str managed_identity_client_id: The client ID of a user-assigned managed identity. Defaults to the value
of the environment variable AZURE_CLIENT_ID, if any. If not specified, a system-assigned identity will be used.
:keyword str shared_cache_username: Preferred username for :class:`~azure.identity.SharedTokenCacheCredential`.
Defaults to the value of environment variable AZURE_USERNAME, if any.
:keyword str shared_cache_tenant_id: Preferred tenant for :class:`~azure.identity.SharedTokenCacheCredential`.
Expand All @@ -67,6 +69,10 @@ def __init__(self, **kwargs: "Any") -> None:
"shared_cache_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
)

managed_identity_client_id = kwargs.pop(
"managed_identity_client_id", os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID)
)

vscode_tenant_id = kwargs.pop(
"visual_studio_code_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID)
)
Expand All @@ -82,7 +88,7 @@ def __init__(self, **kwargs: "Any") -> None:
credentials.append(EnvironmentCredential(authority=authority, **kwargs))
if not exclude_managed_identity_credential:
credentials.append(
ManagedIdentityCredential(client_id=os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID), **kwargs)
ManagedIdentityCredential(client_id=managed_identity_client_id, **kwargs)
)
if not exclude_shared_token_cache_credential and SharedTokenCacheCredential.supported():
try:
Expand Down
23 changes: 15 additions & 8 deletions sdk/identity/azure-identity/tests/test_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,20 +265,27 @@ def test_default_credential_shared_cache_use(mock_credential):


def test_managed_identity_client_id():
"""The credential should initialize ManagedIdentityCredential with the value of AZURE_CLIENT_ID"""
"""the credential should accept a user-assigned managed identity's client ID by kwarg or environment variable"""

expected_client_id = "the-client"
with patch.dict(os.environ, {EnvironmentVariables.AZURE_CLIENT_ID: expected_client_id}, clear=True):
with patch(DefaultAzureCredential.__module__ + ".ManagedIdentityCredential") as mock_credential:
DefaultAzureCredential()
expected_args = {"client_id": "the-client"}

mock_credential.assert_called_once_with(client_id=expected_client_id)
with patch(DefaultAzureCredential.__module__ + ".ManagedIdentityCredential") as mock_credential:
DefaultAzureCredential(managed_identity_client_id=expected_args["client_id"])
mock_credential.assert_called_once_with(**expected_args)

with patch.dict(os.environ, {}, clear=True):
# client id can also be specified in $AZURE_CLIENT_ID
with patch.dict(os.environ, {EnvironmentVariables.AZURE_CLIENT_ID: expected_args["client_id"]}, clear=True):
with patch(DefaultAzureCredential.__module__ + ".ManagedIdentityCredential") as mock_credential:
DefaultAzureCredential()
mock_credential.assert_called_once_with(**expected_args)

mock_credential.assert_called_once_with(client_id=None)
# keyword argument should override environment variable
with patch.dict(
os.environ, {EnvironmentVariables.AZURE_CLIENT_ID: "not-" + expected_args["client_id"]}, clear=True
):
with patch(DefaultAzureCredential.__module__ + ".ManagedIdentityCredential") as mock_credential:
DefaultAzureCredential(managed_identity_client_id=expected_args["client_id"])
mock_credential.assert_called_once_with(**expected_args)


def get_credential_for_shared_cache_test(expected_refresh_token, expected_access_token, cache, **kwargs):
Expand Down
23 changes: 15 additions & 8 deletions sdk/identity/azure-identity/tests/test_default_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,20 +253,27 @@ async def test_default_credential_shared_cache_use():


def test_managed_identity_client_id():
"""The credential should initialize ManagedIdentityCredential with the value of AZURE_CLIENT_ID"""
"""the credential should accept a user-assigned managed identity's client ID by kwarg or environment variable"""

expected_client_id = "the-client"
with patch.dict(os.environ, {EnvironmentVariables.AZURE_CLIENT_ID: expected_client_id}, clear=True):
with patch(DefaultAzureCredential.__module__ + ".ManagedIdentityCredential") as mock_credential:
DefaultAzureCredential()
expected_args = {"client_id": "the client"}

mock_credential.assert_called_once_with(client_id=expected_client_id)
with patch(DefaultAzureCredential.__module__ + ".ManagedIdentityCredential") as mock_credential:
DefaultAzureCredential(managed_identity_client_id=expected_args["client_id"])
mock_credential.assert_called_once_with(**expected_args)

with patch.dict(os.environ, {}, clear=True):
# client id can also be specified in $AZURE_CLIENT_ID
with patch.dict(os.environ, {EnvironmentVariables.AZURE_CLIENT_ID: expected_args["client_id"]}, clear=True):
with patch(DefaultAzureCredential.__module__ + ".ManagedIdentityCredential") as mock_credential:
DefaultAzureCredential()
mock_credential.assert_called_once_with(**expected_args)

mock_credential.assert_called_once_with(client_id=None)
# keyword argument should override environment variable
with patch.dict(
os.environ, {EnvironmentVariables.AZURE_CLIENT_ID: "not-" + expected_args["client_id"]}, clear=True
):
with patch(DefaultAzureCredential.__module__ + ".ManagedIdentityCredential") as mock_credential:
DefaultAzureCredential(managed_identity_client_id=expected_args["client_id"])
mock_credential.assert_called_once_with(**expected_args)


def get_credential_for_shared_cache_test(expected_refresh_token, expected_access_token, cache, **kwargs):
Expand Down

0 comments on commit 902b5e8

Please sign in to comment.