This time I would like to explain how we can authenticate interactively to Azure resources from interactive Python code execution like Jupyter notebooks.
Running a static Python app that utilizes Azure resources, the most convenient way to authenticate to Azure would probably be using a Service Principal. Or if the App is hosted on Azure environment that would be an Azure Managed Identity. These apps act on behalf of the user principal that it contains and is programmed to use it.
Any user that executes the code simply has permissions on the resources the Service Principal has access to….
Bad practise
But while running python code on Jupyter notebooks you are running an interactive session (from your own machine or docker instance). This would mean that code is executed on your users behalf. You could utilize a Service Principal like many code examples will do. But you might not want to haull credentials (service principals) arround. This is a serious security concern. Like I said before any user that will have access to the Service Principal secret will have access to the permissions assigned to it. Also, using them in Jupyter would mean having secrets in plain text, which is bad practise number one!
But for you it doesn’t have to be this way, there are other ways and those aren’t described in a lot of the code-examples.
While most code-examples for consuming Azure resources, utilize Service principals and their object ids and secrets or managed identities. These are examples for scripts or apps and running autonomiously. It might need a database for storing the user database. In that case any user that would like to use it would have to authenticate itself agains this user database. That is their use case…
An example can be found here: https://docs.microsoft.com/en-us/azure/python/python-sdk-azure-authenticate
Interactively
But within Jupyter we are interactively running code that would need access to resources on behalf of the user.
Like KQLMagic?
As I descibed in my previous blog how you could authenticate KQLmagic to a Log analytics workspace using devicecode. This devicecode method is more preferable than keeping secrets in the code and also the fact that in some point in time you need to replace them, copy, paste credentials??? awfull practise!
So the first Azure resource that I would like to explain, on how to utilize securely, will be Azure Keyvault!
With this skill under our belts things are starting to get more secure.
Two Solutions!?
In fact there are multiple solutions! I will descibe 2 of them and their use cases. First we will setup an Azure keyvault then we will build 2 code examples that will utilise the keyvault.
Setup
To help you reproduce every step I will include instructions to deploy a keyvault and create a secret. So let’s start with that.
Create keyvault
You will need at least contributor access to an Azure subscription to complete this step.
We could complete this step even within Python but let’s save that for another time. I provide the commands for Azure CLI. You can open an Azure CLI console easily https://shell.azure.com
A menu could popup to help you create a storage account. Just proceed with that, it will cost none to little on your subscription. Make sure Bash is selected…
First we create a resource group
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
az group create –name "ContosoResourceGroup" –location eastus |
Next we create a randomized name, keyvault and a secret, and we format some output that we will need later.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#we create a randomized name, the name of the keyvault has to be unique within the current cloud | |
#it will be publicly available using this name. | |
keyvaultName="keyvault-$(cat /proc/sys/kernel/random/uuid | cut -c-7)" | |
keyvaultURI=$(az keyvault create –name $keyvaultName –resource-group "ContosoResourceGroup" –location eastus –query "properties.vaultUri" –output tsv) | |
az keyvault secret set –vault-name $keyvaultName –name "ExamplePassword" –value "sampleSecretValueIn_plaintext" | |
tenantID=$(az account show –output tsv –query tenantId) | |
echo "The URI for the keyvault is: $keyvaultURI" | |
echo "The ID for the tenant is: $tenantID" |
The output will contain two lines stating the URI of the keyvault and the tenant ID where your keyvault will reside. Copy the values so we can use them in the following steps.
Having a resource and sample data in place now it is time to run code from Jupyter using Visual Studio Code.
I use the Python extension for Visual Studio Code which I highly recommend, but if you don’t have access to Visual Studio Code you could follow along using Jupyter as this code is purely python of course but I warn you it isn’t not half as fun!
Code
For this solution we have two options which I will describe both.
Option 1: Devicecode using msrestazure and Microsoft Azure Active Directory Authentication Library (ADAL) for Python, the advantage being that we hold a cached access-token that typically is valid for one hour. So we authenticate once and in the coming hour we will access.
The disadvantage: we need extra code to handle the situation of expiration of the access-token or do this manually by executing the request again.
If expiration is not a problem in your use case I would go for this option.
Option 2: Devicecode using azure-identity from the azure sdk for python. This approach supports the client libraries that are contained in the sdk. It provides a very clean and simple way of authentication by just a couple of lines code. And this method will prevent the access-token to expire as it isn’t cached. You could see this as a security measure…
The disadvantage: because the access-token isn’t cached every request to a resource has to be authenticated.
It really depends on your scenario which is convenient for you. If you need access to several resources or have multiple calls to the same resource choose option 1.
If caching access-tokens is a potential security risk to you and need to run just a couple of calls to resources during the day choose option 2.
And maybe you should shift to other authentication methods than devicecode in code after you have gained access to keyvault by using the devicecode.
Option 1 (ADAL)
Step 1: First we install and or upgrade needed libraries
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
!pip install msrestazure ––no–cache–dir ––upgrade | |
!pip install azure–keyvault ––no–cache–dir ––upgrade |
Step 2: If they are successfully are installed and updated we import the needed methods
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import adal, uuid, time | |
from msrestazure.azure_active_directory import AADTokenCredentials | |
from azure.keyvault import KeyVaultClient | |
from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD |
Step 3: Now we set the variables we need
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
secret_name = 'ExamplePassword' | |
vault_uri = '<replace this text and brackets>' | |
tenant_id='<replace this text and brackets>' | |
cloud=AZURE_PUBLIC_CLOUD | |
authority_host_uri = cloud.endpoints.active_directory + \ | |
'/' + tenant_id | |
keyvault_resource_uri = 'https://vault.azure.net' | |
client_id = '04b07795-8ddb-461a-bbee-02f9e1bf7b46' |
secret_name: this is the name of the secret value that is stored in the keyvault, we created this in the cloudshell before with the name ‘ExamplePassword’ which is an example of a secret we stored.
vault_uri: This URI is a unique address on the internet for your keyvault, the value is provided in the cloudshell when we created the keyvault. Please replace everything between the single quotes.
tenant_id: This is a unique ID for our Azure Active directory and also provided in the cloudshell when we created the keyvault. If you want to find your tenant ID by using the portal you can find it using this link.
cloud: This constant is used to assign which Azure cloud you are using. There are several National clouds that have their own addresses for authentication. Using these constants we can quickly resolve the different URI’s. This is handy feature of the ADAL. If you use Azure cloud, which is most common, you can leave the value to AZURE_PUBLIC_CLOUD.
authority_host_uri: we formulate the authority host here.
keyvault_resource_uri: If you use public cloud this is the URI to which your credential will be authenticated. For the sake of simplicity I didn’t put much effort in resolving this by the cloud constant. Azure keyvault has it’s own authentication boundary which is also a reason why the access policies exist next to IAM and in the other way around. So the access-token we will receive will only work to keyvault. For other resources you will need another access token. And a different uri to authenicate to. Keep this in mind if you need more Azure Resources.
client_id: This is the application ID that will be used to identify by which application the request is requested. This value is the default ID of “Microsoft Azure Cross-platform Command Line Interface“. See screenshot in step 4.
Step 4: Next we are creating an authentication request and save the received access token to a tokencredential that can be utilised by the azure.keyvault library.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
context = adal.AuthenticationContext(authority_host_uri, api_version=None) | |
code = context.acquire_user_code(keyvault_resource_uri, client_id) | |
print(code['message']) | |
kv_token = context.acquire_token_with_device_code(keyvault_resource_uri, code, client_id) | |
kv_credential = AADTokenCredentials(kv_token, client_id) |
This will display instructions how to complete the authentication request. Copy the code, click the url or browse to https://aka.ms/devicelogin
past the code, sign in to your account and close the browsing session.
If you have passed this successfully you are ready to use keyvault.
Step 5: utilize the credential, create the keyvault client object, print the value.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
keyvault_client = KeyVaultClient(kv_credential) | |
supersecret = keyvault_client.get_secret(vault_uri, secret_name, '') | |
print("The secret value is: "+ supersecret.value) |
And we are done!
Now you are ready to save any secrets to Azure Keyvault and use them in python by only signing in to your account by using the browser, “I love it when a plan comes together….”
For instance you could use this to retrieve a secret of a Service Principal and work further using that, but if that is your plan go for Option 2 because it is cleaner code and doesn’t cache your access-token…
Option 2 (Azure SDK for Python)
Step 1: First we install and or upgrade needed libraries
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
!pip install azure–identity ––no–cache–dir ––upgrade | |
!pip install azure–keyvault–secrets ––no–cache–dir ––upgrade |
Step 2: If they are successfully are installed and updated we import the needed methods
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from azure.identity import DeviceCodeCredential | |
from azure.keyvault.secrets import SecretClient |
Step 3: Now we set the variables we need
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
secret_name = 'ExamplePassword' | |
vault_uri = '<replace this text and brackets>' | |
tenant_id='<replace this text and brackets>' | |
authority_host_uri = 'login.microsoftonline.com' | |
keyvault_resource_uri = 'https://vault.azure.net' | |
client_id = '04b07795-8ddb-461a-bbee-02f9e1bf7b46' |
secret_name: this is the name of the secret value that is stored in the keyvault, we created this in the cloudshell before with the name ‘ExamplePassword’ which is an example of a secret we stored.
vault_uri: This URI is a unique address on the internet for your keyvault, the value is provided in the cloudshell when we created the keyvault. Please replace everything between the single quotes.
tenant_id: This is a unique ID for our Azure Active directory and also provided in the cloudshell when we created the keyvault. If you want to find your tenant ID by using the portal you can find it using this link.
authority_host_uri: we formulate the authority host here, notice the difference to option one…
keyvault_resource_uri: If you use public cloud this is the URI to which your credential will be authenticated to. For the sake of simplicity I didn’t put much effort in resolving this by the cloud constant. Azure keyvault has it’s own authentication boundary which is also a reason why the access policies exist next to IAM and in the other way around. So the access-token we will receive will only work to keyvault. For other resources you will need another access token. And a different uri to authenicate to. Keep this in mind if you need more Azure Resources.
client_id: This is the application ID that will be used to identify by which application the request is requested. This value is the default ID of “Microsoft Azure Cross-platform Command Line Interface”. See screenshot option 1 step 4.
Step 4: Create a credential object to the authority host.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
credential = DeviceCodeCredential(client_id, authority=authority_host_uri, tenant=tenant_id) |
Notice how much more simple the previous step was compared to option 1 step 4, basically we achieved the same goal. But now we aren’t authenticated yet.
Step 5: utilize the credential, create the keyvault client object, print the value.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
secret_client = SecretClient(vault_uri, credential) | |
supersecret = secret_client.get_secret(secret_name) | |
print("The secret value is: "+ supersecret.value) |
After the keyvault client object is created we still didn’t have to authenticate.
The moment get_secret is called, devicelogin will be executed and instructions will appear.
Everytime you will execute a method on the keyvault client devicelogin will appear.
And we are done!
Conclusion
To sum up, if we need utilise any Azure resource we have multiple options to sign in to Azure Active Directory to get access to those resources. For interactive sessions the safest method will be utilising device code login. Microsoft Azure Active Directory Authentication Library (ADAL) for Python and azure sdk for python both offer options to achieve this.
Which is most convenient to use? This depends on your use case. Familiarise yourself with the different behaviours and you will understand their advantages and shortcomings.
Like I said at the end of the Option 1, now you have gained access to keyvault securely your could shift to other authentication mechanisms without having to hard code credential information.
Information about these libraries:
https://github.com/Azure/azure-sdk-for-python
https://azure.github.io/azure-sdk-for-python/
https://github.com/AzureAD/azure-activedirectory-library-for-python
https://adal-python.readthedocs.io/
Usefull other resources about MS and Python:
https://devblogs.microsoft.com/python/
https://docs.microsoft.com/windows/python/resources
One thought on “How to authenticate Python interactively to Microsoft Azure”