← Back to list

Managed Identity vs Connection Strings in Azure Functions

Connection strings are one of the easiest ways to connect an Azure Function to another Azure service.

Vishwas Acharya · 2026-08-09 08:25 · 0 claps · 12.5 min read paywalled
#azure-functions #managed-identity #cloud-security #dotnet #microsoft-azure
Open on Medium ↗
Wiki topics: GEN · Genomics & Sequencing ☁️ · DevOps & Cloud

Managed Identity vs Connection Strings in Azure Functions

Connection strings are one of the easiest ways to connect an Azure Function to another Azure service.

Add a setting:

AzureWebJobsStorage=<storage-connection-string>

Read it from configuration, and the application works.

That simplicity is also the problem.

A connection string often contains enough information to access a storage account, database, queue, or messaging service. If it is exposed through source control, logs, deployment output, screenshots, or an incorrectly secured configuration system, the credential can be used outside the Function App.

Managed Identity takes a different approach:

Connection string:
Application receives and stores a credential.

Managed Identity:
Application receives an Azure identity.
Azure issues temporary access tokens for that identity.

Managed Identity removes long-lived application secrets, but it does not remove configuration or security decisions.

You still need to configure:

  • The identity
  • The target resource
  • Azure role assignments
  • Resource endpoints
  • Local-development credentials
  • Network access
  • Trigger and binding support
  • Deployment permissions

So which approach should you use?

This article compares connection strings and Managed Identity in Azure Functions, demonstrates both approaches with .NET, and explains where each one fits.

A simple Azure Function scenario

Consider a queue-triggered Azure Function that processes orders:

Application
    ↓
Azure Storage Queue
    ↓
Azure Function
    ↓
Process order

The function needs permission to:

  • Read queue messages
  • Hide messages while processing
  • Delete successfully processed messages
  • Add failed messages to a poison queue when required

There are two common ways to provide that access:

Option 1: Storage connection string
Option 2: Microsoft Entra authentication through Managed Identity

Both can work.

The key differences are how the application authenticates, how access is restricted, and who is responsible for protecting and rotating credentials.

Approach 1: Using a connection string

A traditional Azure Storage connection string looks conceptually like this:

DefaultEndpointsProtocol=https;
AccountName=myaccount;
AccountKey=<storage-account-key>;
EndpointSuffix=core.windows.net

It is normally stored as a Function App environment variable:

OrderQueueStorage=<connection-string>

The queue trigger references the setting name:

public sealed class OrderQueueFunction
{
    [Function("ProcessOrder")]
    public async Task RunAsync(
        [QueueTrigger(
            "orders",
            Connection = "OrderQueueStorage")]
        string message)
    {
        await ProcessOrderAsync(message);
    }

    private static Task ProcessOrderAsync(string message)
    {
        return Task.CompletedTask;
    }
}

The Functions runtime finds OrderQueueStorage, reads the connection string, and uses it to access the storage account.

This is easy to understand and usually quick to configure.

Advantages of connection strings

1. Simple initial setup

You do not need to:

  • Enable a Managed Identity
  • Create Azure role assignments
  • Understand token-based authentication
  • Wait for RBAC propagation
  • Configure identity-specific endpoints

For small prototypes, demos, and temporary development environments, this simplicity can be useful.

2. Predictable local development

A developer can place a local connection string in local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
    "OrderQueueStorage": "<local-or-development-connection-string>"
  }
}

The file should remain excluded from source control.

3. Broad compatibility

Some services, libraries, older extensions, or third-party systems may not support Microsoft Entra authentication.

A connection string may be the only supported integration method.

4. Fewer Azure identity dependencies

The connection does not depend on:

  • Managed Identity availability
  • Azure token acquisition
  • RBAC configuration
  • Identity selection
  • Tenant configuration

That can simplify some isolated test scenarios.

Risks of connection strings

1. A secret must exist somewhere

Even when the value is not hard-coded, it must be stored in a protected location such as:

  • Function App environment variables
  • Azure Key Vault
  • A CI/CD secret store
  • Local developer secrets

The application or deployment process still needs access to the secret.

2. Credentials may be overly powerful

