From 72f624724a513baa1646bbb23b62644c9cadd0a3 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Mon, 30 Jan 2023 16:44:09 -0800 Subject: [PATCH 01/18] Added `WorkloadIdentityCredential` --- sdk/identity/azure-identity/CHANGELOG.md | 1 + .../azure/identity/_credentials/__init__.py | 2 + .../azure/identity/_credentials/default.py | 13 ++-- .../identity/_credentials/token_exchange.py | 2 +- .../_credentials/workload_identity.py | 66 +++++++++++++++++++ .../identity/aio/_credentials/__init__.py | 2 + .../identity/aio/_credentials/default.py | 13 ++-- .../aio/_credentials/workload_identity.py | 56 ++++++++++++++++ .../DefaultAzureCredentialAuthFlow.md | 14 ++-- .../DefaultAzureCredentialAuthFlow.svg | 2 +- 10 files changed, 153 insertions(+), 18 deletions(-) create mode 100644 sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py create mode 100644 sdk/identity/azure-identity/azure/identity/aio/_credentials/workload_identity.py diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index fbec14607ad8..c022ae39a010 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features Added - Added `AzureDeveloperCredential` for Azure Developer CLI. ([#27916](https://github.com/Azure/azure-sdk-for-python/pull/27916)) +- Added `WorkloadIdentityCredential` for Workload Identity Federation on Kubernetes ### Breaking Changes diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/__init__.py b/sdk/identity/azure-identity/azure/identity/_credentials/__init__.py index b95ebc6a9ca4..4b9ed2562c46 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/__init__.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/__init__.py @@ -19,6 +19,7 @@ from .user_password import UsernamePasswordCredential from .vscode import VisualStudioCodeCredential from .client_assertion import ClientAssertionCredential +from .workload_identity import WorkloadIdentityCredential __all__ = [ @@ -39,5 +40,6 @@ "SharedTokenCacheCredential", "AzureCliCredential", "UsernamePasswordCredential", + "WorkloadIdentityCredential", "VisualStudioCodeCredential", ] diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index 34d58dda51db..ed1f353eb5ce 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -18,6 +18,7 @@ from .azure_cli import AzureCliCredential from .azd_cli import AzureDeveloperCliCredential from .vscode import VisualStudioCodeCredential +from .workload_identity import WorkloadIdentityCredential if TYPE_CHECKING: from azure.core.credentials import TokenCredential @@ -33,12 +34,15 @@ class DefaultAzureCredential(ChainedTokenCredential): 1. A service principal configured by environment variables. See :class:`~azure.identity.EnvironmentCredential` for more details. - 2. An Azure managed identity. See :class:`~azure.identity.ManagedIdentityCredential` for more details. - 3. On Windows only: a user who has signed in with a Microsoft application, such as Visual Studio. If multiple + 2. WorkloadIdentityCredential if environment variable configuration is set by the Azure workload + identity webhook. + 3. An Azure managed identity. See :class:`~azure.identity.ManagedIdentityCredential` for more details. + 4. The identity currently logged in to the Azure Developer CLI. + 5. On Windows only: a user who has signed in with a Microsoft application, such as Visual Studio. If multiple identities are in the cache, then the value of the environment variable ``AZURE_USERNAME`` is used to select which identity to use. See :class:`~azure.identity.SharedTokenCacheCredential` for more details. - 4. The identity currently logged in to the Azure CLI. - 5. The identity currently logged in to Azure PowerShell. + 6. The identity currently logged in to the Azure CLI. + 7. The identity currently logged in to Azure PowerShell. This default behavior is configurable with keyword arguments. @@ -119,6 +123,7 @@ def __init__(self, **kwargs: Any) -> None: credentials = [] # type: List[TokenCredential] if not exclude_environment_credential: credentials.append(EnvironmentCredential(authority=authority, **kwargs)) + credentials.append(WorkloadIdentityCredential(client_id=managed_identity_client_id, **kwargs)) if not exclude_managed_identity_credential: credentials.append(ManagedIdentityCredential(client_id=managed_identity_client_id, **kwargs)) if not exclude_azd_cli_credential: diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/token_exchange.py b/sdk/identity/azure-identity/azure/identity/_credentials/token_exchange.py index 6397a5842c2e..3acaaae146f5 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/token_exchange.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/token_exchange.py @@ -21,7 +21,7 @@ def __init__( def get_service_account_token(self) -> str: now = int(time.time()) - if now - self._last_read_time > 300: + if now - self._last_read_time > 600: with open(self._token_file_path) as f: self._jwt = f.read() self._last_read_time = now diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py b/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py new file mode 100644 index 000000000000..6c07426bebcc --- /dev/null +++ b/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py @@ -0,0 +1,66 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from typing import Any +import logging +from azure.core.credentials import AccessToken +from .token_exchange import TokenExchangeCredential +from .._internal.decorators import log_get_token + +_LOGGER = logging.getLogger(__name__) + + +class WorkloadIdentityCredential: + """WorkloadIdentityCredential supports Azure workload identity on Kubernetes. + See https://learn.microsoft.com/azure/aks/workload-identity-overview for more information + + :param str tenant_id: ID of the application's Azure Active Directory tenant. Also called its "directory" ID. + :param str client_id: the client ID of an Azure AD app registration. + :param str file: The path to a file containing a Kubernetes service account token that authenticates the identity. + """ + + def __init__( + self, + tenant_id: str, + client_id: str, + file: str, + **kwargs: Any + ) -> None: + kwargs.pop("token_file_path", None) + self._credential = TokenExchangeCredential( + tenant_id=tenant_id, + client_id=client_id, + token_file_path=file, + **kwargs + ) + + def __enter__(self): + if self._credential: + self._credential.__enter__() + return self + + def __exit__(self, *args): + if self._credential: + self._credential.__exit__(*args) + + def close(self) -> None: + """Close the credential's transport session.""" + self.__exit__() + + @log_get_token("WorkloadIdentityCredential") + def get_token(self, *scopes: str, **kwargs: Any) -> AccessToken: + """Request an access token for `scopes`. + + This method is called automatically by Azure SDK clients. + + :param str scopes: desired scopes for the access token. This method requires at least one scope. + For more information about scopes, see + https://learn.microsoft.com/azure/active-directory/develop/scopes-oidc. + :keyword str tenant_id: optional tenant to include in the token request. + + :rtype: :class:`azure.core.credentials.AccessToken` + + :raises ~azure.identity.CredentialUnavailableError: environment variable configuration is incomplete + """ + return self._credential.get_token(*scopes, **kwargs) diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/__init__.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/__init__.py index 61cd298a9eb0..454bf593f1bd 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/__init__.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/__init__.py @@ -16,6 +16,7 @@ from .azd_cli import AzureDeveloperCliCredential from .vscode import VisualStudioCodeCredential from .client_assertion import ClientAssertionCredential +from .workload_identity import WorkloadIdentityCredential __all__ = [ @@ -33,4 +34,5 @@ "SharedTokenCacheCredential", "VisualStudioCodeCredential", "ClientAssertionCredential", + "WorkloadIdentityCredential", ] diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py index 6947ec006a8e..59b41b403665 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py @@ -17,6 +17,7 @@ from .managed_identity import ManagedIdentityCredential from .shared_cache import SharedTokenCacheCredential from .vscode import VisualStudioCodeCredential +from .workload_identity import WorkloadIdentityCredential if TYPE_CHECKING: from azure.core.credentials_async import AsyncTokenCredential @@ -32,12 +33,15 @@ class DefaultAzureCredential(ChainedTokenCredential): 1. A service principal configured by environment variables. See :class:`~azure.identity.aio.EnvironmentCredential` for more details. - 2. An Azure managed identity. See :class:`~azure.identity.aio.ManagedIdentityCredential` for more details. - 3. On Windows only: a user who has signed in with a Microsoft application, such as Visual Studio. If multiple + 2. WorkloadIdentityCredential if environment variable configuration is set by the Azure workload + identity webhook. + 3. An Azure managed identity. See :class:`~azure.identity.aio.ManagedIdentityCredential` for more details. + 4. The identity currently logged in to the Azure Developer CLI. + 5. On Windows only: a user who has signed in with a Microsoft application, such as Visual Studio. If multiple identities are in the cache, then the value of the environment variable ``AZURE_USERNAME`` is used to select which identity to use. See :class:`~azure.identity.aio.SharedTokenCacheCredential` for more details. - 4. The identity currently logged in to the Azure CLI. - 5. The identity currently logged in to Azure PowerShell. + 6. The identity currently logged in to the Azure CLI. + 7. The identity currently logged in to Azure PowerShell. This default behavior is configurable with keyword arguments. @@ -109,6 +113,7 @@ def __init__(self, **kwargs: Any) -> None: credentials = [] # type: List[AsyncTokenCredential] if not exclude_environment_credential: credentials.append(EnvironmentCredential(authority=authority, **kwargs)) + credentials.append(WorkloadIdentityCredential(client_id=managed_identity_client_id, **kwargs)) if not exclude_managed_identity_credential: credentials.append(ManagedIdentityCredential(client_id=managed_identity_client_id, **kwargs)) if not exclude_azd_cli_credential: diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/workload_identity.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/workload_identity.py new file mode 100644 index 000000000000..f3c50a9d641d --- /dev/null +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/workload_identity.py @@ -0,0 +1,56 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from typing import Any +import logging +from azure.core.credentials import AccessToken +from .token_exchange import TokenExchangeCredential +from .._internal.decorators import log_get_token_async +from .._internal import AsyncContextManager + +_LOGGER = logging.getLogger(__name__) + + +class WorkloadIdentityCredential(AsyncContextManager): + """WorkloadIdentityCredential supports Azure workload identity on Kubernetes. + See https://learn.microsoft.com/azure/aks/workload-identity-overview for more information + + :param str tenant_id: ID of the application's Azure Active Directory tenant. Also called its "directory" ID. + :param str client_id: the client ID of an Azure AD app registration. + :param str file: The path to a file containing a Kubernetes service account token that authenticates the identity. + """ + def __init__(self, tenant_id: str, client_id: str, file: str, **kwargs: Any) -> None: + kwargs.pop("token_file_path", None) + self._credential = TokenExchangeCredential( + tenant_id=tenant_id, + client_id=client_id, + token_file_path=file, + **kwargs + ) + + async def __aenter__(self): + if self._credential: + await self._credential.__aenter__() + return self + + async def close(self) -> None: + """Close the credential's transport session.""" + + if self._credential: + await self._credential.__aexit__() + + @log_get_token_async + async def get_token(self, *scopes: str, **kwargs: Any) -> AccessToken: + """Asynchronously request an access token for `scopes`. + + This method is called automatically by Azure SDK clients. + + :param str scopes: desired scopes for the access token. This method requires at least one scope. + For more information about scopes, see + https://learn.microsoft.com/azure/active-directory/develop/scopes-oidc. + :keyword str tenant_id: optional tenant to include in the token request. + :rtype: :class:`azure.core.credentials.AccessToken` + :raises ~azure.identity.CredentialUnavailableError: environment variable configuration is incomplete + """ + return await self._credential.get_token(*scopes, **kwargs) diff --git a/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.md b/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.md index f4fd2a80e8a0..d4e9c74fc4e0 100644 --- a/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.md +++ b/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.md @@ -5,15 +5,13 @@ %% 2. Run command: mmdc -i DefaultAzureCredentialAuthFlow.md -o DefaultAzureCredentialAuthFlow.svg flowchart LR; - A(Environment):::deployed ==> B(Managed Identity):::deployed ==> C(Azure CLI):::developer ==> D(Azure PowerShell):::developer ==> E(Interactive browser):::interactive; + A(Environment):::deployed ==> B(Workload Identity):::deployed ==> C(Managed Identity):::deployed ==> D(Azure Developer CLI):::developer ==> E(Azure CLI):::developer ==> F(Azure PowerShell):::developer ==> G(Interactive browser):::interactive; subgraph CREDENTIAL TYPES; direction LR; Deployed(Deployed service):::deployed ==> Developer(Developer):::developer ==> Interactive(Interactive developer):::interactive; %% Hide links between boxes in the legend by setting width to 0. The integers after "linkStyle" represent link indices. - linkStyle 4 stroke-width:0px; - linkStyle 5 stroke-width:0px; end; %% Define styles for credential type boxes @@ -22,9 +20,9 @@ flowchart LR; classDef interactive fill:#A5A5A5, stroke:#828282; %% Add API ref links to credential type boxes - click A "https://docs.microsoft.com/python/api/azure-identity/azure.identity.environmentcredential?view=azure-python" _blank; - click B "https://docs.microsoft.com/python/api/azure-identity/azure.identity.managedidentitycredential?view=azure-python" _blank; - click C "https://docs.microsoft.com/python/api/azure-identity/azure.identity.azureclicredential?view=azure-python" _blank; - click D "https://docs.microsoft.com/python/api/azure-identity/azure.identity.azurepowershellcredential?view=azure-python" _blank; - click E "https://docs.microsoft.com/python/api/azure-identity/azure.identity.interactivebrowsercredential?view=azure-python" _blank; + click A "https://learn.microsoft.com/python/api/azure-identity/azure.identity.environmentcredential?view=azure-python" _blank; + click C "https://learn.microsoft.com/python/api/azure-identity/azure.identity.managedidentitycredential?view=azure-python" _blank; + click E "https://learn.microsoft.com/python/api/azure-identity/azure.identity.azureclicredential?view=azure-python" _blank; + click F "https://learn.microsoft.com/python/api/azure-identity/azure.identity.azurepowershellcredential?view=azure-python" _blank; + click G "https://learn.microsoft.com/python/api/azure-identity/azure.identity.interactivebrowsercredential?view=azure-python" _blank; ``` diff --git a/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.svg b/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.svg index 8437c57b3a19..454fb7779eac 100644 --- a/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.svg +++ b/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.svg @@ -1 +1 @@ -
CREDENTIAL TYPES
Interactive developer
Developer
Deployed service
Environment
Managed Identity
Azure CLI
Azure PowerShell
Interactive browser
\ No newline at end of file +
CREDENTIAL TYPES
Interactive developer
Deployed service
Developer
Environment
Workload Identity
Managed Identity
Azure Developer CLI
Azure CLI
Azure PowerShell
Interactive browser
\ No newline at end of file From 1a9e719cc9469be021bbe83fe538f189704e6fa0 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Mon, 30 Jan 2023 17:11:20 -0800 Subject: [PATCH 02/18] update --- .../azure-identity/azure/identity/_credentials/default.py | 8 +++++++- .../azure/identity/aio/_credentials/default.py | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index ed1f353eb5ce..2ef5c09b7561 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -123,7 +123,13 @@ def __init__(self, **kwargs: Any) -> None: credentials = [] # type: List[TokenCredential] if not exclude_environment_credential: credentials.append(EnvironmentCredential(authority=authority, **kwargs)) - credentials.append(WorkloadIdentityCredential(client_id=managed_identity_client_id, **kwargs)) + if all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): + client_id = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) + credentials.append(WorkloadIdentityCredential( + client_id=client_id, + tenant_id=os.environ[EnvironmentVariables.AZURE_TENANT_ID], + file=os.environ[EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE], + **kwargs)) if not exclude_managed_identity_credential: credentials.append(ManagedIdentityCredential(client_id=managed_identity_client_id, **kwargs)) if not exclude_azd_cli_credential: diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py index 59b41b403665..fb90dca5075f 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py @@ -113,7 +113,13 @@ def __init__(self, **kwargs: Any) -> None: credentials = [] # type: List[AsyncTokenCredential] if not exclude_environment_credential: credentials.append(EnvironmentCredential(authority=authority, **kwargs)) - credentials.append(WorkloadIdentityCredential(client_id=managed_identity_client_id, **kwargs)) + if all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): + client_id = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) + credentials.append(WorkloadIdentityCredential( + client_id=client_id, + tenant_id=os.environ[EnvironmentVariables.AZURE_TENANT_ID], + file=os.environ[EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE], + **kwargs)) if not exclude_managed_identity_credential: credentials.append(ManagedIdentityCredential(client_id=managed_identity_client_id, **kwargs)) if not exclude_azd_cli_credential: From 0d6a248bc0ad1d6155981980cb4590460e2a6f30 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 07:37:00 -0800 Subject: [PATCH 03/18] updates --- sdk/identity/azure-identity/azure/identity/__init__.py | 2 ++ .../azure-identity/azure/identity/_credentials/default.py | 2 +- sdk/identity/azure-identity/azure/identity/aio/__init__.py | 2 ++ .../azure-identity/azure/identity/aio/_credentials/default.py | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/__init__.py b/sdk/identity/azure-identity/azure/identity/__init__.py index ae32ac90f66f..8030b55ee033 100644 --- a/sdk/identity/azure-identity/azure/identity/__init__.py +++ b/sdk/identity/azure-identity/azure/identity/__init__.py @@ -25,6 +25,7 @@ SharedTokenCacheCredential, UsernamePasswordCredential, VisualStudioCodeCredential, + WorkloadIdentityCredential, ) from ._persistent_cache import TokenCachePersistenceOptions @@ -53,6 +54,7 @@ "TokenCachePersistenceOptions", "UsernamePasswordCredential", "VisualStudioCodeCredential", + "WorkloadIdentityCredential", ] from ._version import VERSION diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index 2ef5c09b7561..d23434be641c 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -124,7 +124,7 @@ def __init__(self, **kwargs: Any) -> None: if not exclude_environment_credential: credentials.append(EnvironmentCredential(authority=authority, **kwargs)) if all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): - client_id = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) + client_id: str = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) credentials.append(WorkloadIdentityCredential( client_id=client_id, tenant_id=os.environ[EnvironmentVariables.AZURE_TENANT_ID], diff --git a/sdk/identity/azure-identity/azure/identity/aio/__init__.py b/sdk/identity/azure-identity/azure/identity/aio/__init__.py index dbde2601be33..3c891665e83e 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/__init__.py +++ b/sdk/identity/azure-identity/azure/identity/aio/__init__.py @@ -19,6 +19,7 @@ SharedTokenCacheCredential, VisualStudioCodeCredential, ClientAssertionCredential, + WorkloadIdentityCredential, ) @@ -37,4 +38,5 @@ "SharedTokenCacheCredential", "VisualStudioCodeCredential", "ClientAssertionCredential", + "WorkloadIdentityCredential", ] diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py index fb90dca5075f..9b0e8e166099 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py @@ -114,7 +114,7 @@ def __init__(self, **kwargs: Any) -> None: if not exclude_environment_credential: credentials.append(EnvironmentCredential(authority=authority, **kwargs)) if all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): - client_id = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) + client_id: str = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) credentials.append(WorkloadIdentityCredential( client_id=client_id, tenant_id=os.environ[EnvironmentVariables.AZURE_TENANT_ID], From 148dda1831a4135a3773465803ccba62979d06da Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 08:49:44 -0800 Subject: [PATCH 04/18] updates --- .../azure-identity/azure/identity/_credentials/default.py | 6 +++--- .../azure/identity/aio/_credentials/default.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index d23434be641c..b13d22eee77b 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -4,7 +4,7 @@ # ------------------------------------ import logging import os -from typing import List, TYPE_CHECKING, Any +from typing import List, TYPE_CHECKING, Any, cast from azure.core.credentials import AccessToken from .._constants import EnvironmentVariables @@ -124,9 +124,9 @@ def __init__(self, **kwargs: Any) -> None: if not exclude_environment_credential: credentials.append(EnvironmentCredential(authority=authority, **kwargs)) if all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): - client_id: str = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) + client_id = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) credentials.append(WorkloadIdentityCredential( - client_id=client_id, + client_id=cast(client_id, str), tenant_id=os.environ[EnvironmentVariables.AZURE_TENANT_ID], file=os.environ[EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE], **kwargs)) diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py index 9b0e8e166099..45091bb043d6 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py @@ -4,7 +4,7 @@ # ------------------------------------ import logging import os -from typing import List, TYPE_CHECKING, Any +from typing import List, TYPE_CHECKING, Any, cast from azure.core.credentials import AccessToken from ..._constants import EnvironmentVariables @@ -114,9 +114,9 @@ def __init__(self, **kwargs: Any) -> None: if not exclude_environment_credential: credentials.append(EnvironmentCredential(authority=authority, **kwargs)) if all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): - client_id: str = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) + client_id = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) credentials.append(WorkloadIdentityCredential( - client_id=client_id, + client_id=cast(client_id, str), tenant_id=os.environ[EnvironmentVariables.AZURE_TENANT_ID], file=os.environ[EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE], **kwargs)) From 4d4b42ac000c797617a868940b96b01b3d23337e Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 09:51:26 -0800 Subject: [PATCH 05/18] update --- .../azure-identity/azure/identity/_credentials/default.py | 2 +- .../azure-identity/azure/identity/aio/_credentials/default.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index b13d22eee77b..c53f5dbf21e6 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -126,7 +126,7 @@ def __init__(self, **kwargs: Any) -> None: if all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): client_id = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) credentials.append(WorkloadIdentityCredential( - client_id=cast(client_id, str), + client_id=cast(str, client_id), tenant_id=os.environ[EnvironmentVariables.AZURE_TENANT_ID], file=os.environ[EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE], **kwargs)) diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py index 45091bb043d6..f7c3e84a349e 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py @@ -116,7 +116,7 @@ def __init__(self, **kwargs: Any) -> None: if all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): client_id = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) credentials.append(WorkloadIdentityCredential( - client_id=cast(client_id, str), + client_id=cast(str, client_id), tenant_id=os.environ[EnvironmentVariables.AZURE_TENANT_ID], file=os.environ[EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE], **kwargs)) From f8974343149630b1a64b1e46409f0a25377f1d37 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 10:13:27 -0800 Subject: [PATCH 06/18] update --- .../azure-identity/azure/identity/_credentials/default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index c53f5dbf21e6..fa59872f0de1 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -80,7 +80,7 @@ class DefaultAzureCredential(ChainedTokenCredential): Directory work or school accounts. """ - def __init__(self, **kwargs: Any) -> None: + def __init__(self, **kwargs: Any) -> None: # pylint: disable=too-many-statements if "tenant_id" in kwargs: raise TypeError("'tenant_id' is not supported in DefaultAzureCredential.") From b3f3747bdf4e65053668c2c8baf83569f030dcc4 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 11:17:42 -0800 Subject: [PATCH 07/18] updates --- .../identity/_credentials/managed_identity.py | 8 +-- .../identity/_credentials/token_exchange.py | 45 ------------- .../_credentials/workload_identity.py | 65 ++++++++----------- .../aio/_credentials/managed_identity.py | 8 +-- .../aio/_credentials/token_exchange.py | 18 ----- .../aio/_credentials/workload_identity.py | 43 ++---------- 6 files changed, 40 insertions(+), 147 deletions(-) delete mode 100644 sdk/identity/azure-identity/azure/identity/_credentials/token_exchange.py delete mode 100644 sdk/identity/azure-identity/azure/identity/aio/_credentials/token_exchange.py diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/managed_identity.py b/sdk/identity/azure-identity/azure/identity/_credentials/managed_identity.py index 39ec113d9b60..625a80de1342 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/managed_identity.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/managed_identity.py @@ -64,17 +64,17 @@ def __init__(self, **kwargs: Any) -> None: self._credential = CloudShellCredential(**kwargs) elif all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): - _LOGGER.info("%s will use token exchange", self.__class__.__name__) - from .token_exchange import TokenExchangeCredential + _LOGGER.info("%s will use workload identity", self.__class__.__name__) + from .workload_identity import WorkloadIdentityCredential client_id = kwargs.pop("client_id", None) or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) if not client_id: raise ValueError('Configure the environment with a client ID or pass a value for "client_id" argument') - self._credential = TokenExchangeCredential( + self._credential = WorkloadIdentityCredential( tenant_id=os.environ[EnvironmentVariables.AZURE_TENANT_ID], client_id=client_id, - token_file_path=os.environ[EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE], + file=os.environ[EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE], **kwargs ) else: diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/token_exchange.py b/sdk/identity/azure-identity/azure/identity/_credentials/token_exchange.py deleted file mode 100644 index 3acaaae146f5..000000000000 --- a/sdk/identity/azure-identity/azure/identity/_credentials/token_exchange.py +++ /dev/null @@ -1,45 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import time -from typing import Any - -from .client_assertion import ClientAssertionCredential - - -class TokenFileMixin: - def __init__( - self, - token_file_path: str, - **_: Any - ) -> None: - super(TokenFileMixin, self).__init__() - self._jwt = "" - self._last_read_time = 0 - self._token_file_path = token_file_path - - def get_service_account_token(self) -> str: - now = int(time.time()) - if now - self._last_read_time > 600: - with open(self._token_file_path) as f: - self._jwt = f.read() - self._last_read_time = now - return self._jwt - - -class TokenExchangeCredential(ClientAssertionCredential, TokenFileMixin): - def __init__( - self, - tenant_id: str, - client_id: str, - token_file_path: str, - **kwargs: Any - ) -> None: - super(TokenExchangeCredential, self).__init__( - tenant_id=tenant_id, - client_id=client_id, - func=self.get_service_account_token, - token_file_path=token_file_path, - **kwargs - ) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py b/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py index 6c07426bebcc..6b8948d14b0c 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py @@ -2,16 +2,33 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +import time from typing import Any -import logging -from azure.core.credentials import AccessToken -from .token_exchange import TokenExchangeCredential -from .._internal.decorators import log_get_token -_LOGGER = logging.getLogger(__name__) +from .client_assertion import ClientAssertionCredential -class WorkloadIdentityCredential: +class TokenFileMixin: + def __init__( + self, + file: str, + **_: Any + ) -> None: + super(TokenFileMixin, self).__init__() + self._jwt = "" + self._last_read_time = 0 + self._file = file + + def get_service_account_token(self) -> str: + now = int(time.time()) + if now - self._last_read_time > 600: + with open(self._file) as f: + self._jwt = f.read() + self._last_read_time = now + return self._jwt + + +class WorkloadIdentityCredential(ClientAssertionCredential, TokenFileMixin): """WorkloadIdentityCredential supports Azure workload identity on Kubernetes. See https://learn.microsoft.com/azure/aks/workload-identity-overview for more information @@ -27,40 +44,10 @@ def __init__( file: str, **kwargs: Any ) -> None: - kwargs.pop("token_file_path", None) - self._credential = TokenExchangeCredential( + super(WorkloadIdentityCredential, self).__init__( tenant_id=tenant_id, client_id=client_id, - token_file_path=file, + func=self.get_service_account_token, + file=file, **kwargs ) - - def __enter__(self): - if self._credential: - self._credential.__enter__() - return self - - def __exit__(self, *args): - if self._credential: - self._credential.__exit__(*args) - - def close(self) -> None: - """Close the credential's transport session.""" - self.__exit__() - - @log_get_token("WorkloadIdentityCredential") - def get_token(self, *scopes: str, **kwargs: Any) -> AccessToken: - """Request an access token for `scopes`. - - This method is called automatically by Azure SDK clients. - - :param str scopes: desired scopes for the access token. This method requires at least one scope. - For more information about scopes, see - https://learn.microsoft.com/azure/active-directory/develop/scopes-oidc. - :keyword str tenant_id: optional tenant to include in the token request. - - :rtype: :class:`azure.core.credentials.AccessToken` - - :raises ~azure.identity.CredentialUnavailableError: environment variable configuration is incomplete - """ - return self._credential.get_token(*scopes, **kwargs) diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/managed_identity.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/managed_identity.py index c06abfff440c..7358b61e586a 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/managed_identity.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/managed_identity.py @@ -71,17 +71,17 @@ def __init__(self, **kwargs: Any) -> None: self._credential = CloudShellCredential(**kwargs) elif all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): - _LOGGER.info("%s will use token exchange", self.__class__.__name__) - from .token_exchange import TokenExchangeCredential + _LOGGER.info("%s will use workload identity", self.__class__.__name__) + from .workload_identity import WorkloadIdentityCredential client_id = kwargs.pop("client_id", None) or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) if not client_id: raise ValueError('Configure the environment with a client ID or pass a value for "client_id" argument') - self._credential = TokenExchangeCredential( + self._credential = WorkloadIdentityCredential( tenant_id=os.environ[EnvironmentVariables.AZURE_TENANT_ID], client_id=client_id, - token_file_path=os.environ[EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE], + file=os.environ[EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE], **kwargs ) else: diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/token_exchange.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/token_exchange.py deleted file mode 100644 index 54547aa546e6..000000000000 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/token_exchange.py +++ /dev/null @@ -1,18 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -from typing import Any -from .client_assertion import ClientAssertionCredential -from ..._credentials.token_exchange import TokenFileMixin - - -class TokenExchangeCredential(ClientAssertionCredential, TokenFileMixin): - def __init__(self, tenant_id: str, client_id: str, token_file_path: str, **kwargs: Any) -> None: - super().__init__( - tenant_id=tenant_id, - client_id=client_id, - func=self.get_service_account_token, - token_file_path=token_file_path, - **kwargs - ) diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/workload_identity.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/workload_identity.py index f3c50a9d641d..6e7e3cb899f7 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/workload_identity.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/workload_identity.py @@ -3,16 +3,11 @@ # Licensed under the MIT License. # ------------------------------------ from typing import Any -import logging -from azure.core.credentials import AccessToken -from .token_exchange import TokenExchangeCredential -from .._internal.decorators import log_get_token_async -from .._internal import AsyncContextManager +from .client_assertion import ClientAssertionCredential +from ..._credentials.workload_identity import TokenFileMixin -_LOGGER = logging.getLogger(__name__) - -class WorkloadIdentityCredential(AsyncContextManager): +class WorkloadIdentityCredential(ClientAssertionCredential, TokenFileMixin): """WorkloadIdentityCredential supports Azure workload identity on Kubernetes. See https://learn.microsoft.com/azure/aks/workload-identity-overview for more information @@ -21,36 +16,10 @@ class WorkloadIdentityCredential(AsyncContextManager): :param str file: The path to a file containing a Kubernetes service account token that authenticates the identity. """ def __init__(self, tenant_id: str, client_id: str, file: str, **kwargs: Any) -> None: - kwargs.pop("token_file_path", None) - self._credential = TokenExchangeCredential( + super().__init__( tenant_id=tenant_id, client_id=client_id, - token_file_path=file, + func=self.get_service_account_token, + file=file, **kwargs ) - - async def __aenter__(self): - if self._credential: - await self._credential.__aenter__() - return self - - async def close(self) -> None: - """Close the credential's transport session.""" - - if self._credential: - await self._credential.__aexit__() - - @log_get_token_async - async def get_token(self, *scopes: str, **kwargs: Any) -> AccessToken: - """Asynchronously request an access token for `scopes`. - - This method is called automatically by Azure SDK clients. - - :param str scopes: desired scopes for the access token. This method requires at least one scope. - For more information about scopes, see - https://learn.microsoft.com/azure/active-directory/develop/scopes-oidc. - :keyword str tenant_id: optional tenant to include in the token request. - :rtype: :class:`azure.core.credentials.AccessToken` - :raises ~azure.identity.CredentialUnavailableError: environment variable configuration is incomplete - """ - return await self._credential.get_token(*scopes, **kwargs) From f883e7d2278b772a296962589317afeabb1cfe0d Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 13:06:23 -0800 Subject: [PATCH 08/18] update --- .../images/mermaidjs/DefaultAzureCredentialAuthFlow.md | 2 ++ .../images/mermaidjs/DefaultAzureCredentialAuthFlow.svg | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.md b/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.md index d4e9c74fc4e0..fefb18853333 100644 --- a/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.md +++ b/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.md @@ -12,6 +12,8 @@ flowchart LR; Deployed(Deployed service):::deployed ==> Developer(Developer):::developer ==> Interactive(Interactive developer):::interactive; %% Hide links between boxes in the legend by setting width to 0. The integers after "linkStyle" represent link indices. + linkStyle 6 stroke-width:0px; + linkStyle 7 stroke-width:0px; end; %% Define styles for credential type boxes diff --git a/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.svg b/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.svg index 454fb7779eac..39f80536b83b 100644 --- a/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.svg +++ b/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.svg @@ -1 +1 @@ -
CREDENTIAL TYPES
Interactive developer
Deployed service
Developer
Environment
Workload Identity
Managed Identity
Azure Developer CLI
Azure CLI
Azure PowerShell
Interactive browser
\ No newline at end of file +
CREDENTIAL TYPES
Interactive developer
Deployed service
Developer
Environment
Workload Identity
Managed Identity
Azure Developer CLI
Azure CLI
Azure PowerShell
Interactive browser
\ No newline at end of file From 13227c6433a02c28c6dc1e23c0563070491443dc Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 13:14:27 -0800 Subject: [PATCH 09/18] Update sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py Co-authored-by: Paul Van Eck --- .../azure/identity/_credentials/workload_identity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py b/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py index 6b8948d14b0c..cc39081635d5 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py @@ -33,7 +33,7 @@ class WorkloadIdentityCredential(ClientAssertionCredential, TokenFileMixin): See https://learn.microsoft.com/azure/aks/workload-identity-overview for more information :param str tenant_id: ID of the application's Azure Active Directory tenant. Also called its "directory" ID. - :param str client_id: the client ID of an Azure AD app registration. + :param str client_id: The client ID of an Azure AD app registration. :param str file: The path to a file containing a Kubernetes service account token that authenticates the identity. """ From fadf86067d5e71f5b3d8f7ffc66b4b3c6f02c47b Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 13:14:36 -0800 Subject: [PATCH 10/18] Update sdk/identity/azure-identity/CHANGELOG.md Co-authored-by: Paul Van Eck --- sdk/identity/azure-identity/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/identity/azure-identity/CHANGELOG.md b/sdk/identity/azure-identity/CHANGELOG.md index 9a4b3c5a0d95..686b1dbc72d8 100644 --- a/sdk/identity/azure-identity/CHANGELOG.md +++ b/sdk/identity/azure-identity/CHANGELOG.md @@ -5,7 +5,7 @@ ### Features Added - Added `AzureDeveloperCredential` for Azure Developer CLI. ([#27916](https://github.com/Azure/azure-sdk-for-python/pull/27916)) -- Added `WorkloadIdentityCredential` for Workload Identity Federation on Kubernetes +- Added `WorkloadIdentityCredential` for Workload Identity Federation on Kubernetes ([#28536](https://github.com/Azure/azure-sdk-for-python/pull/28536)) - Added support to use "TryAutoDetect" as the value for `AZURE_REGIONAL_AUTHORITY_NAME` to enable auto detecting the appropriate authority ([#526](https://github.com/AzureAD/microsoft-authentication-library-for-python/issues/526)) ## 1.13.0b1 (2023-01-10) From c57c090d2c534cd622bd9e81b5e1959a7498b468 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 13:14:50 -0800 Subject: [PATCH 11/18] Update sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py Co-authored-by: Paul Van Eck --- .../azure/identity/_credentials/workload_identity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py b/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py index cc39081635d5..278bee487740 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py @@ -30,7 +30,7 @@ def get_service_account_token(self) -> str: class WorkloadIdentityCredential(ClientAssertionCredential, TokenFileMixin): """WorkloadIdentityCredential supports Azure workload identity on Kubernetes. - See https://learn.microsoft.com/azure/aks/workload-identity-overview for more information + See the `workload identity overview `_ for more information. :param str tenant_id: ID of the application's Azure Active Directory tenant. Also called its "directory" ID. :param str client_id: The client ID of an Azure AD app registration. From 0fe40b16636fa6140e765a150fa1f8e7880aa56c Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 13:56:28 -0800 Subject: [PATCH 12/18] updates --- .../azure/identity/_credentials/workload_identity.py | 3 ++- .../azure/identity/aio/_credentials/workload_identity.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py b/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py index 278bee487740..e224d8a7a2b9 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/workload_identity.py @@ -30,7 +30,8 @@ def get_service_account_token(self) -> str: class WorkloadIdentityCredential(ClientAssertionCredential, TokenFileMixin): """WorkloadIdentityCredential supports Azure workload identity on Kubernetes. - See the `workload identity overview `_ for more information. + See the `workload identity overview `_ + for more information. :param str tenant_id: ID of the application's Azure Active Directory tenant. Also called its "directory" ID. :param str client_id: The client ID of an Azure AD app registration. diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/workload_identity.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/workload_identity.py index 6e7e3cb899f7..b46090677449 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/workload_identity.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/workload_identity.py @@ -9,10 +9,11 @@ class WorkloadIdentityCredential(ClientAssertionCredential, TokenFileMixin): """WorkloadIdentityCredential supports Azure workload identity on Kubernetes. - See https://learn.microsoft.com/azure/aks/workload-identity-overview for more information + See the `workload identity overview `_ + for more information. :param str tenant_id: ID of the application's Azure Active Directory tenant. Also called its "directory" ID. - :param str client_id: the client ID of an Azure AD app registration. + :param str client_id: The client ID of an Azure AD app registration. :param str file: The path to a file containing a Kubernetes service account token that authenticates the identity. """ def __init__(self, tenant_id: str, client_id: str, file: str, **kwargs: Any) -> None: From 4b2224da0c5c88e9019fe3348b0858f520d275e8 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 13:57:41 -0800 Subject: [PATCH 13/18] updates --- sdk/identity/azure-identity/azure/identity/_constants.py | 2 +- .../azure-identity/azure/identity/_credentials/default.py | 2 +- .../azure/identity/_credentials/managed_identity.py | 2 +- .../azure-identity/azure/identity/aio/_credentials/default.py | 2 +- .../azure/identity/aio/_credentials/managed_identity.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_constants.py b/sdk/identity/azure-identity/azure/identity/_constants.py index ab091afa0283..846a26757ae7 100644 --- a/sdk/identity/azure-identity/azure/identity/_constants.py +++ b/sdk/identity/azure-identity/azure/identity/_constants.py @@ -49,4 +49,4 @@ class EnvironmentVariables: AZURE_REGIONAL_AUTHORITY_NAME = "AZURE_REGIONAL_AUTHORITY_NAME" AZURE_FEDERATED_TOKEN_FILE = "AZURE_FEDERATED_TOKEN_FILE" - TOKEN_EXCHANGE_VARS = (AZURE_AUTHORITY_HOST, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN_FILE) + WORKLOAD_IDENTITY_VARS = (AZURE_AUTHORITY_HOST, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN_FILE) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index fa59872f0de1..d67b9d6dc750 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -123,7 +123,7 @@ def __init__(self, **kwargs: Any) -> None: # pylint: disable=too-many-statement credentials = [] # type: List[TokenCredential] if not exclude_environment_credential: credentials.append(EnvironmentCredential(authority=authority, **kwargs)) - if all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): + if all(os.environ.get(var) for var in EnvironmentVariables.WORKLOAD_IDENTITY_VARS): client_id = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) credentials.append(WorkloadIdentityCredential( client_id=cast(str, client_id), diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/managed_identity.py b/sdk/identity/azure-identity/azure/identity/_credentials/managed_identity.py index 625a80de1342..4cc6c7b81c64 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/managed_identity.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/managed_identity.py @@ -63,7 +63,7 @@ def __init__(self, **kwargs: Any) -> None: from .cloud_shell import CloudShellCredential self._credential = CloudShellCredential(**kwargs) - elif all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): + elif all(os.environ.get(var) for var in EnvironmentVariables.WORKLOAD_IDENTITY_VARS): _LOGGER.info("%s will use workload identity", self.__class__.__name__) from .workload_identity import WorkloadIdentityCredential diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py index f7c3e84a349e..02de0ad5563c 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py @@ -113,7 +113,7 @@ def __init__(self, **kwargs: Any) -> None: credentials = [] # type: List[AsyncTokenCredential] if not exclude_environment_credential: credentials.append(EnvironmentCredential(authority=authority, **kwargs)) - if all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): + if all(os.environ.get(var) for var in EnvironmentVariables.WORKLOAD_IDENTITY_VARS): client_id = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) credentials.append(WorkloadIdentityCredential( client_id=cast(str, client_id), diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/managed_identity.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/managed_identity.py index 7358b61e586a..5a520c71b14e 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/managed_identity.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/managed_identity.py @@ -70,7 +70,7 @@ def __init__(self, **kwargs: Any) -> None: from .cloud_shell import CloudShellCredential self._credential = CloudShellCredential(**kwargs) - elif all(os.environ.get(var) for var in EnvironmentVariables.TOKEN_EXCHANGE_VARS): + elif all(os.environ.get(var) for var in EnvironmentVariables.WORKLOAD_IDENTITY_VARS): _LOGGER.info("%s will use workload identity", self.__class__.__name__) from .workload_identity import WorkloadIdentityCredential From 7efd5598fb35a09a7c706e60d567863dd121cf40 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 16:23:59 -0800 Subject: [PATCH 14/18] update --- sdk/identity/azure-identity/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sdk/identity/azure-identity/README.md b/sdk/identity/azure-identity/README.md index 6a398ebbf509..358861384030 100644 --- a/sdk/identity/azure-identity/README.md +++ b/sdk/identity/azure-identity/README.md @@ -50,13 +50,11 @@ authentication flow. This can also be selected manually by running `az login --u #### Authenticate via the Azure Developer CLI -`DefaultAzureCredential` and `AzureDeveloperCliCredential` can authenticate as the user -signed in to the [Azure Developer CLI][azd_cli]. To sign in to the Azure Developer CLI, run -`azd login`. On a system with a default web browser, the Azure Developer CLI will launch -the browser to authenticate a user. +Developers coding outside of an IDE can also use the [Azure Developer CLI][azure_developer_cli] to authenticate. Applications using the `DefaultAzureCredential` or the `AzureDeveloperCliCredential` can then use this account to authenticate calls in their application when running locally. + +To authenticate with the [Azure Developer CLI][azure_developer_cli], users can run the command `azd login`. For users running on a system with a default web browser, the Azure Developer CLI will launch the browser to authenticate the user. -When no default browser is available, `azd login` will use the device code -authentication flow. This can also be selected manually by running `azd login --use-device-code`. +For systems without a default web browser, the `azd login --use-device-code` command will use the device code authentication flow. ## Key concepts @@ -82,7 +80,9 @@ this library's credential classes. ![DefaultAzureCredential authentication flow](https://raw.githubusercontent.com/Azure/azure-sdk-for-python/main/sdk/identity/azure-identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.svg) 1. **Environment** - `DefaultAzureCredential` will read account information specified via [environment variables](#environment-variables "environment variables") and use it to authenticate. +1. **Workload Identity** - If the application is deployed to an Azure Kubernetes service with Managed Identity enabled, `DefaultAzureCredential` will authenticate with it. 1. **Managed Identity** - If the application is deployed to an Azure host with Managed Identity enabled, `DefaultAzureCredential` will authenticate with it. +1. **Azure Developer CLI** - If the developer has authenticated via the Azure Developer CLI `azd login` command, the `DefaultAzureCredential` will authenticate with that account. 1. **Azure CLI** - If a user has signed in via the Azure CLI `az login` command, `DefaultAzureCredential` will authenticate as that user. 1. **Azure PowerShell** - If a user has signed in via Azure PowerShell's `Connect-AzAccount` command, `DefaultAzureCredential` will authenticate as that user. 1. **Interactive browser** - If enabled, `DefaultAzureCredential` will interactively authenticate a user via the default browser. This is disabled by default. @@ -400,6 +400,7 @@ additional questions or comments. [auth_code_cred_ref]: https://aka.ms/azsdk/python/identity/authorizationcodecredential [azure_appconfiguration]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/appconfiguration/azure-appconfiguration [azure_cli]: https://learn.microsoft.com/cli/azure +[azure_developer_cli]:https://aka.ms/azure-dev [azure_core_transport_doc]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/CLIENT_LIBRARY_DEVELOPER.md#transport [azure_eventhub]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/eventhub/azure-eventhub [azure_keyvault_certificates]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk//keyvault/azure-keyvault-certificates From e2f55da74e42dd4050b46b945ec2ec162ac1b4f1 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Tue, 31 Jan 2023 16:27:56 -0800 Subject: [PATCH 15/18] Updates --- .../azure-identity/azure/identity/_credentials/default.py | 5 ++++- .../azure/identity/aio/_credentials/default.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index d67b9d6dc750..a47fa8a838a7 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -104,6 +104,9 @@ def __init__(self, **kwargs: Any) -> None: # pylint: disable=too-many-statement managed_identity_client_id = kwargs.pop( "managed_identity_client_id", os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) ) + workload_identity_client_id = kwargs.pop( + "workload_identity_client_id", managed_identity_client_id + ) interactive_browser_client_id = kwargs.pop("interactive_browser_client_id", None) shared_cache_username = kwargs.pop("shared_cache_username", os.environ.get(EnvironmentVariables.AZURE_USERNAME)) @@ -124,7 +127,7 @@ def __init__(self, **kwargs: Any) -> None: # pylint: disable=too-many-statement if not exclude_environment_credential: credentials.append(EnvironmentCredential(authority=authority, **kwargs)) if all(os.environ.get(var) for var in EnvironmentVariables.WORKLOAD_IDENTITY_VARS): - client_id = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) + client_id = workload_identity_client_id credentials.append(WorkloadIdentityCredential( client_id=cast(str, client_id), tenant_id=os.environ[EnvironmentVariables.AZURE_TENANT_ID], diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py index 02de0ad5563c..86428a47f441 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py @@ -97,6 +97,9 @@ def __init__(self, **kwargs: Any) -> None: managed_identity_client_id = kwargs.pop( "managed_identity_client_id", os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) ) + workload_identity_client_id = kwargs.pop( + "workload_identity_client_id", managed_identity_client_id + ) vscode_tenant_id = kwargs.pop( "visual_studio_code_tenant_id", os.environ.get(EnvironmentVariables.AZURE_TENANT_ID) @@ -114,7 +117,7 @@ def __init__(self, **kwargs: Any) -> None: if not exclude_environment_credential: credentials.append(EnvironmentCredential(authority=authority, **kwargs)) if all(os.environ.get(var) for var in EnvironmentVariables.WORKLOAD_IDENTITY_VARS): - client_id = managed_identity_client_id or os.environ.get(EnvironmentVariables.AZURE_CLIENT_ID) + client_id = workload_identity_client_id credentials.append(WorkloadIdentityCredential( client_id=cast(str, client_id), tenant_id=os.environ[EnvironmentVariables.AZURE_TENANT_ID], From 974b341b0afdbcbcd0649574cea14618cccc9ceb Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Wed, 1 Feb 2023 13:56:31 -0800 Subject: [PATCH 16/18] update --- .../azure-identity/azure/identity/_credentials/default.py | 2 ++ .../azure-identity/azure/identity/aio/_credentials/default.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index a47fa8a838a7..f732e1870999 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -68,6 +68,8 @@ class DefaultAzureCredential(ChainedTokenCredential): 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 workload_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 interactive_browser_client_id: The client ID to be used in interactive browser credential. If not specified, users will authenticate to an Azure development application. :keyword str shared_cache_username: Preferred username for :class:`~azure.identity.SharedTokenCacheCredential`. diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py index 86428a47f441..a32cf54ca95a 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py @@ -62,6 +62,8 @@ class DefaultAzureCredential(ChainedTokenCredential): **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 workload_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.aio.SharedTokenCacheCredential`. Defaults to the value of environment variable AZURE_USERNAME, if any. :keyword str shared_cache_tenant_id: Preferred tenant for :class:`~azure.identity.aio.SharedTokenCacheCredential`. From d65c523e5857d972ce550c97f4d396016c85029c Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Wed, 1 Feb 2023 14:55:44 -0800 Subject: [PATCH 17/18] Update sdk/identity/azure-identity/azure/identity/_credentials/default.py Co-authored-by: Charles Lowell <10964656+chlowell@users.noreply.github.com> --- .../azure-identity/azure/identity/_credentials/default.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/_credentials/default.py index f732e1870999..e86efad250d1 100644 --- a/sdk/identity/azure-identity/azure/identity/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/_credentials/default.py @@ -68,8 +68,8 @@ class DefaultAzureCredential(ChainedTokenCredential): 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 workload_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 workload_identity_client_id: The client ID of an identity assigned to the pod. Defaults to the value + of the environment variable AZURE_CLIENT_ID, if any. If not specified, the pod's default identity will be used. :keyword str interactive_browser_client_id: The client ID to be used in interactive browser credential. If not specified, users will authenticate to an Azure development application. :keyword str shared_cache_username: Preferred username for :class:`~azure.identity.SharedTokenCacheCredential`. From 4e5fe07bbc2b652a99fbdd2b413087e407abeab4 Mon Sep 17 00:00:00 2001 From: Xiang Yan Date: Wed, 1 Feb 2023 14:55:51 -0800 Subject: [PATCH 18/18] Update sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py Co-authored-by: Charles Lowell <10964656+chlowell@users.noreply.github.com> --- .../azure-identity/azure/identity/aio/_credentials/default.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py index a32cf54ca95a..5e3189228e33 100644 --- a/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py +++ b/sdk/identity/azure-identity/azure/identity/aio/_credentials/default.py @@ -62,8 +62,8 @@ class DefaultAzureCredential(ChainedTokenCredential): **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 workload_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 workload_identity_client_id: The client ID of an identity assigned to the pod. Defaults to the value + of the environment variable AZURE_CLIENT_ID, if any. If not specified, the pod's default identity will be used. :keyword str shared_cache_username: Preferred username for :class:`~azure.identity.aio.SharedTokenCacheCredential`. Defaults to the value of environment variable AZURE_USERNAME, if any. :keyword str shared_cache_tenant_id: Preferred tenant for :class:`~azure.identity.aio.SharedTokenCacheCredential`.