PowerShell dla administratorów SQL Server: Wybrane przykłady zastosowań cz. II (Policy-Based Management) Autor: Damian Widera PowerShell to nowy język skryptowy firmy Microsoft, którego istnienia nie sposób pominąd. Można co prawda zadad sobie pytanie po co uczyd się i poznawad kolejny język programowania? Pierwszy artykuł z serii prezentował ogólne możliwości, które daje użycie tego języka. W drugim artykule postanowiłem pokazad, w jaki sposób można użyd skryptu PowerShell do programowania i zarządzania systemem zarządzania politykami serwera. Wprowadzenie do Policy-Based Management System zarządzania politykami serwera (ang. Policy-Based Management, PBM) jest z całą pewnością jedną z najważniejszych nowości, która została wprowadzona do SQL Server 2008. PBM pozwala na zarządzanie instancjami SQL Server poprzez definiowanie polityk (ang. policies). Administratorzy baz danych mogą używad PBM między innymi za pomocą SQL Server Management Studio (SSMS),czyli tworzyd polityki oraz zarządzad serwerem, bazami danych, tabelami, indeksami lub innymi obiektami zawartymi w SQL Server. W SQL Server 2008 zostały przygotowane szablony reguł oparte o najlepsze praktyki zaimplementowane w narzędziu Best Practices Analyzer, używanym przez administratorów z poprzednimi wersjami systemu SQL Server. System Policy Based Management ma strukturę hierarchiczną i składa się z trzech podstawowych elementów: PBM management facets jest to zestaw reguł modelujących zachowanie lub charakterystykę określonego obiektu. Liczba i charakterystyka własności jest wbudowana w każdą regułę i może być zmieniona (dodana, usunięta) tylko przez twórcę reguły. Każda polityka może wykorzystywać jedną lub więcej reguł. PBM conditions są to warunki określające zestaw dozwolonych stanów, w których może znaleźć się obiekt kontrolowany przez politykę zarządzania serwerem. Dozwolone stany dla każdego obiektu są określane przez omówione powyżej zestawy reguł i charakterystyk (facets). PBM policies polityki składają się z warunków, które wymuszają określone zachowanie obiektu. Polityka może zawierać tylko jeden warunek. Polityki zarządzania serwerem w konsoli PowerShell Po zainstalowaniu SQL Server 2008 dostępny jest host oraz konsola PowerShell, z poziomu której można mied dostęp do obiektów serwera. Nic nie stoi więc na przeszkodzie, aby programowad oraz zarządzad serwerem SQL, o ile tylko użytkownik ma do tego odpowiednie uprawnienia. 1
W niniejszym artykule przedstawię przykład, w którym pokażę, w jaki sposób wykonad warunek i politykę sprawdzającą, czy funkcjonalności dostępne we wcześniejszych wersjach systemu w narzędziu Surface Area Configuration (SAC) są odpowiednio skonfigurowane (dokładny opis znaczenia każdego z wyżej wymienionych opcji można znaleźd w Books Online): AdHocRemoteQueriesEnabled ustawione na false, ClrIntegrationEnabled ustawione na false, DatabaseMailEnabled ustawione na false, OleAutomationEnabled ustawione na false, RemoteDacEnabled ustawione na false, ServiceBrokerEndpointActive ustawione na false, SoapEndpointsEnabled ustawione na false, SqlMailEnabled ustawione na false, WebAssistantEnabled ustawione na false, XPCmdShellEnabled ustawione na false. Zapewne wielu użytkowników wie, jak należy wykonad to zadanie z poziomu SSMS, ponieważ to zagadnienie było tematem artykułów na stronach TechNet. Osoby, które zaczynają poznawad nowe funkcje SQL Server 2008 odsyłam do Akademii SQL (publikowanej również na polskich stronach TechNet), w ramach której na jednej z pierwszych lekcji pokazano krok po kroku, jak należy rozpocząd pracę z PBM. Wykonanie skryptu tworzącego politykę zarządzania serwerem SQL. Aby w pełni skorzystad z obiektów SQL Server 2008 oraz znacząco ułatwid sobie wykonanie skryptu należy w konsoli PowerShell załadowad odpowiednie biblioteki. Można to zrobid wybierając jeden z poniższych sposobów: Umieścić referencje do bibliotek w pliku profilu hosta, i ładować biblioteki za każdym razem, kiedy uruchamiany jest host PowerShell, Umieścić referencje do bibliotek w skrypcie, który będzie uruchamiany w ramach tego zadania; z obiektów zawartych w bibliotekach można będzie skorzystać dopiero po załadowaniu skryptu, Załadować biblioteki ręcznie w konsoli PowerShell. Używając słowa host PowerShell mam na myśli mini konsolę PowerShell, czyli aplikację sqlps.exe, która zostaje zainstalowana w momencie instalacji SQL Server 2008. Ekran aplikacji pokazano na rys. 1. Rysunek 1. Aplikacja sqlps.exe. 2
Można także skorzystad z systemowego hosta PowerShell, czyli aplikacji powershell.exe pod warunkiem jednak, że doinstalowany zostanie provider SQL Server (zostało to opisane w *1+). Ekran aplikacji powershell.exe pokazano na rys. 2. Rysunek 2. Aplikacja powershell.exe. Komendy ładujące biblioteki mają postad: [reflection.assembly]::loadwithpartialname("microsoft.sqlserver.connectioninfo") [reflection.assembly]::loadwithpartialname("microsoft.sqlserver.smo") [reflection.assembly]::loadwithpartialname("microsoft.sqlserver.smoenum") [reflection.assembly]::loadwithpartialname("microsoft.sqlserver.dmf") [reflection.assembly]::loadwithpartialname("microsoft.sqlserver.management.sdk.sfc") [reflection.assembly]::loadwithpartialname("microsoft.sqlserver.policyenum") Pamiętając architekturę systemu PBM należy postępowad według następującego schematu: a) Utworzyć nowy warunek lub warunki korzystając z listy dostępnych obiektów (ang. facets). b) Utworzyć politykę zarządzania serwerem bazując na wcześniej wykonanych warunkach. Nic nie stoi na przeszkodzie, aby raz wykonane warunki użyd w wielu politykach, ponieważ warunki same w sobie nie definują polityki; należy je raczej rozumied jako brzegowe ograniczenia, których obiekt nie może pokonad jeśli nie chce naruszad polityki. Poniżej zaprezentowany kod ma za zadanie przygotowad zmienne, które będą wykorzystywane w trakcie przykładu: #zmienna pcname przechowa nazwę komputera. $pcname = get-item env:\computername #ścieżka do servera $serverpath = ('SQLSERVER:\SQL\' + $pcname.value + '\default') $servername = get-item $serverpath # ścieżka do obiektu polityk zarządzania serwerem $policiespath = ('SQLSERVER:\SQLPOLICY\' + $pcname.value + '\default') $policies = get-item $policiespath #nazwa biblioteki $PBM = 'Microsoft.SqlServer.Management.Dmf.' Pierwsza zmienna - $pcname przechowuje informację o nazwie komputera, na którym wykonywany jest skrypt. Nazwa ta jest pobierana ze zmiennych środowiskowych systemu operacyjnego, których pełna lista jest dostępna po wykonaniu poleceo: get-psdrive 3
Cd env: DIR Druga i trzecia zmienna przechowują ścieżki do obiektu serwera i obiektów polityk dla instancji domyślnej. Czwarta zmienna - $PBM przechowuje stałą tekstową stanowiącą, która umożliwi tworzenie pozostałych obiektów, takich jak warunki czy polityki. Utwórzmy zatem nowy warunek. W tym celu należy stworzyd obiekt klasy Microsoft.SqlServer.Management.Dmf.Condition, nadad mu unikalną nazwę oraz wskazad, jakiego obiektu (ang. facet) będzie on dotyczył: $cond = new-object ($PBM + 'Condition') $policies, "SAC 2005 Config" $cond.facet = "ISurfaceAreaFacet" Póki co wykonaliśmy jedynie definicję warunku. Można sprawdzid, że nie zawiera ona jeszcze żadnych zdefiniowanych własności: Rysunek 3. Definicja warunku SAC 2005 Config. Wstawmy w utworzoną powyżej definicję informacje obiektu SurfaceAreaFacet, które muszą zostad sprawdzone. Zanim do tego przejdziemy należy zdefiniowad kilka stałych, które uproszczą tworzenie warunków: $war_and = [Microsoft.SqlServer.Management.Dmf.OperatorType]::AND $war_rowne = [Microsoft.SqlServer.Management.Dmf.OperatorType]::EQ $wyl_false = [Microsoft.SqlServer.Management.Dmf.ExpressionNodeFunction+Function]::False $O_FALSE = new-object ($PBM + 'ExpressionNodeFunction') $wyl_false Pierwsza stała- $war_and pozwoli łączyd ze sobą elementy warunku tak, że jeśli jeden z tych elementów nie będzie spełniony, to cała polityka zostanie naruszona (w analogiczny sposób można utworzyd zmienną, która wykona operację OR). Stała $war_rowne pozwoli wykonad operację porównywania wartości, którą posiada sprawdzana własnośd sprawdzanego obiektu do innej wartości. W naszym przypadku będziemy porównywad z wartością false, która jest reprezentowana przez obiekt $O_FALSE. Zdefiniujmy zatem te własności obiektu SurfaceAreaConfiguration, które zostaną sprawdzone w polityce: $wl_adhoc = new-object ($PBM + 'ExpressionNodeAttribute') "AdHocRemoteQueriesEnabled" $wl_clr = new-object ($PBM + 'ExpressionNodeAttribute') "ClrIntegrationEnabled" $wl_dbmail = new-object ($PBM + 'ExpressionNodeAttribute') "DatabaseMailEnabled" 4
$wl_ole = new-object ($PBM + 'ExpressionNodeAttribute') "OleAutomationEnabled" $wl_dac = new-object ($PBM + 'ExpressionNodeAttribute') "RemoteDacEnabled" $wl_broker = new-object ($PBM + 'ExpressionNodeAttribute') "ServiceBrokerEndpointActive" $wl_soap = new-object ($PBM + 'ExpressionNodeAttribute') "SoapEndpointsEnabled" $wl_sqlmail = new-object ($PBM + 'ExpressionNodeAttribute') "SqlMailEnabled" $wl_web = new-object ($PBM + 'ExpressionNodeAttribute') "WebAssistantEnabled" $wl_cmd = new-object ($PBM + 'ExpressionNodeAttribute') "XPCmdShellEnabled" Mając zdefiniowane własności obiektu, możemy dla każdej z nich wypełnid definicje warunku. Polega to na zbudowaniu kolejnego obiektu, w którym wskazujemy sposób sprawdzania wartości dla danej własności. W naszym przypadku każda wartośd własności obiektu SurfaceAreaConfiguration ma byd przyrównana do wartości false: $o_adhoc = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_adhoc, $O_FALSE $o_clr = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_clr, $O_FALSE $o_dbmail = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_dbmail, $O_FALSE $o_ole = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_ole, $O_FALSE $o_dac = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_dac, $O_FALSE $o_broker = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_broker, $O_FALSE $o_soap = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_soap, $O_FALSE $o_sqlmail = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_sqlmail, $O_FALSE $o_web = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_web, $O_FALSE $o_cmd = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_cmd, $O_FALSE Nadeszła pora, aby powiązad utworzone obiekty relacjami, które pomiędzy nimi występują. W przypadku, który omawianym w tym artykule, wszystkie własności obiektu SurfaceAreaConfiguration połączone są relacją AND, to znaczy wszystkie własności muszą byd spełnione, aby nie została naruszona polityka: $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_adhoc, $o_clr $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_dbmail $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_ole $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_dac $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie, $o_broker $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_soap $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_sqlmail $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_web $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_cmd Można zauważyd, że wykorzystałem jeden obiekt $o_wszystkie do skonstruowania relacji pomiędzy wcześniej wykonanymi obiektami. Z drugiej strony w każdym przypisaniu relacji można tworzyd nowy obiekt i podawad go jako jeden z parametrów do kolejnego obiektu. Obiekt $o_wszystkie jest obiektem klasy Microsoft.SqlServer.Management.Dmf.ExpressionOperatorNode, a jego zadaniem jest utworzenie wyrażenia w postaci: 5
Left OpType Right Lewa częśd wyrażenia (Left) zostanie połączona z prawą częścią (Right) za pomocą operatora OpType. Analizując ten zapis można stwierdzic, że w ten sposób można skonstruowad znacznie bardziej logicznie skomplikowany warunek, niż użyty w naszym przykładzie, w którym wszystkie wyrażenia łączone są operatorem AND. Na rysunku 4 pokazałem zawartośd obiektu $o_wszystkie po wykonaniu komend opisanych na wcześniejszych fragmentach kodu: Rysunek 4. Obiekt $o_wszystkie. Definiowanie warunku można uznad za zakooczone i jedynym krokiem, który pozostał do wykonania, to jego formalne zatwierdzenie przy użyciu komendy Create(). Dodatkowo, należy przypisad do własności ExpressionNode obiektu Condition obiekt $o_wszystkie: $cond.expressionnode = $o_wszystkie $cond.create() Po wykonaniu opisanych powyżej komend można przekonad się, że warunek został utworzony i jest możliwe jego wykorzystanie przy tworzeniu polityk zarządzania serwerem (rysunek 5). Rysunek 5. Lista warunków widziana z poziomu konsoli PowerShell. Warunek można także odnaleźd w Eksploratorze Obiektów w konsoli SSMS. W tym celu należy rozwinąd folder Management i przejśd do węzła Conditions, jak pokazano na rysunku 6: 6
Rysunek 6. Widok Eksploratora Obiektów z utworzonym warunkiem. Mając utworzony warunek można przystąpid do zbudowania polityki zarządzania serwerem. Nie jest to zadanie trudne i w najprostszym przypadku polega na wykonaniu poniżej opisanych kroków: 1. Zbudowaniu obiektu klasy Microsoft.SqlServer.Management.Dmf.Policy podając ścieżkę do elementu Policies oraz nazwę nowego obiektu (polityki) 2. Zapisaniu w własności Condition nazwy warunku, na bazie którego zostanie wykonana polityka 3. Określeniu sposobu uruchomienia polityki, mając do wyboru jeden z czterech trybów: a. CheckOnChanges, który uaktywnia sprawdzanie polityki w sytuacji dokonywania zmian w obiekcie, którego ta polityka dotyczy. Rozwiązane jest to przy pomocy mechanizmu Event Notification, b. CheckOnSchedule, w którym zostaje utworzone zadanie usługi Agent, które uruchamiane jest zgodnie z podanym interwałem, c. Enforce, która działa w ten sam sposób, co opisany wcześniej tryb CheckOnChanges, ale zapobiega zatwierdzeniu zmian, jeśli naruszają politykę, d. None, w którym polityka nie jest sprawdzana. 4. Ustawieniu własności Enabled polityki na jedną z wartości: True lub False. Domyślna wartość własności Enabled to False co oznacza, że polityka nie będzie uruchomiona nawet jeśli jej tryb jest inny niż None. 5. Wywołaniu metody Create()na obiekcie klasy Microsoft.SqlServer.Management.Dmf.Policy. Poniższy kod demonstruje opisane powyżej kroki: #zmienna wyliczeniowa przechowująca informacje o sposobie uwuchamiania polityki $pol_exec = [Microsoft.SqlServer.Management.Dmf.AutomatedPolicyEvaluationMode]::None $policyobject = new-object ($PBM + 'Policy') $policies, "SAC 2005 Policy" $policyobject.condition = "SAC 2005 Config" $policyobject.automatedpolicyevaluationmode = $pol_exec 7
$policyobject.enabled = $false $policyobject.create() Krótkie sprawdzenie przy użyciu konsoli PowerShell pozwala upewnid się, że polityka została pomyślnie utworzona i może zostad użyta zgodnie z przypisanymi jej własnościami (rys. 7): Rysunek 7. Lista polityk zarządzania serwerem widziana z poziomu konsoli PowerShell. Narzędzie wspomagające tworzenie skryptów PowerShell Skrypty PowerShell można pisad na wiele sposobów i używając wielu dostępnych narzędzi. Ze zrozumiałych względów najbardziej zainteresowany byłem użyciem narzędzi darmowych. Można do nich zaliczyd dostępną w systemie operacyjnym Windows aplikację Notepad, która jednak w tym przypadku nie jest wygodnym w użyciu edytorem. Dużo łatwiejsze staje się tworzenie skryptów PowerShell przy użyciu programu PowerGUI firmy Quest, który posiada wiele ulepszeo istotnych dla każdego programisty: a) Wbudowany debugger, b) Wbudowany mechanizm IntelliSense, c) Sprawdzanie poprawności wpisywanego kodu Nie są to oczywiście wszystkie zalety wspomnianej aplikacji, ale te ułatwiają kodowanie od samego początku. Przykładowy ekran aplikacji pokazałem na rys. 8. 8
Rysunek 8. Ekran aplikacji PowerGUI Podsumowanie W artykule zaprezentowałem niektóre aspekty praktycznego zastosowania języka PowerShell do zarządzania i programowania SQL Server. Zawarte fragmenty kodu były łatwe i niezbyt długie, ale to w zupełności powinno wystarczyd, aby przekonad o możliwościach, które oferuje PowerShell. Nie są to oczywiście jedyne możliwości tego języka. Mając na uwadze przyszłośd, jaka otwiera się przed PowerShell należy poważnie zastanowid się nie tylko nad przyswojeniem sobie reguł, które tym językiem rządzą, ale również nauczyd się używad go na co dzieo. Literatura [1] Medrala, Potasiński, Szeliga, Widera: Serwer 2008. Administracja i programowanie, wyd. Helion Załącznik W załączniku znajduje się kompletny skrypt, który posłużył do wykonania opisanego w artykule przykładu: [reflection.assembly]::loadwithpartialname("microsoft.sqlserver.connectioninfo") [reflection.assembly]::loadwithpartialname("microsoft.sqlserver.smo") [reflection.assembly]::loadwithpartialname("microsoft.sqlserver.smoenum") [reflection.assembly]::loadwithpartialname("microsoft.sqlserver.dmf") [reflection.assembly]::loadwithpartialname("microsoft.sqlserver.management.sdk.sfc") [reflection.assembly]::loadwithpartialname("microsoft.sqlserver.policyenum") #zmienna pcname przechowa nazwe komputera. 9
$pcname = get-item env:\computername #sciezka do servera $serverpath = ('SQLSERVER:\SQL\' + $pcname.value + '\default') $servername = get-item $serverpath # sciezka do obiektu polityk zarzadzania serwerem $policiespath = ('SQLSERVER:\SQLPOLICY\' + $pcname.value + '\default') $policies = get-item $policiespath #nazwa biblioteki $PBM = 'Microsoft.SqlServer.Management.Dmf.' $cond = new-object ($PBM + 'Condition') $policies, "SAC 2005 Config" $cond.facet = "ISurfaceAreaFacet" $war_and = [Microsoft.SqlServer.Management.Dmf.OperatorType]::AND $war_rowne = [Microsoft.SqlServer.Management.Dmf.OperatorType]::EQ $wyl_false = [Microsoft.SqlServer.Management.Dmf.ExpressionNodeFunction+Function]::False $O_FALSE = new-object ($PBM + 'ExpressionNodeFunction') $wyl_false $wl_adhoc = new-object ($PBM + 'ExpressionNodeAttribute') "AdHocRemoteQueriesEnabled" $wl_clr = new-object ($PBM + 'ExpressionNodeAttribute') "ClrIntegrationEnabled" $wl_dbmail = new-object ($PBM + 'ExpressionNodeAttribute') "DatabaseMailEnabled" $wl_ole = new-object ($PBM + 'ExpressionNodeAttribute') "OleAutomationEnabled" $wl_dac = new-object ($PBM + 'ExpressionNodeAttribute') "RemoteDacEnabled" $wl_broker = new-object ($PBM + 'ExpressionNodeAttribute') "ServiceBrokerEndpointActive" $wl_soap = new-object ($PBM + 'ExpressionNodeAttribute') "SoapEndpointsEnabled" $wl_sqlmail = new-object ($PBM + 'ExpressionNodeAttribute') "SqlMailEnabled" $wl_web = new-object ($PBM + 'ExpressionNodeAttribute') "WebAssistantEnabled" $wl_cmd = new-object ($PBM + 'ExpressionNodeAttribute') "XPCmdShellEnabled" $o_adhoc = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_adhoc, $O_FALSE $o_clr = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_clr, $O_FALSE $o_dbmail = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_dbmail, $O_FALSE $o_ole = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_ole, $O_FALSE $o_dac = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_dac, $O_FALSE $o_broker = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_broker, $O_FALSE $o_soap = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_soap, $O_FALSE $o_sqlmail = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_sqlmail, $O_FALSE $o_web = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_web, $O_FALSE $o_cmd = new-object ($PBM + 'ExpressionNodeOperator') $war_rowne, $wl_cmd, $O_FALSE $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_adhoc, $o_clr $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_dbmail $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_ole 10
$o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_dac $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie, $o_broker $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_soap $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_sqlmail $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_web $o_wszystkie = new-object ($PBM + 'ExpressionNodeOperator') $war_and, $o_wszystkie,$o_cmd $cond.expressionnode = $o_wszystkie $cond.create() $pol_exec = [Microsoft.SqlServer.Management.Dmf.AutomatedPolicyEvaluationMode]::None $policyobject = new-object ($PBM + 'Policy') $policies, "SAC 2005 Policy" $policyobject.condition = "SAC 2005 Config" $policyobject.automatedpolicyevaluationmode = $pol_exec $policyobject.enabled = $false $policyobject.create() Autor Damian Widera (MCT, MCITP DBA, MCSD.NET) Od 8 lat zajmuje się projektowaniem, tworzeniem i wdrażaniem aplikacji wykorzystujących platformę.net, SQL Server oraz Oracle. Obecnie pracuje jako project manager dla LGBS Polska. Pracował także jako trener, programista, administrator baz danych, twórca dokumentacji oraz analityk biznesowy. Aktywnie współpracuje z polskim oddziałem Microsoft publikując artykuły, webcasty oraz porady z zakresu SQL Server na stronach TechNet. Jest współautorem książki Serwer SQL 2008. Administracja i programowanie. Prelegent na wielu konferencjach, m.in. Microsoft Heroes Happen Here, C2C, European PASS Conference, Microsoft Technology Summit, Energy Launch, TechED. Jest współtwórcą oraz liderem jednej z największych grup pasjonatów SQL Server w Polsce Śląskiej Regionalnej Grupy Microsoft (PLSSUG Katowice). Od listopada 2008 jest prezesem Polish SQL Server Users Group (PLSSUG) w Polsce. W styczniu 2009 nagrodzony tytułem MVP w kategorii SQL Server. 11