Categories
ConfigMgr

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”):

<?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>

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

' 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

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

DaRT-Enable.ps1 Code

Import-Module "Dism"

$ErrorActionPreference = "Stop";

<# Ensure you have copied the following files into a local folder/remote file share:
\(root)
Unattend.xml - SUPPLIED in blog post.
\Windows\System32:
DartConfig.dat - created during DaRT image build, copy form Windows\System32 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 to confirm target computer is ping-able.
Function Ping([string]$computerName) {
if (Test-Connection -ComputerName $computerName -Quiet -Count 1) {
return $true 
}
else {
write-host "Error, remote file server specified, $computerName, is inaccessible." -ForegroundColor Red -BackgroundColor Black
exit 1
}
}

# 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 source WinPE4/5 image architecture(x86/amd64)"
}

#Find host name/local path of DaRT Files Share, if a UNC Path test server 'pings'.
$dartFiles = Read-Host "Enter DaRT source UNC/local path (assumes x86 & amd64 sub-folders)"
$arrDartFiles = $dartFiles.split("\")
If ($arrDartFiles[0] -eq "") { #Null first item means UNC path due to "\\"
$dartHost = $arrDartFiles[2]
Ping($dartHost)
}
Else {
$FileExists = Test-Path $dartFiles
If ($FileExists -ne $True) {
Write-Host "Supplied local DaRT files path invalid, please review." -ForegroundColor Red -BackgroundColor Black
exit 1
}
}

#Build DaRT-enabled image
$TempMountPath = "$([System.IO.Path]::GetTempPath())BootImage_$(Get-Random)";
New-Item -Path $TempMountPath -Type Directory -Force | Out-Null
Write-Host "Mounting Windows Image $imgPath, in folder $TempMountPath. Index 1"
Mount-WindowsImage -ImagePath $imgPath -Path $TempMountPath -Index 1
Write-host "Copying Unattend.xml file."
copy-item "$dartFiles\$imgArch\Unattend.xml" $TempMountPath
$peremoteSource = "$dartFiles\$imgArch\Windows\System32"
Write-host "Copying DaRT RemoteRecovery files."
#DaRT ToolsXX.cab files
copy-item "$peRemoteSource\DartConfig.dat" "$TempMountPath\Windows\System32"
copy-item "$peRemoteSource\FirewallExceptionChange.dll" "$TempMountPath\Windows\System32"
copy-item "$peRemoteSource\LockingHooks.dll" "$TempMountPath\Windows\System32"
copy-item "$peRemoteSource\mfc100u.dll" "$TempMountPath\Windows\System32"
copy-item "$peRemoteSource\MSDartCmn.dll" "$TempMountPath\Windows\System32" 
copy-item "$peRemoteSource\msvcp100.dll" "$TempMountPath\Windows\System32"
copy-item "$peRemoteSource\msvcr100.dll" "$TempMountPath\Windows\System32"
copy-item "$peRemoteSource\RdpCore.dll" "$TempMountPath\Windows\System32"
copy-item "$peRemoteSource\rdpencom.dll" "$TempMountPath\Windows\System32"
copy-item "$peRemoteSource\RemoteRecovery.exe" "$TempMountPath\Windows\System32"
copy-item "$peRemoteSource\WaitForConnection.exe" "$TempMountPath\Windows\System32"
Write-host "Copying Launcher Script."
#Custom all-in-one launcher script
copy-item "$peRemoteSource\PEremote.vbs" "$TempMountPath\Windows\System32"

Write-host "Un-Mounting Windows Image."
Dismount-WindowsImage -Path $TempMountPath -Save
Remove-Item $tempMountPath -Force
Write-host "Image preparation complete, now overwrite existing boot image file (make a backup!) and update distribution points."

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.

Leave a Reply

Your email address will not be published. Required fields are marked *