Functions/Test-ShouldDeployDacpac.ps1

<#
.SYNOPSIS
    Determines whether a DACPAC should be deployed to a database.
 
.DESCRIPTION
    The Test-ShouldDeployDacpac function analyzes various conditions to determine if a DACPAC file
    should be deployed to a target database. It checks for database existence, compares deployment
    settings, examines file dates, and optionally compares hashes to make the deployment decision.
 
.PARAMETER settings
    Specifies the settings object containing connection and deployment configuration information.
    This can include properties like serverName, TargetDatabaseName, sqlAdminLogin, etc.
 
.PARAMETER SettingsToCheck
    Optional. Specifies the specific settings to monitor for changes. If not provided, default
    settings will be retrieved based on the DACPAC file and connection settings.
 
.PARAMETER dacpacfile
    Specifies the path to the DACPAC file to evaluate for deployment.
 
.PARAMETER publishFile
    Specifies the path to the publish profile file. Currently not used for change detection
    but included for potential future use.
 
.PARAMETER DBDeploySettingsFile
    Optional. Specifies the path to a file-based deployment settings file. If provided, the function
    will use file-based comparison instead of database-based comparison.
 
.PARAMETER IgnoreDate
    Optional switch. When specified, ignores the DACPAC file date when determining if deployment
    is needed. Only settings changes will trigger deployment.
 
.PARAMETER CompareHash
    Optional switch. When specified, compares the DACPAC hash with the previously stored hash
    to determine if the DACPAC content has changed.
 
.OUTPUTS
    Boolean
    Returns $true if the DACPAC should be deployed, $false otherwise.
 
.EXAMPLE
    $settings = @{
        serverName = "localhost"
        TargetDatabaseName = "MyDB"
        sqlAdminLogin = "sa"
        sqlAdminPassword = "password"
    }
    Test-ShouldDeployDacpac -settings $settings -dacpacfile "C:\MyApp.dacpac"
     
    Determines if MyApp.dacpac should be deployed to MyDB on localhost.
 
.EXAMPLE
    Test-ShouldDeployDacpac -settings $settings -dacpacfile "MyApp.dacpac" -CompareHash
     
    Uses hash comparison to determine if the DACPAC content has changed since last deployment.
 
.EXAMPLE
    Test-ShouldDeployDacpac -settings $settings -dacpacfile "MyApp.dacpac" -IgnoreDate
     
    Determines deployment need based only on settings changes, ignoring file modification dates.
 
.NOTES
    The function checks multiple conditions for deployment:
    - Database doesn't exist
    - No previous deployment settings found
    - Deployment settings have changed
    - DACPAC file is newer than last deployment (unless IgnoreDate is used)
    - DACPAC hash has changed (if CompareHash is used)
     
    If any error occurs during evaluation, the function defaults to returning $true (deploy).
#>

