Sunday, August 09, 2015

PowerShell + SCCM 2012 : Create Packages & Programs


It has been a while, since I chartered the waters of WMI and Configuration Manager, so pardon any silly mistakes made. One of my friend from PSBUG asked me few questions revolving around creating packages & programs in ConfigMgr using PowerShell.

Every ConfigMgr admin knows that new Application model has been introduced in ConfigMgr 12 but the Packages are here to stay for a while. Packages and Programs are ideal for deploying Scripts (one time or reoccurring ones) and better suited for deploying apps during OSD (heard this one).

ConfigMgr ideally has 3 ways of working with it and below is the pic which says it all :




The post is broken up in 3 parts (based on how you use ConfigMgr):

  1. GUI Way - Doing this to show background on how we do it manually.
  2. Cmdlet Way - using the CM cmdlets to create the package and program
  3. WMI Way - exploring WMI to do the same.



GUI Way :

I believe that doing things for few first times the GUI way helps understand and grasp the process but moving on we should try to automate repetitive tasks. Below is an animated gif showing how to create a minimalist Package & Program (for 7-zip) in ConfigMgr Console :



Cmdlet Way :

Using the cmdlets is straight forward but for sake of people who are starting new with PowerShell way of managing ConfigMgr. Below are the detailed steps.

Import the ConfigMgr Module. You should have ConfigMgr cmdlet library installed on your box now
PS>Import-Module -Name ConfigurationManager

Once done, the next step is discover the cmdlets. How you ask 
PS>Get-Command -Noun CMPackage -Module ConfigurationManager

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Cmdlet          Export-CMPackage                                   ConfigurationManager
Cmdlet          Get-CMPackage                                      ConfigurationManager
Cmdlet          Import-CMPackage                                   ConfigurationManager
Cmdlet          New-CMPackage                                      ConfigurationManager
Cmdlet          Remove-CMPackage                                   ConfigurationManager
Cmdlet          Set-CMPackage                                      ConfigurationManager

Now go ahead and read the help for the cmdlet New-CMPackage to understand what I will be doing next. Create a new Package:
PS>New-CMPackage –Name “7Zip – PS Way” –Path "\\dexsccm\Packages\7-zip\v9.20"

If one looks closely at the syntax of the New-CMPackage file they will immediately notice that the cmdlet doesn't let you set whole lot of options in the package you just created. See below the different parameter sets for the cmdlet:
PS>Get-Command New-CMPackage -Syntax

New-CMPackage -Name  [-Description ] [-Manufacturer ] [-Language ] [-Version ] [-Path ] [-DisableWildcardHandling] [-ForceWildcardHandling] [-WhatIf] [-Confirm] [commonparameters]

New-CMPackage -FromDefinition -PackageDefinitionName  -SourceFileType  -SourceFolderPathType  -SourceFolderPath  [-DisableWildcardHandling] [-ForceWildcardHandling] [-WhatIf] [-Confirm] commonparameters]

New-CMPackage -FromDefinition -PackagePath  -PackageNoSourceFile [-DisableWildcardHandling] [-ForceWildcardHandling] [-WhatIf] [-Confirm] [commonparameters]

New-CMPackage -FromDefinition -PackagePath  -SourceFileType  -SourceFolderPathType  -SourceFolderPath  [-DisableWildcardHandling] [-ForceWildcardHandling] [-WhatIf]
[-Confirm] [commonparameters]

New-CMPackage -FromDefinition -PackageDefinitionName  -PackageNoSourceFile [-DisableWildcardHandling] [-ForceWildcardHandling] [-WhatIf] [-Confirm] [commonparameters] 

So how does one Set all those properties for a Package via PowerShell ??
Go ahead and read the help for Set-CMPackage cmdle and you will know this is the cmdlet which will do the rest customization needed for the Package created. Suppose I want to enable binary differential replication for this package along with setting the distribution priority to high for this package, use below :
PS>Set-CMPackage -Name “7Zip – PS Way” -EnableBinaryDeltaReplication
PS>Set-CMPackage -Name “7Zip – PS Way” -DistributionPriority High 

Did you notice above, I had to use the Set-CMPackage cmdlet twice, Why ?
Hint - Check what are Parameter sets for a cmdlet in PowerShell.

