Desired State Configuration #4 – SQL Server Installation und Konfiguration

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:

Powershell Desired State Configuration - Overview - New Environment

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!

SQLDays 2018 in München

Dieses Jahr war ich erstmalig auf den SQLDays in München genauergesagt in Erding (Erdinger Stadthalle), bei den SQLDays handelt es sich um eine Schulungs-Veranstaltung der ppedv. In den zwei bzw vier Tagen geht es vor allem um die namensgebende Microsoft-DataPlatform Umgebung rund um den SQL Server, an zwei Workshop-Tagen und zwei Konferenz-Tagen können die Teilnehmer aus einer Vielzahl von interessanten Sessions von hochrangigen Sprechern auswählen. Zum Beispiel hielt Georg Urban die Keynote und Markus Raatz hielte folgenden Vortrag:

Markus Raatz : Echtzeitdatensets in Power BI: Einführung und HowTo

Von Echtzeit-Daten, wie sie meist im Streaming-Verfahren von IoT-Datenquellen geliefert werden, geht ein großer Reiz aus. Nichts ist schöner, als auf ein Browserfenster zu starren und zu sehen, wie sich der winzige Lieferwagen von allein Meter um Meter auf mein Haus zubewegt! Auch Dashboards, in denen Liniengrafiken auf- und abwärts zucken, können uns stundenlang beschäftigen. Der technische Hintergrund dazu sind Power BI Streaming Datasets, deren Möglichkeiten wir uns in diesem demo-reichen Vortrag einmal ansehen werden. Was sind mögliche Visualisierungen, welche Datenquellen bieten sich an und welche Beschränkungen der Technologie gibt es?

Aber auch die Sponsoren liessen es sich nicht nehmen ihr Wissen zu verbreiten, hier ist Andreas Heberger von Amazon mit seinem Vortrag zu nennen:

Orange Is The New Blue – Warum Amazon Web Services eine gute Wahl für Ihre Microsoft SQL Workloads ist

Wir zeigen Ihnen welche Möglichkeiten die AWS Plattform bietet, Ihre Microsoft SQL Workloads performant, flexibel und kosteneffizient in der Cloud zu betreiben. Desweiteren bietet AWS Möglichkeiten den administrativen Aufwand zu verringern, damit Sie sich wieder auf Kernaufgaben konzentrieren können. Dabei beleuchten wir die Vor – und Nachteile der einzelnen Varianten, von der klassischen Installation auf IaaS VMs, dem AWS managed Service (RDS) sowie Migrationsstrategien. Darüber hinaus wollen wir Wege aufzeigen wie vorhandene Lizenzinvestments weiterverwendet werden können, und wie Sie mithilfe von AWS sogar Ihre Lizenzkosten verringern können.

Neben den genannten waren viele nationale und europäische Sprecher auf den SQLDays und trugen so zu einem ordentlichen Wissenstransfer bei!

Mein Vortrag zu Azure SQL Database konnte ein wenig auf dem zeitlich früheren Vortrag von Greogor Reimling aufbauen, der den Teilnehmern etwas über die Azure Managed Instance erzählte.

Gregor Reimling - SQLDays 2018 - Azure SQL Managed Instance

Kurz vor der Mittagspause stand ich dann im großen Saal und durfte den interessierten Teilnehmern etwas zu Azure SQL Database und deren Administration mit Powershell näher bringen, für alle die an meinem Vortrag teilgenommen haben, hier auch noch einmal ein Hinweis wegen der gescheiterten Demo => hier ist der Nachtrag bzw die Korrektur

Die Organisation der Veranstaltung war sehr gut, Teilnehmer und Sprecher der SQLDays wurden sehr gut versorgt, man musste sich um nichts Gedanken machen, egal ob Getränke und Snacks zwischen den Sessions oder Mittagessen oder während der gemeinsamen Abendveranstaltung! Ausreichend Getränke und super-leckeres Essen sorgten für eine angenehme Atmosphäre und motivierte Mitwirkende!

Bjoern Peters - Azure SQL Database - SQLDays 2018

Natürlich habe ich auch im Rahmen meiner Anwesenheit (ich war nur den Dienstag auf den SQLDays) auch mehreren Sessions gelauscht, wie zum Beispiel Andre Essing:

The joy of analytics – Lassen Sie uns zusammen ein Data Warehouse in die Cloud malen

Es gibt eine Menge Wege und Möglichkeiten Mehrwerte aus seinen Daten zu ziehen. Seit Jahren machen wir das mit den gleichen Techniken in unseren „klassischen“ Data Warehouse Umgebungen. Aber die Welt ist im steten Wandel und so auch die Daten. Wo die Daten vor ein paar Jahren noch ausschließlich strukturiert waren, wächst die Zahl von schemalosen Datentypen ins unermessliche, und eine Ende ist nicht in Sicht. Wir kämpfen mit Daten, bei denen sich ständig das Schemata ändert und versuchen Mehrwerte aus neuen Datentypen wie Soziale Medien, Videos und Bildern zu ziehen. In dieser Whiteboard Session möchte ich mit Ihnen über moderne Wege sprechen, Ihre Daten zu speichern, zu analysieren und zu visualisieren. Zusammen zeichnen wir ein Modern Data Warehouse, schauen uns an welche Möglichkeiten sich bieten verschiedenste Daten zu analysieren und warum uns die Cloud hier hilft. Besuchen Sie meinen Vortrag und zeichnen Sie mit mir zusammen ein modernes Data Warehouse.

Andre Essing - SQLDays 2018

Es hat auf jeden Fall sehr viel Spaß gemacht und es war meine erste Teilnahme an den SQLDays, die meine Münchener Kollegen immer wieder als großartige Veranstaltung erwähnt haben, daher erfüllt es mich auch ein wenig mit Stolz dort als Sprecher dabei sein zu dürfen. Vielleicht klappt es ja auch im nächsten Jahr wieder mit einem spannenden exklusiven Vortrag für die SQLDays 2019 in Erding. Termin steht schon 😉

SQLDays 2019 – Save the date: 14.-17. Okt. 2019 München

Weitere Informationen findet ihr auf der Homepage der SQLDays…

Powershell – Ermitteln aller installierter SQL Server Services

Manchmal sind es die kleinen Dinge, die einem das Leben erleichtern können… 😉
Vor oder nach der Installation des SQL Servers auf einem Windows Server sollte man auch einen entsprechenden Virenschutz installieren, oder man möchte bestimmte Dienste/Services aus dem Monitoring ausklammern… dann benötigt man für die jeweiligen Ausnahmen die Pfade und Namen der ausführenden Programme. Natürlich kann man sich diese Informationen manuell über die Services-Ansicht holen, aber diesen „Klick-Aufwand“ kann man sich getrotzt sparen, denn es geht viel einfacher.

Beispiel aus dem Leben

Für einige unserer Kunden und deren Anti-Viren-Strategie müssen wir unseren Kollegen immer eine EMail zukommen lassen, welche Dienste (ausführbaren Programme) und deren Verzeichnisse NICHT gescannt werden sollen… wir haben uns also auf ein Format geeinigt, welches wir schnell und einfach liefern können und wo die Kollegen die relevanten Daten auf „einen“ Blick entnehmen können, um diese dann in der Administrations-Oberfläche für den jeweiligen SQL Server zu hinterlegen. Da wir nicht nur einen SQL Server installieren, sondern ein paar mehr… musste eine „universelle“ Lösung her.

Overview SQLServer Services GUI

Wo bekommen wir also diese Daten idealerweise her? Natürlich aus dem Betriebssystem und dem bereits mitgelieferten WMI-Stack… also ran an die Services-Informationen

Get-WmiObject win32_service

