Functions/Get-DacPacHash.ps1
|
<#
.SYNOPSIS Calculates a hash for a DACPAC file and its dependencies. .DESCRIPTION The Get-DacPacHash function generates a SHA256 hash for a DACPAC file by analyzing its model.xml content, referenced DACPACs, and pre/post deployment scripts. This hash can be used to determine if the DACPAC has changed since a previous deployment. .PARAMETER dacpacPath Specifies the path to the DACPAC file. Can be a relative path if rootPath is provided, or an absolute path. .PARAMETER rootPath Optional. Specifies the root path to use when dacpacPath is relative. If not provided, the function will use the directory containing the DACPAC file as the root path. .OUTPUTS String Returns a SHA256 hash string representing the DACPAC content and its dependencies. .EXAMPLE Get-DacPacHash -dacpacPath "C:\MyProject\bin\MyDatabase.dacpac" Calculates the hash for the specified DACPAC file. .EXAMPLE Get-DacPacHash -dacpacPath "MyDatabase.dacpac" -rootPath "C:\MyProject\bin" Calculates the hash using a relative path and specified root directory. .NOTES This function will recursively calculate hashes for any referenced DACPACs. Pre-deployment and post-deployment scripts are included in the hash calculation. #> function Get-DacPacHash { [CmdletBinding()] [OutputType([String])] param ( [Parameter(Mandatory = $true, HelpMessage = "Path to the DACPAC file")] [string]$dacpacPath, [Parameter(HelpMessage = "Root path for relative dacpacPath resolution")] $rootPath ) [xml]$dacpacXml = New-Object xml $dacPacZipModelStream = $null $IsRootDacPac = $null -eq $rootPath try { if ($null -eq $rootpath){ $dacpacitem = (Get-Item $dacpacPath) $FulldacPacPath = $dacpacitem.FullName $rootpath = $dacpacitem.Directory } else{ $FulldacPacPath = Join-Path $rootPath $dacpacPath } Write-Verbose "getting DacPac hash for $FulldacPacPath" $Zip = [io.compression.zipfile]::OpenRead($FulldacPacPath) } catch [System.IO.FileNotFoundException], [System.Management.Automation.ItemNotFoundException] { throw "Can't open dacpac file $dacpacPath doesn't exist" } catch { $Ex = New-Object System.Exception ("Error reading dacpac $dacpacPath probably not a valid dacpac", $_.Exception) throw $ex } try { if (-not ($Zip.Entries.Name -eq "model.xml")) { Throw "Can't find the model.xml file in the dacpac, would guess this isn't a dacpac" } $dacPacZipModelStream = $Zip.GetEntry("model.xml").Open() $dacpacXml.Load($dacPacZipModelStream) $checksum = Get-ModelChecksum $dacpacXml; foreach ($dacpac in (Get-ReferencedDacpacsFromModel -modelxml $dacpacXml)) { $checksum += Get-DacPacHash -dacpacPath $dacpac -rootPath $rootPath } if ($IsRootDacPac) { $Zip.Entries | Where-Object { $_.Name -in ("predeploy.sql", "postdeploy.sql")} | ForEach-Object { $stream = $Zip.GetEntry($_.Name).open() $checksum += (Get-FileHash -InputStream $stream -Algorithm SHA256).Hash; $stream.Close(); $stream.Dispose(); } } } catch { Throw } finally { if ($null -ne $dacPacZipModelStream) { $dacPacZipModelStream.Close() $dacPacZipModelStream.Dispose() } if ($null -ne $Zip) { $Zip.Dispose() } } return $checksum } |