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. 



Montag, 12. März 2018

Gruppenrichtlinien Tipps und Tricks

Hier eine Sammlung von nützlichen GPO Einstellungen, Tipps und Tricks. 


Automatisch Webseite/Programme nach Benutzer-Anmelung starten

Da wir an unserer Schule mit Untis arbeiten, sollen Benutzer, welche sich in Klassenzimmern anmelden, automatisch die Webseite gestartet bekommen.
Dazu muss eine Gruppenrichtlinie erstellt werden, in der Reichenfolge weit genug nach oben geschoben.
In Computerkonfiguration --> Richtlinien --> Administrative Vorlagen --> System --> Anmelden --> "Diese Programme bei der Benutzeranmeldung ausführen" auf aktiviert stellen und bei den auszuführenden Elemente
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" https://www.google.de
eingeben. Bei der Sicherheitsfilterung kann Authentifizierte Benutzer entfernt werden und eine Computergruppe hinzugefügt werden, z.B. schule-klassenzimmer

Automatisch DisplaySwitch.exe /clone

Um einem Rechner beim Anmelden automatisch zum Clonen der Bildschrime zu bringen kann man auch DisplaySwitch.exe /clone entsprechend per GPO ausführen lassen. 
In Computerkonfiguration --> Richtlinien --> Administrative Vorlagen --> System --> Anmelden --> "Diese Programme bei der Benutzeranmeldung ausführen" auf aktiviert stellen und bei den auszuführenden Elemente "C:\Windows\System32\DisplaySwitch.exe /clone" eintragen. Bei der Sicherheitsfilterung sollten nur PCs oder PC Gruppen eingetragen werden, bei denen das Klonen ausgeführt werden soll. 
Dadurch können sich Kolleginnen und Kollegen den Bildschirm nach belieben auf Erweitert, Clone usw. einstellen ohne dass diese Einstellung für folgende Unterrichtsstunden Auswirkungen hat, da sie immer wieder auf den Standard zurückgesetzt wird. 

Windows 10 Schneller Benutzerwechsel (auch aus dem Sperrbildschrim) wieder einschalten:

Nach einigen Minuten sperrt Windows 10 Benutzer, welche inaktiv sind. Dies verhindert z.B. unbefugten Zugriff auf Home-Laufwerke. Da beim Raumwechsel von Klassen sich immer wieder ein neuer Benutzer anmelden können muss, muss dies auch über den Sperrbildschrim erreichbar sein.

Um diese Option zu aktivieren erstellen Sie eine neue Gruppenrichtlinie (GPO) und verknüpfen Sie diese mit der Organisationseinheit (OU) schule.
Unter Computerkonfiguration --> Richtlinien --> Administrative Vorlagen --> System --> Anmelden --> "Einstiegspunkte für die schnelle Benutzerumschalteung ausblenden" muss auf Nein gesetzt werden. Achten Sie dabei darauf, dass die Richtlinie in der Hirarchie weit oben steht, da ansonsten die Standardrichtlinien den Wert wieder überschreiben.

Systemsteuerungselemente wieder einblenden

Die Systemsteuerung in Windows 10 ist aufgrund der Gruppenrichtlinien im Auslieferungszustand komplett gesperrt. Wenn Benutzer aber z.B. Bluetoothgeräte koppeln wollen oder die Anzeigeeinstellungen anpassen möchten muss dies angepasst werden.
Dazu muss (wie immer) eine Gruppenrichtlinie angelegt werden, welche in der Reihenfolge weit genug oben ist.
In Computerkonfiguration --> Richtlinien --> Administrative Vorlagen --> Systemsteuerung 
Dort muss "Zugriff auf Systemsteuerung und PC-Einstellungen nicht zulassen" auf  "Deaktiviert"
und "Nur angegebene Systemsteuerungssymbole anzeigen" auf Aktiviert gestellt werden. Bei den Systemsteuerungssymbolen kann man nun die Elemente Einblenden, z.B. "Display"
Die Namen aller Einstellungen finden sich hier.