ConfigMgr : Adding DaRT 8.1 Remote Viewer to Existing Windows PE4/5 Images

There are a few articles out there that detail DaRT Remote Viewer integration, I was unable to find one single article that covered MDOP 2013 R2 DaRT 8.1 integration with SCCM 2012 Task Sequences – specifically addressing the requirement for RemoteRecovery.exe to launch on WinPE load, not after clicking ‘Next’ on the initial start-up screen.

This article is focused on modification of existing Windows PE 4/5 images to facilitate RemoteRecovery.exe launch at boot. For a “true” DaRT image, refer to this article.

My requirements were ‘simple:’

  1. Launch DaRT RemoteRecovery on a static port during PXE-boot WinPE startup, allowing Operator selection of Task Sequence, including a WINRE/DaRT environment.
  2. Creation of shortcuts based upon Asset Tag if a Dell Platform, or IP address if not, on a remote File Share. Shortcuts should be created for both Dart 7 and DaRT 8.1 Remote Connections.
  3. I’m currently not using MDT-integration, so I also wanted to avoid using MDT components if possible – especiaslly for something as simple as this.
  4. The process should be automated for future/existing boot images – ideally using PowerShell.

Initially I reviewed using the Boot Image Prestart Files/Command however this meant that someone had to click ‘Next’ before RemoteRecovery was launched, unless the Task Sequence was mandatory.

Credits, the following sources were (bastardised!) used to compile the scripts/process outlined in this article:

 

 Pre-requisite actions:

Check out my later post on creating the DaRT WinRE image itself, using Powershell. This will provide the required DartConfig.dat, PEremote.vbs and Unattend.xml files.

  1. Export files listed below, contained in DaRT Tools32.cab and Tools64.cab and DartConfig.dat*, to a remote file share or local folder. Ensure the files are extracted to folder structure as follows:
    • amd64WindowsSystem32
    • x86WindowsSystem32
  2. Create Unattend.xml and PEremote.vbs (code below). Place in folder structure as follows:
    • Unattend.xml – root folder for each architecture created in #1.
    • PEremote.vbs – WindowsSystem32 folder for each architecture created in #1.
  3. Download your vanilla winpe.wim files to a machine that has DISM cmdlets installed.
  4. Create a File Share, Username and Password for creation of DaRT Shortcuts
  5. Create and execute the DaRT-Enable.ps1 script (code below). 

Example File/Folder Structure

Files/folders in amd64 folder:

Files in amd64WindowsSystem32 folder:

*Note you will need to extract the DartConfig.dat file form your existing DaRT image, or run through the wizard as outlined here: http://www.ideadata.co.uk/index.php/pxe-booting-dart-8-1-with-sccm-2012-including-remote-viewer/

 

Unattend.xml for amd64 platform (for x86 simply replace the “amd64” reference to “x86”):

{code lang:text showtitle:false lines:false hidden:false}<?xml version=”1.0″ encoding=”utf-8″?>
<unattend xmlns=”urn:schemas-microsoft-com:unattend”>
<settings pass=”windowsPE”>
<component name=”Microsoft-Windows-Setup” processorArchitecture=”amd64″ publicKeyToken=”31bf3856ad364e35″ language=”neutral” versionScope=”nonSxS” xmlns:wcm=”http://schemas.microsoft.com/WMIConfig/2002/State”>
<Display>
<ColorDepth>16</ColorDepth>
<HorizontalResolution>1024</HorizontalResolution>
<RefreshRate>60</RefreshRate>
<VerticalResolution>768</VerticalResolution>
</Display>
<RunSynchronous>
<RunSynchronousCommand wcm:action=”add”>
<Description>Start Remote Connection</Description>
<Order>1</Order>
<Path>CSCRIPT X:WindowsSystem32PEremote.vbs /ShortCutShare:\<Server FQDN>mapping$ /UserID:USER /UserDomain:DOM /UserPassword:PASSWORD</Path>
</RunSynchronousCommand>
</RunSynchronous>
</component>
</settings>
</unattend>{/code}

PEremote.vbs code – note this is platform agnostic, use the same script for both x86 and amd64:

