Archive for the ‘Powershell’ Category

Für Powershell .Net Framework Tracing aktivieren

13 Dezember 2014

Manchmal ist nicht klar warum etwas nicht funktioniert. Da Powershell stark auf das .Net Framework angewiesen ist, kann man auch dessen Trace-Möglichkeiten nutzen, um eine tiefergehende Fehleranalyse zu ermöglichen. Hilfreich ist das Tracing z. B. in Verbindung mit der Netzwerkkommunikation.

Unter .Net kann man das Netzwerktracing wie in diesem Artikel beschrieben aktivieren: http://msdn.microsoft.com/en-us/library/ty48b824(v=vs.110).aspx. Damit der Trace-Mitschnitt in Powershell aktiviert werden kann, muss die in der betreffenden Powershellversion die Datei Powershell.exe.config angelegt, bzw. bearbeitet werden. Dabei ist von Bedeutung, ob man mit Powershell 32-Bit oder 64-Bit arbeitet. Zum Thema Powershell 32-/64-Bit siehe hier: http://newyear2006.wordpress.com/2012/06/20/prfen-ob-eine-powershell-sitzung-in-einer-32bit-oder-64bit-prozess-umgebung-luft/.

Kurz: Unter einem 64-Bit System findet man die 64-Bit Powershell-Version unter C:\WINDOWS\SYSTEM32\WINDOWSPOWERSHELL\V1.0 und die 32-Bit Powershell-Version unter C:\WINDOWS\SYSWOW64\WINDOWSPOWERSHELL\V1.0. Auf einem reinen 32-Bit-System ist der Pfad wieder wie bei der 64-Bit Fassung.

Wenn man nun also seine Powershell.exe.config, die so aussieht

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

   <system.diagnostics>

        <sources>

            <source name="System.Net">

                <listeners>

                    <add name="System.Net"/>

                </listeners>

            </source>

            <source name="System.Net.Sockets">

                <listeners>

                    <add name="System.Net"/>

                </listeners>

            </source>

        </sources>

        <switches>

            <add name="System.Net" value="Verbose" />

            <add name="System.Net.Sockets" value="Verbose" />

        </switches>

        <sharedListeners>

            <add name="System.Net"

                type="System.Diagnostics.TextWriterTraceListener"

                initializeData="System.Net.log"

                />

        </sharedListeners>

        <trace autoflush="true" />

    </system.diagnostics>

</configuration>

im Verzeichnis C:\Windows\System32\WindowsPowershell\V1.0\ angelegt hat, kann man anschließend Powershell starten. Wichtig, die Einstellungen wirken sich erst aus, wenn eine neue Powershell.exe gestartet wird.

Man bekommt nun in der Datei System.Net.Log die Trace-Daten gespeichert. Die System.Net.Log wird in dem Verzeichnis der Powershell.exe gespeichert.

Möchte man die Trace-Möglichkeit in Powershell-ISE nutzen, muss man natürlich die Datei Powershell_ise.exe.config bearbeiten.

Das Format der Trace-Daten ist kurz hier angerissen: http://msdn.microsoft.com/en-us/library/46fcs6sz(v=vs.110).aspx.

Fehlende Cmdlets in Powershell durch Polyfills nachbilden

17 November 2014

In der HTML5-Welt sind Polyfills ein fester Begriff. Es geht um das Nachrüsten von Funktionen, die in bestimmten Browsern nicht vorhanden sind. Die Definition stammt von Remy Sharp einer der Größen in der HTML5/Javascript-Welt https://remysharp.com/2010/10/08/what-is-a-polyfill.

Was hat das mit Powershell zu tun? Auch in Powershell haben wir das Problem, dass Microsoft es wegen Menpowerproblemen oder strategischen Gründen nicht geschafft hat, Cmdlets aus Windows 8 zurückzuportieren für Windows 7. Ein Beispiel ist Rename-Printer. http://technet.microsoft.com/en-us/library/hh918360.aspx. Oder ein neues kommendes Cmdlet ist Expand-Archive aus Windows 10, welches sicher auch auf alten Windows Versionen benötigt wird.

Polyfills, sogenannte Drop-in Polyfills, wie im Buch “Building Polyfills” von Brandon Satrom definiert, prüfen, ob eine bestimmte Funktion vorhanden ist, wenn diese fehlt, wird diese nachimplementiert. Ist die Funktion bereits vorhanden, so wird die bestehende native Funktion verwendet. In Powershell kann man genau dieses verhalten nachbilden.

Zunächst muss man herausfinden, ob ein bestimmtes Cmdlet verfügbar ist. Zunächst sollte dazu das zugehörige Modul geladen werden. Als zweites sollte dann mittels Get-Command abgefragt werden, ob das betreffende Cmdlet vorhanden ist.

Die Prüfung, ob Rename-Printer vorhanden ist, sieht z. B. so aus:

if (-not (Get-Command Rename-Printer -ErrorAction SilentlyContinue))
{
       # Rename-Printer ist nicht vorhanden
}

Wichtig dabei ist der Parameter –ErrorAction SilentlyContinue, der dafür sorgt, dass wenn das Cmdlet nicht vorhanden ist, keine Fehlermeldung erscheint.

Wie könnte nun ein konkreter Nachbau von Rename-Printer aussehen? Früher hatte man so etwas über WMI gelöst, demzufolge kommen diese Zeilen dem Ziele nahe:

