Troubleshooting

Props to a post I happened to see on reddit around the time I was ready to publish this: https://www.reddit.com/r/Windows10/comments/9d7uqt/yo/

One Method of Simplifying DMP File Analysis

Some time ago we implemented a new VMware Horizon View based VDI  environment and for a while everything seemed to be going well.  Over time customers would periodically call to report that their VDI’s appeared to reboot and in reviewing the machines we noticed some unusual patterns.  After digging around a bit we found cause to be concerned because a significant number of VDI’s were BSOD’ing regularly.

Investigation

Every time a customer reported that their VDI seemed to spontaneously restart I’d check the Event Logs to confirm the restart was due to a bugcheck and not due to Windows Updates.  Once confirmed I’d check for memory dumps then analyze them.  After a while more customers became vocal about the stability of their VDI and the process I was following became tedious:

  1. Check Event Viewer to validate bugcheck vs other process initiated restarts
  2. Check C:\Windows for a MEMORY.dmp
  3. Check C:\Windows\minidump for *.dmp’s
  4. Move the .dmp files to a staging area
  5. Analyze each file with WinDBG

Too many clicks and keyboard action if you ask me. (^_^)

DMP File Analysis Simplification

After evaluating a number of VDI’s I found that in nearly every case the VDI did indeed experience a bugcheck-initiated restart so I stopped performing step 1.  And truthfully, a machine shouldn’t have any .DMP files so even if one was present I wanted to know about it.  With that out of the way I decided to automate the rest of the process in a manner that was good enough for me via a PowerShell script.

Check for & Pull .DMP File(s)

The first function in the script would check key areas for .DMP files, move them to a storage location then populate an array with details on whether or not a .DMP was found.


# centralized bsod collection location
[string]$StorageLocation = '\\Server01\Playground$\CrashDumpStorage'

# store the details of the crash files found
[System.Collections.ArrayList]$CrashDumpDetails = @()

# name of the machine that BSODd<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>
[string]$BSODComputer = 'BSODSystem01'

# path to remote machine
[string]$RemotePath = '\\' + $BSODComputer + '\C$'

# path to data storage location
[string]$MoveDestination = Join-Path $StorageLocation $BSODComputer

# check for memory.dmp
[bool]$MemoryDMP = $false
$MemoryDMPDate = ''
if(Test-Path -Path "$RemotePath\Windows\Memory.dmp" -PathType Leaf -ErrorAction Stop)
    {
        if(!(Test-Path -Path $MoveDestination -PathType Container -ErrorAction Stop))
            {
                New-Item -Path $MoveDestination -ItemType directory -ErrorAction Stop | Out-Null
            }

        [bool]$MemoryDMP = $true

        $MemoryDMPDate = (Get-ChildItem -Path "$RemotePath\Windows\Memory.dmp").LastWriteTime
        Move-Item -Path "$RemotePath\Windows\Memory.dmp" -Destination $MoveDestination -Verbose -Force -ErrorAction Stop
    }

# check for mini dumps
[bool]$MiniDMP = $false
$MiniDMPCount = 0
if(Test-Path -Path "$RemotePath\Windows\Minidump" -PathType Container)
    {
        $MiniDMPCount = (Get-ChildItem -Path "$RemotePath\Windows\Minidump" -ErrorAction Stop | Measure-Object).Count
        if($MiniDMPCount -gt 0)
            {
                if(!(Test-Path -Path $MoveDestination -PathType Container -ErrorAction Stop))
                    {
                        New-Item -Path $MoveDestination -ItemType directory -ErrorAction Stop | Out-Null
                    }

                [bool]$MiniDMP = $true
                Get-ChildItem -Path "$RemotePath\Windows\Minidump" | Sort-Object -Property LastWriteTime | Select -First 5 | % { Move-Item -Path $_.FullName -Destination $MoveDestination -Verbose -Force -ErrorAction Stop }
            }
    }

# add details to arraylist
$CrashDumpDetails += [pscustomobject]@{
    ComputerName = $BSODComputer;
    DumpFileDir = $MoveDestination;
    MemoryDMP = $MemoryDMP;
    MemoryDMPDate = $MemoryDMPDate;
    MiniDMP = $MiniDMP;
    MiniDMPCount = $MiniDMPCount;
}

I would store the output of the function in a variable so the operator (typically me) can see the stats:


$GetCrashDumpFiles | Format-Table -AutoSize

ComputerName    Status DumpFileDir                            MemoryDMP MemoryDMPDate        MiniDMP MiniDMPCount OtherDMP OtherDMPCount
------------    ------ -----------                            --------- -------------        ------- ------------ -------- -------------
BSODMACHINE001  OK     \\Server01\Playground$\BSODMACHINE001      False                        False            0    False             0
BSODMACHINE002  OK     \\Server01\Playground$\BSODMACHINE002       True 9/4/2018 10:35:02 AM    True            2    False             0

Analyzing the .DMP Files

Now that all the .DMP files are in one place I can analyze them with cdb.exe from the Debugging Tools for Windows.

I settled on using an Run Script File instead of hard-coding the command the function allowing me to point to a specific file depending on the operation.  I had a few ‘analysis command files’ (aka run script files) with various commands within, but my core file contains only !analyze -v which is sufficient for my day-to-day tasks.

From there it was just a matter of looping through the directories for .DMP files, running cdb against them and storing the output in a text file for review.


# hold the cdb analysis results
[System.Collections.ArrayList]$AnalysisDetails = @()

# run script file / analysis command file
[string]$AnalysisCommandsFile = "\\Server01\Playground$\DebuggingTools\AutomaticAnalysis\Core.Analysis.1.txt"

# path to cdb.exe
[string]$CDBEXE = '\\Server01\Playground$\DebuggingTools\10\x64\cdb.exe'

# loop through each dmp file and analyze it
foreach($DMPFile in $(get-childitem -path '\\Server01\Playground$\BSODMACHINE002' -Filter *.dmp))
    {
        # generate a log for each dmp file named after both the dump fil and the run script file / analysis command file used
        $Log = (Split-Path -Path $DMPFile.FullName -Parent) + "\" + (Split-Path -Path $DMPFile.FullName -Leaf) + '_' + (Split-Path -Path $AnalysisCommandsFile -Leaf).ToString().Replace('.txt','.log')

        # run cdb against the .dmp fiile using the run script file / analysis command file
        $ServiceTiming = Measure-Command { &$CDBEXE -z "$($DMPFile.FullName)" -c "`$`$<$AnalysisCommandsFile;Q" | Tee-Object -FilePath $Log }

        # capture the exit code
        $CDBExitCode = $LASTEXITCODE

        # populate the analysis results into the array
        $AnalysisDetails += [pscustomobject]@{
            DumpFile = $DMPFile.FullName;
            Date = $DMPFile.LastWriteTime;
            CDBStatus = $CDBExitCode;
            AnalysisDuration = $ServiceTiming;
        }

        Remove-Variable Log,ServiceTiming,CDBExitCode -ErrorAction SilentlyContinue
    }

And you'll see a log file for each .DMP file in the directory:

BSODAnalysis-001

The output again provides some potentially useful stats:


$AnalysisDetails | Sort -Descending -Property Date | Format-Table -AutoSize

