Migrate an application to use passwordless connections with Azure Storage

Application requests to Azure Storage must be authenticated using either account access keys or passwordless connections. However, you should prioritize passwordless connections in your applications when possible. This tutorial explores how to migrate from traditional authentication methods to more secure, passwordless connections.

Security risks associated with Shared Key authorization

The following code example demonstrates how to connect to Azure Storage using a storage account key. When you create a storage account, Azure generates access keys for that account. Many developers gravitate towards this solution because it feels familiar to options they have worked with in the past. For example, connection strings for storage accounts also use access keys as part of the string. If your application currently uses access keys, consider migrating to passwordless connections using the steps described later in this document.

var blobServiceClient = new BlobServiceClient(
    new Uri("https://<storage-account-name>.blob.core.windows.net"),
    new StorageSharedKeyCredential("<storage-account-name>", "<your-access-key>"));

Storage account keys should be used with caution. Developers must be diligent to never expose the keys in an unsecure location. Anyone who gains access to the key is able to authenticate. For example, if an account key is accidentally checked into source control, sent through an unsecure email, pasted into the wrong chat, or viewed by someone who shouldn't have permission, there's risk of a malicious user accessing the application. Instead, consider updating your application to use passwordless connections.

Migrate to passwordless connections

Many Azure services support passwordless connections through Azure AD and Role Based Access control (RBAC). These techniques provide robust security features and can be implemented using DefaultAzureCredential from the Azure Identity client libraries.

Important

Some languages must implement DefaultAzureCredential explicitly in their code, while others utilize DefaultAzureCredential internally through underlying plugins or drivers.

DefaultAzureCredential supports multiple authentication methods and automatically determines which should be used at runtime. This approach enables your app to use different authentication methods in different environments (local dev vs. production) without implementing environment-specific code.

The order and locations in which DefaultAzureCredential searches for credentials can be found in the Azure Identity library overview and varies between languages. For example, when working locally with .NET, DefaultAzureCredential will generally authenticate using the account the developer used to sign-in to Visual Studio, Azure CLI, or Azure PowerShell. When the app is deployed to Azure, DefaultAzureCredential will automatically discover and use the managed identity of the associated hosting service, such as Azure App Service. No code changes are required for this transition.

Note

A managed identity provides a security identity to represent an app or service. The identity is managed by the Azure platform and does not require you to provision or rotate any secrets. You can read more about managed identities in the overview documentation.

The following code example demonstrates how to connect to Service Bus using passwordless connections. The next section describes how to migrate to this setup for a specific service in more detail.

A .NET Core application can pass an instance of DefaultAzureCredential into the constructor of a service client class. DefaultAzureCredential will automatically discover the credentials that are available in that environment.

var serviceBusClient = new ServiceBusClient(
    new Uri("https://<your-service-bus-namespace>.blob.core.windows.net"),
    new DefaultAzureCredential());

Steps to migrate an app to use passwordless authentication

The following steps explain how to migrate an existing application to use passwordless connections instead of a key-based solution. These same migration steps should apply whether you are using access keys directly, or through connection strings.

Configure roles and users for local development authentication

When developing locally, make sure that the user account that is accessing blob data has the correct permissions. You'll need Storage Blob Data Contributor to read and write blob data. To assign yourself this role, you'll need to be assigned the User Access Administrator role, or another role that includes the Microsoft.Authorization/roleAssignments/write action. You can assign Azure RBAC roles to a user using the Azure portal, Azure CLI, or Azure PowerShell. You can learn more about the available scopes for role assignments on the scope overview page.

In this scenario, you'll assign permissions to your user account, scoped to the storage account, to follow the Principle of Least Privilege. This practice gives users only the minimum permissions needed and creates more secure production environments.

The following example will assign the Storage Blob Data Contributor role to your user account, which provides both read and write access to blob data in your storage account.

Important

In most cases it will take a minute or two for the role assignment to propagate in Azure, but in rare cases it may take up to eight minutes. If you receive authentication errors when you first run your code, wait a few moments and try again.

  1. In the Azure portal, locate your storage account using the main search bar or left navigation.

  2. On the storage account overview page, select Access control (IAM) from the left-hand menu.

  3. On the Access control (IAM) page, select the Role assignments tab.

  4. Select + Add from the top menu and then Add role assignment from the resulting drop-down menu.

    A screenshot showing how to assign a role.

  5. Use the search box to filter the results to the desired role. For this example, search for Storage Blob Data Contributor and select the matching result and then choose Next.

  6. Under Assign access to, select User, group, or service principal, and then choose + Select members.

  7. In the dialog, search for your Azure AD username (usually your user@domain email address) and then choose Select at the bottom of the dialog.

  8. Select Review + assign to go to the final page, and then Review + assign again to complete the process.

Sign-in and migrate the app code to use passwordless connections

For local development, make sure you're authenticated with the same Azure AD account you assigned the role to on your Blob Storage account. You can authenticate via the Azure CLI, Visual Studio, Azure PowerShell, or other tools such as IntelliJ.

