Powershell und NTFS

Es geht wieder einmal um meine beiden Lieblingsthemen Microsoft und BlueArc. Seit nunmehr einem Jahr liegen unsere Daten auf dem BlueArc Mercury System und werden von dort via NFS zur Verfügung gestellt. Für die Windowsclients gibt bzw. gab es bisher zwei BSD-Samba Server die eben diese NFS-Freigabe weitergeleitet haben. Natürlich beherrscht das Mercury auch CIFS und der Zwischenschritt über Samba war noch von dem Vorgängersystem. Wie auch immer, sollen die Verzeichnisse nun direkt vom Mercury kommen sowohl für CIFS wie auch NFS für unixbasierte Clients. Das ist in der Theorie kein Problem und steht auch schön im Hochglanzprospekt beschrieben. In der Praxis funktioniert das auch für ein frisches Dateisystem mehr oder minder problemlos, sofern man dem NFS-Teil einfach sagt er solle sämtliche Rechteänderungen einfach in die Tonne treten und die NTFS-Vorgaben umsetzen, ansonsten kann es schon mal sein das man sich mit chmod/chgrp/chown ein paar Zugriffsrechte zerlegt, teilweise auch rekursiv, was man vermeiden möchte.

Gut, wir haben nur halt kein frisches Dateisystem sondern 35.000 Accounts mit 36.000.000 Dateien. Mein Vorschlag zu behaupten der Plattenstappel wäre abgebrannt und man hätte auch kein Backup um es wieder herzustellen damit man bei Null anfangen kann, wurde leider nicht angenommen.
Zusätzlich sollte es noch einige Umstrukturierungen im Active Directory geben, genauer gesagt eine Reorganisation der Nutzerobjekte in neue OUs.
Damit verbunden dann auch gleich eine neue Primärgruppe der Nutzer, womit sich auch die Gruppe unter Unix ändert.
Da unsere Nutzerobjekte alle vom Novell eDirectory verwaltet werden, muss man da nur ein paar Kleinigkeiten ändern und für Neuanlagen hat man das dann erledigt. Die Bestandskunden möchte man aber dann doch aus Perfomancegründen lieber direkt im AD ändern, da ich inzwischen die Powershell ja ganz nützlich finde, war das auch relativ einfach und sieht so aus:

$Students = "ou=Hier Sind Alle Nutzer, dc=Objekte, dc=Unserer, dc=Domäne"
$GidNumber = 1000513
$Filer = "FullQualifiedDomainName-unseres-Mercury"
$OldOU = @("ou=A-L", "ou=M-Z" )

foreach ($CurrentOU in $OldOU)
{
    write-host "Moving Users from $CurrentOU";
    $SearchBase = "LDAP://$env:computername/$CurrentOU,$Students"
    write-host $SearchBase
    $adsi = [ADSI]$SearchBase

    foreach($CurrentAccount in $adsi.Children)
    {
        if($CurrentAccount.ObjectCategory -like "CN=Person*")
        {
            $FC = ([String]$CurrentAccount.SAMAccountName).Substring(0,1)
            $NewOU = "OU=$FC, $Students"
            $Name = $CurrentAccount.SAMAccountName
            $CurrentAccount.ProfilePath = "\\$Filer\home\users\$Name\Profile"
            $CurrentAccount.UnixHomeDirectory = "/home/users/$Name"
            $CurrentAccount.GidNumber = $GidNumber
            write-Host "Moving " $CurrentAccount.SAMAccountName " To " $NewOU "New Profile: " $CurrentAccount.ProfilePath " New UnixHomedir: " $CurrentAccount.UnixHomeDirectory >> Move.log

            $CurrentAccount.SetInfo()
            dsmove $CurrentAccount.distinguishedName -newParent $NewOu
        }
        else
        {
            write-host "Skipping Object " $CurrentAccount.distinguishedName >> Move.log
        }
    }
}

Bisher waren die Nutzer je nach Nachname in die OU=A-L bzw. M-Z einsortiert, jetzt werden sie in eine OU mit dem ersten Buchstaben der UID eingegliedert. Damit haben nun also Nutzerobjekte ein neues Zuhause, einen neuen Windowsprofilpfad, eine Neue gidNumber und ein neues UnixHomeDirectory. Problem eins gelöst ;)

