TSQL Tuesday #96: Community Menschen die meinen Weg beeinflusst haben

TSQL Tuesday #96: Menschen die meinen Weg beeinflusst haben

T-SQL Tuesday ist eine wiederkehrende Blog-Serie, die von Adam Machanic (b | t) gestartet wurde, jeden Monat ist ein Blogger Gastgeber für ein Thema rund um den SQL Server und jeder kann einen Blogbeitrag zu diesem bestimmten Thema schreiben. Diesen Monat ist Ewald Cress‏ (blog | twitter) unser Gastgeber und es geht um die Menschen, die uns in unserem Leben mit den Daten in der Community beeinflusst haben.

Ich habe lange Zeit keinen Bezug zu einer Community gehabt, in meiner Jugend war ich ehrenamtlich beim Deutschen Roten Kreuz und jetzt wieder mit “euch” unterwegs.

Wer hat mich in den letzten Jahren beeinflusst?

Eigentlich fing alles mit dem PASSCAMP  2013 an… Ok, vielleicht schon etwas früher, als ich mich Mitte 2012 bei Twitter anmeldete und viel von einer #sqlfamily las.

Hier gilt es auf jeden Fall Brent Ozar zu erwähnen, der mich mit seinem Wissen und Blogbeiträge schon seit Jahren inspiriert und im täglichen DBA-Leben weiter bringt. Auch seine Beiträge zum Thema “Warum sollte ich einen Blog betreiben?” zählen zu meinen immer wieder genannten Gründen. Leider habe ich ihn noch nicht persönlich treffen können, aber das kommt garantiert noch. Seine lustige Art und Weise komplizierte Dinge einem verständlich zu erklären ist einfach großartig!

Dann in 2013 war es mein Besuch beim PASSCamp und somit mein erster direkter Kontakt mit der deutschen sqlfamily aka SQLPass. Hier kann man nur die üblichen Verdächtigen aufzählen, die eigentlich immer auf solchen Veranstaltungen anwesend sind. => Oliver Engels, Tillmann Eitelberg, Kostja Klein, Niko Neugebauer und Andreas Wolter, um nur einige zu nennen… Ich fand deren Gruppendynamik genial und das ganze Miteinander… jeder kennt jeden, jeder lacht mit jedem und jeder redet mit jedem, keiner wird ausgegrenzt!

Da wollte ich mitmachen, irgendwie dazugehören… aber wie???

Henning L. @hlhr_dev Jun 2 thanks to all the speakers and specially to @sql_williamd for this great event #SQLGrillen

Also begann ich nach dieser Erfahrung mich mehr mit der PASS und deren Aktivitäten zu beschäftigen und fand unter anderem Cathrine Wilhelmsen, deren Community-Aktivitäten mich ebenfalls anspornten und aufzeigten, was ich wie anfangen muss => mehr Teilnahmen an lokalen bzw nationalen Aktivitäten der PASS. Dann kam das SQLGrillen von William Durkin und die Session von Andre Kamann über PoSh meets Ola Hallengren und das Zusammentreffen mit Andre Essing, welche mich dazu motivierte selber über meinen Schatten zu springen und als Sprecher in der PASS aufzutreten. Bei den folgenden zwei SQLSaturdays (Rheinland und München 2016) war ich dann erstmalig als Volunteer unterwegs und konnte so in die nationalen Aktivitäten der PASS Deutschland hinein schnuppern. Wie es der Zufall wollte oder das Netzwerk die Kugeln rollte, war der SQLSaturday in München der nächste Baustein in meiner “Community-Karriere” und bescherte mir das Azure Meetup Hamburg.

In 2017 kamen dann die ersten öffentlichen Talks dazu, mal Firmenintern, mal in der PASS-RGV Hamburg, beim SQLGrillen 2017 (DANKE William) und gleich doppelt beim SQLSaturday Rheinland 2017 (Dank an Olli, Tillmann und Kostja)… all das in Verbindung mit meinen Blog- und Twitter-Aktivitäten sorgte dann dafür dass Microsoft mich mit dem MVP-Award auszeichnete.

Ich möchte auf diesem Wege also folgenden Menschen der #sqlfamily meinen Dank aussprechen:

