The Deployment Bunny

OS Deployment, Virtualization, Microsoft based Infrastructure…

  • about.me

    about.me/mikaelnystrom

  • Archives

  • Meta

Posts Tagged ‘MDT’

OSD – BIOS upgrade during OS Deployment in MDT/ConfigMgr (v3)

Posted by Mikael Nystrom on July 20, 2016

This is the third version of the script solution; it is very simple. You detect make/model in any way you would like to, create a rule based on BIOS version, if the rule match, nothing happens, otherwise it runs any command you want. That said, you could use this for other task as well. Most common question is “does it work with any Make/Model”`No, there are some vendors that does not provide the ability to run a BIOS upgrade silently without reboot without control, that is, the darn thing reboots immediately and that usually breaks the Task Sequence. I have one thing for those vendors to say “Shame on you!”

here are older posts on the subject

https://deploymentbunny.com/2011/05/20/step-by-step-upgrading-bios-during-litetouch-deployment-for-dell-hewlett-packard-and-lenovo/

https://deploymentbunny.com/2013/12/16/step-by-step-upgrading-bios-during-litetouchzerotouch-deployment-for-dell-hewlett-packard-and-lenovo-v2/

Detect the Model

The detection in the script is actually whatever you want, it is just a “If-then”  here are two sample lines detection that you will find in the script

if($((Get-WmiObject Win32_ComputerSystem).model) -eq 'HP EliteBook 8560w'){}
if($ModelAlias -eq 'HP EliteBook 8460p'){}

Use the detection method for each model you like, very simple

Detect the BIOS version

This is also not that hard, here are 2 sample lines from the script

if($((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion) -ne '68SCF Ver. F.63'){}
if($((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion) -ne '786G1 v01.27'){}

Store the upgrade files

The upgrade files are stored with the model name, like this:

image

The Silent Command

You also need to figure out the command, not really hard, download the bios upgrade, read how to run it from a command line, update the script and you are done, here is a sample of one of those sections.

$Exe = 'hpqflash.exe'
$Location = "$SCRIPTDIR\Source\HP EliteBook 8460p"
$Executable = $Location + "\" + $exe
Set-Location -Path $Location
Invoke-Exe -Executable "$Executable" -Arguments "/s /f 68SCE.CAB" –Verbose

Run the script

in MDT you can import the folder with the script as a application and set this as the command line:

PowerShell.exe -ExecutionPolicy Bypass -File Install-BIOSUpgrade.ps1

In ConfigMgr you can import this as a Package and then run the command line the same way

It is also possible to run this outside of a task sequence if you like, it works as a stand alone script, however, you cannot use the integration with MDT of course.

If you want it is possible to add a custom property in customsettings.ini, something like “NeedReboot”, then you can add the following to happen if the BIOS has been upgraded

$tsenv.Value(“NeedReboot”) = “YES”

If you then in the step after this, set a condition on a reboot step, well then it will reboot when needed, otherwise not, you can read about that in one of my old postings here

https://deploymentbunny.com/2013/12/16/step-by-step-upgrading-bios-during-litetouchzerotouch-deployment-for-dell-hewlett-packard-and-lenovo-v2/

The result

BiosUpgradeResult
It was needed.

BiosUpgradeResultNotNeeded
It was not needed.

The Script


Function Import-SMSTSENV{
    try
    {
        $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
        Write-Output "$ScriptName - tsenv is $tsenv "
        $MDTIntegration = "YES"
        
        #$tsenv.GetVariables() | % { Write-Output "$ScriptName - $_ = $($tsenv.Value($_))" }
    }
    catch
    {
        Write-Output "$ScriptName - Unable to load Microsoft.SMS.TSEnvironment"
        Write-Output "$ScriptName - Running in standalonemode"
        $MDTIntegration = "NO"
    }
    Finally
    {
    if ($MDTIntegration -eq "YES"){
        $Logpath = $tsenv.Value("LogPath")
        $LogFile = $Logpath + "\" + "$ScriptName.log"

    }
    Else{
        $Logpath = $env:TEMP
        $LogFile = $Logpath + "\" + "$ScriptName.log"
    }
    }
}
Function Start-Logging{
    start-transcript -path $LogFile -Force
}
Function Stop-Logging{
    Stop-Transcript
}
Function Invoke-Exe{
    [CmdletBinding(SupportsShouldProcess=$true)]
 
    param(
        [parameter(mandatory=$true,position=0)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Executable,
 
        [parameter(mandatory=$false,position=1)]
        [string]
        $Arguments
    )
 
    if($Arguments -eq "")
    {
        Write-Verbose "Running $ReturnFromEXE = Start-Process -FilePath $Executable -ArgumentList $Arguments -NoNewWindow -Wait -Passthru"
        $ReturnFromEXE = Start-Process -FilePath $Executable -NoNewWindow -Wait -Passthru
    }else{
        Write-Verbose "Running $ReturnFromEXE = Start-Process -FilePath $Executable -ArgumentList $Arguments -NoNewWindow -Wait -Passthru"
        $ReturnFromEXE = Start-Process -FilePath $Executable -ArgumentList $Arguments -NoNewWindow -Wait -Passthru
    }
    Write-Verbose "Returncode is $($ReturnFromEXE.ExitCode)"
    Return $ReturnFromEXE.ExitCode
}

# Set vars
$SCRIPTDIR = split-path -parent $MyInvocation.MyCommand.Path
$SCRIPTNAME = split-path -leaf $MyInvocation.MyCommand.Path
$SOURCEROOT = "$SCRIPTDIR\Source"
$SettingsFile = $SCRIPTDIR + "\" + $SettingsName
$LANG = (Get-Culture).Name
$OSV = $Null
$ARCHITECTURE = $env:PROCESSOR_ARCHITECTURE

#Try to Import SMSTSEnv
. Import-SMSTSENV

# Set more vars
$Make = $tsenv.Value("Make")
$Model = $tsenv.Value("Model")
$ModelAlias = $tsenv.Value("ModelAlias")
$MakeAlias = $tsenv.Value("MakeAlias")

#Start Transcript Logging
. Start-Logging

#Output base info
Write-Output ""
Write-Output "$ScriptName - ScriptDir: $ScriptDir"
Write-Output "$ScriptName - SourceRoot: $SOURCEROOT"
Write-Output "$ScriptName - ScriptName: $ScriptName"
Write-Output "$ScriptName - Current Culture: $LANG"
Write-Output "$ScriptName - Integration with MDT(LTI/ZTI): $MDTIntegration"
Write-Output "$ScriptName - Log: $LogFile"
Write-Output "$ScriptName - Model (win32_computersystem): $((Get-WmiObject Win32_ComputerSystem).model)"
Write-Output "$ScriptName - Name (Win32_ComputerSystemProduct): $((Get-WmiObject Win32_ComputerSystemProduct).Name)"
Write-Output "$ScriptName - Version (Win32_ComputerSystemProduct): $((Get-WmiObject Win32_ComputerSystemProduct).Version)"
Write-Output "$ScriptName - Model (from TSENV): $Model"
Write-Output "$ScriptName - ModelAlias (from TSENV): $ModelAlias"

#Check Model
if($((Get-WmiObject Win32_ComputerSystem).model) -eq 'HP EliteBook 8560w'){
    Write-Output "Model is $((Get-WmiObject Win32_ComputerSystem).model)"
    Write-Output "Checking BIOS Version"
    Write-Output "Version is $((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion)"
    if($((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion) -ne '68SVD Ver. F.50'){
        Write-Output "Needs upgrade"
        $Exe = 'hpqflash.exe'
        $Location = "$SCRIPTDIR\Source\HP EliteBook 8560w"
        $Executable = $Location + "\" + $exe
        Set-Location -Path $Location
        Invoke-Exe -Executable "$Executable" -Arguments "/s /p LCadmin1.bin" -Verbose
    }
    else
    {
        Write-Output "No Need to upgrade"
    }
}
if($((Get-WmiObject Win32_ComputerSystem).model) -eq 'HP ProBook 6570b'){
    Write-Output "Model is $((Get-WmiObject Win32_ComputerSystem).model)"
    Write-Output "Checking BIOS Version"
    Write-Output "Version is $((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion)"
    if($((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion) -Like '*ICE*'){
        if($((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion) -ne '68ICE Ver. F.62'){
            Write-Output "Needs upgrade"
            $Exe = 'hpqflash.exe'
            $Location = "$SCRIPTDIR\Source\HP ProBook 6570b"
            $Executable = $Location + "\" + $exe
            Set-Location -Path $Location
            Invoke-Exe -Executable "$Executable" -Arguments "/s /f 68ICE.cab" -Verbose
        }
        else
        {
            Write-Output "No Need to upgrade"
        }
    }
    if($((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion) -Like '*ICF*'){
        if($((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion) -ne '68ICF Ver. F.62'){
            Write-Output "Needs upgrade"
            $Exe = 'hpqflash.exe'
            $Location = "$SCRIPTDIR\Source\HP ProBook 6570b"
            $Executable = $Location + "\" + $exe
            Set-Location -Path $Location
            Invoke-Exe -Executable "$Executable" -Arguments "/s /f 68ICF.cab" -Verbose
        }
        else
        {
            Write-Output "No Need to upgrade"
        }
    }
}
if($ModelAlias -eq 'HP EliteBook 8460p'){
    Write-Output "Model is $((Get-WmiObject Win32_ComputerSystem).model)"
    Write-Output "Checking BIOS Version"
    Write-Output "Version is $((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion)"
    if($((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion) -Like '*SCF*'){
        if($((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion) -ne '68SCF Ver. F.63'){
            Write-Output "Needs upgrade"
            $Exe = 'hpqflash.exe'
            $Location = "$SCRIPTDIR\Source\HP EliteBook 8460p"
            $Executable = $Location + "\" + $exe
            Set-Location -Path $Location
            Invoke-Exe -Executable "$Executable" -Arguments "/s /f 68SCF.CAB" -Verbose
            }
        else
            {
            Write-Output "No Need to upgrade"
        }
    }
    if($((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion) -Like '*SCE*'){
        if($((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion) -ne '68SCE Ver. F.63'){
            Write-Output "Needs upgrade"
            $Exe = 'hpqflash.exe'
            $Location = "$SCRIPTDIR\Source\HP EliteBook 8460p"
            $Executable = $Location + "\" + $exe
            Set-Location -Path $Location
            Invoke-Exe -Executable "$Executable" -Arguments "/s /f 68SCE.CAB" -Verbose
            }
        else
            {
            Write-Output "No Need to upgrade"
        }
    }
}
if($((Get-WmiObject Win32_ComputerSystem).model) -eq 'HP Compaq dc7900 Small Form Factor'){
    Write-Output "Model is $((Get-WmiObject Win32_ComputerSystem).model)"
    Write-Output "Checking BIOS Version"
    Write-Output "Version is $((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion)"
    if($((Get-WmiObject Win32_Bios).SMBIOSBIOSVersion) -ne '786G1 v01.27'){
        Write-Output "Needs upgrade"
        $Exe = 'hpqflash.exe'
        $Location = "$SCRIPTDIR\Source\HP Compaq dc7900 Small Form Factor\HPQFlash"
        $Executable = $Location + "\" + $exe
        $SourceFile = $Location + "\" + "Password01.bin"
        $Destination = $env:TEMP
        $DestinationFile = $Destination + "\" + "Password01.bin"
        Copy-Item -Path $SourceFile -Destination $DestinationFile -Force -Verbose 
        Set-Location -Path $Location
        Invoke-Exe -Executable $Executable -Arguments "/s /p $DestinationFile"
    }
    else
    {
        Write-Output "No Need to upgrade"
    }
}

#Stop Logging
. Stop-Logging


/mike

Posted in BIOS, ConfigMgr, MDT, OS Deployment, OSD, PowerShell | Tagged: , , , , , | 2 Comments »

OSD – The future of MDT is going to be PowerShell!

Posted by Mikael Nystrom on May 18, 2016

At MMSmoa today, Michael Niehaus presented a new approach for MDT, the shift from VBscript to PowerShell. They idea is to get the code up on GitHub. The basic engine and framework will be done, but it will allow for contributors to test and verify and even provide suggestions.

You can check it out here: https://github.com/mtniehaus/PSD

The New Task Sequence:

image

image

The Install App Step:

image

/mike

Posted in MDT | Tagged: | 6 Comments »

OSD – Export drivers using a task sequence in Lite Touch and some PowerShell

Posted by Mikael Nystrom on May 18, 2016

Yesterday i did a demo of a Task Sequence I use to extract drivers from a computer that already have all drivers correctly installed, could be a system that I need to reinstall, or a new machine with fairly new drivers installed. The Task Sequence basically grabs information from the computer, such as Operating System, Architecture and Model or Modelalias, grabs the drivers and copy them to the deployment share. I can then import the drivers to a ConfigMgr package or use them in LiteTouch for deployment.

The script is fairly simple and easy to change to fit in your environment.

The Task Sequence

It contains 3 steps, a gather step to get the inventory of the machine correctly, a set finish action to avoid reboot if your are using finish action in customsettings.ini and the application that grabs the drivers. Since it is a application this task sequence works only when Windows is running, not in WinPE.

image
The Task Sequence.

The Application

The Script runs as an application, so you need to download the script to a folder and then import it as an application with following settings:

PowerShell.exe -ExecutionPolicy Bypass -File ExportDrivers.ps1

image
The Extract Drivers Application in Deployment Workbench.

The Script

The PowerShell script is a generic script I use a s “wrapper”, so it does have functions that is not really needed in this scenario, so it is possible to make it shorter if you for any reason want that. The script detects if it has been invoked from a task sequence or not, if it has, it will create a path based on deployment root, Operating System, Architecture and ModelAlias (If you don’t use ModelAlias UserExit, start to do that or change to Model in the script), otherwise it will export the drivers to C:\ExportedDrivers

The active part of the script looks like this:

image
The active part of the script.

Note that it will delete all drivers that begins with PRN. That is printer drivers and I usually do not want them.

ExportDrivers.ps1

<#
 Install Wrapper 1.0
 Author: Mikael Nystrom
 http://www.deploymentbunny.com 
#>
 
Function Invoke-Exe{
    [CmdletBinding(SupportsShouldProcess=$true)]
 
    param(
        [parameter(mandatory=$true,position=0)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Executable,
 
        [parameter(mandatory=$false,position=1)]
        [string]
        $Arguments
    )
 
    if($Arguments -eq "")
    {
        Write-Verbose "Running $ReturnFromEXE = Start-Process -FilePath $Executable -ArgumentList $Arguments -NoNewWindow -Wait -Passthru"
        $ReturnFromEXE = Start-Process -FilePath $Executable -NoNewWindow -Wait -Passthru
    }else{
        Write-Verbose "Running $ReturnFromEXE = Start-Process -FilePath $Executable -ArgumentList $Arguments -NoNewWindow -Wait -Passthru"
        $ReturnFromEXE = Start-Process -FilePath $Executable -ArgumentList $Arguments -NoNewWindow -Wait -Passthru
    }
    Write-Verbose "Returncode is $($ReturnFromEXE.ExitCode)"
    Return $ReturnFromEXE.ExitCode
}
Function Get-OSVersion([ref]$OSv){
    $OS = Get-WmiObject -class Win32_OperatingSystem
    Switch -Regex ($OS.Version)
    {
    "6.1"
        {If($OS.ProductType -eq 1)
            {$OSv.value = "Windows 7 SP1"}
                Else
            {$OSv.value = "Windows Server 2008 R2"}
        }
    "6.2"
        {If($OS.ProductType -eq 1)
            {$OSv.value = "Windows 8"}
                Else
            {$OSv.value = "Windows Server 2012"}
        }
    "6.3"
        {If($OS.ProductType -eq 1)
            {$OSv.value = "Windows 8.1"}
                Else
            {$OSv.value = "Windows Server 2012 R2"}
        }
    "10."
        {If($OS.ProductType -eq 1)
            {$OSv.value = "Windows 10"}
                Else
            {$OSv.value = "Windows Server 2016"}
        }
    DEFAULT { "Version not listed" }
    } 
}
Function Import-SMSTSENV{
    try
    {
        $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
        Write-Output "$ScriptName - tsenv is $tsenv "
        $MDTIntegration = "YES"
         
        #$tsenv.GetVariables() | % { Write-Output "$ScriptName - $_ = $($tsenv.Value($_))" }
    }
    catch
    {
        Write-Output "$ScriptName - Unable to load Microsoft.SMS.TSEnvironment"
        Write-Output "$ScriptName - Running in standalonemode"
        $MDTIntegration = "NO"
    }
    Finally
    {
    if ($MDTIntegration -eq "YES"){
        $Logpath = $tsenv.Value("LogPath")
        $LogFile = $Logpath + "\" + "$ScriptName.log"
 
    }
    Else{
        $Logpath = $env:TEMP
        $LogFile = $Logpath + "\" + "$ScriptName.txt"
    }
    }
}
Function Start-Logging{
    start-transcript -path $LogFile -Force
}
Function Stop-Logging{
    Stop-Transcript
}
 
# Set Vars
$SCRIPTDIR = split-path -parent $MyInvocation.MyCommand.Path
$SCRIPTNAME = split-path -leaf $MyInvocation.MyCommand.Path
$SOURCEROOT = "$SCRIPTDIR\Source"
$LANG = (Get-Culture).Name
$OSV = $Null
$ARCHITECTURE = $env:PROCESSOR_ARCHITECTURE
 
#Try to Import SMSTSEnv
. Import-SMSTSENV
 
#Start Transcript Logging
. Start-Logging
 
#Detect current OS Version
. Get-OSVersion -osv ([ref]$osv) 
 
#Output base info
Write-Output ""
Write-Output "$ScriptName - ScriptDir: $ScriptDir"
Write-Output "$ScriptName - SourceRoot: $SOURCEROOT"
Write-Output "$ScriptName - ScriptName: $ScriptName"
Write-Output "$ScriptName - OS Name: $OSV"
Write-Output "$ScriptName - OS Architecture: $ARCHITECTURE"
Write-Output "$ScriptName - Current Culture: $LANG"
Write-Output "$ScriptName - Integration with MDT(LTI/ZTI): $MDTIntegration"
Write-Output "$ScriptName - Log: $LogFile"
 
if($MDTIntegration -eq "YES"){
    $RootFolder = $tsenv.Value("Deployroot")
    $Arch = $tsenv.Value("Architecture")
    $Model = $tsenv.Value("ModelAlias")
    $Path = $RootFolder + "\Drivers\" + $OSV + "\" + $Arch + "\" + $Model
}
else{
    $Path = "C:\ExportedDrivers"
}

Write-Output "$ScriptName - Driver export path: $Path"

#Export Drivers
Export-WindowsDriver -Destination $Path -Online -Verbose

#Get Printer Drivers
Get-ChildItem -Path $Path -Filter PRN* -Directory | Remove-Item -Force -Recurse

. Stop-Logging

/mike

Posted in Lite Touch, MDT, OS Deployment, OSD | Tagged: , , , | 6 Comments »

OSD – In MDT 2013 Update 2–Sometimes “WimSplit” works and sometimes not

Posted by Mikael Nystrom on May 11, 2016

and i do not like “sometimes”. The need for WimSplit is big, the most common reason is to install Windows on a UEFI based machine. In that case the boot media must be FAT32 and a single file cannot be larger then 4095 MB, but a plain vanilla Windows Server 2016 is bigger, so…

The fix

Make sure that the operating system name ends with .WIM

image

Make sure that the Settings.XML file in the Deployment share\Control folder has the following setting:

image

And, look it is working…

image

/mike

Posted in MDT, OS Deployment, OSD | Tagged: , , | 7 Comments »

OS Deployment – Using the PowerShell to work with the MDT Database module: Sample 1

Posted by Mikael Nystrom on April 22, 2016

During the OSD class in Phoenix this week we worked with the MDT Database and some one asked if it was possible to use PowerShell to modify the database and and the same time verify if the mac address or the computer name was already in use before creating the database entry. The short answer was –Yes, of course. So I decided to create a sample on how that could look like.

Working with the database is pretty simple using the PowerShell module that Michael Niehaus created https://blogs.technet.microsoft.com/mniehaus/2009/05/14/manipulating-the-microsoft-deployment-toolkit-database-using-powershell/

So, using that module and the Active Directory PowerShell module means that we can now check if the Mac Address is already in use or if the computer name already exists in the MDT database or in Active Directory. The PowerShell script sample is using regex to verify that Mac Address as well as the computer name. Besides creating the object if it does not exist (or you can use the –Force switch to override) it also adds a database role to the computer.

Note: That you should also fix the database, since it is broken by default, just follow these steps: https://syscenramblings.wordpress.com/2016/01/15/mdt-database-the-powershell-module-fix/

The Script:


#Add Computer to the MDT Database 1.0
Param(
    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [ValidateLength(3,16)]
    [ValidatePattern('[A-Z][0-9]')]
    [String]$ComputerName,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [ValidateSet('Standard PC','RnD','Admin Workstation')] 
    [String]$Role,

    [Parameter(Mandatory=$true,ValueFromPipeline=$true)] 
    [ValidatePattern('^([0-9a-fA-F]{2}[:-]{0,1}){5}[0-9a-fA-F]{2}$')]
    [String]$MacAddress,

    [Switch]$Force
)

#Import the Modules and connect to the database
Import-Module MDTDB -ErrorAction Stop
Import-Module ActiveDirectory -ErrorAction Stop
Connect-MDTDatabase -sqlServer MDT01 -database MDT01 -instance SQLExpress -ErrorAction stop

#Create function for check AD if name exists
Function CheckIfComputerInADExists{
    Param(
        $ComputerName
    )
    try {
        $Null = Get-ADComputer $ComputerName
        Return $True
    }
    Catch {
        Return $False
    }
}

#Create function for check MDT DB if name exists
Function CheckIfComputerInMDTExists{
    Param(
        $ComputerName
    )
    $result = Get-MDTComputer | Where-Object -Property OSDComputerName -EQ -Value $ComputerName
    if($result -ne $null){Return $True}else{$False}
}

#Create function for check MDT DB if MAC exists
Function CheckIfMacAddressInMDTExists{
    Param(
        $MacAddress
    )
    $result = Get-MDTComputer -macAddress $MacAddress
    if($result -ne $null){Return $True}else{$False}
}

#Check if Computer exists in Active Directory
$CheckAD = CheckIfComputerInADExists -ComputerName $ComputerName
if($CheckAD -eq $true){
    Write-Warning "$ComputerName exists in Active Directory"
    if(!($Force)){BREAK}
    }else{Write-Host "AD Name check OK"}

#Check if Computer exists in the MDT database
$CheckMDT = CheckIfComputerInMDTExists -ComputerName $ComputerName
if($CheckMDT -eq $true){
    Write-Warning "$ComputerName exists in the MDT database"
    if(!($Force)){BREAK}
    }else{Write-Host "MDT name check OK"}

#Check if MacAddress exists in the MDT database
$CheckMAC = CheckIfMacAddressInMDTExists -MacAddress $MacAddress
if($CheckMAC -eq $true){
    Write-Warning "$MacAddress exists in the MDT database"
    if(!($Force)){BREAK}
    }else{Write-Host "MDT macaddress check OK"}

#Create array for all settings the computer should have
$Settings = @{
OSInstall='YES';
OSDComputerName="$ComputerName"
}

#If computer name exists and we used the -Force switch, remove it
if($CheckMDT -eq $true){
    Get-MDTComputer -description $ComputerName | Remove-MDTComputer
    }

#If MacAddress exists and we used the -Force switch, remove it
if($CheckMAC -eq $true){
    Get-MDTComputer -macAddress $MacAddress | Remove-MDTComputer
    }

#Create Computer in MDT Database
$NewMDTComputer = New-MDTComputer -macAddress $MacAddress -description $ComputerName -settings $Settings

#Add role to Computer in MDT Database
$NewMDTComputer | Set-MDTComputerRole -roles $Role


/mike

Posted in MDT, OS Deployment, OSD, PowerShell | Tagged: , , , | Leave a Comment »

OSD – Update your boot image on all PXE servers with one PowerShell script (LiteTouch)

Posted by Mikael Nystrom on March 9, 2016

Working at a customer (you know who you are) I asked

– How do you update all PXE\WDS servers around the globe?

– We logon and update them

– Would you like to have script that does that?

– Yes

So, here it is:


$Servers = "server1","Server2","Server3","Server4"
Foreach($Server in $Servers){
    wdsutil.exe /Verbose /Progress /Replace-Image /Image:"Lite Touch Windows PE (x86)" /Server:$Server /ImageType:Boot /Architecture:x86 /ReplacementImage /ImageFile:"D:\MDTProduction\Boot\LiteTouchPE_x86.wim" 
    wdsutil.exe /Verbose /Progress /Replace-Image /Image:"Lite Touch Windows PE (x64)" /Server:$Server /ImageType:Boot /Architecture:x64 /ReplacementImage /ImageFile:"D:\MDTProduction\Boot\LiteTouchPE_x64.wim" 
}

/mike

Posted in MDT, OS Deployment, OSD | Tagged: , | Leave a Comment »

PowerShell is King – Get MDT Monitor data using the OData Feed using a PowerShell Function

Posted by Mikael Nystrom on March 7, 2016

I have done same blogposts that includes the usage of the MDT Monitor:

https://deploymentbunny.com/2013/03/06/powershell-is-king-i-need-to-monitor-os-deployment-in-mdt-2012-not-using-deployment-workbench/

https://deploymentbunny.com/2013/12/09/nice-to-know-dumping-mdt-monitor-data-to-a-webpage-using-powershell/

https://deploymentbunny.com/2013/12/13/nice-to-know-dumping-mdt-monitor-data-to-a-webpage-using-powershell-update/

and they all use the same basic function.

During class last week I’ve got the question (You know who you are) –Is it possible to get the Dart information? and just two days later I’ve got on other question on how to extract the Step name, so, here it is a slightly updated and modified PowerShell function that will extract data from the MDT Monitor OData feed without the use of any MDT binary’s.

The updated Function:

Function Get-MDTOData{
    <#
    .Synopsis
        Function for getting MDTOdata
    .DESCRIPTION
        Function for getting MDTOdata
    .EXAMPLE
        Get-MDTOData -MDTMonitorServer MDTSERVER01
    .NOTES
        Created:	 2016-03-07
        Version:	 1.0

        Author - Mikael Nystrom
        Twitter: @mikael_nystrom
        Blog   : http://deploymentbunny.com

    .LINK
        http://www.deploymentbunny.com
    #>
    Param(
    $MDTMonitorServer
    ) 
    $URL = "http://" + $MDTMonitorServer + ":9801/MDTMonitorData/Computers"
    $Data = Invoke-RestMethod $URL
    foreach($property in ($Data.content.properties) ){
        $Hash =  [ordered]@{ 
            Name = $($property.Name); 
            PercentComplete = $($property.PercentComplete.’#text’); 
            Warnings = $($property.Warnings.’#text’); 
            Errors = $($property.Errors.’#text’); 
            DeploymentStatus = $( 
            Switch($property.DeploymentStatus.’#text’){ 
                1 { "Active/Running"} 
                2 { "Failed"} 
                3 { "Successfully completed"} 
                Default {"Unknown"} 
                }
            );
            StepName = $($property.StepName);
            TotalSteps = $($property.TotalStepS.'#text')
            CurrentStep = $($property.CurrentStep.'#text')
            DartIP = $($property.DartIP);
            DartPort = $($property.DartPort);
            DartTicket = $($property.DartTicket);
            VMHost = $($property.VMHost.'#text');
            VMName = $($property.VMName.'#text');
            LastTime = $($property.LastTime.'#text') -replace "T"," ";
            StartTime = $($property.StartTime.’#text’) -replace "T"," "; 
            EndTime = $($property.EndTime.’#text’) -replace "T"," "; 
            }
        New-Object PSObject -Property $Hash
    }
}

And is here some examples on how you can use the data:

Pure Vanilla Commandline:

Get-MDTOData -MDTMonitorServer SRVMDT01

image

Using GridView

Get-MDTOData -MDTMonitorServer SRVMDT01 | Out-GridView

image

Create a WebPage
$Title = "ViaMonstra Deployment Status"
$Head = "<style>"
$Head = $Head + "BODY{background-color:peachpuff;}"
$Head = $Head + "TABLE{border-width: 2px;border-style: solid;border-color: black;border-collapse: collapse;}"
$Head = $Head + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:thistle}"
$Head = $Head + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black}"
$Head = $Head + "</style>"

Get-MDTOData -MDTMonitorServer SRVMDT01 | Sort -Property Name |
ConvertTo-Html  `
-Title $Title  `
-Head $Head  `
-Body (Get-Date -UFormat "%Y-%m-%d - %T ")  `
-PreContent "<H2>$Title for: $ENV:COMPUTERNAME </H2><P>Generated by Power of the Force</P>"  `
-PostContent "<P>For details, contact support@viamonstra.com</P>"  `
-Property Name,PercentComplete,Warnings,Errors,DeploymentStatus,StepName,TotalSteps,CurrentStep,DartIP,DartPort,DartTicket,VMHost,VMName,LastTime,StartTime,EndTime |
ForEach {
if($_ -like "*<td>Successfully completed</td>*"){$_ -replace "<tr>", "<tr bgcolor=green>"}
elseif($_ -like "*<td>Failed</td>*"){$_ -replace "<tr>", "<tr bgcolor=red>"}
else{$_}
} > C:\inetpub\wwwroot\default.htm 

image

You can also download and use this Module directly from https://www.powershellgallery.com/packages/GetMDTOdataModule/1.0 by running the following command in PowerShell (version 5)

Find-Module GetMDTOdataModule | Install-Module -Scope CurrentUser

/mike

Posted in MDT, PowerShell | Tagged: , | 7 Comments »

Inside the Task Sequence – Generate Application Migration File

Posted by Mikael Nystrom on March 3, 2016

Today during class we worked on Refresh and Replace scenarios. We touched based on a tiny step in the Task Sequence that is actually really cool. In the Task Sequence for Lite Touch (MDT Standalone) there is one step that does not exist in Zero Touch and that is the “Generate Application Migration File” step.

That little step will do a really smart thing, it will basically make sure that registered file types that can be open with an application will automatically be saved by USMT. It does that by grabbing all non-default file types and then check if there is a corresponding application that can open the file by examining the registry.

image
The “Generate Application Migration File” step.

So, what’s cool about that step?

If you open the file it states:

‘ // Purpose:   Generate an XML file for automatically capturing user data
‘ //            (documents) associated with installed applications.

And if you continue looking in the file you will see the flow, like this:

‘ Determine the version of USMT to use

‘ Create the file and write the standard header

‘ Get the list of all registry keys in HKCR

‘ Look at all file extensions to see if they map to an application

‘ Read the default value to see what app is associated with this extension

‘ See if the file can be opened (has a shell\open\command handler)

‘ Add it to the XML file

‘ Write the rest of the XML file

‘ Make sure the ZTIUserState.wsf script knows to use this script

‘ Cleanup and Exit

So, that is why you don’t need to spend a whole lot of time on USMT when doing Lite Touch.

Try It out

If you would like to see what it actually collects, you can run this interactive, that way it will generate a XML file. If you open it up you will then see what it would have migrated and not.

Open an elevated Command Prompt and execute the following:

cscript \\MDTSERVER\MDTSHARE\Scripts\ZTIAppXmlGen.wsf /capture

You will get an output that looks like this:

image

If you then open the XML (It is going to be located in C:\MININT\SMSOSD\OSDLOGS and named ZTIAppXmlGen.xml its going to look something like this

image

/mike

Posted in MDT, Lite Touch, OSD, OS Deployment | Tagged: , , , | 6 Comments »

OSD – How make PXE work in ConfigMgr OSD, MDT and WDS work perfectly fine – Stop using DHCP Option 66 & 67

Posted by Mikael Nystrom on February 29, 2016

During the OSD class last week I did get some questions regarding the use of IP Helpers in the routers or using Option 66 and 67 in the DHCP server, since booth methods seems to be working.

That is kind of true, but in reality not…

The design for PXE is based on IP Helpers. That means that the client should broadcast and say:

– “Hi, I need some IP stuff and I need something to boot on”

image

How is it supposed to work?

Now, if the router is configured for IP helpers (it already have one that points to the DHCP server, so the only configuration you need to do is “add one more”) the broadcast will be picked up by the router, encapsulated and sent to both the DHCP server as well as the PXE (Windows Deployment Server). The DHCP server will grab a free IP and subnet, gateway addresses that fits the network the client is located on, send it to the router and tell the router to send the packet to the client. The PXE server will “after knowing what kind of computer it is (BIOS / UEFI / x86 / x64) pick the correct boot file and send that information to the router and tell it to send the packet to the client.

This means that the client will receive two different packages and the client will merge them into one, consume the packet and we are done.

What happens when Option 66 & 67 is involved?

Now, let us assume that we don’t have the IP address to the PXE server configured in the IP Helper but instead we have option 66 & 67 configured, well the first part is the same, but the DHCP server will also forward the option 66 & 67 to client, it should work, right?

Sometimes it does, but since the PXE client could not present itself it might or might not get the correct boot file, it basically means that you can either boot on BIOS machines OR you can boot on UEFI machines and the client will not receive two packages (which client expects) so, sometimes it does not handle that correct (depending on firmware, Network Adapter, Vendor and such. It would be possible to create DHCP filters, multiple scopes and such to make UEFI based machines boot on one range of IP’s and otter IP’s for other filters, but that is just pure pain to manage

The number ONE issue we work with at customer sites when it comes to PXE boot is the bloody Option 66 & 67.

So, the short story is very, very simple, just configure the darn routers correctly and have a happy life.

Some notes from others:

PXE booting with WDS – DHCP Scope vs IP Helpers:

http://techthoughts.info/pxe-booting-wds-dhcp-scope-vs-ip-helpers/

Microsoft TechNet: Managing Network Boot Programs:

https://technet.microsoft.com/en-us/library/cc732351(v=ws.10).aspx

In this document it says: Although Microsoft does not recommend this method, you can use the following DHCP options to direct PXE clients to an appropriate NBP to download

Microsoft Support: PXE clients computers do not start when you configure the Dynamic Host Configuration Protocol server to use options 60, 66, 67
https://support.microsoft.com/en-us/kb/259670

In this document it clearly says: Important: Microsoft does not support the use of these options on a DHCP server to redirect PXE clients

 

So, go to the network team, give them a bottle of nice Whiskey and ask the nicely. Problem solved

/mike

Posted in MDT, Windows Deployment Services, ConfigMgr, OSD, OS Deployment | Tagged: , , , , | 6 Comments »

Deployment Fundamentals, Vol. 6: Deploying Windows 10 Using Microsoft Deployment Toolkit (and some PowerShell)

Posted by Mikael Nystrom on January 6, 2016

Yes, the book is finally done and it is up on Amazon. The book follows all the others by being a build-while-you-read book, it includes a complete set of PowerShell scripts that will build your entire lab environment (The script has been changed, so it will be easier to use them at customer sites or other test/lab environments if needed). The focus of the book is as you could guess by the title to deploy Windows 10 using MDT and LiteTouch. The versions we did use in the book are Windows 10 1511, MDT 2013 Update 2 and the new ADK. It has been hard work, late nights, but darn I still love writing books… You can find the book on Amazon.com as well as other sites. oh, btw, the book also includes a complete hydration kit that uses MDT and PowerShell to build your complete lab environment.

image

Happy reading and deploying

/mike

Posted in Book, Deployment, MDT | Tagged: , , , | 17 Comments »

 
Follow

Get every new post delivered to your Inbox.

Join 7,427 other followers