Make sure you're authenticated with the same Azure AD account you assigned the role to. You can authenticate via the Azure CLI, Visual Studio, or Azure PowerShell.

Sign-in to Azure through the Azure CLI using the following command:

az login

Next you will need to update your code to use passwordless connections.

  1. To use DefaultAzureCredential in a .NET application, add the Azure.Identity NuGet package to your application.

    dotnet add package Azure.Identity
    
  2. At the top of your Program.cs file, add the following using statement:

    using Azure.Identity;
    
  3. Identify the locations in your code that currently create a BlobServiceClient to connect to Azure Storage. This task is often handled in Program.cs, potentially as part of your service registration with the .NET dependency injection container. Update your code to match the following example:

    // TODO: Update <storage-account-name> placeholder to your account name
    var blobServiceClient = new BlobServiceClient(
        new Uri("https://<storage-account-name>.blob.core.windows.net"),
        new DefaultAzureCredential());
    
  4. Make sure to update the storage account name in the URI of your BlobServiceClient. You can find the storage account name on the overview page of the Azure portal.

    Screenshot showing how to find the storage account name.

Run the app locally

After making these code changes, run your application locally. The new configuration should pick up your local credentials, such as the Azure CLI, Visual Studio, or IntelliJ. The roles you assigned to your local dev user in Azure will allow your app to connect to the Azure service locally.

Configure the Azure hosting environment

Once your application is configured to use passwordless connections and runs locally, the same code can authenticate to Azure services after it is deployed to Azure. For example, an application deployed to an Azure App Service instance that has a managed identity enabled can connect to Azure Storage.

Create the managed identity using the Azure portal

The following steps demonstrate how to create a system-assigned managed identity for various web hosting services. The managed identity can securely connect to other Azure Services using the app configurations you set up previously.

Some app hosting environments support Service Connector, which helps you connect Azure compute services to other backing services. Service Connector automatically configures network settings and connection information. You can learn more about Service Connector and which scenarios are supported on the overview page.

The following compute services are currently supported:

  • Azure App Service
  • Azure Spring Cloud
  • Azure Container Apps (preview)

For this migration guide you'll use App Service, but the steps are similar on Azure Spring Apps and Azure Container Apps.

Note

Azure Spring Apps currently only supports Service Connector using connection strings.

  1. On the main overview page of your App Service, select Service Connector from the left navigation.

  2. Select + Create from the top menu and the Create connection panel will open. Enter the following values:

    • Service type: Choose Service bus.
    • Subscription: Select the subscription you would like to use.
    • Connection Name: Enter a name for your connection, such as connector_appservice_servicebus.
    • Client type: Leave the default value selected or choose the specific client you'd like to use.

    Select Next: Authentication.

  3. Make sure System assigned managed identity (Recommended) is selected, and then choose Next: Networking.

  4. Leave the default values selected, and then choose Next: Review + Create.

  5. After Azure validates your settings, select Create.

The Service Connector will automatically create a system-assigned managed identity for the app service. The connector will also assign the managed identity a Azure Service Bus Data Owner role for the service bus you selected.

Alternatively, you can also enable managed identity on an Azure hosting environment using the Azure CLI.

You can use Service Connector to create a connection between an Azure compute hosting environment and a target service using the Azure CLI. The CLI automatically handles creating a managed identity and assigns the proper role, as explained in the portal instructions.

If you're using an Azure App Service, use the az webapp connection command:

az webapp connection create storage-blob \
    --resource-group <resource-group-name> \
    --name <webapp-name> \
    --target-resource-group <target-resource-group-name> \
    --account <target-storage-account-name> \
    --system-identity

If you're using Azure Spring Apps, use the az spring-cloud connection command:

az spring-cloud connection create storage-blob \
    --resource-group <resource-group-name> \
    --service <service-instance-name> \
    --app <app-name> \
    --deployment <deployment-name> \
    --target-resource-group <target-resource-group> \
    --account <target-storage-account-name> \
    --system-identity

If you're using Azure Container Apps, use the az containerapp connection command:

az containerapp connection create storage-blob \
    --resource-group <resource-group-name> \
    --name <containerapp-name> \
    --target-resource-group <target-resource-group-name> \
    --account <target-storage-account-name> \
    --system-identity

Assign roles to the managed identity

Next, you need to grant permissions to the managed identity you created to access your storage account. You can do this by assigning a role to the managed identity, just like you did with your local development user.

If you connected your services using the Service Connector you do not need to complete this step. The necessary configurations were handled for you:

  • If you selected a managed identity while creating the connection, a system-assigned managed identity was created for your app and assigned the Storage Blob Data Contributor role on the storage account.

  • If you selected connection string, the connection string was added as an app environment variable.

Test the app

After making these code changes, browse to your hosted application in the browser. Your app should be able to connect to the storage account successfully. Keep in mind that it may take several minutes for the role assignments to propagate through your Azure environment. Your application is now configured to run both locally and in a production environment without the developers having to manage secrets in the application itself.

Next steps

In this tutorial, you learned how to migrate an application to passwordless connections.

You can read the following resources to explore the concepts discussed in this article in more depth: