<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>Azure Database Support Blog articles</title>
    <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/bg-p/AzureDBSupport</link>
    <description>Azure Database Support Blog articles</description>
    <pubDate>Fri, 21 Aug 2026 22:09:29 GMT</pubDate>
    <dc:creator>AzureDBSupport</dc:creator>
    <dc:date>2026-08-21T22:09:29Z</dc:date>
    <item>
      <title>Getting Started with Azure SQL Data Sync Using User-Assigned Managed Identity (UAMI)</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/getting-started-with-azure-sql-data-sync-using-user-assigned/ba-p/4547758</link>
      <description>&lt;P&gt;Azure SQL Data Sync is a powerful service that enables data synchronization across multiple Azure SQL Databases. Traditionally, Data Sync relied on SQL Authentication, requiring administrators to manage usernames and passwords for both hub and member databases.&lt;/P&gt;
&lt;P&gt;With the introduction of &lt;STRONG&gt;User-Assigned Managed Identity (UAMI)&lt;/STRONG&gt; support, Azure SQL Data Sync now provides a more secure and modern authentication model built on Microsoft Entra ID. This enhancement reduces credential management overhead, eliminates password storage concerns, and helps organizations strengthen their security posture.&lt;/P&gt;
&lt;P&gt;In this article, we'll explore how to onboard Azure SQL Data Sync with UAMI, configure the required permissions, create synchronization components, and review best practices for long-term management.&lt;/P&gt;
&lt;H1&gt;Why Use UAMI with Azure SQL Data Sync?&lt;/H1&gt;
&lt;P&gt;User-Assigned Managed Identity offers several advantages over traditional SQL authentication:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Eliminates password storage and rotation requirements&lt;/LI&gt;
&lt;LI&gt;Reduces credential exposure risks&lt;/LI&gt;
&lt;LI&gt;Integrates with Microsoft Entra ID authentication&lt;/LI&gt;
&lt;LI&gt;Supports centralized identity management&lt;/LI&gt;
&lt;LI&gt;Improves compliance and security posture&lt;/LI&gt;
&lt;LI&gt;Simplifies operational maintenance&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Whether you're deploying a new synchronization topology or modernizing an existing Data Sync environment, UAMI provides a secure cloud-native alternative to password-based authentication.&lt;/P&gt;
&lt;H1&gt;Architecture Overview&lt;/H1&gt;
&lt;P&gt;A typical UAMI-enabled Azure SQL Data Sync deployment looks like this:&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Authentication Method:&lt;/P&gt;
&lt;P&gt;Microsoft Entra ID via User-Assigned Managed Identity&lt;/P&gt;
&lt;P&gt;The same managed identity can be used by Data Sync to authenticate against all participating databases.&lt;/P&gt;
&lt;H1&gt;Prerequisites&lt;/H1&gt;
&lt;P&gt;Before configuring Azure SQL Data Sync with UAMI, ensure the following prerequisites are completed:&lt;/P&gt;
&lt;H3&gt;1. Enable Microsoft Entra Authentication&lt;/H3&gt;
&lt;P&gt;The Azure SQL Server hosting both Hub and Member databases must have Microsoft Entra authentication enabled and an Entra administrator configured.&lt;/P&gt;
&lt;H3&gt;2. Create a User-Assigned Managed Identity&lt;/H3&gt;
&lt;P&gt;Create a UAMI within Azure and collect the following information:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Managed Identity Name&lt;/LI&gt;
&lt;LI&gt;Client ID&lt;/LI&gt;
&lt;LI&gt;Resource ID&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;These values will be required during configuration.&lt;/P&gt;
&lt;H3&gt;3. Install the Required PowerShell Module&lt;/H3&gt;
&lt;P&gt;UAMI support requires the Azure SQL preview PowerShell module:&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;Install-Module Az.Sql -RequiredVersion 6.6.0-preview `
-AllowPrerelease `
-Force `
-AllowClobber&lt;/LI-CODE&gt;
&lt;P&gt;Or a later version that includes Data Sync UAMI support.&lt;/P&gt;
&lt;H1&gt;Phase 1: Configure Database Access&lt;/H1&gt;
&lt;P&gt;Before Data Sync can use a managed identity, the identity must be granted access to all databases participating in synchronization.&lt;/P&gt;
&lt;P&gt;Connect to each Hub and Member database using a Microsoft Entra administrator account and create a user for the managed identity.&lt;/P&gt;
&lt;P&gt;Example:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;DECLARE @MSIname SYSNAME = '&amp;lt;UAMI_NAME&amp;gt;';
DECLARE @dbName SYSNAME = DB_NAME();
DECLARE @clientId UNIQUEIDENTIFIER = '&amp;lt;CLIENT_ID&amp;gt;';

-- Create User
-- Grant db_datareader
-- Grant db_datawriter
-- Grant CONTROL permissions&lt;/LI-CODE&gt;
&lt;P&gt;The UAMI must be created and granted permissions on every database participating in synchronization.&lt;/P&gt;
&lt;H1&gt;Phase 2: Create the Sync Group&lt;/H1&gt;
&lt;P&gt;After permissions are configured, create the Sync Group using UAMI authentication.&lt;/P&gt;
&lt;P&gt;Example PowerShell:&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;New-AzSqlSyncGroup `
-ResourceGroupName $resourceGroup `
-ServerName $serverName `
-DatabaseName $hubDatabase `
-Name $syncGroup `
-HubDatabaseAuthenticationType UserAssigned `
-ResourceId $identityResourceId&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H3&gt;Key Parameters&lt;/H3&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Parameter&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;HubDatabaseAuthenticationType&lt;/td&gt;&lt;td&gt;Authentication method for Hub Database&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;UserAssigned&lt;/td&gt;&lt;td&gt;Enables UAMI authentication&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ResourceId&lt;/td&gt;&lt;td&gt;Full ARM Resource ID of the UAMI&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;H1&gt;Phase 3: Add Sync Members&lt;/H1&gt;
&lt;P&gt;Once the Sync Group is created, add member databases and specify UAMI authentication.&lt;/P&gt;
&lt;P&gt;Example:&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;New-AzSqlSyncMember `
-ResourceGroupName $resourceGroup `
-ServerName $serverName `
-DatabaseName $hubDatabase `
-SyncGroupName $syncGroup `
-Name $memberName `
-MemberDatabaseAuthenticationType UserAssigned `
-ResourceId $identityResourceId&lt;/LI-CODE&gt;
&lt;P&gt;At this stage, both Hub and Member databases are configured to authenticate using the managed identity.&lt;/P&gt;
&lt;H1&gt;Phase 4: Configure Synchronization&lt;/H1&gt;
&lt;H2&gt;Refresh Hub Schema&lt;/H2&gt;
&lt;P&gt;Refresh the schema metadata before selecting tables for synchronization.&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;Update-AzSqlSyncSchema `
-ResourceGroupName $resourceGroup `
-ServerName $serverName `
-DatabaseName $hubDatabase `
-SyncGroupName $syncGroup&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;Configure the Synchronization Schema&lt;/H2&gt;
&lt;P&gt;Data Sync requires an explicit schema definition specifying which tables and columns will participate in synchronization.&lt;/P&gt;
&lt;P&gt;Example schema:&lt;/P&gt;
&lt;LI-CODE lang="json"&gt;{
  "Tables": [
    {
      "QuotedName": "[dbo].[contacts]",
      "Columns": [
        {
          "QuotedName": "[id]"
        },
        {
          "QuotedName": "[name]"
        }
      ]
    }
  ]
}&lt;/LI-CODE&gt;
&lt;P&gt;Apply the schema:&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;Update-AzSqlSyncGroup `
-ResourceGroupName $resourceGroup `
-ServerName $serverName `
-DatabaseName $hubDatabase `
-Name $syncGroup `
-HubDatabaseAuthenticationType UserAssigned `
-ResourceId $identityResourceId `
-SchemaFile "C:\schema.json"&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;Trigger Synchronization&lt;/H2&gt;
&lt;P&gt;After configuration is complete, trigger the initial synchronization.&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;Start-AzSqlSyncGroupSync `
-ResourceGroupName $resourceGroup `
-ServerName $serverName `
-DatabaseName $hubDatabase `
-SyncGroupName $syncGroup&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H1&gt;Phase 5: Rotating UAMIs&lt;/H1&gt;
&lt;P&gt;Over time, organizations may need to replace an existing managed identity.&lt;/P&gt;
&lt;P&gt;UAMI_v1 → UAMI_v2&lt;/P&gt;
&lt;P&gt;Only one UAMI can be assigned to a Sync Group or Sync Member at a time.&lt;/P&gt;
&lt;P&gt;To rotate identities:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Grant required database permissions to the new UAMI.&lt;/LI&gt;
&lt;LI&gt;Remove the current UAMI.&lt;/LI&gt;
&lt;LI&gt;Assign the new UAMI in the same operation.&lt;/LI&gt;
&lt;/OL&gt;
&lt;H3&gt;Update Sync Group&lt;/H3&gt;
&lt;LI-CODE lang="powershell"&gt;Update-AzSqlSyncGroup `
-HubDatabaseAuthenticationType UserAssigned `
-ResourceId $newUamiResourceId `
-RemoveIdentityResourceId $oldUamiResourceId
&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H3&gt;Update Sync Member&lt;/H3&gt;
&lt;LI-CODE lang="powershell"&gt;Update-AzSqlSyncMember `
-MemberDatabaseAuthenticationType UserAssigned `
-ResourceId $newUamiResourceId `
-RemoveIdentityResourceId $oldUamiResourceId&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H1&gt;Common Pitfalls&lt;/H1&gt;
&lt;P&gt;During implementation, the following issues are commonly encountered:&lt;/P&gt;
&lt;H3&gt;Pitfall #1: Missing Entra Administrator&lt;/H3&gt;
&lt;P&gt;If the SQL Server does not have an Entra administrator configured, UAMI authentication will fail.&lt;/P&gt;
&lt;H3&gt;Pitfall #2: Missing Database Permissions&lt;/H3&gt;
&lt;P&gt;The managed identity must be created and granted permissions in every participating database.&lt;/P&gt;
&lt;H3&gt;Pitfall #3: Multiple UAMIs Assigned&lt;/H3&gt;
&lt;P&gt;Azure SQL Data Sync supports only one UAMI per Sync Group or Sync Member at a time.&lt;/P&gt;
&lt;H3&gt;Pitfall #4: Identity Included During PATCH Updates&lt;/H3&gt;
&lt;P&gt;When updating schemas through REST APIs, avoid re-submitting the identity block if the managed identity is already assigned. This may result in a DataSyncMultipleIdentities error.&lt;/P&gt;
&lt;H1&gt;Validation Checklist&lt;/H1&gt;
&lt;P&gt;Before moving to production, verify the following:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Microsoft Entra administrator configured&lt;/LI&gt;
&lt;LI&gt;UAMI created successfully&lt;/LI&gt;
&lt;LI&gt;UAMI user created in all Hub and Member databases&lt;/LI&gt;
&lt;LI&gt;Required permissions granted&lt;/LI&gt;
&lt;LI&gt;Sync Group configured with UserAssigned authentication&lt;/LI&gt;
&lt;LI&gt;Sync Members configured with UserAssigned authentication&lt;/LI&gt;
&lt;LI&gt;Schema refreshed successfully&lt;/LI&gt;
&lt;LI&gt;Synchronization schema applied&lt;/LI&gt;
&lt;LI&gt;Initial synchronization completed successfully&lt;/LI&gt;
&lt;LI&gt;Data synchronized correctly between Hub and Member databases&lt;/LI&gt;
&lt;/OL&gt;
&lt;H1&gt;Best Practices&lt;/H1&gt;
&lt;P&gt;For secure and scalable deployments:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Prefer UAMI over SQL Authentication for new deployments.&lt;/LI&gt;
&lt;LI&gt;Use dedicated managed identities for Data Sync workloads.&lt;/LI&gt;
&lt;LI&gt;Follow least-privilege access principles where possible.&lt;/LI&gt;
&lt;LI&gt;Test configuration changes in non-production environments first.&lt;/LI&gt;
&lt;LI&gt;Maintain documentation for identity ownership and rotation procedures.&lt;/LI&gt;
&lt;LI&gt;Monitor synchronization health after identity updates.&lt;/LI&gt;
&lt;LI&gt;Standardize naming conventions for managed identities.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;References&lt;/H1&gt;
&lt;P&gt;The following Microsoft resources provide additional details for implementing Azure SQL Data Sync with User-Assigned Managed Identities:&lt;/P&gt;
&lt;H3&gt;1. Configure Microsoft Entra Authentication for Azure SQL Database&lt;/H3&gt;
&lt;P&gt;Configure an Entra administrator for Azure SQL Database, which is a prerequisite for UAMI-based authentication.&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/azure/azure-sql/database/authentication-aad-configure" target="_blank"&gt;https://learn.microsoft.com/azure/azure-sql/database/authentication-aad-configure&lt;/A&gt;&lt;/P&gt;
&lt;H3&gt;2. Manage User-Assigned Managed Identities&lt;/H3&gt;
&lt;P&gt;Learn how to create and manage User-Assigned Managed Identities in Azure. Managed identities eliminate the need to manage credentials in code and can be reused across multiple Azure resources. Manage user-assigned managed identities using the Azure portal explains the process and prerequisites. &lt;A href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/manage-user-assigned-managed-identities-azure-portal" target="_blank"&gt;[Manage use...soft Learn | Learn.Microsoft.com]&lt;/A&gt;&lt;/P&gt;
&lt;H3&gt;3. Connect to Azure SQL Using Microsoft Entra Authentication&lt;/H3&gt;
&lt;P&gt;Configure and validate Entra-based connectivity for Azure SQL Database before granting permissions to the managed identity.&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/azure/azure-sql/database/authentication-microsoft-entra-connect-to-azure-sql" target="_blank"&gt;https://learn.microsoft.com/azure/azure-sql/database/authentication-microsoft-entra-connect-to-azure-sql&lt;/A&gt;&lt;/P&gt;
&lt;H3&gt;4. Azure SQL Data Sync PowerShell Documentation&lt;/H3&gt;
&lt;P&gt;Review PowerShell cmdlets used to create Sync Groups, Sync Members, refresh schemas, and trigger synchronization.&lt;/P&gt;
&lt;H3&gt;5. Azure SQL REST API Documentation&lt;/H3&gt;
&lt;P&gt;Use REST APIs for automation and Infrastructure-as-Code deployment scenarios involving Azure SQL Data Sync.&lt;/P&gt;
&lt;H1&gt;Conclusion&lt;/H1&gt;
&lt;P&gt;User-Assigned Managed Identity support represents a significant security enhancement for Azure SQL Data Sync. By eliminating dependency on stored credentials and leveraging Microsoft Entra authentication, organizations can improve security, simplify operations, and reduce administrative overhead.&lt;/P&gt;
&lt;P&gt;Whether you're building a new synchronization topology or migrating from SQL Authentication, UAMI provides a modern, scalable, and cloud-native authentication model for Azure SQL Data Sync.&lt;/P&gt;
&lt;P&gt;As cloud environments continue to adopt identity-first security practices, implementing UAMI for Azure SQL Data Sync is an important step toward a more secure and manageable data platform.&lt;/P&gt;</description>
      <pubDate>Tue, 18 Aug 2026 09:12:37 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/getting-started-with-azure-sql-data-sync-using-user-assigned/ba-p/4547758</guid>
      <dc:creator>Mohamed_Baioumy_MSFT</dc:creator>
      <dc:date>2026-08-18T09:12:37Z</dc:date>
    </item>
    <item>
      <title>Azure SQL Data Sync Fails with "Cannot Insert NULL": Understanding the Root Cause</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/azure-sql-data-sync-fails-with-quot-cannot-insert-null-quot/ba-p/4547709</link>
      <description>&lt;P&gt;Azure SQL Data Sync is a powerful service that enables data synchronization across multiple Azure SQL Databases. While synchronization failures are relatively uncommon, one error that administrators occasionally encounter is SQL Server Error 515, indicating that a NULL value cannot be inserted into a non-nullable column.&lt;/P&gt;
&lt;P&gt;At first glance, this appears to be a straightforward data-quality problem. However, in many cases, the actual root cause lies elsewhere: inconsistencies between Azure SQL Data Sync tracking metadata and the underlying source data.&lt;/P&gt;
&lt;P&gt;This article explains:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Common causes of Error 515 during synchronization&lt;/LI&gt;
&lt;LI&gt;How to troubleshoot the issue&lt;/LI&gt;
&lt;LI&gt;How to identify invalid tracking records&lt;/LI&gt;
&lt;LI&gt;Safe mitigation approaches to restore synchronization&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;The Error&lt;/H2&gt;
&lt;P&gt;A synchronization operation may fail with an error similar to the following:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;SqlException Error Code: -2146232060&lt;/P&gt;
&lt;P&gt;SqlError Number: 515&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Message:&lt;/P&gt;
&lt;P&gt;Cannot insert the value NULL into column 'column_name',&lt;/P&gt;
&lt;P&gt;table 'dbo.table_name';&lt;/P&gt;
&lt;P&gt;column does not allow nulls.&lt;/P&gt;
&lt;P&gt;INSERT fails.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;SqlError Number: 3621&lt;/P&gt;
&lt;P&gt;The statement has been terminated.&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Although the error references a NULL value being inserted into a destination table, the root cause is not always missing data. In many cases, the issue originates from synchronization metadata maintained by Azure SQL Data Sync.&lt;/P&gt;
&lt;H1&gt;How Azure SQL Data Sync Tracks Changes&lt;/H1&gt;
&lt;P&gt;Azure SQL Data Sync relies on internal tracking tables to detect and replicate data changes between Hub and Member databases.&lt;/P&gt;
&lt;P&gt;Whenever rows are inserted, updated, or deleted, synchronization metadata is recorded in tracking tables. Data Sync uses this metadata to determine what changes need to be propagated to other databases.&lt;/P&gt;
&lt;P&gt;If the tracking metadata becomes inconsistent with the actual source table contents, Data Sync may attempt to synchronize invalid records, resulting in failures such as:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;Cannot insert the value NULL into column...&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;H1&gt;Common Root Causes&lt;/H1&gt;
&lt;H2&gt;Scenario 1: Schema Mismatch Between Databases&lt;/H2&gt;
&lt;P&gt;One of the most common causes of synchronization failures is a schema mismatch between synchronized databases.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Database&lt;/th&gt;&lt;th&gt;Column Definition&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Hub&lt;/td&gt;&lt;td&gt;NULL Allowed&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Member A&lt;/td&gt;&lt;td&gt;NOT NULL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Member B&lt;/td&gt;&lt;td&gt;NOT NULL&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;If Data Sync replicates a row containing a NULL value from the Hub database, synchronization will fail when the destination database does not allow NULL values.&lt;/P&gt;
&lt;H3&gt;Areas to Validate&lt;/H3&gt;
&lt;P&gt;Ensure the following are identical across all synchronized databases:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Column nullability (NULL vs NOT NULL)&lt;/LI&gt;
&lt;LI&gt;Data types&lt;/LI&gt;
&lt;LI&gt;Column length&lt;/LI&gt;
&lt;LI&gt;Constraints&lt;/LI&gt;
&lt;LI&gt;Primary key definitions&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Even small schema differences can cause synchronization failures.&lt;/P&gt;
&lt;H2&gt;Scenario 2: Invalid Tracking Metadata&lt;/H2&gt;
&lt;P&gt;A less obvious but frequently encountered scenario involves orphaned records in Data Sync tracking tables.&lt;/P&gt;
&lt;P&gt;This can occur when:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Primary key values are updated directly&lt;/LI&gt;
&lt;LI&gt;Data is modified outside expected application workflows&lt;/LI&gt;
&lt;LI&gt;Historical tracking records become disconnected from source data&lt;/LI&gt;
&lt;LI&gt;Synchronization metadata references rows that no longer exist&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;When Data Sync processes these stale entries, synchronization may fail with Error 515 even though the source data itself appears valid.&lt;/P&gt;
&lt;H1&gt;Troubleshooting Process&lt;/H1&gt;
&lt;H2&gt;Step 1: Verify Column Definitions&lt;/H2&gt;
&lt;P&gt;Begin by examining the affected table and column identified in the error message.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;sp_help 'dbo.table_name'&lt;/LI-CODE&gt;
&lt;P&gt;Review the schema on both Hub and Member databases and verify that:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The affected column has the same definition everywhere&lt;/LI&gt;
&lt;LI&gt;NULL settings are identical&lt;/LI&gt;
&lt;LI&gt;Data types and lengths match&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;If discrepancies exist, align the schemas across all synchronized databases before proceeding.&lt;/P&gt;
&lt;H2&gt;Step 2: Review the Table Schema&lt;/H2&gt;
&lt;P&gt;If the schema appears consistent, review the complete definition of the affected table.&lt;/P&gt;
&lt;P&gt;Pay particular attention to:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Primary key columns&lt;/LI&gt;
&lt;LI&gt;Identity columns&lt;/LI&gt;
&lt;LI&gt;Constraints&lt;/LI&gt;
&lt;LI&gt;Nullable settings&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Identifying the primary key is especially important for the next validation step.&lt;/P&gt;
&lt;H2&gt;Step 3: Check for Orphaned Tracking Records&lt;/H2&gt;
&lt;P&gt;Run the following query against both Hub and Member databases.&lt;/P&gt;
&lt;P&gt;Replace:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;table_name&lt;/LI&gt;
&lt;LI&gt;primary_key&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;with the actual table and primary key column names.&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;SELECT COUNT(*)
FROM DataSync.table_name_dss_tracking t
WHERE sync_row_is_tombstone = 0
AND NOT EXISTS
(
SELECT *
FROM dbo.table_name s
WHERE t.primary_key = s.primary_key
);&lt;/LI-CODE&gt;
&lt;P&gt;For tables with composite primary keys, include all key columns in the comparison.&lt;/P&gt;
&lt;H2&gt;How to Interpret the Results&lt;/H2&gt;
&lt;H3&gt;Result &amp;gt; 0&lt;/H3&gt;
&lt;P&gt;One or more orphaned tracking records exist.&lt;/P&gt;
&lt;P&gt;This indicates that the tracking table contains entries that reference records no longer present in the source table.&lt;/P&gt;
&lt;P&gt;This is a strong indicator that invalid synchronization metadata is causing the failure.&lt;/P&gt;
&lt;H3&gt;Result = 0&lt;/H3&gt;
&lt;P&gt;No orphaned records were detected.&lt;/P&gt;
&lt;P&gt;If the synchronization error persists, further investigation should focus on schema consistency, data quality, and additional synchronization diagnostics.&lt;/P&gt;
&lt;H1&gt;Mitigation Option 1: Correct Schema Differences&lt;/H1&gt;
&lt;P&gt;If schema inconsistencies are found:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Align the table definition across all synchronized databases.&lt;/LI&gt;
&lt;LI&gt;Ensure NULL and NOT NULL settings are consistent.&lt;/LI&gt;
&lt;LI&gt;Verify primary key definitions match.&lt;/LI&gt;
&lt;LI&gt;Reinitialize synchronization if necessary.&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;After schema alignment, synchronization can typically resume successfully.&lt;/P&gt;
&lt;H1&gt;Mitigation Option 2: Clean Invalid Tracking Data&lt;/H1&gt;
&lt;P&gt;If orphaned tracking records are identified, remove the invalid synchronization metadata.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;STRONG&gt;Important:&lt;/STRONG&gt; Always validate and test cleanup operations in a non-production environment before executing them in production.&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;P&gt;The following query removes tracking entries that no longer correspond to records in the source table:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;DELETE
FROM DataSync.table_name_dss_tracking
WHERE sync_row_is_tombstone = 0
AND NOT EXISTS
(
SELECT *
FROM dbo.table_name s
WHERE DataSync.table_name_dss_tracking.primary_key = s.primary_key
);&lt;/LI-CODE&gt;
&lt;P&gt;Replace:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;table_name&lt;/LI&gt;
&lt;LI&gt;primary_key&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;with the appropriate values for your environment.&lt;/P&gt;
&lt;P&gt;After cleanup, Data Sync can rebuild valid change tracking information and synchronization typically returns to a healthy state.&lt;/P&gt;
&lt;H1&gt;Additional Validation Query&lt;/H1&gt;
&lt;P&gt;The following query can help identify historical deletion records that exist in tracking tables:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;SELECT tr.id1
FROM DataSync.table2_dss_tracking tr
LEFT JOIN dbo.table2 orig
ON tr.id1 = orig.id1
WHERE tr.sync_row_is_tombstone = 1
AND orig.id1 IS NULL
AND tr.last_change_datetime &amp;gt;
DATEADD(day, -20, GETUTCDATE());&lt;/LI-CODE&gt;
&lt;P&gt;This can provide additional insight into how synchronization metadata is tracking deleted records.&lt;/P&gt;
&lt;H1&gt;Understanding the Underlying Cause&lt;/H1&gt;
&lt;P&gt;The most important takeaway is that the NULL value reported in the synchronization error is often not the actual problem.&lt;/P&gt;
&lt;P&gt;A common sequence looks like this:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;A primary key value is modified directly.&lt;/LI&gt;
&lt;/OL&gt;
&lt;LI-CODE lang="sql"&gt;UPDATE dbo.table_name
SET primary_key = new_value;&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Data Sync tracking metadata continues to reference the original key value.&lt;/LI&gt;
&lt;LI&gt;The source table and tracking table become inconsistent.&lt;/LI&gt;
&lt;LI&gt;During synchronization, Data Sync attempts to process the stale tracking record.&lt;/LI&gt;
&lt;LI&gt;The synchronization operation fails and surfaces a &lt;STRONG&gt;"Cannot insert the value NULL into column"&lt;/STRONG&gt; error.&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;In these scenarios, cleaning invalid tracking records resolves the inconsistency and restores successful synchronization.&lt;/P&gt;
&lt;H1&gt;Best Practices to Prevent Recurrence&lt;/H1&gt;
&lt;P&gt;To minimize the likelihood of synchronization failures:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Keep schemas identical across all synchronized databases&lt;/LI&gt;
&lt;LI&gt;Avoid updating primary key values whenever possible&lt;/LI&gt;
&lt;LI&gt;Use surrogate keys for synchronized tables&lt;/LI&gt;
&lt;LI&gt;Validate schema consistency before deploying schema changes&lt;/LI&gt;
&lt;LI&gt;Periodically investigate Data Sync tracking tables when troubleshooting synchronization failures&lt;/LI&gt;
&lt;LI&gt;Test schema modifications in non-production environments before deployment&lt;/LI&gt;
&lt;/OL&gt;
&lt;H1&gt;Conclusion&lt;/H1&gt;
&lt;P&gt;When Azure SQL Data Sync reports a:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;Cannot insert the value NULL into column...&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;P&gt;error, it is important not to assume that the problem is caused by missing data in the source table.&lt;/P&gt;
&lt;P&gt;A structured troubleshooting approach should include:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Verifying schema consistency across synchronized databases&lt;/LI&gt;
&lt;LI&gt;Reviewing primary key definitions&lt;/LI&gt;
&lt;LI&gt;Investigating Data Sync tracking tables for orphaned records&lt;/LI&gt;
&lt;LI&gt;Cleaning invalid synchronization metadata when appropriate&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;In many real-world cases, stale tracking records are the true root cause. Identifying and removing these invalid entries can restore synchronization quickly and avoid unnecessary application or schema changes.&lt;/P&gt;
&lt;P&gt;Have you encountered similar Azure SQL Data Sync issues in your environment? Share your experience and troubleshooting techniques in the comments below.&lt;/P&gt;</description>
      <pubDate>Tue, 18 Aug 2026 07:55:05 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/azure-sql-data-sync-fails-with-quot-cannot-insert-null-quot/ba-p/4547709</guid>
      <dc:creator>Mohamed_Baioumy_MSFT</dc:creator>
      <dc:date>2026-08-18T07:55:05Z</dc:date>
    </item>
    <item>
      <title>Closing the SQL Data Sync Monitoring Gap: Building Proactive Alerts with Azure Logic Apps</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/closing-the-sql-data-sync-monitoring-gap-building-proactive/ba-p/4542212</link>
      <description>&lt;P&gt;&lt;A class="lia-external-url" href="https://azure.microsoft.com/en-us/updates?id=sql-data-sync-retirement" target="_blank"&gt;Microsoft Azure SQL Data Sync will be retired on September 30, 2027&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;U style="color: rgb(30, 30, 30);"&gt;&lt;STRONG&gt;Introduction&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;One of the most common lessons we've learned while working with customers running distributed applications is that &lt;STRONG&gt;data synchronization failures rarely happen suddenly.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;Most synchronization issues leave breadcrumbs long before a full outage occurs. A warning here.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;A failed row there. A transient connectivity issue that appears harmless today but becomes tomorrow's production incident.&lt;/P&gt;
&lt;P&gt;For organizations still leveraging &lt;STRONG&gt;Azure SQL Data Sync&lt;/STRONG&gt;, there is an important operational challenge:&lt;/P&gt;
&lt;P&gt;SQL Data Sync does not expose synchronization health as an Azure Monitor metric.&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;This means teams cannot simply create an Azure Monitor alert rule and be notified when synchronization health begins to degrade.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;Recently, while working in my own Azure lab environment, We explored a practical way to close this observability gap. The goal was simple:&lt;/P&gt;
&lt;P&gt;&lt;BR /&gt;&lt;STRONG&gt;Detect Data Sync warnings and errors early and automatically notify operations teams before synchronization failures impact business applications.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The result was a lightweight, serverless monitoring solution built entirely with &lt;STRONG&gt;Azure Logic Apps and Managed Identity&lt;/STRONG&gt;, requiring minimal operational overhead.&lt;/P&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;The Hidden Monitoring Challenge&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;Many Azure services integrate seamlessly with Azure Monitor and emit metrics that can be used for alerting, dashboards, and incident management.&lt;/P&gt;
&lt;P&gt;SQL Data Sync is different.&lt;/P&gt;
&lt;P&gt;Although synchronization activity is recorded, the health information resides within the &lt;STRONG&gt;Sync Group Logs API&lt;/STRONG&gt;, rather than being surfaced directly as alertable Azure Monitor metrics.&lt;/P&gt;
&lt;P&gt;As a result:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Errors can go unnoticed until synchronization stops completely.&lt;/LI&gt;
&lt;LI&gt;Warning events may be ignored because they're not visible in operational dashboards.&lt;/LI&gt;
&lt;LI&gt;Support teams often learn about issues only after a downstream application reports stale or inconsistent data.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;What's particularly concerning is that:&lt;/P&gt;
&lt;P&gt;Row-level warnings frequently appear well before a hard synchronization failure.&lt;/P&gt;
&lt;P&gt;These warning entries can serve as valuable leading indicators, giving operations teams time to investigate before users experience impact.&lt;/P&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;Building a Proactive Monitoring Approach&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;To address this challenge, I built a lightweight monitoring workflow using Azure Logic Apps.&lt;/P&gt;
&lt;P&gt;Instead of waiting for failures to be reported, the Logic App proactively queries the Sync Group Logs API every 15 minutes and looks for:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Error events&lt;/LI&gt;
&lt;LI&gt;Warning events&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;If any matching entries are found, an email notification is automatically sent to the designated operational contact or on-call distribution list.&lt;/P&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;High-Level Architecture&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;This design is:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&amp;nbsp;Serverless&lt;/LI&gt;
&lt;LI&gt;&amp;nbsp;Low cost&lt;/LI&gt;
&lt;LI&gt;&amp;nbsp;Simple to deploy&lt;/LI&gt;
&lt;LI&gt;&amp;nbsp;Secure through Managed Identity&lt;/LI&gt;
&lt;LI&gt;&amp;nbsp;Easily customizable&lt;BR /&gt;&lt;U style="color: rgb(30, 30, 30); font-size: 28px;"&gt;&lt;STRONG&gt;&lt;BR /&gt;Solution Architecture Walkthrough&lt;BR /&gt;&lt;/STRONG&gt;&lt;/U&gt;&lt;U style="color: rgb(30, 30, 30); font-size: 28px;"&gt;&lt;STRONG&gt;&lt;STRONG style="color: rgb(30, 30, 30); font-size: 16px;"&gt;Step 1: Scheduled Monitoring&amp;nbsp;&lt;/STRONG&gt;&lt;/STRONG&gt;&lt;/U&gt;
&lt;P&gt;The Logic App runs every 15 minutes.To avoid missing events that occur between execution intervals, the solution queries the previous 20 minutes of logs.&lt;/P&gt;
&lt;P&gt;Why a 20-Minute Lookback on a 15-Minute Schedule?&lt;/P&gt;
&lt;LI-CODE lang=""&gt;Every 15 Minutes ▼ Query Last 20 Minutes ▼ 5-Minute Overlap ▼&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P data-line-index="9"&gt;&lt;STRONG style="color: rgb(30, 30, 30);"&gt;&lt;U&gt;Key Benefit:&lt;/U&gt;&lt;/STRONG&gt;&lt;/P&gt;
The 20-minute lookback intentionally overlaps the 15-minute execution schedule, ensuring that warning and error events are not missed due to execution delays, transient platform issues, or timing gaps between runs. This design improves monitoring reliability and early issue detection&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;Step 2: Managed Identity Authentication&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;Security was a key design principle.&lt;/P&gt;
&lt;P&gt;Rather than storing credentials, secrets, or service principal passwords, the Logic App uses a system-assigned managed identity.&lt;/P&gt;
&lt;P&gt;The managed identity is granted the SQL DB Contributor role on the Hub SQL Server.&lt;/P&gt;
&lt;P&gt;Benefits include:&lt;/P&gt;
&lt;UL&gt;
&lt;LI style="list-style-type: none;"&gt;
&lt;UL&gt;
&lt;LI&gt;No credential management&lt;/LI&gt;
&lt;LI&gt;Automatic token acquisition&lt;/LI&gt;
&lt;LI&gt;Improved security posture&lt;/LI&gt;
&lt;LI&gt;Native Azure RBAC integration&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;Step 3: Querying the Sync Group Logs API&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;The Logic App executes an HTTP GET request against the SQL Data Sync Logs endpoint.&lt;/P&gt;
&lt;P&gt;Example endpoint:&lt;/P&gt;
&lt;P&gt;&lt;A href="https://management.azure.com/subscriptions/%3csubscriptionId%3e/resourceGroups/%3cresourceGroup%3e/providers/Microsoft.Sql/servers/%3cHubServer%3e/databases/%3cHubDatabase%3e/syncGroups/%3cSyncGroup%3e/logs?api-version=2020-11-01-preview&amp;amp;startTime=%3cstartUtc%3e&amp;amp;endTime=%3cendUtc%3e&amp;amp;type=All" target="_blank" rel="noopener"&gt;https://management.azure.com/subscriptions/&amp;lt;subscriptionId&amp;gt;/resourceGroups/&amp;lt;resourceGroup&amp;gt;/providers/Microsoft.Sql/servers/&amp;lt;HubServer&amp;gt;/databases/&amp;lt;HubDatabase&amp;gt;/syncGroups/&amp;lt;SyncGroup&amp;gt;/logs?api-version=2020-11-01-preview&amp;amp;startTime=&amp;lt;startUtc&amp;gt;&amp;amp;endTime=&amp;lt;endUtc&amp;gt;&amp;amp;type=All&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;The request is authenticated using Managed Identity against the Azure Resource Manager endpoint.&lt;/P&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;Step 4: Parsing the Response&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;The API returns log entries as a JSON array.&lt;/P&gt;
&lt;P&gt;Important fields include:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="width: 37.1296%; border-width: 1px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;Field&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;Description&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;timestamp&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;When the event occurred&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;type&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Error, Warning, or informational event&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;source&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Component generating the event&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;details&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Detailed event information&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;tracingId&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Correlation identifier&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;operationStatus&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Result of the operation&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 25.2116%" /&gt;&lt;col style="width: 74.7279%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;After parsing the response, each field becomes available for workflow processing.&lt;/P&gt;
&lt;P&gt;&lt;STRONG style="color: rgb(30, 30, 30);"&gt;&lt;U&gt;Step 5: Filter and Prioritize Actionable Events&lt;/U&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Not every log entry warrants operational attention. To reduce noise and focus on meaningful health signals, the workflow filters events to retain only those classified as &lt;STRONG&gt;Error&lt;/STRONG&gt; or &lt;STRONG&gt;Warning&lt;/STRONG&gt;.&lt;/P&gt;
&lt;P&gt;A &lt;STRONG&gt;Filter Array&lt;/STRONG&gt; action is used to evaluate each log entry and keep only actionable events:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;By filtering out informational and non-critical messages, operations teams can focus on events that may indicate synchronization issues, service degradation, or potential failures requiring investigation.&lt;/P&gt;
&lt;LI-CODE lang=""&gt;&lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="380815" data-lia-user-login="Or" class="lia-mention lia-mention-user"&gt;Or&lt;/a&gt;( equals(item()?['type'], 'Error'), equals(item()?['type'], 'Warning') )&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;By filtering out informational and non-critical messages, operations teams can focus on events that may indicate synchronization issues, service degradation, or potential failures requiring investigation.&lt;/P&gt;
&lt;P data-olk-copy-source="MailCompose"&gt;&lt;U&gt;&lt;STRONG&gt;Step 6: Notify Operations Teams&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;After filtering, the workflow determines whether any actionable events were identified.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Condition:&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang=""&gt;length(body('Filter_array')) &amp;gt; 0&lt;/LI-CODE&gt;
&lt;P&gt;When one or more warning or error events are detected, the Logic App automatically generates an email notification to the designated operations or support team.&lt;/P&gt;
&lt;P&gt;The alert includes key diagnostic information such as:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Event details&lt;/LI&gt;
&lt;LI&gt;Warning or error classification&lt;/LI&gt;
&lt;LI&gt;Tracing ID&lt;/LI&gt;
&lt;LI&gt;Timestamp information&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;Example Subject&lt;BR /&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang=""&gt;SQL Data Sync Alert: MySyncGroup - 3 Warning/Error Events Detected&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;Example Notification&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang=""&gt;Type: Warning Timestamp: 2026-07-28T12:15:00Z Details: Failed row synchronization detected Tracing ID: abc123xyz&lt;/LI-CODE&gt;
&lt;P&gt;Providing this information upfront enables engineers to begin troubleshooting immediately, reducing the time required to identify and remediate synchronization issues.&lt;/P&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;Why This Matters&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;In many organizations, data synchronization is a business-critical workload. Consider scenarios such as:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Application databases synchronizing across multiple regions&lt;/LI&gt;
&lt;LI&gt;Distributed retail platforms&lt;/LI&gt;
&lt;LI&gt;Financial transaction processing systems&lt;/LI&gt;
&lt;LI&gt;Operational reporting environments&lt;/LI&gt;
&lt;LI&gt;Legacy SQL Data Sync deployments supporting core business processes&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;In these environments, early detection can significantly reduce operational risk. The difference between investigating a warning today and responding to a synchronization failure tomorrow may be the difference between a routine operational activity and a customer-impacting incident.&lt;/P&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;Key Benefits of the Solution&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Improved Visibility:&lt;BR /&gt;&lt;/STRONG&gt;Surfaces health signals that would otherwise remain hidden within SQL Data Sync logs, providing operations teams with greater insight into synchronization activity.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Early Detection&lt;/STRONG&gt;&lt;BR /&gt;Identifies warning conditions and row-level synchronization issues before they escalate into service-impacting failures.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Security-First Architecture&lt;/STRONG&gt;&lt;BR /&gt;Leverages Managed Identity for authentication, eliminating the need to store or manage credentials.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Cost Efficiency &lt;BR /&gt;&lt;/STRONG&gt;Built on the Azure Logic Apps Consumption Plan, keeping operational costs low while delivering continuous monitoring capabilities.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Operational Simplicity&amp;nbsp; &amp;nbsp;&lt;BR /&gt;&lt;/STRONG&gt;Requires no custom services, virtual machines, or ongoing infrastructure maintenance, making deployment and management straightforward.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;U&gt;Recommendations and Operational Best Practices&lt;/U&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Based on testing and operational experience, consider the following best practices:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;&lt;STRONG&gt; Treat Warnings as Actionable Signals&lt;/STRONG&gt;&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;Many organizations focus exclusively on errors; however, warnings often provide the earliest indication of synchronization degradation. Investigating warning events proactively can help prevent future outages and service disruptions.&lt;/P&gt;
&lt;OL start="2"&gt;
&lt;LI&gt;&lt;STRONG&gt; Maintain an Up-to-Date On-Call Distribution List&lt;/STRONG&gt;&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;Ensure alerts are delivered to the appropriate responders. An outdated distribution list can be just as ineffective as having no alerting mechanism in place.&lt;/P&gt;
&lt;OL start="3"&gt;
&lt;LI&gt;&lt;STRONG&gt; Use Overlapping Monitoring Windows&lt;/STRONG&gt;&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;Always query a slightly larger time interval than the Logic App execution schedule. This approach helps prevent missed events caused by execution delays, scheduling drift, or transient service interruptions.&lt;/P&gt;
&lt;OL start="4"&gt;
&lt;LI&gt;&lt;STRONG&gt; Review Trends Regularly&lt;/STRONG&gt;&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;Even when warnings do not immediately impact synchronization, recurring patterns may reveal underlying issues such as:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Connectivity challenges&lt;/LI&gt;
&lt;LI&gt;Schema inconsistencies&lt;/LI&gt;
&lt;LI&gt;Permission and access problems&lt;/LI&gt;
&lt;LI&gt;Data quality concerns&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Periodic trend analysis can help teams identify and address systemic issues before they affect business operations.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;U&gt;Conclusion&lt;/U&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Although Azure SQL Data Sync does not currently expose native Azure Monitor metrics for synchronization health, organizations are not limited to reactive monitoring approaches.&lt;/P&gt;
&lt;P&gt;By combining:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Azure Logic Apps&lt;/LI&gt;
&lt;LI&gt;Managed Identity&lt;/LI&gt;
&lt;LI&gt;SQL Data Sync Logs API&lt;/LI&gt;
&lt;LI&gt;Automated Email Notifications&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;organizations can implement a lightweight yet powerful monitoring solution that surfaces hidden warning and error signals before they evolve into production incidents.&lt;/P&gt;
&lt;P&gt;In modern cloud operations, resilience is not defined solely by how quickly teams respond to failures. It is equally determined by their ability to identify weak signals early and take proactive action before customers are impacted.&lt;/P&gt;
&lt;P&gt;This Logic App-based approach demonstrates how a relatively small amount of automation can significantly improve operational visibility, accelerate issue detection, and reduce the Mean Time to Detect (MTTD) for Azure SQL Data Sync environments.&lt;/P&gt;
&lt;div data-video-id="https://www.youtube.com/watch?v=gGvBHrWGXMs/1786761072767" data-video-remote-vid="https://www.youtube.com/watch?v=gGvBHrWGXMs/1786761072767" class="lia-video-container lia-media-is-center lia-media-size-large"&gt;&lt;iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FgGvBHrWGXMs%3Ffeature%3Doembed&amp;amp;display_name=YouTube&amp;amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DgGvBHrWGXMs&amp;amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FgGvBHrWGXMs%2Fhqdefault.jpg&amp;amp;type=text%2Fhtml&amp;amp;schema=youtube" allowfullscreen="" style="max-width: 100%"&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 18 Aug 2026 17:02:30 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/closing-the-sql-data-sync-monitoring-gap-building-proactive/ba-p/4542212</guid>
      <dc:creator>Karunakar_Kotha</dc:creator>
      <dc:date>2026-08-18T17:02:30Z</dc:date>
    </item>
    <item>
      <title>Lessons Learned #551: Azure SQL Connection Timeouts: Three Things to Check</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-551-azure-sql-connection-timeouts-three-things/ba-p/4544401</link>
      <description>&lt;P&gt;An application starts reporting intermittent timeouts when connecting to Azure SQL Database. Some requests succeed, others fail, and a test from a developer’s laptop works perfectly. The database appears online, no recent deployment seems related, and the natural reaction is to ask:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;Is Azure SQL unavailable?&lt;/LI&gt;
&lt;LI&gt;Is the firewall blocking the connection?&lt;/LI&gt;
&lt;LI&gt;Should we increase the connection timeout?&lt;/LI&gt;
&lt;LI&gt;Should we change the driver or scale the database?&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Those are reasonable questions, but they may lead the investigation in the wrong direction. The most important lesson is simple: &lt;STRONG&gt;A timeout tells us how long the application waited. It does not tell us what the application was waiting for.&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;Not every “SQL timeout” happens inside Azure SQL&lt;/H2&gt;
&lt;P&gt;From the application’s point of view, opening a database connection may involve several operations:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;Resolving the server name.&lt;/LI&gt;
&lt;LI&gt;Reaching the SQL endpoint.&lt;/LI&gt;
&lt;LI&gt;Obtaining a Microsoft Entra access token.&lt;/LI&gt;
&lt;LI&gt;Waiting for an available pooled connection.&lt;/LI&gt;
&lt;LI&gt;Completing the SQL login.&lt;/LI&gt;
&lt;LI&gt;Executing the first command.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;When all these operations are reported through the same application method or log entry, it can look as though Azure SQL took thirty seconds to accept the connection. &lt;STRONG&gt;In reality, only part of that time may have been spent connecting to the database.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;In one anonymized support scenario, the application experienced problems mainly on its first connection. Network tests were successful and no corresponding SQL connection failure was identified. The investigation eventually showed that &lt;STRONG&gt;access-token acquisition was consuming&lt;/STRONG&gt; a significant part of the available time. &lt;STRONG&gt;Increasing the SQL timeout or changing the firewall would not have addressed the real delay.&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;Check 1: Capture the complete error and the exact time&lt;/H2&gt;
&lt;P&gt;A screenshot containing only “Connection Timeout Expired” is rarely enough.&lt;/P&gt;
&lt;P&gt;Capture:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;The &lt;STRONG&gt;complete exception and inner exception.&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;The &lt;STRONG&gt;operation being performed&lt;/STRONG&gt;.&lt;/LI&gt;
&lt;LI&gt;The &lt;STRONG&gt;driver &lt;/STRONG&gt;and &lt;STRONG&gt;version&lt;/STRONG&gt;.&lt;/LI&gt;
&lt;LI&gt;The &lt;STRONG&gt;authentication &lt;/STRONG&gt;method.&lt;/LI&gt;
&lt;LI&gt;The &lt;STRONG&gt;exact timestamp in UTC&lt;/STRONG&gt;.&lt;/LI&gt;
&lt;LI&gt;Whether the &lt;STRONG&gt;issue affects every connection or only some of them&lt;/STRONG&gt;.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The wording around the timeout matters. For example, a timeout while obtaining a connection from the pool points toward the application’s pooling and concurrency behavior. A pre-login or TLS error belongs to a different investigation. A command timeout after the connection was established is usually a query-performance problem rather than a connection problem.&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&amp;nbsp;&lt;/DIV&gt;
&lt;H2&gt;Check 2: Measure the application timeline&lt;/H2&gt;
&lt;P&gt;The application should record important operations separately. A simple timeline can completely change the investigation:&lt;/P&gt;
&lt;P&gt;10:14:20.100 Token acquisition started&lt;/P&gt;
&lt;P&gt;10:14:28.400 Token acquired&lt;/P&gt;
&lt;P&gt;10:14:28.405 SQL connection started&lt;/P&gt;
&lt;P&gt;10:14:29.050 SQL connection established&lt;/P&gt;
&lt;P&gt;The complete operation took almost nine seconds, but Azure SQL connection establishment took less than one second.&lt;/P&gt;
&lt;P&gt;Useful measurements include:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;Token-acquisition duration.&lt;/LI&gt;
&lt;LI&gt;Time waiting for a pooled connection.&lt;/LI&gt;
&lt;LI&gt;SQL connection-open duration.&lt;/LI&gt;
&lt;LI&gt;SQL command duration.&lt;/LI&gt;
&lt;LI&gt;Number of retry attempts.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Applications using Microsoft Entra authentication must obtain an access token before authenticating to Azure SQL. Measuring that operation separately helps distinguish an identity delay from a database connectivity problem.&lt;/P&gt;
&lt;P&gt;This is particularly useful when the issue appears:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;On the first connection after startup.&lt;/LI&gt;
&lt;LI&gt;After a token expires.&lt;/LI&gt;
&lt;LI&gt;Only with Managed Identity or Workload Identity.&lt;/LI&gt;
&lt;LI&gt;Intermittently, while SQL authentication connections remain unaffected.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Check 3: Test from the application environment&lt;/H2&gt;
&lt;P&gt;A successful connection from a laptop does not validate the path used by an application running in:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;Azure App Service.&lt;/LI&gt;
&lt;LI&gt;Azure Functions.&lt;/LI&gt;
&lt;LI&gt;Azure Kubernetes Service.&lt;/LI&gt;
&lt;LI&gt;A virtual machine.&lt;/LI&gt;
&lt;LI&gt;An on-premises application server.&lt;/LI&gt;
&lt;LI&gt;A container or integration runtime.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The laptop and the application may use different DNS servers, routes, firewalls, proxies and identities. Connectivity and DNS tests should therefore be performed from the environment that is actually failing.&lt;/P&gt;
&lt;P&gt;This becomes especially important when Private Endpoint is used. The application should continue connecting with: &amp;lt;server&amp;gt;.database.windows.net&lt;/P&gt;
&lt;P&gt;It should not use the Private Endpoint IP address or the privatelink.database.windows.net hostname directly. Direct login attempts using the private IP or the private-link FQDN fail; the normal logical-server FQDN must remain in the connection string.&lt;/P&gt;
&lt;P&gt;From the affected environment, confirm that:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;The expected DNS server answers the request.&lt;/LI&gt;
&lt;LI&gt;The server FQDN resolves to the expected private IP.&lt;/LI&gt;
&lt;LI&gt;The Private Endpoint connection is approved.&lt;/LI&gt;
&lt;LI&gt;The Private DNS zone is linked correctly.&lt;/LI&gt;
&lt;LI&gt;The resolved address is reachable through the intended route.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;A test from an unrelated machine is still useful for comparison, but it does not prove that the application path is healthy.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table style="width: 871px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;STRONG&gt;Observed symptom&lt;/STRONG&gt;&lt;/td&gt;&lt;td&gt;&lt;STRONG&gt;Likely investigation area&lt;/STRONG&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Timeout while obtaining a connection from the pool&lt;/td&gt;&lt;td&gt;Application connection pooling&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Server name cannot be resolved&lt;/td&gt;&lt;td&gt;DNS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;TCP connection to the endpoint cannot be established&lt;/td&gt;&lt;td&gt;Network path, firewall or routing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Error during the pre-login handshake&lt;/td&gt;&lt;td&gt;TLS, driver, network interruption or pre-login processing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Authentication or access-token error&lt;/td&gt;&lt;td&gt;Microsoft Entra authentication, identity or token acquisition&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Timeout during the post-login phase&lt;/td&gt;&lt;td&gt;Login completion, session initialization or server-side processing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Execution or command timeout after connecting&lt;/td&gt;&lt;td&gt;Query execution and database performance&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;H2&gt;Avoid changing several things at once&lt;/H2&gt;
&lt;P&gt;During a production incident, it is tempting to:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;Increase the timeout.&lt;/LI&gt;
&lt;LI&gt;Add firewall rules.&lt;/LI&gt;
&lt;LI&gt;Change the connection policy.&lt;/LI&gt;
&lt;LI&gt;Upgrade the driver.&lt;/LI&gt;
&lt;LI&gt;Restart the application.&lt;/LI&gt;
&lt;LI&gt;Clear connection pools.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Applying several changes together makes it difficult to determine which one helped, and some may only hide the symptom.&lt;/P&gt;
&lt;P&gt;A better approach is to define one hypothesis: We believe DNS in the application environment is resolving the public endpoint instead of the Private Endpoint.&lt;/P&gt;
&lt;P&gt;Then define:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;The evidence supporting the hypothesis.&lt;/LI&gt;
&lt;LI&gt;One controlled change.&lt;/LI&gt;
&lt;LI&gt;The expected result.&lt;/LI&gt;
&lt;LI&gt;How the result will be measured.&lt;/LI&gt;
&lt;LI&gt;How the change will be reverted.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Azure SQL supports Proxy and Redirect connection policies, which determine how traffic flows after reaching the Azure SQL gateway. The policy is configured for the logical server, so it should be verified before making firewall assumptions or changes.&lt;/P&gt;
&lt;H2&gt;What should we collect before opening a support request?&lt;/H2&gt;
&lt;P&gt;A small but precise evidence package can avoid several rounds of questions:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;Complete error and inner exception.&lt;/LI&gt;
&lt;LI&gt;Exact UTC timestamps.&lt;/LI&gt;
&lt;LI&gt;Application platform and location.&lt;/LI&gt;
&lt;LI&gt;Public or Private Endpoint.&lt;/LI&gt;
&lt;LI&gt;Server FQDN used by the application.&lt;/LI&gt;
&lt;LI&gt;Driver and version.&lt;/LI&gt;
&lt;LI&gt;Authentication method.&lt;/LI&gt;
&lt;LI&gt;Token, pool, connection and command durations.&lt;/LI&gt;
&lt;LI&gt;DNS result from the affected environment.&lt;/LI&gt;
&lt;LI&gt;Whether the issue is constant, intermittent or limited to the first connection.&lt;/LI&gt;
&lt;LI&gt;Recent application, network, identity or configuration changes.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Wed, 05 Aug 2026 18:21:05 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-551-azure-sql-connection-timeouts-three-things/ba-p/4544401</guid>
      <dc:creator>Jose_Manuel_Jurado</dc:creator>
      <dc:date>2026-08-05T18:21:05Z</dc:date>
    </item>
    <item>
      <title>Understanding Azure SQL Long-Term Retention Immutability Configuration with Terraform and AzAPI</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/understanding-azure-sql-long-term-retention-immutability/ba-p/4543091</link>
      <description>&lt;H2&gt;Executive Summary&lt;/H2&gt;
&lt;P&gt;Organizations using Azure SQL Database Long-Term Retention (LTR) backup policies may encounter failures when attempting to disable backup immutability while also specifying an immutability mode in the same request. In the investigated scenario, the customer observed that the operation succeeded when performed through ARM templates but failed when executed through Terraform using AzAPI-based resources.&lt;/P&gt;
&lt;P&gt;The investigation determined that the Azure SQL resource provider enforces validation rules that prevent TimeBasedImmutabilityMode from being specified when TimeBasedImmutability is set to Disabled. The issue was not caused by the Azure SQL service itself, but rather by how configuration values were being submitted through Terraform and AzAPI resource updates.&lt;/P&gt;
&lt;P&gt;The recommended mitigation is to ensure that immutability mode is omitted or explicitly set to null when disabling time-based immutability. This allows the request to comply with the resource provider's validation requirements.&lt;/P&gt;
&lt;H1&gt;Introduction&lt;/H1&gt;
&lt;P&gt;Azure SQL Database supports immutable Long-Term Retention (LTR) backups to help organizations meet compliance, governance, and data protection requirements. These policies allow administrators to control whether retained backups can be modified or deleted.&lt;/P&gt;
&lt;P&gt;During an investigation involving Terraform and AzAPI deployments, a customer reported inconsistent behavior when attempting to disable backup immutability. While equivalent ARM template operations completed successfully, Terraform-based deployments generated validation errors.&lt;/P&gt;
&lt;P&gt;This article explains the observed behavior, the investigation findings, the confirmed root cause, and the recommended mitigation.&lt;/P&gt;
&lt;H1&gt;Issue Description&lt;/H1&gt;
&lt;H2&gt;Reported Symptoms&lt;/H2&gt;
&lt;P&gt;The customer reported the following behavior:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Enabling and managing Long-Term Retention backup immutability worked successfully.&lt;/LI&gt;
&lt;LI&gt;Configurations involving immutability mode settings could be applied successfully under certain conditions.&lt;/LI&gt;
&lt;LI&gt;Attempts to disable immutability through Terraform resulted in failures.&lt;/LI&gt;
&lt;LI&gt;Similar operations appeared to succeed when performed using ARM templates.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Technical Environment&lt;/H2&gt;
&lt;P&gt;The discussion confirmed the following components:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Azure SQL Database Long-Term Retention (LTR) backup policies&lt;/LI&gt;
&lt;LI&gt;Backup immutability configuration&lt;/LI&gt;
&lt;LI&gt;Terraform deployments&lt;/LI&gt;
&lt;LI&gt;AzAPI resources and AzAPI resource updates&lt;/LI&gt;
&lt;LI&gt;ARM template deployments&lt;/LI&gt;
&lt;LI&gt;Azure SQL Resource Provider validation logic&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Expected Behavior&lt;/H2&gt;
&lt;P&gt;When administrators disable backup immutability, the configuration update should be accepted and the policy should transition to a disabled state.&lt;/P&gt;
&lt;H2&gt;Actual Behavior&lt;/H2&gt;
&lt;P&gt;Requests submitted through Terraform/AzAPI included both:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;TimeBasedImmutability = Disabled&lt;/LI&gt;
&lt;LI&gt;TimeBasedImmutabilityMode = Unlocked&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The Azure SQL resource provider rejected this configuration, returning an error indicating that an immutability policy mode cannot be specified when backup immutability is not enabled.&lt;/P&gt;
&lt;H1&gt;Investigation and Troubleshooting&lt;/H1&gt;
&lt;H2&gt;1. Initial Customer Question&lt;/H2&gt;
&lt;P&gt;The customer sought clarification on whether enabling, disabling, locking, and unlocking backup immutability should all be possible through AzAPI resources and whether a product issue existed.&lt;/P&gt;
&lt;H2&gt;2. Review of Azure SQL Resource Provider Behavior&lt;/H2&gt;
&lt;P&gt;The support team reviewed requests submitted to the Azure SQL resource provider and compared successful and unsuccessful operations. The investigation focused on configuration differences between ARM template deployments and Terraform-driven updates.&lt;/P&gt;
&lt;H3&gt;Confirmed Finding&lt;/H3&gt;
&lt;P&gt;When ARM templates disabled immutability, the request contained:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;TimeBasedImmutability = Disabled&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;and did &lt;STRONG&gt;not&lt;/STRONG&gt; include an immutability mode parameter.&lt;/P&gt;
&lt;H3&gt;Confirmed Finding&lt;/H3&gt;
&lt;P&gt;When Terraform attempted to disable immutability, the request included both:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;TimeBasedImmutability = Disabled&lt;/LI&gt;
&lt;LI&gt;TimeBasedImmutabilityMode = Unlocked&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This resulted in a validation failure from the Azure SQL resource provider.&lt;/P&gt;
&lt;H2&gt;3. Validation of Error Behavior&lt;/H2&gt;
&lt;P&gt;The team verified that the error was generated by the Azure SQL resource provider and was reproducible outside Terraform, including equivalent testing through ARM deployments when the conflicting parameter combination was supplied.&lt;/P&gt;
&lt;H3&gt;Confirmed Error&lt;/H3&gt;
&lt;P&gt;The resource provider returned an error equivalent to:&lt;/P&gt;
&lt;P&gt;Cannot set immutability policy mode when backup immutability is not enabled.&lt;/P&gt;
&lt;H2&gt;4. Assessment of Terraform and AzAPI Behavior&lt;/H2&gt;
&lt;P&gt;The investigation identified an important behavioral difference.&lt;/P&gt;
&lt;P&gt;Terraform itself did not yet expose dedicated Time-Based Immutability parameters in its SQL modules. As a result, the customer was using AzAPI resources to perform direct REST-based operations.&lt;/P&gt;
&lt;P&gt;The team discovered that:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;azapi_resource behaved as expected.&lt;/LI&gt;
&lt;LI&gt;azapi_resource_update could retrieve and reuse an existing property value when no value was explicitly provided.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This behavior caused the immutability mode value to persist unexpectedly during updates.&lt;/P&gt;
&lt;H2&gt;5. Reproduction and Verification&lt;/H2&gt;
&lt;P&gt;The engineering discussion included review and validation of the reported behavior. Testing confirmed that requests containing immutability mode while immutability was disabled were expected to fail due to platform validation.&lt;/P&gt;
&lt;H1&gt;Root Cause&lt;/H1&gt;
&lt;H2&gt;Confirmed Root Cause&lt;/H2&gt;
&lt;P&gt;The failure occurred because the update request attempted to disable backup immutability while simultaneously providing a value for TimeBasedImmutabilityMode.&lt;/P&gt;
&lt;P&gt;Azure SQL validation rules require immutability mode to be associated only with an enabled immutability configuration. When immutability is disabled, an immutability mode must not be supplied.&lt;/P&gt;
&lt;P&gt;An additional contributing factor was the behavior of azapi_resource_update, which could retain a previously configured immutability mode value when no new value was explicitly provided. Consequently, requests unintentionally included an immutability mode even though the intent was to disable immutability entirely.&lt;/P&gt;
&lt;P&gt;The available evidence supports this conclusion through:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Comparison of successful ARM template requests and failing Terraform requests.&lt;/LI&gt;
&lt;LI&gt;Reproduction of the same validation behavior by the Azure SQL resource provider.&lt;/LI&gt;
&lt;LI&gt;Validation of the AzAPI update behavior involving retained values.&lt;/LI&gt;
&lt;/OL&gt;
&lt;H1&gt;Mitigation and Resolution&lt;/H1&gt;
&lt;H2&gt;Recommended Mitigation&lt;/H2&gt;
&lt;P&gt;When disabling backup immutability:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Set TimeBasedImmutability to Disabled.&lt;/LI&gt;
&lt;LI&gt;Do not provide TimeBasedImmutabilityMode.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Terraform/AzAPI Workaround&lt;/H2&gt;
&lt;P&gt;The investigation determined that explicitly setting:&lt;/P&gt;
&lt;P&gt;TimeBasedImmutabilityMode = null&lt;/P&gt;
&lt;P&gt;prevents the previous value from being reused and allows the request to be processed correctly.&lt;/P&gt;
&lt;H2&gt;Configuration Matrix Discussed&lt;/H2&gt;
&lt;P&gt;The support team identified the following expected behavior:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Operation&lt;/th&gt;&lt;th&gt;Immutability Mode Requirement&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Locking backups&lt;/td&gt;&lt;td&gt;Mode should be set to Locked&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Unlocking while remaining enabled&lt;/td&gt;&lt;td&gt;Mode may be supplied and is recommended for clarity&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Disabling immutability&lt;/td&gt;&lt;td&gt;Mode should not be supplied&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;This guidance was explicitly discussed during the investigation.&lt;/P&gt;
&lt;H2&gt;Validation&lt;/H2&gt;
&lt;P&gt;After applying the mitigation:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The disable operation should complete without the immutability mode conflict.&lt;/LI&gt;
&lt;LI&gt;Requests should no longer trigger the Azure SQL validation error related to immutability mode usage.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;Recommendations and Best Practices&lt;/H1&gt;
&lt;H2&gt;Recommendations Supported by the Investigation&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;Ensure that immutability mode is not included when disabling backup immutability.&lt;/LI&gt;
&lt;LI&gt;Review Terraform templates for dynamically generated properties that may continue to emit previously populated values.&lt;/LI&gt;
&lt;LI&gt;When using AzAPI update resources, explicitly manage nullable properties where supported to avoid unintended value persistence.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1&gt;Important Considerations&lt;/H1&gt;
&lt;P&gt;Behavior may vary depending on:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Azure SQL API version&lt;/LI&gt;
&lt;LI&gt;Terraform provider version&lt;/LI&gt;
&lt;LI&gt;AzAPI provider implementation details&lt;/LI&gt;
&lt;LI&gt;Existing Long-Term Retention backup state&lt;/LI&gt;
&lt;LI&gt;Whether previously locked backups exist&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Always validate deployment behavior in a non-production environment before applying configuration changes broadly.&lt;/P&gt;
&lt;H1&gt;Conclusion&lt;/H1&gt;
&lt;P&gt;This investigation demonstrated that the inability to disable Azure SQL Long-Term Retention backup immutability was not caused by a platform defect in Azure SQL. Instead, the failure occurred because requests attempted to specify an immutability mode while immutability itself was being disabled.&lt;/P&gt;
&lt;P&gt;The issue was further influenced by AzAPI update behavior that could preserve previously configured values unless explicitly cleared. Setting the immutability mode to null, or removing it entirely when disabling immutability, resolved the problem.&lt;/P&gt;
&lt;P&gt;The key technical takeaway is that TimeBasedImmutabilityMode and TimeBasedImmutability must be configured consistently with Azure SQL resource provider validation rules, particularly during infrastructure-as-code deployments.&lt;/P&gt;
&lt;H2&gt;Public Documentation&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/azure-sql/database/long-term-retention-overview?view=azuresql" target="_blank"&gt;Azure SQL Database Long-Term Retention documentation&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/azure-sql/database/backup-immutability?view=azuresql" target="_blank"&gt;Azure SQL Backup Immutability documentation&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/templates/microsoft.sql/servers/databases/backuplongtermretentionpolicies?pivots=deployment-language-bicep" target="_blank"&gt;ARM/Bicep resource documentation for backup Long-Term Retention policies&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/azure-sql/database/single-database-create-terraform-quickstart?view=azuresql&amp;amp;tabs=azure-cli" target="_blank"&gt;Terraform provider documentation for Azure SQL Database&lt;/A&gt;&lt;/LI&gt;
&lt;/UL&gt;</description>
      <pubDate>Sat, 01 Aug 2026 17:35:42 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/understanding-azure-sql-long-term-retention-immutability/ba-p/4543091</guid>
      <dc:creator>Mohamed_Baioumy_MSFT</dc:creator>
      <dc:date>2026-08-01T17:35:42Z</dc:date>
    </item>
    <item>
      <title>Troubleshooting SQL Authentication Failures on Azure SQL Database Geo-Secondary Replicas</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/troubleshooting-sql-authentication-failures-on-azure-sql/ba-p/4542414</link>
      <description>&lt;H1&gt;Scenario Background&lt;/H1&gt;
&lt;P&gt;During a recent investigation, a customer reported that SQL Authentication was failing when connecting to an Azure SQL Database geo-secondary replica.&lt;/P&gt;
&lt;P&gt;The customer confirmed the following:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;SQL Authentication worked successfully against the primary replica.&lt;/LI&gt;
&lt;LI&gt;The geo-secondary database was healthy and synchronized.&lt;/LI&gt;
&lt;LI&gt;The replicated database user existed in the secondary database.&lt;/LI&gt;
&lt;LI&gt;Microsoft Entra authentication continued to work successfully.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Because the database was available and replication health was normal, the initial assumption was that the issue might be related to replication or database accessibility.&lt;/P&gt;
&lt;P&gt;After reviewing the security configuration, the issue was identified as a mismatch in SQL login configuration between the primary and secondary logical servers.&lt;/P&gt;
&lt;P&gt;This scenario highlights an important security consideration when designing Azure SQL Database disaster recovery solutions:&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Active Geo-Replication replicates user databases, but server-level objects such as SQL logins stored in the master database are not automatically replicated.&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;Common Symptoms&lt;/H2&gt;
&lt;P&gt;You may encounter one or more of the following symptoms:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;SQL Authentication succeeds against the primary replica but fails against the geo-secondary.&lt;/LI&gt;
&lt;LI&gt;Microsoft Entra authentication continues to work successfully.&lt;/LI&gt;
&lt;LI&gt;Replication health appears normal and synchronized.&lt;/LI&gt;
&lt;LI&gt;The database user exists in the secondary database.&lt;/LI&gt;
&lt;LI&gt;Connection attempts return errors such as:&lt;BR /&gt;
&lt;P data-line-index="0"&gt;&lt;STRONG&gt;&lt;SPAN class="lia-text-color-8"&gt;Login failed for user '&amp;lt;login_name&amp;gt;'.&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P data-line-index="1"&gt;&lt;STRONG&gt;&lt;SPAN class="lia-text-color-8"&gt;Invalid username or password&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;These symptoms can occur even when the secondary database has successfully replicated and is fully accessible through other authentication methods&lt;/P&gt;
&lt;H2&gt;Understanding the Architecture&lt;/H2&gt;
&lt;P&gt;Azure SQL Active Geo-Replication replicates &lt;STRONG&gt;user databases&lt;/STRONG&gt; to the secondary region, but it does &lt;STRONG&gt;not&lt;/STRONG&gt; automatically replicate server-level objects stored in the logical server's master database, including SQL logins.&amp;nbsp;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/active-geo-replication-security-configure?view=azuresql" target="_blank" rel="noopener"&gt;Configure Security for Disaster Recovery - Azure SQL Database | Microsoft Learn&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Because SQL Authentication depends on a server-level login, a replicated database user alone is not sufficient. The corresponding login must also exist on the secondary logical server and be correctly mapped. &lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/active-geo-replication-configure-portal?view=azuresql&amp;amp;tabs=portal" target="_blank" rel="noopener"&gt;Tutorial: Geo-replication &amp;amp; failover in portal - Azure SQL Database | Microsoft Learn&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;For SQL Authentication to work successfully:&lt;/P&gt;
&lt;OL data-spread="false"&gt;
&lt;LI&gt;The SQL login must exist on the target logical server.&lt;/LI&gt;
&lt;LI&gt;The login SID must match the SID of the corresponding login on the primary server.&lt;/LI&gt;
&lt;LI&gt;The database user SID must correctly map to the SQL login SID.&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;The relationship can be summarized as:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;img&gt;
&lt;P&gt;&lt;STRONG&gt;Figure 1. SQL Login and Database User Synchronization Behavior in Azure SQL Geo-Replication&lt;/STRONG&gt;&lt;/P&gt;
&lt;/img&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Even though the database user is replicated successfully, SQL Authentication can fail because login validation occurs before database access is granted&lt;/P&gt;
&lt;H2&gt;Understanding the Root Cause&lt;/H2&gt;
&lt;P&gt;When SQL Authentication is used, Azure SQL first validates the server-level login.&lt;/P&gt;
&lt;P&gt;Authentication failures can occur when:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;The SQL login does not exist on the secondary logical server.&lt;/LI&gt;
&lt;LI&gt;The login exists but has a different SID than the corresponding login on the primary server.&lt;/LI&gt;
&lt;LI&gt;The login-user mapping is broken because the replicated database user references a different SID.&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;Microsoft documentation specifically calls out that when using SQL logins and database users, the same logins must be present on the target server as part of geo-replication disaster recovery planning.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;For more details: &lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/active-geo-replication-security-configure?view=azuresql#set-up-user-access-to-a-secondary-or-recovered-database" target="_blank" rel="noopener"&gt;Configure Security for Disaster Recovery - Azure SQL Database | Microsoft Learn&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;Validate Login Configuration&lt;/H2&gt;
&lt;P&gt;Start by comparing the SQL logins on both logical servers:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;SELECT name, sid
FROM sys.sql_logins
WHERE type_desc = 'SQL_LOGIN';
&lt;/LI-CODE&gt;
&lt;P&gt;Verify:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The login exists on both servers.&lt;/LI&gt;
&lt;LI&gt;The SID matches between the primary and secondary servers.&lt;/LI&gt;
&lt;LI&gt;The login is not disabled.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;Resolution Option 1: Use Contained Database Users&lt;/H2&gt;
&lt;P&gt;Contained users eliminate the dependency on server-level logins because authentication information is stored inside the user database.&lt;/P&gt;
&lt;P&gt;Example:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;CREATE USER AppUser
WITH PASSWORD = 'StrongPassword';
&lt;/LI-CODE&gt;
&lt;P&gt;Because contained users are part of the user database, they are replicated together with the database and can continue working after failover without additional login configuration.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;For more information:&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/en-us/sql/relational-databases/security/contained-database-users-making-your-database-portable?view=sql-server-ver17" target="_blank" rel="noopener"&gt;Contained User Access to Contained Databases - SQL Server | Microsoft Learn&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/active-geo-replication-security-configure?view=azuresql" target="_blank" rel="noopener"&gt;Configure Security for Disaster Recovery - Azure SQL Database | Microsoft Learn&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;Resolution Option 2: Create the Missing Login on the Secondary Server&lt;/H2&gt;
&lt;P&gt;If SQL logins are required, retrieve the SID from the primary server's master database:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;SELECT name, sid
FROM sys.sql_logins
WHERE name = 'AppLogin';
&lt;/LI-CODE&gt;
&lt;P&gt;Then create the login on the secondary server's master database using the same SID:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;CREATE LOGIN AppLogin
WITH PASSWORD = 'StrongPassword',
SID = 0x010600000000006400000000000000001E104AE44775E14EBED5F8248FEF66EC;&lt;/LI-CODE&gt;
&lt;P&gt;Microsoft also uses this same SID-preservation approach when configuring geo-replication security&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/active-geo-replication-configure-portal?view=azuresql&amp;amp;tabs=portal" target="_blank" rel="noopener"&gt;Tutorial: Geo-replication &amp;amp; failover in portal - Azure SQL Database | Microsoft Learn&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;Resolution Option 3: Recreate the Login if the SID Does Not Match&lt;/H2&gt;
&lt;P&gt;If the login already exists on the secondary server's master database but uses a different SID, drop and recreate it using the SID from the primary server.&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;DROP LOGIN AppLogin;
GO

CREATE LOGIN AppLogin
WITH PASSWORD = 'StrongPassword',
SID = 0x010600000000006400000000000000001E104AE44775E14EBED5F8248FEF66EC;
GO&lt;/LI-CODE&gt;
&lt;P&gt;This ensures the login is correctly mapped to the replicated database user.&lt;/P&gt;
&lt;H2&gt;&amp;nbsp;&lt;/H2&gt;
&lt;H2&gt;Alternative Validation and Remediation Using PowerShell&lt;/H2&gt;
&lt;P&gt;In our scenario, the customer was using the Azure Portal Query Editor and was unable to validate the configuration through traditional tools such as SQL Server Management Studio (SSMS) or Azure Data Studio (ADS). PowerShell provides an alternative method to both validate connectivity and apply the required login configuration on the secondary server&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;In our case, the customer was primarily testing connectivity through the Azure Portal Query Editor and did not always have access to traditional management tools such as SQL Server Management Studio (SSMS) or Azure Data Studio (ADS).&lt;/P&gt;
&lt;P&gt;PowerShell can be used both to validate connectivity and to apply the required login configuration on the secondary server.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Step 1: Connect to Azure&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;Connect-AzAccount

Set-AzContext -SubscriptionId "&amp;lt;SubscriptionID&amp;gt;"

$token = (Get-AzAccessToken -ResourceUrl "https://database.windows.net").Token
&lt;/LI-CODE&gt;
&lt;H6&gt;&lt;STRONG&gt;Step 2: Check whether the login exists on the secondary logical server&lt;/STRONG&gt;&lt;/H6&gt;
&lt;P&gt;Connect to the &lt;STRONG&gt;master&lt;/STRONG&gt; database on the secondary server and review the existing SQL logins&lt;SPAN style="color: rgb(30, 30, 30);"&gt;:&lt;/SPAN&gt;&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;Invoke-Sqlcmd -ServerInstance "tcp:&amp;lt;server-name&amp;gt;.database.windows.net,1433" -Database "master" -AccessToken $token -Query "SELECT name, sid FROM sys.sql_logins;"&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Step 3: Create the Missing Login on the Secondary Server&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;If the login does not exist on the secondary logical server, connect to the secondary server's master database and create it using the SID from the primary:&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;Invoke-Sqlcmd -ServerInstance "tcp:&amp;lt;server-name&amp;gt;.database.windows.net,1433" -Database "master" -AccessToken $token -Query "CREATE LOGIN AppLogin WITH PASSWORD = 'StrongPassword', SID = 0x010600000000006400000000000000001E104AE44775E14EBED5F8248FEF66EC;"
&lt;/LI-CODE&gt;
&lt;H6&gt;&lt;STRONG&gt;Step 4: Recreate the Login if the SID Does Not Match&lt;/STRONG&gt;&lt;/H6&gt;
&lt;P&gt;If the login already exists but has a different SID than the primary, recreate it using the SID from the primary server&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;Invoke-Sqlcmd -ServerInstance "tcp:&amp;lt;server-name&amp;gt;.database.windows.net,1433" -Database "master" -AccessToken $token `
-Query "  DROP LOGIN AppLogin;

        CREATE LOGIN AppLogin
        WITH PASSWORD = 'StrongPassword',
        SID = 0x010600000000006400000000000000001E104AE44775E14EBED5F8248FEF66EC;
    "&lt;/LI-CODE&gt;
