BIOS to UEFI SecureBoot on Lenovo Desktops Gotcha!

Been working with several IT departments trying to get our BIOS to UEFI solution qualified on as many OEM hardware models as possible, but unfortunately we have hit a snag that will affect Lenovo customers who need to move from BIOS to UEFI with SecureBoot using automated tools.

Lenovo does have an WMI API for programmatically making changes to the BIOS from within Windows (either the full OS or WinPE). That’s great! Unfortunately there are two areas where their implementation is lacking compared to Dell or HP:

  1. On Lenovo Laptops, we can change from BIOS to UEFI with SecureBoot, but they don’t offer the ability to move from BIOS to UEFI without SecureBoot. Why would we want to do that? Well if we were installing Windows 7 in UEFI mode (with anticipation of upgrading to Windows 10 with SecureBoot in the future).
  2. On Lenovo Desktops, the opposite problem, we can change from BIOS to UEFI without SecureBoot, but we can’t change to BIOS to UEFI with SecureBoot. And this is a problem.

I did contact Lenovo directly, and their official response is that they are aware of the issue, but the lack of support for API access to/from SecureBoot on desktop models is “by-Design”. Lenovo is only half right, Disabling Secure Boot must always require physical presence, that is clearly documented by UEFI spec:

http://www.uefi.org/sites/default/files/resources/UEFI_Secure_Boot_in_Modern_Computer_Security_Solutions_2013.pdf

 DISABLING SECURE BOOT 

[…]

Users may disable Secure Boot entirely, using a system setup screen enabled at boot time. Each manufacturer has its own interface for this option. In all cases, end user must be physically present to establish proof of possession (POP) associated with the changes.

However, Enabling secure boot has no such requirement (that I can find), and Dell, HP, and Lenovo ThinkPad devices do support enabling SecureBoot programmatically.

I have tried to explain this point to Lenovo, but to no success. This sucks for customers that need to use tools to make changes at scale. Manually enabling SecureBoot can be a labor intensive process.

Recommendation:

Therefore, I have to unfortunately make the recommendation:

Guidance: Enterprise customers should avoid Lenovo Desktops if they are still using Windows 7 and have plans to upgrade to Windows 10 with SecureBoot in the near future. Lenovo does not have any enterprise management tools to support this.

-Keith

Display WPF XAML code in PowerShell

Last week I went to the Minnesota Management Summit at the Mall of America #MMSMOA, and I got inspired to work on a few projects in my backlog.

One of the presentations I went to was with Ryan Ephgrave (@EphingPosh on Twitter.com), and his talk on “Better Know a PowerShell UI“.

Overall it was a great presentation, and I learned a lot. And got me thinking about some of the things I could do with a framework I started earlier in the year but never got around to finishing.

WPF4PS

Without further adieu, I present Windows Presentation Framework for PowerShell (WPF4PS). It is also the first project I’ve released as source code on GitHub:

https://github.com/keithga/WPF4PS

Background

Most WPF + PowerShell examples are created with a lot of custom code to add in event handlers for the User Interface elements. The goal is to find all control elements on the page and if there is a pre-defined function created, then use it. Which means minimal code for overhead.

Example

Here is a fully functional example:

  • Load the WPF4PS module
  • Import a XAML defined in Visual Studio
  • Create a scriptBlock to handle the button Click
  • Create a HashTable to pass data between our script that the XAML Window
  • Call the Show-XAMLWindow function
  • Get the value of the TextBox from the Hash

wpf4ps

<#
.SYNOPSIS
WPF4PS framework Examples

.DESCRIPTION
Simple Example

.NOTES
Copyright Keith Garner, All rights reserved.

#>

[cmdletbinding()]
param()

import-module $PSScriptRoot\wpf4ps -force

