The Deployment Bunny

OS Deployment, Virtualization, Microsoft based Infrastructure…

Posts Tagged ‘Microsoft Deployment Toolkit’

Adding Configurations to the Applications Pane using AppDescriptors

Posted by Mikael Nystrom on September 3, 2011

The Deploymentguys has invented some new stuff, just check this out, this is really cool. Smile

AppDescriptorDemo

Read the complete story: http://blogs.technet.com/b/deploymentguys/archive/2011/09/02/adding-configurations-to-the-applications-pane-using-appdescriptors.aspx?utm_medium=twitter&utm_source=twitterfeed

Download the ZIP: AppDescriptors.zip

/mike

Posted in Deployment | Tagged: | Leave a Comment »

Online Session – Microsoft Deployment Toolkit 2012

Posted by Mikael Nystrom on August 24, 2011

Yes, it is true, there will be a new version of MDT

This session will cover the changes and updates we have in MDT 2012 BETA 1. A session full of demos. We will spend time on both the new features but we will also spend some time one advanced/fun scenarios. That means customsettings.ini, userexists, scripts and some tips and trix.

The session will be in the Swedish language and on the 1 September 2011

Download link for MDT 2012 BETA 1 https://connect.microsoft.com/site14

Link to the event : https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032490004&Culture=sv-SE

/mike

Posted in Deployment | Tagged: , , , , | Leave a Comment »

Nice to know: – Blocking OS install on unsupported models in Microsoft Deployment Toolkit (MDT)

Posted by Mikael Nystrom on June 4, 2011

A customer asked me:

- Is it possible to block an attempt to upgrade, refresh or install the Operating System if that model is not “certified?

– Yes, we can set the OSinstall property to N based on model, that will block any attempt

- But that will block that model from running any Task Sequence? That is not what I want…

Well, that is correct, so I needed a way to block that particular model on that particular Task Sequence, so I  would like something a bit more sophisticated, something like this:

For task sequence “Windows 7 Enterprise x86 – Basic” the following models are supported and for the task sequence “Windows Server 2008 R2 – Basic” the following models are supported (or something like that)  and here is how you do it:

(This has been tested on MDT 2010, MDT2010 update 1 and MDT2012)

Create a new group in your task sequence

  • Open up your deployment workbench
  • Open your task sequence
  • Add a new group called “Certified Hardware”

Like this:

image

Setting condition on the group

  • Select the group Certified Hardware
  • Select Options for that group
  • Add the following condition:

Task sequence variable “Model not equals HP ProBook 5310m” (if that is the model that you support for this Task sequence)

Like this:

image

Adding the “blocker”

  • Select the group
  • Add a “Set Task Sequence Variable”
  • Task Sequence Variable = OSInstall
  • Value = N

Like this:

image

Adding script that will write in the log and display “why” the OS install failed

Without this step it will just display a “failed”, but maybe, just maybe you would like to have it a bit more sophisticated. So let us add a script

The script should go into the Scripts folder and you can download it from my SkyDrive http://cid-8563304f134ddcb6.office.live.com/self.aspx/BlogFiles/ZTIUnSupportedHardware.zip

  • Download it
  • Extract it
  • Save it in the scripts folder
  • Add a run command before the Set Task Sequence Variable
  • Use this command : cscript.exe “%SCRIPTROOT%\ZTIUnSupportedHardware.wsf”

It should look like this:

image_thumb[3]_thumb[1]

Let us take a look in the BDD.log file

Here is how it looks in BDD.log

image_thumb[7]_thumb[1]

Next step

With this in place you can prohibit installations of Domain Controllers on Laptops, Hyper-V servers on Virtual Machines and people trying to refresh a Machine from XP to XP even if you have NO device drivers for that model, my guess is that this will solve a couple of “Ops, sorry”

If you want to you can add scripts and other logic to this, things that automatically runs a scripts that will collect the hardware information to a log so that you then can figure out what drivers are needed or something like that, or you could add the “Send Email” script so it will send an email to you saying that some “schmuck” tried to do something bad. You could also add the userexit script for ModelAlias and use that. You could also add other conditions, like Firmware, certain kind of hardware and so on. It would be for example possible to run a Web Services that checks if this computer is correctly added into the asset management database or something like that.

Yes, you can also use CustomSettings.ini to perform similar

And here is a couple of samples of that:

Sample No:1 – Blocking on Model

Here is the “easy” way, the only downside with this one is that it will only block based on Model and that could be perfectly ok in many situations.

[Settings]
Priority=Model, Default
Properties=MyCustomProperty

[Default]
OSInstall=N

[HP ProBook 5310m]

OSInstall=Y

[Virtual Machine]
OSInstall=Y

Sample No:2 – Blocking on Model + Task Sequence:

