Blogger for SCConfigMgr.com

Recently I got the opportunity to join the team of awesome bloggers at SCConfigMgr.com.

SCMainLogoSmall

I’m  really honored as the team is really great, in fact they are highly respected in the community 🙂 and it’s a real pleasure for me to join the team. I’ve written my first blog post there about WinPE and wireless support, check it out:

Build a WinPE with wireless support
http://www.scconfigmgr.com/2018/03/06/build-a-winpe-with-wireless-support

Blog post summary:
The post describes the usage of WinRE as a replacement for the normal WinPE with some additional enhancements to get full wireless support. To provide user driven connection handling I built a .NET program (can be found on my GitHub site) to support the wireless setup process.

For the time being I do not plan to retire my newly started blog here at oliverkieselbach.com. I will need to figure out how to handle this new situation with two blogs but I’m sure I will find a good way to do that.

I can truly encourage everyone to check out SCConfigMgr.com as we have great articles around everything in the Enterprise Mobility and Security space.

Feel free to leave a comment and stay tuned for my next blog articles, thanks!

Use Delivery Optimization with DHCP Option on Pre-Windows 10 version 1803

The new Windows 10 Peer 2 Peer feature Delivery Optimization was enhanced by the setting to query DHCP option ID 234 to get a Group ID (DOGroupIdSource). It was implemented into the latest Windows 10 version 1803 based on my feedback. I’m a little proud that the idea was well received and my product feedback was implemented as new option within six month.

It was officially announced with Windows Insider Preview 17063. It can be read here: https://blogs.windows.com/windowsexperience/2017/12/19/announcing-windows-10-insider-preview-build-17063-pc/#zluZyU1FMlLMzU6h.97 in the section New features for IT Pros in Delivery Optimization.

I’ve written a blog post how Delivery Optimization works and how to use this feature with new Windows versions 1803+, see here: Configure Delivery Optimization with Intune for Windows Update for Business.  It’s really great to have it with Windows 10 version 1803+ but I need to provide this functionality to older Windows 10 versions (1703 and 1709) also. I want to use this very good and flexible grouping capability from now on for all devices even pre-version 1803.

Therefore I have designed a solution to provide this functionality to older versions of Windows 10 and switch over to the native Windows 10 implemented solution as soon as the device gets upgraded to 1803.

First problem is to get the DHCP option from the DHCP server. Luckily I’ve written a small C++ program to do that in the past. I’ve written it to even work with a WinPE environment. The small binary can be used to send the DHCP option ID x request. When executed the binary expects a result as a string value. Below an example:

DhcpOption.exe <OptionID>

DhcpOptionExeExample

It’s even possible to specify a debug switch

DhcpOption.exe <OptionID> [debug]

to get debug output for troubleshooting:

DhcpOptionExeDebugExample

The C++ solution can be found on my GitHub here:
https://github.com/okieselbach/Helpers/tree/master/DhcpOption_VC2017

The compiled x64 binary DhcpOption.exe can be found here:
https://github.com/okieselbach/Intune/tree/master/ManagementExtension-Samples/DOScript

With this little helper we can use the Intune Management Extension and design a PowerShell script to:

  1. query DHCP server for the Option ID 234
  2. write result (Group ID) to the registry

Prerequisite is an available Option ID 234 on the DHCP server. See Configure Delivery Optimization with Intune for Windows Update for Business how to configure the DHCP server for this.

To support travelling users we need to make sure the device will query the DHCP server from time to time to get the group ID belonging to the current DHCP scope the client is using. For this we schedule the script as a scheduled task and run it at logon and on every unlock of the workstation.

The Delivery Optimization service will query the registry value for every new request and this makes sure a client uses the Group ID delivered by DHCP from the particular site.

To make sure we do not interfere with the native implementation starting with 1803 I implemented a logic to disable the scheduled task and remove the registry key as soon as Windows 10 version 1803, a build greater than 16299 is found.

The DhcpOption.exe will be provided from an Azure Blob Storage account. We need to create a Storage account as type Blob storage and a container to store the DhcpOption.exe. Then we use the provided download link in the script to get the binary during install. A guide how to create the Azure storage account is included in my article Gather Windows 10 AutoPilot info in Azure Blob Storage during wipe and reload. You don’t need the SAS token, just the container and upload DhcpOption.exe. The SAS token is only needed to support POSTS methods for uploading new files to the blob storage. The DhcpOption.exe is uploaded once and then provided for download only.

