Windows 1709 In Place Upgrade Bug

Thanks to Johan Arwidmark and Dan Vega for pointing me out to this bug. It took me a while to set up a scenario where it could reproduce, but it’s a good bug. Windows 10 In-Place Upgrade is an important new feature of Windows 10, and it’s good to have MDT support it.

The Bug

When upgrading to Windows 10 Version 1709 using the built-in MDT “Standard Client Upgrade Task Sequence”, you will get an error during OS upgrade within a MDT Wizard page that shows something like:

A VBScript Runtime Error has occurred:
Error: 500 = Variable is undefined

VBScript Code:
-------------------
IsThereAtLeastOneApplicationPresent

At this point, the OS *has* been upgraded, but the Task Sequence can no longer continue.

The Background

During OS upgrades, MDT needs a way to hook into the OS Installation process when done and continue installation tasks in the New OS.

Before In-Place upgrades, MDT would perform this by adding in a custom step into the unattend.xml file. Perhaps you have seen this segment of code before:

<FirstLogonCommands>
 <SynchronousCommand wcm:action="add">
  <CommandLine>wscript.exe %SystemDrive%\LTIBootstrap.vbs</CommandLine>
  <Description>Lite Touch new OS</Description>
  <Order>1</Order>
 </SynchronousCommand>
</FirstLogonCommands>

Windows would run the LTIBootStrap.vbs script, which would call the LiteTouch.wsf script, which would find the existing Task Sequence environment and kick of the remaining steps within the “State Restore” of the Task Sequence.

For the Windows 10 In-Place upgrade process, instead of processing the unattend.xml file, they have their own method of calling back into our LiteTouch environment using a SetupComplete.cmd file. This SetupComplete.cmd is responsible for finding our LiteTouch script and calling it.

The Analysis

It took me a while to setup a repro scenario (my test lab is configured for new-computer scenarios with the Windows 10 Eval bits, which can’t be used in an In-Place upgrade scenario). But I was able to reproduce the issue, and I got the bug, and was able to get the bdd.log file for analysis.

The challenge here is that during In-Place upgrade, I can’t open a Cmd.exe window for debugging using F8 or Shift-F10. Instead I hard coded a “start cmd.exe” line into the SetupComplete.cmd file.

What I observed is that the Window being displayed was the ZTIGather.wsf progress screen. LiteTouch.wsf will kick off the ZTIGather.wsf script early in the process, and will show a customized version of the LiteTouch wizard as a progress dialog. Well clearly the wizard isn’t working properly. But after closer analysis, ZTIGather.wsf shouldn’t be running AT ALL. For some reason, LiteTouch.wsf didn’t recall that it was in the MIDDLE of a task sequence, and that it should just directly go back to the TS in progress.

MDT has two methods for storing variables. When within the SMS Stand Alone Task Sequencing Engine, MDT LiteTouch scripts will read the SMS variables through the Microsoft.SMS.TSEnvironment COM object. But if ZTIUtility.vbs can’t open the variable store, it will store variables locally in the Variables.dat file.

Finally, after getting a powershell.exe window, creating the Microsoft.SMS.TSEnvironment, and making a couple of test calls to verify the contents of the variable store, a surprise. All variables returned empty *Successfully* (which is bad) but writes caused an Exception (which is correct). Since we were not running the SMS Stand Alone Task Sequencing Engine, all calls should have caused an exception.

Why is the Microsoft.SMS.TSEnvironment COM object registered, but not working? Well, that’s still under investigation, but now we can work on a fix/work around for MDT!!!

The Fix

The fix (Not written by me), is to force the Microsoft.SMS.TSEnvironment COM object to unregister before calling LiteTouch.wsf. This can be done by a simple call at the start of the SetupComplete.cmd script:

:: Workaround for incorrectly-registered TS environment
reg delete HKCR\Microsoft.SMS.TSEnvironment /f

The issue has been acknoleged by Microsoft, and this fix is currently targeted for the next release of MDT.

The Code

Thanks!

k

Advertisements

Make DisMount-DiskImage work

TL;DR – DisMount-DiskImage doesn’t work the same it did in Windows Server 2012 R2, here is how to make it work in Windows 10 and Server 2016.

Dirty Boy

OK, with the release of Windows 1709, I’ve been downloading all sorts of *.ISO images from MSDN to try out the latest features, kits, and support utilities. As I develop scripts to auto mount and extract the contents, sometimes I leave the ISO images mounted, so I’ll need to clear everything off before I begin a new test run.

I developed a new powershell script function to do all of this for me: Dismount-Everything. I had the VHDX part working, but somehow the DVD part wasn’t working well.

I specifically wanted to dismount ISO images, even though I might now recall the path where they came from, even though the drive letter should be easily visible.

Blogs

I went to a couple of blog sites to find out how to dismount ISO images, and got some hits.

https://rcmtech.wordpress.com/2012/12/07/powershell-mounting-and-dismounting-iso-images-on-windows-server-2012-and-windows-8/