A storage account key is not naturally scoped to one queue or one operation.

It may provide broad access to the storage account, depending on how it is used.

By contrast, Azure RBAC lets you assign specific data roles to a Managed Identity at a selected scope. Microsoft recommends granting the narrowest practical scope when authorizing access to Azure Storage.

3. Secret rotation creates operational work

When an account key or password is rotated, every dependent application must receive the new value.

A poor rotation process can cause:

Credential rotated
    ↓
Function App still has old credential
    ↓
Triggers stop
    ↓
Messages remain unprocessed

4. Secrets can leak

Common leakage paths include:

  • Source control
  • Debug logs
  • Screenshots
  • Exported configuration
  • Pipeline output
  • Support tickets
  • Shared development files

Masking secrets reduces risk, but it does not remove the underlying credential.

Approach 2: Using Managed Identity

A Managed Identity gives an Azure resource an identity in Microsoft Entra ID.

For an Azure Function, Azure can manage the credential lifecycle and allow the application to request tokens for supported services without storing a client secret.

Microsoft describes Managed Identity as a way for App Service and Azure Functions applications to access other Azure resources without requiring application-managed credentials.

The flow becomes:

Azure Function
    ↓ Uses its Managed Identity
Microsoft Entra ID
    ↓ Issues short-lived token
Azure Storage
    ↓ Evaluates assigned RBAC role
Access allowed or denied

The application does not store the storage account key.

Types of Managed Identity

Azure supports two main Managed Identity types.

System-assigned Managed Identity

The identity belongs directly to one Azure resource.

Function App
    └── System-assigned identity

Characteristics:

  • Enabled directly on the Function App
  • Lifecycle is tied to that Function App
  • Deleted when the Function App is deleted
  • Simple when one application needs its own identity

User-assigned Managed Identity

The identity is created as a separate Azure resource.

User-assigned identity
    ├── Function App A
    ├── Function App B
    └── Deployment slot or another service

Characteristics:

  • Exists independently of the Function App
  • Can be attached to multiple supported resources
  • Keeps a stable identity when applications are recreated
  • Useful when several resources require the same permission model
  • Requires explicit identity selection when several identities are attached

Neither type is universally better.

Use system-assigned identity when the permission lifecycle should follow one Function App.

Use user-assigned identity when identity reuse, stable identity references, or pre-provisioned permissions are important.

Enabling a system-assigned Managed Identity

In the Azure portal:

Function App
→ Identity
→ System assigned
→ Status: On
→ Save

Azure creates an identity for the Function App.

However, enabling the identity does not automatically grant access to a storage account, queue, database, or Service Bus namespace.

Authentication and authorization are separate:

Managed Identity exists
        ≠
Managed Identity has permission

You must assign the identity an appropriate role on the target resource.

Assigning the correct queue role

For an Azure Storage Queue trigger, the Function App needs queue-data permissions.

Depending on the operations required, relevant built-in roles include:

  • Storage Queue Data Reader
  • Storage Queue Data Contributor
  • Storage Queue Data Message Processor

The Message Processor role is intended for processing queue messages, including peeking, retrieving, and deleting messages. Azure provides separate RBAC roles for queue-data access, and these roles can be assigned to managed identities.

A role assignment can be scoped at different levels:

Subscription
Resource group
Storage account
Individual queue, where supported

Prefer the narrowest scope that supports the required operation.

Avoid assigning broad roles such as Owner merely to make an authorization error disappear.

Also remember:

Management-plane roles and data-plane roles are different.

For example, being Contributor on a storage account does not automatically provide permission to read blob data through Microsoft Entra authentication. Microsoft explicitly distinguishes management roles from storage data-access roles.

Identity-based configuration for an Azure Storage Queue trigger

With a connection string, the setting contains a secret:

OrderQueueStorage=<connection-string>

With an identity-based connection, the configuration uses a setting collection based on a shared prefix.

For example:

OrderQueueStorage__queueServiceUri=https://mystorageaccount.queue.core.windows.net

The trigger still references the prefix:

public sealed class OrderQueueFunction
{
    [Function("ProcessOrder")]
    public async Task RunAsync(
        [QueueTrigger(
            "orders",
            Connection = "OrderQueueStorage")]
        string message)
    {
        await ProcessOrderAsync(message);
    }

