The Deployment Bunny

OS Deployment, Virtualization, Microsoft based Infrastructure…

  • about.me

    about.me/mikaelnystrom

  • Archives

  • Meta

Posts Tagged ‘OSD’

OSD – Add information to the Computer during OSD using a Custom Tattoo Step

Posted by Mikael Nystrom on December 2, 2016

When using an MDT (integrated in ConfigMgr or standalone) there is a step called Tattoo, this step will write information to the registry as well as to the WMI repository.

Let us see what it looks by default:

Get-WMIObject –Class Microsoft_BDD_Info

image
Result.

Get-Item -Path ‘HKLM:\SOFTWARE\Microsoft\Deployment 4’

image

Cool, but I need my own stuff?

We have done this for various  reasons and many customers, it could be used for making sure the computer ends up in certain ConfigMgr Collections, or that applications behave in a certain way, or that the asset management tools does the job differently, or that the correct support team can know what the computer was configured for, or what task sequence that was used, or…. As you see the are some valid reasons for this, no doubt.

The question is of course, should you modify the existing ZTITatto.wsf and corresponding .MOF file?, No, you should not do that, instead you should create new files and use them instead.

So, here is the download : https://github.com/DeploymentBunny/Files/tree/master/Tools/Custom-ZTITatoo

To make the work in a task sequence you need to add the files to you Scripts folder, update the CustomSettings.ini if you are using Custom Properties and add a step in the Task Sequence.

Here is a sample of CustomSettings.ini

image

I have added 4 new properties, added them to a MacAddress, so first step is to verify that the ZTIGather process reads this correct, let us verify this by running:

cscript \\redepl01\mdtproduction$\Scripts\ZTIGather.wsf /inifile:c:\Temp\CustomSettings.ini

image
Result after running the following command to perform the inventory.

Now, lets run the custom tattoo script:

cscript \\redepl01\mdtproduction$\Scripts\ViaMonstraTatoo.wsf

image
That looks great!

Now, let us verify that we get the correct data from WMI and Registry:

First, get the WMI repository:

Get-WmiObject -Class ViaMonstra_Info

image
The WMI repository was updated.

Let us check the Registry:

Get-Item -Path ‘HKLM:\SOFTWARE\ViaMonstra\OSD’

image
The Registry was updated.

At LabCenter

The Customer LabCenter deploys computer for hands-on labs and they wanted each computer to be tattooed with information regarding the LAB and one thing they use it for is to be able to display the information directly in the screen using BGinfo, but they also use it for other purposes. BGInfo reads the Registry values, or WMI

image

How to change my own stuff then?

Download the files from:https://github.com/DeploymentBunny/Files/tree/master/Tools/Custom-ZTITatoo and edit the scripts, (.wsf and .mof) by replacing the values that are in use, like

  • ViaMonstra
  • ViaRole
  • ViaClass
  • ViaOwner
  • ViaTag

Save, and verify

/mike

Posted in ConfigMgr, Lite Touch, MDT, OSD, Zero Touch | Tagged: , , , , | 3 Comments »

OSD – Microsoft Deployment Toolkit Build 8443 is out

Posted by Mikael Nystrom on November 14, 2016

Today a new version of MDT was released, the main reason is Quality Updates and full support for Windows Server 2016 as well as Windows 10 1607. It does require ADK 1607. If you are using MDT in your OS deployment solution, go ahead, download, read, update and enjoy live.

image

/mike

Posted in MDT, OSD | Tagged: , | 2 Comments »

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: , , , , , | 3 Comments »

OSD–Customizing Images and deployment, think about that for a second

Posted by Mikael Nystrom on July 12, 2016

This is one of those blogposts that tries to answer lost of question and and the same time create new questions, but I get a lot of questions regarding the topic of “Image Customization”, and mainly about how-to, but

Why?

Before you even consider doing this, you need to understand why. The reason “we have always done that” is not really a great answer. There are basically to kinds of devices. The device is either going to be used as a “normal” computer by a user or it is a “thing” or in other words a task-oriented computer, like a teller machine, a kiosk machine. The later type needs heavy modifications to be adjusted, but spending the same effort a a regular computer is just waste of time.

But my users are idiots?

No, they are not. There will always be a few percent that don’t want to learn or actually have a hard time figuring out how to perform certain operations. But modifying every car on the planet so that every person can drive them is not really a smart thing, if a few percent of the org cannot select to add an application from ConfigMgr, why would you make it impossible for others do to so. Help them few percent that cannot do it and ask yourself the following?

– Do you reimage the phone for them?

– Do you reimage the TV set for them?

– Do you reimage the car stereo for them?

Windows 10 is different in more ways you can imagine