DumpFile                                                  Date                 CDBStatus AnalysisDuration
--------                                                  ----                 --------- ----------------
\\Server01\Playground$\BSODMACHINE002\090418-15656-01.dmp 9/4/2018 10:35:25 AM         0 00:00:41.0912218
\\Server01\Playground$\BSODMACHINE002\Memory.dmp          9/4/2018 10:35:02 AM         0 00:00:11.0954740
\\Server01\Playground$\BSODMACHINE002\070218-10000-01.dmp 7/2/2018 9:49:21 AM          0 00:00:29.8022249

And although this is pretty helpful as is, you’re left to open each .log file to see what’s going on.  But that’s no fun.

[Try to] Determine Probable Cause

The bugcheck analysis typically lists the file that was likely the cause of the BSOD right at the top of the log and since the formatting is consistent, we can just extract that from the file.

foreach($LogFile in $(Get-ChildItem -Path '\\Server01\Playground$\BSODMACHINE002' -Filter *.log))
    {
        $ProbablyCausedBy = (Select-String -Path $LogFile.FullName -Pattern "Probably caused by").Line.ToString().Replace('Probably caused by : ','')
        Write-Host "$($LogFile.FullName) BSOD probably caused by: $ProbablyCausedBy"
        Remove-Variable ProbablyCausedBy -ErrorAction SilentlyContinue
    }

That output looks like this:


\\Server01\Playground$\BSODMACHINE002\070218-10000-01.dmp_Core.Analysis.1.log BSOD probably caused by: vm3dmp.sys ( vm3dmp+25900 )
\\Server01\Playground$\BSODMACHINE002\090418-15656-01.dmp_Core.Analysis.1.log BSOD probably caused by: vm3dmp.sys ( vm3dmp+25760 )
\\Server01\Playground$\BSODMACHINE002\Memory.dmp_Core.Analysis.1.log BSOD probably caused by: vm3dmp.sys ( vm3dmp+25760 )

Putting it All Together

With this done, I can now throw an array of machines to kick off the whole process, walk away and come back to some meaningful information.

After much analysis, web scouring, testing and opening a case with VMware we confirmed the root cause of our VDI’s BSOD’ing was due to a flaw [bug] in our version of VMware Tools, 10.1.15.  Basically when someone reconnected to their session, if the display parameters had changed (e.g.: Started the VDI session on their home machine with a resolution of 1024×768 then came into the office, resumed their session with a resolution of 1920×1080; OR Possibly caused by a difference in DPI) it would cause the machine to BSOD.  The customer would never see a BSOD because of the nature of the VDI client – it would just look like a [spontaneous] restart.

The fix was to upgrade to VMware Tools 10.2.x.   But VDA 7.2.x wasn’t compatible with VMware Tools 10.2.x which meant upgrading the VDA to 7.4.x.
The correct remediation procedure looks like this:

  • Uninstall VDA 7.2.x
  • Uninstall VMware Tools 10.1.x
  • Reboot
  • Install VMware 10.2.x
  • Reboot
  • Install VDA 7.4.x
  • Reboot

If you’re using AppVolumes or a GPU then you’ll need to modify this accordingly because order of operations is key!

Because this process required multiple restarts, and more importantly killed one’s ability to reconnect to the machine, I created a Task Sequence to handle this.  Once the VDI’s were updated, they were rock solid.

The full .DMP analysis PowerShell script can be found here.

In Conclusion

Although this script may not be perfect, it meets my needs of simplifying what would normally be a manual process, allowing me to focus more on providing great timely customer service and just ‘getting it done‘ as efficiently as possible.  In fact, I’ve used this script to analyze .DMPs on physical assets making that process easy.  In a future version of this I’d like to intelligently handle processed .DMP files and logs so we’re not constantly analyzing the same set of files. (i.e.: After analysis move them into a ‘Processed’ directory or something)

I may have been able to simplify this a bit by using existing products, like Nir Sofir’s BlueScreen View or something else I’m not yet aware of.  But there’s often a challenge in introducing new software into an environment and many NirSoft utilities are classified as PUA’s so they’re killed with extreme prejudice.  So, I try to stick with the tools Microsoft provides to get the job done when it makes sense to do so.

Remember, this is not the way, it’s just a way.

Good Providence to you!

Advertisements

MDT Tutorial Part 11: Troubleshooting Part 7: Non-Fatal OSD Errors & Warnings

Living Table of Contents

What These Guides Are:
A guide to help give you some insight into the troubleshooting process in general.

What These Guides Are Not:
A guide to fix all issues you’re going to encounter.

We’re going to role-play a bunch of scenarios and try to work through them.  Remember in math where you had to show your work?  Well, what follows is like that which is why this post is [more than] a [little] lengthy.

 

[Some] Non-Fatal OSD Errors & Warnings:

  • No networking adapters found, the network drivers for your device ar enot present

  • An invalid SLShareDynamicLogging value of <path> was specified

  • Unable to Create WebService class

You made a bunch of changes to your lab environment, tested the CS.INI before imaging but after performing a B&C you see these errors/warnings:

Yikes a bunch of errors here!

Yikes a bunch of errors here!

  • No networking adapters found, The network drivers for your device are not present
  • An invalid SLShareDynamicLogging value of \\ITF1MDT01\DeploymentShare$\TSLogs\BnC-22404895 was specified.
  • 2x Unable to create WebService class

To be sure, you delete the logs for that machine, run another B&C and confirm the BDD.log was indeed present and getting populated.  But at the end of the second B&C you received the same error and inspected the BDD.log further:

Troubleshoot-031.PNG

Starting from the top of the log:

  • The”Apply Windows PE” Task Sequence step completed successfully as evidenced by “Event 41001 sent: LTIApply processing completed successfully.”
    .
  • Then the “Add mass storage drivers to sysprep.inf for XP and 2003” Task Sequence step completed successfully as evidenced by “Event 41001 sent: ZTIDrivers processing completed successfully.”
    • Furthermore, the process is able to reach and write to \\ITF1MDT01\DeploymentShare$\TSLogs\BnC-22404895 so we know it was good at this point.
      This is important!
      .
  • After that the “Execute Sysprep” Task Sequence step completed successfully as evidenced by “Event 41001 sent: LTISysprep processing completed successfully.”
    • And that process was also able to reach and write to \\ITF1MDT01\DeploymentShare$\TSLogs\BnC-22404895.
      This is also important!
      .
  • Then we get to the “Apply Windows PE (BCD)” Task Sequence Step and almost as soon as it starts we see our errors:
    • No networking adapters found, The network drivers for your device are not present
    • An invalid SLShareDynamicLogging value of \\ITF1MDT01\DeploymentShare$\TSLogs\BnC-22404895 was specified.

From here you hypothesize that it’s the sysprep process that is creating this problem.
And like a wise man frequently tells me: We gotta peel this onion!

  • Found the error text in ZTIUtility.vbs: “An invalid SLShareDynamicLogging value”
  • Just above the error code it, two functions are called:
    • ValidateConnection
    • VerifyPathExists
      .
  • ValidateConnection
    • Is what drops the “Validating connection to” text in the log
    • Calls the ValidateNetworkConnectivity function which
      • Executes the query ​select * from win32_NetworkAdapter where Installed = true and adaptertypeid = 0" and evaluates the count of the results
      • If the result count is 0, the function exits after dropping “No networking adapters found, The network drivers for your device are not present” text in the log.
        .
  • VerifyPathExists checks the path of SLShareDynamicLogging, creating the directory if missing.  We know the directory exists because the files are where we expect them to be.
    .
  • Finally, the line that generates the “An invalid SLShareDynamicLogging” log entry is part of an if block, and is generated if the directory doesn’t exist.

With a better understanding of the process, we may have a good idea as to what might be happening:

  1. everything’s fine up until sysprep
  2. sysprep might be removing the NIC drivers and thus the adapter doesn’t work
  3. when the ‘Apply Windows PE (BCD)’ Task Sequence step (LTIApply.wsf) is executed immediately after sysprep, it fails to connect to the share because of Step 2 above
  4. once in WinPE the NIC is once again fully functional so everything works as expected

But what about the two Unable to create WebService class warning?  Thinking back to the recent changes you made, one of them was enabling Monitoring.   A little sleuthing might take you to a post by Michael Niehaus on Troubleshooting MDT 2012 Monitoring via a TechNet response by Johan Arwidmark.

After going through the guide, it still doesn’t work so you disable Monitoring, reimage, and the error goes away.  You re-enable Monitoring, image again, get the same result.

The good news is you’ve confirmed Monitoring is “to blame” for those errors.

A closer inspection of the BDD.log shows it’s part of the same LTIApply step it and that’s when it clicks: If there’s no network connectivity, there’s no way for the WebService to succeed.

At the very least, this is a plausible cause.

So for these errors and warnings, it probably “is what it is” and likely not that big of a deal in the grand scheme of things.

FWIW: I can reproduce this behavior with MDT8443 & ADK 1703 on Gen 2 and Gen 1 VM’s using both the Network Adapter and Legacy Network Adapter.  : (

Is it Possible to Fix All of These Errors?

Probably – in fact I’m fairly certain I have an older lab environment where I do not have this problem.  But there’s also something to be said about the return on investment and the law of diminishing returns.

If I can consistently reproduce something, and it really does create problems, I personally believe there’s value in looking into further.  It may be a typo or faux pas on my part or perhaps a valid ‘bug’ for a certain scenarios.

If it’s a one-off, something I see one in 30, it may not be worth investing a significant amount of time & effort for so little reward.

Copypasta Closing

Hopefully these examples will help give you an idea of the overall troubleshooting process.  Most of the time the problems you’ll encounter will be caused by a typso, order of operations or a ‘known issue’ that requires a specific process to be followed.

As you make changes to your environment, here’s what I recommend:

  • Be diligent about keeping a change log so you can easily backtrack
  • Backup your CS.INI or Bootstrap.ini before you make any changes
  • Backup your ts.xml or unattend.xml (in DeploymentShare\Control\TaskSequenceID) before you make any changes
  • Introduce small changes at time with set checkpoints in between and set milestones markers where you backup core files (e.g cs.ini bootstrap.ini ts.xml unattend.xml etc) to help minimize frustration troubleshooting.

And if when you do run into some turbulence, upload relevant logs (at least smsts.log but be prepared to submit others depending on the issue) to a file sharing service like OneDrive, post on TechNet then give a shout to your resources on Twitter.

Good Providence to you!

Deployment Error Invalid DeploymentType value "" specified.  The deployment will not proceed.

MDT Tutorial Part 11: Troubleshooting Part 5: Invalid DeploymentType value “” specified. The deployment will not proceed.

Living Table of Contents

 

What These Guides Are:
A guide to help give you some insight into the troubleshooting process in general.

What These Guides Are Not:
A guide to fix all issues you’re going to encounter.

We’re going to role-play a bunch of scenarios and try to work through them.  Remember in math where you had to show your work?  Well, what follows is like that which is why this post is [more than] a [little] lengthy.

Invalid DeploymentType value “” specified. The deployment will not proceed.

You enabled the Windows Updates steps in the Build & Capture Task Sequence so you can have a fully patched Windows 10 v1511 WIM.

Troubleshoot-014.PNG

And you also updated your CustomSettings.ini so that your Build & Capture VM would set all the properties/variables but not start immediately.


[Settings]
Priority=MACAddress,GetAbbrModel,Build,Default
Properties=OfficeCode,AbbrModel

;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
; BEGIN MACADDRESS SECTION
; This is my Windows 10 v1511 build & capture VM
[00:15:5D:13:79:01]
SkipTaskSequence=NO
TaskSequenceID=BC151164ENT
SkipComputerName=NO
OSDComputerName=BnC-#UCase(Right(Replace(Replace("0000000%SERIALNUMBER%"," ","",1,-1,1),"-","",1,-1,1),8))#
SkipDomainMembership=NO
JoinWorkgroup=BnC-WrkGrp
SkipUserData=NO
SkipComputerBackup=NO
ComputerBackupLocation=NETWORK
BackupDir=Captures\%OSDComputerName%
BackupFile=%OSDComputerName%_%TaskSequenceID%_#year(date) & "-" & month(date) & "-" & day(date) & "_" & Hour(Now()) & Minute(Now())#.wim
SkipProductKey=NO
SkipLocaleSelection=NO
SkipTimeZone=NO
SkipAdminPassword=NO
SkipCapture=NO
DoCapture=YES
SkipBitLocker=NO
SkipSummary=NO

; END MACADDRESS SECTION
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
; BEGIN GETABBRMODEL SECTION
; Lets get the abbreviated model
[GetAbbrModel]
UserExit=jgp_GetAbbrModel.vbs
AbbrModel=#GetAbbrModel#
; END GETABBRMODEL SECTION
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
; BEGIN BUILD SECTION
; Set things here for use below
[Build]
OSDComputerName=%OfficeCode%-%AbbrModel%-#UCase(Right(Replace(Replace("0000000%SERIALNUMBER%"," ","",1,-1,1),"-","",1,-1,1),8))#
; END GETABBRMODEL SECTION
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
; BEGIN DEFAULT SECTION
[Default]
OSInstall=Y
; Skip Screen: Task Sequence
SkipTaskSequence=NO
; Skip Screen: Computer Details
SkipComputerName=NO
; Skip Screen: Computer Details
SkipDomainMembership=NO
; Skip Screen: Move Data and Settings & User Data (Restore)
SkipUserData=NO
; Skip Screen: Computer Backup
SkipComputerBackup=NO
; Skip Screen: Product Key
SkipProductKey=NO
; Skip Screen: Locale & Time
SkipLocaleSelection=NO
KeyboardLocale=en-US
UserLocale=en-US
UILanguage=en-US
; Skip Screen: Locale & Time
SkipTimeZone=NO
; https://msdn.microsoft.com/en-us/library/ms912391(v=winembedded.11).aspx
TimeZoneName=Eastern Standard Time
; Skip Screen: Administrator Password
SkipAdminPassword=NO
; Skip Screen: Capture Image
SkipCapture=NO
; Skip Screen: BitLocker
SkipBitLocker=NO
; Skip Screen: Ready to begin
SkipSummary=NO
; Skip Screen: R
SkipFinalSummary=NO
SLShare=%DeployRoot%\TSLogs
SLShareDynamicLogging=%DeployRoot%\TSLogs\%OSDComputerName%
EventService=http://ITF1MDT01:9800
; END DEFAULTSECTION
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Having learned your lesson from last time, you test the updated CustomSettings.ini & spent a few minutes verifying the output in the console.  Feeling confident, you boot into WinPE and when the Wizard I displays, verify all the defaults are set correctly.

This slideshow requires JavaScript.

You click begin and almost immediately the process halts with an obscure error:

Deployment Error Invalid DeploymentType value

Deployment Error Invalid DeploymentType value “” specified.  The deployment will not proceed.

You’d be forgiven for thinking you’ve made a mistake and I’d be willing to bet you’d figure this out but that road wouldn’t be fun.

Fortunately Michael Niehaus himself confirmed this is a bug (SRC1, SRC2) and there are two options to fixing/working around the bug:

  1. Easiest: Update your CS.INI with SkipProductKey=YES.
  2. Hardest (and arguably unsupported?):
    1. Open DeployWiz_ProductKeyVista.vbs in the Scripts directory
    2. Find: ​if oProperties("DeploymentType") = "UPGRADE" then
    3. Replace: If Property("DeploymentType") = "UPGRADE" then
    4. Save the file and try again
Note: There appears to be a plausible explanation of what’s happening here if you’re interested.

Since you rarely need to prompt for a Product Key – especially considering you can stick it in the unattend.xml or you go down the easy path and now the image kicks off.

Troubleshoot-028.PNG

Copypasta Closing

Hopefully these examples will help give you an idea of the overall troubleshooting process.  Most of the time the problems you’ll encounter will be caused by a typso, order of operations or a ‘known issue’ that requires a specific process to be followed.

As you make changes to your environment, here’s what I recommend:

  • Be diligent about keeping a change log so you can easily backtrack
  • Backup your CS.INI or Bootstrap.ini before you make any changes
  • Backup your ts.xml or unattend.xml (in DeploymentShare\Control\TaskSequenceID) before you make any changes
  • Introduce small changes at time with set checkpoints in between and set milestones markers where you backup core files (e.g cs.ini bootstrap.ini ts.xml unattend.xml etc) to help minimize frustration troubleshooting.

And if when you do run into some turbulence, upload relevant logs (at least smsts.log but be prepared to submit others depending on the issue) to a file sharing service like OneDrive, post on TechNet then give a shout to your resources on Twitter.

Good Providence to you!

MDT Tutorial Part 11: Troubleshooting Part 4: Task Sequence Variable is Being Overwritten

Living Table of Contents

 

What These Guides Are:
A guide to help give you some insight into the troubleshooting process in general.

What These Guides Are Not:
A guide to fix all issues you’re going to encounter.

We’re going to role-play a bunch of scenarios and try to work through them.  Remember in math where you had to show your work?  Well, what follows is like that which is why this post is [more than] a [little] lengthy.

Task Sequence Variable is Being Overwritten – The Case of the Incorrectly Named Log Folder

After [finally] fixing your computer naming issue, you decide to take a look at the live BDD.log just to keep an eye on things.  You hop into the TSLogs directory & find 2 directories that have the same time stamp:

Troubleshoot-008

According to your CS.INI the “real” log directory should be named after the machine so how did that other one get created?  You open the BDD.log in BLD-HPV-22404895 and it’s practically empty, but you do see that the property is being set to the other directory.

Troubleshoot-009.PNG

There are some clues in the BDD that lead you to above that point to a smoking gun.

You open the BDD.log in the other directory & scroll to the top & see a familiar set of lines: The same ones you see when testing the CustomSettings.ini manually:

Troubleshoot-011

While the machine is imaging you press F8 to launch a console, fire up CMTrace, open the smsts.log (in %temp%\smstslog), scroll to the top & search downwards for ‘dyn’:

Troubleshoot-012.PNG

Ok, you’re piecing it together:

  1. Initially it’s good, points to the proper location
  2. Then at some point it changes
  3. In the first, short, BDD.log you see ‘Task Sequence’ mentioned
  4. In the second, long, BDD.log you see lines that look like it’s processing the CustomSettings.ini, something that also happens during the Task Sequence,
  5. The smsts.log shows a step that sets a variable
  6. SLShareDynamicLogging isn’t a property (or variable) that is ‘last write wins’, so it can’t be a case of a rogue CustomSettings.ini

So maybe it’s the Task Sequence?

Troubleshoot-013

Bingo was his name.

 

Copypasta Closing

Hopefully these examples will help give you an idea of the overall troubleshooting process.  Most of the time the problems you’ll encounter will be caused by a typso, order of operations or a ‘known issue’ that requires a specific process to be followed.

As you make changes to your environment, here’s what I recommend:

  • Be diligent about keeping a change log so you can easily backtrack
  • Backup your CS.INI or Bootstrap.ini before you make any changes
  • Backup your ts.xml or unattend.xml (in DeploymentShare\Control\TaskSequenceID) before you make any changes
  • Introduce small changes at time with set checkpoints in between and set milestones markers where you backup core files (e.g cs.ini bootstrap.ini ts.xml unattend.xml etc) to help minimize frustration troubleshooting.

And if when you do run into some turbulence, upload relevant logs (at least smsts.log but be prepared to submit others depending on the issue) to a file sharing service like OneDrive, post on TechNet then give a shout to your resources on Twitter.

Good Providence to you!

Title: Windows Setup Body: Windows could not parse or process unattend answer file [C:windowsPantherunattend.xml] for pass [specialize]. The answer file is invalid.

MDT Tutorial Part 11: Troubleshooting Part 3: Windows could not parse or process unattend answer file [C:\windows\Panther\unattend.xml] for pass [specialize].  The answer file is invalid.

Living Table of Contents

 

What These Guides Are:
A guide to help give you some insight into the troubleshooting process in general.

What These Guides Are Not:
A guide to fix all issues you’re going to encounter.

We’re going to role-play a bunch of scenarios and try to work through them.  Remember in math where you had to show your work?  Well, what follows is like that which is why this post is [more than] a [little] lengthy.

Windows could not parse or process unattend answer file [C:\windows\Panther\unattend.xml] for pass [specialize].  The answer file is invalid.

Your last victory is short lived as the same error message appears and this time unattend.xml looks fine:

Troubleshoot-010.PNG

Stumped, you might search for ‘Microsoft-Windows-Shell-Setup’ which might lead you here:
https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup

As you review each section carefully the issue becomes clear: The computer name is more than 15 characters.

Copypasta Closing

Hopefully these examples will help give you an idea of the overall troubleshooting process.  Most of the time the problems you’ll encounter will be caused by a typso, order of operations or a ‘known issue’ that requires a specific process to be followed.

As you make changes to your environment, here’s what I recommend:

  • Be diligent about keeping a change log so you can easily backtrack
  • Backup your CS.INI or Bootstrap.ini before you make any changes
  • Backup your ts.xml or unattend.xml (in DeploymentShare\Control\TaskSequenceID) before you make any changes
  • Introduce small changes at time with set checkpoints in between and set milestones markers where you backup core files (e.g cs.ini bootstrap.ini ts.xml unattend.xml etc) to help minimize frustration troubleshooting.

And if when you do run into some turbulence, upload relevant logs (at least smsts.log but be prepared to submit others depending on the issue) to a file sharing service like OneDrive, post on TechNet then give a shout to your resources on Twitter.

Good Providence to you!

Title: Windows Setup Body: Windows could not parse or process unattend answer file [C:windowsPantherunattend.xml] for pass [specialize]. The answer file is invalid.

MDT Tutorial Part 11: Troubleshooting Part 2: Windows could not parse or process unattend answer file [C:\windows\Panther\unattend.xml] for pass [specialize].  The answer file is invalid.

Living Table of Contents

 

What These Guides Are:
A guide to help give you some insight into the troubleshooting process in general.

What These Guides Are Not:
A guide to fix all issues you’re going to encounter.

We’re going to role-play a bunch of scenarios and try to work through them.  Remember in math where you had to show your work?  Well, what follows is like that which is why this post is [more than] a [little] lengthy.

Windows could not parse or process unattend answer file [C:\windows\Panther\unattend.xml] for pass [specialize].  The answer file is invalid.

You boot your special VM, click the ‘Run the Deployment Wizard to install a new Operating System‘ button and it immediately starts.  Excellent!  It applies the OS, reboots and you’re faced with this error:

Title: Windows Setup Body: Windows could not parse or process unattend answer file [C:\windows\Panther\unattend.xml] for pass [specialize].  The answer file is invalid.

Windows could not parse or process unattend answer file [C:\windows\Panther\unattend.xml] for pass [specialize]. The answer file is invalid.

Well this is strange, because you didn’t touch the unattend.xml so what gives?
Fortunately, this dialog provides some meaningful insight:

    • The unattend file is C:\Windows\Panther\unattend.xml
    • The specific area is the specialize pass

Press SHIFT+F10 here to open a command prompt and then open C:\Windows\Panther\unattend.xml with notepad

Troubleshoot-005

You search for ‘specialize’ and after taking a very close look see that your computer name is incorrect.  It should be some two or three character prefix not %OfficeCode%.

Troubleshoot-006

Since that is set via the CS.INI, you run the CustomSettings.ini test again and now you see what was missed before:

Troubleshoot-007.PNG

You review the CS.INI and find your problems

  1. You didn’t define the OfficeCode property: Wasn’t added to the Properties line
  2. You didn’t set a value for OfficeCode.

With that fixed, you run the test again, the variable is populated and as you reimage the machine, you see it is named correctly in the logs.

Copypasta Closing

Hopefully these examples will help give you an idea of the overall troubleshooting process.  Most of the time the problems you’ll encounter will be caused by a typso, order of operations or a ‘known issue’ that requires a specific process to be followed.

As you make changes to your environment, here’s what I recommend:

  • Be diligent about keeping a change log so you can easily backtrack
  • Backup your CS.INI or Bootstrap.ini before you make any changes
  • Backup your ts.xml or unattend.xml (in DeploymentShare\Control\TaskSequenceID) before you make any changes
  • Introduce small changes at time with set checkpoints in between and set milestones markers where you backup core files (e.g cs.ini bootstrap.ini ts.xml unattend.xml etc) to help minimize frustration troubleshooting.

And if when you do run into some turbulence, upload relevant logs (at least smsts.log but be prepared to submit others depending on the issue) to a file sharing service like OneDrive, post on TechNet then give a shout to your resources on Twitter.

Good Providence to you!

MDT Tutorial Part 10: CustomSettings.ini Validation Testing & Troubleshooting Part 1

Living Table of Contents

 

Today’s Agenda: Troubleshooting

  • CustomSettings.ini Validation Testing
  • Troubleshooting OSD Issues

Recommended Reading

CustomSettings.ini Validation Testing

If you haven’t already done so, go ahead and make some useful edits to your CustomSettings.ini.  Since my aim is to have a dedicated ‘build’ machine that boots and automatically images the proper Task Sequence, this is what my CS.INI looks like now:


[Settings]
Priority=MACAddress,GetAbbrModel,Build,Default
Properties=AbbrModel

;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
; BEGIN MACADDRESS SECTION
; This is my Windows 10 v1511 build VM
[00:15:5D:13:79:01]
SkipTaskSequence=YES
TaskSequenceID=B151164ENT
SkipComputerName=YES
SkipDomainMembership=YES
JoinWorkgroup=BLD-WrkGrp
SkipUserData=YES
SkipComputerBackup=YES
SkipProductKey=YES
SkipLocaleSelection=YES
SkipTimeZone=YES
SkipAdminPassword=YES
SkipCapture=YES
SkipBitLocker=YES
SkipSummary=YES
; END MACADDRESS SECTION
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
; BEGIN GETABBRMODEL SECTION
; Lets get the abbreviated model
[GetAbbrModel]
UserExit=jgp_GetAbbrModel.vbs
AbbrModel=#GetAbbrModel#
; END GETABBRMODEL SECTION
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
; BEGIN BUILD SECTION
; Set things here for use below
[Build]
OSDComputerName=%OfficeCode%-%AbbrModel%-#UCase(Right(Replace(Replace("0000000%SERIALNUMBER%"," ","",1,-1,1),"-","",1,-1,1),8))#
; END GETABBRMODEL SECTION
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
; BEGIN DEFAULT SECTION
[Default]
OSInstall=Y
SkipTaskSequence=NO
SkipComputerName=NO
SkipDomainMembership=NO
SkipUserData=NO
SkipComputerBackup=NO
SkipProductKey=NO
SkipLocaleSelection=NO
SkipTimeZone=NO
SkipAdminPassword=NO
SkipCapture=NO
SkipBitLocker=NO
KeyboardLocale=en-US
UserLocale=en-US
UILanguage=en-US
; https://msdn.microsoft.com/en-us/library/ms912391(v=winembedded.11).aspx
TimeZoneName=Eastern Standard Time
SLShare=%DeployRoot%\TSLogs
SLShareDynamicLogging=%DeployRoot%\TSLogs\%OSDComputerName%
; END DEFAULTSECTION
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Following the methods outlined in the recommended reading section, set up an area for testing your CustomSettings.ini.  When I execute my test, it runs, I see a bunch of data:

There are no obvious errors, I see see some custom properties and various built-in properties getting set so it looks good.  Time to execute for real!

Troubleshooting – Part 1

Troubleshooting MDT (and SCCM) is something of an art and this is not a once-size-fits-all silver bullet post.

I ran across this post SCCM 2012 – How to catch errors in Task Sequence around the same time I started using MDT and found it greatly helped me to hone in on OSD issues.  Because it has worked well for me, I’m recommending it – and others like it – to you.

Task Sequence Setup

Take a look at the links at the top to see how others are setting up their Task Sequences and adjust to suit your needs but here’s a basic example:

Troubleshoot-041.PNG

Right now when your Task Sequence fails you see this dialog:

Troubleshoot-042.PNG

This dialog is incredibly helpful without making any adjustments and you should ge able to get an idea as to what went wrong without having to go super deep into the logs.  But once you get into a production scenario, you’ll likely suppress this dialog and I find that having the Try/Catch steps in the Task Sequence makes it easier from a log reviewing perspective, especially after the Task Sequence gets busy.

Crack open the smsts.log and immediately you see red:

Troubleshoot-043.PNG

With the Try/Catch model you can easily hone in on the offending step:

  1. Scroll to the bottom of the log
  2. Search for key text in the log
    • Option 1: Search for: Try) ignored
    • Option 2: Search for: Catch) has been

