ericOnline
ericOnline

Reputation: 1967

Validating `DefaultAzureCredential()` using Python SDK?

What are some example methods for logging and handling exceptions using try/except and DefaultAzureCredential()?

Example:

When DefaultAzureCredential() is used in a Python Azure Function, it generates a few WARNING messages and one of the .get_token methods in the credential chain succeeds.

I want to log which one succeeded and which ones failed.

WARNING:azure.identity._internal.decorators:EnvironmentCredential.get_token failed: EnvironmentCredential authentication unavailable. Environment variables are not fully configured.

WARNING:azure.identity._internal.decorators:ManagedIdentityCredential.get_token failed: ManagedIdentityCredential authentication unavailable, no managed identity endpoint found.

WARNING:azure.identity._internal.decorators:SharedTokenCacheCredential.get_token failed: SharedTokenCacheCredential authentication unavailable. No accounts were found in the cache.

WARNING:azure.identity._internal.decorators:VisualStudioCodeCredential.get_token failed: Failed to get Azure user details from Visual Studio Code.

And then it just succeeds, but there is no message indicating which one succeeded. In this case, running snippets in a .ipynb file in VS Code).

How do I log and handle errors when using DefaultAzureCredential() in production?

Looking for an example of something like:

try:
    credentials = DefaultAzureCredential()
    logging.info(f'<whichever>.get_token succeeded')
except Error1 as e1:
    logging.error(f'EnvironmentCredential.get_token failed: EnvironmentCredential authentication unavailable. Environment variables are not fully configured.', e1)
except Error2 as e2:
    logging.error(f'ManagedIdentityCredential.get_token failed: ManagedIdentityCredential authentication unavailable, no managed identity endpoint found.', e2)
...<etc>

Upvotes: 4

Views: 13794

Answers (2)

Charles Lowell
Charles Lowell

Reputation: 241

Just to supplement the accepted answer with some more details, let me add that DefaultAzureCredential does log this information. For example, if you enable INFO-level logging:

import logging
from azure.identity import DefaultAzureCredential

logger = logging.getLogger('azure.identity')
logger.setLevel(logging.INFO)

handler = logging.StreamHandler(stream=sys.stdout)
formatter = logging.Formatter('[%(levelname)s %(name)s] %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

You'll see log messages like these from DefaultAzureCredential:

[INFO] DefaultAzureCredential - EnvironmentCredential is unavailable
[INFO] DefaultAzureCredential - ManagedIdentityCredential is unavailable
[INFO] DefaultAzureCredential - SharedTokenCacheCredential is unavailable
[INFO] DefaultAzureCredential acquired a token from VisualStudioCodeCredential

You'll see the failed attempts logged only the first time the DefaultAzureCredential instance acquires a token. Once one of its constituent credentials provides a token, it uses that credential exclusively. So this particular DefaultAzureCredential will always authenticate through Visual Studio Code, and in the logs you'll see this message whenever it does so:

[INFO] DefaultAzureCredential acquired a token from VisualStudioCodeCredential

Upvotes: 8

Kibrantn
Kibrantn

Reputation: 609

There are a few ways I might approach this but none are particularly pretty. At the risk of a naive suggestion, if you want a fine-grained understanding of what worked in sequence, you could take an approach similar to what your example sketched out using the components credentials listed here:

for credential in [EnvironmentCredential, ManagedIdentityCredential, ...]:
  try:
    credentials = credential()
    # Validate credential functionality...
    logging.info('{}.get_token succeeded'.format(credential))
  except Error1 as e1:
    logging.error('{} failed due to {}'.format(credential, e1))

I suggest this because it does not seem like what you want is exposed in an easily accessible manner, looking at the ChainedCredential source here (history is built up but only used in the log message).

More "self-contained" approaches might involve creating a custom wrapper that basically does what ChainedCredential does for DefaultAzureCredential in running the list of get_token calls, but with more ability to export the success history, you could likely hack that off of the code I linked in 2 and do something directly comparable to the DefaultAzureCredential logic, but at the end of the day it's all isomorphisms of the sample above.

Don't hesitate to shout if I've misunderstood some aspect of the question or your requirements/constraints; and as always feel free to reach out if you need to get in touch with us on our github (full disclosure, am a maintainer for some of the other Python Azure SDKs)

Upvotes: 2

Related Questions