Exception: AttributeError: 'DefaultAzureCredential' object has no attribute 'signed_session' using Azure Function and Python
Asked Answered
C

4

9

I wrote an Azure function that runs Python3 to simply turn on an Azure VM.

The function app has a system assigned managed identity that I've given VM contributor role. To have the function use the Managed Identity, I am using the DefaultAzureCredential() class.

The error I am getting is: Exception: AttributeError: 'DefaultAzureCredential' object has no attribute 'signed_session' I've done tons of research and can't seem to find the solution.

Here is the code that is related:

from azure.identity import DefaultAzureCredential
credentials = DefaultAzureCredential()
compute_client = ComputeManagementClient(credentials, subscription_id)
# Starting the VM
print('\nStarting VM ' + VM_NAME)
vm_start = compute_client.virtual_machines.start(
    RG_NAME, VM_NAME)
vm_start.wait()

You'll have to forgive me, I'm only new to Python, but very interested in learning.

Cove answered 12/8, 2020 at 20:12 Comment(0)
A
8

EDIT May 2022: As of May 2022, all SDKs have been re-released with native support for azure-identity. If you still encounter this error with a given SDK on its latest version, please open an issue asking for a re-release of that SDK here: https://github.com/Azure/azure-sdk-for-python/issues

Original response:

This is addressed here: https://learn.microsoft.com/en-us/azure/developer/python/azure-sdk-authenticate?tabs=cmd

Search "Using DefaultAzureCredential with SDK management libraries" on this page and it will take you to the section that covers your problem in more detail.

In a nutshell....

They updated the DefaultAzureCredential class and it no longer has a 'signed_session' attribute. The newest versions of the management libraries should be updated to handle this. As mentioned in another solution, update your azure-cli library to ensure you have the latest. However, not all of the management libraries have been updated yet. You can use this wrapper created by a member of the Azure SDK engineering team for the time being. https://gist.github.com/lmazuel/cc683d82ea1d7b40208de7c9fc8de59d

# Wrap credentials from azure-identity to be compatible with SDK that needs msrestazure or azure.common.credentials
# Need msrest >= 0.6.0
# See also https://pypi.org/project/azure-identity/

from msrest.authentication import BasicTokenAuthentication
from azure.core.pipeline.policies import BearerTokenCredentialPolicy
from azure.core.pipeline import PipelineRequest, PipelineContext
from azure.core.pipeline.transport import HttpRequest

from azure.identity import DefaultAzureCredential

class CredentialWrapper(BasicTokenAuthentication):
    def __init__(self, credential=None, resource_id="https://management.azure.com/.default", **kwargs):
        """Wrap any azure-identity credential to work with SDK that needs azure.common.credentials/msrestazure.
        Default resource is ARM (syntax of endpoint v2)
        :param credential: Any azure-identity credential (DefaultAzureCredential by default)
        :param str resource_id: The scope to use to get the token (default ARM)
        """
        super(CredentialWrapper, self).__init__(None)
        if credential is None:
            credential = DefaultAzureCredential()
        self._policy = BearerTokenCredentialPolicy(credential, resource_id, **kwargs)

    def _make_request(self):
        return PipelineRequest(
            HttpRequest(
                "CredentialWrapper",
                "https://fakeurl"
            ),
            PipelineContext(None)
        )

    def set_token(self):
        """Ask the azure-core BearerTokenCredentialPolicy policy to get a token.
        Using the policy gives us for free the caching system of azure-core.
        We could make this code simpler by using private method, but by definition
        I can't assure they will be there forever, so mocking a fake call to the policy
        to extract the token, using 100% public API."""
        request = self._make_request()
        self._policy.on_request(request)
        # Read Authorization, and get the second part after Bearer
        token = request.http_request.headers["Authorization"].split(" ", 1)[1]
        self.token = {"access_token": token}

    def signed_session(self, session=None):
        self.set_token()
        return super(CredentialWrapper, self).signed_session(session)

if __name__ == "__main__":
    import os
    credentials = CredentialWrapper()
    subscription_id = os.environ.get("AZURE_SUBSCRIPTION_ID", "<subscription_id>")

    from azure.mgmt.resource import ResourceManagementClient
    client = ResourceManagementClient(credentials, subscription_id)
    for rg in client.resource_groups.list():
        print(rg.name)

Your code would then look like:

from cred_wrapper import CredentialWrapper
credentials = CredentialWrapper()
compute_client = ComputeManagementClient(credentials, subscription_id)
# Starting the VM
print('\nStarting VM ' + VM_NAME)
vm_start = compute_client.virtual_machines.start(
    RG_NAME, VM_NAME)
vm_start.wait()

You should be cooking from there!

Azalea answered 30/9, 2020 at 0:30 Comment(3)
Thank you for your comment Bubba. I managed to fix it by updating the library. The learning continues! Have a lovely day.Cove
Updating the package is definitely the ideal solution. Best of luck in your learning and development!Azalea
Thanks you @Azalea ! exactly what I needed.Lingam
C
2

Looks like it's fixed if you use the preview version of azure-mgmt-compute (17.0.0b1)

Another gotchya because of the version bump is they changed the start function from start to begin_start.

Hope this helps somebody!

Cove answered 13/8, 2020 at 12:4 Comment(0)
N
1

I ran into this issue and cannot upgrade the Azure management libraries in question. However, the wrapper does not work as it leads to another error:

self._token = self._credential.get_token(*self._scopes)
AttributeError: 'CredentialWrapper' object has no attribute 'get_token'

To get around this I had to pass through the get_token call in the CredentialWrapper class:

class CredentialWrapper(BasicTokenAuthentication):
    def __init__(...):
        ...
        # Set credential to instance variable
        self.credential = credential

    ...

    def get_token(self, *scopes, **kwargs):
        # Pass get_token call to credential
        return self.credential.get_token(*scopes, **kwargs)

For reference the library versions I'm using are:

azure-common==1.1.25
azure-core==1.9.0
azure-identity==1.5.0
azure-mgmt-compute==17.0.0
azure-mgmt-core==1.2.2
azure-mgmt-network==16.0.0
azure-mgmt-resource==10.2.0
msrestazure==0.6.4
Newt answered 14/1, 2021 at 16:5 Comment(0)
D
1

I have faced the similar (Signed_Session) kind of issue while working with Azure nsgs and have fixed it. There might be 2 causes.

  1. Azure library versions mismatch. for me combination of below 2 libraries are working. azure-identity==1.6.1 and azure-mgmt-network==19.0.0

  2. May be you are importing the incorrect library. I am working with nsgs for this. I installed the library called "azure-mgmt" and imported the "NetworkManagementClient" class then I have faced "signed session" issue. Later, I uninstalled the "azure-mgmt" library and installed another library, "azure-mgmt-network==19.0.0" and now it is working fine.

To import/work with "azure.mgmt.network import NetworkManagementClient", we need to install "azure-mgmt-network==19.0.0" library but not "azure-mgmt.

Just think on above 2 ways..it may helps you.

Dorise answered 5/1, 2022 at 10:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.