diff --git a/private/functions/Get-EncryptedObjectKeystream.ps1 b/private/functions/Get-EncryptedObjectKeystream.ps1 index a9a170612e3..a795d8644c1 100644 --- a/private/functions/Get-EncryptedObjectKeystream.ps1 +++ b/private/functions/Get-EncryptedObjectKeystream.ps1 @@ -22,8 +22,8 @@ function Get-EncryptedObjectKeystream { - Invoke-DbaDbDecryptObject .PARAMETER FamilyGuid - The family GUID of the database that holds the object, as reported by dbi_familyGUID of DBCC DBINFO. - One value per database, stable for the life of the database. + The family GUID of the database that holds the object, as reported by family_guid of the catalog view + sys.database_recovery_status. One value per database, stable for the life of the database. .PARAMETER ObjectId The object id of the encrypted object. diff --git a/public/Invoke-DbaDbDecryptObject.ps1 b/public/Invoke-DbaDbDecryptObject.ps1 index 1f87d5bb20a..9c34c16ede8 100644 --- a/public/Invoke-DbaDbDecryptObject.ps1 +++ b/public/Invoke-DbaDbDecryptObject.ps1 @@ -305,8 +305,9 @@ function Invoke-DbaDbDecryptObject { Stop-Function -Message "Reading the encrypted objects on $instance without a dedicated admin connection uses DBCC PAGE, which $azurePlatform does not support." -Target $instance -Continue } - # DBCC PAGE and DBCC DBINFO are limited to sysadmin, so checking up front gives a clear - # message instead of a permission error in the middle of reading the pages. + # DBCC PAGE is limited to sysadmin, so checking up front gives a clear message instead of + # a permission error in the middle of reading the pages. sys.database_recovery_status, which + # supplies the family GUID, needs only VIEW SERVER STATE, which a sysadmin already holds. $querySysadmin = @" SELECT IS_SRVROLEMEMBER('sysadmin') AS IsSysadmin "@ @@ -450,16 +451,21 @@ AND p.is_ms_shipped = 0 $familyGuid = $null $imageValueMap = @{ } + # The family GUID comes from the catalog view sys.database_recovery_status, a typed + # uniqueidentifier present since SQL Server 2005, rather than by scraping dbi_familyGUID + # out of DBCC DBINFO's text dump. The two were measured byte for byte across SQL Server + # 2019, 2022 and 2025, for user databases and master, and always agree, so the swap keeps + # the keystream input identical while dropping a trace flag toggle and a regex over prose. try { - $familyGuidRow = @($db.Query("DBCC DBINFO WITH TABLERESULTS") | Where-Object Field -eq "dbi_familyGUID") + $familyGuidRow = @($db.Query("SELECT family_guid FROM sys.database_recovery_status WHERE database_id = DB_ID()")) } catch { - Stop-Function -Message "Couldn't read dbi_familyGUID of database $($db.Name) on $instance" -ErrorRecord $_ -Target $instance -Continue + Stop-Function -Message "Couldn't read the family GUID of database $($db.Name) on $instance" -ErrorRecord $_ -Target $instance -Continue } - if ($familyGuidRow.Count -eq 0) { - Stop-Function -Message "Couldn't read dbi_familyGUID of database $($db.Name) on $instance" -Target $instance -Continue + if ($familyGuidRow.Count -eq 0 -or $null -eq $familyGuidRow[0].family_guid -or $familyGuidRow[0].family_guid -is [System.DBNull]) { + Stop-Function -Message "Couldn't read the family GUID of database $($db.Name) on $instance" -Target $instance -Continue } - $familyGuid = [guid]$familyGuidRow[0].VALUE + $familyGuid = [guid]$familyGuidRow[0].family_guid $wantedObjectId = @($objectCollection.ID) diff --git a/tests/Invoke-DbaDbDecryptObject.Tests.ps1 b/tests/Invoke-DbaDbDecryptObject.Tests.ps1 index d66375c58ce..680472f274d 100644 --- a/tests/Invoke-DbaDbDecryptObject.Tests.ps1 +++ b/tests/Invoke-DbaDbDecryptObject.Tests.ps1 @@ -723,8 +723,8 @@ SELECT 'áéíñóú¡¿' as SampleUTF8;" $routeText = InModuleScope dbatools -Parameters @{ DatabaseName = $dbname; Instance = $TestConfig.InstanceMulti1 } { $server = Connect-DbaInstance -SqlInstance $Instance $db = $server.Databases[$DatabaseName] - $familyGuidRow = @($db.Query("DBCC DBINFO WITH TABLERESULTS") | Where-Object Field -eq "dbi_familyGUID") - $familyGuid = [guid]$familyGuidRow[0].VALUE + $familyGuidRow = @($db.Query("SELECT family_guid FROM sys.database_recovery_status WHERE database_id = DB_ID()")) + $familyGuid = [guid]$familyGuidRow[0].family_guid $encryptedId = @(@($db.Query("SELECT m.object_id AS ObjectId FROM sys.sql_modules AS m WHERE m.definition IS NULL")) | ForEach-Object { [int]$PSItem.ObjectId }) $result = @{ } @@ -773,8 +773,8 @@ SELECT 'áéíñóú¡¿' as SampleUTF8;" $scanText = InModuleScope dbatools -Parameters @{ DatabaseName = $dbname; Instance = $TestConfig.InstanceMulti1 } { $server = Connect-DbaInstance -SqlInstance $Instance $db = $server.Databases[$DatabaseName] - $familyGuidRow = @($db.Query("DBCC DBINFO WITH TABLERESULTS") | Where-Object Field -eq "dbi_familyGUID") - $familyGuid = [guid]$familyGuidRow[0].VALUE + $familyGuidRow = @($db.Query("SELECT family_guid FROM sys.database_recovery_status WHERE database_id = DB_ID()")) + $familyGuid = [guid]$familyGuidRow[0].family_guid $encryptedId = @($db.Query("SELECT m.object_id AS ObjectId FROM sys.sql_modules AS m WHERE m.definition IS NULL")) | ForEach-Object { [int]$PSItem.ObjectId }