Public/Compare-sqmServerConfiguration.ps1

<#
.SYNOPSIS
    Compares important configuration settings between two SQL Server instances.
 
.DESCRIPTION
    Displays differences in the following areas: sp_configure, instance properties (Collation, Version, MaxMemory), database settings (optional). Output as a list with old/new values.
 
.PARAMETER SourceInstance
    Source instance (reference).
 
.PARAMETER TargetInstance
    Target instance (server to compare). Mandatory.
 
.PARAMETER SqlCredential
    PSCredential for both instances (if identical). For different credentials, separate parameters are required (simplified).
 
.PARAMETER CompareDatabases
    When set, databases (Name, Owner, RecoveryModel, Collation) are compared.
 
.PARAMETER EnableException
    Throw exceptions immediately.
 
.EXAMPLE
    Compare-sqmServerConfiguration -SourceInstance "SQL01" -TargetInstance "SQL02"
 
.NOTES
    Uses Connect-DbaInstance and SMO objects.
#>

function Compare-sqmServerConfiguration
{
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param (
        [Parameter(Mandatory = $true)]
        [string]$SourceInstance,
        [Parameter(Mandatory = $true)]
        [string]$TargetInstance,
        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]$SqlCredential,
        [Parameter(Mandatory = $false)]
        [switch]$CompareDatabases,
        [Parameter(Mandatory = $false)]
        [switch]$EnableException
    )
    
    begin
    {
        $functionName = $MyInvocation.MyCommand.Name
        if (-not (Get-Module -ListAvailable -Name dbatools))
        {
            throw "dbatools-Modul nicht gefunden."
        }
        $results = [System.Collections.Generic.List[PSCustomObject]]::new()
        function Get-ServerProps($inst)
        {
            $srv = Connect-DbaInstance -SqlInstance $inst -SqlCredential $SqlCredential -ErrorAction Stop
            $cfg = $srv.Configuration
            $props = @{
                Version = $srv.VersionString
                Edition = $srv.Edition
                Collation = $srv.Collation
                LoginMode = $srv.LoginMode
                MaxMemory = $cfg.MaxServerMemory.ConfigValue
                MinMemory = $cfg.MinServerMemory.ConfigValue
                MaxDop  = $cfg.MaxDegreeOfParallelism.ConfigValue
                CTP        = $cfg.CostThresholdForParallelism.ConfigValue
                BackupDirectory = $srv.BackupDirectory
                DefaultFile = $srv.DefaultFile
                DefaultLog = $srv.DefaultLog
            }
            return $props
        }
        function Get-DatabaseSimple($inst)
        {
            $dbs = Get-DbaDatabase -SqlInstance $inst -SqlCredential $SqlCredential -ErrorAction Stop
            $dbs | ForEach-Object {
                [PSCustomObject]@{
                    Name = $_.Name
                    Owner = $_.Owner
                    RecoveryModel = $_.RecoveryModel
                    Collation = $_.Collation
                }
            }
        }
    }
    
    process
    {
        try
        {
            $sourceProps = Get-ServerProps $SourceInstance
            $targetProps = Get-ServerProps $TargetInstance
            foreach ($key in $sourceProps.Keys)
            {
                if ($sourceProps[$key] -ne $targetProps[$key])
                {
                    $results.Add([PSCustomObject]@{
                            Setting        = $key
                            SourceValue = $sourceProps[$key]
                            TargetValue = $targetProps[$key]
                            Category    = "Instance"
                        })
                }
            }
            if ($CompareDatabases)
            {
                $sourceDbs = Get-DatabaseSimple $SourceInstance | Where-Object { -not $_.IsSystemObject }
                $targetDbs = Get-DatabaseSimple $TargetInstance | Where-Object { -not $_.IsSystemObject }
                $allDbNames = ($sourceDbs.Name + $targetDbs.Name) | Sort-Object -Unique
                foreach ($dbName in $allDbNames)
                {
                    $s = $sourceDbs | Where-Object { $_.Name -eq $dbName }
                    $t = $targetDbs | Where-Object { $_.Name -eq $dbName }
                    if (-not $s)
                    {
                        $results.Add([PSCustomObject]@{ Setting = "Database '$dbName'"; SourceValue = "<missing>"; TargetValue = $t.Owner; Category = "Database" })
                        continue
                    }
                    if (-not $t)
                    {
                        $results.Add([PSCustomObject]@{ Setting = "Database '$dbName'"; SourceValue = $s.Owner; TargetValue = "<missing>"; Category = "Database" })
                        continue
                    }
                    if ($s.Owner -ne $t.Owner)
                    {
                        $results.Add([PSCustomObject]@{ Setting = "$dbName Owner"; SourceValue = $s.Owner; TargetValue = $t.Owner; Category = "Database" })
                    }
                    if ($s.RecoveryModel -ne $t.RecoveryModel)
                    {
                        $results.Add([PSCustomObject]@{ Setting = "$dbName RecoveryModel"; SourceValue = $s.RecoveryModel; TargetValue = $t.RecoveryModel; Category = "Database" })
                    }
                    if ($s.Collation -ne $t.Collation)
                    {
                        $results.Add([PSCustomObject]@{ Setting = "$dbName Collation"; SourceValue = $s.Collation; TargetValue = $t.Collation; Category = "Database" })
                    }
                }
            }
            return $results
        }
        catch
        {
            Invoke-sqmLogging -Message $_.Exception.Message -FunctionName $functionName -Level "ERROR"
            if ($EnableException) { throw }
            return $null
        }
    }
}