Oliver Engels
Tillmann Eitelberg
Kostja Klein
Andre Essing
William Durkin
Andre Kamann

Ein besonderer Dank geht an Gabi Münster für die Unterstützung bei meinem ersten “großen” öffentlichen Auftritt, im Endeffekt geht/ging es um den “Arschtritt” um über meinen Schatten zu springen. Natürlich halfen auch viele Gespräche und Twitterkontakte mit zahlreichen anderen Community-Mitgliedern (Chrissy, Claudio, Rob, Dirk, Volker und vor allem immer wieder Conny!), um mich nun als Mitglied (zumindest der deutschen) SQLFamilie zu fühlen! VIELEN DANK! Weitere Ziele sind geplant für 2018 😉

Ein ganz besonderer Dank geht an meinen Team-Lead Thorsten Moeller, der mich immer wieder bei meinen Aktivitäten unterstützt und ein noch viel größerer Dank gilt meiner Frau, die diese Aktivitäten ebenfalls unterstützt und mir immer “den Rücken frei hält”!

Powershell meets SQL Server – Backup einer Remote Datenbank

Auf dem SQLSaturday #525 in St. Augustin hatte ich bereits das Vergnügen Andre Kamann zu lauschen, was er alles mit Powershell und dem SQL Server anstellt, meinen zweiten Kontakt zu Andre hatte ich jetzt beim SQLGrillen in Lingen, bei dem er uns die wesentliche Vereinfachung eines Rollouts des Ola Hallengren Skriptes näher brachte. Irgendwie hat mich diese “Vereinfachung” inspiriert… ich bin eigentlich auch eher ein “fauler Hund” und versuche mir mit Tools, Skripten  oder eigenen unterstützenden Webseiten das DBA Leben zu erleichtern. Heute kam dann ein Ticket zu mir, bei dem es um ein einfaches Backup einer Datenbank ging, der Kunde hat Probleme mit seiner Applikation und möchte nun eine Kopie der Datenbank an den Software-Hersteller schicken. Solche Aufgaben haben wir immer wieder, daher dachte ich heute morgen an Andre und seine Powershell-Skripte.
Viele werden jetzt sagen, so ein Backup ist doch recht einfach => entweder auf den Server per RDP oder von seiner Workstation mit dem SQL Server Management Studio verbinden, dann die Datenbank auswählen und mit der rechten Maus “Tasks => Backup => …” Klar das geht, ABER da wir hier meistens über Citrix Steppingsstones oder Admingates in die Kundennetze müssen, erleichtert es uns die Arbeit schon um einiges, wenn wir den einen oder anderen Hop reduzieren können.

Mein Powershell-Template

Da dies meine ersten Schritte mit Powershell-Skripten sind, musste ich mich erst damit auseinandersetzen, wie man am besten ein solches Powershell Skript strukturell aufbaut. Hierzu habe ich auf diesen Powershell Blogs gelesen und keinen “Standard” ausfindig machen können, aber überall konnte ich mir ein wenig abschauen und somit einen “ersten Wurf” eines Powershell Skript Templates erstellen. Natürlich muss sich dieser noch in der täglichen Arbeit bewähren und eventuell angepasst werden, aber erst einmal kann ich (denke ich zumindest) damit arbeiten.

Angefangen habe ich natürlich mit einem Header

# #############################################################################
# DBA - SCRIPT - POWERSHELL
# NAME: template.ps1
# 
# AUTHOR: Björn Peters, SQL-aus-Hamburg.de
# DATE: 2016/08/24
# EMAIL: info@sql-aus-hamburg.de
# 
# COMMENT: This script will....
#
# VERSION HISTORY
# 1.0 2016.08.24 Initial Version.
# 
#
# OPEN POINTS TO ADD
# -Add a Function to ...
# -Fix the...
# 
# #############################################################################

Aus dem Tagesgeschäft weiß ich bzw habe ich die Erkenntnis gezogen, dass es für den DBA/Ausführenden am einfachsten ist, wenn die ggfs manuell zu konfigurierenden Parameter (wenn sie nicht übergeben werden) ganz oben stehen sollten, damit man sie möglichst schnell findet und einfach editieren kann. Also kommt direkt in meinem Powershell Skript Template nach dem Header ein Abschnitt zur Konfiguration des Skriptes.

