Using SCCM USB Bootable Media in UEFI/GPT/BitLocker Scenarios When Local and Remote Boot Images Are Different

A customer recently had a requirement for rebuilds to be done in remote sites via USB flash drives configured as SCCM Bootable Media due to a lack of local SCCM Distribution Points and PXE Boot capability.

Using devices in UEFI mode with BitLocker enabled makes this tricky when the Boot Image associated with the Task Sequence becomes out of sync with the Boot Image on the USB media. If the boot images don’t match then SCCM attempts to pre-stage onto the local disk and fails as the OSDisk is unavailable due to it being encrypted with BitLocker (the drive appears as “RAW” and cannot be accessed) and none of the other partitions are large enough or available.

I worked around this by creating a PowerShell PreStart script and adding it to the Boot Media ISO image. The script runs before the Task Sequence begins. It creates a Diskpart configuration text file on the fly in the ‘X:\Windows\Temp’ folder of the running WinPE. After creating the Diskpart configuration file, it then runs Diskpart referencing the configuration file in order to create suitably-sized/lettered partitions to successfully boot from using UEFI and that are also accessible for the Task Sequence to download and pre-stage the latest Boot Image if it’s required (i.e. if it’s different to the boot image on the USB).

Problem solved!

The command for the PreStart script that I used was:

cmd /C PowerShell.exe -ExecutionPolicy ByPass -File PreStart.ps1

And the PowerShell code contained with PreStart.ps1 is shown below:

<#
.DESCRIPTION
    Configures GPT disk layout using DiskPart.exe to avoid Boot Image mismatching when using SCCM Bootable Media
.EXAMPLE
    PowerShell.exe -ExecutionPolicy ByPass -File .ps1
.NOTES
    Author:         Jonathan Conway
    Modified:       06/04/2019
    Version:        1.0
#>

# Display warning and request confirmation from engineer
$Shell = New-Object -ComObject "WScript.Shell"
$Button = $Shell.Popup("Proceeding will wipe all local data from all local drives. Hold Power Button until device powers off to cancel. Click OK to proceed.", 0, "WARNING", 0)

# Set variables
$DiskPartFile = "X:\Windows\Temp\DiskpartConfig.txt"

if (Get-Volume | Where-Object {$_.DriveLetter -eq 'C' -and $_.DriveType -eq 'Removable'}) {
Get-Partition -DriveLetter 'C' | Set-Partition -NewDriveLetter 'U'
}

# Create contents of DiskPart configuration file
Write-Output "SELECT DISK 0" | Out-File -Encoding utf8 -FilePath "$DiskpartFile"
Write-Output "CLEAN" | Out-File -Encoding utf8 -FilePath "$DiskpartFile" -Append
Write-Output "CONVERT GPT" | Out-File -Encoding utf8 -FilePath "$DiskpartFile" -Append
Write-Output "CREATE PARTITION EFI SIZE=200" | Out-File -Encoding utf8 -FilePath "$DiskpartFile" -Append
Write-Output "ASSIGN LETTER=S" | Out-File -Encoding utf8 -FilePath "$DiskpartFile" -Append
Write-Output "FORMAT QUICK FS=FAT32" | Out-File -Encoding utf8 -FilePath "$DiskpartFile" -Append
Write-Output "CREATE PARTITION MSR SIZE=128" | Out-File -Encoding utf8 -FilePath "$DiskpartFile" -Append
Write-Output "CREATE PARTITION PRIMARY" | Out-File -Encoding utf8 -FilePath "$DiskpartFile" -Append
Write-Output "ASSIGN LETTER=C" | Out-File -Encoding utf8 -FilePath "$DiskpartFile" -Append
Write-Output "FORMAT QUICK FS=NTFS" | Out-File -Encoding utf8 -FilePath "$DiskpartFile" -Append
Write-Output "EXIT" | Out-File -Encoding utf8 -FilePath "$DiskpartFile" -Append

# Run DiskPart
Start-Process -FilePath "diskpart.exe" -ArgumentList "/s $DiskPartFile" -Wait

In my environment this formats the disks in a way which allows my Task Sequence to progress whatever state the UEFI partitions are in (i.e. BitLocker enabled or not).

A pop up warning is shown on screen stating:

Proceeding will wipe all local data from all local drives. Hold Power Button until device powers off to cancel. Click OK to proceed“.

Clicking OK continues ahead and starts the Diskpart process before progressing to the Task Sequence selection screen 🙂

/ JC

Advertisements
Posted in BitLocker, OSD, PowerShell, SCCM Current Branch, WinPE | Tagged , | Leave a comment

SCCM Collection Query for Duplicate MAC Addresses

The WQL query below is can be used in SCCM to display all duplicate MAC addresses which will likely cause issues with OSD:


SELECT R.ResourceID, R.ResourceType, R.Name, R.SMSUniqueIdentifier, R.ResourceDomainORWorkgroup, R.Client
FROM SMS_R_System AS r full JOIN SMS_R_System AS s1 ON s1.ResourceId = r.ResourceId full JOIN SMS_R_System AS s2 ON s2.Name = s1.Name
WHERE s1.Name = s2.Name AND s1.ResourceId != s2.ResourceId
ORDER BY r.MACAddresses

The WQL query below is a prompted query to show entries for a specified MAC Address to see if it is being used my multiple devices in the SCCM database:


SELECT SMS_R_System.Name, SMS_R_System.MACAddresses
FROM  SMS_R_System
WHERE SMS_R_System.MACAddresses = ##PRM:SMS_R_System.MACAddresses##

/ JC

Posted in SCCM Current Branch | Tagged , | Leave a comment

WMI/WQL “LIKE” Query Wildcards

Quick post today.

Standard Windows/DOS wildcards don’t work in WMI “LIKE” queries as they use WQL language instead:

