Passwort für Deployment mit MDT

Damit nicht jeder seinen Rechner mit MDT neu aufsetzen kann nutzt man das Prestaging-Verfahren mit Hinterlegung der PXE GUID im AD – nur hindert das diejenigen die auf so einem prestaged Rechner sitzen MDT anzuwerfen, deswegen sollte man die MDT Task Sequenzen mit einem Kennwort schützen. Da wir sowieso den Rechnernamen über die MDT Datenbank “mitgeben” habe ich ein “freies” Feld in der Datenbank (ScanStateArgs da wir USMT nicht nutzen) zweckentfremdet und schreibe dort ein zufälliges Kennwort rein (welches ich dem berechtigten Installateur natürlich bekannt gebe) – in der MDT TS wird dann ganz weit oben der Aufruf für diese HTA eingefügt (welche halt natürlich irgendwo am MDT Deployment Share herumkugeln muss):

<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
      <title>Deployment password</title>
      <HTA:APPLICATION ID="App Mapping" APPLICATIONNAME="App Mapping" VERSION=".01" BORDER="thin" SCROLL="no" SHOWMENU="no" SYSMENU="no" SINGLEINSTANCE="yes" WINDOWSTATE="Normal"/>
   </head>
   <script language="vbscript" type="text/vbscript" src="..\ZTIUtility.vbs"></script>
   <script language="vbscript" type="text/vbscript">
      on error resume next
      sub Window_onload
         on error resume next
         
         Set oTSProgressUI = CreateObject("Microsoft.SMS.TSProgressUI") 
         oTSProgressUI.CloseProgressDialog 
         Set oTSProgressUI = Nothing         
         
         x=350
         y=150
         window.resizeTo x,y
         window.moveTo ((screen.availWidth \ 2) - (x \ 2)), ((screen.availHeight \ 2) - (y \ 2))
         MDTPassword.Focus
      end sub

      sub Document_onKeyDown
         alt=window.event.altKey
         if window.event.keyCode=13 then
            CheckPassword
         end if
         if window.event.keyCode=116 or window.event.keyCode=27 or (alt=true and window.event.keyCode=115) then
            window.event.keyCode=0
            window.event.cancelBubble=true
            window.event.returnValue=false
         end if       
      end sub

      Function CheckPassword
         if Trim(MDTPassword.Value) = "" Then
            MsgBox "Password empty/only blanks"
            MDTPassword.Focus
            Exit Function
         end if
         if IsNull(oEnvironment.Item("ScanStateArgs")) or Trim(oEnvironment.Item("ScanStateArgs")) = "" Then 
            MsgBox "No password in MDT database, cannot continue"
            MDTPassword.Focus
            Exit Function
         end if
         if oEnvironment.Item("ScanStateArgs") = MDTPassword.value then
            self close ()
         else
            msgbox "Wrong password"
            MDTPassword.Value=""
            MDTPassword.Focus
         end if
      End Function
   </script>
   <body>
      <br/>
      <Font Face=Arial size=3>
         <b>Deployment password: </b>
         <input name="MDTPassword" type="password" value="" size="10">
      </font>
      <input id=runbutton class="button" type="button" value=" OK " name="ok_button" onClick="CheckPassword">
   </body>
</html>

Windows Embedded Standard 7 auf Hyper-V via RDP

Wenn man Windows Embedded Standard 7 (WES7) auf Hyper-V (in meinem Fall auf Windows 8.1) betreibt hat man bei Zugriff auf Hyper-V Remote Connection via RDP (also die Remote Connection läuft innerhalb einer RDP Session) keinen Maussupport (“Mouse not captured in remote Desktop session”) – die Integration Services lösen das Problem normalerweise nur sind die offenbar in der aktuellen Version nicht für WES7 supported bzw. funktionieren schlicht nicht (installieren lassen sie sich nämlich).

Wenn man aber die “alten” Versionen im Kompatibilitätsmodus installiert pfeift das einwandfrei (Link):

WES7_IntegrationServices

 

 

PXE GUID einer Hyper-V Generation 2 VM ermitteln