&lt;H6&gt;&lt;STRONG&gt;Step 5: Validate connectivity&lt;/STRONG&gt;&lt;/H6&gt;
&lt;P&gt;After creating or correcting the login, test the connection by authenticating with the SQL login against the secondary replica.&lt;/P&gt;
&lt;P&gt;This approach is especially useful when administrators cannot use SSMS or Azure Data Studio and need to validate or remediate the configuration directly through PowerShell&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;Invoke-Sqlcmd `
    -ServerInstance "myserver.database.windows.net" `
    -Database "MyDatabase" `
    -Username "AppLogin" `
    -Password "StrongPassword" `
    -Query "SELECT DB_NAME();"&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;A successful result confirms that SQL Authentication is working and that the login can access the target database.&lt;/P&gt;
&lt;H2&gt;Key Takeaways&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;Active Geo-Replication replicates user databases, not SQL logins stored in the master database.&lt;/LI&gt;
&lt;LI&gt;A replicated database user alone does not guarantee successful SQL Authentication.&lt;/LI&gt;
&lt;LI&gt;Always validate login existence and SID consistency across primary and secondary logical servers.&lt;/LI&gt;
&lt;LI&gt;Contained database users can simplify authentication management in geo-replicated environments.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H2&gt;References&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/active-geo-replication-security-configure?view=azuresql" target="_blank" rel="noopener" data-tabster="{&amp;quot;restorer&amp;quot;:{&amp;quot;type&amp;quot;:1}}"&gt;Configure Security for Disaster Recovery - Azure SQL Database&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/active-geo-replication-overview?view=azuresql&amp;amp;tabs=tsql" target="_blank" rel="noopener"&gt;Active Geo-Replication - Azure SQL Database | Microsoft Learn&lt;/A&gt;&lt;/LI&gt;
&lt;/UL&gt;</description>
      <pubDate>Thu, 30 Jul 2026 14:12:46 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/troubleshooting-sql-authentication-failures-on-azure-sql/ba-p/4542414</guid>
      <dc:creator>hudajazmawi</dc:creator>
      <dc:date>2026-07-30T14:12:46Z</dc:date>
    </item>
    <item>
      <title>Lessons Learned #550: From a Support Case to Reusable Knowledge</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-550-from-a-support-case-to-reusable-knowledge/ba-p/4541950</link>
      <description>&lt;P&gt;Reaching Lessons Learned #550 is an important milestone for me. However, the value of this series is not only the number of articles published. Each article started with a technical question, an unexpected behavior, a support investigation, or a scenario that required additional testing and analysis.&lt;/P&gt;
&lt;P&gt;Some cases resulted in a configuration change. Others required a query, a script, a workaround, a product clarification, or a different troubleshooting approach. &lt;STRONG&gt;Over time, I have learned that resolving the immediate issue is only one part of the work.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;A support case becomes even more valuable when the knowledge gained during the investigation can help another engineer or customer facing a similar situation. &lt;STRONG&gt;Every support case may contain a lesson. The challenge is to identify it, validate it, and make it reusable.&lt;/STRONG&gt;&lt;/P&gt;
&lt;H2&gt;Identify the Reusable Lesson&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;Not every detail from a support case needs to become an article.&lt;/STRONG&gt; The first step is to identify the part of the investigation that may be useful outside the original scenario.&lt;/P&gt;
&lt;P&gt;This could be:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;an unexpected product behavior;&lt;/LI&gt;
&lt;LI&gt;a common misunderstanding;&lt;/LI&gt;
&lt;LI&gt;a diagnostic query;&lt;/LI&gt;
&lt;LI&gt;a troubleshooting method;&lt;/LI&gt;
&lt;LI&gt;a configuration requirement;&lt;/LI&gt;
&lt;LI&gt;a limitation that may not be immediately visible;&lt;/LI&gt;
&lt;LI&gt;a way to interpret a metric or error message;&lt;/LI&gt;
&lt;LI&gt;a test that helped confirm the technical explanation.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;For example, the specific customer environment may be unique, but the method used to distinguish CPU pressure from Data IO pressure may be useful in many other investigations. Similarly, the original application architecture may be complex, but the test used to isolate a network path may be simple and reusable.&lt;/P&gt;
&lt;P&gt;The objective is not to reproduce the complete support case. The objective is to extract the lesson that may help others.&lt;/P&gt;
&lt;H2&gt;Explain the Symptom Clearly&lt;/H2&gt;
&lt;P&gt;A useful technical article should begin with a behavior that readers can recognize.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Connections fail only from one application instance.&lt;/LI&gt;
&lt;LI&gt;Query duration increases after a service-tier migration.&lt;/LI&gt;
&lt;LI&gt;CPU reaches a high percentage, but the workload remains constrained by another resource.&lt;/LI&gt;
&lt;LI&gt;A failover restores normal operation without fully explaining the original cause.&lt;/LI&gt;
&lt;LI&gt;A monitoring result appears different from what was initially expected.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;A reader should be able to determine quickly whether the scenario resembles a problem they are investigating.&lt;/P&gt;
&lt;H2&gt;Describe How the Conclusion Was Reached&lt;/H2&gt;
&lt;P&gt;A solution is more useful when the reader understands how it was validated.&lt;/P&gt;
&lt;P&gt;For that reason, I normally try to explain:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;what was initially observed;&lt;/LI&gt;
&lt;LI&gt;which evidence was reviewed;&lt;/LI&gt;
&lt;LI&gt;which possibilities were considered;&lt;/LI&gt;
&lt;LI&gt;which tests were performed;&lt;/LI&gt;
&lt;LI&gt;what result supported the conclusion;&lt;/LI&gt;
&lt;LI&gt;which limitations remained.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The objective is to provide enough context for the reader to understand why the conclusion is reasonable and under which conditions it applies.&lt;/P&gt;
&lt;H2&gt;Separate Mitigation from Explanation&lt;/H2&gt;
&lt;P&gt;A mitigation may restore service without fully explaining the technical cause.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;restarting an application may reset the connection pool;&lt;/LI&gt;
&lt;LI&gt;a failover may disconnect blocking sessions;&lt;/LI&gt;
&lt;LI&gt;scaling may increase several resource limits simultaneously;&lt;/LI&gt;
&lt;LI&gt;recompiling a query may temporarily produce a better execution plan;&lt;/LI&gt;
&lt;LI&gt;reverting a deployment may remove the immediate impact.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;These actions can be valid and necessary.&lt;/P&gt;
&lt;P&gt;However, when converting the case into reusable knowledge, it is important to distinguish between:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;what restored normal operation;&lt;/LI&gt;
&lt;LI&gt;what was confirmed as the contributing condition;&lt;/LI&gt;
&lt;LI&gt;what remained unconfirmed.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This distinction helps prevent a successful recovery action from being interpreted as a complete root-cause explanation.&lt;/P&gt;
&lt;H2&gt;Include Something Practical&lt;/H2&gt;
&lt;P&gt;The most useful articles normally provide something the reader can apply.&lt;/P&gt;
&lt;P&gt;This may be:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;a query;&lt;/LI&gt;
&lt;LI&gt;a script;&lt;/LI&gt;
&lt;LI&gt;a checklist;&lt;/LI&gt;
&lt;LI&gt;a sequence of tests;&lt;/LI&gt;
&lt;LI&gt;a monitoring recommendation;&lt;/LI&gt;
&lt;LI&gt;a comparison table;&lt;/LI&gt;
&lt;LI&gt;a list of questions to ask;&lt;/LI&gt;
&lt;LI&gt;an example of the expected and unexpected results.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Even a short article can be valuable if it gives the reader a practical next step.&lt;/P&gt;
&lt;P&gt;For example, a troubleshooting article may suggest comparing:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;affected and unaffected periods;&lt;/LI&gt;
&lt;LI&gt;successful and unsuccessful connections;&lt;/LI&gt;
&lt;LI&gt;current and previous execution plans;&lt;/LI&gt;
&lt;LI&gt;CPU, Data IO, and log write utilization;&lt;/LI&gt;
&lt;LI&gt;the original and alternative network paths;&lt;/LI&gt;
&lt;LI&gt;behavior before and after one controlled change.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The practical element is what transforms an explanation into a reusable resource.&lt;/P&gt;
&lt;H2&gt;Document the Boundaries of the Conclusion&lt;/H2&gt;
&lt;P&gt;A technical conclusion is more reliable when its limitations are clearly described.&lt;/P&gt;
&lt;P&gt;During a support investigation, the available evidence may not allow us to determine every detail.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;the historical telemetry may be limited;&lt;/LI&gt;
&lt;LI&gt;the behavior may not be reproducible;&lt;/LI&gt;
&lt;LI&gt;the exact application request may not be identifiable;&lt;/LI&gt;
&lt;LI&gt;the test environment may differ from production;&lt;/LI&gt;
&lt;LI&gt;an internal implementation detail may not be externally visible.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;In these situations, it is useful to explain both what was confirmed and what could not be confirmed.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;P&gt;The behavior was reproduced only through the affected network path. The same endpoint and authentication method worked successfully through an alternative path. The tests confirmed that the network path was a relevant condition, although the available evidence did not identify the specific component responsible.&lt;/P&gt;
&lt;P&gt;This type of conclusion is precise, useful, and transparent.&lt;/P&gt;
&lt;H2&gt;A Simple Model I Normally Follow&lt;/H2&gt;
&lt;P&gt;When deciding whether a support investigation can become reusable knowledge, I normally consider the following sequence:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Observe:&lt;/STRONG&gt; What behavior was reported or measured?&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Clarify:&lt;/STRONG&gt; What was the exact scope and impact?&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Investigate:&lt;/STRONG&gt; Which evidence was relevant?&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Reproduce&lt;/STRONG&gt;: Could the behavior be tested under controlled conditions?&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Validate&lt;/STRONG&gt;: Which result supported or challenged the explanation?&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Mitigate:&lt;/STRONG&gt; What action reduced the immediate impact?&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Conclude:&lt;/STRONG&gt; What did the available evidence allow us to confirm?&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Share:&lt;/STRONG&gt; Which part of the investigation may help someone else?&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Not every case follows these steps in the same order, and not every investigation provides a complete answer.&lt;/P&gt;
&lt;P&gt;However, this approach helps transform an individual technical experience into something that can be understood and reused.&lt;/P&gt;
&lt;H2&gt;Questions That Help Identify a Lessons Learned Article&lt;/H2&gt;
&lt;P&gt;Before writing an article, I normally consider questions such as:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Was the behavior unexpected or difficult to interpret?&lt;/LI&gt;
&lt;LI&gt;Could the same question affect other Azure SQL users?&lt;/LI&gt;
&lt;LI&gt;Was there an important difference between the initial assumption and the final conclusion?&lt;/LI&gt;
&lt;LI&gt;Did the investigation produce a useful query, script, or test?&lt;/LI&gt;
&lt;LI&gt;Is there a limitation or condition that should be better understood?&lt;/LI&gt;
&lt;LI&gt;Can the scenario be explained without customer-specific information?&lt;/LI&gt;
&lt;LI&gt;What should another engineer or customer do when facing the same behavior?&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;If the investigation provides a useful answer to one or more of these questions, it may contain a lesson worth sharing.&lt;/P&gt;
&lt;H2&gt;Conclusion&lt;/H2&gt;
&lt;P&gt;After 550 Lessons Learned articles, the most important lesson may be that technical support knowledge should not remain only inside an individual service request. A support case starts with an immediate need: understand the behavior, reduce the impact, and identify the appropriate next action.&lt;/P&gt;
&lt;P&gt;However, once the investigation is complete, we have an opportunity to go one step further. We can extract the reusable part of the experience, explain how the conclusion was reached, document its limitations, and provide something practical for the next person facing a similar situation.&lt;/P&gt;
&lt;P&gt;That is how an individual support case can become shared technical knowledge. Resolving a case helps one specific situation. Sharing the validated lesson may help many others avoid starting the same investigation from zero.&lt;/P&gt;</description>
      <pubDate>Wed, 29 Jul 2026 10:05:15 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-550-from-a-support-case-to-reusable-knowledge/ba-p/4541950</guid>
      <dc:creator>Jose_Manuel_Jurado</dc:creator>
      <dc:date>2026-07-29T10:05:15Z</dc:date>
    </item>
    <item>
      <title>Lessons Learned #549: Reproduce, Challenge, and Validate – Testing Technical Assumptions</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-549-reproduce-challenge-and-validate-testing/ba-p/4541931</link>
      <description>&lt;P&gt;During an Azure SQL investigation, the available evidence may point to a technically reasonable explanation.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;A network change may appear to explain a connectivity issue.&lt;/LI&gt;
&lt;LI&gt;High CPU may appear to explain a performance degradation.&lt;/LI&gt;
&lt;LI&gt;A query plan change may appear to explain a longer execution time.&lt;/LI&gt;
&lt;LI&gt;A failover may appear to explain why the behavior disappeared.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;These explanations may be correct. However, before considering them confirmed, I normally try to reproduce the behavior and validate the conditions under which it occurs. A reasonable explanation is still a hypothesis until the available evidence supports it consistently.&lt;/P&gt;
&lt;H2&gt;Reproduce the Smallest Useful Scenario&lt;/H2&gt;
&lt;P&gt;A reproduction does not always need to recreate the complete production environment. In many cases, a smaller test provides a clearer result.&lt;/P&gt;
&lt;P&gt;For a connectivity issue, the test may require only:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;one client;&lt;/LI&gt;
&lt;LI&gt;one database endpoint;&lt;/LI&gt;
&lt;LI&gt;the same authentication method;&lt;/LI&gt;
&lt;LI&gt;the relevant network path;&lt;/LI&gt;
&lt;LI&gt;a clearly identified timestamp.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;For a performance issue, it may require:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;one representative query;&lt;/LI&gt;
&lt;LI&gt;the same parameters;&lt;/LI&gt;
&lt;LI&gt;the relevant database configuration;&lt;/LI&gt;
&lt;LI&gt;a controlled execution period.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The objective is to isolate the behavior being investigated while reducing unnecessary variables. The smaller the scenario, the easier it normally becomes to understand why the result changes.&lt;/P&gt;
&lt;H2&gt;Define the Expected Result&lt;/H2&gt;
&lt;P&gt;Before running a test, I try to define what result I expect. For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Hypothesis&lt;/STRONG&gt;: The connection behavior depends on a specific network path.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Expected Result&lt;/STRONG&gt;: The behavior should occur when the connection uses that path and should not occur when an alternative path is used.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Result That Would Challenge the Hypothesis:&lt;/STRONG&gt; The same behavior occurs independently of the network path.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The same approach can be used for performance investigations.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Hypothesis&lt;/STRONG&gt;: The query slowdown is caused by Data IO saturation.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Expected Result: &lt;/STRONG&gt;Query duration should increase when Data IO reaches its service-level limit.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Result That Would Challenge the Hypothesis&lt;/STRONG&gt;: The same slowdown occurs while Data IO remains low.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Defining the expected result before the test helps avoid interpreting every outcome as confirmation of the initial explanation.&lt;/P&gt;
&lt;H2&gt;Change One Variable at a Time&lt;/H2&gt;
&lt;P&gt;When several conditions are changed simultaneously, it may be difficult to determine which one affected the result.&lt;/P&gt;
&lt;P&gt;For example, suppose a performance test includes:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;a larger service objective;&lt;/LI&gt;
&lt;LI&gt;updated statistics;&lt;/LI&gt;
&lt;LI&gt;a new index;&lt;/LI&gt;
&lt;LI&gt;lower concurrency.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;If performance improves, the result is positive, but it may not clearly identify which change produced the improvement.&lt;/P&gt;
&lt;P&gt;A more useful approach is to test each relevant change separately.&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Test&lt;/th&gt;&lt;th&gt;Change&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Test 1&lt;/td&gt;&lt;td&gt;Original configuration&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Test 2&lt;/td&gt;&lt;td&gt;Updated statistics only&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Test 3&lt;/td&gt;&lt;td&gt;New index only&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Test 4&lt;/td&gt;&lt;td&gt;Higher service objective only&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;This does not mean that every investigation requires an extensive test matrix.&lt;/P&gt;
&lt;P&gt;The objective is simply to avoid changing several important variables at the same time when we need to understand which one explains the behavior.&lt;/P&gt;
&lt;H2&gt;Compare Where the Behavior Occurs and Where It Does Not&lt;/H2&gt;
&lt;P&gt;Understanding where a behavior does not occur can be as useful as reproducing where it does.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Does the connection fail from one application server but succeed from another?&lt;/LI&gt;
&lt;LI&gt;Does the query perform normally with different parameters?&lt;/LI&gt;
&lt;LI&gt;Does the issue occur only through one network path?&lt;/LI&gt;
&lt;LI&gt;Does the previous execution plan perform better?&lt;/LI&gt;
&lt;LI&gt;Does the behavior disappear when concurrency is reduced?&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;These comparisons help define the boundaries of the problem.&lt;/P&gt;
&lt;P&gt;They may not immediately identify the complete root cause, but they help determine which conditions are relevant and which ones are less likely to explain the result.&lt;/P&gt;
&lt;H2&gt;Challenge the Explanation&lt;/H2&gt;
&lt;P&gt;Once a test appears to support a hypothesis, I normally try to challenge it. For example, if performance improves after scaling the database, it may be tempting to conclude that CPU was insufficient.&lt;/P&gt;
&lt;P&gt;However, scaling may also provide:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;more memory;&lt;/LI&gt;
&lt;LI&gt;higher Data IO capacity;&lt;/LI&gt;
&lt;LI&gt;greater transaction-log throughput;&lt;/LI&gt;
&lt;LI&gt;additional workers;&lt;/LI&gt;
&lt;LI&gt;different resource limits.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The improvement is important evidence, but additional information may still be required to identify which resource was actually limiting the workload.&lt;/P&gt;
&lt;P&gt;Similarly, if a failover restores normal performance, it may also have:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;disconnected blocking sessions;&lt;/LI&gt;
&lt;LI&gt;refreshed application connections;&lt;/LI&gt;
&lt;LI&gt;caused query recompilation;&lt;/LI&gt;
&lt;LI&gt;reset a temporary condition.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The action that restores normal operation may not, by itself, fully explain the original cause.&lt;/P&gt;
&lt;H2&gt;Document the Result and Its Limitations&lt;/H2&gt;
&lt;P&gt;Not every test produces a definitive answer.&lt;/P&gt;
&lt;P&gt;A useful conclusion should describe:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;which conditions were tested;&lt;/LI&gt;
&lt;LI&gt;what result was expected;&lt;/LI&gt;
&lt;LI&gt;what result was observed;&lt;/LI&gt;
&lt;LI&gt;whether the result was repeatable;&lt;/LI&gt;
&lt;LI&gt;which differences from the original scenario remained;&lt;/LI&gt;
&lt;LI&gt;what the evidence did and did not allow us to confirm.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;P&gt;The behavior was reproduced only when the connection used the affected network path. The same authentication method and database endpoint worked successfully through an alternative path. Based on these tests, the network path was confirmed as a relevant condition. The available evidence did not identify the specific network component responsible for the behavior.&lt;/P&gt;
&lt;P&gt;This conclusion is useful because it clearly separates what was validated from what remains unknown.&lt;/P&gt;
&lt;H2&gt;Conclusion&lt;/H2&gt;
&lt;P&gt;Reproduction is one of the most valuable troubleshooting tools available to an engineer.&lt;/P&gt;
&lt;P&gt;However, its purpose is not simply to make an error occur again.&lt;/P&gt;
&lt;P&gt;A useful reproduction should help us understand:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;which conditions are required;&lt;/LI&gt;
&lt;LI&gt;which conditions are not relevant;&lt;/LI&gt;
&lt;LI&gt;what result supports the hypothesis;&lt;/LI&gt;
&lt;LI&gt;what result challenges it;&lt;/LI&gt;
&lt;LI&gt;how consistently the behavior can be observed.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The most effective tests are often simple:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;reproduce the smallest useful scenario;&lt;/LI&gt;
&lt;LI&gt;define the expected result;&lt;/LI&gt;
&lt;LI&gt;change one variable at a time;&lt;/LI&gt;
&lt;LI&gt;compare affected and unaffected conditions;&lt;/LI&gt;
&lt;LI&gt;document the limitations.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Reproduction does not only confirm that a behavior exists. It helps define the conditions under which the technical conclusion can be trusted.&lt;/P&gt;</description>
      <pubDate>Wed, 29 Jul 2026 09:50:09 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-549-reproduce-challenge-and-validate-testing/ba-p/4541931</guid>
      <dc:creator>Jose_Manuel_Jurado</dc:creator>
      <dc:date>2026-07-29T09:50:09Z</dc:date>
    </item>
    <item>
      <title>Lesson Learned #548: From Symptoms to Evidence – How I Approach an Azure SQL Investigation</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lesson-learned-548-from-symptoms-to-evidence-how-i-approach-an/ba-p/4541921</link>
      <description>&lt;P&gt;After working on many Azure SQL support cases, I have learned that the initial service request details are essential, although they may not always provide sufficient information to understand the complete technical situation.&lt;/P&gt;
&lt;P&gt;When a service request is initially created, the available information may be limited to a brief description such as:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The database is running slowly.&lt;/LI&gt;
&lt;LI&gt;CPU utilization is high.&lt;/LI&gt;
&lt;LI&gt;Connections are failing.&lt;/LI&gt;
&lt;LI&gt;The issue started after a deployment.&lt;/LI&gt;
&lt;LI&gt;Performance improved after a failover.&lt;/LI&gt;
&lt;LI&gt;The workload became slower after a service-tier migration.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;These details provide an important starting point for the investigation. However, the initial description may not yet include all the relevant timestamps, metrics, logs, configuration details, or historical context required for a complete technical assessment.&lt;/P&gt;
&lt;P&gt;This is completely understandable. At the time the service request is created, the immediate priority is normally to describe the observed behavior and its impact. Additional technical context can then be collected progressively during the investigation.&lt;/P&gt;
&lt;P&gt;For that reason, I normally use the initial service request details to understand the reported symptom and determine which additional information may be required. The first objective is to clarify the observed behavior, define the affected scope, and identify the evidence that may help us evaluate the different possible explanations.&lt;/P&gt;
&lt;P&gt;In this article, I would like to share the approach I normally follow when moving from an initial service request description to an evidence-based technical conclusion.&lt;/P&gt;
&lt;H2&gt;Clarifying the Observed Behavior&lt;/H2&gt;
&lt;P&gt;The first thing I normally try to understand is the exact impact.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Was the application completely unavailable, or was it slower than usual?&lt;/LI&gt;
&lt;LI&gt;Did the behavior affect all users or only a specific group?&lt;/LI&gt;
&lt;LI&gt;Did it affect all queries or only one process?&lt;/LI&gt;
&lt;LI&gt;Was the issue continuous or intermittent?&lt;/LI&gt;
&lt;LI&gt;Did it affect one database, several databases, or the complete logical server?&lt;/LI&gt;
&lt;LI&gt;Did the problem occur only from a particular network location?&lt;/LI&gt;
&lt;LI&gt;Did existing connections continue working while new connections failed?&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;These questions may appear simple, but the answers can significantly change the direction of the investigation. For example, the initial service request details may indicate:&lt;/P&gt;
&lt;P&gt;Connections to Azure SQL Database are failing. After reviewing the scenario, we may find that:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;other applications can connect successfully;&lt;/LI&gt;
&lt;LI&gt;only one application instance is affected;&lt;/LI&gt;
&lt;LI&gt;existing connections continue working;&lt;/LI&gt;
&lt;LI&gt;only new connections are failing;&lt;/LI&gt;
&lt;LI&gt;the issue occurs from one specific network path.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;With this additional information, the situation may no longer appear to be a general Azure SQL connectivity issue. The investigation may instead need to focus on areas such as:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;application connection pooling;&lt;/LI&gt;
&lt;LI&gt;authentication;&lt;/LI&gt;
&lt;LI&gt;DNS resolution;&lt;/LI&gt;
&lt;LI&gt;token renewal;&lt;/LI&gt;
&lt;LI&gt;network routing;&lt;/LI&gt;
&lt;LI&gt;firewall rules;&lt;/LI&gt;
&lt;LI&gt;a specific application instance;&lt;/LI&gt;
&lt;LI&gt;client-side resource pressure.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Similarly, a service request may indicate that the database is slow. Before looking for the cause, I normally try to understand what “slow” means in that specific situation.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Are all queries slower?&lt;/LI&gt;
&lt;LI&gt;Is only one stored procedure affected?&lt;/LI&gt;
&lt;LI&gt;Has execution time increased from seconds to minutes?&lt;/LI&gt;
&lt;LI&gt;Is the delay occurring while opening the connection?&lt;/LI&gt;
&lt;LI&gt;Is the delay occurring while executing the command?&lt;/LI&gt;
&lt;LI&gt;Is the application waiting on Azure SQL Database or on another dependency?&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Before reviewing metrics, logs, or execution plans, it is important to convert the initial description into a precise and measurable technical symptom.&lt;/P&gt;
&lt;H2&gt;Distinguishing Observations from Possible Explanations&lt;/H2&gt;
&lt;P&gt;One of the most useful habits I have developed is to separate what has been observed from what still needs to be validated.&lt;/P&gt;
&lt;P&gt;Consider the following statement: The application became slow when database CPU reached 95%.&lt;/P&gt;
&lt;P&gt;From this description, we may have two observations:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;the application experienced a slowdown;&lt;/LI&gt;
&lt;LI&gt;database CPU reached 95%.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;However, the relationship between those two observations still needs to be validated. High CPU may have caused the slowdown, but it could also be part of a larger chain of events. For example, CPU utilization may have increased because:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;the application started sending more requests;&lt;/LI&gt;
&lt;LI&gt;blocking caused requests to accumulate;&lt;/LI&gt;
&lt;LI&gt;a query execution plan changed;&lt;/LI&gt;
&lt;LI&gt;application retries generated additional workload;&lt;/LI&gt;
&lt;LI&gt;a scheduled process started running;&lt;/LI&gt;
&lt;LI&gt;concurrency increased;&lt;/LI&gt;
&lt;LI&gt;a query began processing more data;&lt;/LI&gt;
&lt;LI&gt;data distribution changed;&lt;/LI&gt;
&lt;LI&gt;statistics changed;&lt;/LI&gt;
&lt;LI&gt;a maintenance task started.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;In other words, high CPU may be the cause of the performance issue, but it may also be the result of another condition. I have seen similar situations after failovers, application restarts, and scaling operations. For example, the available details may indicate: Performance returned to normal after a failover.&lt;/P&gt;
&lt;P&gt;The failover is an important part of the investigation, but it may not, by itself, fully explain the original cause.&lt;/P&gt;
&lt;P&gt;A failover may also:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;disconnect blocking sessions;&lt;/LI&gt;
&lt;LI&gt;cause queries to compile again;&lt;/LI&gt;
&lt;LI&gt;refresh application connections;&lt;/LI&gt;
&lt;LI&gt;reset a temporary condition;&lt;/LI&gt;
&lt;LI&gt;clear some cached state;&lt;/LI&gt;
&lt;LI&gt;coincide with a reduction in workload.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;For this reason, I normally try to distinguish between:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;what was initially reported;&lt;/LI&gt;
&lt;LI&gt;what has been confirmed by telemetry;&lt;/LI&gt;
&lt;LI&gt;what is currently considered a possible explanation;&lt;/LI&gt;
&lt;LI&gt;what has already been validated.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This distinction helps prevent an early assumption from becoming the final conclusion before sufficient evidence is available.&lt;/P&gt;
&lt;H2&gt;Building a Timeline&lt;/H2&gt;
&lt;P&gt;When timestamps and historical information are available, I normally try to build a timeline. A timeline is often one of the most useful parts of an investigation because several events may initially appear related until they are placed in the correct order. Consider the following example:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Time&lt;/th&gt;&lt;th&gt;Event&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;10:00 UTC&lt;/td&gt;&lt;td&gt;Application deployment completed&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10:05 UTC&lt;/td&gt;&lt;td&gt;Active sessions started increasing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10:10 UTC&lt;/td&gt;&lt;td&gt;Query duration increased&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10:12 UTC&lt;/td&gt;&lt;td&gt;Data IO reached 100%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10:15 UTC&lt;/td&gt;&lt;td&gt;CPU reached 95%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10:20 UTC&lt;/td&gt;&lt;td&gt;Application timeouts were reported&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10:30 UTC&lt;/td&gt;&lt;td&gt;Application service was restarted&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10:35 UTC&lt;/td&gt;&lt;td&gt;Session count returned to normal&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;If the initial service request description mentions only the CPU peak and the application timeouts, CPU may appear to be the most likely starting point.&lt;/P&gt;
&lt;P&gt;However, the timeline shows that the number of active sessions and Data IO utilization increased before CPU reached 95%. This does not immediately confirm the root cause, but it changes the questions that should be asked.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Why did the number of sessions increase?&lt;/LI&gt;
&lt;LI&gt;Did requests start taking longer because of IO pressure?&lt;/LI&gt;
&lt;LI&gt;Did application retries contribute to the workload increase?&lt;/LI&gt;
&lt;LI&gt;Was the CPU peak the initial cause, or was it a consequence of the accumulating workload?&lt;/LI&gt;
&lt;LI&gt;Did the application deployment change request volume or execution patterns?&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Depending on the situation, the timeline may include information from:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Azure Monitor metrics;&lt;/LI&gt;
&lt;LI&gt;Query Store;&lt;/LI&gt;
&lt;LI&gt;application logs;&lt;/LI&gt;
&lt;LI&gt;deployment history;&lt;/LI&gt;
&lt;LI&gt;audit events;&lt;/LI&gt;
&lt;LI&gt;scaling operations;&lt;/LI&gt;
&lt;LI&gt;failovers;&lt;/LI&gt;
&lt;LI&gt;configuration changes;&lt;/LI&gt;
&lt;LI&gt;maintenance processes;&lt;/LI&gt;
&lt;LI&gt;network changes.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The objective is not to collect every piece of information available. The objective is to identify the sequence of events that may explain what happened before, during, and after the reported incident.&lt;/P&gt;
&lt;H2&gt;Using Each Data Source for the Right Question&lt;/H2&gt;
&lt;P&gt;Another lesson I have learned is that no single source of telemetry normally explains the complete situation.&lt;/P&gt;
&lt;P&gt;Different data sources answer different questions.&lt;/P&gt;
&lt;H3&gt;Azure Monitor&lt;/H3&gt;
&lt;P&gt;Azure Monitor can help identify whether the database reached limits related to:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;CPU;&lt;/LI&gt;
&lt;LI&gt;Data IO;&lt;/LI&gt;
&lt;LI&gt;log write;&lt;/LI&gt;
&lt;LI&gt;sessions;&lt;/LI&gt;
&lt;LI&gt;workers;&lt;/LI&gt;
&lt;LI&gt;storage;&lt;/LI&gt;
&lt;LI&gt;connection failures;&lt;/LI&gt;
&lt;LI&gt;deadlocks.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;These metrics are very useful for identifying when resource pressure occurred. However, a database-level metric may not directly identify the query, application, or operation responsible for that resource usage.&lt;/P&gt;
&lt;H3&gt;Query Store&lt;/H3&gt;
&lt;P&gt;Query Store may help identify:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;changes in query duration;&lt;/LI&gt;
&lt;LI&gt;increased CPU consumption;&lt;/LI&gt;
&lt;LI&gt;changes in execution count;&lt;/LI&gt;
&lt;LI&gt;increased logical reads;&lt;/LI&gt;
&lt;LI&gt;execution-plan changes;&lt;/LI&gt;
&lt;LI&gt;query regressions.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;It can be especially useful when a database-level metric needs to be correlated with specific query activity.&lt;/P&gt;
&lt;H3&gt;Dynamic Management Views&lt;/H3&gt;
&lt;P&gt;Dynamic Management Views may provide information about:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;active requests;&lt;/LI&gt;
&lt;LI&gt;waits;&lt;/LI&gt;
&lt;LI&gt;blocking;&lt;/LI&gt;
&lt;LI&gt;sessions;&lt;/LI&gt;
&lt;LI&gt;open transactions;&lt;/LI&gt;
&lt;LI&gt;memory grants;&lt;/LI&gt;
&lt;LI&gt;current resource consumption.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This information is especially valuable while the issue is occurring.&lt;/P&gt;
&lt;P&gt;However, some of the data may no longer be available after the event has ended.&lt;/P&gt;
&lt;H3&gt;Application Logs&lt;/H3&gt;
&lt;P&gt;Application logs may help identify:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;connection timeouts;&lt;/LI&gt;
&lt;LI&gt;command duration;&lt;/LI&gt;
&lt;LI&gt;retry behavior;&lt;/LI&gt;
&lt;LI&gt;connection-pool exhaustion;&lt;/LI&gt;
&lt;LI&gt;authentication errors;&lt;/LI&gt;
&lt;LI&gt;dependency failures;&lt;/LI&gt;
&lt;LI&gt;changes in request volume.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;These logs often provide context that may not be visible from the database side.&lt;/P&gt;
&lt;H3&gt;Deployment and Configuration History&lt;/H3&gt;
&lt;P&gt;Deployment and configuration history may help explain why the behavior began at a particular time.&lt;/P&gt;
&lt;P&gt;This may include:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;application releases;&lt;/LI&gt;
&lt;LI&gt;schema changes;&lt;/LI&gt;
&lt;LI&gt;index operations;&lt;/LI&gt;
&lt;LI&gt;compatibility-level changes;&lt;/LI&gt;
&lt;LI&gt;connection-string updates;&lt;/LI&gt;
&lt;LI&gt;service-tier changes;&lt;/LI&gt;
&lt;LI&gt;network changes;&lt;/LI&gt;
&lt;LI&gt;security configuration changes.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The important point is to choose the evidence according to the question being investigated.&lt;/P&gt;
&lt;P&gt;For example, if I am investigating whether a workload is limited by Data IO rather than CPU, reviewing only CPU percentage may not provide sufficient information.&lt;/P&gt;
&lt;P&gt;I would also want to understand:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;when Data IO reached its limit;&lt;/LI&gt;
&lt;LI&gt;whether query duration increased during the same period;&lt;/LI&gt;
&lt;LI&gt;which queries generated the highest number of reads;&lt;/LI&gt;
&lt;LI&gt;whether the workload started reading more data;&lt;/LI&gt;
&lt;LI&gt;whether the same degradation occurred when Data IO remained below its limit;&lt;/LI&gt;
&lt;LI&gt;whether additional IO capacity changed the result.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The investigation becomes more effective when every piece of telemetry is connected to a specific technical question.&lt;/P&gt;
&lt;P&gt;Collecting more data does not automatically produce a better conclusion.&lt;/P&gt;
&lt;H2&gt;Keeping More Than One Possible Explanation Open&lt;/H2&gt;
&lt;P&gt;It is easy to identify one technically reasonable explanation and begin searching only for evidence that supports it. I normally try to avoid doing this, especially during the first stages of the investigation. For example, imagine that a database is migrated from a DTU-based service tier to a vCore-based service tier and the workload subsequently performs more slowly.&lt;/P&gt;
&lt;P&gt;One possible explanation is that the new environment does not provide sufficient CPU.&lt;/P&gt;
&lt;P&gt;However, other possibilities may include:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;lower Data IO capacity;&lt;/LI&gt;
&lt;LI&gt;lower transaction-log throughput;&lt;/LI&gt;
&lt;LI&gt;different memory availability;&lt;/LI&gt;
&lt;LI&gt;an execution-plan change;&lt;/LI&gt;
&lt;LI&gt;statistics changes;&lt;/LI&gt;
&lt;LI&gt;a compatibility-level difference;&lt;/LI&gt;
&lt;LI&gt;increased concurrency;&lt;/LI&gt;
&lt;LI&gt;a change in request volume;&lt;/LI&gt;
&lt;LI&gt;a change in data size or distribution;&lt;/LI&gt;
&lt;LI&gt;an application change unrelated to the migration.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Each possible explanation requires different evidence. If CPU remains moderate while Data IO repeatedly reaches its limit, increasing the number of vCores without reviewing the storage characteristics may not address the main constraint.&lt;/P&gt;
&lt;P&gt;If Query Store shows a plan regression immediately after the migration, the selected service tier may not be the primary cause. If execution count doubled after an application deployment, the database may simply be processing more work than before. Keeping several possible explanations open does not make the investigation less decisive.&amp;nbsp; It reduces the risk of reaching a conclusion before the relevant evidence has been reviewed.&lt;/P&gt;
&lt;H2&gt;Defining What Would Challenge the Hypothesis&lt;/H2&gt;
&lt;P&gt;One of the most useful questions I ask during troubleshooting is: What result would demonstrate that my current hypothesis may not be correct?&lt;/P&gt;
&lt;P&gt;Suppose the current hypothesis is: The performance degradation is caused by Data IO saturation.&lt;/P&gt;
&lt;P&gt;Evidence supporting this hypothesis may include:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Data IO reaches 100% during the affected periods;&lt;/LI&gt;
&lt;LI&gt;query duration increases at the same time;&lt;/LI&gt;
&lt;LI&gt;the affected queries perform a high number of physical reads;&lt;/LI&gt;
&lt;LI&gt;CPU remains below its limit;&lt;/LI&gt;
&lt;LI&gt;performance improves when the workload runs with additional IO capacity.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;However, I should also look for results that may challenge the hypothesis.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Does the same slowdown occur while Data IO remains low?&lt;/LI&gt;
&lt;LI&gt;Are there periods with high Data IO but normal application performance?&lt;/LI&gt;
&lt;LI&gt;Are queries with very few reads also affected?&lt;/LI&gt;
&lt;LI&gt;Does additional IO capacity consistently improve the workload?&lt;/LI&gt;
&lt;LI&gt;Does changing another variable produce a larger improvement?&lt;/LI&gt;
&lt;LI&gt;Does the issue occur in an environment where the same IO pressure is not present?&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;If an investigation only searches for supporting evidence, almost any initial theory may appear correct.&lt;/P&gt;
&lt;P&gt;Actively looking for evidence that challenges the current explanation makes the final conclusion stronger and more reliable.&lt;/P&gt;
&lt;H2&gt;Conclusion&lt;/H2&gt;
&lt;P&gt;After working on many support investigations, one of the most important lessons I have learned is that the initial service request details and the final technical conclusion naturally serve different purposes. The initial description provides the first available information about the observed behavior and its impact. At that stage, some of the relevant logs, metrics, timestamps, configuration details, or historical context may not yet be available.&lt;/P&gt;
&lt;P&gt;A reliable investigation begins with the initial service request details and develops progressively as additional evidence and technical context become available.&lt;/P&gt;</description>
      <pubDate>Wed, 29 Jul 2026 09:37:18 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lesson-learned-548-from-symptoms-to-evidence-how-i-approach-an/ba-p/4541921</guid>
      <dc:creator>Jose_Manuel_Jurado</dc:creator>
      <dc:date>2026-07-29T09:37:18Z</dc:date>
    </item>
    <item>
      <title>Working Around the Lack of Partition Switching in Microsoft Fabric SQL Database</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/working-around-the-lack-of-partition-switching-in-microsoft/ba-p/4540918</link>
      <description>&lt;H2 class="lia-indent-padding-left-30px"&gt;Overview&lt;/H2&gt;
&lt;P&gt;One of the current limitations of SQL Database in Microsoft Fabric is the lack of support for &lt;STRONG&gt;partition switching.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;BR /&gt;For DBA who use partition switching in SQL Server or Azure SQL Database to implement sliding-window retention patterns, archival processes, or large-scale data cleanup operations, this limitation can create challenges when migrating workloads to Fabric.&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;P&gt;This article describes a practical approach that significantly improved delete performance in a real-world scenario where partition switching was not available.&lt;/P&gt;
&lt;H2&gt;The Challenge&lt;/H2&gt;
&lt;P&gt;A customer needed to remove a subset of data from a large table based on a specific business filter (filter_id= 2).&lt;/P&gt;
&lt;P&gt;In traditional SQL Server environments, partition switching is often used for this type of workload because it is a metadata-only operation and can move or remove large volumes of data almost instantly.&lt;/P&gt;
&lt;P&gt;However, SQL Database in Microsoft Fabric currently does not support partition switching, as documented in the Microsoft Learn article below:&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/en-us/fabric/database/sql/limitations#table-level" data-tabster="{&amp;quot;restorer&amp;quot;:{&amp;quot;type&amp;quot;:1}}" target="_blank"&gt;Limitations for SQL database - Microsoft Fabric&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;img&gt;Switch partition is a limitation in SQL Database in Fabric&lt;/img&gt;
&lt;P&gt;As a result, the customer had to rely on a standard DELETE operation.&lt;/P&gt;
&lt;H2&gt;Symptoms&lt;/H2&gt;
&lt;P&gt;Although only a relatively small number of rows were being removed, the delete operation consistently required more than an hour to complete.&lt;/P&gt;
&lt;P&gt;Investigation showed that the issue was not caused by the delete predicate itself. Instead, the target table was referenced by multiple foreign key relationships.&lt;/P&gt;
&lt;P&gt;During the delete operation, SQL had to validate referential integrity across all dependent tables, introducing significant overhead and causing the query to run much longer than expected.&lt;/P&gt;
&lt;H2&gt;Root Cause&lt;/H2&gt;
&lt;P&gt;The performance degradation was due to the presence of multiple foreign key constraints referencing the table, which required validation during the DELETE operation. See this picture for the FKs tables:&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;This resulted in:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Row-by-row constraint validation across multiple dependent tables&lt;/LI&gt;
&lt;LI&gt;Additional overhead during data modification operations&lt;/LI&gt;
&lt;LI&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;Increased execution time for DELETE queries.&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Alternative Approach&lt;/H2&gt;
&lt;P&gt;Since partition switching was unavailable, the following strategy was used:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Temporarily disable or remove foreign key constraints on dependent tables. We used this command:&lt;BR /&gt;&lt;BR /&gt;&lt;LI-CODE lang="sql"&gt;-- Disable all FK constraints referencing the table
ALTER TABLE dbo.ChildTable1 NOCHECK CONSTRAINT FK_ChildTable1_sales_orders;
-- ... repeat for all the constraints (Foreign Keys)
 &lt;/LI-CODE&gt;&lt;/LI&gt;
&lt;LI&gt;Delete related data from child tables first.&lt;BR /&gt;&lt;BR /&gt;&lt;LI-CODE lang="sql"&gt;-- Perform DELETE
DELETE FROM dbo.ChildTable1 WHERE order_id = 2;
&lt;/LI-CODE&gt;&lt;/LI&gt;
&lt;LI&gt;Execute the delete operation on the parent table.&lt;BR /&gt;&lt;BR /&gt;&lt;LI-CODE lang="sql"&gt;-- Perform DELETE
DELETE FROM dbo.SalesOrders WHERE order_id = 2;
 &lt;/LI-CODE&gt;&lt;/LI&gt;
&lt;LI&gt;Re-enable and validate the foreign key constraints.&lt;BR /&gt;&lt;BR /&gt;&lt;LI-CODE lang="sql"&gt;-- Re-enable constraints
ALTER TABLE dbo.ChildTable1 WITH CHECK CHECK CONSTRAINT FK_ChildTable1_sales_orders;&lt;/LI-CODE&gt;&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;This approach reduced the amount of referential integrity work performed during the delete process.&lt;/P&gt;
&lt;H2&gt;Results&lt;/H2&gt;
&lt;P&gt;After the foreign key dependencies were removed from the execution path, the delete operation completed in seconds instead of more than an hour.&lt;/P&gt;
&lt;P&gt;While this approach does not provide the same metadata-only efficiency as partition switching, it can be an effective workaround for large data cleanup operations in Fabric SQL Database when foreign key enforcement becomes the primary bottleneck.&lt;/P&gt;
&lt;H2&gt;Key Takeaways&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;Partition switching is currently not supported in SQL Database in Microsoft Fabric.&lt;/LI&gt;
&lt;LI&gt;Workloads that rely on sliding-window retention patterns may require alternative implementation strategies.&lt;/LI&gt;
&lt;LI&gt;Foreign key constraints can have a significant impact on delete performance.&lt;/LI&gt;
&lt;LI&gt;For controlled maintenance activities, temporarily disabling or dropping foreign key constraints, performing the cleanup, and then restoring and validating the constraints may dramatically improve performance.&lt;/LI&gt;
&lt;LI&gt;Always validate the impact on application behavior and data integrity before implementing this approach in production.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Conclusion&lt;/H2&gt;
&lt;P&gt;The absence of partition switching in SQL Database in Microsoft Fabric does not prevent large-scale data cleanup operations, but it may require a different design approach. By identifying foreign key validation as the true performance bottleneck and restructuring the delete process accordingly, substantial performance improvements can often be achieved while maintaining data integrity.&lt;/P&gt;
&lt;P&gt;This can serve as a practical alternative until native partition switching support becomes available in Fabric SQL Database.&lt;/P&gt;</description>
      <pubDate>Wed, 29 Jul 2026 06:16:46 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/working-around-the-lack-of-partition-switching-in-microsoft/ba-p/4540918</guid>
      <dc:creator>hudajazmawi</dc:creator>
      <dc:date>2026-07-29T06:16:46Z</dc:date>
    </item>
    <item>
      <title>Enabling DB Auditing in Azure SQL DB hangs for a long time</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/enabling-db-auditing-in-azure-sql-db-hangs-for-a-long-time/ba-p/4541678</link>
      <description>&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Issue&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;An issue was brought to our attention recently where an azure SQL DB was generating an error when the customer was trying to enable database level Auditing. The first error was observed when the customer was trying to enable auding with a storage location as the destination. Here’s the error message he received:&lt;/P&gt;
&lt;P&gt;The portal would just hang (For hours) while saving the enable Audit related changes:&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;The customer experience was the same even while enabling the sql server auditing via portal. The portal would hang in a similar manner as shown above.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;The Second issue&lt;/STRONG&gt; reported was that after fixing the storage issue reported above, when the customer was trying to enable database level Auditing using their Devops tools, while Auditing was successfully enabled, the audit files (**.xel) created in the storage location (Audit folder) were empty. This is despite the customer trying DML events to trigger the Auditing to capture the events. Here’s how the storage location looked:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;No Audit logs were observed even at the portal:&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Error&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Here’s the &lt;STRONG&gt;first error message&lt;/STRONG&gt; while cx tried enabling Auditing using a DevOps tool:&lt;/P&gt;
&lt;P&gt;&lt;SPAN class="lia-text-color-8"&gt;&lt;EM&gt;The resource write operation failed to complete successfully, because it reached terminal provisioning state 'Failed'.\n\tCode: ResourceDeploymentFailure\n\tMessage: The resource write operation failed to complete successfully, because it reached terminal provisioning state 'Failed'.\n\tTarget: Exception Details: (InternalServerError) An unexpected error occured while processing the request. Tracking ID: '227365fe-a3cc-4ea8-b097-014fe57584c5' Code: InternalServerError Message: An unexpected error occured while processing the request. Tracking ID: '227365fe-a3cc-4ea8-b097-014fe57584c5'&lt;/EM&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Workaround/Fix:&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;To &lt;STRONG&gt;fix the first issue&lt;/STRONG&gt; on the Azure portal hanging while saving the DB audit settings, after in-depth investigations, we recommended the customer, When configuring immutable blob storage for auditing, ensure that &lt;STRONG&gt;Allow protected append writes&lt;/STRONG&gt; is set to either &lt;STRONG&gt;Append blobs&lt;/STRONG&gt; or &lt;STRONG&gt;Block and append blobs&lt;/STRONG&gt;. The &lt;STRONG&gt;None&lt;/STRONG&gt; option isn't supported. We had noticed that this setting was set to None initially.&lt;/P&gt;
&lt;P&gt;To enable it to true:&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;Azure portal -&amp;gt; storage account page -&amp;gt; search for containers -&amp;gt; choose sqldauditlogs container&amp;nbsp; -&amp;gt; access policy blade -&amp;gt; edit immutable blob storage policy and select the value of ""Allow protected append writes to" to Append blobs.&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;Here’s a snapshot below:&lt;/EM&gt;&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Our public documents clearly state that: When configuring immutable blob storage for auditing, ensure that &lt;STRONG&gt;Allow protected append writes&lt;/STRONG&gt; is set to either &lt;STRONG&gt;Append blobs&lt;/STRONG&gt; or &lt;STRONG&gt;Block and append blobs&lt;/STRONG&gt;. The &lt;STRONG&gt;None&lt;/STRONG&gt; option isn't supported. For time-based retention policies, the storage account's retention interval must be shorter than the SQL Auditing retention setting. Configurations where the storage policy is set, but SQL Auditing retention is 0, aren't supported.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Workaround for 2&lt;SUP&gt;nd&lt;/SUP&gt; issue&lt;/STRONG&gt; ( Auditing not capturing events)&lt;/P&gt;
&lt;P&gt;We analyzed the json file of the devops tool shared by the customer and found the following settings:&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;&lt;EM&gt;From the audit setting shown above, we can see that the end customer has used the predicate expression&amp;nbsp;&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;"&lt;STRONG&gt;predicateExpression":"object_name = 'SensitiveData&lt;/STRONG&gt;'&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;in the audit configuration. &lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;This means that the operations that object SensitiveData will only be captured in the audit records.&lt;BR /&gt;The operations on other tables/objects will not be captured.&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;P&gt;Once the customer made changes to the json file/Auditing configuration, the auditing started capturing events as expected.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;The customer also understood both the scenarios involved in this troubleshooting exercise.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;References&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/auditing-overview?view=azuresql" target="_blank"&gt;Auditing - Azure SQL Database and Azure Synapse Analytics | Microsoft Learn&lt;/A&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;A href="https://learn.microsoft.com/en-us/azure/storage/blobs/immutable-storage-overview#time-based-retention-policies" target="_blank"&gt;Overview of immutable storage for blob data - Azure Storage | Microsoft Learn&lt;/A&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/en-us/rest/api/sql/extended-server-blob-auditing-policies/create-or-update?view=rest-sql-2025-01-01&amp;amp;tabs=HTTP" target="_blank"&gt;Extended Server Blob Auditing Policies - Create Or Update - REST API (Azure SQL Database) | Microsoft Learn&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-monitor/logs/manage-access?tabs=portal" target="_blank"&gt;Manage access to Log Analytics workspaces - Azure Monitor | Microsoft Learn&lt;/A&gt;&lt;/P&gt;</description>
      <pubDate>Wed, 29 Jul 2026 06:13:36 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/enabling-db-auditing-in-azure-sql-db-hangs-for-a-long-time/ba-p/4541678</guid>
      <dc:creator>Tancy</dc:creator>
      <dc:date>2026-07-29T06:13:36Z</dc:date>
    </item>
    <item>
      <title>SQL Data Sync Retirement: Migration Insights and Modern Alternatives</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/sql-data-sync-retirement-migration-insights-and-modern/ba-p/4539083</link>
      <description>&lt;P&gt;&lt;EM style="color: rgb(30, 30, 30);"&gt;What started as a routine customer discussion quickly evolved into a strategic modernization conversation. A service that had quietly synchronized business-critical data for years was approaching retirement, prompting an important question: What should organizations do next?&amp;nbsp;&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;Every service retirement is an opportunity to reassess architecture, reduce technical debt, and build for the future.&lt;/EM&gt;&lt;/P&gt;
&lt;H2&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;SPAN style="color: rgb(30, 30, 30); font-size: 32px;"&gt;When a Retirement Notification Becomes a Business Conversation&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/EM&gt;&lt;/H2&gt;
&lt;P&gt;Recently, while working with a customer, we reviewed their Azure SQL Database architecture and discovered a critical dependency on &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/azure-sql/database/sql-data-sync-data-sql-server-sql-database?view=azuresql" target="_blank" rel="noopener"&gt;SQL Data Sync&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;For years, the service had reliably synchronized data across multiple databases, enabling applications, reporting workloads, and distributed business processes.&lt;/P&gt;
&lt;P&gt;Like many organizations, the customer viewed Data Sync as infrastructure that simply worked in the background. However, the discussion took a different turn when we reviewed Microsoft's retirement announcement:&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Azure SQL Data Sync will be retired on September 30, 2027.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;What initially appeared to be a migration challenge quickly became an opportunity to modernize the customer's data movement architecture and align with Microsoft's future investments in data integration, analytics, and cloud-native services.&lt;/P&gt;
&lt;H1&gt;&lt;STRONG&gt;Understanding SQL Data Sync&lt;/STRONG&gt;&lt;/H1&gt;
&lt;P&gt;Azure SQL Data Sync was designed to synchronize selected data between Azure SQL Databases and, in some cases, between Azure and on-premises databases.&lt;/P&gt;
&lt;P&gt;Organizations have commonly used Data Sync for:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Hybrid data synchronization&lt;/LI&gt;
&lt;LI&gt;Distributed application architectures&lt;/LI&gt;
&lt;LI&gt;Globally distributed applications&lt;/LI&gt;
&lt;LI&gt;Bi-directional data synchronization&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;While the service has served customers well, organizations should begin evaluating alternative solutions now to ensure sufficient planning, testing, and adoption time before retirement.&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;Because both databases were already in Azure, the discussion quickly moved toward identifying strategic alternatives.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;&lt;BR /&gt;&lt;STRONG style="font-size: 32px;"&gt;Customer's Setup&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;This particular customer had a simple, familiar layout: one Azure SQL Database feeding another, both fully in Azure, connected by SQL Data Sync. No on-premises leg, no complicated topology — just two cloud databases that needed to stay aligned.&lt;/P&gt;
&lt;P&gt;Their requirements were equally straightforward, and honestly, the kind every team asks for:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Reliable, dependable synchronization&lt;/LI&gt;
&lt;LI&gt;Low operational overhead — nobody wanted a new system to babysit&lt;/LI&gt;
&lt;LI&gt;Something with a real future, not another service on a retirement countdown&lt;/LI&gt;
&lt;LI&gt;Room to scale as data volumes grow&lt;/LI&gt;
&lt;LI&gt;An Azure-native fit, not a bolt-on third-party tool&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Because both databases already lived in Azure, the conversation moved quickly toward the platform's own native tooling — starting with the option that ended up being the strongest fit.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;Option 1: Azure Data Factory (Recommended Strategy)&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;For customers running Azure SQL Database to Azure SQL Database synchronization, &lt;A href="https://learn.microsoft.com/en-us/azure/data-factory/" target="_blank" rel="noopener"&gt;Azure Data Factory&lt;/A&gt; (ADF) emerged as the strongest strategic recommendation.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;Why ADF?&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Azure Data Factory provides:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Fully managed Azure-native data movement&lt;/LI&gt;
&lt;LI&gt;Enterprise-grade monitoring&lt;/LI&gt;
&lt;LI&gt;Flexible orchestration&lt;/LI&gt;
&lt;LI&gt;Scalability from development through production environments&lt;/LI&gt;
&lt;LI&gt;Long-term Microsoft investment and support&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The migration pattern we typically recommend looks like:&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;U&gt;Phase1 :Initial Full Load&lt;/U&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Before anything can stay in sync, both sides need to start from the same place. Phase 1 is a one-time bulk copy: Azure Data Factory reads everything from the source database and writes it into the target, establishing a clean baseline.&lt;/P&gt;
&lt;img&gt;&lt;STRONG&gt;Phase 1: Initial Full Load, establishing baseline parity between Database A and Database B&lt;/STRONG&gt;&lt;/img&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;U&gt;Phase2 :Incremental Synchronization&lt;/U&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Once both databases match, you don't need to keep copying everything — just what's changed. This is where&amp;nbsp;&lt;STRONG&gt;Change Tracking (CT)&lt;/STRONG&gt;&amp;nbsp;or&amp;nbsp;&lt;STRONG&gt;Change Data Capture (CDC)&lt;/STRONG&gt; comes in: SQL Server-native features that flag which rows were inserted, updated, or deleted since the last run. ADF's incremental pipeline reads only those deltas and applies them downstream, on whatever schedule the business needs — minutes, hours, or daily.&lt;/P&gt;
&lt;img&gt;Phase 2: Incremental Synchronization using Change Tracking / Change Data Capture, running on a repeating schedule&lt;/img&gt;
&lt;P&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;U&gt;&lt;STRONG&gt;Phase 3: Scheduling and Monitoring&lt;BR /&gt;&lt;/STRONG&gt;&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;Once both phases are live, ADF takes over the operational side: scheduling pipeline runs, monitoring their health, retrying failures automatically, and alerting your team when something needs attention. That's a level of visibility SQL Data Sync's built-in sync groups never really offered&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;ADF handles:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Scheduling&lt;/LI&gt;
&lt;LI&gt;Pipeline execution&lt;/LI&gt;
&lt;LI&gt;Monitoring&lt;/LI&gt;
&lt;LI&gt;Retry mechanisms&lt;/LI&gt;
&lt;LI&gt;Alerting&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This model often delivers greater visibility and operational control than traditional SQL Data Sync implementations.&lt;/P&gt;
&lt;H4&gt;When You Don't Need Synchronization — You Need a Copy&lt;/H4&gt;
&lt;P&gt;Partway through the engagement, one question reframed the whole discussion:&amp;nbsp;&lt;EM&gt;do we actually need two-way synchronization, or do we just need a readable copy of the database somewhere else?&lt;/EM&gt; That distinction matters more than it sounds, and it points to three other options worth knowing.&lt;/P&gt;
&lt;H6&gt;&lt;STRONG&gt;Option 1: Active Geo-Replication&lt;/STRONG&gt;&lt;/H6&gt;
&lt;P&gt;If the goal is disaster recovery, serving read traffic closer to users, or keeping the business running through a regional outage, &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/azure-sql/database/active-geo-replication-overview?view=azuresql&amp;amp;tabs=tsql" target="_blank" rel="noopener"&gt;Active Geo-Replication&lt;/A&gt; is usually a better fit than rebuilding sync logic from scratch. It gives you a continuously updated, readable secondary — not a bi-directional sync target.&lt;/P&gt;
&lt;H4&gt;&lt;U&gt;&lt;STRONG&gt;Best fit&lt;/STRONG&gt;&lt;/U&gt;&lt;/H4&gt;
&lt;UL&gt;
&lt;LI&gt;Disaster recovery scenarios&lt;/LI&gt;
&lt;LI&gt;Read-intensive applications&lt;/LI&gt;
&lt;LI&gt;Global user distribution&lt;/LI&gt;
&lt;LI&gt;Secondary readable databases&lt;/LI&gt;
&lt;/UL&gt;
&lt;H4&gt;&lt;U&gt;&lt;STRONG&gt;Less ideal for&lt;/STRONG&gt;&lt;/U&gt;&lt;/H4&gt;
&lt;UL&gt;
&lt;LI&gt;Complex data transformations&lt;/LI&gt;
&lt;LI&gt;Bi-directional updates&amp;nbsp;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG style="color: rgb(30, 30, 30);"&gt;Option 2: Database Copies and Read Replicas&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Some organizations do not require continuous synchronization at all.&lt;/P&gt;
&lt;P&gt;In those cases:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Read Replicas&lt;/LI&gt;
&lt;LI&gt;Database Copy&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Good Use Cases&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Reporting databases&lt;/LI&gt;
&lt;LI&gt;Analytics environments&lt;/LI&gt;
&lt;LI&gt;Refreshable staging systems&lt;/LI&gt;
&lt;LI&gt;Read-only workloads&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This approach significantly reduces architectural complexity while still meeting many business requirements.&lt;/P&gt;
&lt;H6&gt;&lt;STRONG&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;Option 3: Microsoft Fabric Mirrored Databases&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/H6&gt;
&lt;P&gt;As &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/fabric/fundamentals/microsoft-fabric-overview" target="_blank" rel="noopener"&gt;Microsoft Fabric&lt;/A&gt; adoption grows, another interesting alternative is &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/sql/sql-server/fabric-database/fabric-mirrored-databases?view=fabric-sqldb" target="_blank" rel="noopener"&gt;Fabric Mirrored Databases.&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;This option is particularly attractive for organizations already investing in:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Microsoft Fabric&lt;/LI&gt;
&lt;LI&gt;OneLake&lt;/LI&gt;
&lt;LI&gt;Real-time analytics&lt;/LI&gt;
&lt;LI&gt;AI and data platform modernization&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Benefits include:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Near real-time data availability&lt;/LI&gt;
&lt;LI&gt;Simplified analytics architecture&lt;/LI&gt;
&lt;LI&gt;Integration with Fabric workloads&lt;/LI&gt;
&lt;LI&gt;Reduced data silos&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;For customers modernizing both operational and analytical platforms, this can be an excellent opportunity to rethink data architecture beyond simple synchronization.&lt;/P&gt;
&lt;H6&gt;&lt;STRONG&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;Option 4: Azure Functions for Event-Driven Synchronization&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/H6&gt;
&lt;P&gt;Not every customer requires a large orchestration platform.&lt;/P&gt;
&lt;P&gt;For lightweight or application-specific synchronization logic, &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-overview" target="_blank" rel="noopener"&gt;Azure Functions&lt;/A&gt; may offer a more agile approach.&lt;/P&gt;
&lt;P&gt;Example Use Cases&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Event-driven updates&lt;/LI&gt;
&lt;LI&gt;Custom business rules&lt;/LI&gt;
&lt;LI&gt;Microservices architectures&lt;/LI&gt;
&lt;LI&gt;Low-volume synchronization requirements&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The tradeoff is that customers assume additional development and operational responsibilities&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&lt;STRONG&gt;Lessons Learned from the Customer Engagement&lt;/STRONG&gt;&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;This engagement reinforced several important lessons:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;&lt;STRONG&gt;Don't Wait Until 2027&lt;/STRONG&gt;&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;Although retirement is over a year away, large organizations often require significant planning, testing, governance approvals, and deployment cycles.&lt;/P&gt;
&lt;P&gt;Starting early reduces risk.&lt;/P&gt;
&lt;OL start="2"&gt;
&lt;LI&gt;&lt;STRONG&gt;There Is No Universal Replacement&lt;/STRONG&gt;&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;The right solution depends on:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Latency requirements&lt;/LI&gt;
&lt;LI&gt;Read versus write workloads&lt;/LI&gt;
&lt;LI&gt;Synchronization direction&lt;/LI&gt;
&lt;LI&gt;Operational complexity&lt;/LI&gt;
&lt;LI&gt;DR requirements&amp;nbsp;&lt;/LI&gt;
&lt;LI&gt;Budget limitation&amp;nbsp;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Different use cases require different migration paths.&lt;BR /&gt;&lt;STRONG&gt;3. Migration Is an Opportunity&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Rather than simply replacing SQL Data Sync, organizations should evaluate:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Data architecture modernization&lt;/LI&gt;
&lt;LI&gt;Observability improvements&lt;/LI&gt;
&lt;LI&gt;Operational simplification&lt;/LI&gt;
&lt;LI&gt;Fabric adoption opportunities&lt;/LI&gt;
&lt;LI&gt;Long-term cloud strategy&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG style="color: rgb(30, 30, 30);"&gt;Final Recommendations&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;For most customers currently using Azure SQL Database → Azure SQL Database synchronization:&lt;/P&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;Requirement&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Recommended Solution&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;Ongoing synchronization&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Azure Data Factory + CDC/Change Tracking&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;Read-only replica&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Active Geo-Replication&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;Simple duplication&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Database Copy or Read Replica&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;Analytics modernization&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Fabric Mirrored Databases&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
&lt;P&gt;Event-driven custom logic&lt;/P&gt;
&lt;/td&gt;&lt;td&gt;
&lt;P&gt;Azure Functions&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 50.00%" /&gt;&lt;col style="width: 50.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;P&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;In my customer's use case, Azure Data Factory with incremental changes (CDC) emerged as the preferred strategic path because it was Azure-native, scalable, supported long-term, and aligned with Microsoft's future direction for data movement and integration.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Closing Thoughts&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;Technology retirements often create urgency, but they also create opportunity. &lt;/SPAN&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;&amp;nbsp;retirement of SQL Data Sync is not merely a migration project. It is an opportunity to reassess data movement architecture, improve resiliency, reduce technical debt, and embrace modern Azure-native services.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;If your organization is currently using SQL Data Sync, now is the right time to inventory your sync groups, identify dependencies, and begin evaluating alternative architectures before September 30, 2027. &lt;BR /&gt;&lt;BR /&gt;&lt;STRONG&gt;References&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/sql-data-sync-retirement-migration?view=azuresql" target="_blank" rel="noopener"&gt;SQL Data Sync Retirement Migration Guide&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://learn.microsoft.com/en-us/azure/azure-sql/database/sql-data-sync-data-sql-server-sql-database?view=azuresql" target="_blank" rel="noopener"&gt;What is SQL Data Sync for Azure SQL Database?&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/azure-sql/database/sql-data-sync-retirement-migration?view=azuresql&amp;amp;source=docs" target="_blank" rel="noopener"&gt;SQL Data Sync retirement: Migrate to alternative solutions&lt;/A&gt;&lt;/LI&gt;
&lt;/UL&gt;</description>
      <pubDate>Sat, 25 Jul 2026 00:46:49 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/sql-data-sync-retirement-migration-insights-and-modern/ba-p/4539083</guid>
      <dc:creator>Karunakar_Kotha</dc:creator>
      <dc:date>2026-07-25T00:46:49Z</dc:date>
    </item>
    <item>
      <title>Lessons Learned #547:Some SQL DB Statistics Remain Outdated While Others Are Automatically Updated</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-547-some-sql-db-statistics-remain-outdated-while/ba-p/4540055</link>
      <description>&lt;P&gt;During the analysis of a SQL Server performance case, we observed an interesting statistics update pattern on a large table.&lt;/P&gt;
&lt;P&gt;Several statistics had recently been updated at different times, while a group of automatically created _WA_Sys_ statistics still showed:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;&lt;STRONG&gt;An older last_updated date.&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;A high modification_counter.&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;A row count significantly lower than the current number of rows in the table.&lt;/STRONG&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;At first glance, this could suggest that &lt;STRONG&gt;AUTO_UPDATE_STATISTICS&lt;/STRONG&gt; was not working correctly. However, a closer review showed that this pattern can be completely consistent with the expected behavior of SQL Server.&lt;/P&gt;
&lt;H2&gt;1. Types of statistics in SQL Server&lt;/H2&gt;
&lt;P&gt;SQL Server can maintain several types of statistics.&lt;/P&gt;
&lt;H3&gt;Automatically created column statistics&lt;/H3&gt;
&lt;P&gt;When &lt;STRONG&gt;AUTO_CREATE_STATISTICS is enabled&lt;/STRONG&gt;, SQL Server can automatically create a &lt;STRONG&gt;single-column statistic&lt;/STRONG&gt; when the Query Optimizer needs cardinality information for a column used in a query predicate. These statistics normally use names such as: &lt;STRONG&gt;_WA_Sys_00000002_47A6D5E5&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The name can be interpreted as: &lt;STRONG&gt;_WA_Sys_&amp;lt;column_id in hexadecimal&amp;gt;&lt;/STRONG&gt;_&lt;STRONG&gt;&amp;lt;object_id in hexadecimal&amp;gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;For example: &lt;STRONG&gt;00000002 &lt;/STRONG&gt;hexadecimal = &lt;STRONG&gt;column_id 2&lt;/STRONG&gt; &lt;STRONG&gt;47A6D5E5 hexadecimal&lt;/STRONG&gt; = table object_id&lt;/P&gt;
&lt;P&gt;The most reliable way to identify the associated column is not to decode the name manually, but to query the SQL Server catalog views:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;DECLARE @TableName sysname = N'dbo.CustomerTransactions'; 
SELECT s.stats_id, s.name AS statistics_name, sc.stats_column_id, c.column_id, 
c.name AS column_name, s.auto_created, s.user_created, s.no_recompute 
FROM sys.stats AS s 
INNER JOIN sys.stats_columns AS sc ON sc.object_id = s.object_id AND sc.stats_id = s.stats_id 
INNER JOIN sys.columns AS c ON c.object_id = sc.object_id AND c.column_id = sc.column_id 
WHERE s.object_id = OBJECT_ID(@TableName) 
AND s.name = N'_WA_Sys_00000002_47A6D5E5' ORDER BY sc.stats_column_id;&lt;/LI-CODE&gt;
&lt;H3&gt;Statistics associated with indexes&lt;/H3&gt;
&lt;P&gt;When SQL Server creates an index, it also creates a statistics object associated with the index. For example: &lt;STRONG&gt;CREATE INDEX IX_CustomerTransactions_ClientId ON dbo.CustomerTransactions(ClientId) t&lt;/STRONG&gt;his creates an index statistics object normally named: &lt;STRONG&gt;IX_CustomerTransactions_ClientId&lt;/STRONG&gt; when a statistics object corresponds to an index, its stats_id matches the index's index_id.&lt;/P&gt;
&lt;H3&gt;User-created statistics&lt;/H3&gt;
&lt;P&gt;Statistics can also be created explicitly: &lt;STRONG&gt;CREATE STATISTICS ST_CustomerTransactions_ClientId_Status ON dbo.CustomerTransactions ( ClientId, StatusId ); &lt;/STRONG&gt;This object is identified in &lt;STRONG&gt;sys.stats&lt;/STRONG&gt; with: &lt;STRONG&gt;user_created = 1 auto_created = 0&lt;/STRONG&gt; and &lt;STRONG&gt;AUTO_UPDATE_STATISTICS&lt;/STRONG&gt; applies to index statistics, automatically created single-column statistics, manually created statistics and filtered statistics.&lt;/P&gt;
&lt;H2&gt;2. Statistics are not updated together&lt;/H2&gt;
&lt;P&gt;Assume that a table has these statistics:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;_WA_Sys_00000002_xxxxxxxx for ClientId&amp;nbsp;&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;_WA_Sys_00000003_xxxxxxxx for StatusId&amp;nbsp;&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;IX_CustomerTransactions_CreatedDate for CreatedDate PK_CustomerTransactions and TransactionId&lt;/STRONG&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;After a large data load, several or all of them could become eligible for an automatic update. &lt;STRONG&gt;However, SQL Server does not immediately update all eligible statistics&lt;/STRONG&gt;. Before compiling a query, the Query Optimizer identifies the statistics that could be relevant to the query predicates and checks whether those statistics are outdated.&lt;/P&gt;
&lt;P&gt;Consider this query:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;SELECT TransactionId, ClientId, Amount FROM dbo.CustomerTransactions WHERE ClientId = 100;&lt;/LI-CODE&gt;
&lt;P&gt;The optimizer might need a histogram on ClientId. If the corresponding statistics object is outdated and has crossed its update threshold, SQL Server may update that specific statistics object.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;It does not need to update unrelated statistics on&lt;/STRONG&gt;:&amp;nbsp;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;StatusId&lt;/LI&gt;
&lt;LI&gt;CreatedDate&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;As a result, statistics on the &lt;STRONG&gt;same table can legitimately have different update times&lt;/STRONG&gt;:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;PK_CustomerTransactions 2026-07-22 10:53&lt;/LI&gt;
&lt;LI&gt;IX_CustomerTransactions_ClientId 2026-07-22 10:50&lt;/LI&gt;
&lt;LI&gt;IX_CustomerTransactions_CreatedDate 2026-07-22 10:13&lt;/LI&gt;
&lt;LI&gt;_WA_Sys_00000003_47A6D5E5 2026-07-19 10:00&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This pattern can be evidence of demand-driven automatic updates rather than evidence of a malfunction.&lt;/P&gt;
&lt;H2&gt;3. What does modification_counter represent?&lt;/H2&gt;
&lt;P&gt;The modification_counter returned by &lt;STRONG&gt;sys.dm_db_stats_properties&lt;/STRONG&gt; represents the number of modifications made to the leading statistics column since that statistics object was last updated. This definition is especially important for multicolumn statistics.&lt;/P&gt;
&lt;P&gt;For example: &lt;STRONG&gt;CREATE INDEX IX_CustomerTransactions_ClientId_Status ON dbo.CustomerTransactions ( ClientId, StatusId );&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The associated statistics object contains:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Histogram&lt;/STRONG&gt;: ClientId&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Density information:&lt;/STRONG&gt; ClientId ClientId, StatusId&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The histogram and modification_counter are based on the leading column, ClientId.&lt;/P&gt;
&lt;P&gt;If only StatusId is modified: &lt;STRONG&gt;UPDATE dbo.CustomerTransactions SET StatusId = 2 WHERE TransactionId = 100&lt;/STRONG&gt;; this does not have the same statistics impact as changing ClientId, because ClientId is the leading histogram column.&lt;/P&gt;
&lt;P&gt;SQL Server statistics contain only one histogram, built on the first key column. Multicolumn statistics additionally contain density information for column prefixes.&lt;/P&gt;
&lt;H2&gt;4. Why do multiple statistics sometimes have similar modification counters?&lt;/H2&gt;
&lt;P&gt;This commonly happens after an insert operation.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;INSERT INTO dbo.CustomerTransactions ( TransactionId, ClientId, StatusId, Amount, CreatedDate ) SELECT TransactionId, ClientId, StatusId, Amount, CreatedDate FROM dbo.StagingCustomerTransactions;&lt;/LI-CODE&gt;
&lt;P&gt;Each inserted row introduces a value for every populated column. Consequently, &lt;STRONG&gt;multiple single-column statistics may show similar increases&lt;/STRONG&gt; in their modification counters. This is particularly visible after a large ETL operation: &lt;STRONG&gt;Table rows before the load&lt;/STRONG&gt;: 487,673 &lt;STRONG&gt;Rows inserted by&lt;/STRONG&gt; the ETL: 515,244 &lt;STRONG&gt;Current approximate row count:&lt;/STRONG&gt; 1,002,917&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Several statistics could then show modification counters close to the number of inserted rows.&lt;/STRONG&gt; That does not mean SQL Server must update all those statistics immediately. They become candidates for updating, but an update is normally triggered when query optimization requires them.&lt;/P&gt;
&lt;H2&gt;5. Does automatic updating apply only to _WA_Sys_ statistics?&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;AUTO_UPDATE_STATISTICS applies to:&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Automatically created _WA_Sys statistics&lt;/LI&gt;
&lt;LI&gt;Index statistics Primary-key index statistics&lt;/LI&gt;
&lt;LI&gt;User-created statistics&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;Each statistics object is evaluated independently&lt;/STRONG&gt;. Therefore, &lt;STRONG&gt;SQL Server might update&lt;/STRONG&gt;: PK_CustomerTransactions &lt;STRONG&gt;while leaving this object&lt;/STRONG&gt; unchanged: _WA_Sys_00000003_47A6D5E5&lt;/P&gt;
&lt;P&gt;The reverse is also possible. The behavior depends on which statistics are considered relevant during compilation or cached-plan validation.&lt;/P&gt;
&lt;H2&gt;6. What happens when a _WA_Sys_ statistic and an index statistic cover the same column?&lt;/H2&gt;
&lt;P&gt;This is one of the most interesting scenarios. &lt;STRONG&gt;Assume SQL Server originally created:&lt;/STRONG&gt; _WA_Sys_00000002_xxxxxxxx for ClientId.&lt;/P&gt;
&lt;P&gt;Later, someone creates this index: &lt;STRONG&gt;CREATE INDEX IX_CustomerTransactions_ClientId ON dbo.CustomerTransactions(ClientId);&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The table now has two statistics objects with histograms on ClientId: &lt;STRONG&gt;_WA_Sys_00000002_xxxxxxxx and IX_CustomerTransactions_ClientId&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Conceptually: &lt;STRONG&gt;_WA_Sys statistic Histogram on ClientId Index statistic Histogram on ClientId&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;For a query such as: &lt;STRONG&gt;SELECT * FROM dbo.CustomerTransactions WHERE ClientId = @ClientId&lt;/STRONG&gt;; the optimizer can have more than one potentially relevant statistics object. It may rely on the index statistics object rather than the older _WA_Sys_ object.&lt;/P&gt;
&lt;P&gt;In that case:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;IX_CustomerTransactions_ClientId Updated recently &lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;_WA_Sys_00000002_xxxxxxxx&lt;/STRONG&gt;
&lt;UL&gt;
&lt;LI&gt;Old last_updated value&lt;/LI&gt;
&lt;LI&gt;High modification_counter&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;This does not necessarily mean that automatic statistics updating has failed. It can mean that the _WA_Sys_ statistic has become redundant and has not been required by recent compilations. &lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;However, this should be presented carefully: The Query Optimizer is not publicly documented as always preferring an index statistic over an equivalent _WA_Sys_ statistic.&lt;/P&gt;
&lt;P&gt;The statistics selected can depend on:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;Query.&lt;/LI&gt;
&lt;LI&gt;Predicates.&lt;/LI&gt;
&lt;LI&gt;Available indexes.&lt;/LI&gt;
&lt;LI&gt;Filtered versus unfiltered statistics.&lt;/LI&gt;
&lt;LI&gt;Statistics freshness.&lt;/LI&gt;
&lt;LI&gt;Sampling quality.&lt;/LI&gt;
&lt;LI&gt;Multicolumn density information.&lt;/LI&gt;
&lt;LI&gt;Cardinality Estimator behavior.&lt;/LI&gt;
&lt;LI&gt;Existing cached plans.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The correct conclusion is that the scenario is possible and plausible, but the execution plan should be inspected before claiming that a specific statistics object was used.&lt;/P&gt;
&lt;P&gt;As we wrote down in multiple articles in our blog (below), identify redudant statistics is part of DBA work to avoid this situation, also, in other situations, I saw that the maintenance plan is taking too much time because we are updating statistics that we are not using or migth be duplicated. The following script identifies statistics whose leading columns overlap:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;DECLARE @TableName sysname = N'dbo.CustomerTransactions'; 
;WITH LeadingStatisticsColumns AS ( SELECT s.object_id, s.stats_id, 
s.name AS statistics_name, 
s.auto_created, s.user_created, 
s.no_recompute, s.has_filter, s.filter_definition, sc.column_id, c.name AS leading_column, 
i.index_id, i.name AS index_name 
FROM sys.stats AS s 
INNER JOIN sys.stats_columns AS sc ON sc.object_id = s.object_id AND sc.stats_id = s.stats_id AND sc.stats_column_id = 1 
INNER JOIN sys.columns AS c ON c.object_id = sc.object_id AND c.column_id = sc.column_id 
LEFT JOIN sys.indexes AS i ON i.object_id = s.object_id AND i.index_id = s.stats_id 
WHERE s.object_id = OBJECT_ID(@TableName) ) 
SELECT leading_column, statistics_name, 
       CASE WHEN index_id IS NOT NULL THEN N'INDEX STATISTICS' WHEN auto_created = 1 THEN N'AUTO-CREATED _WA_SYS' 
	        WHEN user_created = 1 THEN N'USER-CREATED STATISTICS' ELSE N'OTHER' 
			END AS statistics_type, index_name, has_filter, filter_definition, no_recompute, COUNT(*) 
			OVER ( PARTITION BY column_id ) AS statistics_on_same_leading_column 
			FROM LeadingStatisticsColumns 
			ORDER BY leading_column, statistics_type, statistics_name;&lt;/LI-CODE&gt;
&lt;P&gt;This does not automatically mean that one object should be deleted. It only identifies an overlap.&lt;/P&gt;
&lt;H2&gt;7. Script&amp;nbsp;&lt;/H2&gt;
&lt;P&gt;The following example demonstrates how an automatically created statistic can coexist with a later index statistic.&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;DROP TABLE IF EXISTS dbo.CustomerTransactions; 
GO 
CREATE TABLE dbo.CustomerTransactions 
( TransactionId int NOT NULL, ClientId int NOT NULL, StatusId tinyint NOT NULL, Amount decimal(12,2) NOT NULL, CreatedDate datetime2(0) NOT NULL, 
CONSTRAINT PK_CustomerTransactions PRIMARY KEY CLUSTERED (TransactionId) ); 
GO&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;Insert sample data&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;;WITH Numbers AS 
( SELECT TOP (200000) ROW_NUMBER() OVER ( ORDER BY (SELECT NULL) ) AS n 
FROM sys.all_objects AS a 
CROSS JOIN sys.all_objects AS b ) 
INSERT INTO dbo.CustomerTransactions ( TransactionId, ClientId, StatusId, Amount, CreatedDate ) 
SELECT n, n % 5000, n % 5, CONVERT(decimal(12,2), n % 10000), DATEADD ( minute, -(n % 100000), SYSUTCDATETIME() ) 
FROM Numbers; 
GO&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;Ensure that automatic statistics creation and updating are enable&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;ALTER DATABASE CURRENT SET AUTO_CREATE_STATISTICS ON; 
GO 
ALTER DATABASE CURRENT SET AUTO_UPDATE_STATISTICS ON; 
GO&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;Trigger automatic statistics creation on ClientId&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;SELECT COUNT_BIG(*) 
FROM dbo.CustomerTransactions 
WHERE ClientId = 100 OPTION (RECOMPILE); 
GO&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;Check the statistics created for ClientId&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;SELECT s.stats_id, s.name AS statistics_name, s.auto_created, s.user_created, 
c.name AS column_name 
FROM sys.stats AS s 
INNER JOIN sys.stats_columns AS sc ON sc.object_id = s.object_id AND sc.stats_id = s.stats_id 
INNER JOIN sys.columns AS c ON c.object_id = sc.object_id AND c.column_id = sc.column_id 
WHERE s.object_id = OBJECT_ID(N'dbo.CustomerTransactions') 
AND c.name = N'ClientId' 
ORDER BY s.stats_id;&lt;/LI-CODE&gt;&lt;img /&gt;
&lt;PRE&gt;&lt;STRONG&gt;Create an index on the same column&lt;/STRONG&gt;&lt;/PRE&gt;
&lt;LI-CODE lang="sql"&gt;CREATE INDEX IX_CustomerTransactions_ClientId ON dbo.CustomerTransactions(ClientId); 
&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;The table can now have:&lt;/STRONG&gt;&lt;/P&gt;
&lt;img /&gt;&lt;img /&gt;
&lt;PRE&gt;&lt;STRONG&gt;Modify the data significantly&lt;/STRONG&gt;&lt;/PRE&gt;
&lt;LI-CODE lang="sql"&gt;UPDATE dbo.CustomerTransactions SET ClientId = ClientId + 10000 WHERE TransactionId &amp;lt;= 100000;&lt;/LI-CODE&gt;
&lt;P&gt;&lt;STRONG&gt;Review the counters again&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;SELECT s.name AS statistics_name, sp.last_updated, sp.rows, sp.rows_sampled, sp.modification_counter 
FROM sys.stats AS s 
OUTER APPLY sys.dm_db_stats_properties ( s.object_id, s.stats_id ) AS sp 
WHERE s.object_id = OBJECT_ID(N'dbo.CustomerTransactions') ORDER BY s.stats_id&lt;/LI-CODE&gt;&lt;img /&gt;
&lt;P&gt;&lt;STRONG&gt;Force a new compilation using ClientId&lt;/STRONG&gt;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;SET STATISTICS XML ON; 
GO 
SELECT COUNT_BIG(*) FROM dbo.CustomerTransactions WHERE ClientId = 10100 OPTION (RECOMPILE); 
GO 
SET STATISTICS XML OFF; 
GO&lt;/LI-CODE&gt;&lt;img /&gt;
&lt;P&gt;&lt;STRONG&gt;After the query, review:&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;The &lt;STRONG&gt;actual execution plan XML.&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;The &lt;STRONG&gt;StatisticsInfo elements.&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;last_updated.&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;modification_counter.&lt;/STRONG&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The exact object updated is an optimizer decision and can vary by SQL Server version, build, compatibility level and query shape. The test should therefore be used to observe the behavior rather than to assume a fixed preference.&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;Finally, as you could see SQL Server choose &lt;STRONG&gt;_WA_Sys_00000002_151102AD instead of IX_CustomerTransactions_ClientId to updat&lt;/STRONG&gt;e. In some situations, depending on execution plan, SQL Server might choose IX_CustomerTransactions_ClientId to update instead of _WA_Sys_00000002_151102AD and for this reason, doesn't mean that SQL Server is not updating the statistics it is depending that it is choosing one of them that the column is involved.&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;My lessons learned, a statistic can be outdated without being relevant, and it can be relevant without being the only available source of cardinality information. Before interpreting an old last_updated value as an automatic statistics failure, identify the leading column, look for overlapping statistics, inspect the execution plan and determine whether there is a real estimation or performance problem.&lt;/P&gt;
&lt;H3&gt;Articles:&lt;/H3&gt;
&lt;P&gt;&lt;A href="https://techcommunity.microsoft.com/blog/azuredbsupport/lesson-learned-482-identifying-potential-duplicate-statistics/4102435" target="_blank"&gt;Lesson Learned #482: Identifying Potential Duplicate Statistics | Microsoft Community Hub&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://techcommunity.microsoft.com/blog/azuredbsupport/lesson-learned-324-query-recompilation-in-azure-sql/3737417" target="_blank"&gt;Lesson Learned #324: Query Recompilation in Azure SQL | Microsoft Community Hub&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://techcommunity.microsoft.com/blog/azuredbsupport/lessons-learned-537-copilot-prompts-for-troubleshooting-on-azure-sql-database/4463038" target="_blank"&gt;Lessons Learned #537: Copilot Prompts for Troubleshooting on Azure SQL Database | Microsoft Community Hub&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://techcommunity.microsoft.com/blog/azuredbsupport/lesson-learned-498understanding-the-role-of-statman-in-sql-server-and-its-resour/4156891" target="_blank"&gt;Lesson Learned #498:Understanding the Role of STATMAN in SQL Server and Its Resource Consumption | Microsoft Community Hub&lt;/A&gt;&lt;/P&gt;
&lt;H3&gt;Disclaimer:&lt;/H3&gt;
&lt;P&gt;The scripts included in this article are provided for demonstration and educational purposes only. They create a sample table, insert a significant number of rows, create indexes and statistics, modify data, and change automatic statistics settings in the current database.&lt;/P&gt;
&lt;P&gt;Run the complete demonstration only in a test or non-production environment. Review and adapt the database name, object names, row volume, and statements before execution.&lt;/P&gt;
&lt;P&gt;The results may vary depending on the SQL Server version, database compatibility level, existing configuration, data distribution, and workload. Always test the scripts in a representative environment before applying any conclusion or change to a production system.&lt;/P&gt;</description>
      <pubDate>Thu, 23 Jul 2026 09:47:54 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-547-some-sql-db-statistics-remain-outdated-while/ba-p/4540055</guid>
      <dc:creator>Jose_Manuel_Jurado</dc:creator>
      <dc:date>2026-07-23T09:47:54Z</dc:date>
    </item>
    <item>
      <title>Lessons Learned #546: Maintaining a Local Azure Resource Inventory</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-546-maintaining-a-local-azure-resource-inventory/ba-p/4539233</link>
      <description>&lt;P&gt;I worked on a service request that our customer has an application works repeatedly with the same Azure resources. I guess that it may be useful to maintain our own persistent inventory instead of retrieving and validating every resource individually during each execution.&lt;/P&gt;
&lt;P&gt;In this example, a PowerShell script maintains an inventory of Azure SQL logical servers in a local JSON file.&lt;/P&gt;
&lt;P&gt;The idea is:&amp;nbsp;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Load the local JSON inventory&lt;/STRONG&gt; -&amp;gt; &lt;STRONG&gt;Query Azure Resource Graph&lt;/STRONG&gt; -&amp;gt; &lt;STRONG&gt;Compare both inventories&lt;/STRONG&gt; -&amp;gt; &lt;STRONG&gt;Validate only detected changes&lt;/STRONG&gt; -&amp;gt; &lt;STRONG&gt;Update the JSON file.&lt;/STRONG&gt;&lt;/LI&gt;
&lt;LI&gt;Azure Resource Graph provides the current list of &lt;STRONG&gt;Microsoft.Sql/servers&lt;/STRONG&gt; resources. The result is compared with the inventory stored by the application.&lt;/LI&gt;
&lt;LI&gt;If a server exists in both inventories, it is marked as:&amp;nbsp;
&lt;UL&gt;
&lt;LI&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;&lt;STRONG&gt;Observed&lt;/STRONG&gt;: &lt;/SPAN&gt;No additional request is required.&amp;nbsp;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;NewValidatedByPointGet&lt;/STRONG&gt;:If a new server is detected, it is validated individually with Get-AzSqlServer before being added&amp;nbsp;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;If a previously known server is missing from the current result&lt;/STRONG&gt;, the script also validates it individually.&amp;nbsp;The possible results are:
&lt;UL&gt;
&lt;LI style="list-style-type: none;"&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;RecoveredByPointGet&lt;/STRONG&gt;: the server still exists and remains in the inventory.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Deleted&lt;/STRONG&gt;: the individual request returns &lt;STRONG&gt;ResourceNotFound&lt;/STRONG&gt;, so the server is removed.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;UnknownRetainedFromCache&lt;/STRONG&gt;: the validation is inconclusive, so the previous inventory entry is preserved.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The JSON file therefore represents the application’s active resource inventory and remains available between executions. I think this approach reduces repeated API requests because individual validation is performed only when a resource is new, missing, or has changed.&lt;/P&gt;
&lt;P&gt;I would like to share the PowerShell Script.&lt;/P&gt;
&lt;LI-CODE lang="powershell"&gt;Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

# ------------------------------------------------------------
# Configuration
# ------------------------------------------------------------

$tenantId       = "&amp;lt;tenant-id&amp;gt;"
$subscriptionId = "&amp;lt;subscription-id&amp;gt;"
$resourceGroup  = "&amp;lt;resource-group&amp;gt;"
$cacheFile      = ".\sql-server-inventory.json"

# Required modules:
# Install-Module Az.ResourceGraph -Scope CurrentUser
# Install-Module Az.Sql -Scope CurrentUser

# Connect-AzAccount -Tenant $tenantId

Set-AzContext `
    -Tenant $tenantId `
    -Subscription $subscriptionId `
    -ErrorAction Stop |
    Out-Null