In this sample we create one property and two priority blocks. If we run this WHEN the task Sequence is known (That means that you need to re-run the gather process with “process rules”, ZTIGather will set the model + Task Sequence ID in to the property Model_TS in the [Init] section, it will then process the [CertifiedHardware] section and the use the Subsection to continue to the section that matches the Model+Task Sequence ID, in this case since my machine is a VM running on Hyper-V it will be “Virtual Machine” and if the Task Sequence is W7X64 it will process the [Virtual Machine_W7X64] section and OSinstall property will be set to Y.

[Settings]
Priority=Init, CertifiedHardware, Default
Properties=MyCustomProperty, Model_TS

[Init]
Model_TS=%Model%_%TaskSequenceID%

[Default]
OSInstall=N

[CertifiedHardware]
SubSection=%Model_TS%

[Virtual Machine_W7X64]
OSInstall=Y

More tips…

You can of course block OS install temporary, during maintenance could be a valid scenario. A good friend (And fellow MVP) Maik Koster created a solution for that (I tricked him into it) and you can read a bit more about it here. http://myitforum.com/cs2/blogs/maikkoster/archive/2011/04/05/implementing-a-very-simple-maintenance-mode-in-mdt-litetouch.aspx

And here are some more discussions on how to pick the info from the Database: http://social.technet.microsoft.com/Forums/en-US/mdt/thread/412e54d9-549f-4828-9d5d-d41d14df77e8

/mike aka the Deployment Bunny

Posted in Deployment, Drivers, TechEd, Windows 7, Windows Server 2008 R2 | Tagged: , , | 2 Comments »

Modifying the Lite Touch Wizard in MDT 2010 – Sample 2

Posted by Mikael Nystrom on May 26, 2011

This is the second part of a story around the MDT Wizard and the Wizard editor and the saga continues, but this time it’s going to be a bit more tricky since we need to add information to the wizard that does not exist by default in MDT. Last post was easy in the way that we just created a new page in the wizard that displays existing environment information, now the game has changed and my customer wants to have some hardware information, something like this:

image

The reason of having this page is to be sure that the machine has the correct hardware configuration, things like correct BIOS version, memory, CPU configuration but also to see that it has the Mac address that we “assume” it have. Also, we want to know if the model alias user exit script works correct and sets the correct model alias since we use that as a part of the driver group.

Now, most of the values can be collected directly from using Make, Model, Memory, Product, Architecture, AssetTag, SerialNumber, UUID, MacAddress. But for the rest we need to get them into the MDT environment somehow and the somehow in this case spells “UserExit”

UserExit is a way to extend MDT by writing some custom code (yes, that will be provided, hang on…), execute it and return the value into a custom property that can be used in rules or showed in the wizard page.

A user exit script is basically a VBscript with one or more functions and you can have them in one big userexit script or many, it is easier to have just one since that will be a bit cleaner and easier to manage. What we need is to get the name of the CPU, Hard drive, number of physical CPU’s and logical CPU’s and last but not least SMBiosversion. Well, I missed one, we also need to get the ModelAlias, but that user exit is already created by the deployment guys, so we just grab that and use it

But first of all, lets create the new Wizard page and here is how

Modifying the Wizard

Download the MDT Wizard editor from http://mdtwizardeditor.codeplex.com/

Fire it up and open the “DeployWiz_Definition_ENU.xml”. It is in the scripts folder (Make a backup of the file first). Then you add a new Wizard pane like this:

image

And then you add this HTML code into that page:


<h1>System information</h1>
<span style=”width: 95%;”>
<table border=”1″ cellspacing=”1″ cellpadding=”1″ width=”650″>
<tbody>
  <tr>
   <td width=”110″ align=”right”><em>Vendor</em></td>
   <td align=”left”><input style=”width: 220px” name=Make readonly></td>
   <td width=”110″ align=”right”><em>Model</em></td>
   <td align=”left”><input style=”width: 220px” name=Model readonly></td>
  </tr>
  <tr>
   <td width=”110″ align=”right”><em>Product</em></td>
   <td align=”left”><input style=”width: 220px” name=Product readonly></td>
   <td width=”110″ align=”right”><em>Memory(in Mb)</em></td>
   <td align=”left”><input style=”width: 220px” name=Memory readonly></td>
  </tr>
  <tr>
   <td width=”110″ align=”right”><em>CPU (in GHz)</em></td>
   <td align=”left”><input style=”width: 220px” name=processorspeed readonly></td>
   <td width=”110″ align=”right”><em>No: CPU\Cores</em></td>
   <td align=”left”><input style=”width: 108px” name=ComputerSystemNumberOfProcessors readonly>\<input style=”width:108px” name=ComputerSystemNumberOfLogicalProcessors readonly></td>
  </tr>
  <tr>
   <td width=”110″ align=”right”><em>Harddisk Info</em></td>
   <td align=”left”><input style=”width: 220px” name=DiskDriveCaptation readonly></td>
   <td width=”110″ align=”right”><em>Capable Architecture</em></td>
   <td align=”left”><input style=”width: 220px” name=”CapableArchitecture” readonly></td>
  </tr>
  <tr>
   <td width=”110″ align=”right”><em>Model Alias</em></td>
   <td align=”left”><input style=”width: 220px” name=MODELALIAS readonly></td>
   <td width=”110″ align=”right”><em>SMBIOSVERSION</em></td>
   <td align=”left”><input style=”width: 220px” name=SMBIOSVERSION readonly></td>
  </tr>

</tbody>
</table>

<table border=”1″ cellspacing=”1″ cellpadding=”1″ width=”650″>
<tbody>
      <tr>
         <td width=”150″ align=”right”><em>CPU</em></td>
        <td><input style=”width: 490px” name=CPUName readonly></td>
      </tr>
      <tr>
      <tr>
         <td width=”150″ align=”right”><em>Serial Number</em></td>
        <td><input style=”width: 490px” name=Serialnumber readonly></td>
      </tr>
      <tr>
         <td width=”150″ align=”right”><em>UUID</em></td>
        <td><input style=”width: 490px” name=UUID readonly></td>
      </tr>
      <tr>
         <td width=”150″ align=”right”><em>Mac Address</em></td>
        <td><input style=”width: 490px” name=MacAddress001 readonly></td>
      </tr>
      <tr>
         <td width=”150″ align=”right”><em>Assettag</em></td>
        <td><input style=”width: 490px” name=Assettag readonly></td>
      </tr>
</tbody>
</table>
</span>


You also need to a “condition” to the page so that you can turn it on or off based on rules and that should look like this:

image

To be sure that you get it correct, here is the code in plain text


Ucase(Property(“SkipHardwareInfo”)) <> “YES”


Modifying CustomSettings.ini

So, we are done with the Wizard, but if we run it now it will not really work, now it is time to modify CustomSettings.ini and that should look like this:

[Settings]
Priority=Init, ModelAliasInit, Default
Properties=MyCustomProperty, SkipHardwareInfo, ComputerSystemNumberOfProcessors, ComputerSystemNumberOfLogicalProcessors, ComputerSystemProductIdentifyingNumber, SMBIOSVersion, CPUName, DiskDriveCaptation, ModelAlias

[Init]
ComputerSystemNumberOfProcessors=#GetComputerSystemNumberOfProcessors()#
ComputerSystemNumberOfLogicalProcessors=#GetComputerSystemNumberOfLogicalProcessors()#
ComputerSystemProductIdentifyingNumber=#GetComputerSystemProductIdentifyingNumber()#
SMBIOSVersion=#GetBIOSSMBIOSVersion()#
CPUName=#GetCPUName()#
DiskDriveCaptation=#GetDiskDriveCaptation()#
UserExit=HardwareInfo.vbs

[ModelAliasInit]
ModelAlias=#SetModelAlias()#
UserExit=ModelAliasExit.vbs


Adding the scripts

We need two scripts, one called hardwareinfo.vbs and the other is ModelAlias.vbs, you can get ModelAlias from http://blogs.technet.com/b/deploymentguys/archive/2009/09/10/using-and-extending-model-aliases-for-hardware-specific-application-installation.aspx. The VBScript hardwareinfo.vbs however you will find here:


‘ //***************************************************************************
‘ // ***** Script Header *****
‘ //
‘ // Solution:  Custom Script for use with the Microsoft Deployment Toolkit
‘ // File:      hardwareinfo.vbs
‘ //
‘ // Purpose:   User exit script to get and set properties to be able to display the HardwareInfo Wizardpane.
‘ //           
‘ // Usage:     Modify CustomSettings.ini similar to this:
‘ //        [Settings]
‘ //        Priority=Init, Default
‘ //        Properties=MyCustomProperty, SkipHardwareInfo, ComputerSystemNumberOfProcessors, ComputerSystemNumberOfLogicalProcessors, ComputerSystemProductIdentifyingNumber, SMBIOSVersion, CPUName, DiskDriveCaptation, ModelAlias
‘ //
‘ //        [Init]
‘ //        ComputerSystemNumberOfProcessors=#SetComputerSystemNumberOfProcessors()#
‘ //        ComputerSystemNumberOfLogicalProcessors=#SetComputerSystemNumberOfLogicalProcessors()#
‘ //        ComputerSystemProductIdentifyingNumber=#SetComputerSystemProductIdentifyingNumber()#
‘ //        SMBIOSVersion=#SetBIOSSMBIOSVersion()#
‘ //        CPUName=#GetCPUName()#
‘ //        DiskDriveCaptation=#GetDiskDriveCaptation()#
‘ //
‘ // Version:   1.0
‘ // Author: Mikael Nystrom –
http://deploymentbunny.com
‘ //***************************************************************************

Function UserExit(sType, sWhen, sDetail, bSkip)
    oLogging.CreateEntry “UserExit:HardwareInfo.vbs started: ” & sType & ” ” & sWhen & ” ” & sDetail, LogTypeInfo
    UserExit = Success
End Function

Function SetComputerSystemNumberOfProcessors()
    oLogging.CreateEntry “UserExit:HardwareInfo.vbs – Getting ComputerSystemNumberOfProcessors”, LogTypeInfo
    Dim objWMI
    Dim objResults
    Dim objInstance
    Dim NumberOfProcessors
    Dim ComputerSystemNumberOfProcessors

    Set objWMI = GetObject(“winmgmts:”)
    Set objResults = objWMI.InstancesOf(“Win32_ComputerSystem”)
        For each objInstance in objResults
            If Not IsNull(objInstance.NumberOfProcessors) Then
                NumberOfProcessors = Trim(objInstance.NumberOfProcessors)
            End If
        Next
            If NumberOfProcessors = “” Then
                NumberOfProcessors = “UNKNOWN”
            End If
    SetComputerSystemNumberOfProcessors = NumberOfProcessors
End Function

Function SetComputerSystemNumberOfLogicalProcessors()
    oLogging.CreateEntry “UserExit:HardwareInfo.vbs – Getting ComputerSystemNumberOfLogicalProcessors”, LogTypeInfo
    Dim objWMI
    Dim objResults
    Dim objInstance
    Dim NumberOfLogicalProcessors
   
    Set objWMI = GetObject(“winmgmts:”)
    Set objResults = objWMI.InstancesOf(“Win32_ComputerSystem”)
        If Err then
        oLogging.CreateEntry “Error querying Win32_ComputerSystem: ” & Err.Description & ” (” & Err.Number & “)”, LogTypeError
    Else
        For each objInstance in objResults
            If Not IsNull(objInstance.NumberOfLogicalProcessors) Then
                    NumberOfLogicalProcessors = Trim(objInstance.NumberOfLogicalProcessors)
            End If
        Next
    End If
    SetComputerSystemNumberOfLogicalProcessors = NumberOfLogicalProcessors
End Function

Function SetCPUName()
    oLogging.CreateEntry “UserExit:HardwareInfo.vbs – Getting CPUName”, LogTypeInfo
    Dim objWMI
    Dim objResults
    Dim objInstance
    Dim Name
    Dim CPUName
   
    Set objWMI = GetObject(“winmgmts:”)
    Set objResults = objWMI.ExecQuery(“SELECT * FROM Win32_Processor”)
        If Err then
        oLogging.CreateEntry “Error querying FROM Win32_Processor: ” & Err.Description & ” (” & Err.Number & “)”, LogTypeError
    Else
        For each objInstance in objResults
            If Not IsNull(objInstance.Name) Then
                    CPUName = Trim(objInstance.Name)
            End If
        Next
    End If
    SetCPUName = CPUName
End Function

Function SetBIOSSMBIOSVersion()
    oLogging.CreateEntry “UserExit:HardwareInfo.vbs – Getting BIOSSMBIOSVersion”, LogTypeInfo
    Dim objWMI
    Dim objResults
    Dim objInstance
    Dim SMBIOSBIOSVersion
   
    Set objWMI = GetObject(“winmgmts:”)
    Set objResults = objWMI.ExecQuery(“SELECT * FROM Win32_BIOS”)
        If Err then
        oLogging.CreateEntry “Error querying Win32_ComputerSystem: ” & Err.Description & ” (” & Err.Number & “)”, LogTypeError
    Else
        For each objInstance in objResults
            If Not IsNull(objInstance.SMBIOSBIOSVersion) Then
                    SMBIOSBIOSVersion = Trim(objInstance.SMBIOSBIOSVersion)
            End If
        Next
    End If
    SetBIOSSMBIOSVersion = SMBIOSBIOSVersion
End Function

Function SetDiskDriveCaptation()
    oLogging.CreateEntry “UserExit:HardwareInfo.vbs – Getting DiskDriveCaptation”, LogTypeInfo
    Dim objWMI
    Dim objResults
    Dim objInstance
    Dim Caption
   
    Set objWMI = GetObject(“winmgmts:”)
           Set objResults = objWMI.ExecQuery(“SELECT * FROM Win32_DiskDrive where mediatype like ‘Fixed%hard disk%’”)
        If Err then
        oLogging.CreateEntry “Error querying Win32_DiskDrive: ” & Err.Description & ” (” & Err.Number & “)”, LogTypeError
    Else
        For each objInstance in objResults
            If Not IsNull(objInstance.Caption) Then
                    Caption = Trim(objInstance.Caption)
            End If
        Next
    End If
    SetDiskDriveCaptation = Caption
End Function

Function SetComputerSystemProductIdentifyingNumber()
    oLogging.CreateEntry “UserExit:HardwareInfo.vbs – Getting ComputerSystemProductIdentifyingNumber”, LogTypeInfo
    Dim objWMI
    Dim objResults
    Dim objInstance
    Dim IdentifyingNumber
    Dim ComputerSystemProductIdentifyingNumber
    Set objWMI = GetObject(“winmgmts:”)
    Set objResults = objWMI.InstancesOf(“Win32_ComputerSystemProduct”)
        For each objInstance in objResults
            If Not IsNull(objInstance.IdentifyingNumber) Then
                IdentifyingNumber = Trim(objInstance.IdentifyingNumber)
            End If
        Next
            If IdentifyingNumber = “” Then
                IdentifyingNumber = “UNKNOWN”
            End If               
    SetComputerSystemProductIdentifyingNumber = IdentifyingNumber
End Function


Wrapping up

So, now you have a new Wizard pane that will give you some more information from the computer before you install the machine and if you don’t like to see that page the only thing you need to do is to add SkipHardwareInfo=YES in Customsettings.ini

/mike

Posted in Deployment | Tagged: , , | 8 Comments »

Things you should know: – Undocumented Properties in MDT 2010 Update 1

Posted by Mikael Nystrom on May 22, 2011

Once again, at 33000 feet over the Atlantic Ocean on my way back from TechEd NA in Atlanta I started to think about all the different properties in MDT 2010 Update 1 that I use which are not really documented, trust me there are “some”. Some of them is, well, not really so useful, but some of them I really use and so should you. So this post is solely made for the purpose of giving you the same “relaxed” life that I have. Hmm, that did not really came out right I think, anyway, You know what I mean, right…

Now, since I not work on that team, I just happen to know them a bit. This is NOT any kind of official description and/or documentation, hopefully someone@microsoft.com will update he documentation sometime around this, especially when virtualization is getting to be more of the standard.

Virtualization Information:

We are deploying more and more virtualization stuff and in MDT 2010 Update 1 we have a bunch of them that you can use:

Property Can be Read Only Description
IsHypervisorRunning True/False Yes Detects if the Microsoft Hypervisor is running on the OS

Can be used when you need to detect if the Hyper-V role is installed and running. In that vase you know that this really is a Hyper-V server and then scripts to enable core parking should run

SupportsVT True/False Yes Returns True if the hardware supports Intel-VT or AMD-V and it is enabled in BIOS

If true you know that this machine should be able to run Virtual PC (and MED-V) otherwise not)

