Desired State Configuration #4 – SQL Server Installation und Konfiguration

Final Blogpost about Desired State Configuration - Photo by Matt Botsford

So endlich ist es soweit… ich habe meine SQL Server Installation mit Powershell Desired State Configuration fertig und möchte euch nun meinen abschließenden Blog-Beitrag präsentieren. Auch wenn noch nicht alles 100% fertig ist und der Ablauf leider noch nicht so ganz so ist, wie ich es mir vorstelle, – es müssen eigentlich noch zu viele Dinge manuell gemacht werden und wie ich mit dem Thema Reboot umgehe ist noch ungeklärt – dennoch möchte ich euch hier meinen Code vorstellen.

Einleitung zur Vorgehensweise

Ich schreibe hier aktuell über Desired State Configuration als Push-Modell, ggfs kommt ein Pull-Modell später noch hinzu. Das heißt ich führe meine Skripte von meiner Workstation aus, die eigentlichen Skripte liegen auf dem Domänen-Controller, alle Server und Workstations sind in einer Domäne und ich führe die Skripte als Domänen-Administrator aus. Noch einmal zur Verdeutlichung meiner Umgebung hier ein Bild von meiner Test-Umgebung:

Die Verzeichnisstruktur liegt auf dem Domänen-Controller in der allgemein zugänglichen SYSVOL-Freigabe, kann aber natürlich auch auf jeder anderen (zentralen) Freigabe liegen. Hier liegen die Ordner für die notwendigen Powershell-Module und SQL Server Installationsmedien.

  • DC01
    • SYSVOL
      • DSC
        • SQL
          • Updates
        • Modules
        • Scripts
        • Configurations

Auf den Ziel-Servern kann man sich ebenfalls eine frei wählbare Struktur ausdenken, erst einmal geht es aber um das Kopieren von Quell- auf Ziel-Server… die Powershell-Module kopiere ich in das Standard-WindowsPowershell-Verzeichnis ( C:\Windows\system32\WindowsPowerShell\v1.0\Modules) und das SQL Server Setup liegt auf dem D-Laufwerk meiner Azure-Maschinen (D:\SQL2017).

Voraussetzungen für Desired State Configuration

Natürlich müssen auch gewisse Voraussetzungen für die Nutzung von DSC gegeben sein, ich beschränke mich hier auf die rudimentären Themen und verweise ansonsten gerne auf den ersten Teil meiner DSC-Blogpost-Serie, in dem ich auf die Details dazu eingehe. Mir ist im Zuge meiner Recherchen bzw Versuche noch ein Problem untergekommen, auf das ich kurz hinweisen möchte (kann sich aber um ein Problem speziell aus meiner Test-Umgebung handeln)… ich konnte erfolgreich die ersten 3 Server mittels DSC ausrollen, beim vierten allerdings bekam ich immer die Fehlermeldung, dass der Server nicht erreicht werden kann… auf ein Löschen/Re-Deploy des Servers (Azure sei Dank) brachte keine Besserung… hier hatte sich der DNS-Server eine falsche lokale IP-Adresse für den Server „gemerkt“ und konnte somit nicht die Namensauflösung vollbringen. Nachdem ich allerdings auf meiner Workstation den DNS-Cache gelöscht hatte, war es mir möglich auch den vierten SQL Server erfolgreich zu installieren und konfigurieren.

