Skip to content

Azure SQL Integration (Managed Identity)

Use this recipe to connect Azure Container Apps to Azure SQL Database with Microsoft Entra authentication first and SQL authentication only as a fallback.

Architecture

flowchart TD
    C[Client] --> I[Container Apps Ingress]
    I --> APP[Java Container App]
    APP --> SQL[Azure SQL Database]
    APP -.-> MI[Managed Identity]
    MI -.-> ENTRA[Microsoft Entra ID]
    MI -.-> SQL

Solid arrows show runtime data flow. Dashed arrows show identity and authentication.

Prerequisites

  • Existing Container App: $APP_NAME in $RG
  • Existing Azure SQL logical server and database
  • Azure SQL server configured with a Microsoft Entra admin
  • Firewall rules, VNet integration, or Private Link already configured for your connectivity model

Step 1: Enable managed identity on the Container App

az containerapp identity assign \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --system-assigned

Step 2: Grant SQL access to the app identity

From a Microsoft Entra-authenticated SQL session, create a contained user for the managed identity.

CREATE USER [<container-app-name>] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [<container-app-name>];
ALTER ROLE db_datawriter ADD MEMBER [<container-app-name>];

Step 3: Configure non-secret settings

az containerapp update \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --set-env-vars SQL_SERVER="$SQL_SERVER.database.windows.net" SQL_DATABASE="$SQL_DATABASE"

Step 4: Java code (managed identity)

Add the Microsoft JDBC Driver dependency:

<dependency>
  <groupId>com.microsoft.sqlserver</groupId>
  <artifactId>mssql-jdbc</artifactId>
</dependency>

Use the driver-managed authentication mode for managed identity:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class AzureSqlRecipe {
    public static void main(String[] args) throws Exception {
        String server = System.getenv("SQL_SERVER");
        String database = System.getenv("SQL_DATABASE");

        String url;
        if (System.getenv("SQL_USER") != null && System.getenv("SQL_PASSWORD") != null) {
            url = String.format(
                "jdbc:sqlserver://%s:1433;database=%s;encrypt=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;user=%s;password=%s;",
                server,
                database,
                System.getenv("SQL_USER"),
                System.getenv("SQL_PASSWORD")
            );
        } else {
            url = String.format(
                "jdbc:sqlserver://%s:1433;database=%s;encrypt=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryManagedIdentity;",
                server,
                database
            );
        }

        try (Connection connection = DriverManager.getConnection(url);
             Statement statement = connection.createStatement();
             ResultSet resultSet = statement.executeQuery("SELECT TOP (1) name FROM sys.tables ORDER BY name")) {
            while (resultSet.next()) {
                System.out.println(resultSet.getString(1));
            }
        }
    }
}

Warning

If you use a user-assigned managed identity, add the client ID parameter required by your JDBC driver version before relying on the ActiveDirectoryManagedIdentity connection string in production.

Step 5: SQL authentication fallback

az containerapp secret set \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --secrets sql-password="<sql-password>"

az containerapp update \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --set-env-vars SQL_SERVER="$SQL_SERVER.database.windows.net" SQL_DATABASE="$SQL_DATABASE" SQL_USER="$SQL_USER" SQL_PASSWORD=secretref:sql-password

Verification

  1. Confirm identity is assigned to the app.
  2. Confirm runtime connectivity by checking application logs.
  3. Confirm Azure SQL firewall or private endpoint connectivity before blaming authentication.

See Also

Sources