Montag, 4. Juni 2018

"Bitte deaktivieren Sie Ihren Adblocker!"

Ich wurde gerade informiert das beim Aufruf von einigen Webseiten aus der Schule ein "Bitte deaktivieren Sie Ihren Adblocker!" Fenster eingeblendet wird.

Die paedML Linux verwendet jedoch keine lokalen Adblocker weshalb das "deaktivieren" etwas schwer fällt. Im Whiltelisting der paedML "rumzumurksen" um "hoffentlich" diese Seiten sehen zu können ist ebenfalls nicht all zu leicht. Aber natürlich kann man auch Feuer mit Feuer bekämpfen. Die Installation von uBlock Origin, einem AdBlocker löst das Problem prompt. Wunderbar, jetzt muss ich gegen Adblocker Warunungen einen Adblocker installieren... Dies ist eigentlich nicht im Sinne des Erfinders, jedoch zur Zeit die einzig mir bekannte Möglichkeit diese Seiten aufrufbar zu halten. Wenn Sie weitere/bessere Möglichkeiten kennen entsprechende Seiten anzuzeigen, ohne deren Monetarisierung zu unterlaufen, würde ich mich über Feedback freuen.


Die Installation von uBlock Origins auf den Clients können Sie per Gruppenrichtlinie durchführen, wie in dieser Quelle beschrieben:

Erstellen oder öffnen Sie eine passende Gruppenrichtlinie.
Unter Benutzerkonfiguration --> Administrative Vorlagen --> Google Chrome --> Erweiterungen wählen Sie die Einstellung --> "Liste der Apps und Erweiterungen konfigurieren, deren Installation erzwungen wurde". Diese müssen Sie "Aktivieren" und unter "Anzeigen" die ID der Erweiterung und der Updateserver hinterlegen. Für uBlock Origin ist dies der Wert:
cjpalhdlnbpafiamejdnhcphjbkeiagm;https://clients2.google.com/service/update2/crx
Stellen Sie wie immer sicher, dass die Richtlinie von allen Computerkonten und Nutzern gelesen werden kann, z.B. mit "Authenticated Users" in welchem sowohl PC als auch Benutzerkonten liegen.

Montag, 7. Mai 2018

OpenVPN für IOS Remotezugriff auf Home-Verzeichnisse

Um auf die Homeshares aus dem Internet zugreifen zu können brauchen Sie einen funktionierenden OpenVPN Zugang. Die Anleitung dazu findet sich im Administratoren Handbuch in Kapitel 20. 

Wenn der Zugang funktioniert haben Sie bereits die zwei Dateien client.ovpn und ucs-root-ca.crt. Die erste Datei enthält die Adresse der Schule und die Konfiguration der Client Einstellungen, die zweite Datei ist das Stammzertifikat zur Authentifizierung des Servers.

OpenVPN Connect Vorbereitungen 

Um einen Zugriff für iOS Geräte herzustellen laden Sie bitte die kostenfreie App OpenVPN Connect herunter. Wenn Sie die client.ovpn und ucs-root-ca.crt über iTunes in den Ordner der App laden kann die Verbindung ohne weitere Arbeiten hergestellt werden.
Die App bietet auch die Möglichkeit die ovpn Datei über andere Quellen wie AirDrop oder eMail einzulesen, jedoch funktioniert dies nicht mit der Zertifikatsdatei.

Um über "einfachem" Wege die Zertifikatsdatei ebenfalls übertragen zu können kann das Zertifikat in die opvn Datei eingearbeitet werden. Die Anleitung dazu findet sich auch in der Hilfe der  OpenVPN Connect App.

Öffnen Sie die client.ovpn und ucs-root-ca.crt mit einem Texteditor.
Dort entfernt man die Zeile "ca ucs-root-ca.crt". Kopieren Sie den Inhalt der ucs-root-ca.crt und fügen Sie diesen am Ende der Datei client.ovpn ziwschen einem "ca" html Tag ein.

          client
          remote 123.4.56.78
          ca ucs-root-ca.crt <-- entfernen
          auth-user-pass
          cipher AES-128-CBC
          comp-lzo yes
          dev tun
          auth-nocache
          <ca>
          -----BEGIN CERTIFICATE-----
          223hE9DISTtt4Btvhp3ubjyApfbcvA3AhA4CsFZ5h3WX2XQgliUOcYBjr2ZyX6OS2
          bzpcT7iZ30WAZk6OBbaGtGbvyZNsvEQWWN2hgyeC9pbjYYoBL5UVIhzAn7pslN8
          xhpaPDCSvpDWpMTb5a6Kt4tGwvIidEJRWgOUagyaSoT2TeMsjg5ltnIF021VcPUUa
          2XkQVWjgJxBvneQ4V0ec2vLvhnb1DbdEtR93bfdbewkOuHHbNK85geSHsrKv8DSL
          P6J71bl00vqSTDOTnAji2RaE5aL5fkVe6dWrrzKnO8UGjThf09BXQTK4Uu2cpFz01S
          ----END CERTIFICATE----
          </ca>

