September, 2009

  • Michael Niehaus' Windows and Office deployment ramblings

    Sorting the contents of an MDT 2010 deployment share

    • 6 Comments

    Quite some time ago (well over a year ago – time flies) I posted a script at http://blogs.technet.com/mniehaus/archive/2008/06/20/sorting-mdt-s-lists-of-applications-task-sequences-patches-etc.aspx that described how to sort the content in an MDT 2008 distribution share.  Now that MDT 2010 has been released, it’s important to first point out that this script will not work with MDT 2010 for a variety of reasons.

    Since MDT 2010 has the same behavior, showing the items in the order that they were added in Workbench, that means you are now in need of a new script.  Fortunately, this isn’t too hard to do using a PowerShell script (and a little bit of knowledge of the underlying workings of the MDT 2010 PowerShell provider).  Here’s the script (also attached in a Zip file for those interested in a simpler download):

    # ***************************************************************************
    #
    # File:      DeploymentShareSorter.ps1
    #
    # Author:    Michael Niehaus
    #
    # Purpose:   This PowerShell script will sort the existing files and folders
    #            in a deployment share.
    #
    #            Note that there should be no one actively adding items to the
    #            deployment share while running this script, as some of the
    #            operations performed could cause these items to be lost.
    #
    #            This requires PowerShell 2.0 CTP3 or later.
    #
    # Usage:     Copy this file to an appropriate location.  Edit the file to
    #            change the $rootPath variable below, pointing to your
    #            deployment share. (This can be a local path or a UNC path.)
    #
    # ------------- DISCLAIMER --------------------------------------------------
    # This script code is provided as is with no guarantee or warranty concerning
    # the usability or impact on systems and may be used, distributed, and
    # modified in any way provided the parties agree and acknowledge the
    # Microsoft or Microsoft Partners have neither accountability or
    # responsibility for results produced by use of this script.
    #
    # Microsoft will not provide any support through any means.
    # ------------- DISCLAIMER --------------------------------------------------
    #
    # ***************************************************************************

    # Constants

    $rootPath = "\\mniehaus-t61p-7\DeploymentShare$"

    # Conect to the deployment share

    Add-PSSnapIn Microsoft.BDD.PSSnapIn -ErrorAction SilentlyContinue
    New-PSDrive -Name DeploymentPointSorter -PSProvider MDTProvider -Root "$rootPath"

    # Functions

    function Sort-MDTFolderItems {

        [CmdletBinding()]
        PARAM
        (
            [Parameter(Position=1, ValueFromPipeline=$true)] $folder
        )
        Process
        {
            Write-Host "Sort-MDTFolderItems: Processing folder $($folder.Name)"
            # Initialize the sorted array
            $sorted = @()
            # Get the list of immediate subfolders, sort by name, and add to the array
            $folderPath = $folder.PSPath.Substring($folder.PSPath.IndexOf("::")+2)
            Get-ChildItem $folderPath | ? {-not $_.PSIsContainer} | Sort Name | % { $sorted += $_.Guid }
            # If there were any items found, process them.
            if ($sorted.Count -gt 0)
            {
                # See if the list is already sorted.  If it is, we don't need to make any updates.
                $compareResults = compare-object $sorted $folder.Item("Member") -SyncWindow 0
                if ($compareResults -eq $null)
                {
                    Write-Host "Already sorted."
                }
                else
                {
                    Write-Host "Saving sorted list."
                    # First remove all members of the list because the PowerShell provider will "optimize" the change by seeing there
                    # were no items added or removed.  Then put all the members back in the sorted order.  (This is actually quite
                    # dangerous to do as it could orphan the items, so we need to immediately put them back.)
                    $folder.Item("Member") = @()
                    $folder.Item("Member") = $sorted
                }
            }
        }
    }

    function Sort-MDTFolderSubfolders {

        [CmdletBinding()]
        PARAM
        (
            [Parameter(Position=1, ValueFromPipeline=$true)] $folder
        )
        Process
        {
            Write-Host "Sort-MDTFolderSubfolders: Processing folder $($folder.Name)"
            # Initialize the arrays
            $sorted = @()
            $unsorted = @()
            # Get the list of immediate child folders, sorted and unsorted
            $folderPath = $folder.PSPath.Substring($folder.PSPath.IndexOf("::")+2)
            $folderList = Get-ChildItem $folderPath | ? {$_.PSIsContainer}
            $folderList | % { $unsorted += $_.Name }
            $folderList | Sort Name | % { $sorted += $_.Name }
            # If there were any subfolders found, process them.
            if ($sorted.Count -gt 0)
            {
                # See if the list is already sorted.  If it is, we don't need to make any updates.
                $compareResults = compare-object $sorted $unsorted -SyncWindow 0
                if ($compareResults -eq $null)
                {
                    Write-Host "Already sorted."
                }
                else
                {
                    Write-Host "Sorting folders."
                    # Create the temporary folder
                    $null = New-Item "$folderPath\__TEMP__" -ItemType Folder
                    # Move the folders into the temporary folder
                    $sorted | % { Move-Item "$folderPath\$_" "$folderPath\__TEMP__" }
                    # Move the folders back
                    $sorted | % { Move-Item "$folderPath\__TEMP__\$_" "$folderPath" }

                    # Remove the temporary folder
                    Remove-Item "$folderPath\__TEMP__"
                }
            }
        }
    }

    # Enumerate the folders and call the functions above to process each one

    Get-ChildItem DeploymentPointSorter: -Recurse | ? {$_.PSIsContainer} | Sort-MDTFolderItems

    Get-ChildItem DeploymentPointSorter: -Recurse | ? {$_.PSIsContainer} | Sort-MDTFolderSubfolders

    Save this as “DeploymentShareSorter.ps1” and you are all set.  A few notes on this script:

    • This script requires PowerShell 2.0 – that’s all I use any more.
    • As provided, this will only sort one deployment share at a time, but it would be fairly simple to do multiple: just duplicate the final two lines, passing each path (e.g. DS001:) that you want to sort.
    • There is one operation in this script that is somewhat dangerous:
    •                  $folder.Item("Member") = @()
                      $folder.Item("Member") = $sorted

      I would suggest avoiding adding other items (drivers, applications, etc.) to the deployment share while this script is running, because those two lines could possibly cause those new items to be lost.  They first remove all items from the current folder and then add them back again (working around an optimization in the PowerShell provider code that tries to be smart about modified items – if you took out the first line the second line would have absolutely no effect).
    • This could take a while the first time through, but it should get faster after that first time because most of the content will already be sorted.
    • Don’t use a folder name of “__TEMP__” because that’s used by the script for sorting purposes.
    • Remember to edit the value assigned to the $rootPath variable.
    • If you want to schedule this script to run using the Windows Task Scheduler, just specify an action command line of “powershell.exe -File C:\Scripts\DeploymentShareSorter.ps1” (assuming the script was saved in C:\Scripts).
  • Michael Niehaus' Windows and Office deployment ramblings

    Setting up an image factory, part 1: Using MDT 2010 Lite Touch with SCVMM 2008 R2

    • 5 Comments

    Yes, I’ve been promising this blog posting for quite some time.  And I’ve been working on this for quite some time, but kept getting distracted either by new releases (Windows 7, Windows Server 2008 R2, MDT 2010, SCVMM 2008 R2, etc.) or by the addition of new features to the PowerShell scripts that I’ve been using.  But I’m determined to get this first part finished today.  Shame sometime can be a motivator :-)

    First you need some additional background information.  I did a presentation at the Microsoft Management Summit, TechEd US, and TechEd Australia where I talked about how to use MDT 2010, ConfigMgr 2007, and SCVMM together for two main purposes:

    • Creating an image factory. 
      • Perform unattended installation and configuration of operating systems (including patches, applications, etc.)
      • Sysprep and capture images for distribution/cloning
      • Create WIM files for deployment to physical hardware, VHD files for use with Virtual Machine Manager and Hyper-V
    • Virtual Machine Customization
      • Rather than having lots of special-purpose VHDs, have a smaller number with the ability to apply specific roles or configuration at the time of deployment

    So this posting is covering the first part, using MDT 2010 Lite Touch together with SCVMM to create an image factory.  Here’s more of a logical picture of what I am talking about:

    image

     

     

    So imagine that you have created a deployment share in MDT 2010 Deployment Workbench, imported your operating systems and all the other required files, and created multiple task sequences to build your reference images.  Now you want a quick and easy way to run all of those task sequences, without having a pile of hardware (so virtual machines are good) and without needing to manually initiate the process on each machine (automating the wizard).  That’s where the “image factory” comes in.

    To implement this, I created a set of PowerShell scripts to initiate the step-by-step process above.  The scripts and their purpose:

    • MDTImageFactory.ps1.  This is the main PowerShell script that drives the whole process (although the bulk of the logic is in the other scripts).
    • MDTDB.psm1.  This is a PowerShell module that is used to manipulate the MDT database (more on that later).
    • ImageFactory.psm1.  This is a PowerShell module that handles the interaction with SCVMM 2008 R2.

    These scripts need to know some details from your environment.  Rather than hard-coding that information in the scripts themselves, this information is stored in a separate XML file named “MDTImageFactorySettings.xml.”  This file contains the following settings:

    • DeploymentShare.  This specifies the path to the MDT 2010 deployment share containing the task sequences that should be executed.
    • VMMServer.  This specifies the name of the SCVMM server.
    • VMMLibrary.  This specifies the name of the SCVMM library on the specified SCVMM server.
    • HyperVHost.  All virtual machines will be created on this server, which is being managed by the SCVMM server.  (It could be the SCVMM server, if that server is also running Hyper-V, or it could be a different machine.)
    • HyperVHostNetwork.  The Hyper-V host may have multiple networks defined; this specifies the name of the network that should be used when creating each virtual machine.  This network must have access to the specified deployment share.
    • HardwareProfile.  A hardware profile specifies the settings that should be used when creating a new virtual machine.  This specifies things like the amount of RAM to allocate to the machine (1GB is suggested), the network adapter type (a legacy adapter is recommended since the drivers are available in most OSes), and other hardware settings.
    • VHD.  This specifies the template VHD that should be used when creating the virtual machine.  (SCVMM provides two templates initially, a small one and a large one, but since these dynamically grow it makes sense to always use the “Blank Disk - Large” template.)
    • MaximumRunning.  Your Hyper-V host might not have the capacity to run all the task sequences (one per VM) at one time, so this specifies a throttling value: the script will ensure that only this number of VMs is activate at one time.  (For example, if your Hyper-V server has 8GB of RAM with no VMs running, you might choose to run 6-7 VMs at one time, so specify 6 or 7 as the value to use.)
    • TaskSequenceFolder.  This specifies the folder in Deployment Workbench containing the task sequences that should be executed.  Normally, all task sequences are selected by specifying “MDT:\Task Sequences” but if you wanted to process only the task sequences in a subfolder you could change this to something like “MDT:\Task Sequences\My Subfolder”.  (Only enabled task sequences that deployment an operating system will be selected, so if you want to skip one while you are working on it just uncheck the “Enabled” checkbox in the properties for that task sequence.)
    • UseDelegation.  Setting this to “True” enables an optimization in the process: the virtual machines can be configured to use a ISO on the SCVMM library share, instead of copying that ISO to the Hyper-V host using BITS.  This is optional, but if you want to enable it be sure to review the requirements for doing this at http://technet.microsoft.com/en-us/library/ee340124.aspx.

    So what is required to set this up in your environment?  First, make sure that your environment is functional, as these scripts won’t magically fix things:

    • Make sure SCVMM 2008 R2 can create and operate VMs on the Hyper-V host.
    • Create your MDT 2010 deployment share and task sequences and make sure they run fine on a Hyper-V VM when manually started through the Deployment Wizard.
    • Make sure PowerShell v2 is installed on the server where you expect to run these scripts.  (SCVMM 2008 R2 works with PowerShell v2; SCVMM 2008 technically only supports PowerShell v1.)
    • If you want to run the scripts on a machine that isn’t the SCVMM server, make sure that the SCVMM console is installed so that the SCVMM PowerShell cmdlets are available.
    • If you want to run the scripts on a machine that isn’t the MDT 2010 server, install MDT 2010 so that the MDT PowerShell cmdlets are available.

    Once that’s done, you can perform the following setup steps:

    1. Set up the MDT database, as this is required for the image factory to work.  There are two steps involved: running the wizard to create the database, and then running another wizard to configure the query rules in CustomSettings.ini.
    2. Make sure that x86 and x64 are enabled for the MDT deployment share.  (The script assumes both ISOs will be available.  The name of the ISO doesn’t matter as the PowerShell script will get the name configured in the deployment share properties.)
    3. Create a SCVMM hardware profile.  This should specify a reasonable amount of RAM (I typically use 1GB) and to use a legacy NIC (as the driver is available in most OSes); all VMs will be created using this hardware profile.
    4. Configure Bootstrap.ini on the deployment point properties so that no user interaction is required to start the deployment process:

      SkipBDDWelcome=YES
      UserID=Administrator
      UserDomain=MTN-DEMO
      UserPassword=password
    5. Make sure the SCVMM library server is in the Internet Explorer “Local intranet” zone (add \\server-name), or make sure the machine you are running the PowerShell scripts on has the UNCAsInternet registry setting configured.  If you see messages like “While scripts from the internet can be useful, this script can potentially harm your computer. Do you want to run <script>?” when trying to run the PowerShell scripts, you’ll understand why you need to worry about this.  (See http://www.leeholmes.com/blog/PowerShellExecutionPoliciesInStandardImages.aspx for some background.)
    6. Create a folder called “PowerShell scripts” in the SCVMM library share.
    7. Copy the attached scripts (after extracting them from the CAB file) into the “PowerShell scripts” folder on the deployment share.
    8. Edit the MDTImageFactorySettings.xml file to specify your server names and settings as described above.
    9. In the SCVMM console, refresh the library share to see the PowerShell scripts that were added.
    10. Right click on the MDTImageFactory.ps1 script to run the process.

    You should then see that connections are made to the MDT deployment share, the MDT database (the settings for the database are retrieved from the deployment share), and the SCVMM server.  A virtual machine will be created for each enabled OS deployment task sequence in the specified folder (and subfolders, recursively), and then the specified number of VMs will be started.  As the first VMs complete they will shut down (as long as the task sequence finishes successfully) and new ones will be started, until all task sequences are finished. 

    As part of the VM creation process, new MDT database entries are created specifying the computer settings, associated to the MAC address of the network adapter for that VM.  These settings include:

    SkipWizard=YES
    SkipFinalSummary=YES
    TaskSequenceID=<the ID of the task sequence to run>
    AdminPassword=P@ssword
    DoCapture=YES
    ComputerBackupLocation=<deployment share UNC>\Captures
    BackupFile=<task sequence ID>.wim
    FinishAction=SHUTDOWN

    So that specifies to skip all the wizards, run a specific task sequence, use a constant local admin password, capture an image to the deployment share using the task sequence ID to name the WIM file, and to shut down the VM when the whole process is complete.

    Here’s what you might expect to see while the VMs are being created:

    image

    and while they are running:

    image

    That display will keep repeating until all task sequences are complete and the virtual machines shut down.

    As the virtual machines complete, there will be two “outputs”:  the WIM file that is written to the “Captures” directory of the deployment share, and the VHD file that is still attached to the VM.  You can turn that VHD into an SCVMM template and use that when creating new VMs.  If you do that, be sure to disconnect the ISO file from the virtual DVD drive and to configure the NIC to specify a dynamic MAC address.  (More on that topic in a future blog post when we talk about virtual machine customization.)

    That’s pretty much the whole process, but it is worth mentioning a few things in closing:

    • The script could be enhanced to add the logic to detach the ISO file and reconfigure the NIC once the VM shuts down, but if I kept delaying while adding more script features this blog posting would never get completed :-)
    • You can monitor the VMs inside the SCVMM console – just click on each one and see the thumbnail of the server display.  This lets you quickly scan through each of the VMs looking for ones that had errors or were just taking too long.
    • These scripts were only tested with a Hyper-V host.  They should work with VMware ESX and Virtual Server, but might require some simple customization to make everything work.  Proceed at your own risk, let me know if you get it to work.

    The “Part 2” blog posting will describe how to perform the same scenario using System Center Configuration Manager 2007 (with or without MDT), but it will take me some time to recover from this posting before I get to that one.

    If I messed up the instructions or left something out, please let me know via e-mail, mniehaus@microsoft.com.  The scripts attached to this blog entry are provided as-is, and are not supported by Microsoft.  See the scripts for the full disclaimer.

  • Michael Niehaus' Windows and Office deployment ramblings

    Automatically update MDT 2010 boot images in WDS

    • 4 Comments

    You’ve probably gone through this cycle if you are using WDS to PXE boot computers to start bare metal Lite Touch deployments:

    • Import new drivers or change bootstrap.ini.
    • “Update deployment share” to generate new WIMs.
    • Import new WIMs into WDS.

    Fortunately, with the new “update” process in MDT 2010, described in more detail at http://blogs.technet.com/mniehaus/archive/2009/07/10/mdt-2010-new-feature-17-customizable-boot-image-process.aspx, it’s pretty simple to add a script to automate this process.  First, the script:

    Option Explicit

    Dim oShell, oEnv

    Set oShell = CreateObject("WScript.Shell")
    Set oEnv = oShell.Environment("PROCESS")

    If oEnv("STAGE") = "ISO" then

        Dim sCmd, rc

        sCmd = "WDSUTIL /Replace-Image /Image:""Lite Touch Windows PE (" & oEnv("PLATFORM") & ")"" /ImageType:Boot /Architecture:" & oEnv("PLATFORM") & " /ReplacementImage /ImageFile:""" & oEnv("CONTENT") & "\Sources\Boot.wim"""
        WScript.Echo "About to run command: " & sCmd

        rc = oShell.Run(sCmd, 0, true)
        WScript.Echo "WDSUTIL rc = " & CStr(rc)

        WScript.Quit 1

    End if

    You’ll need to update the image name in the string above if you’ve changed it from the default of “Lite Touch Windows PE (x86)” and “Lite Touch Windows PE (x64)” since the script doesn’t know what you’ve changed the values to.  Save the edited script as something like “C:\Scripts\UpdateExit.vbs”.  Then, edit the “C:\Program Files\Microsoft Deployment Toolkit\Templates\LiteTouchPE.xml” file so that these lines:

    <!-- Exits -->
    <Exits>
      <Exit>cscript.exe "%INSTALLDIR%\Samples\UpdateExit.vbs"</Exit>
    </Exits>

    Instead look like this:

    <!-- Exits -->
    <Exits>
      <Exit>cscript.exe "%INSTALLDIR%\Samples\UpdateExit.vbs"</Exit>
      <Exit>cscript.exe "C:\Scripts\UpdateExit.vbs"</Exit>
    </Exits>

    Then make a change that requires re-generating the WIM and ISOs, e.g. change something in bootstrap.ini.  You’ll see in the “Update Deployment Share” output the generated WDSUTIL command that updates the boot image in WDS.  If WDS is located on a different server, you’ll need to update the command in the script above to add “/Server:WDSServerName” to the command.  (WDSUTIL must also be available on the machine, so you may need to install the RSAT WDS tools.)

    Extra credit for someone who can convert this into a PowerShell script and look up the right boot image name :-)

  • Michael Niehaus' Windows and Office deployment ramblings

    Running PowerShell scripts as part of a task sequence

    • 4 Comments

    Both MDT 2010 Lite Touch and ConfigMgr 2007 run the same task sequencer.  This task sequencer can run any command that you want, just specify the command line to use.  That’s the simple part – the harder part is figuring out what this command line should do.  Often the command, a VBScript or PowerShell script, needs to get information from the task sequence itself, accessing variables in the task sequence environment.  Remember, these task sequence variables aren’t environment variables – they are distinctly separate, so you can’t use the PowerShell “Env:” drive.

    If you are using MDT, building a VBScript that includes the ZTIUtility.vbs script makes accessing task sequence variables pretty simple, as you can then reference something like this in your script:

    sValue = oEnvironment.Item("MYVAR")

    But PowerShell is now the rage – what if you wanted to do the same thing using PowerShell?  Fortunately that’s not too difficult either.  Here’s a simple example that gets the value of a particular variable:

    $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
    Write-Host $tsenv.Value("_SMSTSLogPath")

    You would then need to set up a task sequence step that ran that PowerShell script.  In the Lite Touch case, I would suggest saving the file in the “Scripts” directory on the deployment share, for example as “Test.ps1”.  You could then create a “Run command line” step in the task sequence that executes this command:

    PowerShell.exe -File "%SCRIPTROOT%\Test.ps1"

    If you were using MDT 2010 integrated with ConfigMgr, the same thing would work, but you would need to add the file to the “Scripts” directory of the MDT toolkit package.  Alternatively, you could create a new software distribution package containing the PowerShell script, specify to use that package on the “Run command line” step of the ConfigMgr task sequence, and then specify a command line that assumes the script is in the working directory:

    PowerShell.exe -File "%SCRIPTROOT%\Test.ps1"

    If you want to change a task sequence variable (or set a new one), you use the same “Value” method:

    $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
    $tsenv.Value("MyVar") = "My Value"

    Maybe you want to do something a little more involved, like create an transcript (log) of the execution of your script.  You can use the _SMSTSLogPath variable to determine where to place the file:

    # Determine where to do the logging
    $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
    $logPath = $tsenv.Value("_SMSTSLogPath")
    $logFile = "$logPath\$($myInvocation.MyCommand).log"

    # Start the logging
    Start-Transcript $logFile

    # Insert your real logic here
    Write-Host "We are logging to $logFile"

    # Stop logging
    Stop-Transcript

    Another useful example is a script that logs the values of all task sequence variables:

    # Determine where to do the logging
    $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
    $logPath = $tsenv.Value("_SMSTSLogPath")
    $logFile = "$logPath\$($myInvocation.MyCommand).log"

    # Start the logging
    Start-Transcript $logFile

    # Write all the variables and their values
    $tsenv.GetVariables() | % { Write-Host "$_ = $($tsenv.Value($_))" }

    # Stop logging
    Stop-Transcript

    Or you could use the same technique to turn all the task sequence variables into PowerShell variables:

    # Determine where to do the logging
    $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment
    $logPath = $tsenv.Value("_SMSTSLogPath")
    $logFile = "$logPath\$($myInvocation.MyCommand).log"

    # Start the logging
    Start-Transcript $logFile

    # Convert the task sequence variables into PowerShell variables
    $tsenv.GetVariables() | % { Set-Variable -Name "$_" -Value "$($tsenv.Value($_))" }

    # Write out a specific variable value
    Write-Host $_SMSTSMDataPath

    # Get all the variables
    Dir Variable:

    # Stop logging
    Stop-Transcript

    (Take out all the extra stuff and this could be reduced to two lines, the one that creates the COM object and the one that calls GetVariables.)

    A few other notes worth mentioning:

    • You need to make sure scripts are enabled before trying to run these via a task sequence.  In the case of MDT Lite Touch, the scripts will typically be run from a network UNC path.  For ConfigMgr, a local path (for download-on-demand or download-and-execute) or a network path (for “run from DP”) will be used.  You can include a step in the task sequence to set the needed execution policy, e.g. “powershell.exe -Command { Set-ExecutionPolicy Unrestricted }”, or configure the same via Group Policy.
    • You may want to add the “-noprofile” parameter to the PowerShell.exe command line as the profile commands may cause issues with your script.
    • The Microsoft.SMS.TSEnvironment COM object is only available while the task sequence is running, so you need to test your script inside of a task sequence.  (This can be a case where the “convert task sequence environment to PowerShell variables could come in handy: do you testing with hard-coded variables, remove the values before deploying the script.)
    • The task sequencer only registers the matching platform of Microsoft.SMS.TSEnvironment.  For example, when the x86 task sequencer is running the x86 Microsoft.SMS.TSEnvironment will be available but the x64 version will not.  For an x64 task sequence, only the x64 Microsoft.SMS.TSEnvironment will be available.
    • ConfigMgr will run the x86 version of the task sequencer even on x64 operating systems, so the x86 version of PowerShell will normally be run in this case.  (For MDT Lite Touch, the x86 version of the task sequencer is used on x86 OSes and the x64 version is used on x64 OSes.)  Make sure you are aware of which platform is running, as that might affect your PowerShell script execution.  (Note that x64 processes run via a ConfigMgr task sequence, done by disabling file system redirection for the task sequence step or by specifying “sysnative” in the path, won’t be able to create the Microsoft.SMS.TSEnvironment object because of the previous note.)
    • If you want to return an error, you should insert an “exit” statement in your PowerShell script, e.g. “exit 1234”.  This will cause PowerShell.exe to return that return code to the task sequencer.
  • Michael Niehaus' Windows and Office deployment ramblings

    Automating Long-Running MDT 2010 Tasks

    • 1 Comments

    There are a few operations in the MDT 2010 Deployment Workbench that can take a while.  We’ve done our best to optimize those processes, but in many cases they will still take a good amount of time to complete.  While you could certainly start an operation in one Deployment Workbench and then open another Deployment Workbench so that you can continue working, it would be even better to schedule these long-running tasks so that they run automatically, ideally when you are sleeping and when there aren’t any active Lite Touch deployments going on.

    Fortunately, since the Deployment Workbench is now PowerShell-based you can now write simple PowerShell scripts to perform these activities.  The following three samples show you how to do it.

    Sample #1:  Updating a deployment share

    The “Update Deployment Share” process takes the latest changes made to the deployment share (bootstrap.ini updates, new drivers, new patches, script changes, etc.) and updates the Windows PE boot images (WIM files and ISOs if requested).  Depending on the extent of the changes, this process might take 15-30 minutes (depending on the I/O performance of the machine – it’s definitely an I/O-bound process, especially for the WIM and DISM actions performed by this process).  The following script will automate the process:

    Start-Transcript C:\Scripts\UpdateDeploymentShare.log

    # Connect to the deployment share
    Add-PSSnapIn Microsoft.BDD.PSSnapIn
    New-PSDrive DS002 MDTProvider \\MTN-SERVER\Distribution$

    # Update the deployment share
    Update-MDTDeploymentShare -Path DS002: -Verbose

    Stop-Transcript

    Save that as “UpdateDeploymentShare.ps1” and schedule it to run nightly using the Windows Task Scheduler.  The action command line required (assuming you saved the script in C:\Scripts) would be:

    PowerShell.exe -File C:\Scripts\UpdateDeploymentShare.ps1

    Make sure you have configured PowerShell to allow script execution, e.g. “Set-ExecutionPolicy RemoteSigned”.  And be sure to update the deployment share path above, as I’m sure your deployment share isn’t at \\MTN-SERVER\Distribution$.

    Sample #2:  Updating media content

    If you haven’t figured out where we moved “media deployment points” in MDT 2010, they now have their own node:  look under the “Advanced Configuration” node in Deployment Workbench and you’ll see the new “Media” node.  You can create one or more media definitions there, specifying what content should be included in the media (by specifying the selection profile to use when copying content to the media), Windows PE settings to use, whether to generate an ISO, etc.

    The “Update Media Content” action is what actually does the work, copying the specified content, generating boot images, and (optionally) generating ISOs.  The following script automates this process, looping through each of the media items that you have defined:

    Start-Transcript C:\Scripts\UpdateMedia.log

    # Connect to the deployment share
    Add-PSSnapIn Microsoft.BDD.PSSnapIn -ErrorAction SilentlyContinue
    New-PSDrive DS002 MDTProvider \\MTN-SERVER\Distribution$

    # Process each of the media items
    Get-ChildItem DS002:\Media | % { Update-MDTMedia -Path "DS002:\Media\$($_.Name)" -Verbose }

    Stop-Transcript

    Save that as “UpdateMedia.ps1” and schedule it to run nightly using the Windows Task Scheduler.  The action command line to run this (assuming again that the script is saved in C:\Scripts) would be:

    PowerShell.exe -File C:\Scripts\UpdateMedia.ps1

    Again, be sure to edit the path above, as \\MTN-SERVER\Distribution$ is the path to my deployment share, not yours.

    Sample #3:  Updating linked deployment shares

    Network deployment points, now called linked deployment shares, also have a new home in MDT 2010 Deployment Workbench: look for them under “Advanced Configuration”, in the “Linked Deployment Shares” folder.   These too use selection profiles to specify what content should be replicated, so arrange your folder structures as needed and create a selection profile that includes the required folders.

    The “Replicate Content” action performs the actual replication, copying the content specified in the configured selection profile.  It can also optionally generate the boot images for the linked deployment share (although if you want to configure the Windows PE settings for that remote deployment share you’ll need to open that deployment share in the Deployment Workbench to make the changes).  The following script initiates the same process:

    Start-Transcript C:\Scripts\UpdateLinkedDS.log

    # Connect to the deployment share
    Add-PSSnapIn Microsoft.BDD.PSSnapIn -ErrorAction SilentlyContinue
    New-PSDrive DS002 MDTProvider \\MTN-SERVER\Distribution$

    # Process each of the linked deployment share items
    Get-ChildItem "DS002:\Linked Deployment Shares" | % { Update-MDTLinkedDS -Path "DS002:\Linked Deployment Shares\$($_.Name)" -Verbose }

    Stop-Transcript

    One last time: edit the path above to specify the path (either a local path or a UNC) to your deployment share.  To run this using the Windows Task Schedule, specify an action command line like so:

    PowerShell.exe -File C:\Scripts\UpdateLinkedDS.ps1

  • Michael Niehaus' Windows and Office deployment ramblings

    MDT 2010 has been released!

    • 0 Comments

    It’s been a long time coming, but we’re finally there:  MDT 2010 was released today.  See http://windowsteamblog.com/blogs/springboard/archive/2009/09/08/mdt-2010-is-released.aspx and http://blogs.technet.com/msdeployment/archive/2009/09/09/mdt-2010-is-released.aspx for the official announcements.  The download is available now at http://www.microsoft.com/downloads/details.aspx?FamilyID=3bd8561f-77ac-4400-a0c1-fe871c461a89&displayLang=en.

  • Michael Niehaus' Windows and Office deployment ramblings

    MDT 2010 New Feature #19: Improved Driver Management

    • 0 Comments

    I’ve been distracted while we worked on fixing the remaining bugs in MDT 2010, which was finally released today.  Now it’s time to get back to the discussion on new features in MDT 2010.  Next up on the list: improved driver management.

    This is really a combination of two features we had already discussed:

    with some capabilities added in that we haven’t already discussed.  First, there are new options available in a Lite Touch task sequence’s “Inject drivers” step:

    image

    Now there are two options when injecting drivers:

    • Inject only matching drivers from the selection profile.  This is the same behavior as MDT 2008, injecting all drivers that matched one of the PnP IDs on the computer.
    • Inject all drivers from the selection profile.  This is new (and roughly corresponds to the similar behavior that is available with a ConfigMgr “Apply Driver Package” step).  Instead of only injecting matching drivers, this injects all the drivers in the selection profile.

    You might choose the first option, you might choose the second – it just depends on how you want to do it.  You might also choose to do both: you could create multiple “Inject driver” steps and specify different options and different selection profiles on both.  For example, you might have one “always apply” selection profile with all the printer drivers that you support (whether currently attached or not) and an “only matching” selection profile for everything else.  You could also set up multiple steps and place conditions on them, using different selection profiles and injection options based on the conditions (e.g. make and model).

    Of course, if you want the process to be more dynamic, you can override the settings on the fly.  I would foresee this being a very common scenario, where you either specify a different selection profile on the fly, or maybe instead specify a list of folders that should be used.  To do this, you need to understand the available task sequence variables that can be configured through CustomSettings.ini:

    • DriverSelectionProfile.  This can be used to override the selection profile configured in the “Inject drivers” step, e.g. changing the default selection profile to “Lenovo T61p Drivers”.
    • DriverGroup.  This is a carry-over from MDT 2008, although now you can specify a folder path in order to select subfolders.  For example, if you created a “Toshiba” folder with a “Tecra M400” folder under it, you could specify “DriverGroup001=Toshiba\Tecra M400” or even “DriverGroup001=%Make%\%Model%”.
    • DriverPaths.  This is a carry-over from BDD 2007 (and before) provided for compatibility only.  It allows you to specify UNC paths containing the drivers to be injected, e.g. “DriverPaths001=\\Server\Share$\Toshiba\Tecra M400” or “DriverPaths001=\\Server\Share$\%Make%\%Model%”.  Because this requires carefully controlling the physical storage of the drivers instead of the logical grouping through folders in the Deployment Workbench, this is frowned upon.  Support for this may be removed in a future release.

    It’s important to understand that these parameters have an “additive” effect.  For example, if you specify a selection profile of “Everything” (all folders) and then specify “DriverGroup001=Toshiba\Tecra M400” the net result will be everything.  But if you specified a selection profile of “Nothing” (no folders) and “DriverGroup001=Toshiba\Tecra M400” then the result would be just the one folder you specified.  (DriverPaths values would be additive as well, but those aren’t recommended.)

    So you have options.  You can create multiple selection profiles and choose which one to use dynamically, something that gets messy if you just want one folder per selection profile (e.g. per model) since every new folder would require a new selection profile.  Or you can choose the “Nothing” selection profile and then specify one or more folders via “DriverGroup”.  I believe that will be the most common approach, as you can then do things like:

    DriverSelectionProfile=Nothing
    DriverGroup001=%Make%
    DriverGroup002=%Make%\%Model%
    DriverGroup003=Peripherals

    If you’ve already experimented with this and have some best practices to share, comments about challenges while implementing this, or just general questions, feel free to e-mail me at mniehaus@microsoft.com

  • Michael Niehaus' Windows and Office deployment ramblings

    ConfigMgr SP2 is speedy

    • 0 Comments

    People have made fun of ConfigMgr, and every version of SMS before that, for being “slow moving software”.  For those of you who try to deploy software to a machine and then wait for it to actually happen, you know what I mean: it was guaranteed to take two minutes from the time you advertised it to the time the first clients acted on it.

    With ConfigMgr SP2, a significant portion of that delay (which was actually happening on the client side, not on the server) was removed.  Now, I can add a new machine into a collection, then go to the machine and initiate a machine policy retrieval cycle and see a popup for new advertisements within a few seconds.  The first time that happened I was a bit startled by the result – surely something must be wrong.  But it wasn’t, the advertisement really was available that quickly.

    ConfigMgr SP2 is still available as an RC through http://connect.microsoft.com, so you can try it out in your lab if you want.  It is expected to be released by the end of October.

  • Michael Niehaus' Windows and Office deployment ramblings

    MDT 2010 New Feature #20: General Purpose ConfigMgr wizard

    • 0 Comments

    In MDT 2008, we provided unknown computer support for ConfigMgr 2007, since it didn’t provide that capability – you first had to import new computers into the ConfigMgr database before you could install an OS, so MDT helped automate that process.  When ConfigMgr 2007 R2 was released, it included unknown computer functionality, so we have now removed most of that from MDT 2010.

    One of the useful parts of this unknown computer process was a pre-execution hook that would run a wizard, leveraging the same wizard framework that we used for MDT 2010 Lite Touch deployments.  This was useful because we provided all the pieces to make it work:  the TSCONFIG.INI file that tells ConfigMgr what to run, the script that gets executed by ConfigMgr (referenced in TSCONFIG.INI), the rules processing logic to gather information from WMI and other data sources, and the wizard files themselves.

    With MDT 2010, we’ve left these pieces in place, but set them up to do something more basic: prompt for a new computer name.  This is provided as a sort of general purpose “sample”, showing how to hook this into ConfigMgr.  While you might not find the sample particularly useful, you can edit the wizard definition (using Notepad or something like the MDT Wizard Editor, http://mdtwizardeditor.codeplex.com/) to add additional panes.

    All the files related to this general purpose sample are located in the “C:\Program Files\Microsoft Deployment Toolkit\SCCM” directory:

    • ZTIMediaHook.wsf.  This is the pre-execution hook script that drives the whole process (gathering information from WMI and other sources, then displaying the wizard).
    • Deploy_SCCM_Definition_ENU.xml.  This file defines the wizard itself, which by default has one pane that asks for the computer name.
    • Deploy_SCCM_Scripts.vbs.  This file contains the initialization and validation scripts called by the wizard pane (which don’t do much in this case).
    • TSConfig.ini.  This file gets added to the boot image and tells ConfigMgr to run the ZTIMediaHook.wsf script.

    So if you wanted too do some customization, the files you would want to change are the “Deploy_SCCM_Definition_ENU.xml” (to add or change wizard panes) and “Deploy_SCCM_Scripts.vbs” (to specify additional initialization or validation logic).

    To actually get these pieces added into a boot image, you can check the “Add media hook files to enable the Deployment Wizard for this boot media” checkbox when running either the “Create Boot Image using Microsoft Deployment” wizard (which creates only new boot image which you would then need to configure the task sequence and ISOs to use) or the “Create Microsoft Deployment Task Sequence” wizard (which can create a new boot image as part of the task sequence creation process).

    Booting from this boot image, you should see a wizard that looks like this:

    image

    You can then specify the computer name you want, which will set the OSDComputerName task sequence variable.  Like I said, pretty simple, but provided as a starting point for your own customizations – just edit the XML and VBS files, create a new boot image, and then deploy.  (Remember that all of these files are actually embedded in the boot image WIM file, so when you make changes you either need to create a new boot image or mount the existing one to change those files.  Update the distribution points after making changes.)

    I’ve also attached a short video that shows the startup process (including the initial ConfigMgr wizard screen that can be used for specifying static IP information and for typing in the media password).

Page 1 of 1 (9 items)