$Filter = "Name=’$($Name)’"
$prn = Get-WMIObject Win32_Printer -Filter $Filter
$res = $prn.RenamePrinter($NewName)
#res.ReturnValue -eq 5 wenn Access Denied, bei 0 OK

Nun möchte man ja aber im Skript konkret das Cmdlet Rename-Printer nutzen, ohne jedesmal per if-Abfrage in Erfahrung zu bringen, ob das Cmdlet vorhanden ist oder ob die alternative Implementierung aufgerufen werden soll. Dazu definiert man in einem Scriptblock das fehlende Cmdlet und führt diesen über den & Call-Operator aus.

$polyfill = {Function Rename-Printer() {"Ersatz für Rename-Printer"}}
&  $polyfill

Wenn man nun unter Windows 7 versucht diesen Aufruf auszuführen passiert nichts, denn Rename-Printer wird nicht gefunden, da die definierte Funktion nur in einem reduzierten Scope zu sehen ist. Um dieses Problem zu lösen, verwendet man explizit den globalen Scope:

$polyfill = {Function Global:Rename-Printer() {"Ersatz für Rename-Printer"}}
&  $polyfill

Nach diesem Aufruf steht nun Rename-Printer zur Verfügung.

Nun gilt es nur noch obige Dinge zu kombinieren, dann erhält man folgendes Cmdlet Polyfill für Powershell um z. B. Rename-Printer nachzubauen:

# Rename-Printer geht nur unter Win8 oder höher, benötigt Admin-Rechte

if (-not (Get-Command Rename-Printer -ErrorAction SilentlyContinue)) {

# damit die Function verfügbar wird, muss sie mittels & ausgeführt werden und mit dem Scope global: versehen werden

&{

Function global:Rename-Printer ($Name, $NewName) {

$Filter = "Name=’$($Name)’"

# anstatt Name doch DeviceID?

$prn = Get-WMIObject Win32_Printer -Filter $Filter

$renres = $prn.RenamePrinter($NewName)

# renres.ReturnValue == 5 wenn Access Denied, bei 0 erfolgreich

}

}

}

Als Template kann man dieses Skelett sehen:

if (-not (Get-Command CmdletName -ErrorAction SilentlyContinue)) {
  &{ Function global:CmdletName ($Parameter) {
             # CmdletName – Nachbau
        }
    }
}

Durch Powershell Polyfills erreicht man bessere und schönere Skripte, da nur zu Beginn notwendige evtl. nicht vorhandene Cmdlets nachgebaut werden müssen. Ein Skript, welches unter Windows 8 läuft, kann somit ohne umschreiben auch direkt auf Windows 7 laufen. Selbst wenn später ein Update doch noch die Unterstützung für das betreffende Cmdlet auf Windows 7 bringen sollte, so wird automatische das dann nativ zur Verfügung stehende Cmdlet verwendet.

Geblockte Dateien von Downloads zulassen

7 Oktober 2014

Wenn man Dateien unter Windows aus dem Internet lädt, werden diese mit dem sogenannten Zone.Identifier in einem Alternate-Data-Stream erweitert. Ist der Zone.Identifier vorhanden und ein Eintrag unter der Sektion [ZoneTransfer] mit ZoneID=3 gesetzt, reagieren viele Windows Programme anders. Bei EXE-Dateien erscheint immer zusätzlich eine Rückfrage, ob die Datei geöffnet werden soll.

Im Explorer lässt sich die Information entfernen, wenn man die Eigenschaften der Datei mittels Rechtsklick öffnet und Zulassen anklickt.

In der Eingabeaufforderung wird die Sache etwas schwieriger. Es sei denn man hat Powershell 3.0 oder höher. Dort gibt es den Befehl Unblock-File.  http://technet.microsoft.com/en-us/library/hh849924.aspx

Wer das Problem hat, nur Powershell 2.0 zur Verfügung zu haben, der wird sich über diese Powershell-Funktion freuen: http://andyarismendi.blogspot.de/2012/02/unblocking-files-with-powershell.html.

Allgemein zum Thema: http://stackoverflow.com/questions/1617509/unblock-a-file-with-powershell. Weitere Möglichkeiten: http://www.robvanderwoude.com/amb_filestreams.php

Von Sysinternals gäb es auch noch was: Streams: http://technet.microsoft.com/en-us/sysinternals/bb897440.aspx

Protokoll zu Alternate Data Streams: http://msdn.microsoft.com/en-us/library/dd433166.aspx

Drucker mittels Powershell in Windows 8.1 kopieren

30 September 2014

Leider gibt es seit Jahren unter Windows keine einfache Möglichkeit einen Drucker zu kopieren. Gemeint sind die Drucker die nach Aufruf von

Control Printers

angezeigt werden.

Dank Powershell ist dies nun unter Windows 8.1 mit den Drucker-Cmdlets  sehr einfach möglich.

Man ruft einfach

Get-Printer | Out-GridView -Title "Quelldrucker auswählen" -PassThru | foreach {Add-Printer -DriverName $_.DriverName -Name "$($_.Name) Kopie" -PortName $_.PortName}

auf und bekommt eine Auswahl der installierten Drucker angezeigt, kann den gewünschten auswählen und klickt auf OK. Man bekommt den neuen Drucker mit dem Namen des alten und dem Anhängsel Kopie angelegt.

Wie immer gibt es aber eine Ausnahme: Leider funktioniert obige Methode nicht bei Druckern die von einem anderen Rechner kommen, die also über die Variante \\Server\Freigabe angesprochen werden. Wird solch ein Drucker versucht zu kopieren, erscheint diese Fehlermeldung:

Add-Printer : Der angegebene Anschluss ist nicht vorhanden. Verwenden Sie "add-printerport", um einen neuen Anschluss hinzuzufügen, oder geben Sie einen vorhandenen
Anschluss an.
In Zeile:1 Zeichen:81
+ Get-Printer | Out-GridView -Title "Quelldrucker auswählen" -PassThru | foreach { …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (MSFT_Printer:ROOT/StandardCimv2/MSFT_Printer) [Add-Printer], CimException
    + FullyQualifiedErrorId : HRESULT 0x80070704,Add-Printer

Wenn man man einen Drucker kopiert hat, fällt auf, dass dieser nicht unbedingt direkt zu sehen ist. In diesem Fall ist dieser Artikel interessant: http://newyear2006.wordpress.com/2014/07/09/gruppierung-bei-windows-druckern-aufheben/

Leider werden bei obiger Methode noch nicht die Einstellungen des zu kopierenden Druckers mitübertragen. D. h. die Kopie des Druckers hat immer die Standardeinstellungen, wie nach einer Neuinstallation des Druckertreibers. Dazu aber ein anderes Mal mehr…

Ereignisanzeige Protokollnamen für Powershell übersetzen

28 Juli 2014

Durch den vorhergehenden Blogeintrag http://newyear2006.wordpress.com/2014/07/28/windows-store-konnte-die-computerlizenzen-nicht-synchronisieren-ergebniscode-0x80070490/ bin ich wieder mal über ein Problem gestolpert, dem ich schon häufiger begegnet bin, deshalb dieses Mal fürs löchrige Gehirn etwas ausführlicher.

Es ging um ein Problem, welches in der Ereignisanzeige mit dem Protokollnamen "Microsoft-Windows-Store-Licensing/Admin" erfasst ist. Möchte man nun diesen Event per Powershell ermitteln, passiert folgendes:

PS>Get-WinEvent -LogName Microsoft-Windows-Store-Licensing/Admin
Get-WinEvent : Auf dem Computer "localhost" wurde kein Ereignisprotokoll gefunden, das
"Microsoft-Windows-Store-Licensing/Admin" entspricht.
In Zeile:1 Zeichen:1
+ Get-WinEvent -LogName Microsoft-Windows-Store-Licensing/Admin
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Microsoft-Windows-Store-Licensing/Admin:String) [Get-WinEvent], Excepti
   on
    + FullyQualifiedErrorId : NoMatchingLogsFound,Microsoft.PowerShell.Commands.GetWinEventCommand

Gut, vielleicht kann man es auch mit Anführungsstrichen probieren? Leider bringt dies auch nichts. Microsoft-Windows-Store-Licensing steht jetzt auch nicht im Verdacht in der falschen Sprache angegeben zu sein oder?

Nächster Versuch, man könnte ja alles mit Namen *Licen* auflisten lassen:

PS> Get-WinEvent -ListProvider *licen*

Name     : Microsoft-Windows-Kernel-LicensingSqm
LogLinks : {}
Opcodes  : {}
Tasks    : {}

Name     : Microsoft-WS-Licensing
LogLinks : {Microsoft-WS-Licensing/Diagnostic, Microsoft-WS-Licensing/Debug, Microsoft-WS-Licensing/Admin}
Opcodes  : {win:Start, win:Stop}
Tasks    : {Service_Init, Service_Stop, Service_Init_LicenseStore, Service_Init_HwidCollect…}

Name     : Microsoft-Windows-Kernel-Licensing-StartServiceTrigger
LogLinks : {}
Opcodes  : {}
Tasks    : {}

Sieht auch nicht sehr ergiebig aus. Wobei Microsoft-WS-Licensing geht ja in die Richtung, wenn WS für Windows Store stehen würde:

PS > (Get-WinEvent -ListProvider *licen*).Loglinks

LogName                          IsImported DisplayNam
——-                           ———- ———
Microsoft-WS-Licensing/Diagnostic      False
Microsoft-WS-Licensing/Debug           False
Microsoft-WS-Licensing/Admin           False

OK, es ist kein Displayname eingetragen, also wird es das auch nicht sein. Oder vielleicht doch? Ich bin ja immer noch der Meinung, WS steht für Windows Store. Da Powershell teilweise immer etwas Daten vor einem verheimlicht, lasse ich mir alle Daten zu einem Objekt ausgeben:

Get-WinEvent -ListProvider *licen*| fl *

Siehe da, hier gibt es auch ein Displayname Feld. Also mal dieses ausgeben lassen:

PS> (Get-WinEvent -ListProvider *licen*).displayname
Microsoft-Windows-Store-Licensing
Microsoft-Windows-LicensingStartServiceTrigger

Ah da isses ja. Der gesuchte Microsoft-Windows-Store-Licensing-Protokollname. Deutlicher wird es damit:

PS C:\Windows\system32> (Get-WinEvent -ListProvider *licen*)| select displayname, providername| fl

DisplayName  :
ProviderName : Microsoft-Windows-Kernel-LicensingSqm

DisplayName  : Microsoft-Windows-Store-Licensing
ProviderName : Microsoft-WS-Licensing

DisplayName  : Microsoft-Windows-LicensingStartServiceTrigger
ProviderName : Microsoft-Windows-Kernel-Licensing-StartServiceTrigger