Aber so erhalten wir eben eine Vielzahl von Informationen und Services, die wir für unsere Zwecke gar nicht gebrauchen können, denn wir wollen ja nur die SQL Server relevanten Services identifizieren und an die Kollegen melden… also müssen wir diese Informationsflut mit einem Filter eindämmen.


Jetzt erhalten wir nur noch alle Services mit dem Pattern „*sql*“, was für einen SQL Server völlig ausreichend ist, denn glücklicherweise werden aktuell alle SQL Server auf Windows Dienste mit eben diesem Pattern bezeichnet. Hier fehlen aber die relevanten Informationen wie Pfad und Exe oder der Anzeigename… damit wir nun auch diese weiteren Informationen erhalten muss man ein wenig tricksen und die Pfad-Angabe – welche hier ebenfalls (versteckt) enthalten ist – zerlegen.

Get-WmiObject win32_service | ?{$_.Name -like '*sql*'} | select Name, DisplayName, @{Name="Path"; Expression={$_.PathName.split('"')[1]}}


Theoretisch könnte man das nun an die Kollegen schicken, damit aber dort kein Fehler bei Copy&Paste passiert, formatieren wir das Ergebnis noch mit dem Powershell-Ausgabe-Format „Format-List“, dann verrutscht man nicht mehr so leicht in der Zeile und es ist etwas übersichtlicher (auf meinem Laptop ist nicht viel installiert, aber ihr versteht zumindest was ich meine) 😉

Get-WmiObject win32_service | ?{$_.Name -like '*sql*'} | select Name, DisplayName, @{Name="Path"; Expression={$_.PathName.split('"')[1]}} | Format-List

Dieses Ergebnis können wir nun ohne Bedenken in ein Mail-Template einfügen und den Kollegen schicken…

SQLDays 2018 in München – Nachtrag – Pause aka Restore

Im Rahmen meines Vortrages auf den SQLDays in München hatte ich versucht einen Workaround zu „Wie spare ich Geld? Ich droppe einfach die Datenbank!“… hierzu möchte ich gerne ein Update posten.
Aber der Reihe nach, denn erst einmal möchte ich mich recht herzlich bei der ppedv Event bedanken für die Einladung nach München, zum anderen bei den zahlreichen Teilnehmern, die meine Session besucht haben! Ich hoffe ihr habt alle etwas aus meiner Session mitgenommen und könnt diese Ideen und Anregungen in eurem Alltag umsetzen bzw integrieren.

Unsplash - Photo by kevin Xue - THANK YOU

Powershell – Workaround um Kosten zu sparen

In meiner Demo ist das Wiederherstellen nach dem „Drop Database“ leider fehlgeschlagen, so dass ich in meiner Demo nicht zeigen konnte wie man aus dem logischen SQL Server das gespeicherte „Last Deleted Database“ entnehmen kann, um daraus einen Restore zu erzeugen. Ich habe diese Code-Zeilen bisher immer nur einmal ausgeführt, also Demo-Umgebung frisch aufsetzen, die Skripte nur auf Syntax überprüfen und hoffen das alles gut geht 😉 Dieses mal hatte ich meine Skripte eben doch einmal vorher ausgeführt, dadurch entstand schon ein Eintrag für „DeletedDatabases“… und somit gab es eben mehr als einen Eintrag und der Restore wußte nicht auf welches Datum er zurück gehen sollte…

