PRTG – Office 365 Lizenzen im Blick
Nachdem das letzte PRTG Skript zur Überwachung der Office 365 Dienste anscheinend doch ganz gut angekommen ist, habe ich eine Anfrage erhalten, ob ich nicht auch etwas schreiben könne, mit dem man die Lizenzen in Office 365 überwachen kann (später haben wir dann noch den DirSync Status mit dazugenommen). Na ja, das hat dann nicht so lange gedauert, bis es mich in den Fingern juckte … der Anfang war relativ leicht, allerdings hat PRTG dann für etwas Hirn-Akrobatik gesorgt, dazu aber später mehr …Deshalb das Wichtigste zuerst: Das funktionierende Skript. Nach einigem Testen und Verfeinern (Danke für die Idee und das Feedback, Ollie), habe ich das fertige Skript auf GitHub veröffentlicht:
PRTG-O365Licensing auf Github.
Die Installation
Einfach die aktuelle Version des Skripts aus GitHub (hier) herunterladen und in den Ordner “Custom Sensors\EXEXML” ablegen.
Da sich das Skript per PowerShell an Office 365 anmelden muss, müssen auf der Probe noch folgende Komponenten installiert werden:
- Microsoft Online Services-Sign in assistant (Download)
- Azure Active Directory-Module for Windows PowerShell (Download)
Wichtig: PRTG startet PowerShell Skripte in der 32-Bit Umgebung, damit muss die Execution Policy der PowerShell 32-Bit min. auf RemoteSigned umgestellt werden. Da aber das Azure Active Directory Modul nur als 64-Bit Version verfügbar ist (und das Skript automatisch auf 64-Bit umschaltet), muss in der PowerShell 64-Bit auch die Execution Policy geändert werden.
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
Update: Windows markiert heruntergeladene Dateien aus dem Internet als potentiell unsicher. Dadurch wird die Ausführung in PowerShell erst einmal unterbunden. Das Skript muss also zuvor entweder per PowerShell oder über die Eigenschaften der Datei im Explorer freigeschaltet werden.
Unblock-File -Path Get-PRTGO365Licenses.ps1
Die Konfiguration
Das Skript benötigt für die Abfrage der entsprechenden Informationen ein Login zum Office 365 Tenant (administrativ). Da ich persönlich ungerne Login Daten im Klartext hinterlege, habe ich diese im übergeordneten Gerät (in PRTG) als Linux Benutzername und Passwort hinterlegt. Somit kann das Skript dann mit folgenden Parametern gestartet werden:
-O365User %linuxuser -O365pass %linuxpassword
Da das Skript schon etwas Last auf die Probe bringt, sollte man den Scanning Interval auf 5 Minuten oder höher stellen.
Das Ergebnis
Nachdem der Sensor den ersten Durchlauf absolviert hat, sollte eine Ausgabe ähnlich der nebenstehenden erfolgen. Wahrscheinlich wollt ihr die Schwellwerte für die verschiedenen Kanäle noch an eure Ansprüche anpassen, aber grundsätzlich sollte das schon mal das Wichtigste abdecken. Es werden folgende Informationen angezeigt:
- Time since last DirSync: Zeit in Stunden seit dem letzten DirSync Lauf (falls DirSync eingesetzt wird)
- Time since last PasswordSync: Zeit in Stunden seit der letzten Passwort Synchronisation (falls PasswordSync eingesetzt wird)
- <Tenant>:<Lizenz> – Active Licenses: Gesamtzahl der Lizenzen
- <Tenant>:<Lizenz> – Available Licenses: Anzahl der verfügbaren Lizenzen
- <Tenant>:<Lizenz> – Consumed Licenses: Anzahl der benutzten Lizenzen
- <Tenant>:<Lizenz> – Warning Licenses: Anzahl der Lizenzen mit Warnungen oder Hinweisen
Je nach dem, welche Berechtigungen der für die Anmeldung an Office 365 benutzte Account hat, kann es sein, dass mehrere Office 365 Tenants abgefragt werden können. Außerdem ist es möglich, dass mehrere Lizenzverträge innerhalb eines Tenants auftauchen. In jedem dieser Fälle erscheinen dann zusätzliche Lizenzaufstellungen für all diese Kombinationen. Werden zu viele Lizenzen angezeigt, kann man dies noch einschränken.
Das Feintuning
Wenn die Abfrage also Lizenzverträge anzeigt, die mich nicht interessieren, oder aber für jeden Vertrag ein eigener Sensor erstellt werden soll, kann das Skript mit weiteren Parametern aufgerufen werden:
- -IncludeSku “Sku1″,”Sku2”,…: Nur die aufgezählten SKUs (also <Tenant>:<Lizenz>) werden ausgelesen
- -ExcludeSku “Sku1″,”Sku2”,…: Die angegebenen SKUs werden nicht ausgelesen
- -ShowMySkus: Ist nur für die manuelle Ausführung in PowerShell gedacht: Gibt eine Liste der SKUs in lesbarer Form aus. Diese kann für das Füllen der Parameter IncludeSku und ExcludeSku verwendet werden
Beispiele:
# Liest nur die Lizenzen zu den Subscriptions ENTERPRISEPACK und POWER_BI_STANDARD des Tenants "Tenant1" aus Get-PRTGO365Licenses.ps1 -O365User user@tenant.onmicrosoft.com -O365Pass MySecretPass -IncludeSku "Tenant1:ENTERPRISEPACK","Tenant1:POWER_BI_STANDARD" # Schließt die Subscription POWER_BI_STANDARD aus dem Monitoring aus Get-PRTGO365Licenses.ps1 -O365User user@tenant.onmicrosoft.com -O365Pass MySecretPass -ExcludeSku "Tenant1:POWER_BI_STANDARD" # Zeigt alle verfügbaren Lizenzen in lesbarer Form an Get-PRTGO365Licenses.ps1 -O365User user@tenant.onmicrosoft.com -O365Pass MySecretPass -ShowMySkus AccountSkuId ActiveUnits WarningUnits ConsumedUnits ------------ ----------- ------------ ------------- xxxxxxxxxx:ENTERPRISEPACK 77 0 74
Die Herausforderung
Das Schwierigste am Skript war nicht die Abfrage der relevanten Daten aus Office 365 oder die Aufbereitung für PRTG, sondern ein “hausgemachtes” Problem von PRTG: Der Probe Prozess von PRTG läuft immer unter 32-Bit, auch wenn das Betriebssystem in 64 Bit installiert ist und der ausführende Computer mehr als 6 GB RAM hat (was beim Core dafür sorgt, dass die 64-Bit Version installiert wird). Nun gibt es das Azure Active Directory Modul für PowerShell nicht als 32-Bit Version und somit kann die 32-Bit PowerShell, die vom PRTG Probe Prozess gestartet wird, auch nicht auf das Modul zugreifen.
Somit muss das Skript zum einen erkennen, dass es in einer 32-Bit Umgebung läuft (einfach) und dann dafür sorgen, dass es sich selbst in einer 64-Bit Umgebung neu aufruft (WTF?). Ok, klingt einfach (die Lösung ist dann auch einfach), aber wie? Ich habe wirklich einige Zeit mit meinem Freund Google verbracht, um den richtigen Weg zu finden. Da ich euch das ersparen möchte, hier die Essenz:
Wird auf einem 64-Bit System ein Prozess gestartet, werden verschiedene Systemordner von Windows automatisch umgeleitet (je nach “Bit-igkeite” des Prozesses), um es den Programmen (oder besser den Programmierern) “einfach” zu machen, weiterhin zu funktionieren, auch ohne genau zu wissen, auf welche DLLs sie zugreifen müssen (32- oder 64-Bit).
Systemordner | 32-Bit Prozess | 64-Bit Prozess |
---|---|---|
%windir%\System32 | 32-Bit Versionen der Dateien | 64-Bit Versionen der Dateien |
%windir%\SysWOW64 | nicht verfügbar | 32-Bit Versionen der Dateien |
%windir%\Sysnative | 64-Bit Versionen der Dateien | nicht verfügbar |
Die Verzeichnisse System32 und SysWOW64 kennt man ja aus der Explorer Ansicht, das Verzeichnis Sysnative ist mir so aber noch nie begegnet. Selbst in einer 32-Bit PowerShell ist es nicht sichtbar, allerdings kann darauf zugegriffen werden. Da es nur für einen 32-Bit Prozess verfügbar ist und darin dann die 64-Bit Systemdateien enthalten sind, kann darüber auch eine 64-Bit PowerShell gestartet werden. Und genau darin liegt der Trick, aus einer 32-Bit PowerShell eine 64-Bit PowerShell zu machen. Der Code dazu sieht dann (in meinem Fall) so aus:
#Check for 32 bit environment if ($env:PROCESSOR_ARCHITECTURE -eq "x86") { # Launch script in 64 bit environment $ScriptParameter = "-O365User '$O365User' -O365Pass '$O365Pass' " if ($IncludeSku -ne $null) { $ScriptParameter += "-IncludeSku '$($IncludeSku -join "','")' " } if ($ExcludeSku -ne $null) { $ScriptParameter += "-ExcludeSku '$($ExcludeSku -join "','")' " } if ($ShowMySkus) { $ScriptParameter += "-ShowMySkus " } # Use Sysnative virtual directory on 64-bit machines Invoke-Expression "$env:windir\sysnative\WindowsPowerShell\v1.0\powershell.exe -file '$($MyInvocation.MyCommand.Definition)' $ScriptParameter" Exit }
Hier ist Zeile 15 eigentlich die interessanteste. Das Skript ruft also eine 64-Bit PowerShell auf und übergibt dorthin den vollständigen Pfad zur eigenen Datei und die (davor mühsam zusammengeklöppelten) Parameter (die zu allem Überfluss auch noch Arrays enthalten können).
Das Fazit
Funktioniert … und wieder was gelernt. Wenn ihr Fehler findet oder Verbesserungsvorschläge habt, benutzt bitte gerne den Issue Tracker des Github Projekts. Ich freue mich natürlich auch immer über Feedback, wenn ihr das Tools einsetzt. Gerne könnt ihr es auch wie Ollie machen und mich mit guten Ideen für neue PRTG Sensoren bombardieren – wenn ich Zeit habe und das Thema eine Lücke auch für andere füllen kann, setze ich mich (sehr wahrscheinlich) dran. Also: Benutzt die Kommentarfunktion!
Hallo Marc
Vielen Dank fürs Teilen der Skripte und die tolle Anleitung. Ergänzend möchte ich noch erwähnen, dass ich auf Windows Server 2012R2 zusätzlich zu Deiner Anleitung noch folgende Powershell Kommandos absetzen musste, um die Berechtigungsfehler beim Ausführen der Skripte zu umgehen:
Unblock-File -Path “C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML\Get-Office365Status.ps1”
Unblock-File -Path “C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML\Get-PRTGO365Licenses.ps1”
Hallo Frank,
danke für die Ergänzung. Da das Skript von einer Website heruntergeladen wurde, markiert sie Windows erst einmal als “gefährlich”. Mit den beschriebenen Befehlen kannst du diese Blockierung aufheben (oder durch Rechtsklick und Anklicken der entsprechenden Option in den Eigenschaften). Ich habe eine entsprechende Anmerkung in den Artikel übernommen.
Gruß,
Marc
Hallo Marc,
auch von mir erst einmal vielen Dank für das Skript. Leider bekomme ich immer XML Fehler, dass es nicht im richtigen Format wäre (XML: The returned XML does not match the expected schema. (code: PE233) — JSON: The returned JSON does not match the expected structure (Invalid JSON.). (code: PE231)). Habe es nun zweimal neu von GitHub geholt und komplett durchgezogen, aber die Meldung bleibt.
Hast du da eine Tipp, wo ich Fehlersuche betreiben kann? Der User ist ein global admin und es kommt auch kein Tendent Fehler.
Besten Dank
Alexander
Hallo Alexander,
der Fehler bedeutet erst mal nur, dass das Skript wahrscheinlich einen PowerShell Fehler erzeugt hat und damit die Ausgabe im XML Format nicht mehr korrekt war. Am einfachsten findest du heraus, was das Problem ist, indem du in den Sensoreinstellung “Write EXE result to disk” aktivierst. Damit wird die Ausgabe des Sensors in die entsprechende Datei auf der Probe gespeichert (“Result of Sensor [ID].txt” im Verzeichnis “Logs (Sensors)”). Wenn du dir diese Datei mal anschaust, wirst du wahrscheinlich einen PowerShell Fehler sehen, anhand dessen du erkennen kannst, was das Problem ist.
Üblicherweise passiert das etwa, wenn die heruntergeladene Datei noch blockiert ist (siehe “Unblock-File” oben) oder die Execution Policy noch nicht korrekt ist (auch im Artikel beschrieben). Wenn du nicht weiter kommst, Poste einfach mal die Fehlermeldung, vielleicht bekommen wir es gemeinsam hin.
Gruß,
Marc
Hallo Marc,
erst mal Entschuldigung, dass ich erst jetzt wieder schreibe, aber hatte “lange frei” und konnte daher nicht weitermachen.
Jetzt habe ich wieder einen Versuch gestartet und alles von vorne neu gemacht (neuer Sensor, User und sowas) und bekomme im PRTG wieder den Fehler “XML: The returned XML does not match the expected schema. (code: PE233) — JSON: The returned JSON does not match the expected structure (Invalid JSON.). (code: PE231)”.
Habe auch mal die Fehlerdatei schreiben lassen:
…\EXEXML\Get-PRTGO365Licenses.ps1 : A parameter cannot be found that
matches parameter name ‘O365User’.At line:1 char:231
+ … tor\custom sensors\EXEXML\Get-PRTGO365Licenses.ps1′ -O365User s-group …
+ ~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-PRTGO365Licenses.ps1],
ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Get-PRTGO365Licenses.ps1
Kann das evtl. an einer zu alten Server- oder PowerShellversion liegen? Lokal auf dem Core läuft es einwandfrei. Nur bei der Ausführung durch PRTG kommt der Fehler. Installiert ist das WMF 1-5, Unblock ist gemacht und ExecutionPolicy ebenso.
Besten Dank
Alexander
Hallo Alexander,
die Meldung kann ich mir so jetzt nicht erklären. Allerdings habe ich gesehen, dass du den Benutzernamen direkt in den Skriptaufruf geschrieben hast (ist das so?). Da der Benutzername anscheinend einen Bindestrich enthält, könnte es sein, dass PowerShell versucht, das als Operator “Minus” zu interpretieren. Kannst du bitte mal versuchen, Benutzername und Passwort entweder in Anführungszeichen zu setzen, oder wie im Artikel beschrieben, etwa in die Linux Anmeldedaten des Sensors zu speichern?
Wenn es das nicht ist, wird es schwierig zu troubleshooten, ohne direkt draufschauen zu können … 😉
Gruß,
Marc
Hallo zusammen,
Ein tolles Skript.
Ich habe leider nur das Problem das ich bei IncludeSKU und mehre auswähle via “Tenant:SKU1″,”Tenant:SKU2” immer den Error SKUs not found im PRTG bekomme.
In der Commandline gibt er es korrekt aus.
Woran kann das noch liegen?
Hallo René,
ich könnte mir vorstellen, dass es ein Problem mit den Anführungszeichen ist. In meinen anderen PRTG Skripten hat es bisher immer funktioniert, mit einfachen Anführungszeichen (‘) zu arbeiten. Vielleicht testest du das mal?
Ich habe schon alle erdenklichen Variationen probiert: ‘,”,`,´
Echt eigenartig im CLI geht es wunderbar egal mit welchen.
hiermit bekomme ich jetzt nur immer den ersten SKU geliefert. Der zweite taucht nicht auf:
-IncludeSku “xxx:PROJECTESSENTIALS”,”xxx:PROJECTPROFESSIONAL”
Hallo Rene,
ich hatte jetzt gerade etwas Zeit, mir das mal genauer anzuschauen. Es sieht mir danach aus, dass PRTG bei der Weitergabe der Parameter an das Powershell Skript ein Problem mit dem Escaping hat. Ich habe mit der Debug Ausgabe und verschiedenen Arten der Parameterübergabe getestet, da stimmt etwas nicht. Spätestens mit einem Parameter in der Form @(‘Parameter1′,’Parameter2’) müsste ja korrekt ankommen, daraus macht PRTG allerdings einen String.
Ich werde dazu mal einen Fall bei Paessler eröffnen, von meinem Skript aus habe ich jetzt 2 Stunden lang unterschiedliche Aufrufe getestet, alle aus der Kommandozeile erfolgreich, über PRTG nicht. Ich hoffe, da gibt es eine Lösung für …
Hallo Marc,
vielen Dank für die Idee und die tolle Umsetzung.
Leider will mein PRTG noch nicht so recht. Ich bekomme die Meldung “Error connecting to your tenant. Please check credentials”, obwohl die Zugangsdaten definitiv korrekt sind. Manuell auf der Probe in der PS x64 ausgeführt bekomme ich korrekte Daten zurück, in der PS x86 dieselbe Fehlermeldung wie in PRTG.
Funktioniert da in PRTG der Aufruf der x64 PS nicht?
Hallo Rudi,
PRTG startet leider externe Programme (also auch PowerShell) immer im 32-Bit Kontext, deshalb der “Aufwand”, von dort aus in die 64-Bit Umgebung zu kommen. Bisher hat das immer funktioniert, ich glaube allerdings, dass das Skript nicht aus der PowerShell x86 heraus funktioniert – müsste ich mal prüfen. Das angesprochene Sysnative-Verzeichnis, das für den korrekten Aufruf der x64 Umgebung notwendig ist, steht dort (meiner Meinung nach) nicht zur Verfügung.
Hey Marc,
ein Kunde hat mich gerade auf deinen Blog aufmerksam gemacht und nutzt dein Script. Sehr ausführliche Erklärung, werde ich in Zukunft gerne hin verlinken wenn es eine Anforderung gibt die unsere nativen Sensoren noch nicht abdecken. 🙂
Viele Grüße,
Felix Saure, Technical Support, Paessler AG