Es geht halt nichts über eine ordentliche Übersetzung! Übrigens wäre es auch noch einfacher gegangen. Einmal über die GUI-Methode, indem man beim betreffenden Protokolleintrag sich die Details anzeigen lässt und dann System erweitert. Dort taucht dann unter Provider ebenso wieder “Microsoft-WS-Licensing” auf.

Man kann nun also mittels

Get-WinEvent -LogName Microsoft-WS-Licensing/Admin -MaxEvents 10

eine schöne Auflistung der Fehler erhalten.

DCOM Fehlermeldungen 10010 in Ereignisanzeige mit BF6C1E47-86EC-4194-9CE5-13C15DCB2001 und 1B1F472E-3221-4826-97DB-2C2324D389AE bei Windows 8.1

28 Juli 2014

Die Anfänger von Microsoft haben mal wieder zugeschlagen und beglücken die Welt mit sinnlosen Fehlermeldungen. Wenn man in der Ereignisanzeige eines Windows 8.1 Rechners diese beiden Meldungen findet:

Der Server "{1B1F472E-3221-4826-97DB-2C2324D389AE}" konnte innerhalb des angegebenen Zeitabschnitts mit DCOM nicht registriert werden.

Der Server "{BF6C1E47-86EC-4194-9CE5-13C15DCB2001}" konnte innerhalb des angegebenen Zeitabschnitts mit DCOM nicht registriert werden.

Dann hat dies mit einem Problem mit Skydrive bzw. Onedrive zu tun. Flankiert werden die Meldungen von der Ereignis-ID 10010 mit der Quelle DistributedCOM.

Da Microsoft erwiesenermaßen keine Ahnung von COM und von DCOM hat, werden wahrscheinlich aktuell viele Windows 8.1 Rechner mit dieser Fehlermeldung beglückt. Kommt mir gerade die Frage in den Sinn: Wer hat COM und DCOM nur erfunden? Mmhh.

Aufgrund dieses Threads http://answers.microsoft.com/en-us/windows/forum/windows8_1-performance/event-error-10010/69540cfb-a90c-47e1-b8a5-0b51eb0302d5 war schnell klar, dass es sich um ein Problem mit Skydrive handelt. Die Härte sind mal wieder Aussagen eines MVPs, man solle die Fehler einfach ignorieren. Gehts noch? OK, wenn schon ignorieren, dann am besten dadurch, dass man MS Produkte ignoriert. Das wäre die passendere Aussage gewesen.

Man kann zwar nun in der Aufgabenplanung hergehen und den “Idle Sync Maintenance Task” und den “Routine Maintenance Task” deaktivieren aber der Beweis, dass diese beiden Aufgaben tatsächlich etwas damit zu tun haben steht noch aus. Denn bei den Aktionen des jeweiligen Tasks ist jeweils nur ein “Benutzerdefinierter Handler” hinterlegt. Sehr aussagekräftig. Allerdings deutet auch der Hinweis bei “Ergebnis der letzten Ausführung” mit der Meldung “Starten des Servers fehlgeschlagen (0x80080005)” auf ein Problem hin. Hier eine kleine Abhandlung zum Fehler 0x80080005: http://blogs.msdn.com/b/adioltean/archive/2005/06/24/432519.aspx.

Aber zum Glück gibt es Powershell. Um die Sache etwas zu verdeutlichen ein kleines Powershell-Skript:

$t=Get-ScheduledTask -TaskPath \microsoft\windows\skydrive\
$t.Actions
$t.actions | select ClassId, Data

Wichtig ist der \ am Ende bei Skydrive! Sonst gibts einen Fehler:

Get-ScheduledTask : Es wurden keine MSFT_ScheduledTask-Objekte gefunden, bei denen die TaskPath-Eigenschaft gleich "\microsoft\windows\skydrive" ist. Überprüfen Sie den Wert der
Eigenschaft, und versuchen Sie es erneut…

Als Ausgabe erhält man:

ClassId                                Data
——-                                —-
{BF6C1E47-86EC-4194-9CE5-13C15DCB2001} IdleSyncMaintenance
{1B1F472E-3221-4826-97DB-2C2324D389AE} RoutineMaintenance

Dies sind genau die oben aufgeführten ClassIDs bei den Fehlermeldungen aus der Ereignisanzeige.

Um diese nun also zu deaktiveren, führt man als Admin noch dies aus:

$t | Disable-ScheduledTask

OK, nun ist man einen Schritt weiter aber wie lange? Funktioniert nun Onedrive nicht mehr? Abwarten und beobachten…

Bei Powershell SSL/TLS-Zertifikate-Prüfung einfach ignorieren

26 Juli 2014

Da alle Welt mittlerweile nach SSL/TLS-Transportverschlüsselung schreit, um die gröbsten Manipulationsmöglichkeiten während einer Datenübertragung, auszuschließen, bekommt man ein Problem, wenn man mal aus der Hüfte etwas ausprobieren möchte.

Aktueller Fall: Ein Web-Dienst soll kurz abgefragt werden um zu sehen, wie er reagiert. Aus Sicherheitsgründen ist dieser Dienst aber nur per HTTPS verfügbar, was zu folgendem Problem führt:

PS> Invoke-WebRequest -Uri "https://192.168.20.77:8802/cgi-bin/gadgetapi?cmd=Login&gsUser=14&gsPass=0815"
Invoke-WebRequest : Die zugrunde liegende Verbindung wurde geschlossen: Für den geschützten SSL/TLS-Kanal konnte keine Vertrauensstellung hergestellt werden..
In Zeile:1 Zeichen:1
+ Invoke-WebRequest -Uri "https://192.168.20.77:8802/cgi-bin/gadgetapi?cmd=Login&g
+ …~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
 