    private static Task ProcessOrderAsync(string message)
    {
        return Task.CompletedTask;
    }
}

The Functions extension interprets OrderQueueStorage as an identity-based connection because it finds the prefixed service configuration rather than one setting containing a storage connection string.

Azure Functions supports identity-based connections for supported triggers and bindings, allowing secrets to be replaced with Managed Identity authentication.

The exact setting requirements depend on:

  • The Azure Functions extension
  • The target Azure service
  • The extension version
  • Whether a system-assigned or user-assigned identity is used

Always verify that the binding extension version supports identity-based connections before migrating.

Accessing Blob Storage from application code

Triggers and bindings are not the only way to connect to Azure services.

Your function may use an Azure SDK client directly:

using Azure.Identity;
using Azure.Storage.Blobs;

var credential = new ManagedIdentityCredential();
var blobServiceClient = new BlobServiceClient(
    new Uri("https://mystorageaccount.blob.core.windows.net"),
    credential);

For a system-assigned identity, ManagedIdentityCredential can use the identity available to the hosted application.

For a user-assigned identity, specify its client ID:

using Azure.Identity;
using Azure.Storage.Blobs;

var credential = new ManagedIdentityCredential(
    clientId: configuration["ManagedIdentityClientId"]);
var blobServiceClient = new BlobServiceClient(
    new Uri(configuration["BlobServiceUri"]!),
    credential);

The user-assigned identity still needs an appropriate storage data role.

What about DefaultAzureCredential?

DefaultAzureCredential is convenient because it can support both local development and Azure-hosted execution.

Conceptually:

var credential = new DefaultAzureCredential();

var blobServiceClient = new BlobServiceClient(
    new Uri(blobServiceUri),
    credential);

During local development, it can use an available developer credential.

When hosted in Azure, it can use Managed Identity.

Microsoft documents DefaultAzureCredential as a credential chain intended to simplify authentication across local and Azure-hosted environments. Its current guidance also recommends being deliberate about the production credential path rather than blindly depending on a large credential chain.

A practical pattern is:

TokenCredential credential;

if (environment.IsDevelopment())
{
    credential = new DefaultAzureCredential();
}
else
{
    credential = new ManagedIdentityCredential();
}

For a user-assigned identity:

TokenCredential credential;

if (environment.IsDevelopment())
{
    credential = new DefaultAzureCredential();
}
else
{
    credential = new ManagedIdentityCredential(
        configuration["ManagedIdentityClientId"]);
}

This makes the production authentication method explicit.

Local development with Managed Identity

A Managed Identity exists only in the Azure hosting environment.

Your local machine does not become the Function App’s Managed Identity.

Instead, local development normally uses a developer identity through tools such as:

  • Azure CLI
  • Visual Studio
  • Visual Studio Code
  • Azure Developer CLI

For example:

az login

Then DefaultAzureCredential can use the signed-in developer identity.

That developer identity must also have permission on the development resource.

The environments therefore use different identities:

Local:
Developer identity
    ↓
Development storage account

Azure:
Function App Managed Identity
    ↓
Azure storage account

This is a security advantage, but it can also confuse developers.

A function that works locally may fail in Azure because the developer has more permissions than the Managed Identity.

The reverse can also happen: Azure works, but local development fails because the developer lacks the required data role.

Cross-subscription access

A common misconception is that Managed Identity works only when the Function App and target resource are in the same subscription.

That is not generally required.

A Function App in one Azure subscription can access a supported resource in another subscription when:

  • The identity can be resolved in the relevant Microsoft Entra tenant
  • The target resource supports Microsoft Entra authentication
  • The identity receives the correct RBAC role
  • Network controls permit the connection

Conceptually:

Subscription A
└── Function App
    └── Managed Identity

Subscription B
└── Storage Account
    └── RBAC assignment for that Managed Identity

The role assignment is created on the target resource in Subscription B.

The important boundary is not merely the subscription. Tenant identity, resource support, authorization, and networking all matter.

Managed Identity does not bypass networking

A valid identity does not guarantee connectivity.