Either one will get you just a few short lines away from the failure, so scroll up a bit and it should become apparent.

Troubleshoot-044.PNG

  • The blue line above is the ‘Catch) has been‘ match.
  • The first red line above that is the ‘Try) ignored‘ line
  • Just above that we see the second red line which is the actual failure:
    Failed to run the action: This will break it.
  • Above that are the details for that specific step.

This is obviously a very simple example, but the process is the same for all errors:

  • Review the smsts.log
  • Find the actual error
  • Evaluate if it’s a problem specific to that step OR if it was caused by an environmental issue such as dependencies.
  • Review other logs as necessary based on what you’re seeing in the smsts (e.g.: domain join failure)

In Closing

I don’t expect you to be an expert at this point, but I hope it and the links in the recommended reading section have helped to get you a little more comfortable with searching the smsts.log for errors.

When doing BnC’s I like to keep my changes small and modular:

  • Test that the basic BnC works fine: OS is installed, sysprep & capture is succesful
  • Add Windows Updates into the mix & repeat the test
  • Prepare your application payload:
    • for some complex applications you’ll rely on scripts so test them outside of the Task Sequence to confirm they are syntactically correct
    • for simple installations make sure you have the correct command line arguments
    • once installed validate the installation & configuration
  • Introduce applications a few at a time doing BnC’s to ensure nothing is broken.
  • Set milestones for yourself so you don’t have to go back to square one
  • Backup files (ts.xml, unattend.xml, scripts etc) before you make any changes.

