mine_detector

First off, I have to give credit to Nickolaj Andersen for some PowerShell code he posted a while back that led to the code provided below.  He actually posted two items which made this version possible.  One is the example shown on “Create an Application in ConfigMgr 2012 with PowerShell“, and the other is a smaller example of “How to Get MSI File Information with PowerShell“.  If you’re looking for anything related to ConfigMgr 2012 I highly recommend his web site.

Now, back to the discussion:  There are several PowerShell examples floating around which allow creating an Application in ConfigMgr, however, most seem to either leave out additional detail or hard-code some options.  Coming from a developer background, I default to thinking in terms of API building, and frameworks.  So, I started with the most robust example I could find and build on top of that.

The first thing I did was combine the two examples Nickolaj posted so that I could fill-out the product code and product version directly from data extracted from the MSI package.  Then I dug into the SCCM .NET API to pull out some additional parameters (properties actually) and exposed them via PowerShell parameters and variable assignments.

This example (below) is predicated on creating an Application using an MSI file.  You can specify optional parameters such as Transforms, maximum installation runtime, cache persistence, and fast/slow link download handling, among others.  I didn’t go into the following aspects of the ConfigMgr Application Model yet, but maybe in the future:

  • References
  • Application Catalog
  • Distribution Settings
  • Content Locations
  • Supersedence

Nor did I go into the following ConfigMgr Application Deployment Type model yet:

  • Requirements
  • Return Codes
  • Dependencies

There are plenty of examples for automating Content Distribution via PowerShell, so just Google or Bing them if you like.

DISCLAIMER:  Test this, and any other, script in a safe, isolated “testing” environment before you try it in a production environment.  This code does not come with ANY warranty or guarantee of any kind.  Use at your own risk.

I have tested this quite a few times on ConfigMgr 2012 R2 SP1 CU1, but it should work fine on SCCM 2012 R2 RTM and later.  I cannot vouch for SCCM 2012 (versus R2) since I do not have any lab or production environments which run on version prior to 2012 R2.

Here’s the PowerShell script:  Add-CMMSIApplication.ps1

<#
Name: Add-CMMSIApplication.ps1

Version: 1.0.0

Author: David M. Stein
 https://skatterbrainz.wordpress.com

Based on previous works by Nickolaj Andersen...
 
Create an Application in ConfigMgr 2012 with PowerShell
How to get MSI file information with PowerShell
Date: 09/07/2015 Comment: create a new MSI application and related deployment types from PS commandline Support: none. you're on your own. ha ha ha ha... Example-1: .\Add-CMMSIApplication.ps1 -AppName "Remote Desktop Manager" -AppVersion 1.0 -Publisher "Microsoft" -Comment "Remote Desktop Manager Utility" -SiteServer "p01.contoso.com" -SiteCode "P01" -SourcePath "\\p01\AppSource\RemoteDesktopMgr" -MSIFileName "rdcman.msi" Example-2: .\Add-CMMSIApplication.ps1 -AppName "Remote Desktop Manager" -AppVersion 1.0 -Publisher "Microsoft" -Comment "Remote Desktop Manager Utility" -SiteServer "p01.contoso.com" -SiteCode "P01" -SourcePath "\\p01\AppSource\RemoteDesktopMgr" -MSIFileName "rdcman.msi" -MaxRunTime 120 -Transforms "Custom1.mst;Custom2.mst" -UIMode Hidden -ExecMode System -FastLink Download -SlowLink Download -PersistCache $true #> # get inputs PARAM ( [parameter(Mandatory = $true, HelpMessage = "Application Name")] [String] $AppName, [parameter(Mandatory = $true, HelpMessage = "Application Version")] [String] $AppVersion = '1.0', [Parameter(Mandatory = $true, HelpMessage = "Application Publisher name")] $Publisher, [Parameter(Mandatory = $false, HelpMessage = "Comment or description")] [String] $Comment, [Parameter(Mandatory = $true, HelpMessage = "Site server FQDN name")] [String] $SiteServer, [Parameter(Mandatory = $true, HelpMessage = "Site Code")] [String] $SiteCode, [Parameter(Mandatory = $true, HelpMessage = "Content Source UNC path")] [String] $SourcePath, [Parameter(Mandatory = $true, HelpMessage = "MSI Filename without path")] [String] $MSIFileName, [Parameter(Mandatory = $true, HelpMessage = "Maximum allowed execution time")] [int] $MaxRunTime = 120, [Parameter(Mandatory = $false, HelpMessage = "Transforms list")] [String] $Transforms, [Parameter(Mandatory = $false, HelpMessage = "User Interaction Mode")] [ValidateSet("Normal","Maximized","Minimized","Hidden")] [String] $UIMode = "Hidden", [Parameter(Mandatory = $false, HelpMessage = "Execution Mode")] [ValidateSet("System","User","Any")] [String] $ExecMode = "System", [Parameter(Mandatory = $false, HelpMessage = "Download option for fast connections")] [ValidateSet("Download","DoNothing")] [String] $FastLink = 'Download', [Parameter(Mandatory = $false, HelpMessage = "Download option for slow connections")] [ValidateSet("Download","DoNothing")] [String] $SlowLink = 'download', [Parameter(Mandatory = $false, HelpMessage = "Persist download content in client cache")] [bool] $PersistCache = $true ) [System.Reflection.Assembly]::LoadFrom((Join-Path (Get-Item $env:SMS_ADMIN_UI_PATH).Parent.FullName "Microsoft.ConfigurationManagement.ApplicationManagement.dll")) | Out-Null [System.Reflection.Assembly]::LoadFrom((Join-Path (Get-Item $env:SMS_ADMIN_UI_PATH).Parent.FullName "Microsoft.ConfigurationManagement.ApplicationManagement.MsiInstaller.dll")) | Out-Null # Variables $Comment2 = "Created by "+$env:USERDOMAIN+"\"+$env:USERNAME $ContentSourcePath = $SourcePath $ApplicationTitle = $AppName $ApplicationPublisher = $Publisher $ApplicationVersion = $AppVersion $ApplicationLanguage = (Get-Culture).Name $ApplicationDescription = $($Comment+" "+$Comment2).Trim() $DeploymentInstallCommandLine = "msiexec /i $MSIFileName /qn" if ($Transforms.length -gt 0) { $DeploymentInstallCommandLine += " TRANSFORMS=$Transforms" } # Get MSI product code and product version $AppSourcePath = [IO.FileInfo]"$ContentSourcePath\$MSIFileName" try { $WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer $MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase","InvokeMethod",$Null,$WindowsInstaller,@($AppSourcePath.FullName,0)) $Query1 = "SELECT Value FROM Property WHERE Property = '$('ProductCode')'" $Query2 = "SELECT Value FROM Property WHERE Property = '$('ProductVersion')'" $View1 = $MSIDatabase.GetType().InvokeMember("OpenView","InvokeMethod",$null,$MSIDatabase,($Query1)) $View1.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View1, $null) $Record1 = $View1.GetType().InvokeMember("Fetch","InvokeMethod",$null,$View1,$null) $View2 = $MSIDatabase.GetType().InvokeMember("OpenView","InvokeMethod",$null,$MSIDatabase,($Query2)) $View2.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View2, $null) $Record2 = $View2.GetType().InvokeMember("Fetch","InvokeMethod",$null,$View2,$null) $PCode = $Record1.GetType().InvokeMember("StringData","GetProperty",$null,$Record1,1) $PVersion = $Record2.GetType().InvokeMember("StringData","GetProperty",$null,$Record2,1) # concatenate uninstall statement and derive software version from product version $DeploymentUninstallCommandLine = "msiexec /x $PCode /qn" $ApplicationSoftwareVersion = $PVersion } catch { Write-Output $_.Exception.Message } # Get ScopeID $GetIdentification = [WmiClass]"\\$($SiteServer)\root\SMS\Site_$($SiteCode):SMS_Identification" $ScopeID = "ScopeId_" + $GetIdentification.GetSiteID().SiteID -replace "{","" -replace "}","" # Create unique ID for application and deployment type $ApplicationID = "APP_" + [GUID]::NewGuid().ToString() $DeploymentTypeID = "DEP_" + [GUID]::NewGuid().ToString() # Create application objects $ObjectApplicationID = New-Object Microsoft.ConfigurationManagement.ApplicationManagement.ObjectId($ScopeID,$ApplicationID) $ObjectDeploymentTypeID = New-Object Microsoft.ConfigurationManagement.ApplicationManagement.ObjectId($ScopeID,$DeploymentTypeID) $ObjectApplication = New-Object Microsoft.ConfigurationManagement.ApplicationManagement.Application($ObjectApplicationID) $ObjectDeploymentType = New-Object Microsoft.ConfigurationManagement.ApplicationManagement.DeploymentType($ObjectDeploymentTypeID,"MSI") # Add content to the Application $ApplicationContent = [Microsoft.ConfigurationManagement.ApplicationManagement.ContentImporter]::CreateContentFromFolder($ContentSourcePath) if ($FastLink -eq "download") { $ApplicationContent.OnFastNetwork = [Microsoft.ConfigurationManagement.ApplicationManagement.ContentHandlingMode]::Download # persist download in client cache if ($PersistCache -eq $true) { $ApplicationContent.PinOnClient = [bool]$true } } else { $ApplicationContent.OnFastNetwork = [Microsoft.ConfigurationManagement.ApplicationManagement.ContentHandlingMode]::DoNothing } if ($SlowLink -eq "download") { $ApplicationContent.OnSlowNetwork = [Microsoft.ConfigurationManagement.ApplicationManagement.ContentHandlingMode]::Download # persist download in client cache if ($PersistCache -eq $true) { $ApplicationContent.PinOnClient = [bool]$true } } else { $ApplicationContent.OnSlowNetwork = [Microsoft.ConfigurationManagement.ApplicationManagement.ContentHandlingMode]::DoNothing } # allow fallback source $ApplicationContent.FallbackToUnprotectedDP = [bool] $true # max runtime in seconds $ObjectDeploymentType.Installer.MaxExecuteTime = [int] $MaxRunTime # hide progress indicator switch ($UIMode) { 'Maximized' {$ObjectDeploymentType.Installer.UserInteractionMode = [Microsoft.ConfigurationManagement.ApplicationManagement.UserInteractionMode]::Maximized} 'Minimized' {$ObjectDeploymentType.Installer.UserInteractionMode = [Microsoft.ConfigurationManagement.ApplicationManagement.UserInteractionMode]::Minimized} 'Normal' {$ObjectDeploymentType.Installer.UserInteractionMode = [Microsoft.ConfigurationManagement.ApplicationManagement.UserInteractionMode]::Normal} 'Hidden' {$ObjectDeploymentType.Installer.UserInteractionMode = [Microsoft.ConfigurationManagement.ApplicationManagement.UserInteractionMode]::Hidden} } # install for System switch ($ExecMode) { 'System' {$ObjectDeploymentType.Installer.ExecutionContext = [Microsoft.ConfigurationManagement.ApplicationManagement.ExecutionContext]::System} 'User' {$ObjectDeploymentType.Installer.ExecutionContext = [Microsoft.ConfigurationManagement.ApplicationManagement.ExecutionContext]::User} 'Any' {$ObjectDeploymentType.Installer.ExecutionContext = [Microsoft.ConfigurationManagement.ApplicationManagement.ExecutionContext]::Any} } # Application information $ObjectDisplayInfo = New-Object Microsoft.ConfigurationManagement.ApplicationManagement.AppDisplayInfo $ObjectDisplayInfo.Language = $ApplicationLanguage $ObjectDisplayInfo.Title = $ApplicationTitle $ObjectDisplayInfo.Description = $ApplicationDescription $ObjectApplication.DisplayInfo.Add($ObjectDisplayInfo) $ObjectApplication.DisplayInfo.DefaultLanguage = $ApplicationLanguage $ObjectApplication.Title = $ApplicationTitle $ObjectApplication.Version = $ApplicationVersion $ObjectApplication.SoftwareVersion = $ApplicationSoftwareVersion $ObjectApplication.Description = $ApplicationDescription $ObjectApplication.Publisher = $ApplicationPublisher # DeploymentType configuration $ObjectDeploymentType.Title = $ApplicationTitle $ObjectDeploymentType.Version = $ApplicationVersion $ObjectDeploymentType.Enabled = $true $ObjectDeploymentType.Description = $ApplicationDescription $ObjectDeploymentType.Installer.Contents.Add($ApplicationContent) $ObjectDeploymentType.Installer.InstallCommandLine = $DeploymentInstallCommandLine $ObjectDeploymentType.Installer.UninstallCommandLine = $DeploymentUninstallCommandLine $ObjectDeploymentType.Installer.ProductCode = "{" + [GUID]::NewGuid().ToString() + "}" $ObjectDeploymentType.Installer.DetectionMethod = [Microsoft.ConfigurationManagement.ApplicationManagement.DetectionMethod]::ProductCode # Add DeploymentType to Application $ObjectApplication.DeploymentTypes.Add($ObjectDeploymentType) # Serialize the Application $ApplicationXML = [Microsoft.ConfigurationManagement.ApplicationManagement.Serialization.SccmSerializer]::SerializeToString($ObjectApplication) $ApplicationClass = [WmiClass]"\\$($SiteServer)\root\SMS\Site_$($SiteCode):SMS_Application" $ObjectApplication = $ApplicationClass.CreateInstance() $ObjectApplication.SDMPackageXML = $ApplicationXML $Temp = $ObjectApplication.Put() $ObjectApplication.Get()
Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s