ApexSQL_DevOps_toolkit.psm1
# # © 2019 Quest Software Inc. ALL RIGHTS RESERVED. # #region Classes #[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidGlobalVars", Scope="function", Target="*")] #[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", Scope="function", Target="*")] #[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", Scope="function", Target="*")] #[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", Scope="function", Target="*")] #[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", Scope="function", Target="*")] #[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingInvokeExpression", Scope="function", Target="*")] #Param() class ApexSqlConnection { [string] $ConnectionName } class ApexSqlDatabaseConnection : ApexSqlConnection { [string] $Server [string] $Database [bool] $WindowsAuthentication [string] $UserName [string] $Password [string] AsParameters() { return $this.AsParameters("") } [string] AsParameters([string] $Variation) { if ($Variation -eq "doc") { $_username = "" $_password = "" if (-not $this.WindowsAuthentication) { $_username = ":$($this.UserName)" $_password = ".$($this.Password)" } return "/dbes:$($this.Server).$($this.Database)$_username$_password" } $id = "" if ($Variation -eq "diff1") { $id = "1" } if ($Variation -eq "diff2") { $id = "2" } $_server = "/server$($id):$($this.Server)" $_database = " /database$($id):$($this.Database)" $_username = "" $_password = "" if (-not $this.WindowsAuthentication) { $_username = " /user$($id):$($this.Username)" $_password = " /password$($id):$($this.Password)" } return "$_server$_database$_username$_password" } } function New-ApexSqlDatabaseConnection { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ConnectionName, [Parameter(Mandatory = $false)] [string] $Server, [Parameter(Mandatory = $false)] [string] $Database, [Parameter(Mandatory = $false, ParameterSetName = "winAuth")] [switch] $WindowsAuthentication, [Parameter(Mandatory = $false, ParameterSetName = "credentials")] [string] $U, [Parameter(Mandatory = $false, ParameterSetName = "credentials")] [string] $P, [Parameter(Mandatory = $false, ParameterSetName = "credentials")] [string] $PasswordFile ) $connection = New-Object -TypeName ApexSqlDatabaseConnection $connection.ConnectionName = $ConnectionName $connection.Server = $Server $connection.Database = $Database $connection.WindowsAuthentication = $WindowsAuthentication $connection.UserName = $U $connection.Password = (&{If(!$WindowsAuthentication -and !$U) {Get-Pass -PasswordFile $PasswordFile} Else {$P}}) return $connection } class ApexSqlSourceControlConnection : ApexSqlConnection { [string] $ConnectionName [string] $Source_Type [string] $UserName [string] $Password [string] $Project [string] $Server [string] $Repository [string] $Branch [string] $Label [string] AsParameters() { return $this.AsParameters("") } [string] AsParameters([string] $Variation = "") { $id = "" if ($Variation -eq "diff1") { $id = "1" } if ($Variation -eq "diff2") { $id = "2" } $_type = "/sourcecontrol_type$($id):$($this.Source_Type)" $_username = " /sourcecontrol_user$($id):$($this.UserName)" $_password = " /sourcecontrol_password$($id):$($this.Password)" $_project = "" $_server = "" $_repository = "" $_branch = "" $_label = "" if ($this.Project) { $_project = " /sourcecontrol_project$($id):""$($this.Project)""" } if ($this.Server) { $_server = " /sourcecontrol_server$($id):""$($this.Server)""" } if ($this.Repository) { $_repository = " /sourcecontrol_repository$($id):""$($this.Repository)""" } if ($this.Branch) { $_branch = " /sourcecontrol_branch$($id):""$($this.Branch)""" } if ($this.Label) { $_label = " /sourcecontrol_label$($id):""$($this.Label)""" } return "$_type$_username$_password$_project$_server$_repository$_branch$_label" } } function New-ApexSqlSource { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ConnectionName, [Parameter(Mandatory = $false)] [ValidateSet("tfs","git","mercurial", "subversion", "perforce", "file", "nuget", "sf")] [String] $Source_Type, [Parameter(Mandatory = $false)] [string] $U, [Parameter(Mandatory = $false)] [string] $P, [Parameter(Mandatory = $false)] [string] $PasswordFile, [Parameter(Mandatory = $false)] [string] $Project, [Parameter(Mandatory = $false)] [string] $Server, [Parameter(Mandatory = $false)] [string] $Repository, [Parameter(Mandatory = $false)] [string] $Branch, [Parameter(Mandatory = $false)] [string] $Label, [Parameter(Mandatory = $false)] [string] $FilePath, [Parameter(Mandatory = $false)] [string] $FolderPath, [Parameter(Mandatory = $false)] [string] $NugetID, [Parameter(Mandatory = $false)] [string] $Version, [Parameter(Mandatory = $false)] [bool] $Latest, [Parameter(Mandatory = $false)] [string] $Source ) if (!$P -and $Source_Type -ne "nuget" -and $Source_Type -ne "sf" -and $Source_Type -ne "file") { $P = Get-Pass -PasswordFile $PasswordFile } if ($Source_Type -ne $null) { switch($Source_Type) { "tfs" { New-ApexSqlTfsSourceControlConnection -ConnectionName $ConnectionName -Server $Server -Project $Project -Label $Label -U $U -P $P } "git" { New-ApexSqlGitSourceControlConnection -ConnectionName $ConnectionName -Repository $Repository -Project $Project -Branch $Branch -Label $Label -U $U -P $P } "mercurial" { New-ApexSqlMercurialSourceControlConnection -ConnectionName $ConnectionName -Repository $Repository -Project $Project -Label $Label -U $U -P $P } "subversion" { New-ApexSqlSubversionSourceControlConnection -ConnectionName $ConnectionName -Repository $Repository -Project $Project -Label $Label -U $U -P $P } "perforce" { New-ApexSqlPerforceSourceControlConnection -ConnectionName $ConnectionName -Server $Server -Repository $Repository -Project $Project -Label $Label -U $U -P $P } "file" { New-ApexSqlFileConnection -ConnectionName $ConnectionName -FilePath $Filepath } "sf" { New-ApexSqlSFConnection -ConnectionName $ConnectionName -FolderPath $Folderpath } "nuget" { New-ApexSqlNugetConnection -ConnectionName $ConnectionName -NugetID $NugetID -Version $Version -Source $Source } default { return } } } else { continue } } function New-ApexSqlGitSourceControlConnection { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ConnectionName, [Parameter(Mandatory = $true)] [string] $Repository, [Parameter(Mandatory = $false)] [string] $Project, [Parameter(Mandatory = $false)] [string] $Branch, [Parameter(Mandatory = $false)] [string] $Label, [Parameter(Mandatory = $true)] [string] $U, [Parameter(Mandatory = $true)] [string] $P ) $connection = New-Object ApexSqlSourceControlConnection $connection.ConnectionName = $ConnectionName $connection.Source_Type = "git" $connection.Repository = $Repository $connection.Project = $Project $connection.Branch = $Branch $connection.Label = $Label $connection.UserName = $U $connection.Password = $P return $connection } function New-ApexSqlTfsSourceControlConnection { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ConnectionName, [Parameter(Mandatory = $true)] [string] $Server, [Parameter(Mandatory = $false)] [string] $Project, [Parameter(Mandatory = $false)] [string] $Label, [Parameter(Mandatory = $true)] [string] $U, [Parameter(Mandatory = $true)] [string] $P ) $connection = New-Object ApexSqlSourceControlConnection $connection.ConnectionName = $ConnectionName $connection.Source_Type = "teamfoundationserver" $connection.Server = $Server $connection.Project = $Project $connection.Label = $Label $connection.UserName = $U $connection.Password = $P return $connection } function New-ApexSqlMercurialSourceControlConnection { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ConnectionName, [Parameter(Mandatory = $true)] [string] $Repository, [Parameter(Mandatory = $false)] [string] $Project, [Parameter(Mandatory = $false)] [string] $Label, [Parameter(Mandatory = $true)] [string] $U, [Parameter(Mandatory = $true)] [string] $P ) $connection = New-Object ApexSqlSourceControlConnection $connection.ConnectionName = $ConnectionName $connection.Source_Type = "mercurial" $connection.Repository = $Repository $connection.Project = $Project $connection.Label = $Label $connection.UserName = $U $connection.Password = $P return $connection } function New-ApexSqlSubversionSourceControlConnection { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ConnectionName, [Parameter(Mandatory = $true)] [string] $Repository, [Parameter(Mandatory = $false)] [string] $Project, [Parameter(Mandatory = $false)] [string] $Label, [Parameter(Mandatory = $true)] [string] $U, [Parameter(Mandatory = $true)] [string] $P ) $connection = New-Object ApexSqlSourceControlConnection $connection.ConnectionName = $ConnectionName $connection.Source_Type = "subversion" $connection.Repository = $Repository $connection.Project = $Project $connection.Label = $Label $connection.UserName = $U $connection.Password = $P return $connection } function New-ApexSqlPerforceSourceControlConnection { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ConnectionName, [Parameter(Mandatory = $true)] [string] $Server, [Parameter(Mandatory = $true)] [string] $Repository, [Parameter(Mandatory = $false)] [string] $Project, [Parameter(Mandatory = $false)] [string] $Label, [Parameter(Mandatory = $true)] [string] $U, [Parameter(Mandatory = $true)] [string] $P ) $connection = New-Object ApexSqlSourceControlConnection $connection.ConnectionName = $ConnectionName $connection.Source_Type = "perforce" $connection.Server = $Server $connection.Repository = $Repository $connection.Project = $Project $connection.Label = $Label $connection.UserName = $U $connection.Password = $P return $connection } class ApexSqlFileConnection : ApexSqlConnection { [string] $FilePath } function New-ApexSqlFileConnection { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ConnectionName, [Parameter(Mandatory = $true)] [string] $FilePath ) $connection = New-Object ApexSqlFileConnection $connection.ConnectionName = $ConnectionName $connection.FilePath = $FilePath return $connection } class ApexSqlSFConnection : ApexSqlConnection { [string] $FolderPath } function New-ApexSqlSFConnection { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ConnectionName, [Parameter(Mandatory = $true)] [string] $FolderPath ) $connection = New-Object ApexSqlSFConnection $connection.ConnectionName = $ConnectionName $connection.FolderPath = $FolderPath return $connection } class ApexSqlNugetConnection : ApexSqlConnection { [string] $NugetID [string] $Version [string] $Source } function New-ApexSqlNugetConnection { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ConnectionName, [Parameter(Mandatory = $true)] [string] $NugetID, [Parameter(Mandatory = $false)] [string] $Version, [Parameter(Mandatory = $false)] [string] $Source ) $connection = New-Object ApexSqlNugetConnection $connection.ConnectionName = $ConnectionName $connection.NugetID = $NugetID $connection.Version = $Version $connection.Source = $Source return $connection } class ApexSqlNotificationSettings { [PSCredential] $Credential [string] $SmtpServer [bool] $UseSSL [int] $Port } function New-ApexSQLNotificationSettings { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $true)] [string] $EmailAddress, [Parameter(Mandatory = $false)] [string] $Password, [Parameter(Mandatory = $false)] [string] $PasswordFile, [Parameter(Mandatory = $true)] [string] $SmtpServer, [Parameter(Mandatory = $true)] [int] $Port, [Parameter(Mandatory = $false)] [switch] $UseSSL ) $Password = (&{If(!$Password) { (Get-Pass -PasswordFile $PasswordFile) } Else {$Password}}) if ($null -ne $Password -and $Password -ne '') { $pass = New-Object System.Security.SecureString $Password.ToCharArray() | ForEach-Object { $pass.AppendChar($_) } $cred = New-Object System.Management.Automation.PSCredential ($EmailAddress, $pass) } else { $pass = '' $cred = New-Object System.Management.Automation.PSCredential ($EmailAddress) } $settings = New-Object -TypeName ApexSqlNotificationSettings $settings.Credential = $cred $settings.SmtpServer = $SmtpServer $settings.UseSSL = $UseSSL $settings.Port = $Port $Options.NotificationSettings = $settings return $settings } class ApexSqlOptions { [string] $PipelineName = "MyPipeline" [string] $OutputLocation [string] $ScriptDirectory [ApexSqlNotificationSettings] $NotificationSettings [string] $OutputLogFile [string] $PackageFilePath [string] $Timestamp [string] $ErrorCodes [string] $FailedSteps [string] $Result = "Success" } function New-ApexSqlOptions { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $PipelineName, [Parameter(Mandatory = $false)] [string] $OutputLocation, [Parameter(Mandatory = $false)] [switch] $NoSubfolders ) $timestamp = Get-Date -Format "MM-dd-yyyy_HH-mm-ss" $options = New-Object -TypeName ApexSqlOptions $options.PipelineName = $PipelineName $options.ScriptDirectory = $global:currentDirectory if (-not (Test-Path $global:nugetExePath)) { throw "Incorrect nuget.exe file path defined in 'global:nugetExePath' variable." } if (!$OutputLocation ) { $OutputLocation = $options.ScriptDirectory } if ($NoSubfolders) { $options.OutputLocation = $OutputLocation } else { $options.OutputLocation = "$OutputLocation\$PipelineName\$timestamp" } $options.Timestamp = $timestamp $options.OutputLogFile = "$($options.OutputLocation)\$($PipelineName)_job_summary.log" $options.NotificationSettings = $null if (-not (Test-Path $options.OutputLocation)) { New-Item -Path $OutputLocation -ItemType Directory -Force | Out-Null } if (-not (Test-Path $options.OutputLocation)) { New-Item -Path $options.OutputLogFile -ItemType File -Force | Out-Null } $options.PackageFilePath = "" Initialize-Globals $pipelineStartedTimestamp = "$($timestamp.remove(2, 1).insert(2, "/").remove(5, 1).insert(5, "/").remove(10, 1).insert(10, " ").replace("-",":"))" $msg = "$($PipelineName) started at $($pipelineStartedTimestamp)" Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append return $options } #endregion #region Helpers function Initialize-Globals { [CmdletBinding(SupportsShouldProcess)] param ( [string] $CurrentDirectory ) $global:currentDirectory = $CurrentDirectory $global:StaticDataSource_ForDataDiff = $null $global:StaticDataPath_ForDataDiff = $null $global:SkippingList = $null $global:nuspec = $null $global:nugetDatabaseScriptsSource = $null #Create and reset ResultSet $global:ResultSet = [ordered]@{} if ($null -eq $Options.Result) { $global:ResultSet.Clear() } } function GetSourceName { [CmdletBinding()] [OutputType('System.String')] param ( [Parameter(Mandatory = $true)] [string] $SourceName ) switch($SourceName) { "Team Foundation Server" { return "teamfoundationserver" } "Git" { return "git" } "Mercurial" { return "mercurial" } "Subversion" { return "subversion" } "Perforce" { return "perforce" } "File" { return "file" } } } function GetStaticDataFolderPath { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] $Path ) [xml]$projecFile = Get-Content $Path $SourceControllInfo = $projecFile.ApexSQLBuildProject.ProjectOptions.Options.Option | Where-Object { $_.id -eq "SourceControlConnection"} $SourceFolder = $SourceControllInfo.ScConnectionInfo.SourceControlFolder return $SourceFolder + "\Tables\StaticData" } function GetToolName { param ( [string] $Step ) switch ($Step) { "Build" {return "ApexSQL Build"} "Populate" {return "ApexSQL Generate"} "Audit" {return "ApexSQL Trigger"} "Review" {return "ApexSQL Enforce"} "Test" {return "ApexSQL Unit Test"} "Script" {return "ApexSQL Script"} "Document" {return "ApexSQL Doc"} "Sync" {return "ApexSQL Diff"} "Sync data" {return "ApexSQL Data Diff"} "Package" {return "ApexSQL Package"} "Deploy" {return "ApexSQL Deploy"} default {return} } } function Get-Pass { param ( [string] $PasswordFile ) if ($PasswordFile.Length -gt 0) { $filePath = "$($options.ScriptDirectory)\Passwords\$($PasswordFile).txt" if (Test-Path -Path $filePath -PathType leaf) { $EncryptedPass = (Get-Content -Path $filePath) if ($EncryptedPass.Length -gt 0) { $DecryptedPass = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR( (ConvertTo-SecureString $EncryptedPass) )) } else { $msg = "No password provided in file ""$($filePath)!""" Write-Warning $msg Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append exit(1) } } else { $msg = "The file ""$($filePath)"" doesn't exist!" Write-Warning $msg Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append exit(1) } } else { $msg = "No password file is provided!" Write-Warning $msg Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append exit(1) } return $DecryptedPass } function Get-ApexSQLToolLocation { param ( [Parameter(Mandatory = $true)] [String] $ApplicationName ) $key = Get-ChildItem HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ -Recurse |Where-Object {$_.PSChildName -like "ApexSQL $($ApplicationName)*_is1"} $key = $key.Name if (Test-Path "HKLM:\$Key") { $ApplicationPath = (Get-ItemProperty -Path "HKLM:\$key" -Name InstallLocation).InstallLocation } else { $reg = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64) $regKey= $reg.OpenSubKey("$key") if ($regKey) { $ApplicationPath = $regKey.GetValue("InstallLocation") } else { $reg = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry32) $regKey= $reg.OpenSubKey("$key") if ($regKey) { $ApplicationPath = $regKey.GetValue("InstallLocation") } else { Out-File -FilePath $Options.OutputLogFile -InputObject "ApexSQL $ApplicationName is not installed" -Append $Options.ErrorCodes += @("Not installed") $Options.FailedSteps += @("ApexSQL $ApplicationName") $Options.Result = "Failure" return $null } } } if ($ApplicationPath) { return $ApplicationPath + "ApexSQL" + $ApplicationName.replace(' ','') + ".com" } } function Start-ApexSQLTool { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ToolName, [Parameter(Mandatory = $true)] [string] $StepName, [Parameter(Mandatory = $true)] [string] $ToolParameters, [Parameter(Mandatory = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $true)] [bool] $StopOnFail, [Parameter(Mandatory = $false)] [bool] $FromPackage, [Parameter(Mandatory = $false)] [bool] $Silent, [Parameter(Mandatory = $false)] [string] $OutputFiles ) $logFile = "$OutputLocation\$($PipelineName).log" $thisIsPackageStep = $false $toolLocation = Get-ApexSQLToolLocation $ToolName if (-not $toolLocation) { return $false } $info = "" if ($ToolName -ne "Script") { if ($Silent -ne $true) { if ($FromPackage -eq $true -and $ToolName -eq "Data Diff") { $thisIsPackageStep = $true $info = "`r`n`r`n`t`t----- Started collecting static data -----`r`n" + "`t`t------------------------------------------`r`n" } else { $info = "`r`n`r`n============================" + ("=" * $ToolName.Length) + "==============`r`n" + "----- Starting execution of ApexSQL $ToolName -----`r`n" + "------------------------------" + ("-" * $ToolName.Length) + "------------`r`n" } } } else { $info = "`r`n`r`n`t----- Started scripting the database -----`r`n" + "`t------------------------------------------`r`n" } Out-File -FilePath $Options.OutputLogFile -InputObject $info -Append $output = Invoke-Expression -Command ("& `"$($toolLocation)`" $ToolParameters") Out-File -FilePath $Options.OutputLogFile -InputObject $output -Append if(($StepName -eq "Compare" -or $StepName -eq "Deploy") -and $ToolName -eq "Diff" -and $lastExitCode -ne 102){ $lastExitCode = 1 } elseif(($StepName -eq "Compare" -or $StepName -eq "Deploy") -and $ToolName -eq "Diff" -and $lastExitCode -eq 102){ $lastExitCode = 0 } if ($lastExitCode -ne 0 -and (-not ($lastExitCode -eq 104 -and $ToolName -eq "Generate"))) { $Options.FailedSteps += @("ApexSQL $ToolName") $Options.ErrorCodes += @($lastExitCode) if ($StopOnFail) { $errorText = "`r`nApexSQL $ToolName failed.`r`nThe process is canceled due to failure return code: $lastExitCode" Out-File -FilePath $Options.OutputLogFile -InputObject $errorText -Append $Options.Result = "Failure" $msg = "$StepName step failed." if($StepName -eq "Compare"){ $msg = "$StepName step failed - Differences found" } Write-Warning $msg $global:ResultSet.Add($global:ResultSet.Count, @{Step=$StepName; Result='Failure'; ErrorCode=$lastExitCode;}) return $false } else { $Options.Result = "Completed with errors" } } else { if ($thisIsPackageStep) { $global:ResultSet.Add($global:ResultSet.Count, @{Step="Package"; Result='Success'; OutputFiles=(&{ if ($null -ne $OutputFiles) {"$($OutputFiles)"} Else {""}} )}) } if ($ToolName -ne "Script" -and $Silent -ne $true -and ($FromPackage -eq $true -and $ToolName -eq "Data Diff") -ne $true) { $msg = "$StepName step passed." if($StepName -eq "Compare"){ $msg = "$StepName step passed - Databases are equal" } Write-Verbose $msg Out-File -FilePath $Options.OutputLogFile -InputObject "`r`n##### $($msg) #####`r`n" -Append $global:ResultSet.Add($global:ResultSet.Count, @{Step=$StepName; Result='Success'; OutputFiles=(&{ if ($null -ne $OutputFiles) {"$($OutputFiles)"} Else {""}} )}) } } return $true } function DisplayCommandLineArgs() { $msg = "`t`t- NuGet ID: $nugetId`r`n" + "`t`t- Version: $nugetVersion`r`n" + "`t`t- Source: $($Options.OutputLocation)`r`n" + "`t`t- Destination: $($Options.OutputLocation)" Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append # Setup the nuget path. if (-Not $nuget -eq "") { $global:nugetExe = $nuget } else { # Assumption, nuget.exe is the current folder Where-Object this file is. $global:nugetExe = Join-Path $source "nuget" } $global:nugetExe if (!(Test-Path $global:nugetExe -PathType leaf)) { $errorText = "'nuget.exe' file was not found. Please provide correct 'nuget.exe' file path.`r`nProces terminated." Write-Warning -Message $errorText Out-File -FilePath $Options.OutputLogFile -InputObject "`r`n`t$($errorText)" -Append $Options.ErrorCodes += @($LastExitCode) $Options.FailedSteps += @("ApexSQL Package") $Options.Result = "Failure" return } } function CleanUp() { if ($clean -eq $false) { return; } $nupkgFiles = @(Get-ChildItem $destination -Filter *.nupkg) if ($nupkgFiles.Count -gt 0) { "Found " + $nupkgFiles.Count + " *.nupkg files. Lets delete these first..." foreach($nupkgFile in $nupkgFiles) { $combined = Join-Path $destination $nupkgFile "... Removing $combined." Remove-Item $combined } "... Done!" } } function PackageTheSpecification() { # Create template .nuspec file $nuspecContent = @' <?xml version="1.0"?> <package > <metadata> <id>$id$</id> <version>$version$</version> <authors>$authors$</authors> <owners>$owners$</owners> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>$description$</description> <releaseNotes>$releaseNotes$</releaseNotes> <copyright>Copyright 2017 - ApexSQL LLC</copyright> <tags></tags> </metadata> </package> '@ New-Item -Path $Options.OutputLocation -Name "Package.nuspec" -ItemType "file" -Value $nuspecContent $shema = $null $data = $null $consolidatedScript = "$($Options.OutputLocation)\Consolidated_script.sql" if ($Consolidate) { $shema = Get-ChildItem -Path $Options.OutputLocation -Filter SchemaSync_*.sql $data =Get-ChildItem -Path $Options.OutputLocation -Filter DataSync_*.sql if ($null -ne $shema) { $content += Get-Content -Path "$($Options.OutputLocation)\$shema" Remove-Item "$($Options.OutputLocation)\$shema" } if ($null -ne $data) { $content += Get-Content -Path "$($Options.OutputLocation)\$data" Remove-Item "$($Options.OutputLocation)\$data" } if ($content) { $content > $consolidatedScript } $content = $null } #Create list of all files included in .nupkg #(this is all files from all included steps excluding Project.nuspec which serves as a template and being removed) $files = "`r`nContent of this package version:`r`n" $content = Get-ChildItem $Options.OutputLocation -Exclude *.nuspec, *_job_summary.log foreach($file in $content) { $files += "$($file.Name)`r`n" $fileNames += "`r`n`t`t- $($file.Name)" } if ($null -ne $content) { $msg = "`tStarted creating the package ...`r`n" Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append DisplayCommandLineArgs if ($Options.Result -eq "Failure") { if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } $logContent = $null try { #Remove log file (and store its content to temp variable in order to get back the content to log file) $logContent = Get-Content $Options.OutputLogFile -Raw Remove-Item $Options.OutputLogFile #Execute packing &$nugetExe pack "$($Options.OutputLocation)" -Properties id=$nugetId -Properties version=$nugetVersion -Properties authors=$nugetAuthors -Properties owners=$nugetOwners -Properties description=$files -Properties releaseNotes=$nugetReleaseNotes -OutputDirectory "$($Options.OutputLocation)" #Get back log content to log file $logContent > $Options.OutputLogFile $logContent = $null $msg = "`tPackage successfully created." Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append $msg = "`r`n`tContent of this package version:" + $fileNames Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append } catch { #Get back log content to log file $logContent > $Options.OutputLogFile $logContent = $null $errorText = "Creating the package failed.`r`n" Write-Warning -Message $errorText Out-File -FilePath $Options.OutputLogFile -InputObject "`t$($errorText)" -Append if ($null -eq $LASTERRORCODE -and $LASTERRORCODE -lt 0) { $lastReturnCode = 1 } $Options.ErrorCodes += $lastReturnCode $Options.FailedSteps += @("ApexSQL Package") $Options.Result = "Failure" return } } else { $errorText = "Creating the package failed. Package can not be empty.`r`n" Write-Warning -Message $errorText Out-File -FilePath $Options.OutputLogFile -InputObject "`t$($errorText)" -Append $lastReturnCode = $LASTERRORCODE if ($null -eq $LASTERRORCODE -and $LASTERRORCODE -lt 0) { $lastReturnCode = 1 } $Options.ErrorCodes += $lastReturnCode $Options.FailedSteps += @("ApexSQL Package") $Options.Result = "Failure" return } #Remove templete .nuspec file Remove-Item "$($Options.OutputLocation)\Package.nuspec" #Get .nupkg file name to add it to $outputs $packageName = @(Get-ChildItem "$($Options.OutputLocation)" -Filter *.nupkg) $global:ResultSet[$global:ResultSet.Count - 1]["OutputFiles"]=$packageName.Name $packageName = $packageName.FullName $Options.PackageFilePath = $packageName #If DoNotRemoveContents switch missing remove all files (except .nupkg and .log) if (!$DoNotRemoveContents) { Get-Childitem "$($Options.OutputLocation)" -Exclude *.nupkg, *_job_summary.log | ForEach-Object ($_) {remove-item $_.fullname -Recurse -Force} } } function PublishPackage() { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $Destination, [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $ApiKey ) if ($ApiKey -eq "") { $errorText = "No NuGet server api key provided - so not pushing anything up." Write-Warning -Message $errorText Out-File -FilePath $Options.OutputLogFile -InputObject $errorText -Append if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } $msg = "`r`n`t----------`r`n`r`n`tStarted publishing the package ...`r`n" Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append $msg = "`t`tGetting all *.nupkg's files to push to: $pushSource" Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append $files = Get-ChildItem $Destination -Filter *.nupkg if ($files.Count -eq 0) { $errorText = "No nupkg files found in the directory: $destination`r`n`tTerminating process." Write-Warning -Message $errorText Out-File -FilePath $Options.OutputLogFile -InputObject "`t`t$($errorText)" -Append $Options.ErrorCodes += 1 $Options.FailedSteps += @("ApexSQL Package") $Options.Result = "Failure" return } foreach($file in $files) { $fileNames += "`r`n`t`t`t- $($file.Name)" } $msg = "`t`tFound:" + $fileNames Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append foreach($file in $files) { try { &$nugetExe push ($file.FullName) -Source $pushSource -apiKey $ApiKey $msg = "`r`n`tPackage successfully published." Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append } catch { $errorText = "`tPackage publishing failed: `r`n`t`t>>$($_.Exception.Message)<<" Write-Warning -Message $errorText Out-File -FilePath $Options.OutputLogFile -InputObject $errorText -Append $lastReturnCode = $LastExitCode if ($LastExitCode -ne 0 -and $LastExitCode -lt 0) { $lastReturnCode = 1 } $Options.ErrorCodes += $lastReturnCode $Options.FailedSteps += @("ApexSQL Package") $Options.Result = "Failure" } } } function ExtractNupkg () { param ( [string] $OutputLocation, [string] $NugetID, [string] $Version, [string] $Source, [string] $nuget = $global:nugetExePath ) $msg = "`r`n`t----------`r`n`r`n`tStarted Extracting the package ...`r`n" Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append if (!$Source -or $null -eq $Source -or $Source -eq "") { $Source = "$($OutputLocation)\\" } if (-Not $Version) { try { &$nuget install $NugetID -Output $OutputLocation -Source "$($Source)" >> $Options.OutputLogFile $msg = "`r`n`tPackage successfully extracted.`r`n`r`n`t----------`r`n" Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append } catch { $errorText = "`tThere was some problem in extracting the .nupkg file.`r`n`t`t>>$($_.Exception.Message)<<" Write-Warning -Message $errorText Out-File -FilePath $Options.OutputLogFile -InputObject $errorText -Append $Options.ErrorCodes += 1 $Options.FailedSteps += @("Extracking the package") $Options.Result = "Failure" } } else { try { &$nuget install $NugetID -Version $Version -Output $OutputLocation -Source $Source >> $Options.OutputLogFile $msg = "`tPackage successful extracted.`r`n`r`n`t----------`r`n" Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append } catch { $errorText = "`tThere was some problem in extracting the .nupkg file.`r`n`t`t>>$($_.Exception.Message)<<" Write-Warning -Message $errorText Out-File -FilePath $Options.OutputLogFile -InputObject $errorText -Append if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } } } function ClearExtractedContents () { param ( [string] $path ) Get-Childitem $path | Where-Object {$_.Name -notlike "*DatabaseScripts*"} | ForEach-Object ($_) {remove-item $_.fullname -Recurse } } function RemoveSnapshots () { param ( [string] $Location ) $Db_SnapShot = "$($Location)\Db_SnapShot.axsnp" $Db_SnapShot_Diff = "$($Location)\Db_SnapShot_Diff.axdsn" #Remove snapshots if (Test-Path $Db_SnapShot) { Remove-Item $Db_SnapShot } if (Test-Path $Db_SnapShot_Diff) { Remove-Item $Db_SnapShot_Diff } } function Set-ProjectSwitch { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [string] $ProjectFile, [ApexSqlConnection] $BuildStepSource, [Switch] $ReviewStep ) $project = "" if ($ProjectFile) { $ProjectFile = $Options.ScriptDirectory + "\Projects\" + $ProjectFile if (-not $ReviewStep) { $project = "/project:""$ProjectFile""" } else { $project = "/rb:""$ProjectFile""" } if ($null -eq $BuildStepSource) { $global:StaticDataPath_ForDataDiff = GetStaticDataFolderPath -Path "$($ProjectFile)" } } return $project } function Set-AdditionalSwitch { [CmdletBinding()] param ( [string] $Additional ) $resultAdditional = "" if ($Additional) { $resultAdditional = $Additional } return $resultAdditional } function LogFail { [CmdletBinding()] param ( [string] $ErrorText, [string] $FilePath ) Write-Warning -Message $errorText Out-File -FilePath $FilePath -InputObject $ErrorText -Append } #endregion function Invoke-ApexSqlBuildStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $false)] [ValidateScript({ if (($_.GetType() -ne [ApexSqlSourceControlConnection]) -and ($_.GetType() -ne [ApexSqlFileConnection]) -and ($_.GetType() -ne [ApexSqlSFConnection])) { Throw "Only source control or file connection can be used as a source" } return $true })] [ApexSqlConnection] $Source, [ValidateScript({ if (($_.GetType() -ne [ApexSqlDatabaseConnection])) { Throw "Only database can be used as a destination" } return $true })] [Parameter(Mandatory = $false)] [ApexSqlConnection] $Database, [Parameter(Mandatory = $false)] [bool] $StopOnFail = $true, [Parameter(Mandatory = $false)] [string] $ProjectFile, [Parameter(Mandatory = $false)] [string] $AdditionalOptions, [Parameter(Mandatory = $false)] [switch] $ExcludeStaticData, [Parameter(Mandatory = $false)] [switch] $NoScript, [Parameter(Mandatory = $false)] [string] $scPass, [Parameter(Mandatory = $false)] [string] $dbPass ) if ($Options.Result -eq "Failure") { LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL Build step due to failure in the pipeline" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } if (($null -eq $Source -and $null -eq $Database) -and $null -eq $ProjectFile) { LogFail -FilePath $Options.OutputLogFile -ErrorText "Source and Database or a Project file must be set" return $Options } $project = Set-ProjectSwitch -Options $Options -ProjectFile $ProjectFile -BuildStepSource $Source $additional = Set-AdditionalSwitch -Additional $AdditionalOptions #Step-related params $static_data = " /isd " if ($ExcludeStaticData) { $static_data = "" } #Configure Source parameters $sourceType = "sc" if ($null -ne $Source) { if ($Source.GetType() -eq [ApexSqlFileConnection]) { $sourceType = "sql" $sourceParams = "/source_name:""$($Source.FilePath)""" } elseif ($Source.GetType() -eq [ApexSqlSFConnection]) { $sourceType = "sf" $sourceParams = "/source_name:""$($Source.FolderPath)""" } else { $sourceParams = $Source.AsParameters() } #Storing Source for DataDiff in Script step - Static Data copying (add '1' to all source parameters [/s: => /s1: ...]) $global:StaticDataSource_ForDataDiff = $($sourceParams).replace(":", "1:").replace("http1:","http:").replace("https1:","https:") } #Configure Database parameters $databaseType = "db" if ($null -ne $Database) { $databaseParams = $Database.AsParameters() } #Full tool parameters $toolParameters = @{$true = "/source_type:$sourceType $sourceParams /output_type:$databaseType $databaseParams "; $false = " $project "}[!$ProjectFile] + " $additional $static_data /drop_if_exists /script_permissions /v /f" $params = @{ ToolName = "Build" StepName = "Build" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail } #Execute the tool Start-ApexSQLTool @params if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } #$global:ResultSet.Add($global:ResultSet.Count, @("Build", (&{ if ($null -ne $Options.Result) {"$($Options.Result)"} Else {""}} ), $null, (&{ if ($null -ne $Options.ErrorCodes) {"$($Options.ErrorCodes)"} Else {""}} ))) } function Invoke-ApexSqlPopulateStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $false)] [ApexSqlDatabaseConnection] $Database, [Parameter(Mandatory = $false)] [int] $RowCount, [Parameter(Mandatory = $false)] [switch] $FillAllTables, [Parameter(Mandatory = $false)] [bool] $StopOnFail = $true, [Parameter(Mandatory = $false)] [string] $ProjectFile, [Parameter(Mandatory = $false)] [string] $AdditionalOptions, [Parameter(Mandatory = $false)] [switch] $NoScript ) if ($Options.Result -eq "Failure") { $global:SkippingList += "`tPopulate`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL Populate step due to failure in the pipeline" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } $project = Set-ProjectSwitch -Options $Options -ProjectFile $ProjectFile $additional = Set-AdditionalSwitch -Additional $AdditionalOptions $fillEmpty = "" if (!$FillAllTables) { $fillEmpty = " /foet" } #Output files names $scriptName = "$($Options.OutputLocation)\Populate_$($Database.ConnectionName)_PopulateScript.sql" $db = (&{ if ($Database) {"$($Database.AsParameters()) "} Else {""}} ) $rows = (&{ if ($RowCount) {"/r:$RowCount "} Else {""}} ) $toolParameters = "$($db) $($rows) $fillEmpty $project $additional /v /f " $params = @{ ToolName = "Generate" StepName = "Populate" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail } #Execute the tool Start-ApexSQLTool @params | Out-Null if ($LASTEXITCODE -eq 104) { $msg = "There are no empty tables in database: $($Database.Database)" Write-Warning -Message $msg } if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } } function Invoke-ApexSqlAuditStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $false)] [ApexSqlDatabaseConnection] $Database, [Parameter(Mandatory = $false)] [bool] $StopOnFail = $true, [Parameter(Mandatory = $false)] [string] $ProjectFile, [Parameter(Mandatory = $false)] [string] $AdditionalOptions, [Parameter(Mandatory = $false)] [switch] $NoScript, [Parameter(Mandatory = $false)] [switch] $NoReport, [Parameter(Mandatory = $false)] [switch] $OverwriteExistingTriggers, [Parameter(Mandatory = $false)] [string] $ExcludeTables ) if ($Options.Result -eq "Failure") { $global:SkippingList += "`tAudit`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL Audit step due to failure in the pipeline" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } $project = Set-ProjectSwitch -Options $Options -ProjectFile $ProjectFile $additional = Set-AdditionalSwitch -Additional $AdditionalOptions $db = (&{ if ($Database) {"$($Database.AsParameters())"} Else {""}} ) #Output files names $reportName = "$($Options.OutputLocation)\AuditReport.pdf" #Full tool parameters $outReport = "" if (!$NoReport) { $outReport = "/sr /rf:pdf /or:""$($reportName)""" } $eat = " /eat " if ($OverwriteExistingTriggers) { $eat = "" } $et = "" if ($ExcludeTables) { $et = " /et:$($ExcludeTables) " } $toolParameters = "$($db) $project $additional /at $($eat) $($et) $outReport /v /f" $params = @{ ToolName = "Trigger" StepName = "Audit" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail OutputFiles = if ($NoReport) {""} else {"AuditReport.pdf"} } #Execute the tool Start-ApexSQLTool @params if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } } function Invoke-ApexSqlReviewStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $true)] [ApexSqlDatabaseConnection] $Database, [Parameter(Mandatory = $false)] [bool] $StopOnFail = $true, [Parameter(Mandatory = $true)] [string] $ProjectFile, [Parameter(Mandatory = $false)] [string] $AdditionalOptions, [Parameter(Mandatory = $false)] [switch] $NoReport, [Parameter(Mandatory = $false)] [switch] $Passed, [Parameter(Mandatory = $false)] [switch] $Failed, [Parameter(Mandatory = $false)] [switch] $Errors ) if ($Options.Result -eq "Failure") { $global:SkippingList += "`tReview`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL Review step due to failure in the pipeline" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } $project = Set-ProjectSwitch -Options $Options -ProjectFile $ProjectFile -ReviewStep $additional = Set-AdditionalSwitch -Additional $AdditionalOptions #Output files names $reportName = "$($Options.OutputLocation)\ReviewReport.html" $scriptName = "$($Options.OutputLocation)\Review_$($Database.ConnectionName)_FixScript.sql" #Report contents $reportContents if ($Passed) { $reportContents += " /os:p" } if ($Failed) { $reportContents += " /os:f" } if ($Errors) { $reportContents += " /os:e" } #Full tool parameters $outReport = "" if (!$NoReport) { $outReport = "/ot:h /on:""$($reportName)""" } $toolParameters = " $($Database.AsParameters()) $project $($additional) $($outReport) $($reportContents) /v /f" $params = @{ ToolName = "Enforce" StepName = "Review" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail OutputFiles = "ReviewReport.html" } #Execute the tool Start-ApexSQLTool @params if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } } function Invoke-ApexSqlTestStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $true)] [ApexSqlDatabaseConnection] $Database, [Parameter(Mandatory = $false)] [string] $ProjectFile, [Parameter(Mandatory = $false)] [switch] $InstallSqlCop, [Parameter(Mandatory = $false)] [bool] $StopOnFail = $true, [Parameter(Mandatory = $false)] [string] $AdditionalOptions, [Parameter(Mandatory = $false)] [switch] $NoReport ) if ($Options.Result -eq "Failure") { $global:SkippingList += "`tTest`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL Test step due to failure in the pipeline" return $Options } $project = Set-ProjectSwitch -Options $Options -ProjectFile $ProjectFile $additional = Set-AdditionalSwitch -Additional $AdditionalOptions #sqlCop to install $sqlCop = "" if ($InstallSqlCop) { $sqlCop = " /install_sqlcop" } #Output files names $testReport = "$($Options.OutputLocation)\TestReport.xml" #Full tool parameters $outReport = "" if (!$NoReport) { $outReport = "/or:""$($testReport)""" } $toolParameters = "$($Database.AsParameters()) $outReport $sqlCop $project $additional /install_tsqlt /v /f" $params = @{ ToolName = "Unit Test" StepName = "Test" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail OutputFiles = "TestReport.xml" } #Execute the tool Start-ApexSQLTool @params if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } } function Invoke-ApexSqlScriptStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $false)] [ApexSqlDatabaseConnection] $Database, [Parameter(Mandatory = $false)] [bool] $StopOnFail = $true ) if ($Options.Result -eq "Failure") { $global:SkippingList += "`tScript`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL Script step due to failure in the pipeline" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } $scriptFolder = "$($Options.OutputLocation)\DatabaseScripts\" New-Item -ItemType Directory -Path "$($scriptFolder)" -F #region Script #Full tool parameters $toolParameters = " $($Database.AsParameters()) /fl:""$($scriptFolder)"" /exc:16384:SQLCop:tSQLt /exc:134217728:tSQLtCLR /eso /in /v /f" $params = @{ ToolName = "Script" StepName = "Script" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail } #Execute the tool (ApexSQL Script) Start-ApexSQLTool @params if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } #endregion #region DataDiff #Full tool parameters if ($null -ne $global:StaticDataSource_ForDataDiff) { $toolParametersDataDiff = " $global:StaticDataSource_ForDataDiff /sf2:""$($scriptFolder)"" /o:8 /sync /f" $paramsDD = @{ ToolName = "Data Diff" StepName = "Script" ToolParameters = $toolParametersDataDiff Options = $Options StopOnFail = $StopOnFail FromPackage = $true } #Execute the tool (ApexSQL Data Diff) Start-ApexSQLTool @paramsDD if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } } else { $tempSrcPath = "$($global:StaticDataPath_ForDataDiff)" $tempDestPath = "$($Options.OutputLocation)\DatabaseScripts\Tables" if ((Test-Path -Path "$($tempSrcPath)") -and (Test-Path -Path "$($tempDestPath)")) { $StaticDataCopyStarted = "`r`n`r`n`t`t----- Started collecting static data -----`r`n" + "`t`t------------------------------------------`r`n" Out-File -FilePath $Options.OutputLogFile -InputObject $StaticDataCopyStarted -Append Copy-Item "$($global:StaticDataPath_ForDataDiff)" -Destination "$($Options.OutputLocation)\DatabaseScripts\Tables" -Recurse #Edit permissions to be able to delete later $Acl = Get-Acl "$($tempDestPath)\StaticData" $Ar = New-Object system.security.accesscontrol.filesystemaccessrule($env:UserName,"FullControl","Allow") $Acl.SetAccessRule($Ar) Set-Acl "$($tempDestPath)\StaticData" $Acl } } $info = "`r`n`r`n`t`t----- Completed collecting static data -----`r`n" + "`t`t--------------------------------------------" Out-File -FilePath $Options.OutputLogFile -InputObject $info -Append #endregion $info = "`r`n`r`n`t----- Completed scripting the database -----`r`n" + "`t--------------------------------------------`r`n`r`n" Out-File -FilePath $Options.OutputLogFile -InputObject $info -Append } function Invoke-ApexSqlPackageStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $false)] [switch] $Publish, [Parameter(Mandatory = $false)] [string] $Format, [Parameter(Mandatory = $false)] [switch] $Obfuscate, [string]$nugetVersion = "", [string]$nugetId = $global:nugetId, [string]$nugetAuthors = $global:nugetAuthors, [string]$nugetOwners = $global:nugetOwners, [string]$nugetReleaseNotes = "", [string]$apiKey = $global:apiKey, [string]$apiKeyFile = $global:apiKeyFile, [string]$pushSource = $global:pushSource, [string]$nuget = $global:nugetExePath, [bool]$clean = $false, [switch]$DoNotRemoveContents, [switch]$Consolidate, [Parameter(Mandatory = $false)] [ApexSqlDatabaseConnection] $Database, [Parameter(Mandatory = $false)] [bool] $StopOnFail = $true ) if ($Options.Result -eq "Failure") { $global:SkippingList += "`tPackage`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL Package step due to failure in the pipeline" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } $info = "`r`n`r`n=============================`r`n" + "----- Started Packaging -----`r`n" + "-----------------------------`r`n" Out-File -FilePath $Options.OutputLogFile -InputObject $info -Append if(($Format -ne "" -and $null -ne $Format) -and $Obfuscate){ $global:SkippingList += "`tPackage`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Cannot use -Format option with -Obfuscate" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } #Script out temp. db to script folder (including static data) if ($null -ne $Database) { Invoke-ApexSqlScriptStep -Options $options -Database $Database | Out-Null if(($Format -ne "" -and $null -ne $Format) -or ($Obfuscate)) { try{ $toolParameters = "" if($Format -ne "" -and $null -ne $Format){ $toolParameters = " /frs /pf:$Format /is:""$($Options.OutputLocation)"" /f" } if($Obfuscate) { $toolParameters = " /obf /is:""$($Options.OutputLocation)"" /f" } $params = @{ ToolName = "Refactor" StepName = "Package" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail OutputFiles = if ($OutputFiles.Length -gt 0) {$OutputFiles} else {$null} } Start-ApexSQLTool @params } catch { $Options.FailedSteps += @("ApexSQL Refactor") $Options.ErrorCodes += @($lastExitCode) if ($StopOnFail) { $stepName = "Package" $Options.Result = "Failure" $msg = "$stepName step failed." Write-Warning $msg LogFail -FilePath $Options.OutputLogFile -ErrorText "`r`nApexSQL $ToolName failed.`r`nThe process is canceled due to failure return code: $lastExitCode" return $false } else { $Options.Result = "Completed with errors" } Out-File -FilePath $Options.OutputLogFile -InputObject $_.Message -Append } } } $ErrorActionPreference = "Stop" $global:nugetExe = "" RemoveSnapshots -Location "$($Options.OutputLocation)" CleanUp PackageTheSpecification if (($apiKey -eq "" -or $null -eq $apiKey) -and ($null -ne $apiKeyFile -and $apiKeyFile -ne "")) { $apiKey = (Get-Pass -PasswordFile $apiKeyFile) } if ($Publish -and $Options.Result -ne "Failure") { PublishPackage -Destination $Options.OutputLocation -ApiKey $apiKey } else { $msg = "`r`n----------`r`n`r`n`tNo publishing." Out-File -FilePath $Options.OutputLogFile -InputObject $msg -Append } if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } #Write step result if ($Options.Result -eq "Success") { $stepName = "Package" $msg = "$stepName step passed." Write-Verbose $msg Out-File -FilePath $Options.OutputLogFile -InputObject "`r`n##### $($msg) #####`r`n" -Append } } function Invoke-ApexSqlNotifyStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $true)] [string[]] $DistributionList, [Parameter(Mandatory = $true)] [ValidateSet("started", "completed", "in progress")] [string] $Status, [string] $Subject, [string] $Body, [string] $FailureEnumeration, [int] $RetryCount = 3 ) if (-not $Subject) { $Subject = "%PipelineName% pipeline notification: %Status% at %DateTime%" if ($Status -ne "started") { $Subject += " with result: %Result%" } } if (-not $FailureEnumeration) { $FailureEnumeration = "%FailedSteps% with error code: %ErrorCodes%`r`n" } [bool] $pipelineResult = $true $stepList = "" for ($i=0; $i -lt $global:ResultSet.Count; $i++) { $stepList += "$($global:ResultSet[$i]["Step"])`r`n" $stepList += "`tstatus:`t$($global:ResultSet[$i]["Result"])`r`n" + (&{if ($($global:ResultSet[$i][1]) -eq "Failure") {"`t$(GetToolName $($global:ResultSet[$i]["Step"])) return code is: $($global:ResultSet[$i]["ErrorCode"])`r`n"} Else {""}}) + (&{if ($global:ResultSet[$i]["OutputFiles"].Length -gt 0) {"`toutput:`t$($global:ResultSet[$i]["OutputFiles"])`r`n`r`n"} Else {"`r`n"}}) if ($($global:ResultSet[$i]["Result"]) -eq "Failure") { $pipelineResult = $false } } $pipelineStarted = "$($Options.Timestamp.remove(2, 1).insert(2, "/").remove(5, 1).insert(5, "/").remove(10, 1).insert(10, " ").replace("-",":"))" $pipelineCompleted = Get-Date -Format "MM/dd/yyyy HH:mm:ss" $pipelineResultReport = @{$true = "Success"; $false = "Failure" }[$pipelineResult] $log = "Name: $($Options.PipelineName)`r`n`r`n" $pipelineReturnCode = @{$true = "0"; $false = "1" }[$pipelineResult] $log += "Started at $($pipelineStarted)`r`n" $log += "Completed at $($pipelineCompleted) with result: $($pipelineResultReport)`r`n" $log += "Return code is: $($pipelineReturnCode)`r`n`r`n" if ($null -ne $Options.FailedSteps) { $log += "Failed step:`r`n $($Options.FailedSteps) with error code: $($Options.ErrorCodes)`r`n`r`n" } $log += "Output files path: $($Options.OutputLocation)`r`n`r`n" $log += "`r`n`r`nPipeline steps:`r`n`r`n" $log += $stepList if ($null -ne $global:SkippingList) { $log += "`r`n==========`r`n`r`nSkipped steps:`r`n`r`n$($global:SkippingList)" } $log += "`r`n==========`r`n For more details check the $($Options.PipelineName)_job_summary.log file in attachment." $Body = "<HTML><HEAD><META http-equiv=""Content-Type"" content=""text/html; charset=iso-8859-1"" /><TITLE></TITLE></HEAD><BODY><p>" if ($Status -eq "started") { $Body = "%PipelineName% %Status% at %DateTime% " -replace "%PipelineName%" ,$Options.PipelineName ` -replace "%Status%", $Status ` -replace "%DateTime%", (&{ If ($Status -eq "started") { "$($pipelineStarted)" } Else { "$($pipelineCompleted)" }}) ` } else { $Body += $log.Replace("`r`n","<br>").Replace("`t"," ") } $Body += "</p></BODY></HTML>" $Subject = $Subject -replace "%PipelineName%", $Options.PipelineName ` -replace "%Status%", $Status ` -replace "%DateTime%", (&{ If ($Status -eq "started") { "$($pipelineStarted)" } Else { "$($pipelineCompleted)" }}) ` -replace "%Result%", $Options.Result ` -replace "%ErrorNum%", $Options.LastExitCode if (-not $Options.FailedSteps) { $failures = "N/A" } else { for ($i = 0; $i -lt $Options.FailedSteps.Count; $i++) { $failures += $FailureEnumeration -replace "%FailedSteps%",$Options.FailedSteps[$i] ` -replace "%ErrorCodes%",$Options.ErrorCodes[$i] } } $fullBody = $Body $mailprops=@{ Subject = $Subject Body = $fullBody To = $DistributionList From = $Options.NotificationSettings.Credential.UserName SmtpServer = $Options.NotificationSettings.SmtpServer UseSsl = $Options.NotificationSettings.UseSSL Port = $Options.NotificationSettings.Port Credential = $Options.NotificationSettings.Credential Attachment = $Options.OutputLogFile } for ($i = 0; $i -lt $RetryCount; $i++) { try { Send-MailMessage @mailprops -BodyAsHtml -ErrorAction:Stop $response = $null break } catch { $response = "`r`nNotification step failed after $RetryCount retries with message:`r`n$($_.Exception)" } } if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } #if ($response) #{ # Out-File -FilePath $Options.OutputLogFile -InputObject $response -Append #} if($Status -ne "start") { #Play beep sound [System.Media.SystemSounds]::Beep.Play() #Write step result ######if ($Options.Result -eq "Success") #####{ ##### $stepName = "Notify" ##### $msg = "$stepName step passed." ##### Write-Verbose $msg #Out-File -FilePath $Options.OutputLogFile -InputObject "`r`n##### $($msg) #####`r`n" -Append #####} } Write-Verbose("`r`n`r`n-----`r`n`r`n`r`n$($log)") } function Invoke-ApexSqlDocumentStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $false)] [ApexSqlDatabaseConnection] $Database, [Parameter(Mandatory = $true, ParameterSetName = "pdf")] [switch] $AsPdf, [Parameter(Mandatory = $true, ParameterSetName = "chm")] [switch] $AsChm, [Parameter(Mandatory = $false)] [bool] $StopOnFail = $true, [Parameter(Mandatory = $false)] [string] $ProjectFile, [Parameter(Mandatory = $false)] [string] $AdditionalOptions, [Parameter(Mandatory = $false)] [switch] $Differential ) if ($Options.Result -eq "Failure") { $global:SkippingList += "`tDocument`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL Document step due to failure in the pipeline" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } $project = Set-ProjectSwitch -Options $Options -ProjectFile $ProjectFile $additional = Set-AdditionalSwitch -Additional $AdditionalOptions #Output files names $sourceSwitches = "" $sourceSnapShot = "" $dbName = "" if ($null -ne $Database.ConnectionName) { $dbName = $Database.ConnectionName } else { $dbName = "Database" } if (!$Differential) { $reportName = "Document.$($PsCmdlet.ParameterSetName)" if ($null -eq $Database) { $sourceSwitches = "/dbsnp:""$($Options.OutputLocation)\Db_SnapShot.axsnp""" } else { $sourceSwitches = "$($Database.AsParameters("doc"))" } } else { $reportName = "DifferentialDocument.$($PsCmdlet.ParameterSetName)" $sourceSwitches = "/dbsnp:""$($Options.OutputLocation)\Db_SnapShot_Diff.axdsn""" } #Full tool parameters $($Database.AsParameters("doc")) $toolParameters = " /ot:$($PsCmdlet.ParameterSetName) /od:""$($Options.OutputLocation)""" + " /on:$reportName $project $additional $sourceSwitches /v /f" $params = @{ ToolName = "Doc" StepName = "Document" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail OutputFiles = $reportName } #Execute the tool Start-ApexSQLTool @params if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } } function Invoke-ApexSqlSchemaSyncStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $false)] [ApexSqlConnection] $Source, [Parameter(Mandatory = $false)] [ApexSqlConnection] $Target, [Parameter(Mandatory = $false)] [bool] $StopOnFail = $true, [Parameter(Mandatory = $false)] [string] $ProjectFile, [Parameter(Mandatory = $false)] [string] $AdditionalOptions, [Parameter(Mandatory = $false)] [switch] $NoReport, [Parameter(Mandatory = $false)] [switch] $NoScript, [Parameter(Mandatory = $false)] [switch] $SourceFromPipeline ) if ($Options.Result -eq "Failure") { $global:SkippingList += "`tSchemaSync`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL Schemasync step due to failure in the pipeline" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } $project = Set-ProjectSwitch -Options $Options -ProjectFile $ProjectFile -ReviewStep $additional = Set-AdditionalSwitch -Additional $AdditionalOptions #Output files names $schemaSyncScript = "$($Options.OutputLocation)\SchemaSync.sql" $schemaSyncReport = "$($Options.OutputLocation)\SchemaReport.html" $srcDefined = (&{ if ($Source) {$true} Else {$false}} ) $dstDefined = (&{ if ($Target) {$true} Else {$false}} ) #Check if source is .nupkg $SFPath = "" if (!$SourceFromPipeline) { if ($Source.NugetID) { if (-not ($Source.Version.Length -gt 0)) { ExtractNupkg -OutputLocation $Options.OutputLocation -NugetID $Source.NugetID -Source $Source.Source } else { ExtractNupkg -OutputLocation $Options.OutputLocation -NugetID $Source.NugetID -Version $Source.Version -Source $Source.Source } $dir = Get-ChildItem $Options.OutputLocation | Where-Object{ $_.PSIsContainer } | Sort-Object LastWriteTime | Select-Object -last 1 | Select-Object -ExpandProperty FullName #Clear all files and folders except DatabaseScripts (which should be used as source) if($null -ne $dir) { ClearExtractedContents -path $dir } $SFPath = Get-ChildItem $dir | Where-Object{ $_.PSIsContainer } | Where-Object{ $_.Name -like "*DatabaseScripts*" } | Select-Object -ExpandProperty FullName | Select-Object -Last 1 $sourceParameters = " /sf1:""$($SFPath)"" " $global:nugetDatabaseScriptsSource = $sourceParameters } elseif ($Source.GetType() -eq [ApexSqlSFConnection]) { $sourceParameters = "/script_folder1:""$($Source.FolderPath)""" } else{ $sourceParameters = "$($Source.AsParameters("diff1"))" } } else { $sourceParameters = " /sf1:""$($Options.OutputLocation)\DatabaseScripts"" " } if ($dstDefined) { $targetParameters = $($Target.AsParameters("diff2")) } else { $targetParameters = "" } #Full tool parameters for silent execution $toolParameters = "$($sourceParameters) /sn2:""$($Options.OutputLocation)\Db_SnapShot.axsnp"" /export /v /f /rece" $params = @{ ToolName = "Diff" StepName = "SchemaSync" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail Silent = $true } #Silent execute the tool Start-ApexSQLTool @params #Define outputs for tool parameters #region tool parameters outputs $OutputFiles = "" $script = "" if (!$NoScript) { $script = " /ot2:sql /on2:""$schemaSyncScript""" $OutputFiles += if ($OutputFiles.Length -gt 0) {", SchemaSync.sql"} else {"SchemaSync.sql"} } $report = "" if (!$NoReport) { $report = " /ot:html /on:""$schemaSyncReport""" $OutputFiles += if ($OutputFiles.Length -gt 0) {", SchemaReport.html"} else {"SchemaReport.html"} } #endregion #Full tool parameters $toolParameters = "$($sourceParameters) $($targetParameters) $report $script $project $additional /dsn:""$($Options.OutputLocation)\Db_SnapShot_Diff.axdsn"" /v /f /rece" $params = @{ ToolName = "Diff" StepName = "SchemaSync" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail OutputFiles = if ($OutputFiles.Length -gt 0) {$OutputFiles} else {$null} } #Execute the tool Start-ApexSQLTool @params #Scripting source $canRunScript = $false if (!$SourceFromPipeline) { #script source $sourceScriptPath = "$($Options.OutputLocation)\SchemaSync_source_scripts"; if(!(Test-Path $sourceScriptPath)) { New-Item -ItemType Directory -Force -Path $sourceScriptPath } if ($Source.NugetID) { Copy-Item -Force -Recurse -Verbose "$SFPath\*" -Destination $sourceScriptPath } elseif ($Source.GetType() -eq [ApexSqlSFConnection]) { Copy-Item -Force -Recurse -Verbose "$($Source.FolderPath)\*" -Destination $sourceScriptPath } elseif ($Source.GetType() -eq [ApexSqlSourceControlConnection]) { $canRunScript = $true $toolScriptParameters = "$sourceParameters /script_folder2:$sourceScriptPath /synchronize /v /f" } elseif ($Source.GetType() -eq [ApexSqlDatabaseConnection]) { $canRunScript = $true $sourceScriptParameters = $Source.AsParameters() $toolScriptParameters = "$sourceScriptParameters /exc:16384:SQLCop:tSQLt /exc:134217728:tSQLtCLR /eso /in /fl:""$sourceScriptPath"" /v /f" } if ($srcDefined -and $canRunScript -and $toolScriptParameters -ne "") { $params = @{ ToolName = "Script" StepName = "SchemaSync" ToolParameters = $toolScriptParameters Options = $Options StopOnFail = $true OutputFiles = if ($OutputFiles.Length -gt 0) {$OutputFiles} else {$null} } Start-ApexSQLTool @params } } #Scripting target if ($dstDefined) { $targetScriptPath = "$($Options.OutputLocation)\SchemaSync_target_scripts"; if(!(Test-Path $targetScriptPath)) { New-Item -ItemType Directory -Force -Path $targetScriptPath } $toolParameters = "$($Target.AsParameters()) /exc:16384:SQLCop:tSQLt /exc:134217728:tSQLtCLR /eso /in /fl:""$targetScriptPath"" /v /f" $params = @{ ToolName = "Script" StepName = "SchemaSync" ToolParameters = $toolParameters Options = $Options StopOnFail = $true OutputFiles = if ($OutputFiles.Length -gt 0) {$OutputFiles} else {$null} } Start-ApexSQLTool @params } if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } } function Invoke-ApexSqlDataSyncStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $false)] [ApexSqlConnection] $Source, [Parameter(Mandatory = $false)] [ApexSqlConnection] $Target, [Parameter(Mandatory = $false)] [bool] $StopOnFail = $true, [Parameter(Mandatory = $false)] [string] $ProjectFile, [Parameter(Mandatory = $false)] [string] $AdditionalOptions, [Parameter(Mandatory = $false)] [switch] $NoReport, [Parameter(Mandatory = $false)] [switch] $NoScript, [Parameter(Mandatory = $false)] [switch] $SourceFromPipeline ) if ($Options.Result -eq "Failure") { $global:SkippingList += "`tDataSync`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL DataSync step due to failure in the pipeline" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } $project = Set-ProjectSwitch -Options $Options -ProjectFile $ProjectFile -ReviewStep $additional = Set-AdditionalSwitch -Additional $AdditionalOptions #Output files names $dataSyncScript = "$($Options.OutputLocation)\DataSync.sql" $dataSyncReport = "$($Options.OutputLocation)\DataReport.html" $sourceParameters = "" if (!$SourceFromPipeline) { #Check if .nupkg sources is already configured if ($null -ne $global:nugetDatabaseScriptsSource) { $sourceParameters = $global:nugetDatabaseScriptsSource } else { #Check if source is .nupkg if ($Source.NugetID) { ExtractNupkg -OutputLocation $Options.OutputLocation -NugetID $Source.NugetID -Version $Source.Version -Source $Source.Source $dir = Get-ChildItem $Options.OutputLocation | Where-Object{ $_.PSIsContainer } | Sort-Object LastWriteTime | Select-Object -last 1 | Select-Object -ExpandProperty FullName #Clear all files and folders except DatabaseScripts (which should be used as source) if($null -ne $dir) { ClearExtractedContents -path $dir } $SFPath = Get-ChildItem $dir | Where-Object{ $_.PSIsContainer } | Where-Object{ $_.Name -like "*DatabaseScripts*" } | Select-Object -ExpandProperty FullName | Select-Object -Last 1 $sourceParameters = " /sf1:""$($SFPath)"" " } else { $sourceParameters = "$($Source.AsParameters("diff1"))" } } } else { $sourceParameters = " /sf1:""$($Options.OutputLocation)\DatabaseScripts"" " } #Define outputs for tool parameters #region tool parameters outputs $OutputFiles = "" $report = "" if (!$NoReport) { $report = " /ot:html /on:""$dataSyncReport""" $OutputFiles += if ($OutputFiles.Length -gt 0) {", DataReport.html"} else {"DataReport.html"} } $script = "" if (!$NoScript) { $script = " /ot2:sql /on2:""$dataSyncScript""" $OutputFiles += if ($OutputFiles.Length -gt 0) {", DataSync.sql"} else {"DataSync.sql"} } #endregion #Full tool parameters $toolParameters = "$($sourceParameters) $($Target.AsParameters("diff2")) $report $script $project$additional /v /f" $params = @{ ToolName = "Data Diff" StepName = "DataSync" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail OutputFiles = if ($OutputFiles.Length -gt 0) {$OutputFiles} else {$null} } #Execute the tool Start-ApexSQLTool @params if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } } function Invoke-ApexSqlDeployStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $false)] [ApexSqlConnection] $Source, [Parameter(Mandatory = $true)] [ValidateSet("Schema", "Data", "Both")] [string] $DeployType, [Parameter(Mandatory = $false)] [switch] $UseCurrentPackage, [Parameter(Mandatory = $false)] [switch] $PreDeployment, [Parameter(Mandatory = $false)] [switch] $PostDeployment, [Parameter(Mandatory = $true)] [ApexSqlDatabaseConnection[]] $Databases ) if ($Options.Result -eq "Failure") { $global:SkippingList += "`tDeploy`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL Deploy step due to failure in the pipeline" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } $info = "`r`n`r`n=============================`r`n" + "----- Started Deploying -----`r`n" + "-----------------------------`r`n" Out-File -FilePath $Options.OutputLogFile -InputObject $info -Append try { if ($UseCurrentPackage) { $PackageFilePath = $Options.PackageFilePath ExtractNupkg -NugetID $global:nugetId -Source $Options.OutputLocation -OutputLocation "$($Options.OutputLocation)\" } else { if($null -ne $Source) { if ($null -ne $Source.Version) { ExtractNupkg -NugetID $Source.NugetID -Source $Source.Source -OutputLocation $Options.OutputLocation } else { ExtractNupkg -NugetID $Source.NugetID -Source $Source.Source -Version $Source.Version -OutputLocation $Options.OutputLocation } } } $lookAtDir = $null if(!$UseCurrentPackage) { $lookAtDir = $Options.OutputLocation } else { $lookAtDir = Get-ChildItem $Options.OutputLocation | Where-Object{ $_.PSIsContainer } | Sort-Object LastWriteTime | Select-Object -last 1 | Select-Object -ExpandProperty FullName } $lookAtDir = Get-ChildItem -Path $Options.OutputLocation $schemaScripts = $lookAtDir | Where-Object -FilterScript {$_.Name -like "SchemaSync.sql"} $dataScripts = $lookAtDir | Where-Object -FilterScript {$_.Name -like "DataSync.sql"} $consolidatedScript = $lookAtDir | Where-Object -FilterScript {$_.Name -like "*Consolidated_script.sql"} $schemaOK = ($DeployType -eq "Schema") -and ($schemaScripts.Count -eq 1) -and $consolidatedScript.Count -eq 0 $dataOK = ($DeployType -eq "Data") -and ($dataScripts.Count -eq 1) -and $consolidatedScript.Count -eq 0 $bothOK = ($DeployType -eq "Both") -and ($schemaScripts.Count -eq 1 -or $dataScripts.Count -eq 1 -or $consolidatedScript.Count -eq 1 ) if (($schemaOK -eq $false -and $dataOK -eq $false) -and $bothOK -eq $false) { $errorText = "There are no schema or data differences.`r`n" + "`tProcess terminated." LogFail -FilePath $Options.OutputLogFile -ErrorText $errorText $Options.ErrorCodes += 102 $Options.FailedSteps += @("ApexSQL Deploy") $Options.Result = "Failure" $global:ResultSet.Add($global:ResultSet.Count, @{Step="Deploy"; Result='Failure'; ErrorCode=$lastExitCode;}) return } foreach ($database in $Databases) { if($PreDeployment){ if(!(New-DeploymentValidation -ExecutionFolder $Options.OutputLocation -ScriptName "SchemaSync_target_scripts" -HtmlReportName "PreValidationReport.html" -Database $database -StepName "Deploy")){ return } } if ($database.WindowsAuthentication) { $credentials = " -E" } else { $credentials = " -U ""$($database.UserName)"" -P ""$($database.Password)""" } $sqlcmdProps = "sqlcmd.exe -S ""$($database.Server)"" -d ""$($database.Database)""$credentials -b -i" if (($schemaOK -or $bothOK) -and $null -ne $schemaScripts) { $schema = $schemaScripts[0].FullName $result = Invoke-Expression -Command "$sqlcmdProps ""$schema""" Out-File -FilePath $Options.OutputLogFile -InputObject $result -Append if ($LastExitCode -ne 0) { $Options.ErrorCodes += @($LastExitCode) $Options.FailedSteps += @("ApexSQL Deploy") $Options.Result = "Failure" throw "Schema synchronization failed" } } if (($dataOK -or $bothOK) -and $null -ne $dataScripts) { $data = $dataScripts[0].FullName $result = Invoke-Expression -Command "$sqlcmdProps ""$data""" Out-File -FilePath $Options.OutputLogFile -InputObject $result -Append if ($LastExitCode -ne 0) { $Options.ErrorCodes += @($LastExitCode) $Options.FailedSteps += @("ApexSQL Deploy") $Options.Result = "Failure" throw "Data synchronization failed" } } } if($PostDeployment){ if(!(New-DeploymentValidation -ExecutionFolder $Options.OutputLocation -ScriptName "SchemaSync_source_scripts" -HtmlReportName "PostValidationReport.html" -Database $database -StepName "Deploy")){ return } } #Write step result $stepName = "Deploy" LogFail -FilePath $Options.OutputLogFile -ErrorText "$stepName step passed." $global:ResultSet.Add($global:ResultSet.Count, @{Step="Deploy"; Result='Success';}) } catch { $Options.FailedSteps += @("ApexSQL Deploy") $Options.ErrorCodes += @($lastExitCode) if ($StopOnFail) { $stepName = "Deploy" $Options.Result = "Failure" $msg = "$stepName step failed." Write-Warning $msg LogFail -FilePath $Options.OutputLogFile -ErrorText "`r`nApexSQL $ToolName failed.`r`nThe process is canceled due to failure return code: $lastExitCode" return $false } else { $Options.Result = "Completed with errors" } Out-File -FilePath $Options.OutputLogFile -InputObject $_.Message -Append } if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } } function Invoke-ApexSqlMaskStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $true)] [ApexSqlConnection] $Database, [Parameter(Mandatory = $true)] [string] $ProjectFile, [Parameter(Mandatory = $false)] [string] $AdditionalOptions, [Parameter(Mandatory = $false)] [switch] $UseCurrentPackage, [Parameter(Mandatory = $false)] [bool] $StopOnFail = $true ) if ($Options.Result -eq "Failure") { $global:SkippingList += "`tMask`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL Mask step due to failure in the pipeline" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } Out-File -FilePath $Options.OutputLogFile -InputObject $info -Append try { if ($UseCurrentPackage) { ExtractNupkg -NugetID $global:nugetId -Source $Options.OutputLocation -OutputLocation "$($Options.OutputLocation)\" } else { if ($null -ne $Source.Version) { ExtractNupkg -NugetID $Source.NugetID -Source $Source.Source -OutputLocation $Options.OutputLocation } else { ExtractNupkg -NugetID $Source.NugetID -Source $Source.Source -Version $Source.Version -OutputLocation $Options.OutputLocation } } $project = Set-ProjectSwitch -Options $Options -ProjectFile $ProjectFile $additional = Set-AdditionalSwitch -Additional $AdditionalOptions #Full tool parameters $toolParameters = "$($Database.AsParameters()) $project $additional /v /f" $params = @{ ToolName = "Mask" StepName = "Mask" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail OutputFiles = if ($OutputFiles.Length -gt 0) {$OutputFiles} else {$null} } } catch { $Options.FailedSteps += @("ApexSQL Mask") $Options.ErrorCodes += @($lastExitCode) if ($StopOnFail) { $stepName = "Mask" $Options.Result = "Failure" $msg = "$stepName step failed." Write-Warning $msg LogFail -FilePath $Options.OutputLogFile -ErrorText "`r`nApexSQL $ToolName failed.`r`nThe process is canceled due to failure return code: $lastExitCode" return $false } else { $Options.Result = "Completed with errors" } Out-File -FilePath $Options.OutputLogFile -InputObject $_.Message -Append } #Execute the tool Start-ApexSQLTool @params if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } } function Invoke-ApexSqlCompareStep { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ApexSqlOptions] $Options, [Parameter(Mandatory = $true)] [ApexSqlConnection] $Source, [Parameter(Mandatory = $true)] [ApexSqlConnection] $Target, [Parameter(Mandatory = $false)] [bool] $StopOnFail = $true ) if ($Options.Result -eq "Failure") { $global:SkippingList += "`tCompare`r`n" LogFail -FilePath $Options.OutputLogFile -ErrorText "Skipping ApexSQL Compare step due to failure in the pipeline" if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } else { return } } Out-File -FilePath $Options.OutputLogFile -InputObject $info -Append try { $summaryFile = "$($Options.OutputLocation)\CompareSummary.txt"; $reportFile = "$($Options.OutputLocation)\CompareStep_summary.html"; #Full tool parameters $toolParameters = "$($Source.AsParameters("diff1")) $($Target.AsParameters("diff2")) /ot:html /on:""$reportFile"" /cso:""$summaryFile"" /v /f /rece" $params = @{ ToolName = "Diff" StepName = "Compare" ToolParameters = $toolParameters Options = $Options StopOnFail = $StopOnFail OutputFiles = if ($OutputFiles.Length -gt 0) {$OutputFiles} else {$null} } } catch { $Options.FailedSteps += @("ApexSQL Compare") $Options.ErrorCodes += @($lastExitCode) if ($StopOnFail) { $stepName = "Compare" $Options.Result = "Failure" $msg = "$stepName step failed - Differences found" Write-Warning $msg LogFail -FilePath $Options.OutputLogFile -ErrorText "`r`nApexSQL $ToolName failed.`r`nThe process is canceled due to failure return code: $lastExitCode" return $false } else { $Options.Result = "Completed with errors" } Out-File -FilePath $Options.OutputLogFile -InputObject $_.Message -Append } #Execute the tool Start-ApexSQLTool @params if ($PSCmdlet.MyInvocation.ExpectingInput) { return $Options } } function New-DeploymentValidation{ [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [string] $ExecutionFolder, [Parameter(Mandatory = $true)] [string] $ScriptName, [Parameter(Mandatory = $true)] [string] $HtmlReportName, [Parameter(Mandatory = $true)] [ApexSqlConnection] $Database, [Parameter(Mandatory = $true)] [string] $StepName ) $targetScriptPath = "$ExecutionFolder\$ScriptName" $xmlReport = "$ExecutionFolder\ValidationReport.xml" $htmlReport = "$ExecutionFolder\$HtmlReportName" $toolParameters = " /sf1:""$targetScriptPath"" $($Database.AsParameters("diff2")) /ot:xml /on:""$xmlReport"" /ot2:html /on2:""$htmlReport"" /v /f /rece" $params = @{ ToolName = "Diff" StepName = $StepName ToolParameters = $toolParameters Options = $Options StopOnFail = $true OutputFiles = if ($OutputFiles.Length -gt 0) {$OutputFiles} else {$null} } return Start-ApexSQLTool @params } |