Eine simple und schnelle Lösung ist das Ausführen dieses Befehls:

add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

Danach ist der angefragte Server zufrieden und liefert das gewünschte Ergebnis:

PS> Invoke-WebRequest -Uri "https://192.168.20.77:8802/cgi-bin/gadgetapi?cmd=Login&gsUser=14&gsPass=0815"
Invoke-WebRequest : Der Remoteserver hat einen Fehler zurückgegeben: (403) Unzulässig.
In Zeile:1 Zeichen:1
+ Invoke-WebRequest -Uri "https://192.168.20.77:8802/cgi-bin/gadgetapi?cmd=Login&g
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
 

OK, 403 ist nicht optimal aber es ging nur darum einen korrekten HTTP-Statuscode zu bekommen!

Weitere Infos zu Powershell und SSL/TLS-Zertifikate und wie man bei Problemen weitere Infos erhält, findet man hier: http://newyear2006.wordpress.com/2014/01/04/ssltls-fehler-in-powershell-bzw-wie-man-zertifikatsprobleme-unter-windows-analysieren-kann/

Windows Drucker will nicht mehr und beim Versuch eine Testseite auszudrucken erscheint Fehlernummer 0x00000006

23 Juli 2014

Ein Kunde meldet sich und meint, sein Drucker druckt nicht mehr. Jetzt kann dies wie immer viele Ursachen haben aber diese ist mal wieder typisch Microsoft. Der Kunde hat ein kleines Netzwerk und somit eine Domäne. Der Drucker wird vom Server verwaltet und ist per Netzwerk dem Client zugeordnet.

Wie gesagt druckt der jetzt nicht mehr. Also die übliche Prozedur mit zunächst Windows-Testseite drucken, um rauszufinden, ob es ein Windows bzw. Treiberproblem ist oder von der Anwendung herrührt.

Mmhh. Windows-Testseite drucken meldet:

[Window Title]

Druckereigenschaften

 

[Main Instruction]

Die Testseite konnte nicht gedruckt werden. Soll die Druckproblembehandlung angezeigt werden? Der Vorgang konnte nicht abgeschlossen werden (Fehler 0x00000006).

 

[Ja] [Nein]

Dasselbe nochmal als Admin probiert half auch nichts. Nett wie Windows ist, bietet es einem die Druckproblembehandlung an. Aber alles was die fabriziert ist: Der Drucker ist nicht der Standarddrucker, soll dies geändert werden? Was hat dies mit dem Fehler zu tun? Nichts! Also wie immer ignorieren und selber den Fehler ausfindig machen.

Zunächst war der Gedanke, vielleicht liegt es am Druckertreiber, also Druckerwarteschlange neu gestartet aber wieder nichts. OK, vielleicht der Treiber selber, Rechner neu gestartet, brachte auch nichts. OK, dann vielleicht mal direkt am Server eine Testseite ausdrucken. Klappt!

Na toll. Der Drucker druckt vom Server aber nicht von der betreffenden Station. Also Netzwerkproblem aber welches?

Also mal Powershell aufgerufen und Test-ComputerSecureChannel probiert:

PS C:\Windows\system32> Test-ComputerSecureChannel

Test-ComputerSecureChannel : Der lokale Computer ist keiner Domäne beigetreten,

 oder die Domäne kann nicht kontaktiert werden.

Bei Zeile:1 Zeichen:27

+ Test-ComputerSecureChannel <<<<

    + CategoryInfo          : NotSpecified: (:) [Test-ComputerSecureChannel],

   ActiveDirectoryObjectNotFoundException

    + FullyQualifiedErrorId : System.DirectoryServices.ActiveDirectory.ActiveD

   irectoryObjectNotFoundException,Microsoft.PowerShell.Commands.TestComputer

  SecureChannelCommand

 

Aha, also ein Problem mit der Domänenkommunikation. Zur Bestätigung noch ein w32tm /monitor abgesetzt:

GetDcList ist fehlgeschlagen mit Fehlercode: 0x8007054B.
Beendet mit Fehler 0x8007054B

 

OK.

 

Übrigens ist das Cmdlet Test-ComputerSecureChannel die optimale Hilfe um Probleme mit der Vertrauensstellung zur Domäne (trust relationship) herauszufinden. Nichts mehr wie früher mit NETDOM, wo man zuerst immer schauen musste, wo man es herbekommt. Test-ComputerSecureChannel ist ab PS 2.0 enthalten, also quasi überall. http://technet.microsoft.com/en-us/library/hh849757.aspx.

 

Grund

Ein Vergleich der Uhrzeit zwischen Server und Client offenbarte dann auch schnell den Grund. Der Client hinkte 7 Minuten hinterm Server her!

Ja dann ist ja die Lösung einfach. Zeit gesetzt und Rechner gebootet, neu angemeldet und immer noch der Fehler. Mist.

Wieder Powershell angeworfen und Reset-ComputerMachinePassword probiert:

PS C:\Windows\system32> Reset-ComputerMachinePassword -Server server
Reset-ComputerMachinePassword : Der lokale Computer ist keiner Domäne beigetret

en, oder die Domäne kann nicht kontaktiert werden.

Bei Zeile:1 Zeichen:30