Good Providence to you!

MDT Tutorial Part 9: Logging

Living Table of Contents

 

Today’s Agenda:

  • Centralized Logging
  • Enable Logging
  • Log Locations

At some point you’re going to run into a problem with your imaging process and knowing where to look to get some answers is going to be paramount.

Recommended Reading:

Centralized Logging

I prefer to keep logs in a central location so they’re easy to find when needed, and for that, we’ll create a new share on our MDT server.  Run the below from the MDT server.


New-Item -Path "C:\DeploymentShare\TSLogs" -ItemType directory

New-SmbShare -Name "TSLogs$" -Path "C:\DeploymentShare\TSLogs" -FullAccess Administrators

Enable Logging

Open your CustomSettings.ini and find a suitable place to add the entries for SLShare and SLShareDynamicLogging depending on your preferred scenario.  Either is fine, just depends on your preference and environment.

Hard Coded Path


SLShare=\\MDTServer\TSLogs$

SLShareDynamicLogging=\\MDTServer\TSLogs$

Relative Path


SLShare=%DeployRoot%\TSLogs

SLShareDynamicLogging=%DeployRoot%\TSLogs

Set Logging via Task Sequence

Alternatively you could add steps to the Task Sequence itself to set those variables to their appropriate values, be it hard coded or relative.

