Quantcast
Channel: ConfigMgr – Adam, the Automator
Viewing all articles
Browse latest Browse all 19

Convert ConfigMgr Application to Package/Program

$
0
0

UPDATE 07/11/14 – Added the ability to convert applications with multiple deployment types. The script will now create a package for each deployment type.

Here’s a script I recently wrote to solve a necessary problem. At my client, we were seeing way too many flaky problems with using application inside OSD task sequences. The same programs would work just fine in a package. It was decided that no more applications would go inside OSD task sequences. This is a script I wrote based on David O’Brien’s script. to not only convert all of the existing applications but also to use in a larger application provisioning script that creates an application and immediately converts it to a package for OSD.

Download this script on the Technet Script Repository

#Requires -Version 3

<#
.SYNOPSIS
 	This creates a ConfigMgr package/program for each deployment type from the attributes of a ConfigMgr application.
.DESCRIPTION
	This reads a ConfigMgr application and gathers all needed attributes from the application itself and it's
	deployment types.  It then creates a package/program for each deployment type in the application.  The resulting
	package(s) will be named $ApplicationName - $DeploymentTypeName" with each program being named $DeploymentTypeName.
.NOTES
	Created on: 07/03/2014
	Created by: Adam Bertram
	Filename:   Convert-CMApplicationToPackage.ps1
	Credits:    http://www.david-obrien.net/2014/01/24/convert-configmgr-applications-packages-powershell/
	Todos:	    If the app has a product code, use this for installation program management in the program
		    Create an option to distribute the package to DPs after creation
.DESCRIPTION
 	This gets all common attributes of a ConfigMgr application that a ConfigMgr package/program has and uses
	these attributes to create a new packages with a program inside for each application deployment type.
.EXAMPLE
    .\Convert-CMApplicationToPackage.ps1 -ApplicationName 'Application 1'
	This example converts the application "Application 1" into a package called "Application 1" and a program
	called "Install Application 1" if it has a single deployment type.
.EXAMPLE
     .\Convert-CMApplicationToPackage.ps1 -ApplicationName 'Application 1' -SkipRequirements
	This example converts the application "Application 1" into a package called "Application 1" and a program
	called Install "Application 1" excluding disk space and OS requirements if it has a single deployment type.
.PARAMETER ApplicationName
 	This is the name of the application you'd like to convert.
.PARAMETER SkipRequirements
	Use this switch parameter if you don't want to bring over any disk or OS requirements from the application.
.PARAMETER OsdFriendlyPowershellSyntax
	Use this switch parameter to convert any program that's simply a reference to a PS1 file that normally works
	in a non-OSD environment to a full powershell syntax using powershell.exe.
.PARAMETER SiteServerName
	The ConfigMgr site server name
.PARAMETER SiteCode
	The ConfigMgr site code.
#>
[CmdletBinding()]
param (
	[Parameter(Mandatory,
			   ValueFromPipeline,
			   ValueFromPipelineByPropertyName)]
	[string]$ApplicationName,
	[switch]$SkipRequirements,
	[switch]$OsdFriendlyPowershellSyntax,
	[string]$SiteServer = 'YOURSITESERVER',
	[string]$SiteCode = 'YOURSITECODE'
)