With this solution we can use the DHCP option for all Windows 10 version until 1803 and as soon as we get upgraded we disable and remove our self and the native implementation takes over. To make sure the native implementation is used as soon 1803 is found we need to implement the new DO settings (DOGroupIdSource). Follow my guide in my previous post for this Configure Delivery Optimization with Intune for Windows Update for Business.

Source Code of the full custom DO install script Register-DOScript.ps1 can be found on my GitHub Gist here:

Make sure to replace the Azure blob storage URL with your own one!

The uninstall script Unregister-DOScript.ps1 is also available via my GitHub Gist:

If we combine the script with Intune Management Extension we can easily deploy this solution to our Azure AD joined modern managed Windows 10 pre-version 1803 devices.

We will get the files for scheduling at C:\ProgramData\CustomScripts

DOScriptProgramDataFiles

and the scheduled task RunCustomDOscript

DOScriptScheduledTask

with task trigger logon or unlock of workstation

DOScriptScheduledTaskTrigger

when executed we will get the Group ID from the DHCP server

MD DHCP Group ID Option in Scope

and the received string is written to the registry

DOGroupIdRegistry

for me this bridges the time until all devices are upgraded to Windows 10 version 1803 and gives me enough flexibility to group my devices to achieve maximum Delivery Optimization benefits.

Happy caching!

How to “Push-button reset” Windows 10

The “push-button reset” (PBR) is the way to do a “factory reset” on Windows 10. It constructs a fresh Windows 10 installation and we can start over again.

Implementing a Microsoft 365 powered device mobility concept for a modern workplace with Windows 10, makes usage of Windows as a Service and provides new ways of self enrollment. Wherever we are, we service the OS from the cloud (Windows Update for Business) and manage it from the cloud (Intune).

In case of failure there are primarily two options:

  • Get a new device and enroll the device to Azure AD again
  • Start push-button reset to run a factory reset and start over again

In case of failure or if we want to re-purpose the device (back into stock or hand over to another employee) we use the push-button reset in this concept.

 

How is push-button reset triggered from the client?

First of all we need to distinguish between a device where the user is local administrator and a device where the user is standard user.

In case of local administrator the push-button reset can be triggered from Settings > Update & Security > Recovery > Reset this PC > Get started

PBR Settings Local Administrator

As shown there are two options available:

  • Keep my files
  • Remove everything

When choosing “Remove everything” we have the following two additional options:

PBRResetEverything

Be aware if “Remove files and clean the drive” is chosen it will really take a long time to complete!

 

In case of standard user we do not have the reset option in settings available. This is relevant when using AutoPilot and a profile with “Disable local admin account creation on the device” as shown below:

AutoPilotProfileSfB

As a standard user the settings are shown with no reset option in the recovery menu:

PBRSettingsStandardUser

For Microsoft 365 powered devices managed by Intune we can deploy the Company Portal and use it to trigger the reset. Open the deployed Company Portal > choose device > … (menu in the upper right) > Reset

PBRCompanyPortal

PBRCompanyPortalDialog

 

How is push-button reset triggered from Intune?

We need to open the Intune portal portal.azure.com and navigate to Intune > Devices > All Devices > pick the particular device > Factory reset

IntuneFactoryReset

as shown in the screenshot there is the additional option to “Retain the enrollment state and user account”.

For detailed description what is retained please refer to: https://docs.microsoft.com/en-us/intune/devices-wipe#factory-reset

If the device is on and connected, it takes less than 15 minutes for a factory reset command to propagate across all device types.

 

What is the “Fresh Start” available in Intune?

The Fresh Start device action removes any apps that were installed on a Windows 10 PC running the Creators Update, then automatically updates the PC to the latest version of Windows. This can be used to help remove pre-installed (OEM) apps that are often delivered with a new PC. You can configure if user data is retained when this device action is issued. In this case, apps and settings are removed, but the contents of the users Home folder are retained.

IntuneFreshStart

IntuneFreshStartDialog

If the device is on and connected, it takes less than 15 minutes for a factory reset command to propagate across all device types.

 

Reset in Action

When a reset is triggered the device will end up rebooting and the reset will occur.