+ Reset-ComputerMachinePassword <<<<  -Server server

    + CategoryInfo          : NotSpecified: (:) [Reset-ComputerMachinePassword

   ], ActiveDirectoryObjectNotFoundException

    + FullyQualifiedErrorId : System.DirectoryServices.ActiveDirectory.ActiveD

   irectoryObjectNotFoundException,Microsoft.PowerShell.Commands.ResetCompute

  rMachinePasswordCommand

Komisch immer diese Fehlermeldungen. In einem anderen Fall hatte dies problemlos zum Erfolg geführt, warum hier nicht? Auf dem anderen Rechner war allerdings Powershell 3.0, in diesem Fall war nur 2.0 installiert.

Lösung:
Also noch Powershell 4.0 von http://www.microsoft.com/de-de/download/details.aspx?id=40855 installiert. Danach war alles gleich viel freundlicher:

PS C:\Windows\system32> Test-ComputerSecureChannel
False
PS C:\Windows\system32> Reset-ComputerMachinePassword -Server server –Credential (Get-Credential)
Cmdlet Get-Credential an der Befehlspipelineposition 1
Geben Sie Werte für die folgenden Parameter an:
Credential
PS C:\Windows\system32> Test-ComputerSecureChannel
False

So jetzt nochmal einen Neustart und alles war gut:

PS C:\Windows\system32> Test-ComputerSecureChannel
True
PS C:\Windows\system32> w32tm /monitor
SERVER.thiel.local *** PDC ***[192.168.16.1:123]:

    ICMP: 0ms Verzögerung

    NTP: +0.0000000s Offset von SERVER.thiel.local

        RefID: (unbekannt) [0xCE383741]

        Stratum: 3

[Warnung]

Die Reversenamenauflösung ist die beste Möglichkeit. Sie ist ggf. nicht

korrekt, da sich das Ref-ID-Feld in Zeitpaketen im Bereich von

NTP-Implementierungen unterscheidet und ggf. keine IP-Adressen verwendet.

PS C:\Windows\system32>

Fazit: Es ist halt immer wieder dasselbe Spiel, sobald Zeitdifferenzen größer 5 Minuten passieren, dann geht das Vertrauen verloren und es kommen die verrücktesten Fehlermeldungen zustande.

Noch ein paar Links mit weiteren Hinweisen:

http://www.techiesweb.com/repair-broken-windows-trust-relationship-between-domain-controller-and-client-machine/

http://www.implbits.com/about/blog/tabid/78/post/don-t-rejoin-to-fix-the-trust-relationship-between-this-workstation-and-the-primary-domain-failed/default.aspx

http://blog.joeware.net/2012/06/05/2508/

http://www.cievo.sk/2012/02/21/reset-computer-accounts-in-active-directory-domain/

Gruppierung bei Windows Druckern aufheben

9 Juli 2014

Wenn man unter Windows 7 oder 8 die “Geräte und Drucker” anzeigen lässt, dann stellt Windows Geräte, die denselben Treiber und denselben Port verwenden aber unterschiedliche Namen haben, alle gruppiert in einem Objekt dar.

Bei Problemen kann dies aber hinderlich sein. Mittels dieser Powershell Zeilen wird im Temp-Verzeichnis, welches auch angelegt wird, falls es nicht vorhanden sein sollte ein Symbol mit Drucker angelegt, welches dann den Zugang zu den Druckern erlaubt:

New-Item -ItemType Directory -Name "C:\Temp\Drucker.{2227A280-3AEA-1069-A2DE-08002B30309D}"
Invoke-Item C:\Temp

OK, vielleicht etwas übers Ziel rausgeschossen. Es geht auch einfacher, man gibt in der oberen Adresszeile eines Windows Explorers dies ein und erhält dann auch die Darstellung der Drucker.

shell:::{2227A280-3AEA-1069-A2DE-08002B30309D}

Oder einfach direkt von der Eingabeaufforderung:

start "" "shell:::{2227A280-3AEA-1069-A2DE-08002B30309D}"

stellt eine sogannte ClassID dar, von der es noch mehrere gibt, z. B. stellt diese ClassID die Systemsteuerung dar:

shell:::{21ec2020-3aea-1069-a2dd-08002b30309d}

Eine Auflistung der möglichen Werte findet man hier: https://code.google.com/p/libfwsi/wiki/ShellFolderIdentifiers

In diesem Zusammenhang auch noch interessant: http://newyear2006.wordpress.com/2013/01/19/windows-8-godmode/ 

Der genaue Hintergrund wird hier beleuchtet: http://msdn.microsoft.com/en-us/library/cc144096(VS.85).aspx#virtual

Windows OEM Key aus BIOS/UEFI bzw. ACPI per Powershell auslesen oder wo kommt der Windowskey für die Aktivierung her?

30 Juni 2014

Letzthin war ich doch etwas geschockt, als es darum ging, einem neuen Rechner Windows 8.1 Update 1 beizubringen. Nicht dass jetzt die Installation unmöglich gewesen wäre aber ein paar Details haben mich etwas stutzig gemacht.

Zunächst muss ich sagen, es handelt sich um einen fertigen ASUS-Tower. Dieser wird mit Windows 7 vorinstalliert ausgeliefert. Da aber Windows 8 drauf sollte und dabei gleich die neueste Fassung mit 8.1 Update 1, wollte ich über die übliche Methode diese Version installieren: http://newyear2006.wordpress.com/2013/09/13/windows-8-1-mit-product-key-von-windows-8-installieren/ indem ich die Installation über einen Microsoft-Demokey vornehmen wollte. Bei der Vorbereitung machte ich mich dann auf die Suche nach dem Windows 8 Key. Aber was soll ich sagen? Es gab keinen! Weder auf dem Tower aufgeklebt noch auf den DVDs oder sonstwo.