# ------------------------------------------------------------
# Helper functions
# ------------------------------------------------------------

function ConvertTo-NormalizedResourceId {
    param(
        [Parameter(Mandatory)]
        [string]$ResourceId
    )

    return $ResourceId.Trim().TrimEnd("/").ToLowerInvariant()
}

function Test-IsResourceNotFound {
    param(
        [Parameter(Mandatory)]
        [System.Management.Automation.ErrorRecord]$ErrorRecord
    )

    $errorText = @(
        $ErrorRecord.Exception.Message
        $ErrorRecord.ErrorDetails.Message
        $ErrorRecord.FullyQualifiedErrorId
        $ErrorRecord.ToString()
    ) -join " "

    return (
        $errorText -match
        "(?i)(\b404\b|ResourceNotFound|ServerNotInSubscriptionResourceGroup)"
    )
}

function New-InventoryItem {
    param(
        [Parameter(Mandatory)]
        [string]$ResourceId,

        [Parameter(Mandatory)]
        [string]$Name,

        [string]$Location,

        [Parameter(Mandatory)]
        [string]$ValidationStatus
    )

    return [pscustomobject]@{
        id               = $ResourceId
        name             = $Name
        location         = $Location
        validationStatus = $ValidationStatus
    }
}

# ------------------------------------------------------------
# 1. Load the persistent inventory
# ------------------------------------------------------------

