Public/Invoke-mssAddDatabaseToAG.TempPoint.ps1

<#
.SYNOPSIS
Fügt eine oder mehrere Datenbanken zu einer Always On-Verfügbarkeitsgruppe hinzu (AutoSeed).
 
.DESCRIPTION
- Prüft, ob die Datenbank bereits in einer AG ist.
- Stellt den Recovery-Modus auf Full (falls nötig).
- Löscht vorhandene Datenbanken auf allen sekundären Replikaten.
- Fügt die Datenbank mit Automatic Seeding zur AG hinzu.
- Bei -All werden die Datenbanken sequentiell hinzugefügt, um Last zu vermeiden.
 
.PARAMETER SqlInstance
Primäre SQL-Instanz (Standard: Computername).
 
.PARAMETER SqlCredential
Anmeldeinformationen.
 
.PARAMETER AvailabilityGroup
Name der Ziel-Verfügbarkeitsgruppe (Pflicht).
 
.PARAMETER Database
Name oder Array von Datenbanken. Wird ignoriert, wenn -All gesetzt ist.
 
.PARAMETER All
Alle Benutzerdatenbanken (die noch nicht in einer AG sind) hinzufügen.
 
.PARAMETER EnableException
Ausnahmen durchlassen.
 
.PARAMETER Confirm
Bestätigung anfordern.
 
.PARAMETER WhatIf
Nur testen.
 
.EXAMPLE
Add-mssDatabaseToAvailabilityGroup -AvailabilityGroup "AG1" -Database "SalesDB"
 
.EXAMPLE
Add-mssDatabaseToAvailabilityGroup -AvailabilityGroup "AG1" -All
 
.NOTES
Setzt Automatic Seeding auf allen Replikaten voraus (kann separat mit Invoke-mssSqlAlwaysOnAutoseeding aktiviert werden).
#>

