Office 365 Powershell Zugänge unter Windows 10 (1809+)

Nachdem der letze Beitrag zum Thema Office 365 Powershell ja mittlerweile veraltet ist, hier die aktuelle Methode eine Verbindung zu Office 365 herzustellen.

„Heute“ nutzt man direkt die AzureAD Module die man via NuGet installiert und nicht mehr die MSOnline „Extra“ Shell.

Installation des AzureAD Module (Lizenzen, Office 365 Benutzer …)

  1. Öffnen der Powershell „Als Administrator“ und das Modul installieren:
PS C:\> Install-Module -Name AzureAD

Nicht vertrauenswürdiges Repository
Sie installieren die Module aus einem nicht vertrauenswürdigen Repository. Wenn Sie diesem Repository vertrauen, ändern
 Sie dessen InstallationPolicy-Wert, indem Sie das Set-PSRepository-Cmdlet ausführen. Möchten Sie die Module von
'PSGallery' wirklich installieren?
[J] Ja  [A] Ja, alle  [N] Nein  [K] Nein, keine  [H] Anhalten  [?] Hilfe (Standard ist "N"): j
PS C:\>

2. Herstellen der Verbindung zum AzureAD (Office 365)

PS C:\> Connect-AzureAD

Account                     Environment TenantId                             TenantDomain    AccountType
-------                     ----------- --------                             ------------    -----------
<UPN>> AzureCloud  affeaffe-b00b54e0e-a0c4-a5b16ca62f5b <DOMAIN> User
PS C:\>

… fertig. Und schon kann man mit den AzureAD-CMDlets wie Get-AzureADUser, Get-AzureADUserLicenseDetail und allen anderen loslegen.

Eine Liste aller CMDlets gibt es mit:

Get-Command -Module AzureAD

Windows Server 2016 RDS Effekte (Black Screen, Startmenü defekt)

Problem:

Ein Terminal Server (RDS Session Host) zeigt nach einiger Zeit verschiedene Effekte, wie z.B. „schwarzer Bildschirm nach der Anmeldung“, „nicht mehr funktionierendes Startmenü“, allgemeine Performance- oder Anmeldeprobleme.

Dies betrifft übrigens nicht zwingend nur RDS Sitzungshosts, sondern prinzipiell alle Windows Server 2016 Maschinen, auf Terminalservern (insbesondere mit User Profile Disks, UPDs) ist das ganze aufgrund potenziell größerer Anmelde-/Benutzeranzahl nur deutlich wahrscheinlicher anzutreffen.

Lösung:

Schuld könnte ein Bug sein, welcher dafür sorgt, dass bei der Benutzeranmeldung erstellte Firewall-Regeln beim löschen des Benutzerprofils (hier kommen die UPDs ins Spiel, unter Einsatz dieser wird das Profil nämlich bei der Abmeldung automatisch wieder vom Server gelöscht) nicht mehr entfernt werden.

Das verursacht einen Registry-Bloat in 

HKLM:\System\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\FirewallRules

und/oder

HKLM:\System\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\RestrictedServices\Configurable\System

Sind diese Keys bereits „überfüllt“, kann es gut sein, dass sich die Werte nicht mehr per GUI (regedit) entfernen lassen – der Key lädt schlichtweg Ewigkeiten und stürzt bei zu wenig RAM ab.

Abhilfe schafft unser kleines PowerShell-Script, das die Einträge nacheinander löscht:

$profiles = get-wmiobject -class win32_userprofile
Clear-Host
Write-Host "`n`n`n`n`n`n`n`n"
Write-Host "Getting Firewall Rules..."
$Rules1 = Get-NetFirewallRule -All | 
  Where-Object {$profiles.sid -notcontains $_.owner -and $_.owner }
$Rules1Count = $Rules1.count
Write-Host "" $Rules1Count "Rules`n"

Write-Host "Getting Firewall Rules from ConfigurableServiceStore..."
$Rules2 = Get-NetFirewallRule -All -PolicyStore ConfigurableServiceStore | 
  Where-Object { $profiles.sid -notcontains $_.owner -and $_.owner }
$Rules2Count = $Rules2.count
Write-Host "" $Rules2Count "Rules`n"

$Total = $Rules1.count + $Rules2.count
Write-Host "Deleting" $Total "Firewall Rules:" -ForegroundColor Green