SupportsNX True/False Yes Returns True if the hardware supports No Execute BIT and it is enabled in BIOS

There could be applications that require this to be disabled or the opposite around, anyway, here is a way of detecting this

Supports64Bit True/False Yes Returns True if the hardware supports 64 Bit OS

Pretty easy, you could use this to pick the correct Task Sequence for the OS install

SupportsHyperVRole True/False Yes Returns True if the hardware supports Microsoft Hyper-V

Also easy, if True, well then you can enable the Hyper-V role, otherwise, you cannot. You could use this flip OS install switch to NO and that will prohibit the install of the OS you Hyper-V is something you really need.

IsVM True/False Yes Returns True if we are running in a VM.

This one is really great, if set to True, well the you are running a VM and most common setting I use for this is

DoNotCreateExtraPartition=YES, since there are NO reason to create the extra bit locker partition in a VM; It is the opposite around, it is a pain in the neck to have the 300 mb partition in the end since that will prohibit the possibilities to extend the virtual hard drive if needed without spending some manual labor of rearrange the BCD

Some others

There are some others that I use from time to time and here you have them

Property Can be Read Only Description
Debug True/False No Returns True if you are running in debug mode, You can also run all scripts with the switch /Debug:True to increase the logging

If you just read the value, you can create a separate section in the customsettings,ini file that will set other parameters differently if you run in debug mode, as one example you could specify SLShareDynamicLogging if this is set to True

OSCurrentVersion 6.1.7601 Yes Will return the version number of the Operating System that we currently run on.
OSCurrentBuild 7601 Yes Will return the Build number which is a subset of the operating system version
IsUEFI True/False Yes Returns True if the boot using UEFI instead of BIOS
OSSKU ENTERPRISE Yes Returns the SKU name from the OS, it is the same name you can see in the WIM file
TaskSequenceVersion 1.0 Yes Returns the value for the Task Sequence version number that you set in the Workbench

Could be used to detect what version you run of the Sequence, if it is running a certain number then certain settings should be made, otherwise not

TaskSequenceName Windows 7 x86 Image Yes Returns the value of the name of the Task Sequence

Most common use I have for this is to set _SMSTSOrgName=Deploying %TaskSequenceName% on %OSDComputer%

/mike

Posted in Deployment | Tagged: , , | 6 Comments »

Just for fun: – The MDT team has a humor

Posted by Mikael Nystrom on May 22, 2011

There is a step in the Task Sequence that checks bios, but most people never check what really happens behind the scenes. Well it checks the BIOS, right. But my best guess is that you did not get this one! 

Verifying WYSIWYGComputers BIOS

I know many vendors, but I have never come across this one “WYSIWYGComputers”, I mean I heard about ACME but this one…

But here is the important takeaway, as long as they have fun, they will keep on creating great stuff.

/mike

<?xml version=”1.0″ encoding=”Windows-1252″?>
<DATABASE xsi:schemaLocation=”urn:schemas.microsoft.com/appx/2006/07/Dbu Driver.xsd” xmlns=”urn:schemas.microsoft.com/appx/2006/07/Dbu” xmlns:dc=”http://purl.org/dc/elements/1.1/&#8221; xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221; MAX_HTMLHELPID=”110033″>
<DRIVER>
<HISTORY>
<DESCRIPTION>
Insert your description here. This field will be written to the log if a match is found.
</DESCRIPTION>
</HISTORY>
<LOOKUP NAME=” _BIOS_DESCRIPTION_HERE_ “>
<DATA NAME=”Computer Manufacturer” VALUETYPE=”string” VALUE=” _COMPUTER_MFG_HERE_ “/>
<DATA NAME=”Model” VALUETYPE=”string” VALUE=” _COMPUTER_MODEL_HERE_”/>
<DATA NAME=”Date” VALUETYPE=”string” VALUE=” _DATE_TIME_HERE_ “/>
</LOOKUP>
</DRIVER>
<DRIVER>
<HISTORY>
<DESCRIPTION>
The WYSIWYG Super Cool Computer 2007 has a bug in version 1.23 of the BIOS that prevents Windows Vista from installing.
Version 1.24 fixes the problem, please update the BIOS. Check http://drivers.WYSIWYGComputers.com/
</DESCRIPTION>
</HISTORY>
<LOOKUP NAME=”XXX ROM BIOS Version 1.23″>
<DATA NAME=”Computer Manufacturer” VALUETYPE=”string” VALUE=”Wysiwyg Computers”/>
<DATA NAME=”Model” VALUETYPE=”string” VALUE=”WYSIWYG Super Cool Computer 2007″/>
<DATA NAME=”Date” VALUETYPE=”string” VALUE=”20060801000000.000000+000″/>
</LOOKUP>
</DRIVER>
</DATABASE>