if (Test-Path -LiteralPath $cacheFile) {
    $jsonContent = Get-Content `
        -LiteralPath $cacheFile `
        -Raw `
        -ErrorAction Stop

    if ([string]::IsNullOrWhiteSpace($jsonContent)) {
        $cachedServers = @()
    }
    else {
        $cachedServers = @(
            $jsonContent |
                ConvertFrom-Json `
                    -ErrorAction Stop
        )
    }
}
else {
    $cachedServers = @()
}

$cachedServersById = @{}

foreach ($cachedServer in $cachedServers) {
    $resourceId = [string]$cachedServer.id

    if ([string]::IsNullOrWhiteSpace($resourceId)) {
        continue
    }

    $normalizedId = ConvertTo-NormalizedResourceId `
        -ResourceId $resourceId

    $cachedServersById[$normalizedId] = $cachedServer
}

Write-Host "Stored inventory: $($cachedServersById.Count) server(s)"

# ------------------------------------------------------------
# 2. Discover the current resources
# ------------------------------------------------------------

$query = @"
Resources
| where subscriptionId =~ '$subscriptionId'
| where resourceGroup =~ '$resourceGroup'
| where type =~ 'microsoft.sql/servers'
| project
    id = tostring(id),
    name = tostring(name),
    location = tostring(location)
"@

try {
    $argResponse = Search-AzGraph `
        -Query $query `
        -Subscription $subscriptionId `
        -First 1000 `
        -ErrorAction Stop
}
catch {
    throw (
        "Azure Resource Graph query failed. " +
        "The existing inventory has not been modified. " +
        "Error: $($_.Exception.Message)"
    )
}

if (
    $null -ne $argResponse -and
    $argResponse.PSObject.Properties.Name -contains "Data"
) {
    $currentServers = @($argResponse.Data)
}
else {
    $currentServers = @($argResponse)
}

$currentServersById = @{}

foreach ($currentServer in $currentServers) {
    $resourceId = [string]$currentServer.id
    $serverName = [string]$currentServer.name

    if (
        [string]::IsNullOrWhiteSpace($resourceId) -or
        [string]::IsNullOrWhiteSpace($serverName)
    ) {
        continue
    }

    $normalizedId = ConvertTo-NormalizedResourceId `
        -ResourceId $resourceId

    $currentServersById[$normalizedId] = $currentServer
}