Moving on, Now time to create the Program (standard) for the package which will install the 7zip package for us. The cmdlet is New-CMProgram, if you still don't know how to figure that out read the help for Get-Command ;)

Let's create the Program:

PS>New-CMProgram -PackageName “7Zip – PS Way” -StandardProgramName "7zip PS Install - Program" -CommandLine "msiexec.exe /I 7z920-x64.msi /quiet /norestart"

Now you can configure a lot of options for the program while creating it or you can also use Set-CMProgram to configure them later, for example I am setting the run type for the above standard program created as hidden type :

Set-CMProgram -PackageName “7Zip – PS Way” -StandardProgramName "7zip PS Install - Program" -StandardProgram -RunType Hidden

One can play with the Set-CMProgram to tweak the program settings as per need, there are a whole lot of the Parameters and switches to play with this cmdlet.

Once the Package and Program have been created it is time to distribute them to the DP Groups or DP. The cmdlet is Start-CMContentDistribution.
Start-CMContentDistribution -PackageName “7Zip – PS Way” -DistributionPointGroupName DexLabDPGroup 


WMI Way :

Let's get to the more adventurous way of creating the Packages & Programs using WMI.
Fair Warning that this is a more complex way and if you don't understand how WMI works then my advice would be to stick to the cmdlet way.