Windows10Resetting

After finishing the reset the device will start in OOBE again for new enrollment or at the logon screen when enrollment was retained.

 

Issues with Lenovo devices and the troubleshooting

During tests I observed Lenovo devices which ran into an error after resetting and starting into OOBE again. Shortly after language and keyboard selection I got this:

OOBESomethingWentWrong

After some debugging I found out that the license.rtf file was not available in C:\Windows\System32 and this blocked the OOBE from displaying the EULA and resulted in the “Something went wrong” error screen. Click on “Try again” generated a loop.

I could solve this problem with a small customization of the PBR. The PBR has options to add a script to the PBR routine. More details see here: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/add-a-script-to-push-button-reset-features

I prepared a script which copies the license.rtf file back to C:\Windows\System32 folder if it is missing after the reset. We need to prepare 2 files and we need the license.rtf from a running Windows 10 device.

EnableCustomizations.cmd

rem EnableCustomizations.cmd

rem Define %TARGETOS% as the Windows folder (This later becomes C:\Windows)
for /F "tokens=1,2,3 delims= " %%A in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\RecoveryEnvironment" /v TargetOS') DO SET TARGETOS=%%C

rem Define %TARGETOSDRIVE% as the Windows partition (This later becomes C:)
for /F "tokens=1 delims=\" %%A in ('Echo %TARGETOS%') DO SET TARGETOSDRIVE=%%A

rem Add back license.rtf file if missing
IF NOT EXIST "%TARGETOS%\System32\license.rtf" (
copy "%TARGETOSDRIVE%\Recovery\OEM\license.rtf" "%TARGETOS%\System32\license.rtf" /y
)
exit /b 0

ResetConfig.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- ResetConfig.xml -->
<Reset>
 <Run Phase="BasicReset_AfterImageApply">
 <Path>EnableCustomizations.cmd</Path>
 <Duration>2</Duration>
 </Run>
 <Run Phase="FactoryReset_AfterImageApply">
 <Path>EnableCustomizations.cmd</Path>
 <Duration>2</Duration>
 </Run>
</Reset>

In the end copy EnableCustomizations.cmd, ResetConfig.xml and the license.rtf to the folder:

C:\Recovery\OEM

After deploying the custom PBR fix for the Lenovo devices the push button reset runs smoothly.

 

Further information

Remove devices by using factory reset or remove company data
https://docs.microsoft.com/en-us/intune/devices-wipe

Push-button reset
https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/push-button-reset-overview

Add a script to push-button reset features
https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/add-a-script-to-push-button-reset-features

 

Happy resetting!

 

Gather Windows 10 AutoPilot info in Azure Blob Storage during wipe and reload

The Modern Management strategy is based on Enterprise Mobility + Security and additional services like Office 365. Microsoft created a new SKU called Microsoft 365 for this. To complete the big picture we need some additional services:

The idea is clear, manage the Windows 10 devices like mobile phones. No more Operating System Deployment (OSD) just provisioning and management from everywhere. Everything is powered by the cloud.

A new member in this story is a feature called Windows AutoPilot. You can compare this with the Device Enrollment Program as you might know from Apple. It provides a managed way of provisioning with near zero touch. IT is able to control the experience the end user will have during enrollment process. To make all this work we need to gather some properties of the device to identify it clearly. The AutoPilot needs the Device Serial Number, Windows Product ID and the Hardware Hash. This information is uploaded to the AutoPilot service and then the device will be recognized during OOBE as an AutoPilot device, and will show a customized enrollment experience.

The Problem

Many organizations are still using Windows 7 and are on it’s way to Windows 10.  Windows 10 is the new “baseline” in this story. It’s aligned with the complete modern management story. It provides the capability to join Azure AD and the usage of a Windows as a Service model.

How do we get to the new baseline?

If we purchase a new device, the OEM vendor takes care of installing Windows 10 with a signature edition and all necessary drivers. In future the hardware information will be synced into our tenant from the OEM vendor. We will get the device information in Intune and we can start to assign an AutoPilot Deployment Profile and start to enroll the device.

DeviceEnrollment

What if we have a bunch of Windows 7 devices in the environment?