{code lang:text showtitle:false lines:false hidden:false}’ Launch Remote Connect during WinPE Start-up – not using ZTIUtility        
Dim oEnv, oFSO, sShotcutName, strComputer

Set oShell = CreateObject(“WScript.Shell”)
Set oEnv = oShell.Environment(“PROCESS”)
Set oFSO = CreateObject(“Scripting.FileSystemObject”)
Set xmlDoc = CreateObject(“Microsoft.XMLDOM”)
Set colNamedArguments = WScript.Arguments.Named
Set dicIPList = CreateObject(“Scripting.Dictionary”)
Set dicPortList = CreateObject(“Scripting.Dictionary”)

strComputer = “.”
Set objWMIService = GetObject(“winmgmts:” & “{impersonationLevel=impersonate}!\” & strComputer & “rootcimv2”)

‘ Validate all arguments are supplied.
If colNamedArguments.Exists(“ShortCutShare”) AND colNamedArguments.Exists(“UserID”) _
    AND colNamedArguments.Exists(“UserDomain”) AND colNamedArguments.Exists(“UserPassword”) Then

    strShortCutShare = colNamedArguments.Item(“ShortCutShare”)
    strUserID = colNamedArguments.Item(“UserID”)
    strUserDomain = colNamedArguments.Item(“UserDomain”)
    strUserPassword = colNamedArguments.Item(“UserPassword”)
Else
    WScript.Echo “Example use: PEremote.vbs /ShortCutShare:\<server FQDN>mapping$ /UserID:USER /UserDomain:DOM /UserPassword:PASSWORD”
    WScript.Quit(1)
End If

‘Connect to target network share
On Error Resume Next
Set NetworkObject = CreateObject(“WScript.Network”)
NetworkObject.MapNetworkDrive “”, strShortCutShare, False, strUserDomain & “” & strUserID, strUserPassword
If Err.Number <>0 Then
    WScript.Quit()
End If
On Error GoTo 0

‘Check OS Environment is mapped to X: – if not script will fail
If oEnv(“SystemDrive”) <> “X:” then
    Wscript.Quit(1)
End if

‘Check remote recovery executable is available
If not oFSO.FileExists(oEnv(“SystemRoot”) & “System32RemoteRecovery.exe”) then
    Wscript.Quit(1)
End if

‘ Start RemoteRecovery.exe minimized
sCmd = “remoterecovery.exe -nomessage”
iRetVal = oSHell.Run(sCmd, 6, false)

‘ Sleep until we see the inv32.xml file; contains ticketID, port ID and IP addresses
tries = 0
Do
    WScript.Sleep 1000
    tries = tries + 1
Loop While not oFSO.FileExists(“inv32.xml”) and tries < 10

If not oFSO.FileExists(“inv32.xml”) then
    Wscript.Quit(1)
End if

‘Set oInv = oUtility.CreateXMLDOMObjectEx(“inv32.xml”)
If xmlDoc.load(“inv32.xml”) = False Then
    ‘ Do nothing, required XML file missing