Start with creating a WMI Instance of SMS_Package class, supply the Package name and the PkgSourcePath while creating the instance.
New-CimInstance -ClassName SMS_Package -Property @{'Name'='7zip - WMI Way';'PkgSourcePath'="\\dexsccm\Pa
ages\7-zip\v9.20"} -Namespace Root/SMS/site_DEX


ActionInProgress               : 1
AlternateContentProviders      :
Description                    :
ExtendedData                   :
ExtendedDataSize               : 0
ForcedDisconnectDelay          : 5
ForcedDisconnectEnabled        : False
ForcedDisconnectNumRetries     : 2
Icon                           :
IconSize                       : 0
IgnoreAddressSchedule          : False
ISVData                        :
ISVDataSize                    : 0
IsVersionCompatible            :
Language                       :
LastRefreshTime                : 4/10/1970 6:35:00 AM
LocalizedCategoryInstanceNames : {}
Manufacturer                   :
MIFFilename                    :
MIFName                        :
MIFPublisher                   :
MIFVersion                     :
Name                           : 7zip - WMI Way
NumOfPrograms                  : 0
PackageID                      : DEX00017
PackageSize                    : 0
PackageType                    : 0
PkgFlags                       : 0
PkgSourceFlag                  : 1
PkgSourcePath                  : \\dexsccm\Packages\7-zip\v9.20
PreferredAddressType           :
------ Snipped -------

There are attributes or properties which you can set on a WMI Instance later after creation, but you need to read the Class documentation for properties with Read/Write access type.

Now, if we look at the Package created, we will soon notice that the PkgSourceFlag is set to 1 (default value - 
STORAGE_NO_SOURCE. The program does not use source files.), Check the  documentation  and you will realize you need to set it to 2 (STORAGE_DIRECT). With the value of 1 set for PkgSourceFlag you will see the below in the properties for the Package.


So let's get to it now. First get the CIMInstance stored in a variable and then use Set-CIMInstance to set the property PkgSourceFlag on it and verify the changes. Below is the code and gif in action (it shows green screen for the code executed)  :
# get the CIM Instance stored in a variable
$package = Get-CimInstance -ClassName SMS_Package -Filter "Name='7zip - WMI Way'" -Namespace root/SMS/site_DEX

# set the PkgSourceFlag on the CIM Instance
Set-CimInstance -InputObject $Package -Property @{'PkgSourceFlag'=[uint32]2} 


Let's move ahead and create a new Program to install the Package using WMI. The WMI class tp focus on is SMS_Program. Before creating the WMI/CIM Instance reading the documentation of the classes is must to avoid any surprises.

The doc for the SMS_Program in Remarks lists below :

A program is always associated with a parent package and typically represents the installation program for the package. Note that more than one program can be associated with the same package. The application uses the PackageID property to make this association. Your application cannot change this property after the SMS_Program object is created. To associate the program with a different package, the application must delete the object and create a new object with a new PackageID value.

As mentioned above, we need PackageID for our Package in order to associate a Program t o it. If you are following this post then the variable $Package already has the PackageID property which we can dot reference and use.

Below is the code snippet which I used to create a new Program for the Package :

$ProgramHash3 = @{
                PackageID=$($package.PackageID);
                ProgramName='7zip WMI Install - Program';
                CommandLine='msiexec.exe /I 7z920-x64.msi /quiet /norestart'
                
                }
New-CimInstance -ClassName SMS_Program -Namespace Root/SMS/site_DEX -Property $ProgramHash3


Note - Found a bug at the MSDN SMS_Program documentation which says that the CommandLine property is not a Key Qualifier. Below is what the doc says :

CommandLine

Data type: String
Access type: Read/Write
Qualifiers: [ResID(904), ResDLL("SMS_RSTT.dll")]
The command line that runs when the program is started. The default value is "".

But while trying to create a new SMS_Program instance, I realized that one has to explicitly pass this while creating the Object and moreover this can't be an empty string too. See the below GIF :

Voila, Now you can read the documentation of the SMS_Program class and try using the Set-CIMInstance to set some of the writable attributes on the Object. As an exercise leaving the Content distribution using WMI here, if I remember correctly I did cover it in one of the earlier posts (not sure which one though).

Resources:

My PowerShell + ConfigMgr Posts collection (quite a few):
http://www.dexterposh.com/p/collection-of-all-my-configmgr.html

Configuration Manager PowerShell Tuesdays: Creating and Distributing a Package / Program
http://blogs.technet.com/b/neilp/archive/2013/01/15/configuration-manager-sp1-powershell-tuesday-creating-and-distribution-a-package-program.aspx


SMS_Package Class:
https://msdn.microsoft.com/en-us/library/cc144361.aspx

7 comments:

  1. Shouldn't that be the CIM way..? I mean it is very similar but there are differences between the two when doing Get-CimInstance and using Invoke-CimMethod...

    Like the article by the way...

    ReplyDelete
    Replies
    1. ConfigMgr admins understand it better when I say 'WMI' , like the post which says 'SCCM' which is now ConfigMgr :) .

      One can purely do this using Get-WMIObject and Invoke-WMImethod too.

      Delete
  2. Differences in the structure for -Filtering is what caught me by surprise. I "thought" its just the same as WMI,,, right? No..! I'm neck deep in it right now which is the only reason I mention it...

    ReplyDelete
    Replies
    1. What differences came in as a surprise ?
      WMI is MSFT implementation of CIM, there might be few subtle differences though.

      CIM Cmdlets let you explore and discover the CIM Classes more easily and also let you specify a lot of parameters e.g KeyOnly, Property etc when running them also CIM is more firewall friendly uses WSMAN and is backwards compatible to using DCOM for communication.

      Delete
  3. Little things but enough to break lines of code. -Filter "Name= 'something'" or "Name Like 'So%'" instead of -Filter 'Name -Like "something"' or 'Name -Like "Some*"' Took me awhile to figure it out.

    ReplyDelete
    Replies
    1. Ohhh ! that.

      Yes the PowerShell filtering operators doesn't work with CIM /WMI as these cmdlets transform to a WQL query behind the scenes. So the filter has to be in proper WQL Syntax.

      Delete
  4. Hi there,

    I am at my wits end here. I appreciate is SCCM and not all of you maybe able to help but maybe you can assist me with the PowerShell. I am trying to deploy 200+ packages to 200+ collections. It will take days if I do this manually.

    The SCCM cmdlet guide is here. https://technet.microsoft.com/en-us/library/jj870939(v=sc.20).aspx

    My command is as follows:

    Start-CMPackageDeployment -CollectionName "Collection1" -PackageName "Package1" -StandardProgramName "Program1" -DeployPurpose Required -DeploymentAvailableDay (get-date) -DeploymentAvailableTime (get-date) -ScheduleEvent AsSoonAsPossible -RerunBehavior RerunIfFailedPreviousAttempt

    I am then asked:

    cmdlet Start-CMPackageDeployment at command pipeline position 1
    Supply values for the following parameters:
    StandardProgram:

    I enter "Program1"

    The error I then get is:

    Start-CMPackageDeployment : Cannot convert 'System.String' to the type 'System.Management.Automation.SwitchParameter'required by parameter 'StandardProgram'.
    At line:1 char:1

    Stuggling to know what the StandardProgram is when it has it at StandardProgramName

    ReplyDelete