Speichern Sie die Datei z.B. unter dem Namen ios.ovpn ab. Die Datei kann nun auf beliebigem Weg in die OpenVPN App übertragen und eingelesen werden und eine Verbindung hergestellt werden.

Schülerinnen und Schüler für OpenVPN Verbindung freischalten

Ein Lehrer kann sich mit diesen Einstellungen direkt Verbinden, Accounts von Schülerinnen und Schülern werden jedoch abgelehnt. Dazu muss dies in der Firewall eingestellt werden. 
Dazu auf der Firewall (10.1.0.11) per Browser anmelden. 
(Ich bitte um Entschuldigung dass meine Firewall auf deutsch umgestellt ist, ich gehe jedoch davon aus, dass sich die Schritte auch auf der englischen Variante gut übertragen lassen.)

Klicken Sie auf System --> Benutzerverwaltung --> Authentifizierungsserver

Klicken Sie auf  "+Hinzufügen". 
Tragen Sie jeweils ein (siehe auch: Bild unten):

  • Beschreibender Name: schuelerUndLehrer.paedml-linux.lokal
  • Hostnamen: server.paedml-linux.lokal
  • Transport: SSL - Encrypted
  • (Dadurch ändert sich der Port auf 636)
  • Suchumfang: Gesamte Unterstruktur
  • Base DN: DC=paedml-linux,DC=lokal
  • Authentifizierungscontainer: CN=users,OU=schule,DC=paedml-linux,DC=lokal
  • Klicken Sie auf Anonym binden um dies zu deaktivieren.
  • Bindungsanmeldedaten:
    • User DN: CN=ldapsuche,CN=Users,DC=paedml-linux,DC=lokal
    • Passwort: 
    • Das Passwort finden Sie auf dem Server in /etc/ldapsuche.secret
  • Benutzer-Namensattribut: samAccountName
  • Gruppen-Mitgliedsattribut: MemberOf
(Die Datei ist bis auf den Authentifizierungscontainer und dem Suchumfang identisch mit den Einstellungen aus "lehrer_server.paedml-linux.lokal".)

Klicken Sie nun auf Speichern.


Nun muss OpenVPN noch diese Benutzereinstellungen mitgeteilt bekommen. Klicken Sie auf
VPN --> OpenVPN. Wählen Sie bei "OpenVPN INTERNET (tun)" die Aktion "Server bearbeiten".
Wählen Sie bei "Backend für die Authentifizierung" den gerade erstellten Server "schuelerUndLehrer.paedml-linux.lokal" an und klicken Sie ganz unten auf Speichern.

Nun werden sowohl Schüler und Lehrer bei einer OpenVPN Verbindung authentifiziert.

IOS Zugriff auf Home-Verzeichnisse

Meine Schule hat mittlerweile zwei iPad Klassen mit 1:1 zuordnung. Die SuS brauchen den Zugriff auf Ihre Heimatverzeichnisse, auch von Zuhause. Um dies umzusetzen haben wir unsere OpenVPN Verbindung und die App FileExplorer eingesetzt.
Das Einrichten von OpenVPN für den iPad-Einsatz wird in einem zweiten Artikel beschrieben.

Einrichten von FileExplorer zur Verbindung der Home-Laufwerke

Zum Einsatz kommt die kostenfreie Variante von FileExplorer. Diese erlaubt die Verbindung zu EINEM Share (z.B. Schüler Home).  Hier eine kurze Anleitung zur Einrichtung. Voraussetzung: die Geräte müssen in einem WLAN mit Zugriff auf den Server (10.1.0.1) liegen. 
  •  App öffnen
  •  Oben links auf das "+" klicken
  •  Windows auswählen 
    • Anzeigename wählen, z.B. "Home" 
    • Host oder IP-Adresse 10.1.0.1 
    • Path Schüler- oder Lehrer-Login im paed Netz, z.B. max.muster 
    • Zugangsdaten: Schülerlogin und Schülerpw
  • Anschließend rechts oben Speichern