OK, es gibt ja die Möglichkeit den Key auf die DVD zu verpacken. Also DVD untersucht und die WIM-Dateien mittels 7zip angeschaut aber nix. Keine Datei mit einem Key. Was geht hier ab?

Ich gebs zu, ich hab dann doch von der Original-DVD Windows 8 installiert. Aber nur um zu sehen, ob später ein Key eingetragen ist. Siehe da, die Installation flutscht durch, ohne nach dem Key zu fragen und führt bei Internetverbindung gleich noch die Aktivierung durch. Das nenn ich ja mal Service! Aber genau diesen Service möchte ich am allerwenigsten.

Die Herausforderung
Jetzt wird es aber interessant. Also auf der DVD ist kein Key zu finden. Aber Windows ist nach der Installation aktiviert. Wie geht das? Wo kommt der Key für die Aktivierung her? Hat es Microsoft etwa eingesehen, dass dieses ganze Zwangsaktivierungsgedöns langfristig deren Untergang ist und aktiviert einfach so? Oder ist die Sache irgendwie an das ASUS-Bios bzw. ASUS UEFI Umgebung gebunden?

Mit der letzten Vermutung kommen wir der Sache näher aber zu meiner Überraschung ganz anders, als ich ursprünglich gedacht hatte. Es hat tatsächlich etwas mit UEFI zu tun aber es wird nicht einfach ein Hersteller ermittelt und dieser freigeschaltet, sondern jeder Rechner bringt seinen eigenen Key im ACPI mit! Dies ist auch keine Besonderheit von ASUS sondern alle Hersteller LENOVO, ACER, DELL usw. alle scheinen diese Variante mittlerweile zu praktizieren!

Die verfügbaren Tools
Hier der erste Artikel bei dem ich über die Sache gestolpert bin: http://www.zdnet.com/will-bios-embedded-windows-8-product-keys-cause-reinstall-troubles-7000008226/. Dieser bestätigte also schon mal, dass es tatsächlich so ist, dass der OEM-Key im BIOS hinterlegt ist. Dann ging es weiter: Wenn der da hinterlegt wird, dann kann man den ja irgendwie auslesen: http://www.nextofwindows.com/how-to-retrieve-windows-8-oem-product-key-from-bios/. Hier kommt neben den üblichen Verdächtigen wie Nirsoft auch RWEverything (http://rweverything.com/download/) zur Sprache, welches ich bisher noch nicht kannte. Der Link bei nextofwindows hat auch ein Python-Script parat. In diesem Script kommen interessante Win32-Funktionen zum Einsatz um den OEM-Key auszulesen! Hier das Script auf Github: https://github.com/christian-korneck/get_win8key#files. Python ist gut aber Powershell wäre besser! http://www.eightforums.com/installation-setup/35444-laptop-encrypted-key-uefi-bios-how-obtain-iso-2.html hat einen Powershellfetzen und spricht mittels WMI den SoftwareLicensingService an, welcher mittels OA3xOriginalProductKey den Key anscheinend auslesen kann. Nur leider klappte dies bei mir nie!

Die Hintergründe
Microsoft hat scheinbar schon lange die Möglichkeit vorgesehen, Daten aus dem BIOS bzw. aus dem Firmwarebereich auszulesen. So gibt es z. B. die Win32-Funktion EnumSystemFirmwareTables bereits seit Windows XP mit 64-Bit und generell seit Windows Vista. Macht auch Sinn, da ja ab Windows Vista durch die Umstellungen der Systemsicherheit vieles abgeschottet und umgebaut wurde. http://msdn.microsoft.com/en-us/library/windows/desktop/ms724259(v=vs.85).aspx. Mittels EnumSystemFirmwareTables und GetSystemFirmwareTable kann man nun die nötigen Daten aus der Firmware auslesen. http://msdn.microsoft.com/en-us/library/windows/desktop/ms724379(v=vs.85).aspx. Diese Funktionen nutzt auch das Pythonskript um den Key auszulesen.

Dazu gibt es auch eine offizielle Beschreibung der Speicherstruktur: http://msdn.microsoft.com/en-us/library/windows/hardware/dn653305(v=vs.85).aspx. Microsoft nennt es Microsoft Software Licensing Tables, welche im ACPI-Bereich unter den Systembeschreibungstabellen zu finden ist. Das Dokument spezifiziert davon zwei, einmal SLIC-Table und einmal MSDM-Table. Obwohl das Tabellenformat identisch ist, ist der Unterschied, dass SLIC für VolumeLicenseKeys und MSDM für individuelle Keys gedacht ist.

Bei Tests hat sich herausgestellt, dass Lenovo wie Asus und wahrscheinlich die meisten Hardwarehersteller MSDM benutzen. Dies zeigen auch die vielen Scripte, die es zum Auslesen gibt, welche sich ausschließlich immer auf MSDM beziehen. Interessant dabei ist natürlich, dass bei der Herstellung der Rechner jeder einzelne mit seinem individuellen Key versehen wird! Okay, früher waren es die individualisierten Keyaufkleber aber durch diese Methode können ja im Prinzip viel mehr Dinge auf dem Rechner aktiviert oder individualisiert werden. Denken wir jetzt an nichts Böses, sondern einfach mal an die bei der Bestellung hochgeladene, individuelle Startgrafik.

