Skip to content

Using multiple apps to talk to Azure SQL Server doesn't work with managed identites #8389

@eerhardt

Description

@eerhardt

Description

When you have 2 projects talking to the same Azure SQL Server, and you use an ACA environment, each app will get a separate managed identity.

This causes problems with Azure SQL Server because the way .NET Aspire grants access to the SQL Server is via setting the SQL Admin:

// Creating a new SqlServer instance requires an administrator,
// so we need to create one here using the empty PrincipalId/PrincipalName
var principalIdParameter = new ProvisioningParameter(AzureBicepResource.KnownParameters.PrincipalId, typeof(string));
infrastructure.Add(principalIdParameter);
var principalNameParameter = new ProvisioningParameter(AzureBicepResource.KnownParameters.PrincipalName, typeof(string));
infrastructure.Add(principalNameParameter);
return new SqlServer(infrastructure.AspireResource.GetBicepIdentifier())
{
Administrators = new ServerExternalAdministrator()
{
AdministratorType = SqlAdministratorType.ActiveDirectory,
IsAzureADOnlyAuthenticationEnabled = true,
Sid = principalIdParameter,
Login = principalNameParameter,
TenantId = BicepFunction.GetSubscription().TenantId
},

internal static SqlServerAzureADAdministrator AddActiveDirectoryAdministrator(AzureResourceInfrastructure infra, SqlServer sqlServer, BicepValue<Guid> principalId, BicepValue<string> principalName)
{
var admin = new SqlServerAzureADAdministratorWorkaround($"{sqlServer.BicepIdentifier}_admin")
{
ParentOverride = sqlServer,
LoginOverride = principalName,
SidOverride = principalId
};
infra.Add(admin);
return admin;
}

Doing it this way means that the last managed identity / app that runs its roles bicep will be the admin, and any previous apps will no longer be able to talk to the SQL Server, since they no longer have access.

Instead of adding managed identities this way, we need to be adding the managed identity as a USER and assigning it a ROLE. See https://learn.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-service-principal-tutorial?view=azuresql.

One idea on how to do this is to use a deployment script to run a SQL script that adds each app's managed identity as a USER and adds appropriate roles for that user.

This is also highly related to Azure SQL Server Provisioning Requires Admin (dotnet/aspire#8381). Maybe we try to solve them both together? We could grant the userPrincipalId (i.e. whoever is doing the deployment) as the SQL Server admin, and then run a deployment script for each app that needs to talk to the SQL Server.

Repro

var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureContainerAppEnvironment("env");

var dbServer = builder.AddAzureSqlServer("sqlserver")
    .RunAsContainer(c => c.WithLifetime(ContainerLifetime.Persistent));

var todosDb = dbServer.AddDatabase("todosdb");

// The ApiDbService project is responsible for managing the database schema and seeding data.
var apiDbService = builder.AddProject<Projects.AspireStarterDb_ApiDbService>("apidbservice")
    .WithReference(todosDb)
    .WaitFor(todosDb)
    .WithHttpsHealthCheck("/health")
    .WithHttpsCommand("/reset-db", "Reset Database", iconName: "DatabaseLightning");

// The ApiService project provides backend HTTP APIs for the web frontend.
var apiService = builder.AddProject<Projects.AspireStarterDb_ApiService>("apiservice")
    .WithReference(todosDb)
    .WaitFor(apiDbService);
$ azd up

cc @sebastienros @davidfowl

Metadata

Metadata

Assignees

Labels

area-integrationsIssues pertaining to Aspire Integrations packagesazureIssues associated specifically with scenarios tied to using Azurebreaking-changeIssue or PR that represents a breaking API or functional change over a prerelease.sqlserverIssues related to SQLServer integrtions

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions