Wednesday, February 20, 2019

MBR to GPT conversion

This will run the MBR2GPT tool's validation before attempting conversion:

@ECHO OFF
MBR2GPT.EXE /validate
IF %ERRORLEVEL% NEQ 0 GOTO ERROR

MBR2GPT.EXE /convert
IF %ERRORLEVEL% NEQ 0 GOTO ERROR

:ERROR
EXIT /B %ERRORLEVEL%

Tuesday, February 19, 2019

Decrypt all of the BitLocker encrypted drives

I needed Bitlocker actually OFF, not just suspended, so here's the script that I used to ensure that it got there:


For Win10:
-----------------------------------
#Decrypt-BitlockerDrives.PS1
#Script by Mark Randol
#randoltech.blogspot.com
#This script finds all local encrypted volumes and decrypt them.
#Does not exit until decryption is completed.

CLS
Clear-BitLockerAutoUnlock #Since we're decrypting all of the drives, and any Auto-Unlock protectors are tied to the encryption on the system drive, these need to go away.

$PossibleDrives = (Get-BitLockerVolume).MountPoint  #get all of the drives that could possibly be encrypted

#Disable-BitLocker -MountPoint $PossibleDrives #start all of the discovered drives decrypting in parallel

foreach ($DriveLetter in $PossibleDrives) {  #step through the drives in series to ensure they get decrypted
    [int]$LastEncryptPercent = 100  #This variable stores the most recent change to the encryption percentage 
    [int]$CurrentEncryptPercent = 2  #This variable stores the current check that we are making on the encryption level
    #If these two variables are equal we know that no progress has been made in decryption since the last check
    do { #check the encryption level of the drive every five minutes until it is fully decrypted
        $CurrentEncryptPercent = (Get-BitLockerVolume -MountPoint $DriveLetter).EncryptionPercentage
        if ($CurrentEncryptPercent -ne $LastEncryptPercent) { #if the percentage of encryption has changed since the last check then write that to the output
            $OutputString = "Drive " + $DriveLetter + $CurrentEncryptPercent.ToString() + "% encrypted"
            Write-Output $OutputString
            $LastEncryptPercent = (Get-BitLockerVolume -MountPoint $DriveLetter).EncryptionPercentage #Since the encryption percentage has changed, lets store the this percentage as our "last" (most recent)
        }
        Start-Sleep -Seconds 300  #wait five minutes before checking again
    }
    while ($CurrentEncryptPercent -ne 0)
}
Write-Output (Get-BitLockerVolume)

---------------------------------



For Win7
#Decrypt-BitlockerDrives.PS1
#Script by Mark Randol
#randoltech.blogspot.com
#
#This script will list out all of the encryptable volumes on the local machine and decrypt them
#Do not exit until decryption is completed.
#there are simpler ways to do this with modern Powershell commands (Get-BitLockerVolume for example)
#but those methods do not work with a native Windows 7 PowerShell environment so this was
#developed to help facilitate Windows 7 to Windows 10 migration.
$WMINameSpace = "root\CIMv2\Security\MicrosoftVolumeEncryption"
$WMIClass = "Win32_EncryptableVolume"
$BitLockerDrives = (Get-Wmiobject -Namespace $WMINameSpace -Class $WMIClass -ComputerName $env:COMPUTERNAME).DriveLetter
foreach ($LockedDrive in $BitLockerDrives) {
    $Status = (Get-Wmiobject -Namespace $WMINameSpace -Class $WMIClass -ComputerName $env:COMPUTERNAME -Filter “DriveLetter=""$LockedDrive""”).ConversionStatus
    if ($Status -ne 0) {
        if ($Status -eq 1) {
            Invoke-Command {manage-bde.exe -off C:}
        }
}
foreach ($LockedDrive in $BitLockerDrives) {
    $Status = (Get-Wmiobject -Namespace $WMINameSpace -Class $WMIClass -ComputerName $env:COMPUTERNAME -Filter “DriveLetter=""$LockedDrive""”).ConversionStatus
    if ($Status -ne 0) {
        do {
            Start-Sleep 15
            $Status = (Get-Wmiobject -Namespace $WMINameSpace -Class $WMIClass -ComputerName $env:COMPUTERNAME -Filter “DriveLetter=""$LockedDrive""”).ConversionStatus
        }
        until ($Status -eq 0)
    }
}
{Exit $LASTEXITCODE}


Wednesday, January 16, 2019

Script to stamp the registry with your OSD variables


# Script by Mark Randol
# randoltech.blogspot.com

$registryPath = "HKLM:\Software\MyCompany\SCCM Operating System Deployment"
[String[]]$OSDVariables = "OSArchitecture","OSDAnswerFilePath","OSDComputerName","OSDImagePackageId","OSDImageVersion","OSDTargetSystemDrive","OSDTargetSystemParition","OSDTargetSystemRoot","OSVersionNumber","_OSDOSImagePackageId","_OSDTargetSystemRoot","_SMSTSAdvertID","_SMSTSAssignedSiteCode","_SMSTSBootImageID","_SMSTSBootMediaPackageID","_SMSTSLaunchMode","_SMSTSLogPath","_SMSTSMachineName","_SMSTSMediaType","_SMSTSOrgName","_SMSTSPackageID","_SMSTSPackageName","_SMSTSSiteCode","_SMSTSStandAloneMedia","_SMSTSSupportUnknownMachines","_SMSTSUserStatePath"

if (!(Test-Path $registryPath)) { New-Item -Path $registryPath -Force | Out-Null }

$InstallDate = Get-Date
New-ItemProperty -Path $registryPath -Name "OSInstallDateTime" -Value $InstallDate -PropertyType STRING -Force | Out-Null

$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
foreach ($OSDVariableName in $OSDVariables)
{
  $OSDVariableValue = $tsenv.Value($OSDVariableName)
  New-ItemProperty -Path $registryPath -Name $OSDVariableName -Value $OSDVariableValue -PropertyType STRING -Force | Out-Null
}

Thursday, January 10, 2019

Command lines for creating a bootable USB thumb drive

Command lines for creating a bootable USB thumb drive:

diskpart
list disk
sel dis #
clean
create par pri
sel par 1
format fs=ntfs quick <--- for Legacy
format fs=fat32 quick <--- for UEFI
act
exit

Thursday, October 18, 2018

Step-by-Step how PXE boots a machine using SCCM OSD

I had a problem that was quite difficult to work through and in order to figure it out I had to go really deep on PXE.  Figured this step-by-step might help someone else in the future.

  1. The network boot client computer sends a broadcast to entire network with option 60 (on any normal network this will only actually broadcast on the local subnet but IP helpers generally get it to the DHCP server).
  2. Both DHCP and the WDS server get the broadcast (either both are assigned as DHCP servers with IP helpers or DHCP options are set to forward the request to the WDS).
  3. DHCP offers an IP address to the client (keyword "offers", this hasn't been accepted yet).
  4. Before the client machine accepts the IP address it waits for a signal from the WDS server WDS.  Before sending the signal back to the client the WDS sever runs a stored procedure, LOOKUPDEVICE, against the SCCM database.  If the client machine is found in SCCM or if there is an advertisement for "Unknown Machines" collection then WDS signals the client to proceed with the PXE boot.
  5. The client machine now accepts the IP offered by DHCP.
  6. DHCP DORA finally completes when the DHCP server acknowledges the client IP assignment.  The client machine now has an IP address and is ready to proceed.
  7. The client machine downloads WDSNBP.COM from PXE server to detect the hardware architecture (x86 or x64)
  8. The client downloads the PXEBOOT.COM boot files for its architecture from PXE server.  The file downloaded at this step is controlled/ monitored by SMSPXE.
  9. SMSPXE runs a stored procedure called getbootaction and depending on the result, it gives the PXE boot files to client.
  10. The client machine now downloads the Boot image, bootmgr.exe and BCD store.  This is an SMB file transfer, all previous file transfers were TFTP. Boot image downloaded here would be dependent on the result of the architecture detection done earlier by WDSNBP file.
  11. Once the Boot image and the other two files are downloaded completely BootMGR and BCD store are used to initialize the WINPE environment.
For my particular problem it turned out to be a bad switch dropping some packets.  It didn't really present itself with the tiny little TFTP (UDP) downloads but as soon as we hit the first SMB file transfer (TCP) things failed.  Made it look like a DHCP handoff problem when it was actually a file transfer problem.  Would never have found it without understanding how this works.

Enjoy!

Another good reference on network boot process:
https://blogs.technet.microsoft.com/dominikheinz/2011/03/18/sccm-pxe-network-boot-process

Friday, August 24, 2018

Powershell Script to prompt for computer name during OSD


function Load-Form {
    $Form.Controls.Add($TBComputerName)
    $Form.Controls.Add($GBComputerName)
    $Form.Controls.Add($ButtonOK)
    $Form.Add_Shown({$Form.Activate()})
    [void] $Form.ShowDialog()
}

function Set-OSDComputerName {
    $ErrorProvider.Clear()
    if ($TBComputerName.Text.Length -eq 0) {
        $ErrorProvider.SetError($GBComputerName, "Please enter a computer name")
    }
    else {
        if ($TBComputerName.Text.Length -gt 15) {
            $ErrorProvider.SetError($GBComputerName, "Computer name cannot be more than 15 characters")
        }
        else {
            $OSDComputerName = $TBComputerName.Text.Replace("[","").Replace("]","").Replace(":","").Replace(";","").Replace("|","").Replace("=","").Replace("+","").Replace("*","").Replace("?","").Replace("<","").Replace(">","").Replace("/","").Replace("\","").Replace(",","")
            $TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment
            $TSEnv.Value("OSDComputerName") = "$($OSDComputerName)"
            $Form.Close()
        }
    }
}

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

$Global:ErrorProvider = New-Object System.Windows.Forms.ErrorProvider

$Form = New-Object System.Windows.Forms.Form   
$Form.Size = New-Object System.Drawing.Size(285,140) 
$Form.MinimumSize = New-Object System.Drawing.Size(285,140)
$Form.MaximumSize = New-Object System.Drawing.Size(285,140)
$Form.StartPosition = "CenterScreen"
$Form.SizeGripStyle = "Hide"
$Form.Text = "Enter Computer Name"
$Form.ControlBox = $false
$Form.TopMost = $true

$TBComputerName = New-Object System.Windows.Forms.TextBox
$TBComputerName.Location = New-Object System.Drawing.Size(25,30)
$TBComputerName.Size = New-Object System.Drawing.Size(215,50)
$TBComputerName.TabIndex = "1"

$GBComputerName = New-Object System.Windows.Forms.GroupBox
$GBComputerName.Location = New-Object System.Drawing.Size(20,10)
$GBComputerName.Size = New-Object System.Drawing.Size(225,50)
$GBComputerName.Text = "Computer name:"

$ButtonOK = New-Object System.Windows.Forms.Button
$ButtonOK.Location = New-Object System.Drawing.Size(195,70)
$ButtonOK.Size = New-Object System.Drawing.Size(50,20)
$ButtonOK.Text = "OK"
$ButtonOK.TabIndex = "2"
$ButtonOK.Add_Click({Set-OSDComputerName})

Load-Form

Friday, August 17, 2018

WinPE Version List

WinPE Version List (Updated June 16, 2018)

The table below shows the main versions of WinPE that you will see in the wild, along with their WinPE version, the Windows version name, and the numeric Windows version string that it was built from.
WinPEWindowsWindows VersionNotes
1.0Windows XP5.1.2600.xFirst version of WinPE.
1.1Windows XP SP15.1.2600.x
1.2Windows Server 20035.2.3790.x
1.5Windows XP SP25.1.2600.xWindows PE 2004.
1.6Windows Server 2003 SP15.2.3790.xWindows PE 2005.
2.0Windows Vista6.0.6000.x
2.1Windows Server 20086.0.6001.x
2.2Windows Server 2008 SP26.0.6002.x
3.0Windows 76.1.7600.xWindows AIK 2.0.
3.1Windows 7 SP16.1.7601.xWindows AIK Supplement for Windows 7 SP1.
4.0Windows 86.2.9200.xWindows ADK (Windows Kits 8.0).
5.0Windows 8.16.3.9300.xWindows ADK (Windows Kits 8.1).
5.1Windows 8.1 Update 16.3.9600.xWindows ADK (Windows Kits 8.1 Update).
10 (1507)Windows 10 150710.0.10240.16384Windows ADK (Windows Kits 10.0) 1507
10 (1511)Windows 10 151110.0.10586.0Windows ADK (Windows Kits 10.0) 1511
10 (1607)Windows 10 160710.0.14393.0Windows ADK (Windows Kits 10.0) 1607
10 (1703)Windows 10 170310.0.15063.0Windows ADK (Windows Kits 10.0) 1703
10 (1709)Windows 10 170910.0.16299.15Windows ADK (Windows Kits 10.0) 1709
10 (1803)Windows 10 180310.0.17134.1Windows ADK (Windows Kits 10.0) 1803