Scripts/Invoke-AzSqlDatabaseMigration.ps1
param( [Parameter(Mandatory=$true)][string] $ServerName = $(throw "Please provide the name of the SQL Server that hosts the SQL Database. (Do not include 'database.windows.net'"), [Parameter(Mandatory=$true)][string] $DatabaseName = $(throw "Please provide the name of the SQL Database"), [Parameter(Mandatory=$true)][string] $UserName = $(throw "Please provide the UserName of the SQL Database"), [Parameter(Mandatory=$true)][string] $Password = $(throw "Please provide the Password of the SQL Database"), [Parameter(Mandatory=$false)][string] $ScriptsFolder = "$PSScriptRoot/sqlScripts", [Parameter(Mandatory=$false)][string] $ScriptsFileFilter = "*.sql", [Parameter(Mandatory=$false)][string] $DatabaseSchema = "dbo" ) Write-Host "Looking for SQL scripts in folder: $ScriptsFolder" #Functions for repeated use function Execute-DbCommand($params, [string]$query) { $result = Invoke-Sqlcmd @params -Query $query -Verbose -QueryTimeout 180 Write-Host "Result from querying $result" } function Execute-DbCommandWithResult($params, [string] $query) { $result = Invoke-Sqlcmd @params -Query $query -Verbose return $result } function Create-DbParams([string] $DatabaseName, [string] $serverInstance, [string] $UserName, [string] $Password) { Write-Host "databasename = $DatabaseName" Write-Host "serverinstance = $serverInstance" Write-Host "username = $UserName" return $params = @{ 'Database' = $DatabaseName 'ServerInstance' = "$serverInstance.database.windows.net" 'Username' = $UserName 'Password' = $Password 'OutputSqlErrors' = $true 'AbortOnError' = $True } } function Get-SqlScriptFileText([string] $scriptPath, [string] $fileName) { $currentfilepath = "$scriptPath/$fileName.sql" return $query = Get-Content $currentfilepath } function Get-CurrentDbVersionNumber () { $selectDbVersionScript = "SELECT TOP 1 CurrentVersionNumber FROM [$DatabaseSchema].[DatabaseVersion] ORDER BY CurrentVersionNumber DESC" $params = Create-DbParams $DatabaseName $ServerName $UserName $Password Write-Host 'Getting Current Database Version Number...' $databaseVersionNumberDataRow = Execute-DbCommandWithResult $params $selectDbVersionScript if ($databaseVersionNumberDataRow.ItemArray.Count -eq 0) { $databaseVersionNumber = 0; } else { $databaseVersionNumber = [Convert]::ToInt32($databaseVersionNumberDataRow.ItemArray[0]) } return $databaseVersionNumber } #Group params needed to connect to database for ease of use $params = Create-DbParams $DatabaseName $ServerName $UserName $Password #Get all tables on the database $query = 'SELECT TABLE_NAME FROM information_schema.tables' $tables = Execute-DbCommandWithResult $params $query # Get script to determine DB version $selectDbVersionScript = "SELECT TOP 1 CurrentVersionNumber FROM [$DatabaseSchema].[DatabaseVersion] ORDER BY CurrentVersionNumber DESC" #Create a DatabaseVersion table if it doesn't exist if(!$tables.ItemArray.Contains('DatabaseVersion')) { Write-Host "The table 'DatabaseVersion' does not exist yet in this database." Write-Host 'Creating DatabaseVersion Table...' $createDbCommand = Get-SqlScriptFileText $ScriptsFolder 'CreateDatabaseVersionTable' Execute-DbCommand $params $createDbCommand Write-Host 'DatabaseVersion Table Created' $databaseVersionNumber = 0 } else { # Get database versionnumber $databaseVersionNumber = Get-CurrentDbVersionNumber } Write-Host "Current databaseVersionNumber : $databaseVersionNumber" #Run all necessary scripts $files = Get-ChildItem -Path $ScriptsFolder -Filter $ScriptsFileFilter | Sort-Object {$_.BaseName -replace "\D+" -as [Int]} for ($i=0; $i -lt $files.Count; $i++) { $fileName = $files[$i].BaseName $fileNameSections = $fileName.Split('_'); if ($fileNameSections.Length -lt 2) { Write-Host "File $fileName skipped for not having all required name sections (version and description)." continue; } $fileVersionStr = $fileNameSections[1]; [int] $scriptVersionNumber = -1; if (-Not([int32]::TryParse($fileVersionStr, [ref]$scriptVersionNumber))) { Write-Host "File $fileName skipped because version is not valid." continue; } [string] $scriptVersionDescription = [convert]::Tostring($fileNameSections[2]) if($scriptVersionDescription.Length -gt 256) { Write-Host "Need to truncate the migration description because its size is" $scriptVersionDescription.Length "while the maximum size is 256" $scriptVersionDescription = $scriptVersionDescription.Substring(0,255) } Write-Host "Found migration #$scriptVersionNumber with description '$scriptVersionDescription'" if($scriptVersionNumber -eq ($databaseVersionNumber + 1)) { Write-Host "Running script #$scriptVersionNumber" # Perform migration $text = Get-SqlScriptFileText $ScriptsFolder $fileName Execute-DbCommand $params $text # Append the new version and description in version table $updateVersionQuery = "INSERT INTO [$DatabaseSchema].[DatabaseVersion] ([CurrentVersionNumber], [MigrationDescription]) VALUES ($scriptVersionNumber, '$scriptVersionDescription')" Execute-DbCommand $params $updateVersionQuery # Update DB version to new version $databaseVersionNumber = $scriptVersionNumber Write-Host "Migration to version $scriptVersionNumber complete." } else { Write-Host "Migration #$scriptVersionNumber skipped" } } #Get New Database Version Number $databaseVersionNumber = Get-CurrentDbVersionNumber Write-Host "Done looping over scripts. Current Database version is $databaseVersionNumber." |