2

Pobieranie obiektów WMI osadzonych wewnątrz innych jako zmienne klas (C#)


Witajcie,

Dzisiaj szybkie spostrzenie dotyczące właściwości pobierania obiektów WMI będących zawartych wewnątrz obiektów zadanej klasy.
Gdy w podstawowej formie pobieramy kolekcję obiektów WMI spełniających zadane warunki, dostajemy w wyniku obiekt klasy System.Management.ManagementObjectCollection. Każdy z System.Management.ManagementObject posiada wypełnione jedynie podstawowe pola, będące zdefiniowane jako typy proste (int, string, bool). Jednak gdy chcemy odnieść się do pola, które jest typu innej klasy WMI, to dostajemy w rezultacie wartość NULL.
Aby wypełnić takie pole(a) należy wywołać na potrzebnym nam obiekcie metodę Get()
Metoda ta w dokumentacji przedstawiona jest jako popinająca reprezentację .NET klasy System.Management.ManagementBaseObject do rzeczywistej instancji WMI. Może być użyta jako skrótowa forma odwołania się do obiektu klasy poprzez wypełnienie atrybutu Path obiektu po czym wywołanie funkcji Get(). Jednak w naszym przypadku chodzi o ponowne podłączenie, które jako efekt uboczny (skutek wcześniejszej optymalizacji) daje pełną reprezetancję klasy wraz z osadzonymi wewnątrz obiektami (albo tablicami!).
Prezentuje to poniższy przykład:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// for kerberos constrained delegation
ConnectionOptions wmiOptions = new ConnectionOptions();
wmiOptions.Authentication = AuthenticationLevel.PacketPrivacy;
wmiOptions.Impersonation = ImpersonationLevel.Impersonate;
// for connection to remote host
ManagementScope scope = new ManagementScope("\\\\" + serverName + "\\root\\SMS\\Site_" + siteCode, wmiOptions);
// for performing query to wmi
WqlObjectQuery wmiQuery = new WqlObjectQuery(query);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, wmiQuery);
// performing search itself
ManagementObjectCollection Colls = searcher.Get();
ManagementBaseObject[] rules = null;
ManagementObject coll = null;
//retrieving objects from collection, in this example we assume only one result
foreach (ManagementObject o in Colls)
{
      coll = o;
      // coll["CollectionRules"] == null
      coll.Get();
      // after previous call it is filled in
      rules = (ManagementBaseObject[])o["CollectionRules"];
}
Continue...
1

Jak zdalnie wywołać opublikowane zadanie na kliencie SCCM 2012


Witajcie,