# --- CONFIG ---#
# Script Path/Directories
$ScriptPath = (Split-Path ((Get-Variable MyInvocation).Value).MyCommand.Path)
$ScriptPluginPath = $ScriptPath + "\plugin\"
$ScriptToolsPath = $ScriptPath + "\tools\"
$ScriptOutputPath = $ScriptPath + "\Output\"
# Date Format
$DateFormat = Get-Date -Format "ddMMyyyy_HHmmss"
# -- END CONFIG ---#

Jetzt kommen wir zu einem der wichtigsten, aber am meisten vernachlässigten Abschnitten eines solchen Powershell Skriptes… der Hilfe.
Bei meinen Recherchen bin ich immer wieder über den Widerspruch gestolpert, dass man auf der einen Seite möglichst sein Skript kurz und übersichtlich gestalten soll, auf der anderen Seite aber gut dokumentiert und erläutert. Natürlich kann man aus einem Skript mehrere Versionen machen, eine zum Ausführen und eine zum Weitergeben… aber das führt meist zu Fehlern da man garantiert nicht immer alle Änderungen in beiden Daten ausführt => also bitte gleich richtig machen und einen Hilfe-Abschnitt integrieren und ausführlich beschreiben.

# --- HELP ---#
<#
.SYNOPSIS
Cmdlet help is awesome.

.DESCRIPTION
This Script does a ton of beautiful things!

.PARAMETER

.INPUTS

.OUTPUTS

.EXAMPLE

.LINK
https://www.sql-aus-hamburg.de
#>
# --- END HELP ---#

Zu guter letzt bleiben nur noch die Abschnitte für die einzelnen Funktionen und das eigentliche Powershell Skript. Früher (zu meinen VB / VBA Zeiten) hatte ich gelernt, dass man die Funktionen immer nach unten schreibt und den eigentlichen Programmcode nach oben. Aber dies scheint (zumindest für Powershell) mittlerweile überholt zu sein, also kommen die Funktionsaufrufe, die eigentliche Logik des Skriptes nach unten.

# --- FUNCTIONS ---#
Param(

)

BEGIN {

}

PROCESS {

}

END {

}
# --- END FUNCTIONS ---#

# --- SCRIPT ---#

Quellen und großen Dank an :
http://www.lazywinadmin.com/2012/03/powershell-my-script-template.html
Bei Francois habe ich die meisten Dinge (nachdem ich sie auch anderweitig unter “Best Practices” für Powershell Skripte gelesen hatte) in einem Template gefunden und für mich am besten geeignet befunden. Also habe ich sein Template nahezu 1:1 übernommen.

Mein erstes Powershell Skript

Nun zum eigentlichen SQL Server Backup Skript, welches ich mit Powershell umsetzen wollte.
Also ich wollte mit “Bordmitteln” von einer zentralen Maschine ein Backup auf einem Remote SQL Server erstellen, welches ich dann “einfach” für den Kunden oder Drittdienstleister kopieren und bereitstellen konnte. Was liefert also die SQL Server Installation von Haus aus für Möglichkeiten? Natürlich die SQL Server Management Objects (SMO), welche im Rahmen der Management Tools Installation mitinstalliert werden .

Welche Parameter möchte ich denn angeben bzw übergeben, damit mein Backup erfolgreich läuft…

  • Servername
  • Instanzname
  • Datenbankname
  • SQL-Login => Username/Passwort

Nach einigem Nachdenken kamen noch zwei weitere Parameter dazu… => CopyOnly (True/False) und OpenInExplorer (True/False)

Param(
          [Parameter(Mandatory=$true)][string]$ServerName,
          [string]$InstanceName,
          [Parameter(Mandatory=$true)][string]$DatabaseName,
          [string]$SQLUserName,
          [string]$SQLPwd,
          [switch]$CopyOnly = $True,
          [switch]$OpenInExplorer = $False
)

Also BEGIN der Funktion