Write-Host "Current observation: $($currentServersById.Count) server(s)"

# ------------------------------------------------------------
# 3. Build the synchronized active inventory
# ------------------------------------------------------------

$activeInventoryById = @()
$activeInventoryIndex = @{}
$deletedServers = @()

foreach ($normalizedId in $currentServersById.Keys) {
    $currentServer = $currentServersById[$normalizedId]

    $resourceId = [string]$currentServer.id
    $serverName = [string]$currentServer.name
    $location   = [string]$currentServer.location

    if ($cachedServersById.ContainsKey($normalizedId)) {
        # The resource is present in both inventories.
        $item = New-InventoryItem `
            -ResourceId $resourceId `
            -Name $serverName `
            -Location $location `
            -ValidationStatus "Observed"

        $activeInventoryIndex[$normalizedId] = $item
        continue
    }

    # The resource is new. Validate it individually.
    Write-Host "Validating new server '$serverName'..."

    try {
        $validatedServer = Get-AzSqlServer `
            -ResourceGroupName $resourceGroup `
            -ServerName $serverName `
            -ErrorAction Stop

        $item = New-InventoryItem `
            -ResourceId $resourceId `
            -Name ([string]$validatedServer.ServerName) `
            -Location ([string]$validatedServer.Location) `
            -ValidationStatus "NewValidatedByPointGet"

        $activeInventoryIndex[$normalizedId] = $item
    }
    catch {
        Write-Warning (
            "New server '$serverName' could not be validated " +
            "and was not added to the inventory. " +
            "Error: $($_.Exception.Message)"
        )
    }
}

# ------------------------------------------------------------
# 4. Validate previously known resources missing from discovery
# ------------------------------------------------------------

foreach ($normalizedId in $cachedServersById.Keys) {
    if ($currentServersById.ContainsKey($normalizedId)) {
        continue
    }

    $cachedServer = $cachedServersById[$normalizedId]

    $resourceId = [string]$cachedServer.id
    $serverName = [string]$cachedServer.name
    $location   = [string]$cachedServer.location

    Write-Host (
        "Server '$serverName' is missing from the current " +
        "observation. Running individual validation..."
    )

    try {
        $validatedServer = Get-AzSqlServer `
            -ResourceGroupName $resourceGroup `
            -ServerName $serverName `
            -ErrorAction Stop

        $item = New-InventoryItem `
            -ResourceId $resourceId `
            -Name ([string]$validatedServer.ServerName) `
            -Location ([string]$validatedServer.Location) `
            -ValidationStatus "RecoveredByPointGet"

        $activeInventoryIndex[$normalizedId] = $item

        Write-Warning (
            "Server '$serverName' was not returned by discovery, " +
            "but individual validation confirmed that it still exists."
        )
    }
    catch {
        if (Test-IsResourceNotFound -ErrorRecord $_) {
            $deletedServers += [pscustomobject]@{
                id               = $resourceId
                name             = $serverName
                location         = $location
                validationStatus = "Deleted"
            }

            Write-Warning (
                "Deleted server detected: '$serverName'. " +
                "It will be removed from the active inventory."
            )
        }
        else {
            # The result is inconclusive. Preserve the previous entry.
            $item = New-InventoryItem `
                -ResourceId $resourceId `
                -Name $serverName `
                -Location $location `
                -ValidationStatus "UnknownRetainedFromCache"

            $activeInventoryIndex[$normalizedId] = $item

            Write-Warning (
                "The status of server '$serverName' could not be " +
                "confirmed. The previous inventory entry was retained. " +
                "Error: $($_.Exception.Message)"
            )
        }
    }
}