Function Test-ShouldDeployDacpac {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "", Justification = "Need to cater for users that want to pass strings ")]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "Settings object containing deployment configuration")]
        $settings,
        
        [Parameter(HelpMessage = "Specific settings to monitor for changes")]
        $SettingsToCheck,
        
        [Parameter(Mandatory = $true, HelpMessage = "Path to the DACPAC file")]
        [string]$dacpacfile,
        
        [Parameter(HelpMessage = "Path to the publish profile file")]
        [string]$publishFile,
        
        [Parameter(HelpMessage = "Path to file-based deployment settings")]
        [string]$DBDeploySettingsFile,
        
        [Parameter(HelpMessage = "Ignore DACPAC file date when determining deployment")]
        [Switch]$IgnoreDate,
        
        [Parameter(HelpMessage = "Compare DACPAC hash to determine if content changed")]
        [switch]$CompareHash
    )
    
    $shouldDeploy = $false
    $TargetUser = ""
    $TargetServer = ""
    $TargetPasswordSecure = $null

    if ($settings.sqlAdminLogin) {
        $TargetUser = $settings.sqlAdminLogin
        $TargetPasswordSecure = $settings.sqlAdminPassword | ConvertTo-SecureString -AsPlainText -Force
    }
    if ($settings.TargetUser) {
        $TargetUser = $settings.TargetUser
        $TargetPasswordSecure = $settings.TargetPasswordSecure
    }

    if ($settings.TargetDatabaseName) {
        $TargetDatabaseName = $settings.TargetDatabaseName
    }

    if ($settings.serverName) {
        $TargetServer = $settings.serverName
    }
    if ($settings.TargetServerName) {
        $TargetServer = $settings.TargetServerName
    }
    if ($null -eq $settings.publishFile){
        $settings.publishFile = $publishFile
    }
    if ($null -ne $Settings.publishFile) {
        Write-Host "Publish File is not currently used to check if settings have changed. Consider adding publish file as a value in the settings parameter"
    }
    
    #Check date of dacpac against last deployment time
    $dacpacDate = (Get-Item $dacpacfile).LastWriteTimeUtc
    
    if  ($null -eq $SettingsToCheck){
        $SettingsToCheck = Get-DefaultSettingsToCheck -dacpacfile $dacpacfile  @settings     
    }

    try {
        Write-Host "Checking if we should deploy database"
        $databaseExists = Test-DatabaseExists `
            -TargetServer $TargetServer `
            -TargetUser $TargetUser `
            -TargetPasswordSecure $TargetPasswordSecure `
            -TargetDatabaseName $TargetDatabaseName

        Write-Verbose "Database exists query result ($databaseExists)"
        if (-not $databaseExists) {
            Write-Host "Should deploy because database doesn't exist"
            $shouldDeploy = $true
        }
        else {
            #Get latest deploy for the required dacpac
            if ([string]::IsNullOrWhiteSpace($DBDeploySettingsFile)) {

          
                $dacpacname = (Get-Item $dacpacfile).basename 

                $SettingsFromDB = Get-DeploySettingsFromDB -DacpacName $dacpacName -Server $TargetServer -User $TargetUser -PasswordSecure $TargetPasswordSecure -Database $TargetDatabaseName
                
                if ($null -eq $SettingsFromDB) {
                    Write-Host "no settings in DB need to deploy"
                    $shouldDeploy = $true
                }
                elseif (Test-HaveDeploySettingsChangedSinceLastDeploy -OldSettings $SettingsFromDB.SettingsToCheck -Settings $SettingsToCheck) {
                    Write-Host "ShouldDeploy? Yes - settings have changed"
                    $shouldDeploy = $true
                }
                elseif (-not $IgnoreDate -and $SettingsFromDB.LastDeployDate -lt $dacpacDate) {
                    Write-Host "last deploy date < dacpac date so we do need to deploy the database"
                    $shouldDeploy = $true
                }   
                elseif($CompareHash){
                    $Hash = Get-DacpacHash $dacpacfile
                    $shouldDeploy = ($SettingsFromDB.Hash -ne $Hash)
                }
            }
            else {
                #previous behaviour using a DBDeploySettingsFile

                if (Test-IsPreviousDeploySettingsFileMissing -DBDeploySettingsFile $DBDeploySettingsFile) {
                    Write-Host "ShouldDeploy? Yes - no settings file"            
                    $shouldDeploy = $true
                }
                elseif (Test-HaveDeploySettingsChangedSinceLastDeploy -OldSettings (Get-DeploySettingsFromFile -DBDeploySettingsFile $DBDeploySettingsFile ) -Settings $SettingsToCheck) {
                    Write-Host "ShouldDeploy? Yes - settings have changed"
                    $shouldDeploy = $true
                }
                else {
                    $dacpacname = (Get-Item $dacpacfile).basename 

                    $SettingsFromDB = Get-DeploySettingsFromDB -DacpacName $dacpacName -Server $TargetServer -User $TargetUser -PasswordSecure $TargetPasswordSecure -Database $TargetDatabaseName
                    
                    if ($null -eq $SettingsFromDB ) {
                        Write-Host "no settings found in Db for $dacpacname"
                        $shouldDeploy = $true
                    }
                    elseif (-not $IgnoreDate -and $SettingsFromDB.LastDeployDate -lt $dacpacDate) {
                        Write-Host "last deploy date < dacpac date so we do need to deploy the database"
                        $shouldDeploy = $true
                    }
                }
            }
        
    
        }
    }
    catch {
        Write-Host "Error Occurred -verbose logging for more detail if required"
        Write-Verbose  ($_ | Format-Table | Out-String)
        $shouldDeploy = $true
    }

    Write-Verbose "Returning from Test-ShouldDeployDacpac with ,$shouldDeploy = $shouldDeploy"

    return $shouldDeploy
}