Multiple Characters = "%" (Percentage)
Single Character    = "_" (Underscore)

For reference, the corresponding Windows wildcards are:

Multiple Characters = "*" (Asterisk)
Single Character    = "?" (Question Mark)

/ JC

Posted in OSD, Tips, Windows | Leave a comment

Use RoboCopy in ‘Run PowerShell Script’ SCCM Task Sequence Steps

I like to use PowerShell for all my scripting these days (all VB and batch files have now been rewritten in PoSh) and I also like to use RoboCopy for any file copies that I need to do such as in an OSD Task Sequence.

The pain in the arse with RoboCopy is the return/exit codes it uses which cause issues when used in PowerShell scripts.

The return codes used by PowerShell are:

0 No files were copied. No failure was encountered. No files were mismatched. The files already exist in the destination directory; therefore, the copy operation was skipped.

1 All files were copied successfully.

2 There are some additional files in the destination directory that are not present in the source directory. No files were copied.

3 Some files were copied. Additional files were present. No failure was encountered.

5 Some files were copied. Some files were mismatched. No failure was encountered.

6 Additional files and mismatched files exist. No files were copied and no failures were encountered. This means that the files already exist in the destination directory.

7 Files were copied, a file mismatch was present, and additional files were present.

8 Several files did not copy.

Because PowerShell expects an exit code of ‘0’ for success, if RoboCopy completes with an exit code of ‘1’ (i.e. All files were copied successfully) then it throws an exit code other than ‘0’.

In an OSD Task Sequence this is picked up as an error and will therefore cause the Task Sequence to fail. Bollocks.

This can easily be prevented using a wee bit of code at the end of the script used to run the RoboCopy.

In the example below I am copying a single ISO image using a PowerShell script in a Task Sequence (using a ‘Run PowerShell Script’ task). The resulting PowerShell exit code will equal ‘1’ as “all files will be copied successfully”.

<#
.SYNOPSIS
    Copies VM Bootable ISO
.DESCRIPTION
	Copies the VM Bootable ISO from the package folder to C:\Media
.EXAMPLE
	PowerShell.exe -ExecutionPolicy ByPass -File <ScriptName>.ps1
.NOTES
	Author:		Jonathan Conway
	Version:	1.0
	Created:	29/11/2017
#>

# Set variable for newest ISO in package folder (in case there are more than one then the most recent will be chosen)
$ISO = Get-ChildItem '.\*.iso' | Sort-Object 'LastWriteTime' | Select-Object -last '1' | Select-Object -ExpandProperty 'Name'

# Run ROBOCOPY to copy the Bootable ISO image to "C:\Media"
& ROBOCOPY ".\" "C:\Media" $ISO

# Robocopy for a single file returns a exit code of "1" (i.e. All files were copied successfully) which causes a Task Sequence error - this "if" statement changes exit code to a "0"
if ($LASTEXITCODE -eq '1') {
    EXIT 0
}

To prevent a Task Sequence failure I can intercept the ‘$LASTEXITCODE’ variable and exit the script with a ‘0’ using an ‘if’ statement.

This will then be picked up by the running Task Sequence and consumed as a ‘success’ which will subsequently allow the Task Sequence to progress without error.

Marvellous!

/ JC

Posted in OSD, PowerShell, SCCM Current Branch, Uncategorized | Leave a comment

Check TPM Status from the Command Line (Enabled | Activated | Owned)

Quick and simple way to see if the TPM on a computer is Enabled, Activated and Owned – all of which are required before using them for BitLocker:

wmic /namespace:\\root\cimv2\security\microsofttpm path win32_tpm get IsEnabled_InitialValue
wmic /namespace:\\root\cimv2\security\microsofttpm path win32_tpm get IsActivated_InitialValue
wmic /namespace:\\root\cimv2\security\microsofttpm path win32_tpm get IsOwned_InitialValue

As long as they all return as “True” you’re good to go.

/ JC

Posted in BitLocker, PowerShell, Uncategorized | Tagged , | 6 Comments

Confirm Service Account Credentials The Easy Way with PowerShell (e.g. SCCM Network Access Account)

Sometimes you will have an AD Service Account configured and you might not be sure what the password is – a good example of this that sometimes catches me out is the SCCM Network Access Account.

To safely test the account username and password we can use PowerShell with the following simple and safe command:

 
Start-Process -FilePath winver.exe /c -Credential (Get-Credential)

This will attempt to run “winver.exe” and a prompt will appear asking for credentials:

AccountCredsPrompt

If the account credentials that you enter are not correct you will see the following error:

AccountCredsFail

But if the credentials provided are correct then “winver.exe” will open as expected and no error message will be produced:

AccountCredsSuccess

Simple but effective 🙂

/ JC

Posted in PowerShell, SCCM Current Branch, Tips, Uncategorized, Windows | Leave a comment

Add CMTrace.exe to Computers Being Deployed via Task Sequence

To make sure you have CMTrace.exe available for use on machines that are deployed via SCCM Task Sequences you can add a “Run Command Line” task immediately after the “Apply Operating System Image” that copies the executable from the boot image being used to deploy the OS (CMtrace.exe is included by default SCCM Current Branch WinPE boot images – WinPE is mapped as X:\ during OSD) and results in it being available once OSD completes:

 cmd /c xcopy X:\SMS\BIN\x64\CMTrace.exe %OSDTargetSystemDrive%\Windows\System32\ /E /H /C /I /Q /Y

This command line will need to be amended in the unlikely scenario (it’s 2017 after all) that you’re deploying a 32-bit Operating System to change the xcopy target path accordingly.

/ JC

Note: This was originally documented on TechNet yonks ago: Link

Posted in OSD, SCCM Current Branch, Tips, Uncategorized, WinPE | Leave a comment