$MyXAML = @"
<Window x:Class="WpfApplication1.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 xmlns:local="clr-namespace:WpfApplication1"
 mc:Ignorable="d"
 Title="MainWindow" FontFamily="Segoe WP Semibold" Width="400" Height="300" Name="WindowMain" >
 <Grid>
 <Label>Hello World</Label>
 <Button x:Name="Button1" Content="Big Red Button" Width="125" Height="25" Background="#FFDD0000" Margin="0,60,0,0"/>
 <TextBox x:Name="textBox1" Height="23" Width="200" />
 </Grid>
</Window>
"@

$MyControl = [scriptBlock]{

    function global:button1_click()
    {
        "Click the Big Red Button`n" + $TextBox1.TExt  | show-MessageBox 
        $WindowMain.Close()
    }

}

$MyHash = [Hashtable]::Synchronized(@{ textBox1 = "Hello World" })

Show-XAMLWindow -XAML $MyXAML -ControlScripts $MyControl -SyncHash $MyHash

$MyHash.TextBox1 | Write-Host

Next

My goal is to work out the kinks and eventually upload/share this on PowerShellGallery.com.

For example:

  • I created two Show-XAMLWindow() functions in the library, one inline and another Async. I still don’t know what the usage case of Async is.
  • Ryan Ephgrave did some XAML + Powershell examples in his “Better Know a PowerShell UI” blog series with XAML “Binding” elements, something I have not used in the past, so I excluded them from this package.
  • I had to do some weirdness with the declaring the functions above as “global” to make them visible to the Module

If you have feedback on the layout or usage, please let me know.

-k

Keith Garner at MMS next week

Just a reminder that I will be at the Minnesota Management Summit (MMS) next week.

For the most part I’m excited about meeting up with ya all, learning about the state of the industry, and new products/features coming out. :^).

See you at the Mall of America!

 

MDT UberBug – More bugs from Johan

Some reminders of other MDT UberBugs out there.

Johan Arwidmark has found been following up with the MDT team on a couple of bugs:

LiteTouch

Issue #3 Editing Unattend.xml via WSIM breaks the deployment

Turns out that if you have an encoded administrator password in your Unattend.xml file (not recommended), then MDT 2013 Update 1 may not reset the <PlainText>False</PlainText> back to True.  This can cause a break during Windows Setup.  This may have something to do with a change in the ADK from MSXML3 to MSXML6. Microsoft is investigating.

ZeroTouch

Issue #2 ConfigMgr deployments displays “Can not find script file “X:\LTIBootstrap.ini”.” and tries Auto logon when

More errors with MSXML3 to MSXML6

Issue #3 The new disk handling logic that was supposed to remove the need for setting OSDPreserveDriveLetter only works for uEFI 

Sadly, I forgot what OSDPreserveDriveLetter was about, so I had to do an internet search. Oh yea, I have a blog post about that. But Frank wrote a better one.

 

Thanks Johan!

MDT UberBug06 – Having two recovery partitions is more secure

Getting some questions lately about recovery partitions in MDT LiteTouch, and yes, they are broken too.

ZTIDiskPart.wsf will automatically create hidden System partitions when you install the OS, this is typically 499MB and is at the start of the disk. In addition, MDT can also create a “Recovery” partition for the WinRE recovery system.

There are several things that are broken about this model, and I have filed several bugs against MDT to have these fixed.

However in MDT 2013 we can see that things have changed, someone noticed that there is an extra “recovery” partition on uEFI machines! Why is that?

Turns out that even tough MDT already creates a Recovery partition on uEFI scenarios, MDT 2013 Update 1 added a 2nd recovery partition, that is never used, and is not marked as “Hidden” for uEFI, so it’s quite confusing.

Additionally, I dislike the use of modifying the primary partition scheme and making the main partition 99%.

My fix is to convert the client.xml task sequence template back to the old MDT 2013 style:

from this:

<defaultVarList>
  <variable name="OSDDiskIndex" property="DiskIndex">0</variable>
  <variable name="OSDPartitions0Type" property="Partitions0Type">Primary</variable>
  <variable name="OSDPartitions0FileSystem" property="Partitions0FileSystem">NTFS</variable>
  <variable name="OSDPartitions0Bootable" property="Partitions0Bootable">True</variable>
  <variable name="OSDPartitions0QuickFormat" property="Partitions0QuickFormat">True</variable>
  <variable name="OSDPartitions0VolumeName" property="Partitions0VolumeName">OSDisk</variable>
  <variable name="OSDPartitions0Size" property="Partitions0Size">99</variable>
  <variable name="OSDPartitions0SizeUnits" property="Partitions0SizeUnits">%</variable>
  <variable name="OSDPartitions0VolumeLetterVariable" property="Partitions0VolumeLetterVariable">OSDisk</variable>
  <variable name="OSDPartitions1Type" property="Partitions1Type">Primary</variable>
  <variable name="OSDPartitions1FileSystem" property="Partitions1FileSystem">NTFS</variable>
  <variable name="OSDPartitions1Bootable" property="Partitions1Bootable">False</variable>
  <variable name="OSDPartitions1QuickFormat" property="Partitions1QuickFormat">True</variable>
  <variable name="OSDPartitions1VolumeName" property="Partitions1VolumeName">Recovery</variable>
  <variable name="OSDPartitions1Size" property="Partitions1Size">100</variable>
  <variable name="OSDPartitions1SizeUnits" property="Partitions1SizeUnits">%</variable> 
  <variable name="OSDPartitions1VolumeLetterVariable" property="Partitions1VolumeLetterVariable"></variable>
  <variable name="OSDDiskPartitions1Type" property="OSDDiskPartitions1Type">Recovery</variable>
  <variable name="OSDPartitions" property="Partitions">2</variable>     
  <variable name="OSDPartitionStyle" property="PartitionStyle">MBR</variable>
</defaultVarList>

Back to the MDT 2013 style:

<defaultVarList>
  <variable name="OSDDiskIndex" property="DiskIndex">0</variable>
  <variable name="OSDPartitions0Type" property="Partitions0Type">Primary</variable>
  <variable name="OSDPartitions0FileSystem" property="Partitions0FileSystem">NTFS</variable>
  <variable name="OSDPartitions0Bootable" property="Partitions0Bootable">True</variable>
  <variable name="OSDPartitions0QuickFormat" property="Partitions0QuickFormat">True</variable>
  <variable name="OSDPartitions0VolumeName" property="Partitions0VolumeName">OSDisk</variable>
  <variable name="OSDPartitions0Size" property="Partitions0Size">100</variable>
  <variable name="OSDPartitions0SizeUnits" property="Partitions0SizeUnits">%</variable>
  <variable name="OSDPartitions0VolumeLetterVariable" property="Partitions0VolumeLetterVariable">OSDisk</variable>
  <variable name="OSDPartitions" property="Partitions">1</variable>
  <variable name="OSDPartitionStyle" property="PartitionStyle">MBR</variable>
</defaultVarList>

I have yet to file a bug on this issue on connect, as the connect web site is broken again today.

-k

MDT Gotcha – Upgrading a Deployment Share

Some notes about upgrading your existing MDT deployment share to MDT 2013 Update 1.

First off, if you are upgrading a share, please backup the share first, so you have a copy that you can reference if necessary. At least backup the \Control and \Scripts folder.

Even better, make a copy of your MDT Deployment Share, and upgrade the COPY.

Finally, if you have been playing around with creating Windows 10 images, be aware if you upgrade from previous versions of the MDT 2013 Update 1 beta, you might miss some fixes in the final version that are *NOT* fixed during the upgrade.

A big problem is the Windows Version 6.0 test in the Capture Image phase of the Client Task Sequence:

build

This test will *fail* to apply the WinPE image to the local machine for capture. Whoops!

To fix, simply remove the “Version > 6” test on this step.

This is only a problem for Windows 10 capture task sequences.