A way to handle this is that we are playing the role of the OEM vendor and do the install of a Windows 10 signature edition on the existing Windows 7 devices. Depending what is available we can use ConfigMgr or MDT. In the context of modern management I like to keep on-premises software as low as possible. I use MDT for that simple task now. If ConfigMgr is available we can build the following the same way.

I use MDT to create a Deployment USB media (removable drive) for that and build up a Standard Task Sequence to deploy Windows 10 for this. We take care of the right drivers and in the end we let the device start the OOBE again (sysprep.exe /oobe /reboot|shutdown). Now we have the same situation like a newly delivered device by the OEM vendor. But we can’t deliver the hardware information directly into our tenant like the OEM vendor will do in the future. Good to know that we can get the hardware information with the PowerShell Script Get-WindowsAutoPilotInfo and upload the information provided via a .csv file our self.

Now imagine a situation where a rollout team is preparing a lot of machines. We would end up in a lot of .csv files on different USB removable drives. To make this a little easier for IT to import the hardware information of new devices to AutoPilot, we build up the following logic:

 

First of all we prepare the Blob Storage for easy csv file storage.

Login to Azure portal and click on “Storage accounts

StorageAccount

Click Add

StorageAccountAdd

fill out name, Account kind: Blob storage

StorageAccountCreation

after creation you should see the storage account

StorageAccountOverview

create a container called hashes

StorageAccountContainer

create a shared access signature for Blob | Write | an extended expiry date/time | HTTPS only and create a SAS token. Shared Access Signature is used to limit the permission and the limit the period of time to access the account. See Delegating Access with a Shared Access Signature

StorageAccountSAS

Copy the SAS token as we need it in the following script.

Download PowerShell Script Get-WindowsAutoPilotInfo and AzCopy. Install AzCopy and get the files from here: C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy

Copy AzCopy files and Get-WindowsAutoPilotInfo.ps1 into MDT share e.g. C:\DeploymentShare\Scripts\CUSTOM\HardwareInfo

Create PowerShell script: Get-HardwareInformation.ps1 and copy to the MDT folder HardwareInfo as well. Replace the SAS token (ending with XXXX) in the script example with the newly created one. Replace ZZZZ with your Storage account name.

The script will look for the Get-WindowsAutoPilotInfo.ps1 script, executes it and creates a computername.csv file in C:\Windows\Temp. From here it will be copied to the blob storage account and copied to the USB removable drive folder autopilot-script-success or autopilot-script-failed. This provides the chance in case of failure (missing internet access during deployment) that the computername.csv can be gathered from the USB drive as well.

hardwareinfocsv


# Author: Oliver Kieselbach
# Date: 11/15/2017
# Description: Generate AutoPilot .csv file and upload to Azure Blob Storage.

# The script is provided "AS IS" with no warranties.

# Downlaod URL for AzCopy:
# http://aka.ms/downloadazcopy

# Downlaod URL for Get-WindowsAutoPilotInfo:
# https://www.powershellgallery.com/packages/Get-WindowsAutoPilotInfo

Function Execute-Command
{
 Param([Parameter (Mandatory=$true)]
 [string]$Command,
 [Parameter (Mandatory=$false)]
 [string]$Arguments)

$pinfo = New-Object System.Diagnostics.ProcessStartInfo
 $pinfo.FileName = $Command
 $pinfo.RedirectStandardError = $true
 $pinfo.RedirectStandardOutput = $true
 $pinfo.CreateNoWindow = $true
 $pinfo.UseShellExecute = $false
 $pinfo.Arguments = $Arguments
 $p = New-Object System.Diagnostics.Process
 $p.StartInfo = $pinfo
 $p.Start() | Out-Null
 $p.WaitForExit()
 [pscustomobject]@{
 stdout = $p.StandardOutput.ReadToEnd()
 stderr = $p.StandardError.ReadToEnd()
 ExitCode = $p.ExitCode
 }
}

$scriptPath = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path)
$fileName = "$env:computername.csv"
$outputPath = Join-Path $env:windir "temp"
$outputFile = Join-Path $outputPath $fileName
$autoPilotScript = Join-Path $scriptPath "Get-WindowsAutoPilotInfo.ps1"

Execute-Command -Command "$psHome\powershell.exe" -Arguments "-ex bypass -file `"$autoPilotScript`" -ComputerName $env:computername -OutputFile `"$outputFile`"" | Out-Null