First, the operating system is “serviced”, that means that you will receive a new version approximately 2 times every year and that will be a upgrade, meaning it will fall back to the same apps you spent so many hours to remove, so that was just a waste of time.

Windows 10 will stick around for a while and it will not change much over the next 9 years (I’ll guess), so it is better that users learn it, they will have it at home and having a look and feel that is similar make sense for most users, but by trying to make Windows 10 look like Windows 7 does not really help people, it just prolong the learning that they need.

If you need to redo the work every time there is a new version, you will spend the rest of the Windows 10 era to find new ways to do these modifications, since the solution that worked in one version most likely will be broken in the next, we can see that happen over and over again.

My users should not be able to run “that”

Ok, so you want to remove the Xbox application, because? What is the danger? What could possible happen. Users are usually afraid of everything, so they don’t click at all when they have no idea what it is, and even if they do, running the Xbox app does not really do any harm. There are Security Baseline Policy’s that include the possibility to turn of many of those settings (not all) but this one will fix a lot of those “consumer” things.

“Enabled "Turn off Microsoft consumer experiences," – https://blogs.technet.microsoft.com/secguide/2016/01/22/security-baseline-for-windows-10-v1511-threshold-2-final/

Maybe there are other tasks that are more important then to make sure that it “looks” like Windows 7?

There are a lot of new security features that are very important, maybe it is better to think of a way to shift from BIOS to UEFI. That makes it possible to take advantage of many things, like Secure Boot and one of the most important features in Windows 10, Credential Guard.

Supported?

If you really want to do image-hacking, maybe you should consider if it is supported. Just because you can make it work today, it might render the possibility to deploy upgrades and updates when the next version comes around?

Yes, I do lost of modifications!

But I also have a long conversation with the customer before we go ahead and do it, i always check with friends inside Microsoft to see if it is kind of “ok”, before we do that.

What could be ok?

That is kind of easy, everything you can modify using GPO, GPP’s is usually perfectly fine to do, in most cases using PowerShell is also fine.

What I’m really try to say is

Think for a while, is that really, REALLY needed? or is it just “something we have always done”

Where can i find Information about this stuff?

https://blogs.technet.microsoft.com/secguide/2016/01/22/security-baseline-for-windows-10-v1511-threshold-2-final/

https://blogs.technet.microsoft.com/mniehaus/2015/12/31/updated-remove-apps-script-and-a-workaround/

http://ccmexec.com/

https://blogs.technet.microsoft.com/deploymentguys/

http://garytown.com/

/mike

Posted in OSD | Tagged: | 7 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: , , | 8 Comments »

OSD – Prevent Windows to “randomly” run Windows Update during the creation of a reference image

Posted by Mikael Nystrom on May 10, 2016

You should absolutely have patches installed in your reference image, no question about the. The default setting in Microsoft Deployment Toolkit is to run Windows Update “when needed” and the default behavior is the same, so basically Windows will run Windows Update when it “feels” for it. When the machine is managed it is controlled, but a reference image is created when the operating system is unmanaged. This could result in all kinds of issues, it could result in a pending reboot “sometimes” or failure to install software “sometimes”. I don’t like “sometimes” or random…

Turn of random Windows Updates during reference image creation

It is actually very easy, you need to change “Protect Your PC” from 1 to 3

image
All settings for ProtectYourPC.

If you open the unattend.xml file in Windows System Image Manager it looks like this:

image
ProtectYourPC is now set to 3.

This way Windows Update will performed manually or since you are using MDT and LiteTouch as your tool to create the reference image, the two Windows Update steps will do the updates and no more “random” updates.

image
The two Windows Update steps in the LTI task sequence.

/mike

Posted in OS Deployment, OSD | Tagged: , | 15 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 »

Working in the Datacenter – Deploying HP Servers and configure BIOS for High Performance using PowerShell

Posted by Mikael Nystrom on March 8, 2016

If you have a HP ProLiant Gen 8 or Gen 9 it is possible to use PowerShell to configure the BIOS. One of the items I really like to configure is the Power Settings, in most cases you would like to have “performance” but the default setting is usually set in “Tree Hugging Mode” (also called power saving). This is especially true if server is going to be used as a Compute server (running Hyper-V).

You can read the previous posts here:

https://deploymentbunny.com/2016/03/07/working-in-the-datacenter-hp-bios-cmdlets-for-windows-powershell-x64/

https://deploymentbunny.com/2016/03/07/working-in-the-datacenter-application-wrapper-for-hp-bios-cmdlets-deploy-it-during-osd/

The Script:

<#
 Install Wrapper 1.0
 Author: Mikael Nystrom
 http://www.deploymentbunny.com 
#>
param($Username,$Password,$mode)
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"}
        }
    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.log"
    }
    }
}
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"