Problem Nummer Zwei war dann etwas hartnäckiger, wie erstellt man für dieses vollgemüllte Dateisystem nun sauberer ACLs? Auch da fangen wir erstmal klein an, erstmal setzen wir die Primarygroup um und weil es dafür auch chgrp tut, anstatt das irgendwo unter Windows zusuchen startet man einfach mal “chgrp -R 1000513 /home” im Screen und wartet dann halt 3 Tage bis das fertig ist …

Währenddessen überlegt man sich dann wie man neue Verzeichniss über die Powershell mit entsprechenden ACLs anlegt und das sieht dann so aus:

$Acl = new-object System.Security.AccessControl.DirectorySecurity
$Acl.SetAccessRuleProtection($false, $true)
$Account = New-Object System.Security.Principal.NTAccount("Domäne", $UserName)
$AccountSID = try { $Account.Translate([System.Security.Principal.SecurityIdentifier]) } catch { Write "Nutzer $UserName nicht gefunden"; exit 1 }
$Permissions = [System.Security.AccessControl.FilesystemRights]"FullControl"
$InheritFlag = [System.Security.AccessControl.InheritanceFlags]::ObjectInherit -bor [System.Security.AccessControl.InheritanceFlags]::ContainerInherit
$PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None
$AclOwner = new-object System.Security.AccessControl.FileSystemAccessRule($AccountSID, "FullControl", $InheritFlag, $PropagationFlag, "Allow")
$Group = new-object System.Security.Principal.NTAccount("Domäne", "Domain Users")
$GroupSD = $Group.translate([System.Security.Principal.SecurityIdentifier])
$AclDA = new-object System.Security.AccessControl.FileSystemAccessRule("Domäne\Domain Admins", "FullControl", $InheritFlag, $PropagationFlag, "Allow")
$AclEA = new-object System.Security.AccessControl.FileSystemAccessRule("Domain\Uberadmins", "FullControl", $InheritFlag, $PropagationFlag, "Allow")
$AclRO = new-object System.Security.AccessControl.FileSystemAccessRule("Domäne\Backupnutzer", "ReadAndExecute", $InheritFlag, $PropagationFlag, "Allow")
foreach($CurrentRule in $AclOwner, $AclDA, $AclEA,  $AclRO)
{
    $Acl.SetAccessRule($CurrentRule)
}
$Acl.SetOwner($AccountSID)
$Acl.SetGroup($GroupSD)
if (!$(Test-Path $Homedir))
{
    try { New-Item -ItemType Directory -Path $Homedir -ea Stop | out-null } catch { Write "Konnte $Homedir nicht anlegen" ; exit 1 }
    try { Set-Acl -Path $Homedir -AclObject $Acl -ea Stop } catch { Write "Konnte ACLs nicht auf $Homedir anwenden"; exit 1 }
}
else
{
    Write "Verzeichnis $Homedir bereits vorhanden"
}

Damit wird ein neuer Securitydescriptor erzeugt mit Full-Control-Rechten für den Besitzer, Domainadmins und Admins der Admins sowie als Veranschaulichung Read&Only für eine Nutzer unter dem das Tapebackup laufen könnte. Dazu noch Owner und Groupinformation geändert, neues Verzeichnis erstellen, die ACL da drüber bügeln und schon haben wir ein schönes neues Homeverzeichnis mit feinen vererbaren NTFS-Regeln.

Problem Drei wie bekommt man diese Regeln jetzt in das bestehende Dateisystem? Tja und hier hab ich dann einfach mal nach 3 Tagen keinen Bock mehr gehabt -.-
Setzt man diese Regel nämlich auf ein bestehendes Verzeichnis, dann vererbt er zwar den Owner auf sämtliche Children, die ACL aber aus einem mir unerfindlichen Grund nicht. In der GUI kann man dann einen Haken setzen “Apply for all Children bla” und angeblich erwischt man den auch über irgendein WMI-Object, aber anscheinend hat das noch niemand in der Realität probiert. “Best-Practice” ist ein:
get-chiditems $Homedir | for-each { set-acl -path $_.fullname }
oder ähnlich, das dauert dann halt leider auch nur etwa 5 Minuten pro Account, statt der 5 Sekunden die das Häckchen in der GUI braucht, naja dafür muss ich nicht 35 tausendmal auf OK klicken.

Linux und Dell