Wer seine Rechner für WDS prestagen muss sollte auch die PXE GUID wissen – was bei Generation 2 Hyper-V VMs gar nicht einfach ist weil sich MS entschlossen hat die nicht anzuzeigen, auch nicht im Hyper-V Manager und auch nicht mit den PowerShell Cmdlets (zumindestens hab ich nichts gefunden); in den Tiefen des WMI Repositories ists aber dann doch versteckt:


(gwmi -namespace root\virtualization\v2 -query &quot;select BIOSGUID from Msvm_VirtualSystemSettingData where InstanceID='Microsoft:$((get-vm MEINE_VM).VMId)'&quot;).BIOSGUID

Client Certificate Authentication mit IIS 8.x

MS hat mit Windows 8/2012 bzw. 8.1/2012R2 in SCHANNEL ein paar Änderungen durchgeführt die u.a. die SCCM 2012 R2 Clientkommunikation (wenn im HTTPS-Modus betrieben) vernichtet (403 Error Codes beim Versuch irgendwas vom DP oder MP zu laden, im IIS Log kann man dann auf 403.16 (=CERT_E_UNTRUSTEDROOT) weiter tracen).

Die Ursache ist ein nicht self signed Zertifikat (s. KB2802568) in den Trusted Root Certification Authorities des Webservers, man kann dieses entweder finden (Get-Childitem cert:\LocalMachine\root -Recurse | Where-Object {$_.Issuer -ne $_.Subject} | Format-List * (aus KB295828)) und löschen oder man ändert das SCHANNEL Verhalten mit

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL]

“ClientAuthTrustMode”=dword:00000002

 

 

Der Vorgang wurde aufgrund von aktuellen Beschränkungen….

Ein weiteres klassisches Beispiel einer sprechenden Fehlermeldung bei der man gleich weiß wo man suchen muss:

RemoteAppRestrictions

Passiert auf einem 2008R2 Terminalserver (wobei die Version vermutlich egal ist), beim Aufruf einer RemoteApp.

Grund war: Der Pfad der RemoteApp war nicht korrekt geschrieben:

RemoteAppRestrictions2

Kein Eintrag im EventLog (nicht mal in den Terminalserverspezifischen), nix – wenn nicht zufällig eine zweite RemoteApp die sehr ähnlich ist funktioniert hätte wäre das Troubleshooting anstrengend geworden….

No connections are available/Firewall bleibt im Public Profile

Windows 7/Server 2008 R2 (zumindestens) – Network and Sharing Center zeigt keine Verbindungen an (obwohl definitive welche da sind) und Firewall steckt im Public Profile fest, egal was man anstellt.

Ursache: Rechte auf HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList sind falsch. Network Location Awareness und Network List Service dürfen nicht das was sie müssen damit das ganze Werkl funktioniert.

Die korrekte Rechte für Windows 7/2008R2 sind:

  • NetworkList + darunter: Administrators (Full), NT Service\netprofm (alles außer Write DAC/Owner)
  • NetworkList/Nla: NT Service\NlaSvc (alles außer Write DAC/Owner) zusätzlich
  • NetworkList/Permissions: SYSTEM (Full), Users (Query/Set value), NT Service\netprofm (Full), Network Configuration Operators (Query/Set Value, Create Subkey)

Setzen, rebooten, freuen 🙂

AD RMS + XPS Viewer + Non Domain Joined = No Go

Als Follow-Up zum Spaß mit AD RMS habe ich nach monatelanger Wartezeit jetzt den rejection letter bekommen – im Grunde läufts auf “works as designed” hinaus, XPS Viewer wird nicht mehr angegriffen (weil alte RMS-Technik), es folgt der Hinweis auf die Modern App (massiv sinnvoll wenn man Windows 7 eingesetzt hat) die bisher noch nicht mal ansatzweise RMS kennt….

Flash 12 Internet Explorer Hänger beseitigen

Flash 12 tendiert dazu den Internet Explorer hängen zu lassen, manchmal für Minuten, manchmal für ewig, manchmal crashed er den IE auch – einer meiner Kollegen hat mühevoll rausgefunden dass es wohl daran liegt dass das Ding nach Hause (flashservice.adobe.com) telefonieren will und nicht kann weil der A-Record nicht auflösbar ist. Eine Lösung ist natürlich im HOSTS-File flashservice.adobe.com auf 127.0.0.1 umzuleiten – eine viel elegantere Methode (wenn man einen Microsoft DNS Server hat) ist die eine neue Forward Lookup Zone mit Namen “flashservice.adobe.com” zu erzeugen und darin einen A-Record mit leerem Namen und 127.0.0.1 als Adresse:

flash

Das (same as parent folder) ist nur die Anzeige wenn man einen A-Record ohne Namen öffnet….

Ist natürlich nicht auf meinem Mist gewachsen, Herr Google hat geholfen 😀

RDS PSDrive in 2012 (R2)

Irgendein kluger Kopf hat für 2012 (R2) beschlossen dass der PowerShell PSDrive Provider für das RDS: Laufwerk leer bleibt, die Directories RDSConfiguration und RemoteApp sind einfach nicht da. Da wir hier von PowerShell sprechen ist .NET im Boot und MS war wieder so nett die dahinter liegende Provider DLLs (C:\Windows\System32\WindowsPowerShell\v1.0\Modules\RemoteDesktopServices\TSPSProvider.dll bzw. TSPSEngine.dll) nicht zu obfuszieren. In der Klasse Microsoft.TerminalServices.PSEngine.RoleQueryHelper werden offenbar die installierten Features der RemoteDesktopServices Rolle ermittelt und nur die passenden Teile dann auch im Provider enabled – was durchaus sinnvoll ist aber das Subfeature “RDS-RD-Server” (Remotedesktopsitzunghost aka RDSH) wird nur dann berücksichtigt wenn etwas was sie im Code “TestHook” nennen existiert. Der TestHook ist ein Registrykey: HKLM/SYSTEM/CCS/Services/TermService/fPSForAllRoles=1 (REG_DWORD), setzt man diesen ist alles so wie es in 2008R2 war.

Wie üblich sind alle Angaben natürlich nicht nur nicht supported sondern vermutlich auch ganz böse und in einer der nächsten Versionen ist alles ganz anders 😀

Volle Kontrolle über DRIVESTOREDIRECT

Mit RDP 7.0 hat Microsoft ja beschlossen gaaaanz schlau zu sein und im RDP-File Parameter DRIVESTOREDIRECT nicht mehr nur einfach die Laufwerksbuchstaben die man gerne in die Session mappen würde zuzulassen sondern da muss man echt das eintragen was der Explodierer zum Zeitpunkt des Verbindungsaufbaus (wie das Teil nachher heißt ist vollkommen schnurz….) anzeigt, also z.B. “CD-Laufwerk (D:)” – da das naturgemäß ein brutal bewegliches Ziel ist (Sprache, Typen, Volumenamen, Laufwerksbuchstabe vorne/hinten, etc.) kann man das nicht wirklich hardcoden und ist damit aufgeschmissen…..gäbe es in der SHELL32.DLL nicht die Funktion SHGetFileInfo() die uns genau das liefert was wir wollen (DisplayName in der SHFILEINFO Struktur die da befüllt wird), hier ein Beispielprogramm dass alle Laufwerke auflistet:

using System;
using System.Runtime.InteropServices;

namespace GetVolumeLabel
{
   [StructLayout(LayoutKind.Sequential)]
   public struct SHFILEINFO
   {
      public IntPtr Icon;
      public int IconIndex;
      public uint Attributes;
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
      public string DisplayName;
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
      public string TypeName;
   };

   class Program
   {
      public const uint SHGFI_DISPLAYNAME = 0x200;

      [DllImport("shell32.dll")]
      public static extern IntPtr SHGetFileInfo(string Path, uint FileAttributes, ref SHFILEINFO Info, uint Size, uint Flags);

      static void Main(string[] args)
      {
         SHFILEINFO oInfo;
         IntPtr oRes;

         for (char cDrive = 'A'; cDrive <= 'Z'; cDrive++ )
         {
            oInfo =  new SHFILEINFO();
            oRes = SHGetFileInfo(cDrive + ":\\", 0, ref oInfo, (uint)Marshal.SizeOf(oInfo), SHGFI_DISPLAYNAME);
            Console.WriteLine(cDrive + ":\\ = '" + oInfo.DisplayName + "'");
         }

      }
   }
}