Else
    Set oTicketNode = xmlDoc.SelectSingleNode(“//A”)
    strDartTicket = oTicketNode.Attributes.getNamedItem(“ID”).value
    
‘ First get the IPv4 entries (skipping locally-administered ones)
    i = 0
For each oIPNode in xmlDoc.SelectNodes(“//L”)
    If Instr(oIPNode.Attributes.getNamedItem(“N”).value, “:”) = 0 and Left(oIPNode.Attributes.getNamedItem(“N”).value, 4) <> “169.” then
        dicIPList.Add i, oIPNode.Attributes.getNamedItem(“N”).value
        dicPortList.Add i, oIPNode.Attributes.getNamedItem(“P”).value
        i=i+1
    End if
Next    
    
    On Error Resume Next
    ‘ Then add the IPv6 entries
i = 0
    For each oIPNode in xmlDoc.SelectNodes(“//L”)
        If Instr(oIPNode.Attributes.getNamedItem(“N”).value, “:”) > 0 then
            dicIPList.Add i, oIPNode.Attributes.getNamedItem(“N”).value
            dicPortList.Add i, oIPNode.Attributes.getNamedItem(“P”).value
            i=i+1
        End if
    Next
    On Error GoTo 0
    
    ‘For Dell Platforms we’ll use the Asset Tag, otherwise the WinPE IPv4 address is used for Shortcut Name
sShortcutName = assetTag

    ‘Use subst to point to X: for long file names
    If not oFSO.DriveExists(“C:”) Then
        iRetVal = oShell.Run(“cmd /c subst C: %SystemDrive%”, 0, true)
        bSub = True
    End If

    Set oLink = oShell.CreateShortcut(strShortCutShare & “DaRTDaRT81_” & sShortcutName & “.lnk”)
    oLink.TargetPath = “””C:Program FilesMicrosoft DaRTv8.1DartRemoteViewer.exe”””
    oLink.Arguments = “-ticket=” & strDartTicket & ” -ipaddress=” & dicIPList(0) & ” -port=” & dicPortList(0)
    oLink.Save

    Set oLink = oShell.CreateShortcut(strShortCutShare & “DaRTDaRT7_” & sShortcutName & “.lnk”)
    oLink.TargetPath = “””C:Program FilesMicrosoft DaRT 7v7DartRemoteViewer.exe”””
    oLink.Arguments = “-ticket=” & strDartTicket & ” -ipaddress=” & dicIPList(0) & ” -port=” & dicPortList(0)
    oLink.Save
End If

Function assetTag ‘Reads asset tag via WMI to generate machine name
Set colItems = objWMIService.ExecQuery(“Select * from Win32_ComputerSystem”)
For Each objItem in colItems
If inStr(objItem.Manufacturer, “Dell”) > 0 Then
Set colSMBIOS = objWMIService.ExecQuery(“Select * from Win32_SystemEnclosure”)
For Each objSMBIOS in colSMBIOS
assetTag = objSMBIOS.SerialNumber
Next
Else
     assetTag = dicIPList(0)
End If
Next
End Function{/code}

When you have completed the above, open a Windows Powershell as Administrator and execute the DaRT-Enable.ps1 script.

DaRT-Enable.ps1 Code

{code lang:text showtitle:false lines:false hidden:false}
$ErrorActionPreference = "Stop"; <# Ensure you have copied the following files into a local folder folder: (root) Unattend.xml - SUPPLIED in blog post. WindowsSystem32: DartConfig.dat - created during DaRT image build, copy form WindowsSystem32 folder of image. FirewallExceptionChange.dll - ToolsXX.cab DaRT specific, obtain from MDOP 2013 R2 LockingHooks.dll - ToolsXX.cabDaRT specific, obtain from MDOP 2013 R2 mfc100u.dll - ToolsXX.cabDaRT specific, obtain from MDOP 2013 R2 MSDartCmn.dll - ToolsXX.cabDaRT specific, obtain from MDOP 2013 R2 msvcp100.dll - ToolsXX.cabDaRT specific, obtain from MDOP 2013 R2 msvcr100.dll - ToolsXX.cabDaRT specific, obtain from MDOP 2013 R2 PEremote.vbs - SUPPLIED in blog post. RdpCore.dll - ToolsXX.cabDaRT specific, obtain from MDOP 2013 R2 rdpencom.dll - ToolsXX.cabDaRT specific, obtain from MDOP 2013 R2 RemoteRecovery.exe - ToolsXX.cabDaRT specific, obtain from MDOP 2013 R2 WaitForConnection.exe - ToolsXX.cabDaRT specific, obtain from MDOP 2013 R2 #> Function Main { ##### Obtain source WIM file location. $imgPath = Read-Host "Enter path & file-name for WinPE4/5 image" $FileExists = Test-Path $imgPath While ($FileExists -ne $True) { $imgPath = Read-Host "Enter path & file-name for WinPE4/5 image" $FileExists = Test-Path $imgPath } ###### Get boot-image specific architecture While ($imgArch -ne "x86" -and $imgArch -ne "amd64") { $imgArch = Read-Host "Enter local source WinPE4/5 image architecture(x86/amd64)" } ###### Find host name/local path of DaRT Files Share $dartFiles = Read-Host "Enter local DaRT source path (assumes x86 & amd64 sub-folders)" $FileExists = Test-Path $dartFiles While ($FileExists -ne $True) { $dartFiles = Read-Host "Enter local DaRT source path (assumes x86 & amd64 sub-folders)" $FileExists = Test-Path $dartFiles } Import-Module "Dism" # Build DaRT-enabled image ## Mount Image $TempMountPath = "$([System.IO.Path]::GetTempPath())BootImage_$(Get-Random)"; New-Item -Path $TempMountPath -Type Directory -Force | Out-Null Write-Output "Mounting Windows Image $imgPath, in folder $TempMountPath. Index 1" Mount-WindowsImage -ImagePath $imgPath -Path $TempMountPath -Index 1 ## Copy Unattended File Write-Output "Copying Unattend.xml file." Copy-Item "$dartFiles$imgArchUnattend.xml" $TempMountPath ## Copy DaRT ToolsXX.cab files $peremoteSource = "$dartFiles$imgArchWindowsSystem32" Write-Output "Copying DaRT RemoteRecovery files." Copy-Item "$peRemoteSourceDartConfig.dat" "$TempMountPathWindowsSystem32" Copy-Item "$peRemoteSourceFirewallExceptionChange.dll" "$TempMountPathWindowsSystem32" Copy-Item "$peRemoteSourceLockingHooks.dll" "$TempMountPathWindowsSystem32" Copy-Item "$peRemoteSourcemfc100u.dll" "$TempMountPathWindowsSystem32" Copy-Item "$peRemoteSourceMSDartCmn.dll" "$TempMountPathWindowsSystem32" Copy-Item "$peRemoteSourcemsvcp100.dll" "$TempMountPathWindowsSystem32" Copy-Item "$peRemoteSourcemsvcr100.dll" "$TempMountPathWindowsSystem32" Copy-Item "$peRemoteSourceRdpCore.dll" "$TempMountPathWindowsSystem32" Copy-Item "$peRemoteSourcerdpencom.dll" "$TempMountPathWindowsSystem32" Copy-Item "$peRemoteSourceRemoteRecovery.exe" "$TempMountPathWindowsSystem32" Copy-Item "$peRemoteSourceWaitForConnection.exe" "$TempMountPathWindowsSystem32" Write-Output "Copying Launcher Script." ## Copy CB all-in-one launcher script Copy-Item "$peRemoteSourcePEremote.vbs" "$TempMountPathWindowsSystem32" ## Unmount/save WIM Write-Output "Un-Mounting Windows Image." Dismount-WindowsImage -Path $TempMountPath -Save Remove-Item $tempMountPath -Force Write-Output "Image preparation complete, now overwrite existing boot image file (make a backup!) and update distribution points." } # Elevation code form the followiung blog post: #### http://blogs.msdn.com/b/virtual_pc_guy/archive/2010/09/23/a-self-elevating-powershell-script.aspx # Get the ID and security principal of the current user account $myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent() $myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID) # Get the security principal for the Administrator role $adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator # Check to see if we are currently running "as Administrator" if ($myWindowsPrincipal.IsInRole($adminRole)) { # We are running "as Administrator" - so change the title and background color to indicate this $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)" $Host.UI.RawUI.BackgroundColor = "DarkBlue" clear-host } else { # We are not running "as Administrator" - so relaunch as administrator Write-Output "This script must be RunAs Administrator... re-launching in an elevated context." # Create a new process object that starts PowerShell $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"; # Specify the current script path and name as a parameter $newProcess.Arguments = $myInvocation.MyCommand.Definition; # Indicate that the process should be elevated $newProcess.Verb = "runas"; # Start the new process [System.Diagnostics.Process]::Start($newProcess); # Exit from the current, unelevated, process exit } # Run your code that needs to be elevated here Main
{/code}

Summary

The net result is realisation of all requirements:

  1. Launch DaRT RemoteRecovery on a static port during PXE-boot WinPE startup – check, achieved using Unattend.xml.
  2. Creation of shortcuts based upon Asset Tag if a Dell Platform, or IP address if not, on a remote File Share – check, shortcuts are created for both Dart 7 and DaRT 8.1 Remote Connections.
  3. I’m currently not using MDT-integration, so I also wanted to avoid using MDT components if possible – check, no MDT. A single launcher script has been created.
  4. The process should be automated for future/existing boot images – ideally using PowerShell – DaRT-Enable.ps1 script automates this task.