Wie Niels schon auf einer Unterseite berichtete, haben wir im Sommer unsere betagten Latitude E6400 gegen etwas aktuellere Hardware eingetauscht, das Latitude E6220. Mir war diesmal wichtig, dass das Gerät klein und stromsparend ist… also im Vergleich zum besagten E6400 welches ich mit einer halbwegs performanten NVidia-Grafikkarte konfiguriert hatte.

Kleiner ist es, nur noch etwas über 12 Zoll, statt 14; kein interens optisches Laufwerk (und auch nicht als extern, weil nicht mitbestellt …); etwas leichter ist es auch und die Akkulaufzeit liegt mit über 4 Stunden deutlich über den 20 Minuten die mein alter gehalten hat (der im Auslieferungszustand auch nur knapp über 2 Stunden hielt). Der Core-i5 ist ausreichend schnell um nicht zu sagen, schneller als mein Spielrechner daheim und die Intelgrafik reicht für Bürozeug mehr als aus, Filme schafft die sicher auch hab ich aber noch nicht getestet. Die meiste Zeit arbeite ich damit tatsächlich, daher ist auf der Festplatte auch nur ein Debian/Testing installiert..
Wie ich das installiert habe weiß ich gar nicht mehr, aber als Niels seines neulich neuinstallieren wollte, ging das nicht so auf anhieb. Ich glaube damals einfach eine Testing-CD auf einen USB-Stick kopiert zu haben, möglich wäre aber auch eine Installation über mein Diskless-Linux zu Hause … war nicht mehr nachvollziehbar, anscheinend war ich an dem Tag betrunken :/

Bis auf anfängliche Abstürze die mit irgendeinem Kernelupdate behoben waren, hatte ich auch keine Probleme bis ich letzte Woche das BIOS updaten wollte. Das ist zwar unter Linux auch schon eine Weile möglich und Dell selbst bietet hier dafür eine Anleitung, Tools und entsprechende Firmwarebinaries, aber natürlich nicht für das E6220 ..
Dann gibt es zumindest noch das Delltechcenter das mir auch unbekannt war, wo man für Windows CAB-Dateien findet die man in WinPE einbauen kann, das half mir nur auch wieder nicht, weil da auch keine Firmwarebinaries drin waren..

Also mittels WAIK mal eben einen USB-Stick mit WinPE zusammengefrickelt und die beiden Windows-Bios-Updates mit drauf kopiert, fertig … naja fast. Der USB-Stick hat sich da irgendwie immer etwas quer gestellt, weil da schon diverse Bootloader ihre Spuren hinterlassen hatten. Da sei nur der Hinweis auf Diskpart gegeben, das ist mehr oder minder selbsterklärend um eine Primärpartition anzulegen und mit Fat32 zu formatieren, ja ich weiß das geht auch in bunt -.-

Dann kopiert man da halt mit xcopy oder was auch immer seine von copype erstellten Dateien drauf, hängt den USB-Stick an den Laptop und bootet und ein paar Augenblicke später hat man dann auch die aktuelleste BIOS Version und nicht mehr die A01

… und jetzt scripten wir um das ActiveDirectory herum …

Vorweg, ich habe 5 Minuten länger gebraucht als geplant, ansonsten möchte ich darauf hinweisen das Mr. Scott immernoch das Maschinendeck am laufen hält und zwar erfolgreich.

Heute habe ich mich mal mit der Windows Powershell beschäftigt. Ich stand vor dem Problem, das ich 400+ Rechner automatisiert mit einem von mir festgelegten Kontenpasswort im ActiveDirectory anlegen wollte. Dabei handelt es sich um die Linuxrechner hier am Standort die in naher Zukunft unsere DXS Umgebung bereichern sollen.

Schritt 1: lernen wie man mit der PowerShell überhaupt etwas macht, das obligatorische “hallo welt!” war sehr einfach.
Schritt 2: Aufrufe über das ADSI-Commandlet um die API zum anlegen von Objekten zu benutzen, das war auch einfach.
Schritt 3: Herausfinden wo im Technet die UserAccountControl-Zahlen stehen, das hat schon etwas länger gedauert
Schritt 4: MultiValue-Felder .. da benutzt man wohl Arrays und .PutEx
Schritt 5: Passwort setzen auf einen vorgegeben Wert und nicht mehr den Computernamen