Logging-001

Get Creative!

You can get pretty creative, and for automated deployments, I usually default to something like this:


SLShare=%DeployRoot%\TSLogs\%TaskSequenceID%

SLShareDynamicLogging=%DeployRoot%\TSLogs\%TaskSequenceID%\%OSDComputerName%

This way I can see what Task Sequence a particular machine ran.
But, to each their own.

SLShare vs SLShareDynamicLogging

Both values do different things:

  • When the SLShare property is set, MDT will copy the deployment logs to that location in a directory named after the computer, pulled from the %OSDComputerName% Task Sequence property (aka variable).  So if you named your machine PC-1511amd64 the full path to find the logs will be \\MDTServer\TSLogs$\PC-1511amd64.
    .
  • The SLShareDynamicLogging property is used for real-time debugging as ALL MDT logs will be written to that file during the Task Sequence.  It will create a file named BDD.log in the specified directory which means if you set SLShareDynamicLogging it to \\MDTServer\Logs you’ll find a file named BDD.log in that directory.  This obviously presents a problem if multiple machines are being imaged at the same time: they’d all be logged in the same file which might make it challenging to follow along.  This is why I recommend appending \%OSDComputerName% to the path so that the BDD.log is in the ‘correct’ directory.
    For our purposes, you can leave this property enabled, but please note this enabling SLShareDynamicLogging does add a bit of overhead, so in a production environment:

    • Only enable it when actually actively troubleshooting an issue and
    • Ideally have it log to a location close to the machine being imaged versus a remote server to avoid traversing the WAN

Putting It All Together

With the CustomSettings.ini updated, image a machine and check your log directory for a folder structure:

Logging002

  • The %OSDComputerName% directory is an artifact caused by the fact that the property (or variable) OSDComputerName wasn’t set at the time the SLShareDynamicLogging property was processed in the CustomSettings.ini.  In my production environment, I have logic in the CustomSettings.ini to properly name the machine based on specific criteria so that by the time we get to the area where SLShareDynamicLogging is assigned, the OSDComputerName property (aka variable) is set resulting in properly named directories.  However in our lab, this logic doesn’t exist (yet), hence why that directory exists.  To fix this, just add something like OSDComputerName=LAB-%SerialNumber% to your CustomSettings.ini and please note that the OSDComputerName property (variable) does not need to be declared.
    .
  • The other directory is the correct directory and it contains the BDD.log that’s actively being updated.

While a machine is imaging, open the BDD.log (in the latter directory mentioned above) with CMTrace from your MDTServer (or whatever machine you’re working on) and you will see live updates:

Logging-003

When the Task Sequence is finished go back into the log directory for that machine and you will to find the logs that were copied up by MDT:

Logging-004

Log Locations

MDT Logs can be a little challenging to locate initially so I recommend you study up those links mentioned in the recommended reading section above.

  • In WinPE & the disk is NOT partitioned:
    • X:\MININT\SMSOSD\OSDLOGS
      .
  • In WinPE & the disk IS partitioned:
    • C:\MININT\SMSOSD\OSDLOGS
    • X:\MININT\SMSOSD\OSDLOGS (very small amount)
      .
  • In WinPE & Task Sequence is running
    • smsts.log will be in X:\Windows\Temp\SMSTSLog
    • All other logs will be in their respective directories mentioned above
      .
  • In Windows & Task Sequence is running
    • smsts.log will be in C:\Users\Administrator\AppData\Local\Temp\SMSTSLog
    • C:\MININT\SMSOSD\OSDLOGS

Keep in mind there are other non-MDT logs you’ll probably need to review that are not listed here.

In Closing

You now have some historical data to dig into if something goes wrong, which will hopefully be few & far in between!

Good Providence to you!

MDT Tutorial Part 6: Customizing Boot Media

Living Table of Contents

 

Today’s Agenda:

  • Adding Files to Boot Media
  • Adding a Cool/Corporate Background
  • Adding Features to Boot Media

Although you can deploy an image with this boot media and so some basic tasks, we can add some additional, and very useful, functionality with ease.

Adding Files to Boot Media

In Part 5 we verified the changes we made to the Bootstrap.ini by not only experiencing it, as in the case of missing authentication prompt and the text customization, but also by viewing the BDD.log in notepad.  For most people that log might as well have looked like this:

If I said it would get easier with a little practice I wouldn’t be lying, but fortunately for us we can have access to a log viewer that’ll make the log viewing process a breeze.

To start, I recommend creating a folder structure to store all the files you want to add and I strongly urge you to separate between architectures.

If you’re remote:

mkdir \\MDTServer\DeploymentShare$\Boot\ExtraFiles\x64\Windows\System32
mkdir \\MDTServer\DeploymentShare$\Boot\ExtraFiles\x86\Windows\System32

If you’re on the MDT Server:

mkdir C:\DeploymentShare\Boot\ExtraFiles\x64\Windows\System32
mkdir C:\DeploymentShare\Boot\ExtraFiles\x86\Windows\System32

The reason is that the 64-bit WinPE media can only run 64-bit EXE’s and not 32-bit EXE’s.  Attempting to run a 32-bit EXE will result in an error similar to the following:

AddFiles-001

Be mindful of that when you select the files you want to bake into your boot media so you’re not bitten by the “Bitness Bug”!  Also, don’t go crazy because the more files you add, the larger the boot media.  Add just enough so you have what you need, not what you might need.

So just carve out the folder structure you want and drop the files wherever you need them to be.

CMTrace

These days CMTrace is the default go-to log viewer and is available publically:

  1. System Center 2012 R2 Configuration Manager Toolkit
  2. SCCM Evaluation ISO in
    1. SMSSETUP\TOOLS directory
    2. SMSSETUP\OSD\bin\I386
    3. SMSSETUP\OSD\bin\x64

However, as pointed out by Johan Arwidmark although the tool is ‘freely available’ it is not free as the EULA licensing states:

INSTALLATION AND USE RIGHTS. You may install and use any number of copies of the software on your devices running validly licensed copies of Microsoft System Center 2012 or later.

While disappointing, it is free to use during your evaluation.  So to quote Cathy Moya:

For anyone just using MDT, hey, install the eval copy and see what you’re missing with Config Manager. 🙂

That said once you have your eval setup you can move on with grabbing the EXE.  It’s probably easiest to grab it from the SCCM Evaluation ISO in the SMSSETUP\OSD\bin directories: I386 for x86 or 32-bit version and x64 for the 64-bit version.  If you’re using the toolkit hower:

  1. Download the MSI
  2. Run the installer (or perform an administrative installation)
  3. Copy the CMTrace.exe in the ClientTools folder
  4. Paste it in \\MDTServer\DeploymentShare$\Boot\ExtraFiles\x86\Windows\System32

This nets you the 32-bit version of CMTrace which is good for 32-bit boot media, but what about the 64-bit executable?  If you’re on a 64-bit machine you can:

  1. Run the CMTrace.exe in the ClientTools folder
  2. Open Task Manager
  3. Go to the Processes tab
  4. Sort by Apps and look for CMTrace_amd64.exe
  5. Secondary mouse click it and select ‘Open file location
  6. It will take you to %Temp% and point you to a file named TRAXXXX.tmp where XXXX are four random characters like 9644 or 1CD7.
  7. Copy the file, for example, TRA9644.tmp
  8. Paste it in \\MDTServer\DeploymentShare$\Boot\ExtraFiles\x64\Windows\System32
  9. Rename TRA9644.tmp to CMTrace.exe

Note: The above step works on Windows 10 and Server 2016; should work on Windows 8 and Server 2012 R2; if not use Process Explorer (or Process Monitor) to figure out where it’s running from.

Open the Deployment Workbench, go to your Deployment Share properties and click on the ‘Windows PE’ tab which will take you to the ‘General’ subtab.  Near the bottom look for ‘Extra directory to add’ next to an empty field where you can point to a location that contains the extra files you wish to add.  Type or browse to the path you created above and click Apply.

AddFiles-002.PNG

Once you finish with x86, at the top of the properties window just under the ‘Windows PE’ tab is a ‘Platform’ drop down.  Click it, select x64 and repeat your change there as well.

Adding a Cool/Corporate Background

While you’re on Extra Files tab, let’s add a Cool/Corporate background to the media!
This is totally optional but many do find it useful.

On the same ‘General’ subtab of the ‘Windows PE’ tab of the Deployment Share properties is a ‘Custom background bitmap file’ field that’s already filled in.  Just substitute that bitmap with one of your own.

AddBackground-001.PNG

Remember to also add the image to your x64 boot media via the ‘Platform’ drop down!

Adding Features to Boot Media

The WinPE boot media is lacking some key features we’re going to want to use so we need to fix that by adding some Optional Components.

Up until now we’ve been working in the ‘General’ subtab of the ‘Windows PE’ tab of the Deployment Share properties and we’re now going to move to the ‘Features’ subtab under the same ‘Windows PE’ tab.

AddFeatures-001

The Features available in this list are pulled from supported .CABs in

  • C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs
  • C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\x86\WinPE_OCs
  • C:\DeploymentShare\Tools\x64
  • C:\DeploymentShare\Tools\x86

Just like before when adding files, add only what you need, and I recommend:

Software Assurance Subscribers Only

If you have an active SA subscription you can download MDOP and add DaRT to your boot media.

  1. Download the MDOP ISO
  2. Mount the ISO
  3. Go into the DaRT\DaRT 10\Installers\Language (e.g.: en-us)
  4. Go into the appropriate architecture subfolder and run the MSI (or perform an administrative installation)
  5. Go into C:\Program Files\Microsoft DaRT\v10
  6. Copy the two Tools .CAB files and place them into the appropriate architecture directory in C:\DeploymentShare\Tools (i.e.: Toolsx64.cab into C:\DeploymentShare\Tools\x64)
  7. Go back to the ‘Windows PE’ tab of your your Deployment Share properties
  8. Go into the ‘Features’ subtab and check ‘Microsoft Diagnostics and Recovery Toolkit (DaRT)’
  9. Repeat for the x64 platform

Updating Boot Media

Once you’ve finished customizing your boot media, it’s time to update the boot media, so whip out your PowerShell code you pulled from before and run it in an elevated PowerShell console or PowerShell ISE.


Import-Module "C:\Program Files\Microsoft Deployment Toolkit\bin\MicrosoftDeploymentToolkit.psd1"
New-PSDrive -Name "DS001" -PSProvider MDTProvider -Root "C:\DeploymentShare"
update-MDTDeploymentShare -path "DS001:" -Verbose

From there, boot your updated ISO and you will see all of your changes:

UpdateBootMedia-001.PNG

  1. PowerShell is present
  2. Storage cmdlets present
  3. CMTrace is present
  4. Cool Background
  5. SA Subscribers: DaRT is present

In Closing

You now have feature rich bootable media that you can leverage to your advantage for various tasks related to OSD.

Good Providence to you!

Facilitating KMS Activations

Occasionally we run into situations where Windows or the Office Suite fails to activate.

The solution: Use the built-in product-specific scripts to activate.

The problem: Many people in IT are not aware of these scripts, and thus how to use them, which results in a guaranteed call or email to the appropriate team.

Since I’m all about empowering people, I figured I’d put together a little script to help facilitate all this.

Enter: KMSActivate-MicrosoftProducts

I probably could have just left it as ‘Activate-MicrosoftProducts’ but I wanted to make sure potential users knew this was specifically for KMS scenarios, not MAK which has it’s own procedure.


[cmdletbinding()]
Param
    (
        [Parameter(Mandatory=$false)]
            [Switch]$ActivateOS = $true,

        [Parameter(Mandatory=$false)]
            [Switch]$ActivateOffice = $true
    )

