Skip to content
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

Add managed_identity_client_id argument to DefaultAzureCredential #13218

Merged
merged 1 commit into from
Aug 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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