--- title: "PowerShell: Sync between multiple Azure SQL databases" description: Azure PowerShell example script to sync between multiple Azure SQL Database services: sql-database ms.service: sql-database ms.subservice: data-movement ms.custom: sqldbrb=1 ms.devlang: PowerShell ms.topic: sample author: stevestein ms.author: sstein ms.reviewer: carlrab ms.date: 03/12/2019 --- # Use PowerShell to sync between multiple SQL Databases This PowerShell example configures SQL Data Sync to sync between multiple Azure SQL databases. [!INCLUDE [quickstarts-free-trial-note](../../../../includes/quickstarts-free-trial-note.md)] [!INCLUDE [updated-for-az](../../../../includes/updated-for-az.md)] [!INCLUDE [cloud-shell-try-it.md](../../../../includes/cloud-shell-try-it.md)] If you choose to install and use the PowerShell locally, this tutorial requires AZ PowerShell 1.4.0 or later. If you need to upgrade, see [Install Azure PowerShell module](/powershell/azure/install-az-ps). If you are running PowerShell locally, you also need to run `Connect-AzAccount` to create a connection with Azure. For an overview of SQL Data Sync, see [Sync data across multiple cloud and on-premises databases with SQL Data Sync in Azure](../../../sql-database/sql-database-sync-data.md). > [!IMPORTANT] > SQL Data Sync does not support Azure SQL Managed Instance at this time. ## Prerequisites - Create an Azure SQL Database from AdventureWorksLT sample database as hub database - Create an Azure SQL Database in the same region as sync database - Create an on premises SQL Server database as member database - Update the parameter placeholders before running the example ## Example ```powershell-interactive using namespace Microsoft.Azure.Commands.Sql.DataSync.Model using namespace System.Collections.Generic # hub database info $subscriptionId = "" $resourceGroupName = "" $serverName = "" $databaseName = "" # sync database info $syncDatabaseResourceGroupName = "" $syncDatabaseServerName = "" $syncDatabaseName = "" # sync group info $syncGroupName = "" $conflictResolutionPolicy = "HubWin" # can be HubWin or MemberWin $intervalInSeconds = 300 # sync interval in seconds (must be no less than 300) # member database info $syncMemberName = "" $memberServerName = "" $memberDatabaseName = "" $memberDatabaseType = "SqlServerDatabase" # can be AzureSqlDatabase or SqlServerDatabase $syncDirection = "Bidirectional" # can be Bidirectional, Onewaymembertohub, Onewayhubtomember # sync agent info $syncAgentName = "" $syncAgentResourceGroupName = "" $syncAgentServerName = "" # temp file to save the sync schema $tempFile = $env:TEMP+"\syncSchema.json" # list of included columns and tables in quoted name $includedColumnsAndTables = "[SalesLT].[Address].[AddressID]", "[SalesLT].[Address].[AddressLine2]", "[SalesLT].[Address].[rowguid]", "[SalesLT].[Address].[PostalCode]", "[SalesLT].[ProductDescription]" $metadataList = [System.Collections.ArrayList]::new($includedColumnsAndTables) Connect-AzAccount Select-AzSubscription -SubscriptionId $subscriptionId # use if it's safe to show password in script, otherwise use PromptForCredential # $user = "username" # $password = ConvertTo-SecureString -String "password" -AsPlainText -Force # $credential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $user, $password $credential = $Host.ui.PromptForCredential("Need credential", "Please enter your user name and password for server "+$serverName+".database.windows.net", "", "") # create a new sync group Write-Host "Creating Sync Group "$syncGroupName"..." New-AzSqlSyncGroup -ResourceGroupName $resourceGroupName -ServerName $serverName -DatabaseName $databaseName -Name $syncGroupName ` -SyncDatabaseName $syncDatabaseName -SyncDatabaseServerName $syncDatabaseServerName -SyncDatabaseResourceGroupName $syncDatabaseResourceGroupName ` -ConflictResolutionPolicy $conflictResolutionPolicy -DatabaseCredential $credential # use if it's safe to show password in script, otherwise use PromptForCredential # $user = "username" # $password = ConvertTo-SecureString -String "password" -AsPlainText -Force # $credential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $user, $password $credential = $Host.ui.PromptForCredential("Need credential", "Please enter your user name and password for server "+$serverName+".database.windows.net", "", "") # add a new sync member Write-Host "Adding member"$syncMemberName" to the sync group..." New-AzSqlSyncMember -ResourceGroupName $resourceGroupName -ServerName $serverName -DatabaseName $databaseName ` -SyncGroupName $syncGroupName -Name $syncMemberName -MemberDatabaseType $memberDatabaseType -SyncAgentResourceGroupName $syncAgentResourceGroupName ` -SyncAgentServerName $syncAgentServerName -SyncAgentName $syncAgentName -SyncDirection $syncDirection -SqlServerDatabaseID $syncAgentInfo.DatabaseId # refresh database schema from hub database, specify the -SyncMemberName parameter if you want to refresh schema from the member database Write-Host "Refreshing database schema from hub database..." $startTime = Get-Date Update-AzSqlSyncSchema -ResourceGroupName $resourceGroupName -ServerName $serverName -DatabaseName $databaseName -SyncGroupName $syncGroupName # waiting for successful refresh $startTime = $startTime.ToUniversalTime() $timer=0 $timeout=90 # check the log and see if refresh has gone through Write-Host "Check for successful refresh..." $isSucceeded = $false while ($isSucceeded -eq $false) { Start-Sleep -s 10 $timer=$timer+10 $details = Get-AzSqlSyncSchema -SyncGroupName $syncGroupName -ServerName $serverName -DatabaseName $databaseName -ResourceGroupName $resourceGroupName if ($details.LastUpdateTime -gt $startTime) { Write-Host "Refresh was successful" $isSucceeded = $true } if ($timer -eq $timeout) { Write-Host "Refresh timed out" break; } } # get the database schema Write-Host "Adding tables and columns to the sync schema..." $databaseSchema = Get-AzSqlSyncSchema -ResourceGroupName $ResourceGroupName -ServerName $ServerName ` -DatabaseName $DatabaseName -SyncGroupName $SyncGroupName ` $databaseSchema | ConvertTo-Json -depth 5 -Compress | Out-File "C:\Users\OnPremiseServer\AppData\Local\Temp\syncSchema.json" $newSchema = [AzureSqlSyncGroupSchemaModel]::new() $newSchema.Tables = [List[AzureSqlSyncGroupSchemaTableModel]]::new(); # add columns and tables to the sync schema foreach ($tableSchema in $databaseSchema.Tables) { $newTableSchema = [AzureSqlSyncGroupSchemaTableModel]::new() $newTableSchema.QuotedName = $tableSchema.QuotedName $newTableSchema.Columns = [List[AzureSqlSyncGroupSchemaColumnModel]]::new(); $addAllColumns = $false if ($MetadataList.Contains($tableSchema.QuotedName)) { if ($tableSchema.HasError) { $fullTableName = $tableSchema.QuotedName Write-Host "Can't add table $fullTableName to the sync schema" -foregroundcolor "Red" Write-Host $tableSchema.ErrorId -foregroundcolor "Red" continue; } else { $addAllColumns = $true } } foreach($columnSchema in $tableSchema.Columns) { $fullColumnName = $tableSchema.QuotedName + "." + $columnSchema.QuotedName if ($addAllColumns -or $MetadataList.Contains($fullColumnName)) { if ((-not $addAllColumns) -and $tableSchema.HasError) { Write-Host "Can't add column $fullColumnName to the sync schema" -foregroundcolor "Red" Write-Host $tableSchema.ErrorId -foregroundcolor "Red" } elseif ((-not $addAllColumns) -and $columnSchema.HasError) { Write-Host "Can't add column $fullColumnName to the sync schema" -foregroundcolor "Red" Write-Host $columnSchema.ErrorId -foregroundcolor "Red" } else { Write-Host "Adding"$fullColumnName" to the sync schema" $newColumnSchema = [AzureSqlSyncGroupSchemaColumnModel]::new() $newColumnSchema.QuotedName = $columnSchema.QuotedName $newColumnSchema.DataSize = $columnSchema.DataSize $newColumnSchema.DataType = $columnSchema.DataType $newTableSchema.Columns.Add($newColumnSchema) } } } if ($newTableSchema.Columns.Count -gt 0) { $newSchema.Tables.Add($newTableSchema) } } # convert sync schema to JSON format $schemaString = $newSchema | ConvertTo-Json -depth 5 -Compress # workaround a powershell bug $schemaString = $schemaString.Replace('"Tables"', '"tables"').Replace('"Columns"', '"columns"').Replace('"QuotedName"', '"quotedName"').Replace('"MasterSyncMemberName"','"masterSyncMemberName"') # save the sync schema to a temp file $schemaString | Out-File $tempFile # update sync schema Write-Host "Updating the sync schema..." Update-AzSqlSyncGroup -ResourceGroupName $resourceGroupName -ServerName $serverName ` -DatabaseName $databaseName -Name $syncGroupName -Schema $tempFile $syncLogStartTime = Get-Date # trigger sync manually Write-Host "Trigger sync manually..." Start-AzSqlSyncGroupSync -ResourceGroupName $resourceGroupName -ServerName $serverName -DatabaseName $databaseName -SyncGroupName $syncGroupName # check the sync log and wait until the first sync succeeded Write-Host "Check the sync log..." $isSucceeded = $false for ($i = 0; ($i -lt 300) -and (-not $isSucceeded); $i = $i + 10) { Start-Sleep -s 10 $syncLogEndTime = Get-Date $syncLogList = Get-AzSqlSyncGroupLog -ResourceGroupName $resourceGroupName -ServerName $serverName -DatabaseName $databaseName ` -SyncGroupName $syncGroupName -StartTime $syncLogStartTime.ToUniversalTime() -EndTime $syncLogEndTime.ToUniversalTime() if ($synclogList.Length -gt 0) { foreach ($syncLog in $syncLogList) { if ($syncLog.Details.Contains("Sync completed successfully")) { Write-Host $syncLog.TimeStamp : $syncLog.Details $isSucceeded = $true } } } } if ($isSucceeded) { # enable scheduled sync Write-Host "Enable the scheduled sync with 300 seconds interval..." Update-AzSqlSyncGroup -ResourceGroupName $resourceGroupName -ServerName $serverName -DatabaseName $databaseName ` -Name $syncGroupName -IntervalInSeconds $intervalInSeconds } else { # output all log if sync doesn't succeed in 300 seconds $syncLogEndTime = Get-Date $syncLogList = Get-AzSqlSyncGroupLog -ResourceGroupName $resourceGroupName -ServerName $serverName -DatabaseName $databaseName ` -SyncGroupName $syncGroupName -StartTime $syncLogStartTime.ToUniversalTime() -EndTime $syncLogEndTime.ToUniversalTime() if ($synclogList.Length -gt 0) { foreach ($syncLog in $syncLogList) { Write-Host $syncLog.TimeStamp : $syncLog.Details } } } ``` ## Clean up deployment After you run the sample script, you can run the following command to remove the resource group and all resources associated with it. ```powershell Remove-AzResourceGroup -ResourceGroupName $ResourceGroupName Remove-AzResourceGroup -ResourceGroupName $SyncDatabaseResourceGroupName ``` ## Script explanation This script uses the following commands. Each command in the table links to command-specific documentation. | Command | Notes | |---|---| | [New-AzSqlSyncAgent](/powershell/module/az.sql/New-azSqlSyncAgent) | Creates a new Sync Agent | | [New-AzSqlSyncAgentKey](/powershell/module/az.sql/New-azSqlSyncAgentKey) | Generates the agent key associated with the Sync agent | | [Get-AzSqlSyncAgentLinkedDatabase](/powershell/module/az.sql/Get-azSqlSyncAgentLinkedDatabase) | Get all the information for the Sync Agent | | [New-AzSqlSyncMember](/powershell/module/az.sql/New-azSqlSyncMember) | Add a new member to the Sync Group | | [Update-AzSqlSyncSchema](/powershell/module/az.sql/Update-azSqlSyncSchema) | Refreshes the database schema information | | [Get-AzSqlSyncSchema](https://docs.microsoft.com/powershell/module/az.sql/Get-azSqlSyncSchema) | Get the database schema information | | [Update-AzSqlSyncGroup](/powershell/module/az.sql/Update-azSqlSyncGroup) | Updates the Sync Group | | [Start-AzSqlSyncGroupSync](/powershell/module/az.sql/Start-azSqlSyncGroupSync) | Triggers a Sync | | [Get-AzSqlSyncGroupLog](/powershell/module/az.sql/Get-azSqlSyncGroupLog) | Checks the Sync Log | ||| ## Next steps For more information about Azure PowerShell, see [Azure PowerShell documentation](/powershell/azure/overview). Additional SQL Database PowerShell script samples can be found in [Azure SQL Database PowerShell scripts](../../../sql-database/sql-database-powershell-samples.md). For more info about SQL Data Sync, see: - Overview - [Sync data across multiple cloud and on-premises databases with SQL Data Sync in Azure](../../../sql-database/sql-database-sync-data.md) - Set up Data Sync - In the portal - [Tutorial: Set up SQL Data Sync to sync data between Azure SQL Database and SQL Server on-premises](../data-sync-sql-server-configure.md) - With PowerShell - [Use PowerShell to sync between an Azure SQL Database and a SQL Server on-premises database](sql-data-sync-sync-data-between-azure-onprem.md) - Data Sync Agent - [Data Sync Agent for SQL Data Sync in Azure](../data-sync-agent.md) - Best practices - [Best practices for SQL Data Sync in Azure](../sql-data-sync-best-practices.md) - Monitor - [Monitor SQL Data Sync with Azure Monitor logs](../../../sql-database/sql-database-sync-monitor-oms.md) - Troubleshoot - [Troubleshoot issues with SQL Data Sync in Azure](../../../sql-database/sql-database-troubleshoot-data-sync.md) - Update the sync schema - With Transact-SQL - [Automate the replication of schema changes SQL Data Sync in Azure](../../../sql-database/sql-database-update-sync-schema.md) - With PowerShell - [Use PowerShell to update the sync schema in an existing sync group](update-sync-schema-in-sync-group.md) For more info about SQL Database, see: - [SQL Database Overview](../../../sql-database/sql-database-technical-overview.md) - [Database Lifecycle Management](https://msdn.microsoft.com/library/jj907294.aspx)