W sieci można znaleźć wiele pytań o zdalne wywoływanie akcji klienta SCCM poprzez skrypty.
W oficjalnym SDK do SCCM 2012 nie ma absolutnie nic na temat takiej możliwości. W wersji do SCCM 2007 jest przykład skryptu wykonywanego lokalnie, wywołującego obiekty klas COM CPAppletMgr oraz UIResourceMgr. Niestety, konfiguracja DCOM, która umożliwiałaby zdalne użycie tych obiektów jest conajmniej uciążliwa, a przy okazji bezużyteczna na dłuższą metę, gdyż niedorzecznym była by wiara, że klasy te (a w szczególności GUID’y) nie zmienią się w przyszłości.
Bardziej naturalną formą integracji z SCCM’em jest wykonywanie wywołań WMI.
I tutaj znowu Microsoft podłożył wszystkim autorom skryptów kukułcze jajo – w SDK do wersji 2012 jest jedynie suchy opis klas WMI dostępnych w kliencie i serwerze, jednakże brakuje jakichkolwiek wskazówek dotyczących ich używania.
Po dłuższych poszukiwaniach odnalazłem fantastyczne narzędzie: SMSCLICTR, które zbiera wszystkie możliwe ustawienia i wywołania w proste do użycia biblioteki .NET. Mogą być one wykorzystane także w powershell’u, jednakże często użycie zewnętrznych typów danych w PS jest zabronione przez polityki bezpieczeństwa systemu.
Ostatecznie nie pozostało nic innego, jak utworzenie własnego kawałka kodu, który będzie wykonywał wymagane akcje.

  1. Po pierwsze, konieczne jest wywołanie odświeżenia polityk klienta SCCM. Może to zostać wykonane przez poniższą funkcję:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function ForceMachinePolicyRefresh
    {
    param($clientname, $username, $userpass, $PolicyID)
    $ms = new-object system.management.managementscope
    $ms.Path = "\\$clientname\root\CCM"
    $ms.options.username = $username
    $ms.options.password = $userpass
    $mc = new-object system.management.managementclass($ms, 'SMS_Client', $null)
    $mc.invokeMethod("TriggerSchedule", $PolicyID)
    return $mc
    }

    Parę uwag do powyższego kodu:

    • Połączenie do przestrzeni nazw WMI następuje w formie bezpośredniej, bez użycia rzutowania, dostępnego w powershell na klasę [wmi], z powodu konieczności przekazania poświadczeń. Kod ten może wywoływać WMI zdalnie, dzięki zastosowaniu $ms.options.username które wspierane jest tylko w przypadku zdalnych połączeń.
    • Jest to ogólna funkcja umożliwiająca wymuszenie wywołania dowolnej harmonogramowej akcji. Tak więc może zostać użyta nie tylko do odświeżania polityki komputera, ale także do zastosowania ustawień konfiguracji lokalnej, co zostanie pokazane za moment.
    • Ostatnią rzeczą, dotyczącą tego małego kawałka kodu jest fakt, iż może zostać użyty jako szablon do manipulacji dowolnymi właściwościami i metodami opublikowanymi przez klasę WMI SMS_Client. Dopiero teraz SDK może się okazać pomocne.
  2. Tak więc mając zdefiniowaną tę procedurę pomocniczą możemy przejść do sedna sprawy. Poniższa funkcja służy do uruchomienia wymuszonej publikacji podanego w argumencie pakietu lub aplikacji. Funkcja pobiera wszystkie wymagane argumenty, tak że jest zupełnie niezależna od kontekstu, w którym jest wykonywana:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    function InvokeOptionalAdvertisement
    {
    Param($clientname, $username, $userpass, $advertID, $packID)
    $mc = ForceMachinePolicyRefresh -clientname $clientname -username $username -userpass $userpass -PolicyID "{00000000-0000-0000-0000-000000000021}"
    $ms = new-object system.management.managementscope
    $ms.path = "\\$clientname\root\ccm\policy\Machine\ActualConfig"
    $ms.options.username = $username
    $ms.options.userpass = $userpass
    $query =new-object System.Management.ObjectQuery
    $query.QueryString = "Select * From CCM_SoftwareDistribution where ADV_AdvertisementID = '$advertID' and PKG_PackageID = '$packID'"
    $searcher = new-object system.management.managementobjectsearcher($query)
    $searcher.Scope = $ms
    $advs = $searcher.Get
    $enum = $advs.GetEnumerator()
    $enum.MoveNext()
    $adv = $enum.Current
    $adv.SetPropertyValue("ADV_RepeatRunBehavior", "RerunAlways")
    $adv.SetPropertyValue("ADV_MandatoryAssignments", "True")
    $adv.Put()
    $query1 = new-object System.Management.ObjectQuery
    $query1.QueryString = "Select ScheduledMessageID FROM CCM_Scheduler_ScheduledMessageID like '" + $adv.ADV_AdvertisementID + "-" + $adv.PKG_PackageID + "%'"
    $searcher1 = new-object System.management.managementobjectsearcher($query1)
    $searcher1.scope = $ms
    $scheds = $searcher1.Get()
    $scheds | Foreach-Object { $mc[1].invokeMethod("TriggerSchedule", $_.ScheduledMessageID) }
    return $adv
    }

    No i oczywiście słowo komentarza do powyższego kodu:

    • Na początku wywołujemy odświeżenie polityki komputera, aby mieć pewność, że pracujemy na aktualnych danych publikacji pobranych z SCCMa. Tę akcję wykonujemy przy użyciu naszej pomocniczej procedury.
    • Następnie definiujemy nowy kontekst zarządzania WMI obejmujący aktualnie załadowaną konfigurację klienta SCCM.
    • Kolejno, tworzymy instację klasy ObjectQuery, która reprezentować będzie zapytanie WMI. To zapytanie pobiera z przestrzeni wszystkie obiekty klasy CCM_SoftwareDistributionktóre pasują do przekazanych wymagań.
    • Poprzez wywołanie metody $searcher.Get umieszczamy w zmiennej $advs wszystkie obiekty będące wynikiem zapytania.
    • Dzięki użyciu sztuczki z pobraniem enumeratora i jego właściwości $enum.Current otrzymujemy tylko pojedynczy obiekt, zamiast kolekcji zawierającej ten obiekt.
    • Teraz pora na wykonanie głównego zadania. Modyfikowane są dwie właściwości obranego obiektu, co powoduje, że zamiast opcjonalnej publikacji, na tym konkretnym kliencie staje się ona obowiązkowa i zostanie wykonana przy następnym wywołaniu harmonogramu tej publikacji.
    • Ostatnia część skryptu listuje wszystkie zarejestrowane w systemie obiekty harmonogramu i wymusza ich uruchomienie. To powoduje także wywołanie akcji harmonogramu dla naszej publikacji, którą chcemy wymusić.
Continue...