https://superuser.com/questions/499264/how-can-i-mount-an-iso-via-powershell-programmatically

But on the superuser site, the author suggests that you should be able to take the output from get-volume and pipe it into Get-DiskImage, but that was not working for me.

Get-Volume [Drive Letter] | Get-DiskImage | Dismount-DiskImage

No matter what, Get-DiskImage didn’t like the volume Path.

Get-Volume would output:

\\?\Volume{1f8dfd40-b7ae-11e7-bf06-9c2a70836dd4}\

but Get-DiskImage would expect:

\\.\CDROM1

Well, Windows NT will create virtual shortcuts between long path names like \\?\volume and something more readable like \\.\CDROM1, so I assumed there was an association there.

Well after testing I found out that this command didn’t work:

get-diskimage -devicepath \\?\Volume{1f8dfd40-b7ae-11e7-bf06-9c2a70836dd4}\

But this command did:

get-diskimage -devicepath \\?\Volume{1f8dfd40-b7ae-11e7-bf06-9c2a70836dd4}

Turns out that I just needed to strip out the trailing \.

Easy!

Code

Get-Volume | 
  Where-Object DriveType -eq 'CD-ROM' |
  ForEach-Object {
    Get-DiskImage -DevicePath  $_.Path.trimend('\') -EA SilentlyContinue
  } |
  Dismount-DiskImage

ZTISelectBootDisk.wsf new with BusType

Several years ago I wrote a script to help select which disk to deploy Windows to during your MDT LiteTouch or ZeroTouch task sequence.

https://keithga.wordpress.com/2013/09/18/ztiselectbootdisk-wsf/

Well, based on a request from my latest client, I have created a similar script that support BusType.

BackGround

My client is trying to install Windows Server 2016 on a Server with a SAN. When the machine boots to WinPE, one of the SAN drives appears *first* as Disk 0 (Zero). By default MDT Task Sequences will deploy to Disk Zero! My ZTISelectBootDisk.wsf already shows how to override. All we need to do is to find a way to tell MDT which disk to choose based on the correct WMI query.

Turns out it was harder than I thought.

What we wanted was the BusType that appears in the “Type” field when you type “Select Disk X” and then “detail disk” in Diskpart.exe.  When we ran “Detail Disk” in DIskpart.exe we could see the bus type: Fibre as compared to regular disks like SCSI or SAS.

The challenge was that the regular Win32_diskDrive WMI query wasn’t returning the BusType value, and we couldn’t figure out how to get that data through other queries.

I tried running some PowerShell queries like ‘Get-Disk’ and noticed that the output type was MSFT_Disk, from a weird WMI Namespace: root\microsoft\windows\storage. But adding that query to the script works! Yea!!!

BusType

What kind of BusTypes are there?

Name Value Meaning
Unknown 0 The bus type is unknown.
SCSI 1 SCSI
ATAPI 2 ATAPI
ATA 3 ATA
1394 4 IEEE 1394
SSA 5 SSA
Fibre Channel 6 Fibre Channel
USB 7 USB
RAID 8 RAID
iSCSI 9 iSCSI
SAS 10 Serial Attached SCSI (SAS)
SATA 11 Serial ATA (SATA)
SD 12 Secure Digital (SD)
MMC 13 Multimedia Card (MMC)
Virtual 14 This value is reserved for system use.
File Backed Virtual  15 File-Backed Virtual
Storage Spaces  16 Storage spaces
NVMe 17 NVMe

For this script we are *excluding* the following devices:

Name Value Meaning
Fibre Channel 6 Fibre Channel
iSCSI 9 iSCSI
Storage Spaces  16 Storage spaces
NVMe 17 NVMe

Meaning that the *FIRST* fixed device not in this list will become the new *Target* OS Disk. Run this query on your machine to see what disk will become the target:

gwmi -namespace root\microsoft\windows\storage -query 'select Number,Size,BusType,Model from MSFT_Disk where BusType <> 6 and BusTy
pe <> 9 and BusType <> 16 and BusType <> 17' | Select -first 1

Requirements

Reminder that this script requires MDT (latest), and the script should be placed in the %DeploymentShare%\Scripts folder. Additionally you should install all the Storage packages for WinPE, sorry I don’t recall *which* packages I selected when I did testing.

Script

-k

 

 

Update CustomSettings.ini file remotely!

Got on a discussion this week with someone how to use PowerShell to update an MDT CustomSettings.ini file over the network. Well a *lot* of CS.ini files.. 🙂

My manager is the Global Ops Manager and now he is asking me to find a way to run [update of customsettings.ini] on about 50 servers worldwide so the other MDT admins don’t have to log onto each server just to add one line.

The example given was to update the AdminPassword in CS.ini. I hope this company is following best practices, and disabling the local Administrator account and/or changing the Password once joined to the domain or connected to SCCM.

Anywho, INI files are a tad bit difficult to modify in Powershell because there are no native PowerShell or .NET functions to perform the action. So instead we need to do some ugly Pinvoke calls to the appropriate Win32 API.

-k

New script – Import Machine Objects from Hyper-V into ConfigMgr

Quick Post, been doing a lot of ConfigMgr OSD Deployments lately, with a lot of Hyper-V test hosts.

For my test hosts, I’ve been creating Machine Objects in ConfigMgr by manually entering them in one at a time (yuck). So I was wondering what the process is for entering in Machine Objects via PowerShell.

Additionally, I was curious how to inject variables into the Machine Object that could be used later on in the deployment Process, in this case a Role.

Up next, how to extract this information from VMWare <meh>.

New Sample for MDT (Custom Actions)

MDTLTIPSSampleAction

MDT Litetouch Action Property Page Sample

Fancy Example

Background

MDT has several pre-defined pages for common task sequence editing tasks. You’ve seen them in the MDT Litetouch Task Sequence Editor, under General, Disks, Images, Settings, and Roles.

They help abstract the ugly command line and scripting code behind the scenes for the user.

Recently I had an idea for a super-wiz-bang property page type for MDT Litetouch, and asked “are there any MDT LTI samples out there?”. I knew Config Mgr had a SDK Sample and I’ve been using it for a while now to create SCCM Task Sequence Actions pages.

The answer came back “There was an MDT Litetouch SDK, but not anymore.” (Long story for another day)

“Someone should create a sample!” I said!

“Cool Keith, when you figure it out, can you share the results? :)” For those of you who wonder, how does one become a Microsoft MVP? This, so here we go.

