Migrating to the SCCM UDI for OSD, part 4: Applications

Continued from part 3b:

Previously in this series I demonstrated how you can manage Operating System selection and application in UDI, and one (rather complicated) method for managing drivers in SCCM/UDI. To complete our UDI experience, I now present a handy script for automation of populating select SCCM applications into UDI.

There is some conventional wisdom floating around out there in the ‘tubes that you should not deploy the new SCCM 2012-style “Applications” in SCCM OSD Task Sequences. Instead, it is asserted that you should use only traditional application “Packages”. Since we have developed all or our application installer using the new “Application” objects, I have chosen to ignore this guidance. Fortunately, it appears that the earlier bugs that led to dispensing of this advice have been corrected in the 2012 R2 CU2-CU3 timeframe.

The script below will make use of Configuration Manager application tags to determine which Applications should be populated into UDI. Additionally, the tags are used to generate application groups in the UDI wizard. If you don’t want an application to appear in UDI, don’t tag it. Applications can have multiple tags, and thus can end up getting defined more than once un UDI. This does not appear to cause any problems during deployment.

This script will re-write the entire UDI designer “.app” file, which is an XML-formatted document that defines all of the applications and groups that will be displayed during the UDI Wizard (the default name for this file is ‘UDIWizard_Config.xml.app’). Because I am writing out the entire file, I chose to use the System.Xml.XmlTextWriter .Net Framework class to do the heavy lifting for me. I probably could have used System.Xml.XmlDocument (which I used in part 3 of this series for updating the main UDI XML control file), but the XmlTextWriter seemed to be a more direct route to getting the job done in this case.

Note that in this code I am again using the SCCM PowerShell cmdlets. In this case, I am using Get-CMCategory and Get-CMApplication. These commands easily could be replaced with simple WMI queries (as seen in part 3 of this series). I also am using some SCCM managed code. I call the “Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer” class in order to use the “Deserialize” method. This method converts the large volume of data in the SDMPackageXML attribute of the application object into somewhat more accessible PowerShell objects.

To use this script in your environment, you will need to update the $outPath, $CMSiteCode, and $CMBinPath variables to match your environment. After running the script, you will need to redistribute the content of your MDT Files package to your distribution points. You UDI clients will read the new data as soon as it is made available on your DP.

# build-UDIImageList.ps1
# J. Greg Mackinnon
# Created: 2015-01-26
# Updated: 2015-03-17 - Added host output to indicate task progress.  Removed block comments for blogging clarity.
# Populates the UDI Configuration Wizard XML APP file with all categorized applications gathered 
# from Configuration Manager.  
# Requires: a local installation of the Configuration Manager administration tools.
#   Modify $outPath, $CMSiteCode, and $CMBinPath to match your environment.
Set-PSDebug -Strict

[string] $outPath = 'O:\sources\os\mdt\files\Scripts\UDIWizard_Config.xml.app'

[string] $CMSiteCode = 'UVM'
[string] $CMBinPath = 'F:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\'
[string] $CMModName = 'ConfigurationManager.psd1'
[string] $CMModPath = Join-Path -Path $CMBinPath -ChildPath $CMModName

[string] $CMDrive = $CMSiteCode + ':\'

Import-Module -Name $CMModPath | out-null
Push-Location $CMDrive

#Gather current "Administrative Categories" used to classify Applications, capture to an Array of strings:
write-host "Gathering Application Categories..."
[String[]] $CMAppGroups = Get-CMCategory -CategoryType AppCategories | % {$_.LocalizedCategoryInstanceName}

[int] $appCount = 1

# Output XML requires: 
    # DisplayName (map to LocalizedDisplayName)
    # Name, 
    # Guid (with ScopeID, ApplicationGUID... maps to "ModelName" CMApplication Property), 
    # description (optional? map to LocalizedDescription), 
    # type (deployment type?), 
    # ProductID (which can be found in the sdmpackagexml.deploymenttypes[#].installer.productcode)
# All separated into "ApplicationGroup" stanzas, with name= attributes, I think we can map this to "LocalizedCategoryInstanceNames"

#$xmlDoc = New-Object System.Xml.XmlDocument # Note this is the same as the  type accelerator
$utf8 = New-Object System.Text.UTF8Encoding
# Create The Document Writer:
$XmlWriter = New-Object System.XMl.XmlTextWriter($outPath,$utf8)
$xmlWriter.Formatting = "Indented"
$xmlWriter.Indentation = "5"

write-host "Gathering CM Applications..."
$CMApps = Get-CMApplication | Select-Object -Property LocalizedDisplayName,LocalizedDescription,ModelName,LocalizedCategoryInstanceNames,IsLatest,IsExpired,IsSuperseded,SDMPackageXML | ? {$_.IsLatest -and ($_.IsExpired -eq $false) -and ($_.IsSuperseded -eq $false)}

foreach ($group in $CMAppGroups) {
    write-host "Processing group: $group" -ForegroundColor yellow
    foreach ($app in $CMApps) {
        if ($app.LocalizedCategoryInstanceNames.contains($group)) {
            write-host "    Adding application " $app.LocalizedDisplayName -ForegroundColor cyan

            $appXml = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::Deserialize($app.SDMPackageXML,$true)




                        $xmlWriter.WriteEndElement() # <-- End Setter
                    $xmlWriter.WriteEndElement() # <-- End Match
                    foreach ($type in $appXml.DeploymentTypes) {
                                if ($type.Installer.Technology -match 'MSI') {
                                } else {
                                    $xmlWriter.WriteString(' ')
                            $xmlWriter.WriteEndElement() # <-- End Setter
                        $xmlWriter.WriteEndElement() # <-- End Match
                $xmlWriter.writeEndElement() # <-- End ApplicationMappings

            $xmlWriter.WriteEndElement() # <-- End Application
            $appCount++ # Increment the appCount by one for use in the "ID" Application element property.
    $XmlWriter.WriteEndElement() # <-- End ApplicationGroup

$xmlWriter.WriteEndElement() # <-- End Application
$xmlWriter.WriteEndDocument() # <-- End XML Document
# Finish The Document


And that is all of the new code that I needed to write for SCCM/UDI. In the final part of this series, I will discuss a few SCCM/UDI quirks that were easily corrected without custom coding:

Series Index: