From a44cdc3bce02c146e94a11504d22d9bbb9b051e4 Mon Sep 17 00:00:00 2001 From: Dimitri Fontaine Date: Mon, 6 Jul 2026 13:33:12 +0200 Subject: [PATCH] feat(mssql): bundle azure-identity for ActiveDirectoryDefault/AzCli/WorkloadIdentity auth Closes #1754 mssql-jdbc 12.6.1 supports ActiveDirectoryDefault, ActiveDirectoryAzCli, and ActiveDirectoryWorkloadIdentity but delegates them to the azure-identity library (com.azure:azure-identity), which was not previously bundled. Users hitting these modes got: java.lang.NoClassDefFoundError: com/azure/identity/DefaultAzureCredentialBuilder Add azure-identity 1.11.1 (the version mssql-jdbc 12.6.1 was certified against) to deps.edn so all non-browser-dependent Entra ID auth modes work out of the box. The SLF4J version conflict flagged by downstream workarounds (miljodir/DataEngineerPersona#6) does not affect the uberjar: Maven resolves to the newer slf4j-api already present via logback-classic. Also update docs/ref/mssql.rst: - Add ActiveDirectoryDefault (developer az login workflow, WSL2-safe), ActiveDirectoryAzCli, ActiveDirectoryWorkloadIdentity with examples - Deprecation note on ActiveDirectoryPassword (blocked by MFA enforcement in most modern Entra tenants, returns AADSTS50076) - Correct the ActiveDirectoryInteractive note: it works on native Windows / macOS / Linux desktops but fails in WSL2 (linux_xdg_open_failed) and headless environments; ActiveDirectoryDefault is the practical alternative - Quick-reference table of all auth modes and their use cases Add classpath assertion test for DefaultAzureCredentialBuilder, mirroring the existing MSAL4J test added in #1753. --- clojure/deps.edn | 1 + clojure/test/pgloader/load_file/ast_test.clj | 8 ++ docs/ref/mssql.rst | 92 ++++++++++++++++---- 3 files changed, 84 insertions(+), 17 deletions(-) diff --git a/clojure/deps.edn b/clojure/deps.edn index 45c18179..e0c6783c 100644 --- a/clojure/deps.edn +++ b/clojure/deps.edn @@ -17,6 +17,7 @@ com.mysql/mysql-connector-j {:mvn/version "9.0.0"} com.microsoft.sqlserver/mssql-jdbc {:mvn/version "12.6.1.jre11"} com.microsoft.azure/msal4j {:mvn/version "1.14.1"} + com.azure/azure-identity {:mvn/version "1.11.1"} org.xerial/sqlite-jdbc {:mvn/version "3.46.0.0"} com.opencsv/opencsv {:mvn/version "5.9"} diff --git a/clojure/test/pgloader/load_file/ast_test.clj b/clojure/test/pgloader/load_file/ast_test.clj index 51e6ac38..5a3b9252 100644 --- a/clojure/test/pgloader/load_file/ast_test.clj +++ b/clojure/test/pgloader/load_file/ast_test.clj @@ -94,6 +94,14 @@ false)) "com.microsoft.aad.msal4j must be on the classpath for ActiveDirectoryPassword / ActiveDirectoryServicePrincipal auth"))) +(deftest test-azure-identity-on-classpath + (testing "azure-identity is bundled — ActiveDirectoryDefault / AzCli / WorkloadIdentity auth modes are available" + (is (try (Class/forName "com.azure.identity.DefaultAzureCredentialBuilder") + true + (catch ClassNotFoundException _ + false)) + "com.azure.identity must be on the classpath for ActiveDirectoryDefault / ActiveDirectoryAzCli / ActiveDirectoryWorkloadIdentity auth"))) + (deftest test-parse-postgresql-synthesises-jdbc-url (testing "postgresql:// synthesises jdbc-url" (let [m (parse-uri "postgresql://user:pass@pg:5432/mydb")] diff --git a/docs/ref/mssql.rst b/docs/ref/mssql.rst index bbcab04d..f030231e 100644 --- a/docs/ref/mssql.rst +++ b/docs/ref/mssql.rst @@ -348,21 +348,33 @@ nor on the lack of support for regular expressions in the engine. Azure SQL / Azure Active Directory Authentication ------------------------------------------------- -pgloader v4 uses the Microsoft JDBC driver (``mssql-jdbc``) and bundles -MSAL4J, so the following non-interactive Azure AD authentication modes work -out of the box. Always use a ``jdbc:sqlserver://`` URL for Azure SQL so that -``encrypt=true`` and ``authentication=`` reach the driver exactly as written: - -``ActiveDirectoryPassword`` - Authenticate with an Azure AD username and password:: - - FROM "jdbc:sqlserver://myserver.database.windows.net:1433;databaseName=mydb;\ -authentication=ActiveDirectoryPassword;user=user@tenant.onmicrosoft.com;\ -password=secret;encrypt=true;trustServerCertificate=false" +pgloader v4 uses the Microsoft JDBC driver (``mssql-jdbc``) and bundles both +MSAL4J and the Azure Identity library (``azure-identity``), so all +non-browser-dependent Entra ID authentication modes work out of the box. +Always use a ``jdbc:sqlserver://`` URL for Azure SQL so that ``encrypt=true`` +and ``authentication=`` reach the driver exactly as written. + +Quick reference: + ++--------------------------------------+------------------------------------------------+ +| Mode | Typical use case | ++======================================+================================================+ +| ``ActiveDirectoryServicePrincipal`` | CI/CD — app registration (client ID + secret) | ++--------------------------------------+------------------------------------------------+ +| ``ActiveDirectoryManagedIdentity`` | Azure VM / App Service / AKS — no credentials | ++--------------------------------------+------------------------------------------------+ +| ``ActiveDirectoryDefault`` | Developer laptop: ``az login`` fallback chain | ++--------------------------------------+------------------------------------------------+ +| ``ActiveDirectoryAzCli`` | Developer laptop: explicit ``az login`` token | ++--------------------------------------+------------------------------------------------+ +| ``ActiveDirectoryWorkloadIdentity`` | Kubernetes OIDC workload identity | ++--------------------------------------+------------------------------------------------+ +| ``ActiveDirectoryPassword`` | **Deprecated** — blocked by MFA enforcement | ++--------------------------------------+------------------------------------------------+ ``ActiveDirectoryServicePrincipal`` - Authenticate as an Azure AD application (client ID + client secret), - suitable for fully automated / CI pipelines:: + Authenticate as an Azure AD application (client ID + client secret) — + the recommended mode for CI/CD pipelines and automated migrations:: FROM "jdbc:sqlserver://myserver.database.windows.net:1433;databaseName=mydb;\ authentication=ActiveDirectoryServicePrincipal;\ @@ -371,16 +383,62 @@ encrypt=true" ``ActiveDirectoryManagedIdentity`` Authenticate using the managed identity of the Azure VM, App Service, - or container that pgloader runs in — no credentials in the URL:: + AKS pod, or container that pgloader runs in — no credentials in the URL:: FROM "jdbc:sqlserver://myserver.database.windows.net:1433;databaseName=mydb;\ authentication=ActiveDirectoryManagedIdentity;encrypt=true" +``ActiveDirectoryDefault`` + The recommended mode for developer workstations. Uses the Azure + Identity ``DefaultAzureCredential`` chain: environment variables → + workload identity → managed identity → **Azure CLI** → Azure PowerShell. + After a one-time ``az login``, no credentials need to appear in the URL:: + + FROM "jdbc:sqlserver://myserver.database.windows.net:1433;databaseName=mydb;\ +authentication=ActiveDirectoryDefault;encrypt=true;trustServerCertificate=false" + + This also works headlessly in WSL2: ``DefaultAzureCredential`` falls back + to ``AzureCliCredential``, which shells out to ``az account + get-access-token`` using your existing ``az login`` session — no browser + popup is needed. + +``ActiveDirectoryAzCli`` + Like ``ActiveDirectoryDefault`` but always uses the Azure CLI token + exclusively. Useful when you want a predictable, unambiguous credential + source on a developer machine:: + + FROM "jdbc:sqlserver://myserver.database.windows.net:1433;databaseName=mydb;\ +authentication=ActiveDirectoryAzCli;encrypt=true;trustServerCertificate=false" + +``ActiveDirectoryWorkloadIdentity`` + For Kubernetes pods with OIDC workload identity federation:: + + FROM "jdbc:sqlserver://myserver.database.windows.net:1433;databaseName=mydb;\ +authentication=ActiveDirectoryWorkloadIdentity;encrypt=true" + +``ActiveDirectoryPassword`` + .. note:: + + **Deprecated by Microsoft.** This mode is incompatible with mandatory + Entra ID multifactor authentication (MFA), which Microsoft now enforces + by default across most tenants. Attempting it in an MFA-required + tenant returns ``AADSTS50076``. Use ``ActiveDirectoryDefault`` or + ``ActiveDirectoryServicePrincipal`` instead. + + :: + + FROM "jdbc:sqlserver://myserver.database.windows.net:1433;databaseName=mydb;\ +authentication=ActiveDirectoryPassword;user=user@tenant.onmicrosoft.com;\ +password=secret;encrypt=true;trustServerCertificate=false" + .. note:: - ``ActiveDirectoryInteractive`` opens a browser window for an OAuth2 - interactive login and **cannot** be used with pgloader, which runs - non-interactively. + ``ActiveDirectoryInteractive`` makes MSAL4J open a system browser via + ``xdg-open`` for an OAuth2 interactive login. It works on native Windows, + native macOS, and Linux desktops with a display server. It **fails** in + WSL2 (``linux_xdg_open_failed`` — WSL2 has no JVM-accessible browser + integration), headless Linux, Docker/Podman containers, and CI pipelines. + Use ``ActiveDirectoryDefault`` with an existing ``az login`` session instead. The native ``mssql://`` scheme defaults to ``encrypt=false`` to match on-premises SQL Server behaviour. For Azure SQL use the