The Function App can still fail when the target resource has:

  • A firewall
  • Private endpoints
  • Selected-network restrictions
  • Disabled public network access
  • Incorrect DNS resolution
  • Missing VNet integration
  • Routing restrictions

Treat authentication and networking separately:

Can the Function reach the resource?
            ↓
Can it authenticate?
            ↓
Is it authorized for the operation?

A timeout usually points toward connectivity.

A 401 generally points toward authentication.

A 403 commonly indicates that the identity was recognized but lacked permission, although network and service-specific policies can also produce authorization-style failures.

Managed Identity advantages

1. No long-lived service secret in application configuration

The application stores resource information such as an endpoint:

https://mystorageaccount.queue.core.windows.net

It does not store an account key.

2. Azure manages credential issuance

Azure obtains and rotates the underlying credentials used by the Managed Identity.

Your application requests tokens rather than handling secret rotation directly.

3. Granular authorization

Azure RBAC can grant the Function App only the access it requires.

For example:

Read and process messages
without
managing the entire storage account

4. Better identity-level auditing

Access can be associated with the Function App’s identity rather than a shared account key used by multiple systems.

5. Easier credential revocation

Removing a role assignment can revoke access without rotating a credential used by unrelated applications.

6. Reduced blast radius

When identities and role scopes are designed carefully, one compromised workload has only the permissions assigned to that workload.

Managed Identity disadvantages

1. More initial configuration

You need to configure both sides:

Function App identity
+
Target-resource authorization

2. RBAC propagation is not always immediate

After creating a role assignment, access may not work instantly.

Redeploying code repeatedly during this period can distract from the actual issue.

3. Troubleshooting spans multiple layers

A failed Managed Identity connection may involve:

  • The wrong identity
  • Missing RBAC role
  • Incorrect role scope
  • Incorrect endpoint
  • Unsupported extension version
  • Token acquisition
  • Network restrictions
  • Cross-tenant configuration
  • Stale role propagation

4. Local development needs a separate identity strategy

The local process cannot directly use the Azure Function’s system-assigned identity.

5. Not every dependency supports it

External services and some legacy Azure integrations still require a key, certificate, SAS token, or password.

Connection strings vs Managed Identity

When I would choose Managed Identity

I would normally prefer Managed Identity when:

  • The function is hosted in Azure
  • The target service supports Microsoft Entra authentication
  • The workload is long-lived
  • The application is production-facing
  • Multiple environments need independent access controls
  • Secret rotation would create operational risk
  • Least-privilege RBAC can be applied
  • Identity-specific auditing is useful
  • The Function App accesses resources across subscriptions

Examples:

Azure Function → Blob Storage
Azure Function → Storage Queue
Azure Function → Service Bus
Azure Function → Key Vault
Azure Function → Azure SQL
Azure Function → App Configuration

Support and configuration differ by service, so the implementation should be verified against that service’s current documentation.

When a connection string may still be reasonable

Connection strings are not automatically wrong.

They may still be appropriate when:

  • The target service does not support Managed Identity
  • You are using a local emulator
  • You are creating a short-lived proof of concept
  • A third-party dependency requires credentials
  • A legacy binding does not support identity-based connections
  • The migration risk is currently higher than the credential risk
  • The connection string contains only non-secret routing information

When a secret-bearing connection string is necessary:

  1. Never hard-code it.
  2. Keep it out of source control.
  3. Store it in a protected configuration system.
  4. Restrict who can read it.
  5. Rotate it regularly.
  6. Monitor its use.
  7. Avoid logging it.
  8. Scope the credential wherever possible.

Azure Key Vault improves secret storage, but it does not turn a shared key into an identity.

The application still retrieves and uses a secret.

A safe migration strategy

Do not migrate every connection at once without validation.

Use a staged process.

Step 1: Inventory existing secrets

Identify:

  • Storage keys
  • Service Bus connection strings
  • SQL usernames and passwords
  • SAS tokens
  • Client secrets
  • Key Vault secrets used as credentials

Step 2: Check service support

Verify that:

  • The Azure service supports Microsoft Entra authentication
  • Your SDK supports token credentials
  • The Function binding extension supports identity-based connections
  • Your hosting plan supports the required configuration

