Functions/Invoke-DatabaseDacpacDeploy.ps1
|
Function Invoke-DatabaseDacpacDeploy { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "", Justification = "OutputDeployScript is reported as not being used but it is")] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$dacpacfile, [Parameter(Mandatory = $true)] [string]$sqlpackagePath, [Parameter(Mandatory = $true)] [string]$action, [Parameter(Mandatory = $true)] [string]$scriptParentPath, #Parameter set TargetServer and Database or ConnectionString [Parameter(Mandatory = $true, ParameterSetName = "IndividualTarget")] [string]$TargetServerName, [Parameter(Mandatory = $true, ParameterSetName = "IndividualTarget")] [string]$TargetDatabaseName, [string]$TargetUser, [securestring]$TargetPasswordSecure, [string]$TargetIntegratedSecurity , [switch]$TargetTrustServerCert, [Parameter(Mandatory = $true, ParameterSetName = "ConnectionStringTarget")] [string]$TargetConnectionString, $ServiceObjective, [String]$AccessToken, [SecureString]$AccessTokenSecure, [string]$TenantId, [string]$ClientId, [string]$ClientSecret, [securestring]$ClientSecretSecure, [string]$PublishFile, [Switch]$OutputDeployScript, [Parameter(Mandatory = $true)] $Variables, [Parameter(Mandatory = $true)] $TargetTimeout, [Parameter(Mandatory = $true)] $CommandTimeout, $SettingsToCheck, [string]$DBScriptPrefix ) try { $EntraSecurity = $false if ($TargetUser) { #Used to be able to extract the password and pass to SQLPackage $TargetCredentials = New-Object System.Management.Automation.PSCredential($TargetUser, $TargetPasswordSecure ) $Security = "/TargetUser:$($TargetCredentials.UserName)", "/TargetPassword:`$(`$TargetCredentials.GetNetworkCredential().Password)" } else { if ($AccessToken -or $AccessTokenSecure -or ( $ClientId -and ( $ClientSecret -or $ClientSecretSecure) -and $TenantId ) ) { $EntraSecurity = $true } else { $TargetIntegratedSecurity = $true } $Security = @() } Write-DBDeployParameterLog -dacpacfile $dacpacfile ` -action $action ` -TargetConnectionString $TargetConnectionString ` -TargetServerName $TargetServerName ` -TargetDatabaseName $TargetDatabaseName ` -TargetIntegratedSecurity $TargetIntegratedSecurity ` -EntraSecurity $EntraSecurity ` -ServiceObjective $ServiceObjective ` -PublishFile $PublishFile ` -Variables $Variables ` -TargetTimeout $TargetTimeout ` -CommandTimeout $CommandTimeout ` -sqlpackagePath $sqlpackagePath ` -Username $TargetUser ` -scriptParentPath $scriptParentPath -ErrorAction Stop $DeployProperties = Get-DeployPropertiesHash -action $action ` -TargetServerName $TargetServerName ` -TargetDatabaseName $TargetDatabaseName ` -TargetUser $TargetUser ` -TargetPasswordSecure $TargetPasswordSecure ` -TargetIntegratedSecurity $TargetIntegratedSecurity ` -ServiceObjective $ServiceObjective ` -PublishFile $PublishFile ` -Variables $Variables ` -TargetTimeout $TargetTimeout ` -dacpacfile $dacpacfile ` -SettingsToCheck $SettingsToCheck $sqlPackageCommand = New-Object Collections.Generic.List[String] Add-ToList $sqlPackageCommand "/Action:$Action" Add-ToList $sqlPackageCommand "/TargetTimeout:$TargetTimeout" if ([string]::IsNullOrWhiteSpace($DBScriptPrefix) ) { $DBScriptPrefix = [io.path]::GetFileNameWithoutExtension($dacpacfile) } # Handle database name extraction for path construction - extract database name from connection string if needed $DatabaseNameForPath = $TargetDatabaseName if ($TargetConnectionString -and [string]::IsNullOrWhiteSpace($TargetDatabaseName)) { # Extract database name from connection string if ($TargetConnectionString -match "Database=([^;]+)") { $DatabaseNameForPath = $matches[1] } elseif ($TargetConnectionString -match "Initial Catalog=([^;]+)") { $DatabaseNameForPath = $matches[1] } else{ throw "Unable to extract database name from connection string. Please ensure the connection string contains 'Database' or 'Initial Catalog' parameter." } } if ($TargetConnectionString) { Add-ToList $sqlPackageCommand "/TargetConnectionString:$TargetConnectionString" } else { $TargetDatabase = "/TargetServerName:$TargetServerName", "/TargetDatabaseName:$TargetDatabaseName" } If ($Action -eq "DriftReport") { if ($Variables -Contains "/TargetTrustServerCertificate:true"){ Add-ToList $sqlPackageCommand ("/TargetTrustServerCertificate:True") } Add-ToList $sqlPackageCommand ("/OutputPath:{0}" -f [IO.Path]::Combine($ScriptParentPath, $DatabaseNameForPath, "$DBScriptPrefix`_drift.xml")) } else { if ($PublishFile) { Add-ToList $sqlPackageCommand "/Profile:$publishFile" } if ($ServiceObjective) { Add-ToList $sqlPackageCommand "/p:DatabaseServiceObjective=$ServiceObjective" } Add-ToList $sqlPackageCommand "/SourceFile:$dacpacFile" Add-ToList $sqlPackageCommand "/v:DeployProperties=`"$(Convert-ToSQLPackageSafeString $DeployProperties)`"" Add-ToList $sqlPackageCommand -items $Variables Add-ToList $sqlPackageCommand "/p:CommandTimeout=$CommandTimeout" if ($Action -eq "Publish") { Add-ToList $sqlPackageCommand ("/DeployScriptPath:{0}" -f [IO.Path]::Combine($ScriptParentPath, $DatabaseNameForPath, "$DBScriptPrefix`_db.sql")) } elseif ($Action -eq "Script") { Add-ToList $sqlPackageCommand ("/DeployScriptPath:{0}" -f [IO.Path]::Combine($ScriptParentPath, $DatabaseNameForPath, "$DBScriptPrefix`_db.sql")) } } if (-not $TargetConnectionString) { Add-ToList $sqlPackageCommand -items $TargetDatabase } if ($TargetTrustServerCert) { Add-ToList $sqlPackageCommand "/TargetTrustServerCertificate:True" } if ($AccessToken -or $AccessTokenSecure -or ( $ClientId -and ( $ClientSecret -or $ClientSecretSecure) -and $TenantId ) ) { if ($AccessTokenSecure -or $AccessToken) { Write-Host "Using Access Token provided clientId and secret being ignored" } else{ Write-Host "Getting Access Token using ClientId and ClientSecret" $AccessTokenSecure = (Get-EntraAccessToken -ClientId $ClientId -ClientSecret $ClientSecret -ClientSecretSecure $ClientSecretSecure -TenantId $TenantId).AccessToken } if ($AccessTokenSecure -is [System.Security.SecureString]) { Write-Host "Extracting Access Token from SecureString" $cred = New-Object System.Management.Automation.PSCredential ("user", $AccessTokenSecure) $AccessToken = $cred.GetNetworkCredential().Password } write-host "Access token first 10 and last 10 characters: $($AccessToken.Substring(0,10))...$($AccessToken.Substring($AccessToken.Length - 10))" $Security += "/AccessToken:$AccessToken" } # $sqlPackageCommand +="/p:CommentOutSetVarDeclarations=true New-Item $ScriptParentPath\$DatabaseNameForPath -ItemType "Directory" -Force | Out-null if ($env:SYSTEM_DEBUG) { $sqlPackageCommand } Add-ToList $sqlPackageCommand -items $Security Function Get-SqlPackageArgument { $sqlPackageCommand | ForEach-Object { if ($_ -like "*/v:DeployProperties*" ) { $_ } else { $ExecutionContext.InvokeCommand.ExpandString($_) } } } (Get-SqlPackageArgument) | ForEach-Object { Write-Verbose $_ } $ErrorActionPreference = "Continue" $LASTEXITCODE = 0 [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'PS variable used internally by PS not by our code')] $PSNativeCommandArgumentPassing ="legacy" invoke-command -ScriptBlock { &$sqlpackagePath (Get-SqlPackageArgument) #Ensure errors are sent to the errorvariable } -ev sqlpackageerror -OutVariable SqlPackageExitCode $ErrorActionPreference = "Stop" if ($Global:LASTEXITCODE -ne 0) { throw "SqlPackage returned non-zero exit code: $LASTEXITCODE" } $result = [PscustomObject]@{Scripts = Get-ChildItem "$ScriptParentPath\$DatabaseNameForPath" -File -Recurse } if ($OutputDeployScript) { $result.Scripts | ForEach-Object { Write-Host "######################################### DB Deploy Script ######################################" Write-Host "Produced file $_" Write-Host "-------------------------------------------------------------------------------------------------" Get-Content $_.FullName | ForEach-Object { if ($_ -notlike '*:setvar DeployProperties*') { Write-Host $_ } else { Write-host ":setvar DeployProperties ### masked ####" } } } } return $result } Catch { Throw $_ } } |