question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

azure.identity.ManagedIdentityCredential does not work in Synapse Analytics notebook

See original GitHub issue
  • Package Name: azure.identity
  • Package Version: 1.5.0
  • Operating System: Synapse Analytics (Linux vm-d0268763 4.15.0-1151-azure #166-Ubuntu SMP Tue Sep 6 17:42:35 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux)
  • Python Version: 3.8.10 | packaged by conda-forge | (default, May 11 2021, 07:01:05) \n[GCC 9.3.0]

Describe the bug

Attempt to use ManagedIdentityCredential in Azure Synapse will produce an error even though I feel it should definitely work. (I even have a hack that forces it to work, see below)

To Reproduce Steps to reproduce the behaviour:

  1. Create a queue in an azure storage account, add some test messages
  2. Open an azure synapse workspace and create spark pool,
  3. Create a new python notebook
  4. Ensure notebook is running with “Use Managed Identity”
  5. Ensure synapse workspace managed identity is added as Role Storage Queue Data Contributor on the storage account
from azure.storage.queue import QueueServiceClient
from azure.identity import ManagedIdentityCredential
queue_service_client = QueueServiceClient(
    account_url = "https://<STOARAGE_ACCOUNT_NAME>.queue.core.windows.net/",
    credential  = ManagedIdentityCredential()
)
queue_client = queue_service_client.get_queue_client("<QUEUE NAME>")
[item for item in queue_client.peek_messages(5)]
ManagedIdentityCredential.get_token failed: ManagedIdentityCredential authentication unavailable, no managed identity endpoint found.
ManagedIdentityCredential.get_token failed: ManagedIdentityCredential authentication unavailable, no managed identity endpoint found.
ManagedIdentityCredential.get_token failed: ManagedIdentityCredential authentication unavailable, no managed identity endpoint found.
ManagedIdentityCredential.get_token failed: ManagedIdentityCredential authentication unavailable, no managed identity endpoint found.
---------------------------------------------------------------------------
CredentialUnavailableError                Traceback (most recent call last)
<ipython-input-64-54ee11f0> in <module>
----> 1 [item for item in queue_client.peek_messages(20)]

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/tracing/decorator.py in wrapper_use_tracer(*args, **kwargs)
     76             span_impl_type = settings.tracing_implementation()
     77             if span_impl_type is None:
---> 78                 return func(*args, **kwargs)
     79 
     80             # Merge span is parameter is set, but only if no explicit parent are passed

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/storage/queue/_queue_client.py in peek_messages(self, max_messages, **kwargs)
    828             return wrapped_messages
    829         except HttpResponseError as error:
--> 830             process_storage_error(error)
    831 
    832     @distributed_trace

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/storage/queue/_shared/response_handlers.py in process_storage_error(storage_error)
     88     serialized = False
     89     if not storage_error.response or storage_error.response.status_code in [200, 204]:
---> 90         raise storage_error
     91     # If it is one of those three then it has been serialized prior by the generated layer.
     92     if isinstance(storage_error, (PartialBatchErrorException,

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/storage/queue/_queue_client.py in peek_messages(self, max_messages, **kwargs)
    818             resolver=self.key_resolver_function)
    819         try:
--> 820             messages = self._client.messages.peek(
    821                 number_of_messages=max_messages,
    822                 timeout=timeout,

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/tracing/decorator.py in wrapper_use_tracer(*args, **kwargs)
     76             span_impl_type = settings.tracing_implementation()
     77             if span_impl_type is None:
---> 78                 return func(*args, **kwargs)
     79 
     80             # Merge span is parameter is set, but only if no explicit parent are passed

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/storage/queue/_generated/operations/_messages_operations.py in peek(self, number_of_messages, timeout, request_id_parameter, **kwargs)
    502         request.url = self._client.format_url(request.url)  # type: ignore
    503 
--> 504         pipeline_response = self._client._pipeline.run(  # type: ignore # pylint: disable=protected-access
    505             request, stream=False, **kwargs
    506         )

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/pipeline/_base.py in run(self, request, **kwargs)
    209             else _TransportRunner(self._transport)
    210         )
--> 211         return first_node.send(pipeline_request)  # type: ignore

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/pipeline/_base.py in send(self, request)
     69         _await_result(self._policy.on_request, request)
     70         try:
---> 71             response = self.next.send(request)
     72         except Exception:  # pylint: disable=broad-except
     73             _await_result(self._policy.on_exception, request)

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/pipeline/_base.py in send(self, request)
     69         _await_result(self._policy.on_request, request)
     70         try:
---> 71             response = self.next.send(request)
     72         except Exception:  # pylint: disable=broad-except
     73             _await_result(self._policy.on_exception, request)

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/pipeline/_base.py in send(self, request)
     69         _await_result(self._policy.on_request, request)
     70         try:
---> 71             response = self.next.send(request)
     72         except Exception:  # pylint: disable=broad-except
     73             _await_result(self._policy.on_exception, request)

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/pipeline/_base.py in send(self, request)
     69         _await_result(self._policy.on_request, request)
     70         try:
---> 71             response = self.next.send(request)
     72         except Exception:  # pylint: disable=broad-except
     73             _await_result(self._policy.on_exception, request)

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/pipeline/_base.py in send(self, request)
     69         _await_result(self._policy.on_request, request)
     70         try:
---> 71             response = self.next.send(request)
     72         except Exception:  # pylint: disable=broad-except
     73             _await_result(self._policy.on_exception, request)

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/pipeline/policies/_redirect.py in send(self, request)
    156         redirect_settings = self.configure_redirects(request.context.options)
    157         while retryable:
--> 158             response = self.next.send(request)
    159             redirect_location = self.get_redirect_location(response)
    160             if redirect_location and redirect_settings['allow']:

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/pipeline/_base.py in send(self, request)
     69         _await_result(self._policy.on_request, request)
     70         try:
---> 71             response = self.next.send(request)
     72         except Exception:  # pylint: disable=broad-except
     73             _await_result(self._policy.on_exception, request)

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/storage/queue/_shared/policies.py in send(self, request)
    536                     self.sleep(retry_settings, request.context.transport)
    537                     continue
--> 538                 raise err
    539         if retry_settings['history']:
    540             response.context['history'] = retry_settings['history']

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/storage/queue/_shared/policies.py in send(self, request)
    510         while retries_remaining:
    511             try:
--> 512                 response = self.next.send(request)
    513                 if is_retry(response, retry_settings['mode']):
    514                     retries_remaining = self.increment(

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/pipeline/_base.py in send(self, request)
     69         _await_result(self._policy.on_request, request)
     70         try:
---> 71             response = self.next.send(request)
     72         except Exception:  # pylint: disable=broad-except
     73             _await_result(self._policy.on_exception, request)

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/pipeline/_base.py in send(self, request)
     69         _await_result(self._policy.on_request, request)
     70         try:
---> 71             response = self.next.send(request)
     72         except Exception:  # pylint: disable=broad-except
     73             _await_result(self._policy.on_exception, request)

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/pipeline/policies/_authentication.py in send(self, request)
    114         :type request: ~azure.core.pipeline.PipelineRequest
    115         """
--> 116         self.on_request(request)
    117         try:
    118             response = self.next.send(request)

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/core/pipeline/policies/_authentication.py in on_request(self, request)
     91 
     92         if self._token is None or self._need_new_token:
---> 93             self._token = self._credential.get_token(*self._scopes)
     94         self._update_headers(request.http_request.headers, self._token.token)
     95 

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/identity/_internal/decorators.py in wrapper(*args, **kwargs)
     25         def wrapper(*args, **kwargs):
     26             try:
---> 27                 token = fn(*args, **kwargs)
     28                 _LOGGER.info("%s succeeded", qualified_name)
     29                 return token

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/identity/_credentials/managed_identity.py in get_token(self, *scopes, **kwargs)
     91         if not self._credential:
     92             raise CredentialUnavailableError(message="No managed identity endpoint found.")
---> 93         return self._credential.get_token(*scopes, **kwargs)
     94 
     95 

~/cluster-env/clonedenv/lib/python3.8/site-packages/azure/identity/_credentials/managed_identity.py in get_token(self, *scopes, **kwargs)
    188         if not self._endpoint_available:
    189             message = "ManagedIdentityCredential authentication unavailable, no managed identity endpoint found."
--> 190             raise CredentialUnavailableError(message=message)
    191 
    192         if len(scopes) != 1:

CredentialUnavailableError: ManagedIdentityCredential authentication unavailable, no managed identity endpoint found.

Expected behavior

ManagedIdentityCredential will work because the notebook session is running with “Run as managed identity” Enabled.

Screenshots If applicable, add screenshots to help explain your problem. N/A

Additional context

I have discovered a work-around; the following script works as expected:

from azure.storage.queue import QueueServiceClient
from azure.identity import ManagedIdentityCredential
from azure.core.credentials import AccessToken

class spoof_token:
    def get_token(*args, **kwargs):
        return AccessToken(
            token=mssparkutils.credentials.getToken(audience="storage"),
            expires_on=int(time.time())+60*10 # some random time in future... synapse doesn't document how to get the actual time
        )

credential = ManagedIdentityCredential()
credential._credential = spoof_token() # monkey-patch the contents of the private `_credential`

queue_service_client = QueueServiceClient(
    account_url = "https://<STOARAGE_ACCOUNT_NAME>.queue.core.windows.net/",
    credential  = ManagedIdentityCredential()
)
queue_client = queue_service_client.get_queue_client("<QUEUE NAME>")
print([item for item in queue_client.peek_messages(5)])

I am sure there should be a more straightforward way to do this? I have sunk hours into this problem to finally find this hacky work-around. I realise this issue might be better raised as a Synapse Support ticket, but I don’t have permissions to do that, and I am still not sure if there is some other obvious method I have missed? The only other azure.identity credential that works is the DeviceCodeCredential; But that cant be automated, and it uses my own credentials instead of the synapse managed identity.

Many thanks

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:12 (6 by maintainers)

github_iconTop GitHub Comments

2reactions
MrNickArchercommented, Oct 25, 2022

As I demonstrated, azure-identity does work in synapse if Microsoft wants it to. It is needed for some use-cases such as accessing storage queue from a synapse python notebook. (Synapse only really has convenient mechanisms to access blob storage and basically nothing else) The alternative is to use keyvault + storage account access key to access the queue. I don’t understand why Microsoft prefers to leave synapse users with that second-rate less secure option.

1reaction
msftbot[bot]commented, Oct 24, 2022

Hi @MrNickArcher. Thank you for opening this issue and giving us the opportunity to assist. We believe that this has been addressed. If you feel that further discussion is needed, please add a comment with the text “/unresolve” to remove the “issue-addressed” label and continue the conversation.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Error in Azure Synapse Notebook Unable to Authenticate to ...
ManagedIdentityCredential: ManagedIdentityCredential authentication unavailable. No identity has been assigned to this resource.
Read more >
Azure Synapse Analytics :- Managed Identity Credential Set ...
As we understand the ask here is why this DefaultAzureCredential()) is working locally but fails on the server , please do let us...
Read more >
Managed identity - Azure Data Factory - Microsoft Learn
This article helps you understand managed identity (formerly known as Managed Service Identity/MSI) and how it works in Azure Data Factory. Note.
Read more >
Synapse Notebook FileNotFoundException when linked ...
Problem Description. I set up an Linked Service to Azure Blob Storage using Managed Identity (as below). The Synapse Workspace's ...
Read more >
Serverless SQL pool self-help - Azure Synapse Analytics
This problem usually happens because your Azure AD identity doesn't have rights to access the file or because a firewall is blocking access...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found