Step 3: Enable the identity

Choose between:

System-assigned identity
or
User-assigned identity

Step 4: Assign minimum required roles

Grant only the data operations needed by the function.

Avoid beginning with Owner or Contributor unless the application truly needs management-plane permissions.

Step 5: Add endpoint-based configuration

Replace:

OrderQueueStorage=<secret>

with an identity-based setting such as:

OrderQueueStorage__queueServiceUri=https://mystorageaccount.queue.core.windows.net

Step 6: Validate networking

Confirm that DNS, firewall, private endpoint, and VNet settings allow the Function App to reach the resource.

Step 7: Test the deployed identity

Do not rely only on local testing.

The developer identity and Function App identity are different security principals.

Step 8: Remove the old secret

After successful validation:

  • Remove the connection string
  • Remove unnecessary Key Vault access
  • Remove pipeline secrets
  • Rotate the old account key if it may have been broadly distributed
  • Update operational documentation

Common mistakes

Mistake 1: Enabling Managed Identity without assigning a role

Result:

Identity exists
but
access is denied

Mistake 2: Assigning Contributor instead of a data role

The application may be able to manage the resource but still be unable to read or process its data. Azure Storage requires explicit data-access roles for Microsoft Entra-authorized data operations.

Mistake 3: Assigning the role to the wrong identity

This often happens when:

  • Several user-assigned identities exist
  • Both system-assigned and user-assigned identities are enabled
  • The role is assigned using the wrong object ID
  • A Function App was recreated and received a new system-assigned identity

Mistake 4: Assuming local success proves Azure access

Your developer account may have broader access than the Function App.

Mistake 5: Ignoring extension versions

A trigger binding must explicitly support identity-based connections.

Updating application code alone may not update the binding behavior.

Mistake 6: Treating every 403 as a networking issue

Confirm:

  • Identity
  • Role
  • Scope
  • Data-plane permission
  • Propagation
  • Network access

Mistake 7: Keeping the secret forever as a fallback

Once the Managed Identity path is validated, leaving the old account key in configuration preserves the original risk.

A practical decision framework

Ask these questions in order:

1. Does the target service support Microsoft Entra authentication?
   ├── No → Use a securely stored credential.
   └── Yes
        ↓
2. Does the Function binding or SDK support token authentication?
   ├── No → Upgrade, redesign, or retain the credential temporarily.
   └── Yes
        ↓
3. Can the required access be represented through Azure RBAC?
   ├── No → Evaluate another supported authentication mechanism.
   └── Yes
        ↓
4. Is the workload hosted in Azure?
   ├── No → Use an appropriate workload identity or credential strategy.
   └── Yes
        ↓
5. Use Managed Identity and grant least privilege.

The biggest lesson

The difference is not merely syntax.

It is an ownership model.

With a connection string:

The application owns a credential.

With Managed Identity:

Azure owns the credential lifecycle.
The application owns an identity.
RBAC defines what that identity can do.

Managed Identity is usually the better production choice for supported Azure-to-Azure communication.

But it is not configuration-free, and it is not permission-free.

You replace secret management with identity and authorization management.

That is a valuable trade:

Fewer stored secrets
More explicit access control
Better workload identity
Smaller credential blast radius

Use connection strings when they are genuinely required.

Use Managed Identity when the platform supports it and the workload deserves a production-grade identity model.

Most importantly, do not ask only:

Can my Azure Function connect?

Also ask:

Which identity is connecting, exactly what can it access, and how would I revoke that access without affecting another application?

That is where Managed Identity provides its real value.


메타데이터
post_id
b2d2fe0edc4c
slug
managed-identity-vs-connection-strings-in-azure-functions-b2d2fe0edc4c
url
https://medium.com/@vishwasacharya/managed-identity-vs-connection-strings-in-azure-functions-b2d2fe0edc4c
canonical_url
https://medium.com/@vishwasacharya/managed-identity-vs-connection-strings-in-azure-functions-b2d2fe0edc4c
author_url
https://medium.com/@vishwasacharya
status
ok
fetched_at
2026-09-20 01:54:03