# ------------------------------------------------------------
# 5. Save the updated active inventory
# ------------------------------------------------------------

$activeInventory = @(
    $activeInventoryIndex.Values |
        Sort-Object name
)

$jsonOutput = ConvertTo-Json `
    -InputObject $activeInventory `
    -Depth 10

$temporaryFile = "$cacheFile.tmp"

Set-Content `
    -LiteralPath $temporaryFile `
    -Value $jsonOutput `
    -Encoding utf8 `
    -Force

Move-Item `
    -LiteralPath $temporaryFile `
    -Destination $cacheFile `
    -Force

# ------------------------------------------------------------
# 6. Report the synchronization result
# ------------------------------------------------------------

Write-Host ""
Write-Host "Active inventory: $($activeInventory.Count) server(s)"

$activeInventory |
    Format-Table `
        name,
        location,
        validationStatus `
        -AutoSize

if ($deletedServers.Count -gt 0) {
    Write-Host ""
    Write-Warning "Confirmed deleted servers:"

    $deletedServers |
        Format-Table `
            name,
            location,
            validationStatus `
            -AutoSize
}&lt;/LI-CODE&gt;
&lt;H4&gt;Disclaimer&lt;/H4&gt;
&lt;P&gt;This PowerShell script is provided as a simplified proof of concept to illustrate a persistent resource inventory pattern.&lt;/P&gt;
&lt;P&gt;It should be reviewed, tested, and adapted before being used in a production environment. Authentication, permissions, error handling, retry policies, concurrency, logging, inventory storage, and operational requirements may differ between environments.&lt;/P&gt;
&lt;P&gt;The local JSON file is suitable for demonstration purposes and small automation scenarios.&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 21 Jul 2026 09:07:08 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-546-maintaining-a-local-azure-resource-inventory/ba-p/4539233</guid>
      <dc:creator>Jose_Manuel_Jurado</dc:creator>
      <dc:date>2026-07-21T09:07:08Z</dc:date>
    </item>
    <item>
      <title>SQL Server 2016 Reaches End of Support: A Customer Engineer's Perspective on What's Next</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/sql-server-2016-reaches-end-of-support-a-customer-engineer-s/ba-p/4538321</link>
      <description>&lt;img /&gt;
&lt;P&gt;&lt;STRONG&gt;Why This Day Matters&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;July 14, 2026 marks the end of Extended Support for SQL Server 2016. After today, organizations running SQL Server 2016 will no longer receive security updates, non-security hotfixes, or product support unless they have a specific support strategy in place.&lt;/P&gt;
&lt;P&gt;As a Customer Engineer, I've spent years helping customers navigate technology lifecycle events. This one feels particularly significant because SQL Server 2016 remains heavily deployed across enterprise environments.&lt;/P&gt;
&lt;P&gt;For official lifecycle dates and milestones, see the Microsoft Lifecycle Policy for SQL Server 2016:&lt;/P&gt;
&lt;P&gt;Reference:&amp;nbsp;&lt;A href="https://learn.microsoft.com/lifecycle/products/sql-server-2016" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/lifecycle/products/sql-server-2016&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Why SQL Server 2016 Feels Different&lt;/P&gt;
&lt;P&gt;SQL Server 2016 introduced capabilities that many organizations still rely on today, including:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Query Store&lt;/LI&gt;
&lt;LI&gt;Always Encrypted&lt;/LI&gt;
&lt;LI&gt;Native JSON Support&lt;/LI&gt;
&lt;LI&gt;Stretch Database&lt;/LI&gt;
&lt;LI&gt;Enhanced security and performance capabilities&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;For many businesses, SQL Server 2016 became the standard data platform supporting ERP systems, billing applications, healthcare workloads, manufacturing systems, and custom line-of-business applications.&lt;/P&gt;
&lt;P&gt;Ten years is a long time in IT. During that period, databases often evolve from "just another server" into business-critical infrastructure.&lt;/P&gt;
&lt;P&gt;Learn more about SQL Server 2016 features:&lt;/P&gt;
&lt;P&gt;Reference:&amp;nbsp;&lt;A href="https://learn.microsoft.com/sql/sql-server/what-s-new-in-sql-server-2016" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/sql/sql-server/what-s-new-in-sql-server-2016&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;EM&gt;What "End of Support" Really Means&lt;/EM&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;EM&gt;One of the most common misconceptions I hear is:&lt;/EM&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;EM&gt;"Will my SQL Server stop working tomorrow?"&lt;/EM&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;&lt;STRONG&gt;No.&lt;/STRONG&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;The server will continue running. Applications will continue connecting.&lt;/P&gt;
&lt;P&gt;What changes is your risk profile.&lt;/P&gt;
&lt;P&gt;Organizations will no longer receive:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Security updates&lt;/LI&gt;
&lt;LI&gt;Product fixes&lt;/LI&gt;
&lt;LI&gt;Technical support&lt;/LI&gt;
&lt;LI&gt;Regulatory assurance associated with supported software&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Microsoft's official guidance on end-of-support considerations can be found here:&lt;/P&gt;
&lt;P&gt;Reference:&amp;nbsp;&lt;A href="https://learn.microsoft.com/sql/sql-server/end-of-support/sql-server-end-of-support-overview" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/sql/sql-server/end-of-support/sql-server-end-of-support-overview&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Option 1: Upgrade to a Supported SQL Server Version&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;For many organizations, the most straightforward path is upgrading to SQL Server 2022 or SQL Server 2025.&lt;/P&gt;
&lt;P&gt;Benefits include:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Continued support&lt;/LI&gt;
&lt;LI&gt;Latest security protections&lt;/LI&gt;
&lt;LI&gt;Performance improvements&lt;/LI&gt;
&lt;LI&gt;New AI and data platform capabilities&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Before upgrading, review compatibility considerations and perform application testing.&lt;/P&gt;
&lt;P&gt;Reference:&amp;nbsp;&lt;A href="https://learn.microsoft.com/sql/database-engine/install-windows/upgrade-sql-server" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/sql/database-engine/install-windows/upgrade-sql-server&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Reference:&amp;nbsp;&lt;A href="https://learn.microsoft.com/sql/sql-server/sql-server-release-notes" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/sql/sql-server/sql-server-release-notes&lt;/A&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Option 2: Move to Azure SQL Managed Instance&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Azure SQL Managed Instance is often my recommendation when customers want to reduce operational overhead while maintaining high SQL Server compatibility.&lt;/P&gt;
&lt;P&gt;Advantages include:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Automated patching&lt;/LI&gt;
&lt;LI&gt;Built-in high availability&lt;/LI&gt;
&lt;LI&gt;Automated backups&lt;/LI&gt;
&lt;LI&gt;Near full SQL Server compatibility&lt;/LI&gt;
&lt;LI&gt;Reduced infrastructure management&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Learn more:&lt;/P&gt;
&lt;P&gt;Reference:&amp;nbsp;&lt;A href="https://learn.microsoft.com/azure/azure-sql/managed-instance/sql-managed-instance-paas-overview" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/azure/azure-sql/managed-instance/sql-managed-instance-paas-overview&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;BR /&gt;Option 3: Lift and Shift to SQL Server on Azure Virtual Machines&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;For organizations seeking minimal application changes, SQL Server on Azure Virtual Machines can provide a familiar environment while eliminating hardware refresh requirements.&lt;/P&gt;
&lt;P&gt;This option also enables customers to take advantage of Extended Security Updates through Azure while planning broader modernization initiatives.&lt;/P&gt;
&lt;P&gt;Reference:&amp;nbsp;&lt;A href="https://learn.microsoft.com/azure/azure-sql/virtual-machines/windows/sql-server-on-azure-vm-iaas-what-is-overview" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/azure/azure-sql/virtual-machines/windows/sql-server-on-azure-vm-iaas-what-is-overview&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Option 4: Purchase Extended Security Updates (ESUs)&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;If immediate migration is not possible, Extended Security Updates provide up to three additional years of Critical security updates.&lt;/P&gt;
&lt;P&gt;ESUs can be valuable when:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Vendor certification delays upgrades&lt;/LI&gt;
&lt;LI&gt;Hardware refresh projects are underway&lt;/LI&gt;
&lt;LI&gt;Business priorities prevent immediate migration&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;However, ESUs should be viewed as a temporary bridge rather than a long-term strategy.&lt;/P&gt;
&lt;P&gt;Reference:&amp;nbsp;&lt;A href="https://learn.microsoft.com/sql/sql-server/end-of-support/sql-server-extended-security-updates" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/sql/sql-server/end-of-support/sql-server-extended-security-updates&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Don't Skip the Assessment Phase&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The most successful migration projects begin with discovery and assessment.&lt;/P&gt;
&lt;P&gt;Common issues identified include:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Deprecated features&lt;/LI&gt;
&lt;LI&gt;Compatibility concerns&lt;/LI&gt;
&lt;LI&gt;Linked Server dependencies&lt;/LI&gt;
&lt;LI&gt;SQL Agent jobs&lt;/LI&gt;
&lt;LI&gt;CLR integrations&lt;/LI&gt;
&lt;LI&gt;Legacy SSIS and SSRS workloads&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Resources that can help:&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/azure/migrate/migrate-services-overview" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/azure/migrate/migrate-services-overview&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/sql/sql-server/azure-arc/overview" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/sql/sql-server/azure-arc/overview&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/sql/dma/dma-overview" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/sql/dma/dma-overview&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Migration Tools Worth Evaluating&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Different migration scenarios require different tools.&lt;/P&gt;
&lt;P&gt;Microsoft provides several options:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Data Migration Assistant (DMA)&lt;/LI&gt;
&lt;LI&gt;SQLPackage&lt;/LI&gt;
&lt;LI&gt;Azure Database Migration Service&lt;/LI&gt;
&lt;LI&gt;Managed Instance Link&lt;/LI&gt;
&lt;LI&gt;Distributed Availability Groups&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Comprehensive migration guidance:&lt;/P&gt;
&lt;P&gt;&lt;A href="https://techcommunity.microsoft.com/blog/microsoftdatamigration/microsoft-options-to-migrate-sql-server-databases/4407666" target="_blank" rel="noopener"&gt;https://techcommunity.microsoft.com/blog/microsoftdatamigration/microsoft-options-to-migrate-sql-server-databases/4407666&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/azure/dms/dms-overview" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/azure/dms/dms-overview&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;A href="https://learn.microsoft.com/sql/tools/sqlpackage/sqlpackage" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/sql/tools/sqlpackage/sqlpackage&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Lessons Learned from Customer Engagements&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;After supporting numerous SQL Server modernization projects, several themes consistently emerge:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The database is rarely the hardest part.&lt;/LI&gt;
&lt;LI&gt;Applications and dependencies drive complexity.&lt;/LI&gt;
&lt;LI&gt;Downtime requirements significantly influence migration strategy.&lt;/LI&gt;
&lt;LI&gt;Organizations that start 6–9 months early typically experience smoother transitions.&lt;/LI&gt;
&lt;LI&gt;ESUs buy time, but they do not replace a modernization plan.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;For migration planning best practices:&lt;/P&gt;
&lt;P&gt;&lt;A href="https://learn.microsoft.com/azure/cloud-adoption-framework/migrate/" target="_blank" rel="noopener"&gt;https://learn.microsoft.com/azure/cloud-adoption-framework/migrate/&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;Final Thoughts&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;If you're reading this on or shortly after July 14, 2026, and SQL Server 2016 is still running in your environment, you're not alone.&lt;/P&gt;
&lt;P&gt;Whether your path forward is:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;SQL Server 2025&lt;/LI&gt;
&lt;LI&gt;Azure SQL Managed Instance&lt;/LI&gt;
&lt;LI&gt;SQL Server on Azure Virtual Machines&lt;/LI&gt;
&lt;LI&gt;Extended Security Updates&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;the most important step is to begin with an assessment and establish a modernization plan.&lt;/P&gt;
&lt;P&gt;SQL Server 2016 served organizations exceptionally well for nearly a decade. The conversation now is not about the end of SQL Server 2016. It's about preparing your data platform for the next decade.&lt;/P&gt;</description>
      <pubDate>Fri, 17 Jul 2026 16:44:50 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/sql-server-2016-reaches-end-of-support-a-customer-engineer-s/ba-p/4538321</guid>
      <dc:creator>Karunakar_Kotha</dc:creator>
      <dc:date>2026-07-17T16:44:50Z</dc:date>
    </item>
    <item>
      <title>Understanding Microsoft Entra ID Group Membership Caching and Azure SQL Authentication Timing</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/understanding-microsoft-entra-id-group-membership-caching-and/ba-p/4538199</link>
      <description>&lt;H6&gt;Contributor: &lt;a href="javascript:void(0)" data-lia-user-mentions="" data-lia-user-uid="3293058" data-lia-user-login="hudajazmawi" class="lia-mention lia-mention-user"&gt;hudajazmawi​&lt;/a&gt;&amp;nbsp;&lt;/H6&gt;
&lt;H2&gt;Executive Summary&lt;/H2&gt;
&lt;P&gt;Organizations frequently use Microsoft Entra ID groups to manage access to Azure SQL databases. This approach simplifies administration, improves security, and supports just-in-time access models.&lt;/P&gt;
&lt;P&gt;In some scenarios, users may experience temporary authentication failures shortly after being granted access through a Microsoft Entra ID group. These failures can appear inconsistent, especially when access succeeds to one database while failing against another. Understanding how group membership caching works during authentication can help explain this behavior and reduce unnecessary troubleshooting efforts.&lt;/P&gt;
&lt;P&gt;This article explains a real-world scenario involving temporary authentication failures after group assignment, describes the underlying authentication behavior, and provides practical recommendations for validation and mitigation.&lt;/P&gt;
&lt;H2&gt;Issue Description&lt;/H2&gt;
&lt;P&gt;A user was granted access to Azure SQL through membership in a Microsoft Entra ID group. Shortly afterward, the user attempted to connect using Microsoft Entra authentication.&lt;/P&gt;
&lt;P&gt;The observed behavior was:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Authentication to certain databases succeeded immediately.&lt;/LI&gt;
&lt;LI&gt;Authentication to other databases failed temporarily.&lt;/LI&gt;
&lt;LI&gt;The issue appeared shortly after the group membership was granted.&lt;/LI&gt;
&lt;LI&gt;Access eventually began working without any configuration changes.&lt;/LI&gt;
&lt;LI&gt;The behavior resolved after a period of time without additional intervention.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;At first glance, the results appeared inconsistent because some connection attempts were successful while others failed, even though the same user credentials and group assignments were being used.&lt;/P&gt;
&lt;H2&gt;Technical Background&lt;/H2&gt;
&lt;P&gt;Azure SQL supports Microsoft Entra authentication, allowing access to be granted through users, groups, and service principals managed within Microsoft Entra ID.&lt;/P&gt;
&lt;P&gt;When a user authenticates, Azure SQL must determine the user's effective permissions. For users who belong to many Microsoft Entra groups, membership information may be cached to improve authentication efficiency and reduce repeated directory lookups.&lt;/P&gt;
&lt;P&gt;Caching is a common design pattern used throughout distributed systems to improve performance, scalability, and reliability. However, because caches contain information retrieved at a specific point in time, there can be a temporary delay before recently changed security information becomes visible to all authentication requests.&lt;/P&gt;
&lt;P&gt;This behavior is particularly important to understand when organizations use:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Just-in-time access workflows&lt;/LI&gt;
&lt;LI&gt;Privileged access management processes&lt;/LI&gt;
&lt;LI&gt;Temporary group assignments&lt;/LI&gt;
&lt;LI&gt;Automated access provisioning&lt;/LI&gt;
&lt;LI&gt;Frequent permission validation testing&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Root Cause&lt;/H2&gt;
&lt;P&gt;The investigation determined that the authentication failures were caused by Microsoft Entra ID group membership caching.&lt;/P&gt;
&lt;P&gt;A login attempt occurred before the user was added to the required Microsoft Entra ID group.&lt;/P&gt;
&lt;P&gt;During that earlier authentication attempt, the user's group memberships were retrieved and cached.&lt;/P&gt;
&lt;P&gt;After the user was added to the required group, subsequent authentication attempts continued using the previously cached membership information until the cache expired. As a result, authentication requests temporarily evaluated permissions using outdated group membership data.&lt;/P&gt;
&lt;P&gt;Because the newly assigned group membership had not yet been reflected in the cached information, authentication failed even though access had already been granted.&lt;/P&gt;
&lt;P&gt;Once the cached membership information expired and fresh group membership data was retrieved, authentication succeeded without any additional configuration changes.&lt;/P&gt;
&lt;H2&gt;Detailed Explanation&lt;/H2&gt;
&lt;P&gt;To understand the behavior, consider the following simplified sequence:&lt;/P&gt;
&lt;H3&gt;Step 1: Initial Authentication&lt;/H3&gt;
&lt;P&gt;A user attempts to connect to Azure SQL before being added to the required Microsoft Entra ID group.&lt;/P&gt;
&lt;P&gt;During this process:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The user's current group memberships are evaluated.&lt;/LI&gt;
&lt;LI&gt;Membership information is cached.&lt;/LI&gt;
&lt;LI&gt;The required access group is not yet present.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Authentication behavior reflects the permissions available at that moment.&lt;/P&gt;
&lt;H3&gt;Step 2: Group Membership Change&lt;/H3&gt;
&lt;P&gt;The user is added to the appropriate Microsoft Entra ID group.&lt;/P&gt;
&lt;P&gt;From an administrative perspective, the access assignment has been completed successfully.&lt;/P&gt;
&lt;P&gt;However, any previously cached authentication information may still reflect the user's earlier membership state.&lt;/P&gt;
&lt;H3&gt;Step 3: Immediate Retesting&lt;/H3&gt;
&lt;P&gt;The user immediately attempts another connection.&lt;/P&gt;
&lt;P&gt;Although the directory now contains the new group membership, the authentication process may still reference cached membership information created before the change occurred.&lt;/P&gt;
&lt;P&gt;The result can be a temporary authentication failure.&lt;/P&gt;
&lt;H3&gt;Step 4: Cache Expiration&lt;/H3&gt;
&lt;P&gt;After the cached data expires or is refreshed, authentication retrieves updated membership information.&lt;/P&gt;
&lt;P&gt;The newly assigned group is now visible during authorization evaluation.&lt;/P&gt;
&lt;P&gt;At this point, authentication succeeds as expected.&lt;/P&gt;
&lt;H3&gt;Why Some Databases May Behave Differently&lt;/H3&gt;
&lt;P&gt;One of the most confusing aspects of these scenarios is that different databases may appear to behave differently even when they use identical group assignment models.&lt;/P&gt;
&lt;P&gt;This typically occurs because authentication state and cache usage can differ depending on the sequence and timing of connection attempts.&lt;/P&gt;
&lt;P&gt;For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Database A may be accessed for the first time after the group assignment occurs.&lt;/LI&gt;
&lt;LI&gt;Database B may have received a connection attempt before the group assignment occurred.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;As a result:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Database A may evaluate fresh membership information and allow access.&lt;/LI&gt;
&lt;LI&gt;Database B may continue referencing previously cached membership information until the cache expires.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;This can create the appearance of inconsistent behavior even though the system is operating as designed.&lt;/P&gt;
&lt;H2&gt;Mitigation and Recommendations&lt;/H2&gt;
&lt;P&gt;The following practices can help reduce the likelihood of encountering similar authentication timing scenarios.&lt;/P&gt;
&lt;H3&gt;1. Assign Access Before Testing&lt;/H3&gt;
&lt;P&gt;Whenever possible, add users to the required Microsoft Entra ID groups before any authentication attempts are made against Azure SQL resources.&lt;/P&gt;
&lt;P&gt;This helps ensure that fresh membership information is used during the first authentication request.&lt;/P&gt;
&lt;H3&gt;2. Avoid Immediate Validation After Permission Changes&lt;/H3&gt;
&lt;P&gt;If a user has recently been granted group-based access, consider allowing time for authentication cache refresh behavior before conducting validation testing.&lt;/P&gt;
&lt;P&gt;Immediate testing can sometimes produce results based on older membership information.&lt;/P&gt;
&lt;H3&gt;3. Plan for Temporary Authentication Delays&lt;/H3&gt;
&lt;P&gt;Organizations implementing just-in-time access should account for the possibility of short propagation and cache refresh intervals when designing operational procedures.&lt;/P&gt;
&lt;H3&gt;4. Use DBCC FLUSHAUTHCACHE When Appropriate&lt;/H3&gt;
&lt;P&gt;For controlled testing and validation scenarios, administrators may use:&lt;/P&gt;
&lt;P&gt;DBCC FLUSHAUTHCACHE;&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;DBCC FLUSHAUTHCACHE;&lt;/LI-CODE&gt;
&lt;P&gt;This command can help refresh authentication cache behavior during troubleshooting and validation activities.&lt;/P&gt;
&lt;P&gt;As with any administrative operation, testing should be performed according to organizational change-management procedures.&lt;/P&gt;
&lt;H3&gt;5. Capture Precise Timing Information&lt;/H3&gt;
&lt;P&gt;When investigating authentication behavior, collecting exact timestamps is extremely valuable.&lt;/P&gt;
&lt;P&gt;Recommended data points include:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Time the user was added to the Microsoft Entra ID group&lt;/LI&gt;
&lt;LI&gt;Time of each authentication attempt&lt;/LI&gt;
&lt;LI&gt;Database target of each connection attempt&lt;/LI&gt;
&lt;LI&gt;Time any cache refresh operation was performed&lt;/LI&gt;
&lt;LI&gt;Time authentication eventually succeeded&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Accurate timestamps help establish a clear correlation between group membership changes and authentication behavior.&lt;/P&gt;
&lt;H2&gt;Validation Guidance&lt;/H2&gt;
&lt;P&gt;If you need to verify whether group membership caching is influencing authentication results, consider the following approach:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Record the exact time a user is added to the required Microsoft Entra ID group.&lt;/LI&gt;
&lt;LI&gt;Record the time of every authentication attempt.&lt;/LI&gt;
&lt;LI&gt;Identify whether any login attempts occurred before the group membership change.&lt;/LI&gt;
&lt;LI&gt;Observe whether successful authentication occurs after a period of time without configuration changes.&lt;/LI&gt;
&lt;LI&gt;Where appropriate, perform controlled tests using authentication cache refresh procedures.&lt;/LI&gt;
&lt;LI&gt;Compare authentication outcomes against the timeline of group membership updates.&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;This structured approach often helps determine whether the observed behavior is related to authentication caching rather than a permission configuration issue.&lt;/P&gt;
&lt;H2&gt;Key Takeaways&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;Temporary authentication failures immediately after group-based access assignment do not necessarily indicate a configuration problem.&lt;/LI&gt;
&lt;LI&gt;Authentication behavior may be influenced by previously cached Microsoft Entra ID group membership information.&lt;/LI&gt;
&lt;LI&gt;Login attempts that occur before a group membership change can affect subsequent authentication behavior until cached data expires.&lt;/LI&gt;
&lt;LI&gt;Different databases may appear to behave differently if they are accessed at different points in the authentication timeline.&lt;/LI&gt;
&lt;LI&gt;Capturing precise timestamps significantly improves troubleshooting accuracy.&lt;/LI&gt;
&lt;LI&gt;Proper testing practices and awareness of cache behavior can reduce confusion and accelerate issue resolution.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Closing Summary&lt;/H2&gt;
&lt;P&gt;Microsoft Entra ID group-based authorization provides a powerful and scalable way to manage Azure SQL access. However, like many modern cloud authentication systems, caching is used to optimize performance and improve efficiency.&lt;/P&gt;
&lt;P&gt;When group memberships change immediately before authentication testing, temporary differences between cached and current membership information may lead to short-lived authentication failures. Understanding this behavior can help administrators accurately interpret results, design effective validation procedures, and avoid unnecessary troubleshooting.&lt;/P&gt;
&lt;P&gt;By assigning permissions before authentication attempts, allowing appropriate time for cache refresh behavior, and capturing precise timing information during investigations, organizations can more effectively manage Microsoft Entra-based access and streamline their operational workflows.&lt;/P&gt;
&lt;P&gt;As always, when troubleshooting authentication scenarios, focusing on the exact sequence and timing of events often provides the clearest path to identifying the underlying cause and validating a successful resolution.&lt;/P&gt;
&lt;H3&gt;Further Reading&lt;/H3&gt;
&lt;P&gt;To learn more about Microsoft Entra authentication and Azure SQL security, review the following Microsoft documentation:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Microsoft Entra authentication for Azure SQL&lt;/STRONG&gt;&lt;BR /&gt;&lt;SPAN data-wra="1"&gt;&lt;A href="https://learn.microsoft.com/azure/azure-sql/database/authentication-aad-overview" data-tabster="{&amp;quot;restorer&amp;quot;:{&amp;quot;type&amp;quot;:1}}" target="_blank"&gt;https://learn.microsoft.com/azure/azure-sql/database/authentication-aad-overview&lt;/A&gt;&lt;/SPAN&gt;&lt;BR /&gt;Explains how Microsoft Entra authentication works with Azure SQL and the benefits of group-based access management.&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;DBCC FLUSHAUTHCACHE (Transact-SQL)&lt;/STRONG&gt;&lt;BR /&gt;&lt;SPAN data-wra="1"&gt;&lt;A href="https://learn.microsoft.com/sql/t-sql/database-console-commands/dbcc-flushauthcache-transact-sql" data-tabster="{&amp;quot;restorer&amp;quot;:{&amp;quot;type&amp;quot;:1}}" target="_blank"&gt;https://learn.microsoft.com/sql/t-sql/database-console-commands/dbcc-flushauthcache-transact-sql&lt;/A&gt;&lt;/SPAN&gt;&lt;BR /&gt;Describes how to clear the database authentication cache and notes that it clears cached Microsoft Entra group membership data stored in the database.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Fri, 17 Jul 2026 09:50:36 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/understanding-microsoft-entra-id-group-membership-caching-and/ba-p/4538199</guid>
      <dc:creator>Mohamed_Baioumy_MSFT</dc:creator>
      <dc:date>2026-07-17T09:50:36Z</dc:date>
    </item>
    <item>
      <title>Lessons Learned #545:Understanding client_ip = 0.0.0.0 in Azure SQL Auditing</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-545-understanding-client-ip-0-0-0-0-in-azure-sql/ba-p/4537949</link>
      <description>&lt;P&gt;During my analysis, I reproduced the same behavior in a test environment using a client connection to Azure SQL Database through a &lt;A class="lia-external-url" href="https://learn.microsoft.com/en-us/azure/azure-sql/database/vnet-service-endpoint-rule-overview" target="_blank"&gt;&lt;STRONG&gt;Microsoft.Sql Virtual Network Service Endpoint &lt;/STRONG&gt;&lt;/A&gt;. After enabling Azure SQL Auditing and reviewing the original Azure SQL Database Audit file (.xel), the connection was also recorded with client_ip = 0.0.0.0.&lt;/P&gt;
&lt;P&gt;This confirms that 0.0.0.0 can represent a valid client connection using a Service Endpoint and should not automatically be interpreted as internal Azure platform.&lt;/P&gt;
&lt;P&gt;When the original client IP is not exposed, one of the best ways to identify the originating application is to configure a meaningful Application Name property in the SQL connection string:Application Name=Customer-Production;&lt;/P&gt;
&lt;P&gt;Therefore, when reviewing Azure SQL audit records with client_ip = 0.0.0.0, check the original .xel audit file and use the application_name, host_name, authenticated principal, database name, and timestamp to correlate the activity with the correct application.&lt;/P&gt;</description>
      <pubDate>Thu, 16 Jul 2026 17:30:03 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-545-understanding-client-ip-0-0-0-0-in-azure-sql/ba-p/4537949</guid>
      <dc:creator>Jose_Manuel_Jurado</dc:creator>
      <dc:date>2026-07-16T17:30:03Z</dc:date>
    </item>
    <item>
      <title>Lessons Learned #544: How to Detect INT Identity Exhaustion Before Inserts Fail.</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-544-how-to-detect-int-identity-exhaustion-before/ba-p/4536565</link>
      <description>&lt;P&gt;Recently, I worked on a support case involving an Azure SQL Database table where the customer &lt;STRONG&gt;had reached the maximum value supported&lt;/STRONG&gt; by the INT data type. The table used an INT IDENTITY(1,1) column as its primary key. Over time, the generated identity value approached the maximum value supported by INT. Once the available range was exhausted, the application was no longer able to insert new rows.&lt;/P&gt;
&lt;P&gt;At that point, the column needed to be changed from INT to BIGINT. However, performing this type of migration on a very large table can be a complex and time-consuming operation. &lt;STRONG&gt;The situation is that an INT column uses 4 bytes and supports values from: -2,147,483,648 to: 2,147,483,647.&lt;/STRONG&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;To prevent similar problems in the future, I suggested using the following query to review the current status of all INT IDENTITY columns in the database. The query uses the &lt;STRONG&gt;sys.identity_columns&lt;/STRONG&gt; catalog view:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;SELECT
    s.name AS SchemaName,
    t.name AS TableName,
    c.name AS ColumnName,
    CONVERT(bigint, c.last_value) AS CurrentIdentityValue,
    CONVERT(bigint, 2147483647) AS MaximumIntValue,
    CONVERT(bigint, 2147483647) - 
        ISNULL(CONVERT(bigint, c.last_value), 0) AS RemainingValues,
    CAST(
        ISNULL(CONVERT(decimal(20,2), c.last_value), 0)
        / 2147483647 * 100
        AS decimal(6,2)
    ) AS PercentUsed
FROM sys.identity_columns AS c
INNER JOIN sys.tables AS t
    ON c.object_id = t.object_id
INNER JOIN sys.schemas AS s
    ON t.schema_id = s.schema_id
WHERE TYPE_NAME(c.system_type_id) = 'int'
ORDER BY PercentUsed DESC;&lt;/LI-CODE&gt;
&lt;P&gt;I prefer using this query instead of relying on: SELECT COUNT(*) FROM dbo.TableName. &lt;STRONG&gt;The number of rows in a table does not necessarily match the current identity value. Rows might have been deleted, transactions might have been rolled back, and identity values might contain gaps.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;For this reason, the current identity value is a better indicator of the remaining capacity. &lt;STRONG&gt;Adding this query as a regular preventive check can help identify identity columns that are approaching their limits before they cause application failures.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;I would like to share with you an example creates a table with an identity seed close to the maximum value supported by INT:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;DROP TABLE IF EXISTS dbo.IdentityCapacityDemo2; 
CREATE TABLE dbo.IdentityCapacityDemo2 ( Id INT IDENTITY(2147483600,1) NOT NULL, CreatedDate datetime2(0) NOT NULL CONSTRAINT DF_IdentityCapacityDemo2_CreatedDate DEFAULT SYSUTCDATETIME(), CONSTRAINT PK_IdentityCapacityDemo2 PRIMARY KEY CLUSTERED (Id) )&lt;/LI-CODE&gt;
&lt;P&gt;Insert 20 rows using this command:&lt;/P&gt;
&lt;LI-CODE lang="sql"&gt;insert into IdentityCapacityDemo2(CreatedDate) values(SYSUTCDATETIME())&lt;/LI-CODE&gt;
&lt;P&gt;Example of returns:&lt;/P&gt;
&lt;img /&gt;
&lt;P&gt;I think runnning this preventive check can help detect identity exhaustion before it affects the application.&lt;/P&gt;</description>
      <pubDate>Mon, 13 Jul 2026 17:28:24 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-544-how-to-detect-int-identity-exhaustion-before/ba-p/4536565</guid>
      <dc:creator>Jose_Manuel_Jurado</dc:creator>
      <dc:date>2026-07-13T17:28:24Z</dc:date>
    </item>
    <item>
      <title>Lessons Learned #543: Evaluating MultiSubnetFailover with Azure SQL Database</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-543-evaluating-multisubnetfailover-with-azure/ba-p/4536554</link>
      <description>&lt;P&gt;Last week, I worked on a support case in which the use of the &lt;STRONG&gt;MultiSubnetFailover connection-string&lt;/STRONG&gt; feature was being considered for an application connecting to Azure SQL Database.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;The expectation was that enabling the following option could improve connection recovery during a database failover changing MultiSubnetFailover to True.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;This option is commonly associated with SQL Server high availability, and Azure SQL Database is also designed to remain available by moving databases between replicas when required. However, after reviewing the Azure SQL Database connectivity architecture and comparing the behavior with the property enabled and disabled, I did not observe a clear improvement.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;The property could be added to the connection string without generating an error, and the application was able to connect successfully with both configurations.&lt;/P&gt;
&lt;H1&gt;What MultiSubnetFailover is designed for&lt;/H1&gt;
&lt;P&gt;MultiSubnetFailover was introduced primarily for SQL Server high-availability configurations such as:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;Always On Availability Group listeners.&lt;/LI&gt;
&lt;LI&gt;SQL Server Failover Cluster Instance virtual network names.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;In a multi-subnet Availability Group, a listener name may resolve to multiple IP addresses located in different network subnets.&lt;/P&gt;
&lt;P&gt;Without &lt;STRONG&gt;MultiSubnetFailover=True&lt;/STRONG&gt;, the application may try those addresses sequentially. If the first address is not currently active, the connection can be delayed while the attempt waits for a timeout. When the option is enabled, supported SQL client drivers can attempt connections to the listener addresses in parallel and use the first address that responds successfully. This can reduce connection time after an Availability Group failover because the SQL client is directly involved in selecting the reachable listener address.&lt;/P&gt;
&lt;H1&gt;Why Azure SQL Database is different&lt;/H1&gt;
&lt;P&gt;Azure SQL Database uses a different connectivity architecture. The application connects to a logical server endpoint: &amp;lt;server-name&amp;gt;.database.windows.net. The Azure SQL connectivity layer receives the connection and routes it to the infrastructure currently hosting the database.&lt;/P&gt;
&lt;P&gt;Depending on the configured connection policy, the Azure SQL gateway &lt;STRONG&gt;either proxies the connection or redirects &lt;/STRONG&gt;the application to the appropriate database node. The important difference is that the SQL client does not receive a list containing the IP addresses of the Azure SQL Database primary and secondary replicas. The decision and the associated routing are managed by the Azure SQL Database platform.&lt;/P&gt;
&lt;P&gt;Although Azure SQL Database internally uses multiple replicas for high availability, this is not the same connectivity model as a SQL Server Availability Group listener that publishes multiple addresses through DNS.&lt;/P&gt;
&lt;H1&gt;What about Failover Groups?&lt;/H1&gt;
&lt;P&gt;Azure SQL Database Failover Groups provide a stable listener endpoint such as: &amp;lt;failover-group-name&amp;gt;.database.windows.net. Following a regional failover, &lt;STRONG&gt;the listener is updated so that it points to the logical server hosting the new primary databases&lt;/STRONG&gt;. This process depends partly on DNS. The listener name remains the same, but its DNS target changes after the failover.&lt;/P&gt;
&lt;P&gt;This is still different from a SQL Server multi-subnet Availability Group listener. The Failover Group listener does not expose the addresses of the Azure SQL Database replicas to the SQL client. Therefore, MultiSubnetFailover=True cannot directly select the new primary replica.&lt;/P&gt;
&lt;P&gt;In this scenario, &lt;STRONG&gt;application recovery continues to depend on the service transition, DNS resolution, and retry behavior.&lt;/STRONG&gt;&lt;/P&gt;
&lt;H1&gt;The importance of retry logic&lt;/H1&gt;
&lt;P&gt;One of the main lessons from this case was that retry logic is more relevant to Azure SQL Database resiliency than enabling MultiSubnetFailover. An application connecting to Azure SQL Database must expect occasional transient connectivity errors. These can occur during maintenance, scaling, failover, network interruptions, or temporary service conditions.&lt;/P&gt;
&lt;P&gt;An appropriate retry strategy should normally include:&lt;/P&gt;
&lt;UL data-spread="false"&gt;
&lt;LI&gt;A &lt;STRONG&gt;limited number&lt;/STRONG&gt; of &lt;STRONG&gt;retry attempts&lt;/STRONG&gt;.&lt;/LI&gt;
&lt;LI&gt;A &lt;STRONG&gt;short delay before the first retry&lt;/STRONG&gt;.&lt;/LI&gt;
&lt;LI&gt;Increasing &lt;STRONG&gt;delays between subsequent&lt;/STRONG&gt; attempts.&lt;/LI&gt;
&lt;LI&gt;A maximum &lt;STRONG&gt;retry interval&lt;/STRONG&gt;.&lt;/LI&gt;
&lt;LI&gt;Creation of a fresh SQL connection.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN style="color: rgb(30, 30, 30);"&gt;&lt;STRONG&gt;For transactions, retry logic requires additional care.&lt;/STRONG&gt; The application must determine whether the transaction was committed, rolled back, or left in an unknown state before repeating the complete operation.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;</description>
      <pubDate>Mon, 13 Jul 2026 16:34:02 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/lessons-learned-543-evaluating-multisubnetfailover-with-azure/ba-p/4536554</guid>
      <dc:creator>Jose_Manuel_Jurado</dc:creator>
      <dc:date>2026-07-13T16:34:02Z</dc:date>
    </item>
    <item>
      <title>Understanding % Characters in Azure Blob File Names Using SQL OPENROWSET (BULK)</title>
      <link>https://techcommunity.microsoft.com/t5/azure-database-support-blog/understanding-characters-in-azure-blob-file-names-using-sql/ba-p/4532593</link>
      <description>&lt;MAIN class="page"&gt;&lt;HEADER&gt;&lt;/HEADER&gt;
&lt;SECTION class="callout"&gt;&lt;/SECTION&gt;
&lt;SECTION class="callout warning"&gt;&lt;/SECTION&gt;
&lt;H2&gt;Why the % character is different&lt;/H2&gt;
&lt;P&gt;In URL percent-encoding, the &lt;CODE&gt;%&lt;/CODE&gt; character starts an encoded sequence. For example:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;CODE&gt;%20&lt;/CODE&gt; represents a space.&lt;/LI&gt;
&lt;LI&gt;&lt;CODE&gt;%25&lt;/CODE&gt; represents a literal &lt;CODE&gt;%&lt;/CODE&gt; character.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Because of this, file names that contain a literal &lt;CODE&gt;%&lt;/CODE&gt; can behave differently from file names containing characters such as &lt;CODE&gt;#&lt;/CODE&gt;, &lt;CODE&gt;&amp;amp;&lt;/CODE&gt;, &lt;CODE&gt;+&lt;/CODE&gt;, &lt;CODE&gt;;&lt;/CODE&gt;, &lt;CODE&gt;=&lt;/CODE&gt;, or &lt;CODE&gt;@&lt;/CODE&gt;, depending on how the path is processed before blob lookup.&lt;/P&gt;
&lt;H2&gt;Observed symptom&lt;/H2&gt;
&lt;P&gt;The customer reported an import failure for a file name similar to:&lt;/P&gt;
&lt;PRE&gt;&lt;CODE&gt;Company Data - Company Data - éêçëЮй_%25$£#.csv&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The error message started at the &lt;CODE&gt;%&lt;/CODE&gt; sequence instead of showing the full file name:&lt;/P&gt;
&lt;PRE&gt;&lt;CODE&gt;Invalid format specification: '%25$#.45752234198410731.csv.processed' cannot be opened&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This was a useful clue because the reported path appeared to begin at the first &lt;CODE&gt;%&lt;/CODE&gt; sequence, while the preceding portion of the file name was not present in the error text.&lt;/P&gt;
&lt;H2&gt;Test setup&lt;/H2&gt;
&lt;P&gt;The examples below use an external data source pointing to Azure Blob Storage. Replace all placeholders with values from the test environment. Sensitive values such as storage account names, container names, credential names, and SAS tokens should be masked before sharing.&lt;/P&gt;
&lt;PRE&gt;&lt;CODE&gt;CREATE DATABASE SCOPED CREDENTIAL [&amp;lt;credential_name&amp;gt;]
WITH IDENTITY = 'SHARED ACCESS SIGNATURE',
SECRET = '&amp;lt;SAS_TOKEN&amp;gt;';

CREATE EXTERNAL DATA SOURCE [MyBlobStorageSource]
WITH (
    TYPE = BLOB_STORAGE,
    LOCATION = 'https://&amp;lt;storage_account&amp;gt;.blob.core.windows.net/&amp;lt;container&amp;gt;',
    CREDENTIAL = [&amp;lt;credential_name&amp;gt;]
);&lt;/CODE&gt;&lt;/PRE&gt;
&lt;H2&gt;Reproduction results&lt;/H2&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table border="1" style="border-width: 1px;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Scenario&lt;/th&gt;&lt;th&gt;Blob name / character&lt;/th&gt;&lt;th&gt;OPENROWSET path&lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;th&gt;Observation&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Space in file name&lt;/td&gt;&lt;td&gt;&lt;CODE&gt;Test File.pdf&lt;/CODE&gt;&lt;/td&gt;&lt;td&gt;&lt;CODE&gt;BULK 'Test File.pdf'&lt;/CODE&gt;&lt;/td&gt;&lt;td class="result-ok"&gt;Succeeded&lt;/td&gt;&lt;td&gt;Spaces alone did not reproduce the failure.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Literal &lt;CODE&gt;%&lt;/CODE&gt; in file name&lt;/td&gt;&lt;td&gt;&lt;CODE&gt;Test%File.pdf&lt;/CODE&gt;&lt;/td&gt;&lt;td&gt;&lt;CODE&gt;BULK 'Test%File.pdf'&lt;/CODE&gt;&lt;/td&gt;&lt;td class="result-fail"&gt;Failed&lt;/td&gt;&lt;td&gt;The file could not be opened when &lt;CODE&gt;%&lt;/CODE&gt; was passed literally.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Literal &lt;CODE&gt;%&lt;/CODE&gt; encoded as &lt;CODE&gt;%25&lt;/CODE&gt;&lt;/td&gt;&lt;td&gt;&lt;CODE&gt;Test%File.pdf&lt;/CODE&gt;&lt;/td&gt;&lt;td&gt;&lt;CODE&gt;BULK 'Test%25File.pdf'&lt;/CODE&gt;&lt;/td&gt;&lt;td class="result-ok"&gt;Succeeded&lt;/td&gt;&lt;td&gt;Encoding &lt;CODE&gt;%&lt;/CODE&gt; as &lt;CODE&gt;%25&lt;/CODE&gt; allowed the same blob to be accessed.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Literal string &lt;CODE&gt;%25&lt;/CODE&gt; in file name&lt;/td&gt;&lt;td&gt;&lt;CODE&gt;Test%25File.pdf&lt;/CODE&gt;&lt;/td&gt;&lt;td&gt;&lt;CODE&gt;BULK 'Test%2525File.pdf'&lt;/CODE&gt;&lt;/td&gt;&lt;td class="result-ok"&gt;Succeeded&lt;/td&gt;&lt;td&gt;The observed result is consistent with one percent-decoding pass during path resolution.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Other special characters&lt;/td&gt;&lt;td&gt;&lt;CODE&gt;#&lt;/CODE&gt;, &lt;CODE&gt;&amp;amp;&lt;/CODE&gt;, &lt;CODE&gt;+&lt;/CODE&gt;, &lt;CODE&gt;;&lt;/CODE&gt;, &lt;CODE&gt;=&lt;/CODE&gt;, &lt;CODE&gt;@&lt;/CODE&gt;&lt;/td&gt;&lt;td&gt;Tested individually&lt;/td&gt;&lt;td class="result-ok"&gt;Succeeded&lt;/td&gt;&lt;td&gt;These characters did not reproduce the same behavior in the tests performed.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;col style="width: 20.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;H2&gt;Commands used for testing&lt;/H2&gt;
&lt;H3&gt;1. Space in the file name&lt;/H3&gt;
&lt;PRE&gt;&lt;CODE&gt;INSERT INTO testfiles(pdfData)
SELECT BulkColumn
FROM OPENROWSET(
    BULK 'Test File.pdf',
    DATA_SOURCE = 'MyBlobStorageSource',
    SINGLE_BLOB
) AS PdfFile;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;H3&gt;2. Literal % in the file name&lt;/H3&gt;
&lt;PRE&gt;&lt;CODE&gt;INSERT INTO testfiles(pdfData)
SELECT BulkColumn
FROM OPENROWSET(
    BULK 'Test%File.pdf',
    DATA_SOURCE = 'MyBlobStorageSource',
    SINGLE_BLOB
) AS PdfFile;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Observed error:&lt;/P&gt;
&lt;PRE&gt;&lt;CODE&gt;Msg 13822, Level 16, State 1
File 'Test%File.pdf' cannot be opened because it does not exist or it is used by another process.&lt;/CODE&gt;&lt;/PRE&gt;
&lt;H3&gt;3. URL-encoded % character&lt;/H3&gt;
&lt;PRE&gt;&lt;CODE&gt;INSERT INTO testfiles(pdfData)
SELECT BulkColumn
FROM OPENROWSET(
    BULK 'Test%25File.pdf',
    DATA_SOURCE = 'MyBlobStorageSource',
    SINGLE_BLOB
) AS PdfFile;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;H3&gt;4. Literal %25 in the blob name&lt;/H3&gt;
&lt;PRE&gt;&lt;CODE&gt;INSERT INTO testfiles(pdfData)
SELECT BulkColumn
FROM OPENROWSET(
    BULK 'Test%2525File.pdf',
    DATA_SOURCE = 'MyBlobStorageSource',
    SINGLE_BLOB
) AS PdfFile;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;H2&gt;Interpretation&lt;/H2&gt;
&lt;P&gt;The tests suggest that the path provided to &lt;CODE&gt;OPENROWSET (BULK)&lt;/CODE&gt; is processed in a way that is consistent with URL-style percent-encoding rules.&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;A literal &lt;CODE&gt;%&lt;/CODE&gt; character in a blob name may need to be referenced as &lt;CODE&gt;%25&lt;/CODE&gt; in the &lt;CODE&gt;BULK&lt;/CODE&gt; path.&lt;/LI&gt;
&lt;LI&gt;If the actual blob name contains the literal characters &lt;CODE&gt;%25&lt;/CODE&gt;, the &lt;CODE&gt;BULK&lt;/CODE&gt; path may need to use &lt;CODE&gt;%2525&lt;/CODE&gt; so that the observed path-resolution behavior resolves it back to &lt;CODE&gt;%25&lt;/CODE&gt;.&lt;/LI&gt;
&lt;LI&gt;Spaces and the tested characters &lt;CODE&gt;#&lt;/CODE&gt;, &lt;CODE&gt;&amp;amp;&lt;/CODE&gt;, &lt;CODE&gt;+&lt;/CODE&gt;, &lt;CODE&gt;;&lt;/CODE&gt;, &lt;CODE&gt;=&lt;/CODE&gt;, and &lt;CODE&gt;@&lt;/CODE&gt; did not reproduce the same behavior in these tests.&lt;/LI&gt;
&lt;LI&gt;The customer error beginning at the &lt;CODE&gt;%25&lt;/CODE&gt; sequence is consistent with the file name being interpreted or transformed when the &lt;CODE&gt;%&lt;/CODE&gt; sequence is encountered.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H2&gt;Practical guidance&lt;/H2&gt;
&lt;DIV class="styles_lia-table-wrapper__h6Xo9 styles_table-responsive__MW0lN"&gt;&lt;table class="guidance-table" border="1" style="width: 97.2222%; height: 410.985px; border-width: 1px;"&gt;&lt;thead&gt;&lt;tr style="height: 35px;"&gt;&lt;th style="height: 35px;"&gt;Recommended handling for &lt;CODE&gt;%&lt;/CODE&gt; characters in blob names&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr style="height: 91.797px;"&gt;&lt;td style="height: 91.797px;"&gt;
&lt;P&gt;&lt;U&gt;Main recommendation&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;Avoid passing blob names containing a literal % directly to OPENROWSET (BULK).&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 91.1719px;"&gt;&lt;td style="height: 91.1719px;"&gt;
&lt;P&gt;&lt;U&gt;When the blob name contains &lt;CODE&gt;%&lt;/CODE&gt;&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;URL-encode the character before generating the BULK path. For example, test referencing a literal % as %25.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 97.1719px;"&gt;&lt;td style="height: 97.1719px;"&gt;
&lt;P&gt;&lt;U&gt;When the blob name contains literal &lt;CODE&gt;%25&lt;/CODE&gt;&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;Test whether the path needs to use %2525. In the observed tests, this was consistent with one percent-decoding pass during path resolution.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;tr style="height: 95.8438px;"&gt;&lt;td style="height: 95.8438px;"&gt;
&lt;P&gt;&lt;U&gt;Application or stored procedure checks&lt;/U&gt;&lt;/P&gt;
&lt;P&gt;Confirm the import process is not decoding, encoding, or re-encoding the file name multiple times before invoking OPENROWSET.&lt;/P&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;colgroup&gt;&lt;col style="width: 100.00%" /&gt;&lt;/colgroup&gt;&lt;/table&gt;&lt;/DIV&gt;
&lt;H2&gt;Optional PowerShell test using Invoke-Sqlcmd&lt;/H2&gt;
&lt;P&gt;If testing outside SSMS, &lt;CODE&gt;Invoke-Sqlcmd&lt;/CODE&gt; can be used with an Entra ID access token. The SAS token is not needed in the command if the database scoped credential already exists in SQL.&lt;/P&gt;
&lt;PRE&gt;&lt;CODE&gt;Connect-AzAccount

$query = @"
INSERT INTO testfiles(pdfData)
SELECT BulkColumn
FROM OPENROWSET(
    BULK 'Test%25File.pdf',
    DATA_SOURCE = 'MyBlobStorageSource',
    SINGLE_BLOB
) AS PdfFile;
"@

Invoke-Sqlcmd `
    -ServerInstance '&amp;lt;managed-instance-fqdn&amp;gt;,3342' `
    -Database '&amp;lt;database_name&amp;gt;' `
    -AccessToken (Get-AzAccessToken -ResourceUrl 'https://database.windows.net/').Token `
    -Query $query `
    -Verbose `
    -ErrorAction Stop&lt;/CODE&gt;&lt;/PRE&gt;
&lt;H2&gt;Conclusion&lt;/H2&gt;
&lt;P&gt;The controlled tests indicate that the import behavior is related to how &lt;CODE&gt;%&lt;/CODE&gt; is handled in the file path supplied to SQL &lt;CODE&gt;OPENROWSET (BULK)&lt;/CODE&gt;. The observed results are consistent with percent-decoding during path resolution: &lt;CODE&gt;%25&lt;/CODE&gt; behaves like a literal &lt;CODE&gt;%&lt;/CODE&gt;, and &lt;CODE&gt;%2525&lt;/CODE&gt; behaves like a literal &lt;CODE&gt;%25&lt;/CODE&gt;.&lt;/P&gt;
&lt;P&gt;Based on these tests, this should not be described as a general limitation with spaces, Unicode characters, or common special characters in blob names. The evidence points specifically to handling of the &lt;CODE&gt;%&lt;/CODE&gt; character and percent-encoded sequences in the path passed to &lt;CODE&gt;OPENROWSET (BULK)&lt;/CODE&gt;.&lt;/P&gt;
&lt;/MAIN&gt;</description>
      <pubDate>Thu, 02 Jul 2026 16:22:14 GMT</pubDate>
      <guid>https://techcommunity.microsoft.com/t5/azure-database-support-blog/understanding-characters-in-azure-blob-file-names-using-sql/ba-p/4532593</guid>
      <dc:creator>Thamires_Lemes</dc:creator>
      <dc:date>2026-07-02T16:22:14Z</dc:date>
    </item>
  </channel>
</rss>