Also nachdem ich nun alle Server entsprechend in der Domäne habe, alle Firewalls und Dienste konfiguriert sind, können wir loslegen mit dem eigentlichen SQL Server Rollout mittels DSC. Entgegen meiner ersten Versuche (siehe Beitrag #2 zu DSC) bin ich jetzt dazu übergegangen alles mit Desired State Configuration zu machen und nur noch wenige Schritte mit reinem Powershell. Aber dazu kommen wir gleich…

In meinem aktuellen Projekt geht es um die regelmäßige Bereitstellung einer SQL Server Umgebung mit AlwaysOn AvailibilityGroups für einige Applikationen OHNE Patching. Das heißt es muss jeden Monat eine neue Umgebung ausgerollt, welche immer den letzten Patch-Level hat. Um dies zu erreichen, habe ich diese Skripte geschrieben. Damit meine Skripte mehrfach bzw modular einsetzbar sind, habe ich die Arbeitsschritte aufgeteilt:

  1. Vorbereitung, Installation und Konfiguration eines SQL Servers (2017 Developer)
  2. Migration der vorhandenen Umgebung auf die neue Umgebung
  3. Vorbereitung, Rollout und Aktivierung der AlwaysOn AvailibilityGroups

Meine Powershell Skripte

Desired-State-Configuration - Meine-Powershell-Skripte

Kurz zur Erläuterung:
Meine Hilfsskripte, welche ich im letzten Blogbeitrag schon erläutert habe

  • Connect Network Share => Erstellt eine Verbindung mit meinem Azure-Fileshare, setzt die PSGallery auf Trusted und „Unblocked“ meine Powershell-Skripte
  • Init_new_Machines => Öffnet die Windows-Firewall für die Netzwerk sowie File und Print-Dienste, formatiert alle Laufwerke, Domänen-Join
  • Remove AG and its secDB => Löscht auf beiden Servern die AGs und auf dem Secondary die verbliebene Datenbank
  • Update Modules => wie der Name sagt, es werden die notwendigen Powershell bzw DSC-Module aktualisiert.
  • 1_SQLServer-Rollout-DSC => Es erfolgt eine Standalone-Installation eines SQL Server 2017 mit Konfiguration nach Best-Practices und Installation der Ola Hallengren Maintenance Skripte
  • 2_Migrate_UserDatabases => Migration aller Datenbank-Objekte mit dbatools
  • 3_Rollout-Start-AvailGroup => Es erfolgt die Vorbereitung, Konfiguration und Start der einzurichtenden Availibilty-Groups mittels DSC und dbatools (noch… eventuell baue ich das auch auf DSC um, aber es ist so schön einfach 😉 )
  • SQLServerRollutwithAG => Ist ein Sammelskript bzw soll ein Sammelskript werden, welches alle Skripte / den Workflow entsprechend steuert
  • SQLServerConfiguration => ein JSON-File, was die Parameter für die Umgebung enthält

Part 1 – Installation und Konfiguration eines SQL Servers mit Desired State Configuration

Ich werde nicht im Detail auf das Skript eingehen und auch nicht alle Code-Zeilen zeigen, sondern „nur“ einen groben Überblick geben, wie ich vorgegangen bin und was ich womit realisiert habe…
Einleitung erfolgt mit der benötigten Powershell-Version, DSC-Modulen und Modulversionen… anschließend die Parameter, die man bei der Nutzung per Kommandozeile eingeben kann/muss.

## At the time of this writing, above modules/version were used. Future versions of this module could have breaking changes and will need to be retested
#Requires -Version 5
#Requires -Modules @{ ModuleName="xPSDesiredStateConfiguration"; ModuleVersion="8.4.0.0" }
#Requires -Modules @{ ModuleName="SqlServerDsc"; ModuleVersion="12.1.0.0" }
#Requires -Modules @{ ModuleName="SecurityPolicyDsc"; ModuleVersion="2.6.0.0" }
#Requires -Modules @{ ModuleName="StorageDSC"; ModuleVersion="4.3.0.0" }
## At the time of this writing, above modules/version were used. Future versions of this module could have breaking changes and will need to be retested

param
(
    # Computer name to install SQL Server On
    [Parameter(Mandatory=$true)]
    [String]
    $ComputerName,

    # Plaintext Password of User who has installation permissons on destination server
    [Parameter(Mandatory = $false)]
    [String]
    $InstallerPwd,

    # Plaintext Password of User who should run the sql server service => e.g. DEV2\sqlserv
    [Parameter(Mandatory = $false)]
    [String]
    $SQLServicePwd,

    # Plaintext Password of User who should run the sql Agent service => e.g. DEV2\sqlserv
    [Parameter(Mandatory = $false)]
    [String]
    $SQLAgentPwd,

    # Plaintext Password of the SA User
    [Parameter(Mandatory = $false)]
    [String]
    $SAPwd,

    # Will it be just for preparation?
    [Parameter(Mandatory=$false)]
    [Switch]
    $Prepare,

    # Will it be just for SQL Server configuration?
    [Parameter(Mandatory=$false)]
    [Switch]
    $Configure
)

Es folgen einige einleitende Zeilen, um die Konfigurationdatei einzulesen und aus JSON umzuwandeln, sowie den „OutputPath“ zu definieren. Der OutputPath ist der Ablage-Ordner für die MOF-Files (also die eigentliche Konfigurationsdatei).

Clear-Host
Write-host "Starting DSC process on"$ComputerName.ToUpper()

## Getting ConfigurationValues from JSON-File
$ConfigurationFile = "SQLServerConfiguration.json"
$Configuration = (Get-Content $ConfigurationFile) -join "`n"  | ConvertFrom-Json

## Defining path to MOF-Files
$OutputPath = $Configuration.General.DSCConfigFolder

Die Parameter werden anschließend überprüft und zu Credentials umgewandelt, damit man später sowohl Username und Passwort getrennt und als Credential verwenden kann. Und schon geht es los mit den ersten DSC-Configurations für das Bereitstellen der notwendigen Powershell-Module, der Installationsmedien und Mounten des ISO-Images . Ich habe aus Debug- und Flexibilitätsgründen mehrere Configurations gebaut um später das Skript für weitere Zwecke (wie man in den Parametern erahnen kann) zu nutzen. Man kann grundsätzlich alles in eine Configuration legen, aber um modular zu bleiben…

Vorbereitungen – Kopieren der Powershell Module und Installationsmedien

Configuration Preparation {
    Import-DscResource -ModuleName PSDesiredStateConfiguration
    
    Node $AllNodes.NodeName {
        LocalConfigurationManager {
            DebugMode = "ForceModuleImport"
            RebootNodeIfNeeded = $true
        }

        File PowershellModules {
            Ensure = 'Present'
            Type = 'Directory'
            SourcePath = $Configuration.InstallationMedia.ModuleSourcePath
            DestinationPath = $Configuration.InstallationMedia.ModuleDestinationPath
            Recurse = $true
        }

        File InstallationFolder {
            Ensure = 'Present'
            Type = 'Directory'
            SourcePath = $Configuration.InstallationMedia.InstallationMediaSourcePath
            DestinationPath = $Configuration.InstallationMedia.InstallationMediaDestinationPath
            Recurse = $true
        }

        WindowsFeature NET-Framework-Core {
            Name = "NET-Framework-Core"
            Ensure = "Present"
            IncludeAllSubFeature = $true
        }
    }
}

Configuration ISOMount {
    Import-DscResource -Module PSDesiredStateConfiguration
    Import-DscResource -ModuleName StorageDsc
    
    Node $AllNodes.NodeName {
        MountImage ISO {
            ImagePath   = $Node.SourcePath
            DriveLetter = 'S'
        }

        WaitForVolume WaitForISO {
            DriveLetter      = 'S'
            RetryIntervalSec = 5
            RetryCount       = 10
        }
    }
}

Wenn also das kopierte ISO-Image am Zielserver gemountet wurde, könnte man – wie ich es im ersten Versuch gemacht habe bzw Chris Lumnah in seinem Blogbeitrag beschrieben hat – mittels Script-Ressource realisiert, könnte man die „setup.exe“ mit der „ConfigurationFile.ini“ aufrufen und eine „unattend-Installation“ durchführen. Ich habe nach zahlreichen Versuchen und einigem Lesen mich für das SQLServerDSC-Modul entschieden, das ist „einfacher“ und nicht ganz so Fehler-anfällig.

Ausführung des eigentlichen SQL Server Setup

Configuration SQLServerSetup {
    Import-DscResource -Module PSDesiredStateConfiguration
    Import-DscResource -Module SqlServerDsc
    Import-DscResource -ModuleName StorageDsc

    Node $AllNodes.NodeName {
        LocalConfigurationManager {
            DebugMode               = "ForceModuleImport"
            RebootNodeIfNeeded      = $true
        }

        WindowsFeature NET-Framework-Core {
            Name                    = "NET-Framework-Core"
            Ensure                  = "Present"
            IncludeAllSubFeature    = $true
        }

        SQLSetup InstallDefaultInstance {
            PsDscRunAsCredential    = $InstallerCredentials
            DependsOn               = '[WindowsFeature]NET-Framework-Core'
            Action                  = "Install"
            UpdateEnabled           = $Configuration.InstallSQL.UpdateEnabled
            UpdateSource            = $Configuration.InstallSQL.UpdateSource
            Features                = $Configuration.InstallSQL.Features
            InstanceName            = $Configuration.InstallSQL.InstanceName
            SQLCollation            = $Configuration.InstallSQL.SQLCollation

            SecurityMode            = $Configuration.InstallSQL.SecurityMode
            SAPwd                   = $SQLSACredentials
            
            AgtSvcAccount           = $SQLServiceCredentials
            SQLSvcAccount           = $SQLServiceCredentials
            #ISSvcAccount           = $Node.ISSvcAccount
            #FTSvcAccount           = $Node.FTSvcAccount
            
            InstallSharedDir        = $Configuration.InstallSQL.InstallSharedDir
            InstallSharedWOWDir     = $Configuration.InstallSQL.InstallSharedWOWDir
            InstanceDir             = $Configuration.InstallSQL.InstanceDir
            InstallSQLDataDir       = $Configuration.InstallSQL.InstallSQLDataDir
            SQLUserDBDir            = $Configuration.InstallSQL.SQLUserDBDir
            SQLUserDBLogDir         = $Configuration.InstallSQL.SQLUserDBLogDir
            SQLTempDBDir            = $Configuration.InstallSQL.SQLTempDBDir
            SQLTempDBLogDir         = $Configuration.InstallSQL.SQLTempDBLogDir
            SQLBackupDir            = $Configuration.InstallSQL.SQLBackupDir

            SourcePath              = 'S:\'
        }
    }
}

Viel brauche ich dazu wohl nicht zu schreiben, da es recht selbst erklärend ist! Weiter geht es mit der eigentlichen Konfiguration des SQL Server nachdem die Installation erfolgreich durchgeführt wurde. Hier richte mich nach den gängigen Best-Practice Empfehlungen der Community zu SQL Servern, wie zum Beispiel „MaxDOP“ und den „Cost Threshold For Parallelism“ oder den maximalen Speicherbedarf des SQL Servers zu begrenzen. Auszugsweise 😉

abschließende Konfiguration des SQL Servers

SqlServerNetwork 'ChangeTcpIpOnDefaultInstance'
        {
            ...
        }
        
        SQLServerMemory SetMAXMemory {
            ...
        }

        SQLServerMaxDop SetMAXDOP {
            ...
        }

        SQLServerConfiguration 'CostThreshold4Parallelism' {
            ...
        }

        UserRightsAssignment PerformVolumeMaintenanceTasks {
            ...
        }

        UserRightsAssignment LockPagesInMemory {
            ...
        }

        SqlWindowsFirewall Create_FirewallRules_For_SQL2017 {
            ...
        }

        SqlServiceAccount SetServiceAccount_User {
            ...
        }

        SqlServiceAccount SetAgentAccount_User {
            ...
        }

Wie man die einzelnen DSC-Ressourcen einsetzt und welche Wert benötigt werden oder nur optional sind, kann man sehr gut in der Microsoft-Dokumentation (nicht alle Examples sind 100%, aber man erhält einen Eindruck) nachlesen. => https://github.com/PowerShell/SqlServerDsc
Nun fehlt nur noch das „Unmount“ des ISO-Images, danach die Schritte „Vorbereitung“, „Installation“ und „Konfiguration“ abgeschlossen. Wie in Desired State Configuration mit Powershell üblich, kommen jetzt am Ende des Skriptes noch die Zuweisungen, „Berechnungen“ und Aufrufe. Beispielsweise für MaxDOP und MaxMemory verwende ich folgende Zeilen:

## Getting CPU-Count and calculate MaxDOP
$CpuCount = 0
$Memory = Get-CimInstance Win32_PhysicalMemory -cn $ComputerName | Measure-Object -Property capacity -Sum 
$MaxMemory = [math]::round((($Memory.Sum * 0.8)/1MB),0)
$CPUCount = (Get-CimInstance Win32_Processor -cn $ComputerName -Property NumberOfLogicalProcessors).NumberOfLogicalProcessors
if ($CPUCount -eq 1) {
    $CPUCount = 1
} elseif ($CPUCount -gt 8) {
    $CPUCount = 8
} else {
    $CPUCount = $CPUCount/2
}

Erstellen der MOF-Datei und Starten der DSC-Konfiguration auf dem Zielserver

Um einen Eindruck davon zu vermitteln, wie ich die einzelnen Schritte der DSC-Installation aufrufe bzw einteile, hier noch ein Beispiel-Abschnitt aus meinem Skript:

if ($Prepare) { 
    $ConfigurationData.AllNodes += @{
        NodeName = $ComputerName
    }
    ## Create MOF-Files
    Preparation -ConfigurationData $ConfigurationData -OutputPath "$OutputPath\Preparation\"
    ## Providing all PowerShell Modules
    Start-DscConfiguration -ComputerName $ComputerName -Path "$OutputPath\Preparation" -Verbose -Wait -Force
}

Im „Prepare“-Schritt benötige ich als Parameter nur den Namen des Zielservers, erstelle mit dem Aufruf des Configuration-Teils, der Übergabe der ConfigurationData und dem „OutputPath“ eine MOF-Datei und meta.MOF-Datei. Diese MOF-Datei kann man separat erstellen, um sie dann als späteren Schritt im Skript oder wie hier direkt nach dem Erstellen auszuführen, theoretisch kann man die MOF-Datei auch mit einem eigenen Skript erstellen und mit einem zweiten Powershell-Skript einlesen und dann auf dem Zielserver ausführen. Ich starte hier die DSC-Konfiguration im direkt Anschluss an das Erstellen gegen den Zielserver aus.

SQL Server Datenbank Wartung mit Ola Hallengren

Als abschließenden Schritt in meiner langen Aufführung möchte ich die Grundlage schaffen, dass Backups und Index-Pflege mit den bekannten Skripten von Ola Hallengren erfolgen können. Da ich auch in weiteren Schritten meines Deployments – Migration und Anlage der Availibility-Groups – auf das Powershell-Community-Modul „dbatools“ nutze, greife auch hier auf die Vereinfachung dafür zurück. Wenn also eine Internet-Verbindung besteht, wird das Skript automatisch heruntergeladen und installiert, wenn nicht wird auf die lokale Kopie zurück gegriffen. (natürlich muss die Datei vorher manuell herunterladen und ablegen)

## Check if Internet is available and download Ola Hallengren Database Maintenance otherwise look into given folder
If (Test-Connection -computer "www.google.com" -count 1 -quiet) {
## Internet-Connection is available
    Write-Host -ForegroundColor Green "$(Get-Date): Connection up!"
    Write-Host "Installing OH-MaintenanceScript from the Internet"
    Install-DbaMaintenanceSolution -SqlInstance $ComputerName -BackupLocation $Configuration.InstallSQL.SQLBACKUPDIR -CleanupTime 167 -ReplaceExisting -InstallJobs
} else {
    Invoke-DbaQuery -SqlInstance $ComputerName -File $Configuration.InstallSQL.InstallationMediaSourcePath"\MaintenanceSolution.sql"
}

In den nächsten Tagen werde ich diese Reihe weiterführen und erzählen wie ich die Migration bewerkstellige und wie das Skript zur Vorbereitung und zum Rollout der Availibility-Groups aussieht. Denn auch hier gibt es das ein oder andere zu beachten.
Für Fragen und/oder Diskussionen rund um das Thema stehe ich gerne zur Verfügung!

2 thoughts on “Desired State Configuration #4 – SQL Server Installation und Konfiguration

  1. Thanks for the ping back.

    One thing to note with DSC that I have learned. Microsoft is making monthly updates to the DSC module to make it better and better. When I first wrote that blog post, the SQLServer DSC module was still in experimental state. Since, it has been made a production module. I have since updated my code on my github, but have not updated my blog post.

    1. It’s a pleasure for me. Thank you very much for your inspiration!

      You’re absolutely right and I should update my code according to this (I also have some trouble with importing DSC-resources and different available modules)… so I should add the version number to the import-dscresource…

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.