# Do not continue until the cmdlet returns information about the deleted database.
if(![string]::IsNullOrEmpty($deleteddatabase)) {
Write-Host "Now restoring database $databasename_backup ..." -foregroundcolor "DarkYellow"
Restore-AzureRmSqlDatabase -FromDeletedDatabaseBackup `
-ResourceGroupName $resourcegroupname `
-ServerName $servername_backup `
-TargetDatabaseName $databasename_backup `
-ResourceId $deleteddatabase.ResourceID `
-DeletionDate $deleteddatabase.DeletionDate `
-Edition "Standard" `
-ServiceObjectiveName "S0"
}

Bin ich so bisher noch gar nicht drüber gestolpert, aber als ich dann nach der Session das Skript und den Output mir noch einmal angesehen habe…

SQLDays AzureSQLDB DeletedDatabases - Scriptproblem

Ich könnte mir vorstellen, dass es noch eine sichere oder sinnvollere Lösung gibt, aber als Ansatz bzw vorübergehende Lösung kann man das erst einmal so machen 😉
Es gibt zumindest zwei Möglichkeiten…

Möglichkeit 1

Aus meiner Sicht am einfachsten, aber unter Umständen nicht sicher, dass tatsächlich das Datum des letzten Löschvorganges übernommen wird, da das Skript zuvor keinen Einfluss auf die Sortierung der Ergebnisse nimmt.

# Do not continue until the cmdlet returns information about the deleted database.
if(![string]::IsNullOrEmpty($deleteddatabase)) {
Write-Host "Now restoring database $databasename_backup ..." -foregroundcolor "DarkYellow"
Restore-AzureRmSqlDatabase -FromDeletedDatabaseBackup `
-ResourceGroupName $resourcegroupname `
-ServerName $servername_backup `
-TargetDatabaseName $databasename_backup `
-ResourceId $deleteddatabase[0].ResourceID `
-DeletionDate $deleteddatabase[0].DeletionDate `
-Edition "Standard" `
-ServiceObjectiveName "S0"
}

Syntaktisches korrekt, funktioniert auf jeden Fall, ABER eben keine 100%ige Gewissheit, dass auch tatsächlich der gewünschte Zeitpunkt ausgewählt wird.

Möglichkeit 2

Etwas „umständlicher“, aber auf jeden Fall sicherer, denn wir wählen explizit das zu letzt hinzugefügte Datum aus.