The Basics

C#

MDT Task Sequence Action Pages are simply C# Windows Form Control Library, with some standard API interfaces so it can be called from the Litetouch Wizard Host. The MDT team designed the API to closely resemble the System Center Configuration Manager Action Page API.

  • There are entry points for when the control is initialized.
    • Use this opportunity to load the UI elements with the saved data from the PropertyManager (aka TS.xml)
  • There are entry points for when the “OK” and “Apply” buttons are pressed.
    • Use this opportunity to save the UI elements with to the PropertyManager

There are several dependent classes required by the sample, they are contained in the ‘c:\program files\Microsoft Deployment Toolkit\bin\Microsoft.BDD.Workbench.dll’ assembly, so you will need add this reference to your project.

Anything else you want to add in the control, can be done if you know the correct C# code to get the job done.

Registration

Once you have created the DLL Library, we will need to add it so MDT Litetouch console knows about it.

First off, copy the DLL to the ‘c:\program files\Microsoft Deployment Toolkit\bin’ folder.

Secondly, we’ll need to add an element to the actions.xml file.

<action>
	<Category>General</Category>
	<Name>Install PowerShellGet Action</Name>
	<Type>BDD_MDTLTIPSSampleControl</Type>
	<Assembly>MDTLTIPSSampleAction</Assembly>
	<Class>MDTLTIPSSampleAction.MDTLTIPSSampleControl</Class>
	<Action>powershell.exe -Command  Install-Package -Force -ForceBootStrap -Name (New-Object -COMObject Microsoft.SMS.TSEnvironment).Value('Package')</Action>
	<Property type="string" name="Package" />
</action>

For this sample, I included a PowerShell libary module with two functions, one to register the new control, the other to remove the control. Easy!

The Sample

The sample in this case is pretty small.

There is one TextBox (as shown above), that prompts the user for the name of a PowerShell Package.

The package name get’s added to the TS.XML, along with the command, in this case it calls PowerShell.exe with the cmdlet Install-Package. We use COM to connect to the SMS environment space to get the package name and go.

You can use the build.ps1 script to compile the sample, and create PowerShell library to install the control within MDT Litetouch.

Future

Well I created this sample, because I have some ideas for some MDT LiteTouch (and SCCM) Action controls.

  • Fancy UI for installation of applications through Chocolatey
  • Run scripts and modules from PowerShellGallery.com
  • Other ideas, let me know (comments or e-mail)

Keith

New Tool: Get the Latest Windows 10 Cumulative Updates!

TL;DR – Programmatically get the latest Windows 10 Cumulative Updates!

Got a request from someone on an internal e-mail Distribution list recently, asking how to find out the latest Windows 10 (or Windows Server 2016) Cumulative Update.

Normally you can find these updates by going to this Microsoft KB article, then finding the right Operating System Version. Then you use the KB article number to go to Windows Update, and find the correct download link, then download the file.

I wanted to update this for another project, so I took it as a challenge to code in PowerShell.

New Tool

For this tool, I placed the source code up on GitHub.com, in a new gist. A gist is just a file that can be edited, version controlled, and shared out publicly on the GitHub.com site.

Given a Windows Version (build number), and a couple other search strings, will programmatically determine what the correct download links are for this Windows 10 (or Windows Server 2016).Â

The output can also be piped into BITS so you can just download locally.

Example

Get the links for the latest Windows 10 Version 1703 Updates:

getupdates1.PNG

 

Additionally we can also download the files using the BITS command Start-BITSTransfer

downloadupates1.PNG

 

Source