Nun kann in der App auf "Home" bzw. den Anzeigenamen geklickt werden um den Inhalt des Homeverzeichnisses zu sehen. 


Montag, 23. April 2018

Schülerordner Links erstellen

Über Freigaben --> Home-Verzeichnisse Schueler --> _klassen können Lehrer die Ordner der Schühler ihrer Klassen anklicken. Da diese jedoch Links auf Linuxbasis sind können diese nicht kopiert werden.

Um eine "Windows Kopie" des Ordners "_klassen" anzulegen habe ich ein kleines Powershell Skript geschrieben. Es erstellt die gleiche Ordnerstruktur des Ordners "_klassen", ändert jedoch die Links zu den Schülerverzeichnissen zu Windows Links. Der neue Ordner liegt laut Skript auf c:\klassen. Er könnte als Alternative mit zwei Unterstrichen als "__klassen" in den Ordner der Schülerverzeichnisse gelegt werden. Dann kann sich jeder Kollege die Links seiner Klasse auch herauskopieren. 


Hier das (einfache) Skript
$a = Get-ChildItem -Path '\\server\Home-Verzeichnisse Schueler\_klassen\'
foreach ($as in $a){
    $b = Get-ChildItem -Path ('\\server\Home-Verzeichnisse Schueler\_klassen\' + $as.Name)
    New-Item -ItemType directory -Path ("c:\klassen\"+$as.Name)
    foreach ($bs in $b){
        write-host $bs.Name
        $WshShell = New-Object -comObject WScript.Shell
        $Shortcut = $WshShell.CreateShortcut("c:\klassen\"+$as.Name+"\"+$bs.Name+".lnk")
        $Shortcut.TargetPath = ("\\server\Home-Verzeichnisse Schueler\"+$bs.Name)
        $Shortcut.Save()
    }
}
Das vorige Skript ließt den Ordner _klassen aus und erstellt eine Kopie mit Windows Links.
Es ist aber auch möglich alle Informationen direkt aus dem LDAP Verzeichnis zu lesen. Das Skript wird dadurch etwas Komplizierter, legt jedoch z.B. auch wirklich nur Klassen an, nicht auch von lehrern angelegte Gruppen.

$speicherpfad="c:\klassen\"
[ADSI]$domain = "LDAP://CN=klassen, CN=schueler,CN=groups,OU=schule, DC=paedml-linux,DC=lokal"
$klassen = $domain.Children.distinguishedName | ForEach-Object {[ADSI]"LDAP://$_"}
New-Item -force -ItemType directory -Path ($speicherpfad)
foreach($k in $klassen){
    New-Item -force -ItemType directory -Path ($speicherpfad + $k.Name.Replace("schule-", ""))
    $schueler = $k.Member | ForEach-Object {[ADSI]"LDAP://$_"}
    foreach($s in $schueler){
        if($s.memberOf[0].Contains("schueler")){
            $WshShell = New-Object -comObject WScript.Shell
            $Shortcut = $WshShell.CreateShortcut("o:\klassen\"+$k.Name.Replace("schule-", "")+"\"+$s.displayName+".lnk")
            $Shortcut.TargetPath = ("\\server\Home-Verzeichnisse Schueler\"+$s.Name)
            $Shortcut.Save()
        }
    }
}
Damit Dateien und Ordner, welche nicht(mehr) benötigt werden, entfernt werden, kann das Skript den jeweiligen Ordner Inhalt auslesen. Falls die Dateien keine Entsprechungen im LDAP finden können Sie gelöscht werden. Das Skript wird dadurch um einiges länger, der "klassen" Ordner ist dadurch aber immer aktuell und ein Abbild der LDAP Struktur, gelöschte Klassen oder Schüler-Links werden entfernt usw.
$speicherpfad="\\server\Home-Verzeichnisse Schueler\__klassen\"
Write-Host "Zielordner ist " $speicherpfad
$start = (Get-Date)
[ADSI]$domain = "LDAP://CN=klassen, CN=schueler,CN=groups,OU=schule, DC=paedml-linux,DC=lokal"
$klassen = $domain.Children.distinguishedName | ForEach-Object {[ADSI]"LDAP://$_"}
#Alle Dateien und Ordner im Speicherpfad bekommen das Attribut Entfernen=$true. Wenn der Ordner existieren muss wird es später wieder auf $false gesetzt.
$alteOrdner = Get-ChildItem -Path ($speicherpfad)
$alteOrdner | Add-Member -NotePropertyName Entfernen -NotePropertyValue $true
$maxi = $alteOrdner|measure
New-Item -force -ItemType directory -Path ($speicherpfad) | Out-Null
$zaehler=0
foreach($k in $klassen){
 
    #Fortschrittsanzeige, falls unerwünscht, mit # auskommentieren.
    $zaehler=$zaehler+1
    write-host ([math]::round(($zaehler/($klassen.Count)*100),0))"%"
 
    #Für jeden existierenden Ordner wird geprüft, ob es eine Klasse mit dem gleichen Namen gibt. Falls nicht bleibt das Attribut Entfernen auf $true.
    for ($i=0;$i -lt $maxi.Count; $i++){
        if($k.Name.Replace("schule-", "") -like $alteOrdner[$i] ){ 
            $alteOrdner[$i].Entfernen = $false 
        }
    }
    New-Item -force -ItemType directory -Path ($speicherpfad + $k.Name.Replace("schule-", "")) | Out-Null
 
    # $zwischenspeicher enthält alle Dateien des jeweiligen Klassenordners
    $zwischenspeicher = Get-ChildItem -Path ($speicherpfad + $k.Name.Replace("schule-", ""))
    # Für jede Datei wird das Attribut Entfernen auf $true gesetzt
    $zwischenspeicher  | Add-Member -NotePropertyName Entfernen -NotePropertyValue $true
 
    $schueler = $k.Member | ForEach-Object {[ADSI]"LDAP://$_"}
    foreach($s in $schueler){
        if($s.memberOf[0].Contains("schueler")){
            $WshShell = New-Object -comObject WScript.Shell
            $Shortcut = $WshShell.CreateShortcut($speicherpfad+$k.Name.Replace("schule-", "")+"\"+$s.displayName+".lnk")
            $Shortcut.TargetPath = ("\\server\Home-Verzeichnisse Schueler\"+$s.Name)
            $Shortcut.Save()
        }
        $max = $zwischenspeicher |measure
        #Falls die Datei durch LDAP bestätigt wurde wird Entfernen = $false
        for ($i=0;$i -lt $max.Count; $i++) {
                if($zwischenspeicher[$i].baseName -like $s.displayName){
                    $zwischenspeicher[$i].Entfernen = $false;
                }
        }
    }
    #Dateien mit Entfernen = $true löschen.
    #Links zu Schülerordnern, welche nichtmehr in der LDAP Gruppe der Klasse vorhanden sind.
    foreach($z in $zwischenspeicher){
        if($z.Entfernen){
            Remove-Item -Path ($z.FullName) -Recurse -force
        }
    }
}
#Ordner mit Entfernen=$true löschen.
foreach($o in $alteOrdner){
    if($o.Entfernen){
             Remove-Item -Path $o.FullName -Recurse -force
    }
}
#Leere Ordner löschen
Get-ChildItem -Path $speicherpfad -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse
$Ende = (Get-Date)
write-host "Das Sript lief für" ([math]::round(($ende - $start).TotalSeconds,0))"Sekunden"

(Erinnerung aus dem Shutdown-Aktikel) Skript Zeit-gesteuert ausführen

Zum zeit-gesteuerten Ausführen öffnen Sie die AdminVM, drücken Sie die Windows-taste und geben Sie "Aufgabenplanung" ein.
Klicken Sie auf "Aufgabe erstellen..." und geben Sie der Aufgabe einen Namen, z.B. "Schulcomputer herunterfahren".
Wählen Sie den Reiter "Trigger" und klicken Sie auf "Neu" und legen Sie hier Ihren Zeiplan an.
Stellen Sie eine Startzeit ein, z.B. 8:00:00 und Täglich. Klicken Sie auf "Wiederholen jede: " und stellen Sie 30 Minuten für die Dauer von 12 Stunden ein.Klicken Sie nun auf OK.
Im Reiter "Aktionen" klicken Sie auf "Neu". Die Aktion "Programm starten" ist voreingestellt. Bei Programm Tragen Sie "Powershell.exe" ein. Bei Parametern Tragen Sie
-ExecutionPolicy Bypass h:\PFAD\meinSkript.ps
ein. Mit einem Klick auf OK sollte das Skript nun täglich alle 30 Minuten ausgeführt werden.
Quelle

Montag, 16. April 2018

Opsi Statusfenster ausschalten.

Standardmäßig erscheint nach jeder Benutzeranmeldung ein kleines Opsi Fenster. In manchen Fällen schließt sich dieses erst relativ spät. Außerdem hat dieses Fenster unangenehme Eigenschaften, z.B. wenn man die Bildschirmauflösung ändert.
Hier möchte ich beschreiben, wie man das Fenster ausblenden kann.

Standardtwert setzen

Als erstes muss der Standartwert für das Ausblenden gesetzt werden.
Nach den öffnen von "Opsi-Configed Local" klicken Sie auf Server-Konfiguration.


Klicken Sie dann auf "Host-Parameter" --> "opsiclientd". Dort klicken Sie auf den Schlüssel "opsiclientd.event.user_login.active" und setzen den Wert auf "false". Bestätigen Sie dies mit einem Klick auf das rote Häkchen. Nun ist der Wert für alle neuen Clients gesetzt und wirksam.
Klicken Sie auf den linken Knopf neben der Server-Konfiguration um zur normalen Übersicht (der Client-Konfiguration) zurück zu kommen.

Werte für vorhandene Clients setzen


Wenn Sie bereits Clients ausgerollt haben müssen Sie den Wert für diese nochmals von Hand setzen.
Markieren Sie die Clients. Auf der rechten Seite finden Sie neben den Reitern "Produktkonfiguration" und "Netboot-Produkte" auch den Reiter "Host-Parameter". Klicken Sie auf "Host-Parameter" --> "opsiclientd". Dort klicken Sie auf den Schlüssel "opsiclientd.event.user_login.active" und setzen den Wert auf "false". Bestätigen Sie dies mit einem Klick auf das rote Häkchen.
Nun muss der Opsi-Client neu aufgespiet werden. Klicken Sie auf Produktkonfiguration und setzen Sie "opsi-client-agent" auf "setup".
Nach einem Neustart und der darauf folgenden neuionstallation des Opsi Agenten ist die Änderung nun wirksam.

Mittwoch, 11. April 2018

Tipparbeit sparen: Admin Anmeldung

Einer der ersten Tricks, welche mir von unserem Dienstleister (Danke Michael!!) verraten wurden habe ich so verinnerlicht, dass ich über deren Besonderheit gar nicht mehr nachdachte.

tl;dr


Login Domänenadmin
p\administrator
Login lokaler Admin
.\administrator

Erklärung


Bei der Anmeldung als Domänen Administrator muss man Windows sagen, an welcher Domäne man sich anmeldet (da es auch einen lokalen Admin gibt).

Der Benutzername ist dann:
paedml-linux\administrator
Samba juckt das aber nicht, es akzeptiert fast jede Zeichenfolge als Domänen-Admin, z.B.
p\administrator
Natürlich geht auch
ichwillnachhause\administrator
paedml-murmelbahn\administrator :) 

Umgekehrt möchte man sich manchmal auch als lokaler Administrator anmelden. Dazu muss man als Domäne die Bezeichnung des PCs eingeben.
computer16\administrator
Da der Computername zum Zeitpunkt der Anmeldung nicht angezeigt wird, kann das etwas anstrengend sein. Aber dafür hat Windows eine eigene Abkürzung eingebaut, die Unabhängig von der paedML an jedem Windows-Domänen PC funktioniert.
.\administrator
meldet sich am lokalen Gerät an, der Name dieses Gerätes wird dann freundlicherweise sogar eingeblendet

Dienstag, 27. März 2018

PCs Herunterfahren

In meiner Schule gibt es nicht nur Computerräume, in jedem Klassenzimmer stehen PCs, welche immer benutzbar sein müssen. Aber wie werden diese ausgeschaltet?

Um dieses Problem zu lösen habe ich ein Powershell-Skript geschrieben, welche die PCs herunterfahren kann. Das Skript wird auf der AdminVM alle 30 Minuten ausgeführt und fährt PCs herunter, falls dies erforderlich ist.

Da mein Skript nicht ohne weiteres auf alle Schulen übertragbar ist möchte ich hier eine Anleitung zum eigenen Zusammenbau geben.Speichern Sie das Skript z.B. auf dem Homelaufwerk des Administrators in einem Order "Skripte" unter einem Namen Ihrer Wahl ab, z.B. "herunterfahren.ps1".

1. Welche Rechner sollen heruntergefahren werden?

Sie können per Powershell Rechner aus Raumgruppen auslesen.
Um allen Rechnern einer Raumgruppe 'schule-Klassenzimmer' einen Shutdown-Befehl zu schicken müssen Sie die Rechner auslesen. Dies funktioniert mit folgendem Befehl:
$groupname = 'schule-Klassenzimmer'
$a = (New-Object System.DirectoryServices.DirectoryEntry((New-Object System.DirectoryServices.DirectorySearcher("(&(objectCategory=Group)(name=$($groupname)))")).FindOne().GetDirectoryEntry().Path)).member | % { (New-Object System.DirectoryServices.DirectoryEntry("LDAP://"+$_)) } | Sort-Object sAMAccountName | SELECT @{name="User Name";expression={$_.Name}},@{name="User sAMAccountName";expression={$_.sAMAccountName}}

Jedem dieser PCs schicken Sie nun den Stop-Computer Befehl.

foreach($as in $a){     
Stop-Computer -ComputerName $as.'User Name' -force -ErrorAction SilentlyContinue
}
Wenn Sie dieses Skript zur gewünschten Zeit ausführen, werden die PCs des Raumes heruntergefahren.

2. Bedingungen zum Herunterfahren

Sie können das Skript auch alle 30 minuten ausführen lassen. Dann ist es sinnvoll, wenn geprüft wird, ob die Comuter heruntergefahren werden sollen oder nicht. Dafür können Sie eine Variable setzen und abfragen.
$herunterfahren=$false...
...
...
if($herunterfahren){     
$groupname = 'schule-Klassenzimmer'     
$a = (New-Object System.DirectoryServices.DirectoryEntry((New-Object System.DirectoryServices.DirectorySearcher("(&(objectCategory=Group)(name=$($groupname)))")).FindOne().GetDirectoryEntry().Path)).member | % { (New-Object System.DirectoryServices.DirectoryEntry("LDAP://"+$_)) } | Sort-Object sAMAccountName | SELECT @{name="User Name";expression={$_.Name}},@{name="User sAMAccountName";expression={$_.sAMAccountName}}     
foreach($as in $a){           
Stop-Computer -ComputerName $as.'User Name' -force -ErrorAction SilentlyContinue       
}}
Nun müssen Sie noch prüfen, ob das Herunterfahren gesetzt werden muss. Dafür können Sie die Abschaltzeit in eine Variable ablegen.
$abschaltZeit = 17
Nun können Sie Prüfen ob die PCs heruntergefahren werden sollen, wenn z.B. die Aktuelle Stunde größer gleich der abschaltZeit ist.
if($heute.Hour -ge $abschaltZeit){   
$herunterfahren=$true
}
In meiner Schule starten die PCs per Bios Einschaltzeit. D.h. die PCs fahren jeden Werktag um 7.40 hoch. In den Ferien ist dies jedoch ein Problem, die PCs sollen möglichst direkt wieder ausgeschaltet werden. Dazu können Sie Prüfen, ob gerade Schulferien vorliegen. Dazu können Sie den aktuellen Ferienkalender von Schulferien.org ICAL herunterladen.
Laden Sie den Inhalt der Datei in Powershell
$ferien = Get-Content -Path "H:\PFAD\ferien_baden-wuerttemberg_2018.ics"
Nun müssen Sie die Prüfen ob das heutige Datum zwischen einem Ferienbeginn und einem Ferienende liegt.
$heute = get-date
foreach($zeile in $ferien){   
if($zeile.toString().Contains("DTSTART")){         
$anfang =[datetime]::ParseExact($zeile.toString().Substring($zeile.Length-8),'yyyyMMdd',$null)   
}   
if($zeile.toString().Contains("DTEND")){       
$ende =[datetime]::ParseExact($zeile.toString().Substring($zeile.Length-8),'yyyyMMdd',$null)    
if (($heute -ge $anfang) -and ($heute -le $ende)){                 
$herunterfahren = $true    
}}}
Für meine Schule frage ich bei den Ferien noch ab, ob es vor 9Uhr morgens ist. Nur dann setze ich das Herunterfahren aufgrund von Ferien. So stellen Sie sicher, dass von Hand eingeschaltete PCs ab 9Uhr nicht alle 30 Minuten abgeschaltet werden.
 if (($heute -ge $anfang) -and ($heute -le $ende) -and ($heute.Hour -le 9)){
Wenn Sie diesen Teil über der Prüfung if($herunterfahren){ einfügen werden die Computer nur heruntergefahren, wenn Ferien sind oder die Abschaltzeit erreicht wurde.

3. Skript Zeit-gesteuert ausführen. 

Zum zeit-gesteuerten Ausführen öffnen Sie die AdminVM, drücken Sie die Windows-taste und geben Sie "Aufgabenplanung" ein.
Klicken Sie auf "Aufgabe erstellen..." und geben Sie der Aufgabe einen Namen, z.B. "Schulcomputer herunterfahren".
Wählen Sie den Reiter "Trigger" und klicken Sie auf "Neu" und legen Sie hier Ihren Zeiplan an.
Stellen Sie eine Startzeit ein, z.B. 8:00:00 und Täglich. Klicken Sie auf "Wiederholen jede: " und stellen Sie 30 Minuten für die Dauer von 12 Stunden ein.Klicken Sie nun auf OK.
Im Reiter "Aktionen" klicken Sie auf "Neu". Die Aktion "Programm starten" ist voreingestellt. Bei Programm Tragen Sie "Powershell.exe" ein. Bei Parametern Tragen Sie
-ExecutionPolicy Bypass h:\PFAD\meinSkript.ps
ein. Mit einem Klick auf OK sollte das Skript nun täglich alle 30 Minuten ausgeführt werden.
Quelle

4. Erweiterungen

Wenn Sie beim Erstellen des Skripts eine Rückgabe in die Konsole wünschen können Sie
Write-Host "Ein Text" 
Write-Host $eineVariable 
Write-Host "Oder auch " $beides 
ausgeben lassen.

Powershell ist ein sehr mächtiges Werkzeug zur Administration von Computern. Ich habe versucht bei der Beschreibung nur das nötigste einzubauen. In meinem eigenen Skript führe ich noch Logdateien, kann die Namen der Ferien auslesen, Rechner aus mehreren Raumgruppen ansteuern uvm. Diese Erweiterungen führen an dieser Stelle allerdings etwas zu weit. Bei Rückfragen oder Anmerkungen senden Sie mir gerne Feedback.Mein vorläufiges Skript finden Sie unter Punkt 6.

5. Ausblick: Herunterfahren abhängig vom WebUntis Stundenplan. 

Mein letztes Projekt war es, zu Prüfen ob das Klassenzimmer in der WebUntis-Sundenplan-Software belegt ist. Sobald das Zimmer an einem Tag nicht mehr gebucht ist werden die PCs heruntergefahren. Das Problem hierbei ist vor allem, dass die Namen der Computer in den Klassenzimmern nicht zwingend mit den in WebUntis hinterlegten Raumnamen übereinstimmt. Die Umsetzung können Sie im Skript unten sehen, das genaue Vorgehen werde ich in einem extra Blog-Eintrag dokumentieren.

6. Mein Skript (ohne Gewähr, Stand März 2018)

Powershell

$d = Get-Content -Path "H:\Skripte\ferien_baden-wuerttemberg_2018.ics"
$abschaltZeit = 17
$raumGruppen =  'schule-Jahnstr', 'schule-KlassenzimmerBerlin'
$raumGruppenUntis = 'schule-Jahnstr'

$success = $false
$ferien=$false
$spaet = $false
$heute = get-date

                $log = "Das Skript wurde um " +$heute+" ausgeführt."
                Out-File -FilePath 'H:\Skripte\logXtreme.txt' -Append -InputObject $log



#### Ließt die Ferien in der Datei $d aus. Falls das heuteige Datum in den Ferien liegt wird $success zu $true #### 

if($heute.Hour -le 9){
### Vor 9 Uhr wird geprüft, ob ein Ferientag vorliegt und heruntergefahren, ab 9Uhr nichtmehr. ###
foreach($ds in $d){
    
    if($ds.toString().Contains("SUMMARY")){
        $testFerien =$ds.toString().TrimStart("SUMMARY:")
    }
    
    if($ds.toString().Contains("DTSTART")){
        $anfang =[datetime]::ParseExact($ds.toString().Substring($ds.Length-8),'yyyyMMdd',$null)

    }
    if($ds.toString().Contains("DTEND")){
        $ende =[datetime]::ParseExact($ds.toString().Substring($ds.Length-8),'yyyyMMdd',$null)

    if (($heute -ge $anfang) -and ($heute -le $ende))
    {
            $success = $true
            $ferien=$testFerien
    }

    }
}
}

#### Prüft, ob die Uhrzeit später als die Abschaltzeit ist, falls ja, wird $spaet ztu $true ####

if($heute.Hour -ge $abschaltZeit){
    $spaet=$true
}
else{
    $spaet=$false
}

#### Falls $success oder $spaet gesetzt ist, werden die PCs angesprochen und gegebenenfalls Heruntergefahren ####

if($success -Or $spaet){
    if($success){
           $log = "Es sind " +$ferien+" Computer fahren herunter"
           Out-File -FilePath 'H:\Skripte\logXtreme.txt' -Append -InputObject $log


    }
    if($spaet){
           $log = "Alle Computer werden um " + $heute + " heruntergefahren"
           Out-File -FilePath 'H:\Skripte\logXtreme.txt' -Append -InputObject $log
    }

    foreach($groupname in $raumGruppen){
        $a = (New-Object System.DirectoryServices.DirectoryEntry((New-Object System.DirectoryServices.DirectorySearcher("(&(objectCategory=Group)(name=$($groupname)))")).FindOne().GetDirectoryEntry().Path)).member | % { (New-Object System.DirectoryServices.DirectoryEntry("LDAP://"+$_)) } | Sort-Object sAMAccountName | SELECT @{name="User Name";expression={$_.Name}},@{name="User sAMAccountName";expression={$_.sAMAccountName}}
        foreach($as in $a)
        {
            Stop-Computer -ComputerName $as.'User Name' -force -ErrorAction SilentlyContinue
        }
    }
}


### Prüft für jeden PC in der RaumGruppe $raumGruppenUntis, ob er einen Eintrag im Stundenplan hat. Für gefundene PCs wird geprüft, ob der Unterricht gerade vorbei ist und dann gegebenenfalls heruntergefahren ###

foreach($groupname in $raumGruppenUntis){
    $a = (New-Object System.DirectoryServices.DirectoryEntry((New-Object System.DirectoryServices.DirectorySearcher("(&(objectCategory=Group)(name=$($groupname)))")).FindOne().GetDirectoryEntry().Path)).member | % { (New-Object System.DirectoryServices.DirectoryEntry("LDAP://"+$_)) } | Sort-Object sAMAccountName | SELECT @{name="User Name";expression={$_.Name}},@{name="User sAMAccountName";expression={$_.sAMAccountName}}
    foreach($as in $a)
    {
### Das Skript Pythonskript raumabfrage.py wird mit einem Raum als Parameter aufgerufen und leifert den Stundenplan dieses Raumes für den heutigen Tag zurück ###
        $untisAusgabe = python 'H:\Skripte\raumabfrage.py' $as.'User Name'
        if($untisAusgabe.Contains('datetime')){
            write-host "PC " $as.'User Name' " in Untis Gefunden"
            $stelleZeit = $untisAusgabe.LastIndexOf('datetime.time')+14
            $stelleDatum = $untisAusgabe.LastIndexOf('datetime.date')+14
            $a = $untisAusgabe.Substring($stelleDatum, $untisAusgabe.Substring($stelleDatum).IndexOf(')'))+", "+$untisAusgabe.Substring($stelleZeit, $untisAusgabe.Substring($stelleZeit).IndexOf(')'))
            $b = $a.Split(",")
            $a=$null
            foreach($bs in $b){
                $bs = $bs.Trim()
                $bs = [int]$bs
                $bs = $bs.ToString("00")
                $a+=$bs
            }
            $endZeit=[datetime]::ParseExact($a,'yyyyMMddHHmm',$null).AddMinutes(45)
            Write-Host "Letzte Stunde " $endZeit
            $heute = Get-Date
            Write-Host "Aktuelle Zeit " $heute
            if(($endZeit.AddMinutes(10) -lt $heute)-and($endZeit.AddMinutes(40) -gt $heute))
            {

                Stop-Computer -ComputerName $as.'User Name' -force -ErrorAction SilentlyContinue
                Write-Host "Der Computer " $as.'User Name' " wird heruntergefahren"
                $log = 'PC '+ $as.'User Name' + ' wurde um ' + $heute + ' heruntergefahren, letzte Stunde endete ' + $endZeit
                Out-File -FilePath 'H:\Skripte\logXtreme.txt' -Append -InputObject $log
            }
            


        }
    }
}

Python 

(Skript erwartet Bezeichnung des Raumes als Parameter)

import webuntis
import datetime
import sys
s = webuntis.Session(
    server='https://zensiert.webuntis.com',
    username='benutzer',
    password='passwort',
    school='schulname',
    useragent='Kontaktdaten des Administrators'
)
s.login()
today = datetime.date.today()
room = s.rooms().filter(name=sys.argv[1])[0]
table = s.timetable(room=room, start=today, end=today).to_table()
print (table)

s.logout()


Nachtrag: 
Bei weiteren Recherchen habe ich zwei weitere nützliche Skripte entdeckt:

  1. WebUntis direkt über Powershell
  2. Sitzungsanzeige über die Powershell 

Beide Quellen werde ich versuchen in meine Anleitung zum Herunterfahren in Zukunft einzuarbeiten.