# Do not continue until the cmdlet returns information about the deleted database.
if(![string]::IsNullOrEmpty($deleteddatabase)) {
Write-Host "Now restoring database $databasename_backup ..." -foregroundcolor "DarkYellow"
Restore-AzureRmSqlDatabase -FromDeletedDatabaseBackup `
-ResourceGroupName $resourcegroupname `
-ServerName $servername_backup `
-TargetDatabaseName $databasename_backup `
-ResourceId ($deleteddatabase.ResourceID | Select-Object -Last 1) `
-DeletionDate ($deleteddatabase.DeletionDate | Select-Object -Last 1) `
-Edition "Standard" `
-ServiceObjectiveName "S0"
}

Syntaktisches korrekt, funktioniert auf jeden Fall, ABER eben keine 100%ige Gewissheit, dass auch tatsächlich der gewünschte Zeitpunkt ausgewählt wird.

SQLDays-AzureSQLDB-DeletedDatabases-Script-optimized

Nun kann ich auch die SQLDays 2018 in München auf jeden Fall als Erfolg verbuchen, da ich wieder etwas an meinen Demos optimieren konnte und den Teilnehmern doch noch zeigen kann, wie dieses Skript funktioniert. In den nächsten Tage werde ich meine Demo-Skripte auf Github entsprechend aktualisieren, damit jeder davon profitieren kann!

Hier findet Ihr/sie nicht nur meine Skripte sondern auch meine Slides als PDF.

Skripte und Slides zu den SQLDays 2018 von SQL_aus_HH auf Github

Azure SQL Database – Probleme beim Löschen von Recovery Service Vault

Ich möchte euch heute von einer Erfahrung berichten, welche ich in den letzten Tagen mehrfach und wiederholt machen musste. Für meine Sessions bei der Imagine Cloud Conference und dem PASS Camp über Azure SQL Databases hatte ich einen Azure Recovery Service Vault angelegt, um dort die Backups für die Langzeit-Archivierung abzulegen. Nachdem die Sessions beendet waren, wollte ich aufräumen bzw für die nächste Session vorbereiten, hierzu wollte ich die ganze Ressource-Gruppe löschen (samt SQL Server, Azure SQL Databases und dem besagten Recovery Service Vault). Egal was ich versucht habe, der Recovery Service Vault ließ sich nicht löschen, der SQL Server mit seinen SQL Databases konnte erfolgreich gelöscht werden, der ARS Vault aber blieb hartnäckig bestehen… Die Fehlermeldung lautete sinngemäß, dass noch registrierte Server / Items vorhanden sind (Please delete any replicated items, registered servers, Hyper-V sites…), welche erst gelöscht werden müssen. In der Service-Übersicht war allerdings nichts hierzu zu finden, keine registrierten Server, keine registrierten Server oder Replicated Items, keine Größen… es konnte nichts gefunden werden, was darauf hindeutete wo sich noch etwas verbirgt.

fast verzweifelt - verzweifelte Suche - StockSnap_27XHUPB48N - aus dem Newsletter von StockSnap

Verzweiflung breitet sich aus

Wie kann ich also nun diesen Azure Recovery Service Vault wieder löschen? Wenn es sich wenigstens um eine generische Ressourcengruppe handeln würde, aber da ich zur Abgrenzung der einzelnen Veranstaltungen bzw der Einfachheit halber alle Ressourcen einer Demo in einer Ressourcengruppe anlege, damit ich sie schneller löschen kann… hätte ich den ARS Vault entweder gerne verschoben oder eben doch ganz gelöscht… (siehe Beitragsbild ganz oben)

Folgende Fehlermeldung habe ich wiederholt bekommen:

Deletion of ARS Vault failed - Error Message

Vault cannot be deleted as there are existing resources within the vault. Please ensure there are no backup items, protected servers or backup management servers associated with this vault. Unregister the following containers associated with this vault before proceeding for deletion : Unregister all containers from the vault and then retry to delete vault

Bei der Kontrolle des Azure Recovery Services, welche Server oder Backup Items noch in dem ASR-Vault konfiguriert sind, schaute ich natürlich als erste in den entsprechenden Service nach… aber egal welche Ansicht ich auch öffnete, ich konnte keinerlei verbliebenen Backups finden.

Deletion of ARS Vault failed - Backup Details on ARS

 

Also erst einmal eine Runde googlen ob jemand anderes dieses Problem schon einmal gehabt hat… und siehe da, so ganz unbekannt ist das Problem mit dem Löschen des Azure Recovery Services nach der Nutzung mit Azure SQL Databases nicht. Felipe de Assis hatte hierzu bereits einen kleinen Beitrag und ein Skript geschrieben => https://social.msdn.microsoft.com/ Der eigentliche Grund liegt darin, dass weder Azure SQL VM Server noch Azure SQL Databases dort als aktiv gelistet werden und diese explizit mittels Powershell zu löschen sind. Ich habe das Skript nach meinen Anpassungen hier unten angehängt, so dass ihr seht was ich meine…

Login-AureRmAccount

$vault = Get-AzureRmRecoveryServicesVault -Name "WingtipDB-LongTime"
Set-AzureRmRecoveryServicesVaultContext -Vault $vault

$containers = Get-AzureRmRecoveryServicesBackupContainer -ContainerType AzureSQL -FriendlyName $vault.Name
ForEach ($container in $containers) {
 $items = Get-AzureRmRecoveryServicesBackupItem -container $container -WorkloadType AzureSQLDatabase
 ForEach ($item in $items) {
 Disable-AzureRmRecoveryServicesBackupProtection -item $item -RemoveRecoveryPoints -ea SilentlyContinue
 }
 Unregister-AzureRmRecoveryServicesBackupContainer -Container $container
}
Remove-AzureRmRecoveryServicesVault -Vault $vault

Damit ließ sich auch der Recovery Service Vault einwandfrei löschen und dann auch die Ressourcegruppe und ich hatte wieder eine saubere Test- bzw Demo-Umgebung.