$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList "$env:COMPUTERNAM\$UserName", ($Password | ConvertTo-SecureString -AsPlainText -Force)
$Con = Connect-HPBIOS -IP $env:COMPUTERNAME -Credential $Credentials -ErrorAction Stop

Write-Output "$ScriptName -  Selected Mode: $Mode"

if($mode -eq "FullPower"){
    Set-HPBIOSPowerProfile -Profile Maximum_Performance -Connection $Con
    Set-HPBIOSPowerRegulator -Regulator Static_High_Performance -Connection $Con
    Write-Output "$ScriptName - HP PowerProfile is set to $((Get-HPBIOSPowerProfile -Connection $Con).HPPowerProfile)"
    Write-Output "$ScriptName - HP PowerRegulator is set to $((Get-HPBIOSPowerRegulator -Connection $Con).HPPowerRegulator)"
}

if($mode -eq "TreeHugging"){
    Set-HPBIOSPowerProfile -Profile Minimum_Power -Connection $Con
    Set-HPBIOSPowerRegulator -Regulator Dynamic_Power_Savings -Connection $Con
    Write-Output "$ScriptName - HP PowerProfile is set to $((Get-HPBIOSPowerProfile -Connection $Con).HPPowerProfile)"
    Write-Output "$ScriptName - HP PowerRegulator is set to $((Get-HPBIOSPowerRegulator -Connection $Con).HPPowerRegulator)"
}

. Stop-Logging

Using the script from the command prompt:

Execute the following from an elevated Command Prompt

PowerShell.exe -ExecutionPolicy ByPass -File Configure-HPBIOS.ps1 -Mode FullPower -UserName Administrator –Password P@ssw0rd

Using the script from the PowerShell prompt:

Execute the following from an elevated PowerShell Prompt

.\Configure-HPBIOS.ps1 -Mode FullPower -UserName Administrator –Password P@ssw0rd

Using the script in a Task Sequence when you deploy the server:

You can run the script as PowerShell script in the Task Sequence, or run it as an Application. I prefer to use it as an application since it then can be controlled using rules.

The Config-HPBios application:

image
Here you can see the Quiet install command in the application.

Quiet install command: PowerShell.exe -ExecutionPolicy ByPass -File Configure-HPBIOS.ps1 -Mode %HPPowerMode% -UserName Administrator -Password %AdminPassword%

This command will execute PowerShell and feed the script with Property HPPowerMode (that needs to be FullPower or TreeHugging). This property can be set in CustomSettings.ini or as step in the Task Sequence.

image
Here you can see the Set Task Sequence Variable HPPowerMode set to FullPower.

The actually configuration will be done during the Install step, since it is an application in my case. but it is possible to run the app directly after the Variable has been set.

Check the log file for the result:

image
The log file shows the settings.

/mike

Posted in HP, OS Deployment, OSD, PowerShell | Tagged: , , , , | 1 Comment »

Working in the Datacenter – Application Wrapper for HP BIOS CmdLets – Deploy it during OSD

Posted by Mikael Nystrom on March 7, 2016

In a previous blogpost a shortly described what you can do with the “HP BIOS CmdLets for Windows PowerShell (x64)” and in this post I’ll give you a PowerShell wrapper for LTI/ZTI to deploy it.

Download from installer from

http://h20566.www2.hpe.com/hpsc/swd/public/detail?sp4ts.oid=5440658&swItemId=MTX_1cedc5b3a4ec4bc7a942f6e682&swEnvOid=4168

image

The LTI/ZTI Wrapper:

It is very simple, basically create a folder called “Install – HPBIOSCmdlets”, in the folder, create a folder called “Source” and in that folder you save the MSI file from HP (the download is a EXE, just unzip it and it will be a MSI file inside) and then you save the PowerShell installer in the “Install – HPBIOSCmdlets” folder, like this

image

Here is the:

<#
 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"}
        }
    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.txt"

    }
    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"

$Executable = "msiexec.exe"
$Arguments = "/i ""$SOURCEROOT\HPBIOSCmdlets-x64.msi"" /qb"

Write-Output "$ScriptName - Executable: $Executable"
Write-Output "$ScriptName - Arguments: $Arguments"

Invoke-Exe -Executable $Executable -Arguments $Arguments -Verbose

. Stop-Logging

Create the Application in the Task Sequence:

(This how it is done in LiteTouch, but it will be the same in ConfigMgr)

Create a new Application, browse to the folder you created and use this as your command line:

PowerShell.exe -ExecutionPolicy Bypass -File Install-HPBIOSCmdlets-x64.ps1

image
It should look something like this.

The logfile

It will end up in C:\Windows\Temp\Deploymentlogs (for LTI) and should look something like this:

image

/mike

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