• Little Shop of Drivers

    I take all my drivers and put them in %DRIVERS_ROOT_PATH% (see batch code below) and the install images I want to mess with in %FILES_ROOT_PATH%.  One folder per driver, the script iterates through each of the folders, and runs imagex /inf for each folder.  As I'm testing, this makes it much easier to start over from scratch as I was trying to get different stuff to work.

    Note:  I wrote this for the x64 Install.wim which only has 4 images in it, the x86 has 7, but it will work for x86 WIM if the bold/italicized number below is changed.  Then all the images within the WIM will be updated.

    Note:  I have put some work into this since my initial posting, and determined updating, rather than reposting made the most sense.  This will now also automate adding packages to the image so long as the packages are in %PACKAGES_ROOT_PATH% (Ensure the Directory name is the same as the .CAB file from the package so that it knows which CAB to install).
    This has also been generalized to work with a WIM that has any number of images.  This script assumes that the WIM file is in root of the folder structure that the drivers and packages are in, but that can easily be changed using the SET statements below.

    @echo off
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ::Check Inputs
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    IF "%1"=="" (
    Echo Enter the directory root for the drivers and packages to add to the image.
    GOTO END
    )

    IF "%2"=="" (
    Echo Enter WIM file name.  This must be in the root of the
    GOTO END
    )

    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ::SET Variables
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    SET REFERENCENAME=%1
    SET MOUNTPOINT=D:\FOO\%REFERENCENAME%
    SET FILES_ROOT_PATH=D:\%REFERENCENAME%
    SET IMAGEFILE=%FILES_ROOT_PATH%\%2
    SET DRIVERS_ROOT_PATH=%FILES_ROOT_PATH%\Drivers
    SET PACKAGES_ROOT_PATH=%FILES_ROOT_PATH%\Packages
    SET LOGS_ROOT_PATH=%FILES_ROOT_PATH%\Logs
    SET WIN_AIK_INSTALL_PATH=C:\Program Files\Windows AIK\Toolsecho %WIN_AIK_INSTALL_PATH%
    Goto :End

    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ::Ensure needed directories exist and are ready to be used
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    if not exist %MOUNTPOINT% (md %MOUNTPOINT%) ELSE *"c:\Program Files\Windows AIK\Tools\x86\imagex.exe" /unmount %MOUNTPOINT%)
    if not exist %LOGS_ROOT_PATH% (md %LOGS_ROOT_PATH%) ELSE (del /s /q %LOGS_ROOT_PATH%>NUL)

    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ::Identify number of images in WIM and process each image
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    for /f "tokens=1,2 delims=:" %%i in ('imagex /info %IMAGEFILE%') do if "%%i"=="Image Count" SET IMAGE_COUNT=%%j
    Echo This WIM contains%IMAGE_COUNT% image(s).
    For /l %%i in (1,1,%IMAGE_COUNT%) do call :update %IMAGEFILE% %%i
    GOTO END

    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ::Process per image steps
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    :update
    Echo Updating %1 - Image #%2
    "c:\Program Files\Windows AIK\Tools\x86\imagex.exe" /mountrw "%1" %2 %MOUNTPOINT%
    for /f %%i in ('dir /ad /b %DRIVERS_ROOT_PATH%') do Call :InstallDriver %%i
    for /f %%i in ('dir /ad /b %PACKAGES_ROOT_PATH%') do Call :InstallPackage %%i %2
    "%WIN_AIK_INSTALL_PATH%\x86\imagex.exe" /unmount /commit %MOUNTPOINT%
    goto :EOF

    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ::Install a specified driver
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    :InstallDriver
    Echo Installing Drivers from %DRIVERS_ROOT_PATH%\%1
    "C:\Program Files\Windows AIK\Tools\PETools\peimg.exe" /verbose /inf=%DRIVERS_ROOT_PATH%\%1\*.inf /image=%MOUNTPOINT%>NUL
    IF ERRORLEVEL 1 ECho       ERROR:  Couldn't Install Driver "%1"
    goto :EOF

    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ::Install a specified package
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    f:InstallPackage
    Echo Installing Package %PACKAGES_ROOT_PATH%\%1
    "%WIN_AIK_INSTALL_PATH%\Servicing\pkgmgr" /n:"%PACKAGES_ROOT_PATH%\%1\%1.xml" /o:%MOUNTPOINT%;%MOUNTPOINT%\Windows /s:%TEMP% /l:%LOGS_ROOT_PATH%\%2-%1>NUL
    IF ERRORLEVEL 1 ECho       ERROR:  Couldn't Install Package "%1"
    :: /m:"%PACKAGES_ROOT_PATH%\%1\%1.cab"
    GOTO :EOF

    :END
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ::Clean up variables
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    SET IMAGE_COUNT=
    SET REFERENCENAME=
    SET IMAGEFILE=
    SET MOUNTPOINT=
    SET FILES_ROOT_PATH=
    SET DRIVERS_ROOT_PATH=
    SET PACKAGES_ROOT_PATH=
    SET WIN_AIK_INSTALL_PATH=

  • Idiosyncratic Windows Deployment Server Vista Setup Options

    I found getting both of the below topics to work the way I wanted in Windows Deployment Server (WDS) to be surprisingly tricky and fraught with unexpected results that took me quite a while to figure out.  After all, one has to run through some large portion of the OS install before finding out that it fails.  Multiple mistakes = multiple OS Installs, which drag out this learning curve rather significantly.  I hope to save people some of these reboots and time lost by sharing what I learned.

    Computer Naming and Domain Join (applies to Windows 2008 Server as well):

    I really like eliminating the majority of repetitive trivial tasks.  When managing desktops in an enterprise, something as simple as A) logon and change the computer name, B) reboot, C) join the computer to the domain, and D) reboot, which only takes 10 minutes can have a significant impact.  Even on a deployment as small as 5000 computers, this can add up to a significant cost.  5000 systems * 10 minutes per system / 60 minutes per hour = ~833 man hours, just renaming the computer and joining it to the domain.  SYSPREP and the mini setup do a lot to help reduce this impact, but that still means that some administrator has to revisit the computer after the OS is deployed to the box and before a user can work.  This seems an incredibly inefficient use of labor to me.

    As a result of this, I like to have the system join the domain during the install process.  Unfortunately, this was a little more challenging in the Vista automation than one would suspect, and there are several postings on this throughout various forums.  Unfortunately, the bits of the information are scattered about in a fashion that doesn't really help to put the full picture together.  The key items I learned are:

    • %MACHINENAME% will pick up the computer name from AD if the netbootGUID attribute is populated for the system UUID as expected (See earlier posting regards this).
    • %MACHINENAME% will function as "*" if the above case is not true.
    • "*" will give the computer a random name (as documented).  However, even if the "Microsoft-Windows-UnattendedJoin" element of the XML is populated correctly, the computer will not be joined to the domain when the system has to generate a name.

    Within the WDS/AIK space, this forces an administrator to pre-stage each and every machine.  Of course, joining the domain can be managed outside of the WDS/AIK space either by manual methods or scripting (i.e. use netdom and batch files) to overcome this limitation, but I wanted to avoid using “Autologon” functionality and writing “code” in order to accomplish something that could be taken care of during the install process.  I may, at some point, end up working on this in order to address the scenarios where it won’t fail above, but I have my learning curve on the “Microsoft Deployment Toolkit” to go through first to see if it provides the level of functionality I desire.

    Note:  At some point we seem to have updated Windows so that the computer can be renamed and joined to a domain in one shot, though it seems that many people either don't know or don't use this.  It is a little tricky too; the computer name must be changed first, then the domain membership.  If this is done in the reverse order, it won’t rename the computer account in AD that was created when the domain was joined.

    Required Unattend.XML settings:

    ·         Specialize Pass

    o    Microsoft-Windows-Shell-Setup\ComputerName = %MACHINENAME%

    o    Microsoft-Windows-UnattendedJoin\Identification\JoinDomain = <Enter Domain Name>

    o    Microsoft-Windows-UnattendedJoin\Identification\MachineObjectOU = <Enter OU>

    o    Microsoft-Windows-UnattendedJoin\Identification\Credentials\Domain = <Enter Domain Name>

    o    Microsoft-Windows-UnattendedJoin\Identification\Credentials\Password = <Enter Domain Name>

    §  Note:  This password will not be encrypted when the following setting is enabled.  Hide Sensitive Data in an Answer File

    o    Microsoft-Windows-UnattendedJoin\Identification\Credentials\UserName = <Enter Account Name>

    Eliminating the Mid-Install Wizard (Out-Of-the-Box-Experience):

    Also, within my continued endeavors to make installs as low touch as possible, I feel having the computer pause for human intervention somewhere in the middle of installing the OS undermines much of the other automation.  Thus, a wizard that pops up mid-install to ask what language it is desired to run the computer in and to create a local account is something I would seek to eliminate.

    Though the dialog the wizard presents a reasonable question (locality settings) that the average user could handle, it wouldn't bother me so much if there wasn't another delay that prevented the system from being used immediately afterwards (the computer goes through the performance tests to determine the Windows Performance Index).  Having a user or administrator sit through that progress bar is also a productivity impact, again the reason I seek to eliminate that interim step.  Also, since the workstation is joined to a domain, I do not want to create additional and unused accounts for no good reason in order to make the box just disappear from the user experience (though I would have settled with doing so and saw some suggestions on forums to this end).

    Normally it will show the wizard if regional settings and creation of a local account are not both configured.  As I stated above, I wanted to avoid creating a user account, thus as a work around, I found that setting Microsoft-Windows-Shell-Setup/OOBE/SkipMachineOOBE to “true” in the oobeSystem pass bypassed this mid-install wizard.  The nice part is that this did allow me to set the settings I desired (i.e. regional settings) and bypass the others (local user account).

    As a warning, the help context states that SkipMachineOOBE is deprecated and shouldn’t be used, which may cause additional issues as the install process may be changed in future versions of Windows, but it currently works for my needs.  Also, heed the warning in the article, and setting SkipMachineOOBE to true may leave the machine in an unusable state, see the next step.

    Required Unattend.XML settings:

    ·         oobeSystem

    o    Microsoft-Windows-Shell-Setup\OOBE\SkipMachineOOBE = True

     

    Suggested Unattend.XML settings:

    ·         oobeSystem

    o    Microsoft-Windows-International-Core\InputLocale = <Enter SelectedLocale>

    o    Microsoft-Windows-International-Core\SystemLocale = <Enter SelectedLocale>

    o    Microsoft-Windows-International-Core\UILanguage = <Enter SelectedLocale>

    o    Microsoft-Windows-International-Core\UserLocale = <Enter SelectedLocale>

    o    Microsoft-Windows-Shell-Setup\OOBE\HideEULAPage = true

    o    Microsoft-Windows-Shell-Setup\OOBE\ProtectYourPC  = 3

    o    Microsoft-Windows-Shell-Setup\OOBE\NetworkLocation = Work

    o    Microsoft-Windows-Shell-Setup\OOBE\SkipMachineOOBE = True

    o    Microsoft-Windows-Shell-Setup\UserAccounts\AdministratorPassword = <Enter a Password>

    Administrator account warning:

    By default, on Vista the administrator account is disabled.  Thus, if no local account is created and the computer does not get properly joined to the domain, the machine will appear to be useless.  This is not as bad as it seems, by following the guidance on Windows Vista Security : Built-in Administrator Account Disabled the computer can still be joined to the domain.  In short, boot into “Safe Mode with Networking” and join the computer to the domain.

    Note:  I found in the documentation for the unattended install settings where it states that the administrator account can be enabled via Microsoft-Windows-Shell-Setup\AutoLogon\Username.  I didn’t have any luck getting this to work on (using Vista SP1 was the only version I tried), but it doesn't matter since there is another workaround.

    On Windows 2008 Server, if the domain join fails the administrator is prompted to set the Administrator account password, so this is not a concern.

  • Trials and Tribulations of Learning the Vista Automated Installation Functionality

    I've pretty much come to the end of my initial learning curve on how to automate Vista installations using the AIK.  There is some great documentation out there on how to execute the specific tasks necessary to add drivers and packages to the image.  However, there are some gaps on how to tie it all together.  It's the subtlties that really hurt my learning curve (and installing the OS over and over and over... to test the effect of each change) that don't seem to be well documented anywhere.  I'm hoping to share at least the trickiest items that I encountered in order to save someone else many hours of learning.  As a note, I have not tried to use what used to be called "Business Desktop Deployment" (BDD) and is now "Microsoft Deployment Toolkit" (MDT) and some of the challenges I have below may be addressed in that.

    I'm doing my deployments via Windows Deployment Server (WDS), the replacement for Remote Installation Server (RIS).  In RIS, I never really used the RIPREP functionality because I found the administrative burden of creating a new RIPREP image for each hardware platform and every time I needed to deploy new software excessive.  Though RIPREP could push the complete OS and applications much faster than going through the install process, I just found it easier to deal with one scripted install I could add drivers for all the hardware to, and deploy applications via SMS. 

    First off, for anyone who has used RIS and the "unattend.txt" methods of installs in the past, there are a couple of features I really miss or have not yet figured out how to do in WDS:

    • What I miss most from RIS: If it did not find a computer object with the netbootGuid attribute populated with the machine's UUID, it would prompt for a computer name during the initial startup screens. This meant I did not have to pre-stage the system before the user or SA installed it, but if it ever had to be re-installed it would keep the same name and OU location in the hierarchy (very useful in DR scenarios) since RIS would populate the netbootGUID with the UUID upon creation (WDS has an approval process scenario that requires manual intervention, but doesn't have a "auto approval" mode). Additionally, since computer name is really the only unique piece of information needed for each and every system I really liked the fact that I could deliver a 4 step install process to users and administrators and leave them with a fully provisioned system:
      1. Press F12 on boot
      2. Log in
      3. Enter computer name
      4. Go do something else for several hours
    • In RIS, if the security on the images was managed in such that a user was only allowed to see one image, RIS automatically selected that image and installed it.  Regardless as whether or not only one image is available, WDS prompts the end user to select an image.
    • In the "unattend.txt" install automation method, the disk configuration options were tied to the image being deployed.  I really liked this feature since I could have both a server OS image and a client OS image on the deployment server and allow the server operator to create the partitions they wanted according to their needs while automating the partitioning of the client system disks.  Now I think I need two WDS servers to provide the same level of functionality.  I don't think this is WDS limitation, but more a limitation related to the 2 stage install process Vista uses.  I still miss this functionality, regardless of where it falls.
    • The ability to have one OS build/WIM and multiple configuration files if the only difference, for example, is that one department doesn't want their users to have certain windows features installed by default (think the default Windows games).
    • The RIS UI loaded very fast, this cut down the time an administrator was sitting idle on a system rebuild, cutting operational costs.

    What I really like about the new tools:

    • Multi-cast - large deployments = nuff said.
    • The administrative tools are much better.
    • The tools and documentation for generating the scripted installs made life a lot easier than the initial learning curve I recall going through with unattend.txt.
    • Driver management.  Run a couple of command lines and the image is updated. There is no longer a need to have to manually update a text file (typos... grrr) and build out a folder structure for every image/driver set managed.  Adding in new drivers to the boot image is much easier than in RIS and uses the same methodology as the install images, which is a very nice win.  And no more drivers all using oemsetup.inf tripping over each other in the boot image and fighting with that.
    • Drive partitioning tools are much better.  Even if the UI can't provide the functionality needed, the ability to drop to a command line and use diskpart for the fine grained configuration desired is awesome.