function Invoke-mssAddDatabaseToAG
    [CmdletBinding(DefaultParameterSetName = 'Specific', SupportsShouldProcess = $true, ConfirmImpact = 'None')]
    param (
        [Parameter(Mandatory = $false, Position = 0)]
        [string]$SqlInstance,
        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]$SqlCredential,
        [Parameter(Mandatory = $true)]
        [string]$AvailabilityGroup,
        [Parameter(Mandatory = $false, ParameterSetName = 'Specific')]
        [string[]]$Database,
        [Parameter(Mandatory = $false, ParameterSetName = 'All')]
        [switch]$All,
        [Parameter(Mandatory = $false)]
        [switch]$EnableException
    )
    
    begin
    {
        $functionName = $MyInvocation.MyCommand.Name
        if (-not $PSBoundParameters.ContainsKey('SqlInstance') -or [string]::IsNullOrWhiteSpace($SqlInstance))
        {
            $SqlInstance = $env:COMPUTERNAME
        }
        if (-not (Get-Module -ListAvailable -Name dbatools))
        {
            throw "dbatools-Modul nicht gefunden."
        }
        Invoke-mssLogging -Message "Starte $functionName auf $SqlInstance, AG: $AvailabilityGroup" -FunctionName $functionName -Level "INFO"
        $results = @()
    }
    
    process
    {
        try
        {
            # Verfügbarkeitsgruppe validieren und sekundäre Replikate ermitteln
            $ag = Get-DbaAvailabilityGroup -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup -ErrorAction Stop
            if (-not $ag) { throw "AG '$AvailabilityGroup' nicht gefunden." }
            $replicas = Get-DbaAgReplica -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup
            $secondaryInstances = $replicas | Where-Object { $_.Role -eq 'Secondary' } | Select-Object -ExpandProperty Name
            
            # Datenbanken ermitteln
            $dbParams = @{ SqlInstance = $SqlInstance; SqlCredential = $SqlCredential; ExcludeSystem = $true; ErrorAction = 'Stop' }
            if ($EnableException) { $dbParams.EnableException = $true }
            
            if ($All)
            {
                $allDbs = Get-DbaDatabase @dbParams | Where-Object { $_.IsAccessible }
                $databases = @()
                foreach ($db in $allDbs)
                {
                    $inAG = Get-DbaAgDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $db.Name -ErrorAction SilentlyContinue
                    if (-not $inAG) { $databases += $db }
                }
                Invoke-mssLogging -Message "$($databases.Count) Datenbanken wurden für Hinzufügung ausgewählt." -FunctionName $functionName -Level "INFO"
            }
            elseif ($Database)
            {
                $dbParams.Database = $Database
                $databases = Get-DbaDatabase @dbParams | Where-Object { $_.IsAccessible }
                $missing = $Database | Where-Object { $_ -notin ($databases.Name) }
                if ($missing)
                {
                    $msg = "Nicht gefunden: $($missing -join ', ')"
                    Invoke-mssLogging -Message $msg -FunctionName $functionName -Level "WARNING"
                    $results += [PSCustomObject]@{ SqlInstance = $SqlInstance; DatabaseName = $missing -join ','; Status = "NotFound"; Message = $msg }
                }
            }
            else { throw "Weder -All noch -Database angegeben." }
            
            if (-not $databases)
            {
                Invoke-mssLogging -Message "Keine Datenbanken zum Hinzufügen." -FunctionName $functionName -Level "WARNING"
                return
            }
            
            # Sequentiell verarbeiten (bei -All wichtig für Last)
            $counter = 0
            foreach ($db in $databases)
            {
                $counter++
                $dbName = $db.Name
                Invoke-mssLogging -Message "Verarbeite Datenbank $counter von $($databases.Count): $dbName" -FunctionName $functionName -Level "INFO"
                
                # Prüfung ob bereits in AG (sicherheitshalber)
                $existingAg = Get-DbaAgDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $dbName -ErrorAction SilentlyContinue
                if ($existingAg)
                {
                    $msg = "Datenbank '$dbName' ist bereits in AG '$($existingAg.AvailabilityGroupName)'. Überspringe."
                    Invoke-mssLogging -Message $msg -FunctionName $functionName -Level "WARNING"
                    $results += [PSCustomObject]@{ SqlInstance = $SqlInstance; DatabaseName = $dbName; Status = "AlreadyInAG"; Message = $msg }
                    continue
                }
                
                # Recovery-Modus auf Full setzen
                if ($db.RecoveryModel -ne 'Full')
                {
                    $setRecoveryAction = "Setze Recovery-Modus für '$dbName' auf Full"
                    if ($PSCmdlet.ShouldProcess($dbName, $setRecoveryAction))
                    {
                        try
                        {
                            Invoke-mssLogging -Message $setRecoveryAction -FunctionName $functionName -Level "INFO"
                            Set-DbaDbRecoveryModel -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $dbName -RecoveryModel Full -ErrorAction Stop
                        }
                        catch
                        {
                            $errMsg = "Fehler beim Setzen des Recovery-Modus: $($_.Exception.Message)"
                            Invoke-mssLogging -Message $errMsg -FunctionName $functionName -Level "ERROR"
                            if ($EnableException) { throw }
                            $results += [PSCustomObject]@{ SqlInstance = $SqlInstance; DatabaseName = $dbName; Status = "SetRecoveryFailed"; Message = $errMsg }
                            continue
                        }
                    }
                    else
                    {
                        $results += [PSCustomObject]@{ SqlInstance = $SqlInstance; DatabaseName = $dbName; Status = "RecoverySkipped"; Message = "WhatIf: Recovery-Modus nicht geändert." }
                        continue
                    }
                }
                
                # Vorhandene Datenbank auf Secondaries löschen
                foreach ($secondary in $secondaryInstances)
                {
                    $secDb = Get-DbaDatabase -SqlInstance $secondary -SqlCredential $SqlCredential -Database $dbName -ErrorAction SilentlyContinue
                    if ($secDb)
                    {
                        $dropAction = "Lösche vorhandene Datenbank '$dbName' auf Secondary '$secondary'"
                        if ($PSCmdlet.ShouldProcess($dbName, $dropAction))
                        {
                            try
                            {
                                Invoke-mssLogging -Message $dropAction -FunctionName $functionName -Level "INFO"
                                Remove-DbaDatabase -SqlInstance $secondary -SqlCredential $SqlCredential -Database $dbName -Confirm:$false -ErrorAction Stop
                            }
                            catch
                            {
                                $errMsg = "Fehler beim Löschen auf '$secondary': $($_.Exception.Message)"
                                Invoke-mssLogging -Message $errMsg -FunctionName $functionName -Level "ERROR"
                                if ($EnableException) { throw }
                                $results += [PSCustomObject]@{ SqlInstance = $secondary; DatabaseName = $dbName; Status = "DropOnSecondaryFailed"; Message = $errMsg }
                                # Nicht abbrechen, versuchen trotzdem hinzuzufügen?
                            }
                        }
                        else
                        {
                            $results += [PSCustomObject]@{ SqlInstance = $secondary; DatabaseName = $dbName; Status = "DropSkipped"; Message = "WhatIf: Löschen übersprungen." }
                        }
                    }
                }
                
                # Zur AG hinzufügen (mit Automatic Seeding)
                $addAction = "Füge Datenbank '$dbName' zur AG '$AvailabilityGroup' hinzu (AutoSeed)"
                if ($PSCmdlet.ShouldProcess($dbName, $addAction))
                {
                    try
                    {
                        Invoke-mssLogging -Message $addAction -FunctionName $functionName -Level "INFO"
                        Add-DbaAgDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup -Database $dbName -SeedingMode Automatic -ErrorAction Stop
                        $results += [PSCustomObject]@{
                            SqlInstance  = $SqlInstance
                            DatabaseName = $dbName
                            Status         = "Success"
                            Message         = "Erfolgreich zur AG hinzugefügt."
                        }
                    }
                    catch
                    {
                        $errMsg = "Fehler beim Hinzufügen: $($_.Exception.Message)"
                        Invoke-mssLogging -Message $errMsg -FunctionName $functionName -Level "ERROR"
                        if ($EnableException) { throw }
                        $results += [PSCustomObject]@{ SqlInstance = $SqlInstance; DatabaseName = $dbName; Status = "AddFailed"; Message = $errMsg }
                    }
                }
                else
                {
                    $results += [PSCustomObject]@{ SqlInstance = $SqlInstance; DatabaseName = $dbName; Status = "AddSkipped"; Message = "WhatIf: Hinzufügen übersprungen." }
                }
            }
        }
        catch
        {
            $errMsg = "Allgemeiner Fehler: $($_.Exception.Message)"
            Invoke-mssLogging -Message $errMsg -FunctionName $functionName -Level "ERROR"
            if ($EnableException) { throw }
            $results += [PSCustomObject]@{ SqlInstance = $SqlInstance; DatabaseName = $null; Status = "GlobalError"; Message = $errMsg }
        }
    }
    
    end
    {
        Invoke-mssLogging -Message "$functionName abgeschlossen." -FunctionName $functionName -Level "INFO"
        return $results
    }
}