Jedna z firm poprosiła mnie niedawno o pomoc w następującej sprawie:
„Co roku wykonujemy tzw. True-up licencyjny dla Microsoft EMEA dotyczący ilości wykorzystywanych licencji SQL. Potrzebujemy wygenerować raport, zawierający wszystkie edycje SQL w naszej firmie, łącznie z wersją SQL (Edition, Standard, Deweloper itp.), jaka na danej maszynie jest zainstalowana”. Posiadamy Configuration Managera 2012, który zarządza wszystkimi maszynami w naszej organizacji.”
Case – wydawałoby się – prosty. Ot – wystarczy odpytać odpowiednie tabele Configuration Managera, by uzyskać wymagane informacje. Problem zaczyna się w momencie, kiedy zachodzi potrzeba uzyskania konkretnych wersji SQL, ponieważ te informacje nie są niestety zbierane przez Configuration Managera. A przynajmniej, nie jest to rozwiązanie dostępne z pudełka.
Tutaj pojawia się fantastyczny przykład wykorzystania możliwości Hardware Inventory, będącego natywnym komponentem Configuration Managera. Czym jest Hardware Inventory i co się zmieniło od wersji 2007, dowiecie się z mojego artykułu, który niebawem ukaże się na stronach Technet. Tymczasem poniżej pokazuję Wam przykład, jak rozwiązać powyższy problem od strony technicznej.
- Krok 1: Tworzymy plik z rozrzerzeniem MOF i zapisujemy w nim poniższy kod:
//=================SQL 2012 Information
[dynamic, provider(“MS_VIEW_INSTANCE_PROVIDER”),
SMS_Report(TRUE),
SMS_Group_Name(“SQL12 Property”),
SMS_Class_ID(“CUSTOM|SQL12_Property|1.0”)]
class cm_sql12 : SMS_Class_Template
{
[SMS_Report(TRUE) ] boolean IsReadOnly;
[SMS_Report(TRUE),key] uint32 PropertyIndex;
[SMS_Report(TRUE),key] string PropertyName;
[SMS_Report(TRUE) ] uint32 PropertyNumValue;
[SMS_Report(TRUE) ] string PropertyStrValue;
[SMS_Report(TRUE) ] uint32 PropertyValueType;
[SMS_Report(TRUE),key] string ServiceName;
[SMS_Report(TRUE),key] uint32 SqlServiceType;
};
//==================SQL Information 2008 (possibly) and 2008 R2
[dynamic, provider(“MS_VIEW_INSTANCE_PROVIDER”),
SMS_Report(TRUE),
SMS_Group_Name(“SQL Property”),
SMS_Class_ID(“CUSTOM|SQL_Property|2.0”)]
class cm_sql08 : SMS_Class_Template
{
[SMS_Report(TRUE) ] boolean IsReadOnly;
[SMS_Report(TRUE),key] uint32 PropertyIndex;
[SMS_Report(TRUE),key] string PropertyName;
[SMS_Report(TRUE) ] uint32 PropertyNumValue;
[SMS_Report(TRUE) ] string PropertyStrValue;
[SMS_Report(TRUE) ] uint32 PropertyValueType;
[SMS_Report(TRUE),key] string ServiceName;
[SMS_Report(TRUE),key] uint32 SqlServiceType;
};
//==================SQL Information 2000 (possibly) and 2005
[dynamic, provider(“MS_VIEW_INSTANCE_PROVIDER”),
SMS_Report(TRUE),
SMS_Group_Name(“SQL Property Legacy”),
SMS_Class_ID(“CUSTOM|SQL_Property_Legacy|2.0”)]
class cm_sql2kand05 : SMS_Class_Template
{
[SMS_Report(TRUE) ] boolean IsReadOnly;
[SMS_Report(TRUE),key] uint32 PropertyIndex;
[SMS_Report(TRUE),key] string PropertyName;
[SMS_Report(TRUE) ] uint32 PropertyNumValue;
[SMS_Report(TRUE) ] string PropertyStrValue;
[SMS_Report(TRUE) ] uint32 PropertyValueType;
[SMS_Report(TRUE),key] string ServiceName;
[SMS_Report(TRUE),key] uint32 SqlServiceType;
};
- Krok 2: Przechodzimy do konsoli Configuration Managera –> Administration –> Client Settings –> Default Client Settings. W Client Settings przechodzimy do sekcji Hardware Inventory oraz wybieramy Set Classes. Importujemy świeżo stworzony plik MOF.
Następnie sprawdzamy czy nasze 3 klasy zostały zaimportowane. Są to odpowiednio:
- SQL12 Property (cm_sql12) – Dla wykrywania danych dotyczących SQL 2012
- SQL 2012SQL Property (cm_sql08) – Dla wykrywania danych dotyczących SQL 2008 i 2008 R2
- SQL Property Legacy (cm_sql2kand05) – Dla wykrywania danych dotyczących SQL 2000 i 2005
Wszystkie 3 klasy powinny być zaznaczone. Zatwierdzamy zmiany.
Krok 3: Kolejną kwestią jest dokonanie odpowiednich zmian w pliku Configuration.mof. Plik znajduje się na serwerze CAS Primary Configuration Managera w lokalizacji: „Microsoft Configuration Manager\inboxes\clifiles.src\hinv\configuration.mof”, na dysku systemowym, o ile lokalizacja Configuration Managera nie została zmieniona podczas instalacji. Przed dokonaniem zmian w tymże pliku bezwzględnie pamiętamy o wykonaniu jego backupu. Na samym końcu pliku PRZED sekcją // Added extensions end dopisujemy poniższy kod:
//———————————————
// SQL 2008 (and possibly higher) Properties
//———————————————
#pragma namespace (“\\\\.\\root\\cimv2”)
[Union, ViewSources{“select IsReadOnly,PropertyIndex,PropertyName,PropertyNumValue,PropertyStrValue,PropertyValueType,ServiceName,SqlServiceType from sqlServiceAdvancedProperty”},ViewSpaces{“\\\\.\\root\\microsoft\\sqlserver\\computermanagement10”}, dynamic,Provider(“MS_VIEW_INSTANCE_PROVIDER”)]
class cm_sql08
{
[PropertySources{“IsReadOnly”} ] boolean IsReadOnly;
[PropertySources{“PropertyIndex”},key ] uint32 PropertyIndex;
[PropertySources{“PropertyName”},key ] string PropertyName;
[PropertySources{“PropertyNumValue”} ] uint32 PropertyNumValue;
[PropertySources{“PropertyStrValue”} ] string PropertyStrValue;
[PropertySources{“PropertyValueType”} ] uint32 PropertyValueType;
[PropertySources{“ServiceName”},key ] string ServiceName;
[PropertySources{“SqlServiceType”},key] uint32 SqlServiceType;
};
//———————————————
// SQL 2000/2005 Properties
//———————————————
#pragma namespace (“\\\\.\\root\\cimv2”)
[Union, ViewSources{“select IsReadOnly,PropertyIndex,PropertyName,PropertyNumValue,PropertyStrValue,PropertyValueType,ServiceName,SqlServiceType from sqlServiceAdvancedProperty”},ViewSpaces{“\\\\.\\root\\microsoft\\sqlserver\\computermanagement”}, dynamic,Provider(“MS_VIEW_INSTANCE_PROVIDER”)]
class cm_sql2kand05
{
[PropertySources{“IsReadOnly”} ] boolean IsReadOnly;
[PropertySources{“PropertyIndex”},key ] uint32 PropertyIndex;
[PropertySources{“PropertyName”},key ] string PropertyName;
[PropertySources{“PropertyNumValue”} ] uint32 PropertyNumValue;
[PropertySources{“PropertyStrValue”} ] string PropertyStrValue;
[PropertySources{“PropertyValueType”} ] uint32 PropertyValueType;
[PropertySources{“ServiceName”},key ] string ServiceName;
[PropertySources{“SqlServiceType”},key] uint32 SqlServiceType;
};
//———————————————
// SQL 2012 Properties
//———————————————
#pragma namespace (“\\\\.\\root\\cimv2”)
[Union, ViewSources{“select IsReadOnly,PropertyIndex,PropertyName,PropertyNumValue,PropertyStrValue,PropertyValueType,ServiceName,SqlServiceType from sqlServiceAdvancedProperty”},ViewSpaces{“\\\\.\\root\\microsoft\\sqlserver\\computermanagement11”}, dynamic,Provider(“MS_VIEW_INSTANCE_PROVIDER”)]
class cm_sql12
{
[PropertySources{“IsReadOnly”} ] boolean IsReadOnly;
[PropertySources{“PropertyIndex”},key ] uint32 PropertyIndex;
[PropertySources{“PropertyName”},key ] string PropertyName;
[PropertySources{“PropertyNumValue”} ] uint32 PropertyNumValue;
[PropertySources{“PropertyStrValue”} ] string PropertyStrValue;
[PropertySources{“PropertyValueType”} ] uint32 PropertyValueType;
[PropertySources{“ServiceName”},key ] string ServiceName;
[PropertySources{“SqlServiceType”},key] uint32 SqlServiceType;
};
//========================
// Added extensions end
//========================
Zapisujemy zmiany wprowadzone w pliku.
Krok 4: Sprawdzamy za pomocą narzędzia mofcomp, czy w nowo wyedytowanym pliku Configuration.mof nie wystąpiły błedy. Plik powinien zostać sparsowany poprawnie.
Krok 5. Należy wykonać odświeżenie polityk Hardware Inventory na maszynach objętych agentem Configuration Manager. Można użyć do tego Powershell Right Click Tools, o których pisałam w poprzednim poście, lub zmienić czas wykonywania Hardware Inventory na polityce Default Client Settings na bardziej odpowiadający naszym wymaganiom.
Krok 6. Po wykonaniu powyższych czynności na bazie SQL Configuration Managera wykonujemy np. takowe zapytanie:
SELECT sys1.Netbios_Name0, MAX(CASE sql3.PropertyName0 WHEN ‘SKUName’ THEN sql3.PropertySTRValue0 END) AS [SQL12 Type],
MAX(CASE sql3.PropertyName0 WHEN ‘SPLEVEL’ THEN sql3.PropertyNUMValue0 END) AS [SQL12 Service Pack],
MAX(CASE sql3.PropertyName0 WHEN ‘VERSION’ THEN sql3.PropertySTRValue0 END) AS [SQL12 Version],
MAX(CASE sql3.PropertyName0 WHEN ‘FILEVERSION’ THEN sql3.PropertySTRValue0 END) AS [SQL12 CU Version],
MAX(CASE sql.PropertyName0 WHEN ‘SKUName’ THEN sql.PropertySTRValue0 END) AS [SQL08 Type],
MAX(CASE sql.PropertyName0 WHEN ‘SPLEVEL’ THEN sql.PropertyNUMValue0 END) AS [SQL08 Service Pack],
MAX(CASE sql.PropertyName0 WHEN ‘VERSION’ THEN sql.PropertySTRValue0 END) AS [SQL08 Version],
MAX(CASE sql.PropertyName0 WHEN ‘FILEVERSION’ THEN sql.PropertySTRValue0 END) AS [SQL08 CU Version],
MAX(CASE sql2.PropertyName0 WHEN ‘SKUName’ THEN sql2.PropertySTRValue0 END) AS [SQL05 Type],
MAX(CASE sql2.PropertyName0 WHEN ‘SPLEVEL’ THEN sql2.PropertyNUMValue0 END) AS [SQL05 Service Pack],
MAX(CASE sql2.PropertyName0 WHEN ‘VERSION’ THEN sql2.PropertySTRValue0 END) AS [SQL05 Version],
MAX(CASE sql2.PropertyName0 WHEN ‘FILEVERSION’ THEN sql2.PropertySTRValue0 END) AS [SQL05 CU Version]
FROM dbo.v_R_System AS sys1 LEFT OUTER JOIN
dbo.v_GS_SQL_Property0 AS sql ON sys1.ResourceID = sql.ResourceID LEFT OUTER JOIN
dbo.v_GS_SQL_Property_Legacy0 AS sql2 ON sys1.ResourceID = sql2.ResourceID LEFT OUTER JOIN
dbo.v_GS_SQL12_Property0 AS sql3 ON sys1.ResourceID = sql3.ResourceID
WHERE (sql.PropertyName0 IN (‘SKUNAME’, ‘SPLevel’, ‘version’, ‘fileversion’)) OR
(sql2.PropertyName0 IN (‘SKUNAME’, ‘SPLevel’, ‘version’, ‘fileversion’)) OR
(sql3.PropertyName0 IN (‘SKUNAME’, ‘SPLevel’, ‘version’, ‘fileversion’))
GROUP BY sys1.Netbios_Name0
Oczywiście to jest jedno z przykładowych zapytań, które zwraca poniższy wynik:
Wyniki tego zapytania można połączyć z innymi tabelami bazy danych Configuration Managera w celu uzyskania bardziej szczegółowych informacji na temat maszyny, oczywiście jeśli zachodzi taka potrzeba.
Pomoce:
1. Plik MOF wyedytowany przez Sherry Kissinger