Function Check-IfRunningWithAdministratorRights { If (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]“Administrator”)) { Write-Warning “ERROR: You DO NOT have Administrator rights to run this script!`nPlease re-run this script as an Administrator!”; Break } }

Function Get-KMSHost { try { return (nslookup -type=srv _vlmcs._tcp | ? { $_ -like '*svr hostname*' }).Replace('svr hostname','').Replace('=','').Trim() } catch { throw $_ } }

Function Get-KMSClientSetupKey
    {
        # https://technet.microsoft.com/en-us/library/jj612867(v=ws.11).aspx
        [cmdletbinding()]
        Param
            (
                [Parameter(Mandatory=$false)]
                    [string]$OperatingSystemCaption = (Get-CimInstance -ClassName win32_operatingsystem).caption
            )

        Switch -Wildcard ($OperatingSystemCaption)
            {
                '*Windows Server 2016 Datacenter*' { return 'CB7KF-BWN84-R7R2Y-793K2-8XDDG' }

                '*Windows Server 2016 Standard*' { return 'WC2BQ-8NRM3-FDDYY-2BFGV-KHKQY' }

                '*Windows Server 2016 Essentials*' { return 'JCKRF-N37P4-C2D82-9YXRT-4M63B' }

                '*Windows 10 Enterprise*' { return 'NPPR9-FWDCX-D2C8J-H872K-2YT43' }

                '*Windows 7 Enterprise*' { return '33PXH-7Y6KF-2VJC9-XBBR8-HVTHH' }

                default { write-host "ERROR INVALID OPERATING SYSTEM CAPTION: $_"; throw }
            }
    }

Function KMSActivate-OperatingSystem
    {
        [cmdletbinding()]
        Param
            (
                [Parameter(Mandatory=$false)]
                    [string]$KMSHost = $(Get-KMSHost),

                [Parameter(Mandatory=$false)]
                    [string]$KMSClientSetupKey = $(Get-KMSClientSetupKey)
            )

        Start-Process -FilePath 'cscript' -ArgumentList "/nologo `"$env:windir\System32\slmgr.vbs`" -dlv" -Wait

        Start-Process -FilePath 'cscript' -ArgumentList "/nologo `"$env:windir\System32\slmgr.vbs`" -skms $KMSHost" -Wait

        Start-Process -FilePath 'cscript' -ArgumentList "/nologo `"$env:windir\System32\slmgr.vbs`" -ipk $KMSClientSetupKey" -Wait

        Start-Process -FilePath 'cscript' -ArgumentList "/nologo `"$env:windir\System32\slmgr.vbs`" -ato" -Wait

        Start-Process -FilePath 'cscript' -ArgumentList "/nologo `"$env:windir\System32\slmgr.vbs`" -dlv" -Wait
    }

Function KMSActivate-OfficeSuite
    {
        [cmdletbinding()]
        Param
            (
                [Parameter(Mandatory=$false)]
                    [string]$KMSHost = $(Get-KMSHost)
            )

        #write-host "KMSHost [$KMSHost]"

        [System.Collections.ArrayList]$OfficeInstallationDirs = @()
        foreach($ProgFilePath in $env:ProgramFiles,${env:ProgramFiles(x86)})
            {
                if(!(Test-Path -Path $ProgFilePath -PathType Container)) { continue }
                foreach($OfficeVersion in (gci "$ProgFilePath\Microsoft Office" -Filter Office* -ErrorAction SilentlyContinue)) { $OfficeInstallationDirs += $OfficeVersion.Fullname }
            }

        foreach($OfficeInstallationDir in $OfficeInstallationDirs)
            {
                if(!(Test-Path -Path "$OfficeInstallationDir\ospp.vbs" -PathType Leaf)) { continue }

                Start-Process -FilePath 'cscript' -ArgumentList "/nologo `"$OfficeInstallationDir\ospp.vbs`" /dstatusall" -Wait

                Start-Process -FilePath 'cscript' -ArgumentList "/nologo `"$OfficeInstallationDir\ospp.vbs`" /sethst:$KMSHost" -Wait

                Start-Process -FilePath 'cscript' -ArgumentList "/nologo `"$OfficeInstallationDir\ospp.vbs`" /act" -Wait

                Start-Process -FilePath 'cscript' -ArgumentList "/nologo `"$OfficeInstallationDir\ospp.vbs`" /dstatusall" -Wait
            }
    }

Check-IfRunningWithAdministratorRights

if($ActivateOS -eq $true) { KMSActivate-OperatingSystem }

if($ActivateOffice -eq $true) { KMSActivate-OfficeSuite }

 

The script will attempt to:

  • Confirm it’s running elevated otherwise it’ll quit
  • Determine the KMS server (or use the one supplied)
  • Determine the OS to set the correct client setup key
  • Determine the version(s) of Office installed (if any)
  • Activate Windows 7, 10 and a few flavors of Server 2016
  • Activate any various versions of Office if present

Not saying this is the best way – just a way.  I merely wanted a turn-key solution for our IT staff and this was what made sense as it solves some of the more infrequent issues that occasionally arose.

Good Providence to you!

Generate WindowsUpdate.Log Without Get-WindowsUpdateLog

Just like knowing that a shrimps heart is located in it’s head area (thorax) you can file this tidbit under useless facts.

If you find yourself in a situation where you need to convert some Windows Update .ETL files into human readable format and the Get-WindowsUpdateLog PowerShell cmdlet isn’t available for whatever reason, you can use TraceFmt.exe to do this for you.

The TraceFmt utility, available through both the Windows Software Development Kit (SDK) and Windows Driver Kit (WDK), takes the details in the trace logs and outputs a human-readable text file containing the formatted trace messages.

Usage:


tracefmt.exe -o "%UserProfile%\Desktop\TraceFmt-WindowsUpdate.log" %SystemRoot%\Logs\WindowsUpdate\WindowsUpdate.20171002.085155.537.1.etl -r srv*%SystemDrive%\Symbols*https://msdl.microsoft.com/download/symbols

Output:


Setting log file to: C:\windows\logs\WindowsUpdate\WindowsUpdate.20171002.085155.537.1.etl
Examining C:\Program Files (x86)\Windows Kits\10\bin\10.0.15063.0\x64\default.tmf for message formats,  3 found.
Searching for TMF files on path: C:\Program Files (x86)\Windows Kits\10\bin\10.0.15063.0\x64
Logfile C:\windows\logs\WindowsUpdate\WindowsUpdate.20171002.085155.537.1.etl:
        OS version              10.0.14393  (Currently running on 10.0.14393)
        Start Time              2017-10-02-08:51:55.537
        End Time                2017-10-02-09:01:57.790
        Timezone is             @tzres.dll,-112 (Bias is 300mins)
        BufferSize              4096 B
        Maximum File Size       128 MB
        Buffers  Written        3
        Logger Mode Settings    (11002009) ( sequential newfile paged)
        ProcessorCount          1

Processing completed   Buffers: 3, Events: 70, EventsLost: 0 :: Format Errors: 0, Unknowns: 7

Event traces dumped to C:\Users\perkinsjg\Desktop\TraceFmt-WindowsUpdate.log
Event Summary dumped to C:\Users\perkinsjg\Desktop\TraceFmt-WindowsUpdate.log.sum

 

Comparison

TraceFMT:

TraceFMTWindowsUpdateLog.png

Get-WindowsUpdateLog:

Get-WindowsUpdateLog

In Closing

The TraceFmt generated log file will not be identical to the one generated by the Get-WindowsUpdateLog PowerShell cmdlet; but it’ll help in a pinch!

For now, I bid you Good Providence!