Erst einmal alle Parameter überpüfen, zusammensetzen und ggfs umsetzen, so dass sie im Skript bzw T-SQL Befehl verwendet werden können.
Vielleicht kann man das optimaler/sicherer gestalten (Anmerkungen/Tips hierzu bitte über die Kommentarfunktion), aber ich wollte ja einen schnellen Erfolg. Zu Beginn der Funktion prüfe ich also, ob es sich um eine Named-Instance oder Default-Instance handelt, wie der User sich anmelden möchte => Windows Authentifizierung oder SQL Login, on das Backup als Copy-Only-Backup erstellt werden soll und natürlich den Zielort des Backups (Default Backup Directory).

BEGIN {
	"Starting Backup on $($ServerName)"
        
        [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | out-null
        [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo") | out-null
		
        $CommonParameters = ''

        if ($InstanceName) {
	    $SQLServer = $ServerName + "\" + $InstanceName
        } else {
            $SQLServer = $ServerName
        }

        $SQLSvr = New-Object 'Microsoft.SqlServer.Management.SMO.Server' $SQLServer
        if ($SQLUserName -and $SQLPwd) {
            $SQLSvr.ConnectionContext.LoginSecure=$false;
            $SQLSvr.ConnectionContext.Login=$SQLUserName
            $SQLSvr.ConnectionContext.Password=$SQLPwd
        }

	$BackupDir = $SQLSvr.Settings.BackupDirectory "BackupName - $($BackupDir)\$($DatabaseName)_db_$($DateFormat).bak"

        if ($CopyOnly -eq $True) { $CommonParameters += " -CopyOnly" }

}

Nun kommt der PROCESS

Wie der Name schon sagt, hier geschieht der eigentliche Prozess, also die eigentlich Hauparbeit… Im Falle eines Backup-Skriptes werden hier die Backup Befehle ausgeführt. Da dies mein Einstieg in die Powershell Programmierung ist, es gibt bestimmt Wege dies anders bzw optimaler zu gestalten, aber für mich funktionierte das erst einmal ganz gut so. Wenn also der String $CommonParameters leer oder Null ist wird der einfache Backup Befehl ausgeführt ansonsten werden die WITH-Parameter an den Backup Befehl angehängt. Ansonsten passiert in diesem PROCESS Schritt nicht viel 😉

PROCESS {
        IF([string]::IsNullOrWhiteSpace($CommonParameters)) {
            Backup-SqlDatabase -InputObject $SqlServer -Database $DatabaseName -BackupFile "$($BackupDir)\$($DatabaseName)_db_$($DateFormat).bak"
        } else {
		    Backup-SqlDatabase -InputObject $SqlServer -Database $DatabaseName -BackupFile "$($BackupDir)\$($DatabaseName)_db_$($DateFormat).bak" $CommonParameters
        }
	}

Alles hat ein ENDe

Eigentlich wäre hier nun alles zu Ende, wir haben ein Backup auf einem Remote SQL Server in das Default Backup Verzeichnis geschrieben… hier könnten wir es abholen und dem Anforder zur Verfügung stellen. Aber ich bin ein wenig faul… also was liegt näher als den letzten Schritt noch etwas zu vereinfachen.
Also habe ich in den END Abschnitt einfach einen Explorer Aufruf mit dem Zielpfad (Default Backup Verzeichnis) hinzugefügt, so muss ich nicht erst lange suchen und hin und her klicken, sondern der Explorer öffnet sich direkt mit dem Zielpfad und ich brauche die Datei nur noch weg zu kopieren. 😉

END {
        "Finished Backup on $($ServerName)"
        if ($OpenInExplorer -eq $True) { 
            $NewBackupDir = "\\$($ServerName)\"
            $NewBackupDir += $BackupDir.Replace(':\', '$\')
            Invoke-Item $NewBackupDir 
        }
	}
}
# --- END FUNCTIONS ---#

Zum Abschluss im Skript muss natürlich noch der ganze Ablauf einmal gestartet werden, also rufe ich als “letzte” Zeile des Skriptes schnell die Funktion auf… FERTIG.

Vielen Dank nochmal an Andre für die Inspiration.