Posted in Deployment | Tagged: , , | Leave a Comment »

Step-by-Step: Upgrading BIOS during LiteTouch Deployment, for Dell, Hewlett-Packard and Lenovo

Posted by Mikael Nystrom on May 20, 2011

Ok, so here is the short( not really sure this is going to be short by the way, well nothing is perfect) story, once again a customer (Thank you Bill for letting me test fun stuff in your environment!) asked me for a feature that I have done multiple times before, so the task was easy, just upgrade the BIOS when we deploy the machine, so I told them that
–Hey, this is the way to do it and the reply was.
–Sorry, It cannot be done as far as we know, since we have Lenovo and Lenovo does not allow you to run the BIOS firmware update in WinPE.
So it was time to start some thinking. We need to figure out how to create a solid solution and we to do an “all-night-long”. I did not just wanted to fix this customers issue, I want some kind of universal solution that will work on any hardware, client, type, vendor and so on. So I fired up my idea-engine and bang, there it was. I had a plan and a solution that would work, should only take a couple of minutes, kind of. At least that was the idea
(Just a quick note: I did not have the capability to test the Dell part of the script since I don’t have a Dell computer (For some reason most vendors are very polite and give me things to play with, but Dell obviously have an other agenda and I cannot afford to buy a Dell computer just to be able to write blog posts. Anyone has a used Dell that you are willing to give away for scientific experiments by the way? However, it should work, if it doesn’t, ping me)

Here is what we need:

  • It should work with any vendor in the same way
  • It should work with both servers and clients
  • It should do some serious logging
  • I should be able reuse some part of the code in other scenarios
  • It should work in WinPE

…Six hours later, Non-Stop “NCIS” at the hotel room, numerous reboots and massive amount of Coke and the solution was done and here is how it works.

We need information to make decisions!

We really need some variables to work with and it would be possible to use just one simple script to solve the problem, but then I started to think a bit, and.. It would be nice to do this  just a bit “Michael Niehaus / Keith Garner style”.

That way I could use these variables for other purposes then just this and I would in the long run have a nice UserExit Script that will collect all kinds of variables to make the deployment even more dynamically then it already is (Why?, Because I can of course…). Now it would be nice if all vendors use the same location in WMI to store information for the same thing, but that’s not going to happen, trust me, the British Empire will shift over to driving on the right side of the road and the US will start using the metric system before that happens, not very likely in other words. So we need to collect the following so that we later on can use BIOS information to determine what version we have and if it should be upgraded or not

  • From WIN32_BIOS we need SMBIOSBIOSVersion (this will be captured as SMBIOSBIOSVersion)
  • From WIN32_BIOS we need Version (this will be captured as BIOSVersion)
  • From Win32_ComputerSystemProduct we need Version (this will be captured as ProductVersion)

This way, we can use different ´properties and variables for different systems and that is pretty nice.

How will we use the different variables?

Well, by combing them differently we will be able to use them in different combinations to get what we need, here is how we will use them

  • Dell = SMBIOSBIOSVersion + Make + Model
  • Hewlett-Packard = BIOSVersion + Make + Model
  • LENOVO = SMBIOSBIOSVersion + Make + ProductVersion

Now, we can use these properties for this purpose, but you can also use these for other purposes, like driver handling, rules for applications and much more. There is also possible to combine this with the ModelAlias userexit script if you feel for it.

Using a user exit script is the solution to get this data

A user exit script is just a simple piece of VB script that runs as a part of the gather process when MDT reads the customsettings.ini file. So we need to create the ZTITheUserExit.vbs script and put that in the Scripts folder and then we need to update the CustomSettings.ini file so that it is used. You can put the execution of the user exit script basically anywhere in the priority, but I prefer to have a separate section called [Init] where I run everything instead of using default, since default in a production environment seems to be ending up as the last priority, time to stop writing and show you how it should look like:

[Settings]
Priority=Init, Default
Properties=BIOSVersion, SMBIOSBIOSVersion, ProductVersion

[Init]
SMBIOSBIOSVersion=#SetSMBIOSBIOSVersion()#
BIOSVersion=#SetBIOSVersion()#
ProductVersion=#SetProductVersion()#
UserExit=ZTITheUserExit.vbs

[Default]
OSInstall=Y

As you can see, the first thing that will happen when MDT starts reading the customsettings.ini file it finds the section init and it the jumps to that section in the file where it will discover that it needs to run ZTITheUserExit.vbs and that it will be three functions in the script that should return the values into BIOSVersion, SMBBIOSBIOSVersion and ProductVersion. After this has been executed you can now use for example  %BIOSVersion% in the same way as other properties such as %Make%, %Model% if you like. My guess right now is that you now would like to see the script, right? So, here it is, just copy the content and save as ZTITheUserExit.vbs in the scripts folder

ZTITheUserExit.vbs


Function UserExit(sType, sWhen, sDetail, bSkip)
    oLogging.CreateEntry “UserExit: Running Script: ” & sType & ” ” & sWhen & ” ” & sDetail, LogTypeInfo
    UserExit = Success
End Function

Function SetSMBIOSBIOSVersion()
    oLogging.CreateEntry “UserExit: Running Function SetSMBIOSBIOSVersion : ” & sType & ” ” & sWhen & ” ” & sDetail, LogTypeInfo
    Dim objWMI
    Dim objResults
    Dim objInstance
    Dim SMBIOSBIOSVersion
   
    Set objWMI = GetObject(“winmgmts:”)
    Set objResults = objWMI.ExecQuery(“SELECT * FROM Win32_BIOS”)
        If Err then
        oLogging.CreateEntry “Error querying Win32_BIOS: ” & Err.Description & ” (” & Err.Number & “)”, LogTypeError
    Else
        For each objInstance in objResults
            If Not IsNull(objInstance.SMBIOSBIOSVersion) Then
                    SMBIOSBIOSVersion = Trim(objInstance.SMBIOSBIOSVersion)
            End If
        Next
    End If
    SetSMBIOSBIOSVersion = SMBIOSBIOSVersion
    oLogging.CreateEntry “UserExit: Result: ” & SetSMBIOSBIOSVersion, LogTypeInfo
End Function

Function SetBIOSVersion()
    oLogging.CreateEntry “UserExit: Running Function SetBIOSVersion : ” & sType & ” ” & sWhen & ” ” & sDetail, LogTypeInfo
    Dim objWMI
    Dim objResults
    Dim objInstance
    Dim BIOSVersion
   
    Set objWMI = GetObject(“winmgmts:”)
    Set objResults = objWMI.ExecQuery(“SELECT * FROM Win32_BIOS”)
        If Err then
        oLogging.CreateEntry “Error querying Win32_BIOS: ” & Err.Description & ” (” & Err.Number & “)”, LogTypeError
    Else
        For each objInstance in objResults
            If Not IsNull(objInstance.Version) Then
                    BIOSVersion = Trim(objInstance.Version)
            End If
        Next
    End If
    SetBIOSVersion = BIOSVersion
    oLogging.CreateEntry “UserExit: Result: ” & SetBIOSVersion, LogTypeInfo
End Function

Function SetProductVersion()
    oLogging.CreateEntry “UserExit: Running Function SetProductVersion : ” & sType & ” ” & sWhen & ” ” & sDetail, LogTypeInfo
    Dim objWMI
    Dim objResults
    Dim objInstance
    Dim ProductVersion
   
    Set objWMI = GetObject(“winmgmts:”)
    Set objResults = objWMI.ExecQuery(“SELECT * FROM Win32_ComputerSystemProduct”)
        If Err then
        oLogging.CreateEntry “Error querying Win32_BIOS: ” & Err.Description & ” (” & Err.Number & “)”, LogTypeError
    Else
        For each objInstance in objResults
            If Not IsNull(objInstance.Version) Then
                    ProductVersion = Trim(objInstance.Version)
            End If
        Next
    End If
    SetProductVersion = ProductVersion
    oLogging.CreateEntry “UserExit: Result: ” & SetProductVersion, LogTypeInfo
End Function



If we run this script as a part of ZTIgather you should get something similar. So lets just look at the output that the userexit script will generate so that you have some kind of reference:

Determining the INI file to use.
Using COMMAND LINE ARG: Ini file = ..\Control\CustomSettings.ini
Finished determining the INI file to use.
Added new custom property MYCUSTOMPROPERTY
Added new custom property REFTYPE
Added new custom property BIOSVERSION
Added new custom property SMBIOSBIOSVERSION
Added new custom property PRODUCTVERSION
Using from [Settings]: Rule Priority = INIT, TASKSEQUENCEID, DEFAULT
—— Processing the [INIT] section ——
USEREXIT: Running Script: SECTION BEFORE INIT
User exit “C:\MDTLab\Scripts\ZTITheUserExit.vbs” called successfully, skip = False.
USEREXIT: Running Function SetBIOSVersion :
USEREXIT: Result: HPQOEM – f
Property BIOSVERSION is now = HPQOEM – f
Using from [INIT]: BIOSVERSION = HPQOEM – f
USEREXIT: Running Function SetSMBIOSBIOSVersion :
USEREXIT: Result: 68CVD Ver. F.0A
Property SMBIOSBIOSVERSION is now = 68CVD Ver. F.0A
Using from [INIT]: SMBIOSBIOSVERSION = 68CVD Ver. F.0A
USEREXIT: Running Function SetProductVersion :
USEREXIT: Result:
Property PRODUCTVERSION is now =
Using from [INIT]: PRODUCTVERSION =
USEREXIT: Running Script: SECTION AFTER INIT
User exit “C:\MDTLab\Scripts\ZTITheUserExit.vbs” called successfully, skip = False.

Now, lets take a quick look, these are the property values we will get in return on a HP Compaq 8540w (My portable datacenter)

Property BIOSVERSION is now = HPQOEM – f
Property SMBIOSBIOSVERSION is now = 68CVD Ver. F.0A
Property PRODUCTVERSION is now =

Seems to be working. Some explanations before we continue, one thing to notice is that the PRODUCTVERSION is blank and that BIOSVERSION does not give use anything useful, on HP Compaq it is the SMBIOSBIOSVERSION that is the real version of the BIOS, on LENOVO it is BIOSVERSION (You can use SMBIOSBIOSVERSION to, but it also contain “rubbish”. The only reason to use PRODUCTVERSION is that on Lenovo machines that will be the model name, not that nasty number that is extremely unique (so, on a LENOVO T410 it would give you “LENOVO T410” as result

Next step, create a script that can use these variables and then determine if the BIOS has the correct version or if it should be upgraded.

And that is a walk in the park, just create script file called ZTIBIOSUpgrade.wsf, save it the Scripts folder:
The format of the file is somewhat of a school example in the way that it contains extensive logging and contain routines for bailing out if things go bad (script has a certain habit of doing exactly that for some reason. It also have one really strange thing, it has a section for Microsoft Hardware which is Hyper-V, Virtual PC and Virtual Server and now, I’m not insane. I did create that section to verify that it works, since it is some much faster to do snapshots and flip back and forward without re-deploying a physical machine.


<job id=”ZTIBIOSUpgrade”>
<script language=”VBScript” src=”ZTIUtility.vbs”/>
<script language=”VBScript”>

‘//—————————————————————————-
‘// Solution: BIOS Upgrade
‘//
‘// The following values must be populated in the CustomSettings.ini
‘// using a userexit script (ZTITheUserExit.vbs
‘//         – BIOSVersion
‘//         – SMBIOSBIOSVersion
‘//         – ProductVersion
‘// To get these values you also need to execute a userexit script
‘// NOTE: The Section for “Microsoft” is only intended as a test/demo/play,
‘// since the BIOS cannot be updated from within the VM itself.
‘//
‘// Usage: cscript ZTIBIOSUpgrade.wsf[/debug:true]
‘// Version: 1.0 – 4 May 2011 – Mikael Nystrom
‘//
‘// This script is provided “AS IS” with no warranties, confers no rights and
‘// is not supported by the authors
‘//
‘//—————————————————————————-

‘//—————————————————————————-
‘// Global constant and variable declarations
‘//—————————————————————————-

‘//—————————————————————————-
‘//
‘// Global constant and variable declarations
‘//
‘//—————————————————————————-

Option Explicit

Dim iRetVal

‘//—————————————————————————-
‘// End declarations
‘//—————————————————————————-

‘//—————————————————————————-
‘// Main routine
‘//—————————————————————————-

‘On Error Resume Next
iRetVal = ZTIProcess
ProcessResults iRetVal
On Error Goto 0

‘//—————————————————————————
‘//
‘// Function: ZTIProcess()
‘//
‘// Input: None
‘//
‘// Return: Success – 0
‘// Failure – non-zero
‘//
‘// Purpose: Perform main ZTI processing
‘//
‘//—————————————————————————
Function ZTIProcess()

    Dim sMake
    Dim sModel
    Dim sSMBIOSBIOSVersion
    Dim sBIOSVersion
    Dim sExeToRun
    Dim sRefBiosVersion
    Dim sProduct
    Dim sProductVersion
    Dim sOSVersion
    Dim sDeployDrive
    Dim sDeployRoot
    Dim sExecuteCommand
   
    iRetVal = Success
    ZTIProcess = iRetval
   
    sMake = oEnvironment.Item(“Make”)
    sModel = oEnvironment.Item(“Model”)
    sSMBIOSBIOSVersion  = oEnvironment.Item(“SMBIOSBIOSVersion “)
    sBIOSVersion = oEnvironment.Item(“BIOSVersion”)
    sProduct = oEnvironment.Item(“Product”)
    sProductVersion = oEnvironment.Item(“ProductVersion”)
    sOSVersion = oEnvironment.Item(“OSVersion”)
    sDeployDrive = oEnvironment.Item(“DeployDrive”)
    sDeployRoot = oEnvironment.Item(“DeployRoot”)
   
   
    oLogging.CreateEntry “———————————————-”, LogTypeInfo
    oLogging.CreateEntry “Config-BIOSUpgrade – Runninng BIOS Upgrade:   “, LogTypeInfo
    oLogging.CreateEntry “Config-BIOSUpgrade – Make is:                 ” & sMake, LogTypeInfo
    oLogging.CreateEntry “Config-BIOSUpgrade – Model is:                ” & sModel, LogTypeInfo
    oLogging.CreateEntry “Config-BIOSUpgrade – Product is:              ” & sProduct, LogTypeInfo
    oLogging.CreateEntry “Config-BIOSUpgrade – ProductVersion is:       ” & sProductVersion, LogTypeInfo
    oLogging.CreateEntry “Config-BIOSUpgrade – SMBIOSBIOSVersion is:    ” & sSMBIOSBIOSVersion, LogTypeInfo
    oLogging.CreateEntry “Config-BIOSUpgrade – BIOSVersion is:          ” & sBIOSVersion, LogTypeInfo
    oLogging.CreateEntry “Config-BIOSUpgrade – OSVersion is:            ” & sOSVersion, LogTypeInfo
    oLogging.CreateEntry “Config-BIOSUpgrade – DeployDrive:             ” & sDeployDrive, LogTypeInfo
    oLogging.CreateEntry “Config-BIOSUpgrade – DeployRoot:              ” & sDeployRoot, LogTypeInfo
    oLogging.CreateEntry “———————————————-”, LogTypeInfo

    If sOSVersion = “WinPE” Then
    oLogging.CreateEntry “Config-BIOSUpgrade – Running WinPE…”, LogTypeInfo
    oLogging.CreateEntry “Config-BIOSUpgrade – Loading battery monitor…”, LogTypeInfo
    oUtility.RunWithHeartbeat(“drvload \Windows\inf\battery.inf”)
    oLogging.CreateEntry “Config-BIOSUpgrade – Loading battery monitor…Done”, LogTypeInfo’
    Else
    oLogging.CreateEntry “Config-BIOSUpgrade – Not running WinPE…”, LogTypeInfo
    oLogging.CreateEntry “Config-BIOSUpgrade – Not loading battery monitor …”, LogTypeInfo
    End If
   
    Select Case sMake
        Case “Dell Computer Corporation”, “Dell Inc.”, “Dell Computer Corp.”
            oLogging.CreateEntry “Config-BIOSUpgrade – ” & sMake & ” is selected”, LogTypeInfo
            oLogging.CreateEntry “Config-BIOSUpgrade – …searching for BIOS upgrades”, LogTypeInfo
            Select Case sModel
                Case “Latitude D420″ : sExeToRun = “LatitudeD420.cmd ” : sRefBiosVersion=”A06″
                Case Else : sExeToRun = “NA”
            End Select
                If sExeToRun = “NA” Then
                    oLogging.CreateEntry “Config-BIOSUpgrade – No support for ” & sModel, LogTypeInfo
                Elseif sSMBIOSBIOSVersion = sRefBiosVersion Then
                    oLogging.CreateEntry “Config-BIOSUpgrade – No upgrade needed”, LogTypeInfo
                Else
                    oLogging.CreateEntry “Config-BIOSUpgrade – Correct version should be ” & sRefBiosVersion, LogTypeInfo
                    oLogging.CreateEntry “Config-BIOSUpgrade – Upgrading ” & sModel & ” from ” & sSMBIOSBIOSVersion & ” to ” & sRefBiosVersion, LogTypeInfo

                    sExecuteCommand = sDeployDrive & “\BIOSUpgrade\Dell\” & sExeToRun & sDeployDrive
                    oLogging.CreateEntry “Config-BIOSUpgrade – Command to execute ” & sExecuteCommand, LogTypeInfo

                    iRetVal = oUtility.RunWithHeartbeat(sExecuteCommand)
                    if (iRetVal = 0) or (iRetVal = 3010) then
                        ZTIProcess = Success
                    Else
                        ZTIProcess = Failure
                    End If
                End If
 
        Case “Microsoft Corporation”
            oLogging.CreateEntry “Config-BIOSUpgrade – ” & sMake & ” is selected”, LogTypeInfo
            oLogging.CreateEntry “Config-BIOSUpgrade – …searching for BIOS upgrades”, LogTypeInfo
            Select Case sModel
                Case “Virtual Machine” : sExeToRun = “VirtualMachine.cmd ” : sRefBiosVersion=”VRTUAL – 3000920″
                Case Else : sExeToRun = “NA”
            End Select
                If sExeToRun = “NA” Then
                    oLogging.CreateEntry “Config-BIOSUpgrade – No support for ” & sModel, LogTypeInfo
                Elseif sBIOSVersion = sRefBiosVersion Then
                    oLogging.CreateEntry “Config-BIOSUpgrade – No upgrade needed”, LogTypeInfo
                Else
                    oLogging.CreateEntry “Config-BIOSUpgrade – Correct version should be ” & sRefBiosVersion, LogTypeInfo
                    oLogging.CreateEntry “Config-BIOSUpgrade – Upgrading (Well, not really, but what the heck, lets try that…” & sModel & ” from ” & sSMBIOSBIOSVersion & ” to ” & sRefBiosVersion, LogTypeInfo

                    sExecuteCommand = sDeployDrive & “\BIOSUpgrade\Microsoft\” & sExeToRun & sDeployDrive
                    oLogging.CreateEntry “Config-BIOSUpgrade – Command to execute ” & sExecuteCommand, LogTypeInfo

                    iRetVal = oUtility.RunWithHeartbeat(sExecuteCommand)
                    if (iRetVal = 0) or (iRetVal = 3010) then
                        ZTIProcess = Success
                    Else
                        ZTIProcess = Failure
                    End If
                End If

        Case “IBM”, “LENOVO”
            oLogging.CreateEntry “Config-BIOSUpgrade – ” & sMake & ” is selected”, LogTypeInfo
            oLogging.CreateEntry “Config-BIOSUpgrade – …searching for BIOS upgrades”, LogTypeInfo
            Select Case sProductVersion
                Case “ThinkPad T410″ : sExeToRun = “ThinkPadT410.cmd ” : sRefBiosVersion = “LENOVO – 1360″
                Case Else : sExeToRun = “NA”
            End Select
                If sExeToRun = “NA” Then
                    oLogging.CreateEntry “Config-BIOSUpgrade – No support for ” & sProductVersion, LogTypeInfo
                Elseif sBIOSVersion = sRefBiosVersion Then
                    oLogging.CreateEntry “Config-BIOSUpgrade – No upgrade needed”, LogTypeInfo
                Else
                    oLogging.CreateEntry “Config-BIOSUpgrade – Correct version for ” & sProductVersion & ” should be ” & sRefBiosVersion, LogTypeInfo
                    oLogging.CreateEntry “Config-BIOSUpgrade – Upgrading ” & sProductVersion & ” from ” & sBIOSVersion & ” to ” & sRefBiosVersion, LogTypeInfo

                    sExecuteCommand = sDeployDrive & “\BIOSUpgrade\Lenovo\” & sExeToRun & sDeployDrive
                    oLogging.CreateEntry “Config-BIOSUpgrade – Command to execute ” & sExecuteCommand, LogTypeInfo

                    iRetVal = oUtility.RunWithHeartbeat(sExecuteCommand)
                    if (iRetVal = 0) or (iRetVal = 1) or (iRetVal = 3010) then
                        ZTIProcess = Success
                    Else
                        ZTIProcess = Failure
                    End If
                End If
       
        Case “Hewlett-Packard”
            oLogging.CreateEntry “Config-BIOSUpgrade – ” & sMake & ” is selected”, LogTypeInfo
            oLogging.CreateEntry “Config-BIOSUpgrade – …searching for BIOS upgrades”, LogTypeInfo
            Select Case sModel
                Case “HP EliteBook 8540w” : sExeToRun = “HPEliteBook8540w.cmd ” : sRefBiosVersion = “68CVD Ver. F.0A”
                Case Else : sExeToRun = “NA”
            End Select
                If sExeToRun = “NA” Then
                    oLogging.CreateEntry “Config-BIOSUpgrade – No support for ” & sModel, LogTypeInfo
                Elseif sSMBIOSBIOSVersion = sRefBiosVersion Then
                    oLogging.CreateEntry “Config-BIOSUpgrade – No upgrade needed”, LogTypeInfo
                Else
                    oLogging.CreateEntry “Config-BIOSUpgrade – Correct version should be ” & sRefBiosVersion, LogTypeInfo
                    oLogging.CreateEntry “Config-BIOSUpgrade – Upgrading ” & sModel & ” from ” & sSMBIOSBIOSVersion & ” to ” & sRefBiosVersion, LogTypeInfo

                    sExecuteCommand = sDeployDrive & “\BIOSUpgrade\Hewlett-Packard\” & sExeToRun & sDeployDrive
                    oLogging.CreateEntry “Config-BIOSUpgrade – Command to execute ” & sExecuteCommand, LogTypeInfo

                    iRetVal = oUtility.RunWithHeartbeat(sExecuteCommand)
                    if (iRetVal = 0) or (iRetVal = 3010) then
                        ZTIProcess = Success
                    Else
                        ZTIProcess = Failure
                    End If
                End If
        Case Else
            oLogging.CreateEntry “Config-BIOSUpgrade – ” & sMake & ” Hey, that’s a brand I have never herd of, interesting…”, LogTypeInfo
    End Select
    oLogging.CreateEntry “———————————————-”, LogTypeInfo
End Function
</script>
</job>


So, now when we have the script created maybe I should try to explain what it actually is doing, I mean you might want to do some modification to it and then it is kind of nice to know where to poke around, So here is the short story.

  • First we make a bunch of Dim’s and the reason is simple and it is called “Option Explicit”. That is set globally in MDT and that means that it will blow up if you try to use a variable that is NOT declared (you might have seen a Error 500: Variable is undefined in MDT.
  • Next I dump all these variables to the bdd.log file, this is mainly need when you trouble shoot things and when you like to look at log files
  • Next up is, well here is the trick to make Lenovo machines run firmware updates in WinPE. The reason they does not work is because the utility looks at the state of power. They do that for one reason, to verify that the machine are running with the power cord connect to a power source and not running on battery (make sense). The secret source is to run this command:
  • oUtility.RunWithHeartbeat(“drvload \Windows\inf\battery.inf”)
  • The oUtility.RunWithHeartbeat is part of a function that gets loaded when we load up the library that is called ZTIUtlity.VBS which is a part of MDT and here is a tip for you, you should always base your script in MDT on a template that will leverage the use of ZTIUtility.vbs. I also set one condition here and that is to only load the driver when we are running WinPE, there is no point in loading it when we run the full-blown OS, it will be loaded in that case.
  • The rest is kind of easy, what will happen is that we will use the %Make% variable in MDT to see in what section we should continue, then when we we find the correct one we will get the %model% in all cases besides when it is a LENOVO, in that case we will use ProductVersion instead and if we find something that is a match when the compare the RefBios value with the version of the BIOS in the machine, it it is the same we just bail out and we are done, otherwise we will find the command to run. My first attempt of creating this had a small “flaw” and it took a couple of “angry-management seconds) to find out why and how to fix it, and the trick is that I need to run many of these firmware updates utilities on a drive letter, some of the work using UNC, but far from all and this must work in all situations, so I need a drive letter, but I cannot trust on MDT to always use Z:, so by using a built in variable called  DeployDrive to deliver that to me and that is why we will build the command to execute based on a bunch of variables
  • So, what does this means, well. In the scripts folder you will have to create a folder structure called BiosUpgrade and in the folder you then create Subfolders for each and every Vendor you have and those folders you the create one folder for each and every model and in those folders you put the firmware. It also means that you need to update this script whenever you have new versions of vendors, models and new versions of BIOS and firmware (well, I could have spent yet one night more to make it one step more dynamically, but hey I need to sleep sometime)

The Batch file…

Now as you can see in the script I have one batch file for each model,

(Looks like this in the VB)
Case “ThinkPad T410″ : sExeToRun = “ThinkPadT410.cmd ” :

that batch file need a header to work, the header will make sure that you will be “in” the folder where the executable is located, that will not be needed every time, but most likely it will be needed most of the time and that batch file should look like this

%1
CD %0\..\
REM *** For testing only *** DIR > list.txt
REM Run your silent command here that will update the BIOS
REM Note: It MUST be silent and it should NOT restart the machine when it is done

Now, since you are (mots likely) a bit curios you will notice that the first line is a %1 and the reason behind that is kind of simple, %1 is the first parameter after the batch file it self, but hey, where did that variable come from??? Well, take a look at the line of execution, if you look really close you will see that we end that that line with a space in the end and the we use the DeployDrive variable and that will result in one thing. When the batch file runs it will first make sure it sets the working drive to the drive where the firmware updates are located and then we run the really old-school command

CD %0\..\

If %1 is the first parameter after the batch file, what the heck is the %0, well here is the fun thing. It’s the batch file name it self (you can test this by creating a batch file with just one line it it “Echo %0” and you will see that the result will be the name of the batch file name itself, the we just use \..\ which sets the working directory to the same location where the script is located and that is exactly what we need. Problem solved (using the MacGyver method)

The Folder Structure

Here is the folder structure that I have in the Scripts folder. In this folder structure I will store all the “Upgrades” plus the batch file. It should look like this to match the script.

image

So, in the scripts folder you will create Dell, Hewlett-Packard, LENOVO and so on, in the next level you create one folder for each model and that folder you store the upgrades,

Last step

The last thing you need to do is pretty easy. Open up your task sequence and add a Run Command that runs the ZTIBiosUpgrade.wsf. The run command should look like this:

cscript.exe “%SCRIPTROOT%\ZTIBiosUpgrade.wsf”

You can put the Run Command in the first section if you want to, there is were we did put it in the customer solution, something like this.

image

(Hey, if you ever wonder when I have time to write many of my postings it’s right now, somewhere over the Atlantic ocean flying at 10.000 feet or more with the headset filled with old-school rock, the flight attended just went pass me asking for more to drink and yes, she gave me a bunch of coke can’s. Thank you Swiss.)

A short note, this will of course work with servers too, just need to do some small adjustments

/Mike

Posted in Deployment, Windows 7, Windows Server 2008 R2 | Tagged: , , , , , , , | 48 Comments »

LTIPause.vbs – Take a break in deployment

Posted by Mikael Nystrom on May 10, 2011

Ok, so I know there is a very nice script called LTISuspend.wsf that will suspend the deployment, kind of perfect when you need to stop the deployment at a certain point and then resume after you have “fixed” the things you need to do.

But that is not always perfect, especially when you are in to troubleshooting, of course that never happens for me since I always build everything perfect the first time, or not. So a good friend of mine once showed me a simple but very useful tip and that is to create a vbscript called LTIPause.vbs, save it in the scripts folder and then use the run command for every section that you would like the task sequence to take a break, when you have checked whatever you need to check you just hit ok and it will continue. The content of the script is very, very small and looks like this



MsgBox “ok”

Hey, it does not get easier then that as far as I see it. Well that works, but can it be improved? Yes, it can. Here is my story of improvement

  • Adding direct output of variables in the msgbox
  • Adding a way to “disable” all Pause commands in all Sequences

Let us start with adding environment variables, the only thing we need to do is to use the variables that are stored in the task sequence and the print out the information on the screen if everything is working it should look something like this when you run it:

image

the LTIPause.vbs should look like this:


Set env = CreateObject(“Microsoft.SMS.TSEnvironment”)
MsgBox “Pausing Task Sequence, click OK to continue…” & chr(13) & _
” ” & chr(13) & _
“Computername:” & chr(9) & chr(9) & env(“OSDComputername”) & chr(13) & _
“IPAddress001:” & chr(9) & chr(9) & env(“IPAddress001″) & chr(13) & _
“DefaultGateWay001:” & chr(9) & env(“DefaultGateway001″) & chr(13) & _
“Phase:” & chr(9) & chr(9) & chr(9) & env(“Phase”) & chr(13) & _
“Make:” & chr(9) & chr(9) & chr(9) & env(“Make”) & chr(13) & _
“Model:” & chr(9) & chr(9) & chr(9) & env(“Model”) & chr(13) & _
“VMPlatform:” & chr(9) & chr(9) & env(“VMPlatform”) & chr(13) & _
“TaskSequenceID:” & chr(9) & chr(9) & env(“TaskSequenceID”) & chr(13) & _
“TaskSequenceName:” & chr(9) & env(“TaskSequenceName”) & chr(13) & _
“TaskSequenceVersion:” & chr(9) & env(“TaskSequenceVersion”) & chr(13) & _
“ViaServerConfig:” & chr(9) & chr(9) & env(“ViaServerConfig”) & chr(13) & _
“ImageFlags:” & chr(9) & chr(9) & env(“ImageFlags”) & chr(13) & _
“ImageBuild:” & chr(9) & chr(9) & env(“ImageBuild”) & chr(13) & _
“SLShare:” & chr(9) & chr(9) & chr(9) & env(“SLShare”) & chr(13) & _
“SLShareDynamicLogging:” & chr(9) & env(“SLShareDynamicLogging”) & chr(13) & _
“DeployRoot:” & chr(9) & chr(9) & env(“DeployRoot”) & chr(13) & _
“DriverGroup001:” & chr(9) & chr(9) & env(“DriverGroup001″) & chr(13) & _
“DriverGroup002:” & chr(9) & chr(9) & env(“DriverGroup002″) & chr(13)_
, 0, “LTIPause”


Sorry for all the chr, that’s just VB formatting and not needed for functionality but it looks better this way. You can add all kind of variables and their corresponding values. (look for Properties in the help, that will get you some of them) So now you have the script, just save it as LTIPause.vbs in the scripts folder and add it to the step you would like the the Task Sequence to pause, like this:

image

Now, instead of removing the pauses in your task sequences you can keep them and just disable them or you can set a condition on these commands and that I do. I add the following to my CustomSettings.ini


[Settings]
Priority=Default
Properties=MyCustomProperty, TestMode

[Default]
OSInstall=Y
TestMode=ENABLE


and then in the task sequence you just set this as a condition to run the command like this:

image

as long as TestMode is set to Enable it will run the command LTIPause at the locations you have decided by putting in LTIPause.vbs script.

If you are testing your Task Sequence from a command line you can also add /TestMode:ENABLE so it would look like this:

\\server\share$\scripts\litetouch.vbs /TestMode:ENABLE

That would result in the same thing.

You might want to read a bit more on this subject:

/mike aka The Deployment Bunny

Posted in Deployment | Tagged: , , | 2 Comments »

Adding drivers using PNPutil and Forfiles

Posted by Mikael Nystrom on May 7, 2011

Hey, don’t believe that this is a correct way of adding driver, but sometime you just need to be a bit a of “MacGyver” (If anyone can remember that TV Series, I’m Old so, hey I remember that anyway. For those that does not remember that it is all about being in trouble and fixing something extremely complicated to get out of trouble using simple things that you have around you and that is exactly what this is)

So, one more I’m working with a customer (still love that) and we are fooling around with drivers for different reasons, anyhow we need to load a bunch of drivers in the running OS and unfortunately you cannot use DISM.exe when the system is online for the purpose of loading drivers or remove them either for that matter, but you can use PNPUtil and that has one easy syntax, now, PNPUtil has one “small” issue, it cannot traverse folder. (Why is it never simple?)

Remembering “The good old days”

Luckily for me I’m a grove up using 8” floppy’s, so I know a bit DOS so I have done the “FOR %variable IN (set) DO command [command-parameters]” stuff a bunch of times (Credit to Björn Österman that explained this stuff many years ago). But then Microsoft started creating all the Resource kits for NT and one of the utilities in there was one of my loved ones, let me introduce Mr. ForeFiles.exe, one of of great hero’s back in the days.

Say Hi to ForFiles.exe

Forefiles.exe can basically do this “For every file that matches this criteria do the following” this is a perfect tool for automation when you need to remove, archive, delete files, like this:

forfiles.exe /P D:\ /M *.log /C “CMD /c del @Path”

That command will delete all the *.log files from the entire D:\ and all subfolders and by adding /D you can use date as a variable in many different ways, but this time we are not going to delete a bunch of files, instead we are going to add a bunch of drivers using pnputil.exe and forefiles.exe

Meet PNPUtil.exe

PNPUtil has a simple syntax and it is used to add drivers when then OS is running and here is the syntax:

Microsoft PnP Utility
Usage:
——
pnputil.exe [-f | -i] [ -? | -a | -d | -e ] <INF name>
Examples:
pnputil.exe -a a:\usbcam\USBCAM.INF      -> Add package specified by USBCAM.INF
pnputil.exe -a c:\drivers\*.inf          -> Add all packages in c:\drivers\
pnputil.exe -i -a a:\usbcam\USBCAM.INF   -> Add and install driver package
pnputil.exe -e                           -> Enumerate all 3rd party packages
pnputil.exe -d oem0.inf                  -> Delete package oem0.inf
pnputil.exe -f -d oem0.inf               -> Force delete package oem0.inf
pnputil.exe -?                           -> This usage screen

There are two things to note here

  1. The dev guy did believe that there are still floppy drives around (I’ll guess hi is as old as I am…)
  2. It does not have the traverse folder function that is needed since I don’t ever have all drivers in ONE folder

Time to for some action

By combining forfiles and pnputil we can create this neat little batch file

forfiles /p %1 /s /m *.inf /c “cmd /c pnputil -a @Path”

Save it as impdrv.cmd and run it using the following command

impdrv.cmd C:\Drivers

And it will search through the entire folder structure from C:\Drivers and add all drivers it can find on a running OS. And yes, you could use this in a task sequence to add drivers after the OS has been loaded if you need this for any reason…

/mike

Posted in Deployment, Drivers | Tagged: , , | 6 Comments »

Password or PIN Code “protect” MDT 2010 LiteTouch

Posted by Mikael Nystrom on January 26, 2011

Well, let me start with this:

This is NOT a secure solution, it is more of a-controlling-the-wizard-so-it-will-be-harder-do-something-really-bad-thing. This story started 2-3 weeks ago, a customer wanted to deploy windows 7 using LiteTouch. But they need a function to limit the selection of Task Sequences showed to the technician. Now that somewhat is a challenge but can be done. Here is how you can do this on your own.

image

Creating Selection Profiles for the Wizard

In MDT 2010 there is a variable called WizardSelectionProfile, using that we can create one selection profile for normal use and one for admin use. The only thing we need now is to feed the script with a parameter for what mode the wizard should run in. So:

Create two selection profiles, call them AllTaskSequences and ApprovedTaskSequences. Pretty much like this:

image

image

Update Customsettings.ini

Next thing is to make sure that your customsettings.ini file is correct and here is a sample of that:

[Settings]
Priority=WizardMode, Default
Properties=MyCustomProperty

[ADMIN]
WizardSelectionProfile=AllTaskSequences

[Default]
WizardSelectionProfile=ApprovedTaskSequences

Creating the PIN “application”

The quick and dirty way is to use an old friend of mine called AutoIT, it has been around for as long as I remember, I start using that for many years ago (NT4 something) for deployment and scripting, it has the ability to convert a scriptbased language into a executable file, the script language is very easy and its fast, so AutoIT here we go

Download AutoIT from http://www.autoitscript.com and install it, then open up the editor and create the following script:

$PIN = InputBox(“Security Check”, “Enter PIN for Admin Mode or wait.”, “”, “*”,300 , 160 , 362 , 200 , 10)
if $PIN = “1044″ Then
        RunWait(“wscript.exe X:\Deploy\Scripts\LiteTouch.wsf /WizardMode:ADMIN”)
        Exit
    Else
        RunWait(“wscript.exe X:\Deploy\Scripts\LiteTouch.wsf”)
        Exit
EndIf

The PIN code is 1044 (Yes, you can alter this…) and the timeout value before the message box will close is set to 10 seconds (you can see the last 10 in the first line). This means that it will stop and prompt for PIN, if you type the correct you will run with the /WizardMode switch set to ADMIN, if you enter the wrong PIN or wait (or hit cancel) it will run without that switch. You might want to feed MDT with other parameters to override the default value, as an example you might want to add /Debugcapture or /Debug on your “admin” command line.

Next up is to compile the script into an .exe file and you need two of them, one for 32bit and one for 64bit. You do that with this application (it will be installed when you install AutoIT). Now, open it up and create LTIRunx86.exe and then once more for LTIRunx64.exe. Be sure the you check the x64 checkbox when you create LTIRunx64.exe

image

Adding the LTIRun32.exe and LTIRunx64.exe files to the media

You need to new folders in your deployment root, open up explorer and browse to the root folder of your deployment share, like C:\Deploymentshare (In my case it is E:\MDTPrd)

and create ExtraX86 and ExtraX64. In those folders you create Windows and in Windows you create System32, like this:

image

Now, put LTIRunX86.exe in Extrax86\Windows\System32 and put LTIRunX64.exe in Extrax64\Windows\System32

Open up Deployment Workbench and right click on your deployment share and make sure you add those folders in the media like this:

image

image

Modifying the unattended.xml files for WinPE

Now, we need to modify the run command in WinPE and we do that by modifying the template files that is used the the media is created. The files are normally located in C:\Program Files\Microsoft Deployment Toolkit\Templates and are called Unattend_PE_x64.xml and Unattend_PE_x86.xml.

Open them and replace the the text that looks like this(Please, make a copy of them before you make the changes…):

wscript.exe X:\Deploy\Scripts\LiteTouch.wsf

with this:

X:\Windows\system32\LTIRunx86.exe

Update the media

Now, the next step is to update the boot media, right click on your deployment share an select update and wait until you have new boot media.

Testing:

Not that complicated, just boot on the media and if you made everything correct, this is how it will look like:

image

And if you enter the PIN 1044 it will look like this:

image

And if you typed in something else / waited / Canceled it will look like this:

image

Now, as you can imagine, you can do much more around this, as an example you could say that if you type in the correct PIN the Wizard will run with all the “skips” set to NO so that you will run the Wizard, but if you don’t type in the correct PIN or wait, it will a normal “silent” deployment.

Once more, from a security standpoint, this is NOT secure, but in many cases this will be just perfect.

Prohibit the F8 – Command Prompt

If you want you can also modify the winpeshl.ini file so that you cannot press F8 to open the CMD when running the Deployment, that is going to make it a bit harder to bypass the PIN.

The file is located in  C:\Program Files\Microsoft Deployment Toolkit\Templates and it is called winpeshl.ini and it looks like this:

[LaunchApps]
%SYSTEMROOT%\System32\bddrun.exe,/bootstrap

Modify it so that it looks like this:

[LaunchApps]
%SYSTEMROOT%\System32\bddrun.exe,/BootstrapNoSF8

Now, if you update your media and boot once more, it should not be possible to press F8 to get into the command prompt.

Mikael Nystrom
MVP Setup/Deployment

Posted in Deployment, TechEd | Tagged: , | 3 Comments »

 
Follow

Get every new post delivered to your Inbox.

Join 1,540 other followers