Das ganze hat mich jetzt die meiste Zeit des Nachmittages beschäftigt, funktioniert aber schon mal soweit. Morgen mach ich das dann noch schön und ordentlich damit keiner mehr meckert.
Und so sieht das bis jetzt aus:

#/*========================================================================*
# * Dennis erster Versuch mit der Powershell
# * Ziel ist es Computerobjekte automatisiert aus einer CSV-Liste anzulegen
# *========================================================================*/ 

#/*------------------------------------------------------------------------*
# * Variablen, was soll man dazu schon sagen ...
# *------------------------------------------------------------------------*/
$dnsdomain = "die.dns.domain.de"
$krbrealm = "DER.KERBEROS.REALM.DE"
$basedn = "ou=bla,dc=fasel,dc=de"
$input = "computer.csv.txt"

#/*------------------------------------------------------------------------*
# * Wie sich die UAC zusammensetzt steht hier: http://support.microsoft.com/kb/305144/
# * 4128 = 4096 + 32 => WORKSTATION_TRUST_ACCOUNT + PASSWD_NOTREQD
# *------------------------------------------------------------------------*/
$AControl = 4128

$adsi = [adsi]"LDAP://ldap-dc/wie/man/ihn/braucht"

Import-csv $input | foreach {
    $de = $adsi.create("Computer", "cn=" + $_.Hostname);
    $de.SetInfo()

    $de.SAMAccountName = $_.Hostname.ToUpper() + "$"
    $de.dNSHostName = $_.Hostname.ToLower() + "." + $dnsdomain
    $hostservice = "HOST/" + $_.Hostname
    $hostfqdnservice = $hostservice + "." + $dnsdomain
    $services = @($hostservice , $hostfqdnservice)

    #/* 2 = Update */
    $de.PutEx(2, "ServicePrincipalName", $services)
    $de.userPrincipalName = "host/" + $_.Hostname + "." + $dnsdomain + "@" + $krbrealm
    $de.SetInfo()

    $de.SetPassword($_.hostpass.ToString())

    $de.userAccountControl = $AControl
    $de.SetInfo()

    $ktfile = $_.Hostname + ".keytab"
    write-host "Computerkonto f�r" $de.userPrincipalName "angelegt"
    ktpass -crypto ALL -ptype KRB5_NT_SRV_HST -out $ktfile -pass $_.hostpass -princ $de.userPrincipalName | out-null

}

Als kleines extra hab ich mir dann doch nochmal ktpass genauer angesehen worüber ich solang geflucht habe. Offensichtlich schreibt das bei uns einfach nur die Passwörter nicht ins Kerberos was die Option +rndpass etwas unsinnig macht.
Wenn man das aber mit “-pass Klartextpasswort” aufruft erzeugt mir das eine Keytab die auch unter Linux richtig funktioniert.
Zum schluss lass ich mir also die Passwörter zufällig von der PowerShell erzeugen und übergebe die als Parameter an ktpass -> Wiese grün …
Aber auch erst morgen …

Mr. Scott macht jetzt Feierabend

Update: Testweise für 150 Klienten ein Konto angelegt mit Zufallspasswort und zugehöriger Keytab, läuft :)
Script ist auch etwas schöner bin nur zu faul es upzudaten, aber ich erstelle jetzt auch unter-OUs und teste vorm anlegen ob  vielleicht schon ein gleichnamiges Objekt vorhanden ist.

mutt Kniffe

Kniffe für Leute mit vielen Mailinglisten: Man kennt das Problem. Man hat über 100 Ordner angelegt. In jedem dieser Ordner sammeln sich 10-1000 Mails pro Tag. Alles lesen kann man sowieso nicht. Die Threadansicht verhindert das gröbste Chaos. Aber im Prinzip möchte man nur das Thread Thema sehen und den Thread ansonsten nur dann aufklappen, wenn er neue Nachrichten enthält:

  • set sort=threads
  • folder-hook ^imaps:// exec collapse-all
  • set collapse_unread=no

Nun das nächste Problem. Man könnte jetzt jeden Thread als Gelesen markieren. Aber im Prinzip möchte man meistens nur ein bischen zwischen den neuen Mails springen und dann den gesamten Ordner als Gelesen markieren:

  • macro index M "<tag-pattern>.*\r<tag-prefix><clear-flag>N<untag-pattern>.*\r" "Mark all messages in this mailbox as read"

M drücken. DONE!