$Result = Measure-Command {

  $start = (Get-Date)
  $i = 0.0

  foreach($rule1 in $Rules1){

    # action
    Remove-ItemProperty -Path "HKLM:\System\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\FirewallRules" -Name $rule1.name

    # progress
    $i = $i + 1.0
    $prct = $i / $total * 100.0
    $elapsed = (Get-Date) - $start
    $totaltime = ($elapsed.TotalSeconds) / ($prct / 100.0)
    $remain = $totaltime - $elapsed.TotalSeconds
    $eta = (Get-Date).AddSeconds($remain)

    # display
    $prctnice = [math]::round($prct,2) 
    $elapsednice = $([string]::Format("{0:d2}:{1:d2}:{2:d2}", $elapsed.hours, $elapsed.minutes, $elapsed.seconds))
    $speed = $i/$elapsed.totalminutes
    $speednice = [math]::round($speed,2) 
    Write-Progress -Activity "Deleting Rules ETA $eta elapsed $elapsednice loops/min $speednice" -Status "$prctnice" -PercentComplete $prct -SecondsRemaining $remain
  }

  foreach($rule2 in $Rules2) {

    # action  
    Remove-ItemProperty -Path "HKLM:\System\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\RestrictedServices\Configurable\System" -Name $rule2.name

    # progress
    $i = $i + 1.0
    $prct = $i / $total * 100.0
    $elapsed = (Get-Date) - $start
    $totaltime = ($elapsed.TotalSeconds) / ($prct / 100.0)
    $remain = $totaltime - $elapsed.TotalSeconds
    $eta = (Get-Date).AddSeconds($remain)

    # display
    $prctnice = [math]::round($prct,2) 
    $elapsednice = $([string]::Format("{0:d2}:{1:d2}:{2:d2}", $elapsed.hours, $elapsed.minutes, $elapsed.seconds))
    $speed = $i/$elapsed.totalminutes
    $speednice = [math]::round($speed,2) 
    Write-Progress -Activity "Deleting Rules from ConfugurableServiceStore ETA $eta elapsed $elapsednice loops/min $speednice" -Status "$prctnice" -PercentComplete $prct -secondsremaining $remain
  }
}

$end = Get-Date
Write-Host end $end 
Write-Host eta $eta

Write-Host $result.minutes min $result.seconds sec

Achtung: bei einem bereits länger laufenden Terminalserver, kann das Script schonmal so ein paar Stunden brauchen um alle Regeln zu entfernen. In einem Fall hier knapp 4 Stunden für etwa 95000 Regeln. Je nach CPU kann das natürlich etwas schneller gehen oder länger dauern – es wird wirklich fertig.

Aus jenen Performancegründen löscht das Script die Registry-Werte direkt, anstatt das eigentlich korrekte Remove-NetFirewallRule Cmdlet zu verwenden.

PHP 7.x auf Windows Server 2016 mit IIS installieren, Schritt für Schritt

Installieren wir ein aktuelles PHP (7.2 in diesem Fall) auf einem Windows Server 2016 in 64bit. Da wir den IIS nutzen und ein lokales CGI-Script-Handling einrichten wollen, nutzen wir die NTS (Non Thread Safe) Variante.

Auf die Unterschiede zwischen „Thread Safe“ und „Non Thread Safe“ gehen wir hier nich genauer ein. Schnelle Admins wissen aber:

  • Apache + LoadModule: Thread Safe
  • Apache + FastCGI: Non-Thread Safe
  • IIS: Thread Safe
  • IIS + FastCGI: Non-Thread Safe « Diese Anleitung

Installation von PHP 7.2 auf Windows Serrver 2016 (IIS)

  1. Windows Server 2016 installieren+updaten, IIS und Tools hinzufügen. Die Installation dauert einen Moment. Der Telnet-Client ist nicht zwingend notwendig, aber oft hilfreich.
    PS C:\> Import-Module servermanager
    PS C:\> Install-WindowsFeature -name web-server,web-cgi –IncludeManagementTools
    PS C:\> Add-WindowsFeature telnet-client
  2. PHP herunterladen und installieren: https://windows.php.net/download/. „VC15 x86 Non Thread Safe„. Wir empfehlen 32bit (x86) wegen der Extensions, es gibt noch nicht alle Extensions in x64. Jetzt grade aktuell war PHP in der Version 7.2.9.
  3. Visual C++ 2015 Runtime Update 3 RC (oder auch gleich 2017) in x86 und x64 herunterladen und installieren: https://www.microsoft.com/DE-de/download/details.aspx?id=52685
  4. Die PHP ZIP-Datei  (als Administrator) entpacken (z.B. „%Program Files (x86)%\PHP“)
  5. Kopieren der „php.ini-development“ in die „php.ini“
    PS C:\Program Files (x86)\php-nts-x86> Copy-Item .\php.ini-development .\php.ini2
  6. PHP.ini bearbeiten
    error_log = C:\inetpub\logs\php_errors.log
    max_execution_time = 60
    max_input_time = 90
    memory_limit = 256M
    default_charset = "UTF-8"
    upload_tmp_dir = "C:\inetpub\temp\IIS Temporary Compressed Files"
    upload_max_filesize = 16M
    allow_url_fopen = Off
    extension=php_wincache.dll
  7. Den PHP Interpreter zur System-PATH-Variable hinzufügen
    1. Systemeigenschaften > Erweitert > Umgebungsvariablen > Systemvariablen > Eintrag „Path“ bearbeiten > Neu > <PFAD ZU PHP> einfügen > OK > OK
  8. „Wincache Extensian for PHP“ 2.0 in 64bit herunterladen: https://www.iis.net/downloads/microsoft/wincache-extension (Aktuell: „wincache-2.0.0.8-dev-7.1-nts-vc14-x64.exe“) und in den PHP-Pfad nach \ext entpacken
  9. PHP im IIS konfigurieren
    1. „Internetinformationsdienste (IIS)-Manager“ starten > Links den Server auswählen (oberste Ebene) > „Handlerzuordnungen“ öffnen
    2. „Modulzuordnung hinzufügen“:
      1. Anforderungspfad: *.php
      2. Modul: FastCgiModule
      3. Ausführbare Datei: „C:\Program Files (x86)\<PHP-PFAD>\php-cgi.exe“
      4. Name: PHP
      5. Einschränkungen: „Handler nur bei folgender Zuordnung aufrufen“ auf „Datei oder Ordner“ stellen
  10. Standartdokument(e) hinzufügen
    1. Internetinformationsdienste (IIS)-Manager“ starten > Links den Server auswählen (oberste Ebene) >Standdartdokument
    2. Hinzufügen > „index.php“
  11. Schon fertig 🙂