$url = "https://ZZZZ.blob.core.windows.net/hashes"
$sasToken = "?sv=2017-04-17&amp;ss=b&amp;srt=o&amp;sp=w&amp;se=2019-10-16T19:47:51Z&amp;st=2017-10-15T11:47:51Z&amp;spr=https&amp;sig=XXXX"
$result = Execute-Command -Command "`"$scriptPath\azcopy.exe`"" -Arguments "/Source:`"$outputPath`" /Dest:$url /Pattern:$fileName /Y /Z:`"$outputPath`" /DestSAS:`"$sasToken`""

if ($result.stdout.Contains("Transfer successfully: 1"))
{
 if (-not (Test-Path $(Join-Path $scriptPath "autopilot-script-success")))
 {
 New-Item -Path $(Join-Path $scriptPath "autopilot-script-success") -ItemType Directory | Out-Null
 }
 Copy-Item -Path $outputFile -Destination $(Join-Path $scriptPath "autopilot-script-success") -Force -ErrorAction SilentlyContinue | Out-Null
}
else
{
 if (-not (Test-Path $(Join-Path $scriptPath "autopilot-script-failed")))
 {
 New-Item -Path $(Join-Path $scriptPath "autopilot-script-failed") -ItemType Directory | Out-Null
 }
 Copy-Item -Path $outputFile -Destination $(Join-Path $scriptPath "autopilot-script-failed") -Force -ErrorAction SilentlyContinue | Out-Null
}

Create PowerShell script: Download-HardwareInformation.ps1
This can be used later on to download all the .csv files from Azure Blob Storage and create the combined .csv for easy upload to AutoPilot. Leave this script on your admin workstation. Replace the StorageAccountKey XXXX with one of your storage account access keys! Replace ZZZZ with your Storage account name.

StorageAccountAccessKey


# Author: Oliver Kieselbach
# Date: 11/15/2017
# Description: Gather AutoPilot .csv file from Azure Blob Storage, delete them and combine into single .csv file.

# The script is provided "AS IS" with no warranties.

#Install-Module AzureRM

$ctx = New-AzureStorageContext -StorageAccountName ZZZZ -StorageAccountKey XXXX
$path = "C:\temp"
$combinedOutput = "C:\temp\combined.csv"

$count = $(Get-AzureStorageContainer -Container hashes -Context $ctx | Get-AzureStorageBlob |measure).Count
if ($count -gt 0)
{
 Get-AzureStorageContainer -Container hashes -Context $ctx | Get-AzureStorageBlob | Get-AzureStorageBlobContent -Force -Destination $path
 $downloadCount = $(Get-ChildItem -Path $path -Filter *.csv | measure).Count
 if ($downloadCount -eq $count)
 {
 Get-AzureStorageContainer -Container hashes -Context $ctx | Get-AzureStorageBlob | Remove-AzureStorageBlob
 }
 # parse all .csv files and combine to single one for easy upload!
 Set-Content -Path $combinedOutput -Value "Device Serial Number,Windows Product ID,Hardware Hash" -Encoding Unicode
 Get-ChildItem -Path $path -Filter "*.csv" | % { Get-Content $_.FullName | Select -Index 1 } | Add-Content -Path $combinedOutput -Encoding Unicode
}

I assume the MDT share is build and a Standard Task Sequence for a vanilla Windows 10 installation is available. Then we add a task sequence step “Run PowerShell Script” to the folder “Custom Tasks“:

RunPowerShellScriptStep

and configure the Get-HardwareInformation.ps1 script:

GetHardwareInformation

Now you are ready to run a MDT deployment of a Windows 10 with an automatic upload of the hardware information to the Azure Blob Storage.

After deployment of the devices you can use the Download-HardwareInformation.ps1 Script to get the combined.csv file and upload it to Microsoft Store for Business (MSfB). The upload is currently available in this portal only.

MSfBAutoPilotUpload

I recommend to use the MSfB to upload the combined.csv only! Management of the devices and profiles should be done in Intune. Currently the portals do not completely share their information. For example a profile created in MSfB will not be shown in Intune and vice versa. With Modern Management where Intune is used I suggest to use MSfB to upload devices and Intune for management of profiles (creation and assignment).

Happy AutoPiloting!

There is another great article from Per Larsen (MVP):
How to collect hardware hash to use in AutoPilot as part of MDT OSD