begin {
	try {
		## This helper function gets all of the supported platform objects that's supported for creating OS requirements for a package,
		## looks for a match between each CI_UniqueID and the OS string and if there's a match, creates a new lazy property instance
		## populates the necessary values and returns an array of objects that can be used to populated the SupportOperatingSystemPlatforms
		## lazy WMI property on the SMS_Program object.
		function New-SupportedOsObject([Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Rules.Rule]$OsRequirement) {
			$SupportedPlatforms = Get-WmiObject -ComputerName $SiteServer -Class SMS_SupportedPlatforms -Namespace "root\sms\site_$SiteCode"
			$SupportedOs = @()
			## Define the array of OS strings to convert to objects
			if ($OsRequirement.Expression.Operator.OperatorName -eq 'OneOf') {
				$AppOsList = $OsRequirement.Expression.Operands.RuleId
			} elseif ($OsRequirement.Expression.Operator.OperatorName -eq 'NoneOf') {
				## TODO: Query the site server for all possible operating system values and remove all OSes in
				## $OsRequirement.DeploymentTypes[0].Requirements[0].Expression.Operands.RuleId
				return $false
			}
			foreach ($AppOs in $AppOsList) {
				foreach ($OsDetail in $SupportedPlatforms) {
					if ($AppOs -eq $OsDetail.CI_UniqueId) {
						$instance = ([wmiclass]("\\$SiteServer\root\sms\site_$SiteCode`:SMS_OS_Details")).CreateInstance()
						if ($instance -is [System.Management.ManagementBaseObject]) {
							$instance.MaxVersion = $OsDetail.OSMaxVersion
							$instance.MinVersion = $OsDetail.OSMinVersion
							$instance.Name = $OsDetail.OSName
							$instance.Platform = $OsDetail.OSPlatform
							$SupportedOs += $instance
						}
					}
				}
			}
			$SupportedOs
		}
		
		if (!(Test-Path "$(Split-Path $env:SMS_ADMIN_UI_PATH -Parent)\ConfigurationManager.psd1")) {
			throw 'Configuration Manager module not found.  Is the admin console intalled?'
		} elseif (!(Get-Module 'ConfigurationManager')) {
			Import-Module "$(Split-Path $env:SMS_ADMIN_UI_PATH -Parent)\ConfigurationManager.psd1"
		}
		$Location = (Get-Location).Path
		Set-Location "$($SiteCode):"
		
		$Application = Get-CMApplication -Name $ApplicationName
		if (!$Application) {
			throw "$ApplicationName not found"
		}
		$ApplicationXML = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::DeserializeFromString($Application.SDMPackageXML)
		
		$SetProgramProps = @{ }
	} catch {
		Write-Error $_.Exception.Message
		exit
	}
}
process {
	try {
		$DeploymentTypes = $ApplicationXML.DeploymentTypes
		
		for ($i = 0; $i -lt $DeploymentTypes.Count; $i++) {
			$PackageName = "$ApplicationName - $($ApplicationXML.DeploymentTypes[$i].Title)"
			$ProgramName = $ApplicationXML.DeploymentTypes[$i].Title
			
			if (Get-CMPackage -Name $PackageName) {
				throw "$PackageName already exists"
			}
			
			$PackageProps = @{
				'Name' = $PackageName;
				'Version' = $ApplicationXML.SoftwareVersion;
				'Manufacturer' = $ApplicationXML.Publisher;
				'Path' = $ApplicationXML.DeploymentTypes[$i].Installer.Contents.Location;
			}
			
			## 07/03/2014 - Even though the New-CMProgram documentation leads you to believe you can use a string for the RunType
			## param, it won't work.  You must use the [Microsoft.ConfigurationManagement.Cmdlets.AppModel.Commands.RunType] object.
			$NewProgramProps = @{
				'StandardProgramName' = $ProgramName;
				'PackageName' = $PackageName;
				'RunType' = [Microsoft.ConfigurationManagement.Cmdlets.AppModel.Commands.RunType]::($ApplicationXML.DeploymentTypes[$i].Installer.UserInteractionMode)
			}
			
			$AppCmdLine = $ApplicationXML.DeploymentTypes[$i].Installer.InstallCommandLine
			## If the command line is simply a reference to a single PS1 file
			if ($OsdFriendlyPowershellSyntax.IsPresent -and ($AppCmdLine -match '^\w+\.ps1$')) {
				$NewProgramProps.CommandLine = "powershell.exe -ExecutionPolicy bypass -NoProfile -NoLogo -NonInteractive -File $AppCmdLine"
			} else {
				$NewProgramProps.CommandLine = $ApplicationXML.DeploymentTypes[$i].Installer.InstallCommandLine
			}
			
			$SetProgramProps = @{
				'EnableTaskSequence' = $true;
				'StandardProgramName' = $ProgramName;
				'Name' = $PackageName;
			}
			
			## 07/03/2014 - Due to a bug in the New-CMprogram cmdlet, even though 15 min or 720 min is allowed via the GUI
			## for the max run time, it doesn't work via the New-CMProgram cmdlet.  To compensate, I'm adding or removing
			## 1 minute and this works.
			$Duration = $ApplicationXML.DeploymentTypes[$i].Installer.MaxExecuteTime
			if ($Duration -eq 15) {
				$Duration = $Duration + 1
			} elseif ($Duration -eq 720) {
				$Duration = $Duration - 1
			}
			$NewProgramProps.Duration = $Duration
			
			if (!$SkipRequirements.IsPresent) {
				$Requirements = $ApplicationXML.DeploymentTypes[$i].Requirements
				$RequirementExpressions = $Requirements.Expression
				$FreeSpaceRequirement = $RequirementExpressions | where { ($_.Operands.LogicalName -contains 'FreeDiskSpace') -and ($_.Operator.OperatorName -eq 'GreaterEquals') }
				if ($FreeSpaceRequirement) {
					$NewProgramProps.DiskSpaceRequirement = $FreeSpaceRequirement.Operands.value / 1MB
					$NewProgramProps.DiskSpaceUnit = 'MB'
				}
			}
			
			switch ($ApplicationXML.DeploymentTypes[$i].Installer.RequiresLogon) {
				$false {
					$NewProgramProps.ProgramRunType = 'OnlyWhenNoUserIsLoggedOn'
				}
				$true {
					$NewProgramProps.ProgramRunType = 'OnlyWhenUserIsLoggedOn'
				}
				default {
					$NewProgramProps.ProgramRunType = 'WhetherOrNotUserIsLoggedOn'
				}
			}
			
			if ($ApplicationXML.DeploymentTypes[$i].Installer.UserInteractionMode -eq 'Hidden') {
				$SetProgramProps['SuppressProgramNotifications'] = $true
			}
			
			if ($ApplicationXML.DeploymentTypes[$i].Installer.SourceUpdateCode) {
				##TODO: Look into setting installation source management on the package
			}
			
			$PostIntallBehavior = $ApplicationXML.DeploymentTypes[$i].Installer.PostInstallBehavior
			if (($PostIntallBehavior -eq 'BasedOnExitCode') -or ($PostIntallBehavior -eq 'NoAction')) {
				$SetProgramProps.AfterRunningType = 'NoActionRequired'
			} elseif ($PostIntallBehavior -eq 'ProgramReboot') {
				$SetProgramProps.AfterRunningType = 'ProgramControlsRestart'
			} elseif ($PostIntallBehavior -eq 'ForceReboot') {
				$SetProgramProps.AfterRunningType = 'ConfigurationManagerRestartsComputer'
			}
			
			$NewPackage = New-CMPackage @PackageProps
			Write-Verbose "Successfully created package name $($NewPackage.Name) ($($NewPackage.PackageID))"
			$NewProgram = New-CMProgram @NewProgramProps
			Set-CMProgram @SetProgramProps
			
			if (!$SkipRequirements.IsPresent) {
				$OsRequirement = $Requirements | where { $_.Expression -is [Microsoft.SystemsManagementServer.DesiredConfigurationManagement.Expressions.OperatingSystemExpression] }
				if ($OsRequirement) {
					$SupportedOs = New-SupportedOsObject $OsRequirement
					$NewProgram.SupportedOperatingSystems = $SupportedOs
					$NewProgram.Put()
				}
			}
			Unlock-CMObject $NewPackage
		}
		
	} catch {
		Write-Error $_.Exception.Message
	}
}

end {
	Set-Location $Location
}

The post Convert ConfigMgr Application to Package/Program appeared first on Adam the Automator.


Viewing all articles
Browse latest Browse all 19

Trending Articles