Installation von Microsoft SQL Server Treibern für PHP unter Windows/IIS

  1. Download der „Microsoft PHP drivers Version for SQL Server“ (aktuell 7.2): https://github.com/Microsoft/msphpsql/releases
  2. Entpacken von x86\php_pdo*.dll nach <PHPPFAD>\ext
  3. Bearbeiten der PHP.ini
    extension=php_sqlsrv_72_nts.dll
  4. Download und Installation der „Microsoft® ODBC Driver 17 for SQL Server“ in 64bit: https://www.microsoft.com/de-DE/download/details.aspx?id=56567
  5. Webserver neustart mit „IISRESET“
  6. Fertig 🙂

Ob das jeweilige Modul geladen werden konnte, lässt sich an der Kommandozeile mit „php -m“ recht gut überprüfen.

Testen der Installation von PHP auf IIS

In C:\inetpub\wwwroot eine Datei „phpinfo.php“ anlegen mit dem folgenden Inhalt:

<?php
    phpinfo();
?>

Und aufrufen von http://<SERVERNAME>/phpinfo.php

 

In Office 365 mit der Powershell die Benutzerkontaktdaten wie Straße oder Ort Massenhaft ändern

Problem

Es sollen viele oder gleich alle Office 365 Benutzer geändert werden. Wegen eines Umzuges stimmt der Ort nicht mehr, wegen eines verdammten CSV-Ascii-Importfehlers stimmen Sonderzeichen nicht oder es hat sich eine andere Eigenschaft bei vielen Benutzern geändert die nun auf einmal angepasst werden soll.

Lösung

Zuerst muss man sich mit der „Windows Azure Active Directory-Modul für Windows PowerShell“ Powershell mit dem Office365 verbinden:

PS C:\> Connect-MsolService
    • Eigenschaften aller Benutzer sofort ändern (hier: ‚Straße‘)
      PS C:\> Get-MsolUser | Set-MsolUser -Street "Neue Strasse 815"
    • Eigenschaften der Benutzer ändern, die noch die alte Straße eingetragen haben
      PS C:\> Get-MsolUser | ? { $_.StreetAddress -eq 'Alte Adresse' } | Set-MsolUser -StreetAddress 'Neue Adresse'
    • Eigenschaften von nur zwei Benutzern ändern
      PS C:\> Get-MsolUser | ? { $_.UserPrincipalName -eq '[email protected]' -Or $_.UserPrincipalName -eq '[email protected]' } | Set-MsolUser

Active Directory: Wann hat sich ein Benutzer zuletzt angemeldet?

Problem

Wann hat sich ein Benutzer oder Konto zuletzt am Active Directory angemeldet? Die Objekt-Registerkarte im ActiveDirectory Benutzer- und Computer SnapIn ist da nicht sondelrich auskunftsfreudig.

Die Objekteigenschaft „LastLogon“ lässt sich zwar an der PowerShell mittels „Get-ADUser“ auslesen, aber nur als Large-Integer, der die Anzahl von 100-Nanosekunden-Intervallen seit dem 1.1.601 in UTC anzeigt. Leicht esoterischer Wert …

Lösung

Dieses PowerShell-Script wirft den Wert lesbar raus und funktioniert auch außerhalb von Domänencontrollern (RSAT-Tools vorausgesetzt):

Import-Module activedirectory
([DateTime][long](
     $(ForEach ($dc in ((Get-ADDomaincontroller -filter *).name)) 
        {
           (Get-ADUser -Identity "administrator" -Properties "LastLogon" -server $dc).LastLogon
        }
 ) | Measure -Maximum).Maximum).AddYears(1600)

Oder für besonders faule Admins auch als Einzeiler:

([DateTime][long]($(ForEach($dc in ((Get-ADDomaincontroller -filter *).name)){(Get-ADUser -Identity "mroelz" -Properties "LastLogon" -server $dc).LastLogon})|Measure -Maximum).Maximum).AddYears(1600)

Es geht natürlich auch via CMD, also an der klassischen Kommandozeile:

wmic NetLogin where (name like "%<USERNAME>%") get name, lastlogon