Powershell Fassung
Auf der weiteren Suche nach einer Fassung in Powershell, bin ich über diese Variante gestolpert: http://winaero.com/blog/how-to-get-the-windows-product-key-without-using-third-party-software/. Auf den ersten Blick genau was ich wollte aber leider wird nur wieder der Key aus der Registrierung ausgelesen, wie man es früher gemacht hat.

Diese Variante war auch ganz hilfreich und enthält noch mehr Infos zu verschiedenen Gegebenheiten und verschiedene Sourcecodes und Scripte: http://forums.mydigitallife.info/threads/43788-C-C-VB-NET-Read-MSDM-license-information-from-BIOS-ACPI-tables/page3.

Aber am Ende war keine Powershellfassung dabei. Also selber machen! Warum eigentlich Powershell? Ja warum? Weil Powershell wird solange da sein, wie sich Microsoft über Wasser halten kann und es ist bei jedem neuen Rechner von Haus aus mit an Bord! Sogar Windows RT bringt es mit. In der reinen Lehre gibt es also nichts anderes.

Also hier der Code:

$SFTCode = @"

 

[DllImport("kernel32")] public static extern uint EnumSystemFirmwareTables (uint FirmwareTableProviderSignature, IntPtr pFirmwareTableBuffer, uint BufferSize);

[DllImport("kernel32")] public static extern uint GetSystemFirmwareTable   (uint FirmwareTableProviderSignature, uint FimrwareTableID, IntPtr pFirmwareTableBuffer, uint BufferSize);

 

"@

 

$SFT = Add-Type -MemberDefinition $SFTCode -Name "SFTKlasse" -Language CSharp -UsingNamespace "System.Reflection", "System.Diagnostics", "System.Collections.Generic" -PassThru

 

# 0x41435049=ACPI ? https://github.com/michaelforney/coreboot/blob/master/src/include/cbmem.h

$firmwareTableProviderSignature = 0x41435049

$StructSize = $SFT::EnumSystemFirmwareTables($firmwareTableProviderSignature, [IntPtr]::Zero, 0)

try

{

    $StructPtr = [Runtime.InteropServices.Marshal]::AllocHGlobal($StructSize)

}

catch [OutOfMemoryException]

{

    throw Error[0]

}

 

$buffer = New-Object Byte[]($StructSize)

$SFT::EnumSystemFirmwareTables($firmwareTableProviderSignature, $StructPtr, $StructSize)

[Runtime.InteropServices.Marshal]::Copy($StructPtr, $buffer, 0, $StructSize)

[Runtime.InteropServices.Marshal]::FreeHGlobal($StructPtr)

 

if (([System.Text.Encoding]::ASCII).GetString($buffer).Contains("MSDM"))

{

    $firmwareTableMSDMID = 0x4d44534d

 

    $StructSize = $SFT::GetSystemFirmwareTable($firmwareTableProviderSignature, $firmwareTableMSDMID, [IntPtr]::Zero, 0)

    try

    {

        $StructPtr = [Runtime.InteropServices.Marshal]::AllocHGlobal($StructSize)

    }

    catch [OutOfMemoryException]

    {

        throw Error[0]

    }

 

    $buffer = New-Object Byte[]($StructSize)

    $SFT::GetSystemFirmwareTable($firmwareTableProviderSignature, $firmwareTableMSDMID, $StructPtr, $StructSize)

    [Runtime.InteropServices.Marshal]::Copy($StructPtr, $buffer, 0, $StructSize)

    [Runtime.InteropServices.Marshal]::FreeHGlobal($StructPtr)

 

    $encoding = [System.Text.Encoding]::GetEncoding(0x4e4)

 

    $key = $encoding.GetString($buffer, 56, 29)

}

$key

Wie immer gilt, von der Formatierung nicht abschrecken lassen, sondern einfach Copy&Paste und loslegen. Übrigens es werden keine Admin-Rechte benötigt! Da einiges durcheinander geht, hier noch der Gist-Verweis: https://gist.github.com/newyear2006/5578386bf4793a334f85#file-getacpi-oemkey-ps1

Würde mich freuen, wenn der eine oder andere seine Erfahrungen im Kommentarbereich verewigen könnte, um ein Gefühl dafür zu bekommen, welche Hersteller die Variante unterstützen.

Zusätzlich interessiert mich besonders:

PS > ([System.Text.Encoding]::ASCII).GetString($buffer)

Dies gibt einen String aus, mit den auf dem Rechner verfügbaren Firmwaretabellen.

Noch ein Hinweis:
Falls es nach dem Copy&Paste zu einem Fehler wie diesem hier kommt:

0×41435049 : Die Benennung "0×41435049" wurde nicht als Name eines Cmdlet, einer Funktion, einer Skriptdatei oder eines ausführbaren Programms erkannt. Überprüfen

Sie die Schreibweise des Namens, oder ob der Pfad korrekt ist (sofern enthalten), und wiederholen Sie den Vorgang.

In Zeile:12 Zeichen:35

+ $firmwareTableProviderSignature = 0×41435049

+ ~~~~~~~~~~

+ CategoryInfo : ObjectNotFound: (0×41435049:String) [], CommandNotFoundException

+ FullyQualifiedErrorId : CommandNotFoundException

dann bitte das x bei 0x41434049 löschen und nochmal eingeben. Scheint eine nette Anomalie von WordPress zu sein.


Folgen

Erhalte jeden neuen Beitrag in deinen Posteingang.