Sunday, March 30, 2014

PowerShell + SCCM 2012 R2 : Create an Application (from MSI) & Deploy it

This is one of the many posts to follow on my experiments with the new Application Model introduced in Configuration Manager 2012.

In this post will use PowerShell to create a new application, deployment type (from a MSI) and then finally deploy it to a machine. So without further delay.....

Let's get to it 

Create the Application using New-CMApplication cmdlet:

New-CMApplication -Name "Quest Active Roles Managment Shell for AD" -Description "Quest Snapin for AD Admins (64-bit only)" -SoftwareVersion "1.51" -AutoInstall $true

The above will create a Application Object in ConfigMgr which can be used in Task Sequences as well (-AutoInstall $true ). Now let's open the Console and have a peek at the properties of the Application created.

The Set-CMApplication Cmdlet can also be used to set property on the Application Object later. 

Let's create a new App Category and then add the above application to it.

New-CMCategory -CategoryType AppCategories -Name "ADAdministration" -Verbose

Set-CMApplication -Name "Quest Active Roles Managment Shell for AD"  -LocalizedApplicationName "Quest AD Snapin"  -LocalizedApplicationDescription "PowerShell Snapin to be used by AD Admins" -AppCategories "ADAdmins" -SendToProtectedDistributionPoint $true -Verbose

Note the Localized Display Name & Description appears in the Software Center/ Catalog.
Wait ! When you try to run the second cmdlet you would encounter an error
if on the console you have properties opened :

This is by design as Application is one of the globally replicated Objects and before someone tries to edit it they need to request lock on the Object.
Read more about SEDO here and want to know how to request SEDO Locks using PowerShell then go to MVP Kaido's post here

Close the open properties of the Application if any and then try again and the second cmdlet will work.

As a mental note will try to look more into SEDO Locks later.

Create the DeploymentType (Auto from MSI File)

Once the application is created we need to create the Deployment type for it . Note in CM 2012 there can be multiple deployment types targeting various devices but here we will take a look at the default one that gets created when we specify a MSI file.

Now the cmdlet is Add-CMDeploymentType :

Add-CMDeploymentType -ApplicationName "Quest Active Roles Managment Shell for AD" -InstallationFileLocation "\\dexsccm\Packages\QuestADSnapin\Quest_ActiveRolesManagementShellforActiveDirectoryx64_151.msi" -MsiInstaller -AutoIdentifyFromInstallationFile -ForceForUnknownPublisher $true -InstallationBehaviorType InstallForSystem

Note the use of parameter -AutoIdentifyFromInstallationFile and -ForceForUnkownPublisher $True here. These parameters corresponds to what one would normally do when using GUI to create the Application (default way of creating Apps).

This will create the Deployment type with appropriate configuration and if you want to change something later take a look at the Set-CMDeploymentType cmdlet.

Distribute the Content 

Before we start deploying the Application to the Devices, we need to distribute the package to the DP or DP Group. The cmdlet here is Start-CMContentDistribution

Start-CMContentDistribution -ApplicationName "Quest Active Roles Managment Shell for AD" -DistributionPointGroupName "Dex LAB DP group" -Verbose

Now once the Content is Distributed then we can go ahead and deploy the Application.

Create a Collection for the Application and Deploy the Application to it

As a standard practice, I will create a Device Collection by the name of the Application and add members to it where the Application ultimately gets deployed to.

Create the Device Collection to refresh every day at the moment when it is created (that is what New-CMSchedule cmdlet does)

New-CMDeviceCollection -Name "Quest Active Roles Managment Shell for AD" -Comment "All the Machines where Quest AD Snapin is sent to" -LimitingCollectionName "All Systems"  -RefreshType Periodic -RefreshSchedule (New-CMSchedule -Start (get-date) -RecurInterval Days -RecurCount 7) -Verbose

Now Add the machine name to the Collection in a Query MemberShip Rule or Direct MemberShip Rule. For this just using Direct membership rule for now. In the upcoming post will go with Query Membership Rule.

Add-CMDeviceCollectionDirectMembershipRule -CollectionName "Quest Active Roles Managment Shell for AD"  -Resource (Get-CMDevice -Name "DexterDC") -Verbose

Now the collection is created let's deploy the Application to this Collection's member.

Start-CMApplicationDeployment -CollectionName "Quest Active Roles Managment Shell for AD" -Name "Quest Active Roles Managment Shell for AD" -DeployAction Install -DeployPurpose Available -UserNotification DisplayAll -AvaliableDate (get-date) -AvaliableTime (get-date) -TimeBaseOn LocalTime  -Verbose


Now we can refresh the Machine Policy on the Members of the Collection by using the old WMI way of connecting to the Client machine through WMI and calling the triggerSchedule method or use Client Notification (which is a great new feature) which will ensure that the Application reaches end machine quickly.

Invoke-CMClientNotification -DeviceCollectionName "Quest Active Roles Managment Shell for AD" -NotificationType RequestMachinePolicyNow -Verbose

After all done let's run the Summary of the Deployment:

Invoke-CMDeploymentSummarization -CollectionName "Quest Active Roles Managment Shell for AD" -Verbose

After this we can see the results in the summary of the Deployment:

And the Application does shows up on the end Machine:

Creating various types of Applications & Packages (Classic ones) are the next in my post list.

Below is the consolidated Gist:

Saturday, March 15, 2014

PowerShell + SCCM 2012 R2 : Client Push Installation

In this post, would share PowerShell way of enabling Client Push Installation on a Site . Divided into 3 Steps:

  1. Enable Client Push Installation
  2. Create Boundary Group, Add Boundary and set Content Location
  3. Distribute the Config Mgr Client Package

Enable Client Push Installation

After loading the ConfigurationManager PowerShell module, if we search for the commands with noun clientpush we get following output:
PS DEX:\> gcm -Noun *clientpush* -Module configurationmanager

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Cmdlet          Get-CMClientPushInstallation                       configurationmanager
Cmdlet          Set-CMClientPushInstallation                       configurationmanager

Now one can go and read up help for these, I personally prefer the Online version of the help as it shows the Parameter sets and is always up to date too. How to do that ?
PS DEX:\> help Set-CMClientPushInstallation -Online

To enable the client push installation we will be using the cmdlet Set-CMClientPushInstallation (you would have guessed that by the Verb-Noun naming convention).

PS DEX:\> Set-CMClientPushInstallation -SiteCode DEX -EnableAutomaticClientPushInstallation $true -EnableSystemTypeServer $true -EnableSystemTypeWorkstation $true -EnableSystemTypeConfigurationManager $true -InstallClientToDomainController $true -ChosenAccount "Dexter\Administrator" -InstallationProperty "SMSSITECODE=DEX" -Verbose

Once the above cmdlet runs it will configure the settings. If you see the cmdlet and parameter names are very descriptive in what they are doing ....still thinking of getting starting with PowerShell in Config Mgr...It's easy ;)


The Client Push installation settings are done, but in order for us to make sure that the discovered clients are able to locate the content we need to create a Boundary group and specify the Server for content location under that.

Create Boundary Group, Add Boundary and set Content Location

Right now I have only one Boundary by the name of my AD Site and no Boundary groups as evident from below ScreenShot:

Let's create a new Boundary Group and add this Boundary to it and specify the content location under it.

#Before starting how to discover the cmdlets we will be using's simple but wanted to point out. (Get-Command is your friend here)

PS DEX:\> gcm -Noun *boundary*

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Cmdlet          Add-CMBoundaryToGroup                              configurationmanager
Cmdlet          Get-CMBoundary                                     configurationmanager
Cmdlet          Get-CMBoundaryGroup                                configurationmanager
Cmdlet          New-CMBoundary                                     configurationmanager
Cmdlet          New-CMBoundaryGroup                                configurationmanager
Cmdlet          Remove-CMBoundary                                  configurationmanager
Cmdlet          Remove-CMBoundaryFromGroup                         configurationmanager
Cmdlet          Remove-CMBoundaryGroup                             configurationmanager
Cmdlet          Set-CMBoundary                                     configurationmanager
Cmdlet          Set-CMBoundaryGroup                                configurationmanager

#creating the Boundary group
PS DEX:\> New-CMBoundaryGroup -Name "DexLabBG" -Description "Boundary Group for my LAB" -DefaultSiteCode DEX -Verbose
VERBOSE: Performing the operation "New" on target "BoundaryGroup: New".

CreatedBy       : DEXTER\Administrator
CreatedOn       : 3/14/2014 8:21:13 AM
DefaultSiteCode : DEX
Description     : Boundary Group for my LAB
GroupID         : 16777217
MemberCount     : 0
ModifiedBy      : DEXTER\Administrator
ModifiedOn      : 3/14/2014 8:21:13 AM
Name            : DexLabBG
Shared          : False
SiteSystemCount : 0

#Now add the Boundary to the Boundary Group
PS DEX:\> Add-CMBoundaryToGroup -Boundary (Get-CMBoundary ) -BoundaryGroup (Get-CMBoundaryGroup ) -Verbose
VERBOSE: Performing the operation "Add" on target "BoundaryToGroup: Name="DexLabBG"".

Everything done till the important piece here is to specify the content location for the above Boundary group, so that clients falling under these Boundaries (part of Boundary group) know where to look for content.

If you look at the ConfigMgr cmdlets there is no cmdlet which does that for you....I though Set-CMBoundaryGroup would do that but it doesn't apparently.

ConfigMgr MVP Kaido was kind enough to mention in comments that there does exist a cmdlet to add a SiteSystem (DP) to the Boundary Group named Add-CMSiteSystemToBoundaryGroup. Do check out his awesome ConfigMgr and PowerShell Script Library at

Though I would be going for the WMI way of doing this action.

Let's search for a class which matches something like *Boundary*
 Get-CimClass -ClassName *boundary* -ComputerName dexsccm -Namespace root/SMS/Site_DEX -Verbose

Now the output of the cmdlet..take a not of the Computername and NameSpace params  which specify the remote SCCM server and the appropriate SMS namespace.

Try epxloring all the classes above and you will come to know that we are going to use SMS_BoundaryGroup class from the above ones.

Note - If you don't want to specify the ComputerName and NameSpace parameter and its agruments every time you use the CIM Cmdlets then you can use the $PSDefaultParameterValues like below :
$PSDefaultParameterValues =@{"get-cimclass:namespace"="Root\SMS\site_DEX";"get-cimclass:computername"="DexSCCM";"get-cimInstance:computername"="DexSCCM";"get-ciminstance:namespace"="Root\SMS\site_DEX"}

If we expand the Methods on the SMS_BoundaryGroup class we will see one which will pique your interest.

Let's give this method a Shot...But wait we need something called ServerNALPath that needs to be passed to the Method.

 How do we get that ?? Don't loose hope yet..with PowerShell v3 it becomes very easy to search WMI Classes based on a property name...see me do it now.

Note - I have setup the $PSDefaultParameterValues so now the Get-CIMClass & Get-CIMInstance cmdlet will by default take the param Computername and Namespace values.

#search for WMI classes with a specific property name
PS DEX:\> Get-CimClass -PropertyName ServerNALPath

   NameSpace: Root/SMS/site_DEX

CimClassName                        CimClassMethods      CimClassProperties
------------                        ---------------      ------------------
SMS_DistributionPoint               {VerifyPackage, C... {BitsEnabled, IsPeerDP, IsProtected, ISVData...}
SMS_AllDistributionPoint            {}                   {ObjectTypeID, PackageID, PackageType, SecureObjectID...}
SMS_BoundaryGroupSiteSystems        {}                   {Flags, GroupID, ServerNALPath, SiteCode}
SMS_PackageStatusDistPointsSumma... {}                   {LastCopied, PackageID, PackageType, SecuredTypeID...}
SMS_TaskSequenceReferenceDps        {}                   {Hash, PackageID, ServerNALPath, SiteCode...}
SMS_TaskSequenceAppReferenceDps     {}                   {Hash, PackageID, ServerNALPath, SiteCode...}

Awesome ..Now I get a feeling we need to work with SMS_DistributionPoint class to get the value for property ServerNALPath. So here we go....I have only one DP in my LAB.

PS DEX:\> Get-CimInstance -ClassName SMS_DistributionPoint | select -Property ServerNALPath -Unique


Easy right......You need to know all the moving parts to work with PowerShell and ConfigMgr (using WMI) and that is exactly the kind of understanding I was looking for ......Ok enough with my talk..moving further

Let's give the AddSiteSystem Method of SMS_BoundaryGroup a shot  now:
Note I got the flag from this Technet I took a guess by the class name that the DP I add will become an instance of the class SMS_boundaryGroupsitesystems . All set

PS DEX:\> $BoundaryGroup = Get-CimInstance -ClassName SMS_BoundaryGroup
PS DEX:\> Invoke-CimMethod -InputObject $BoundaryGroup -MethodName AddSiteSystem -Arguments @{ServerNALPath = [string[]]
'["Display=\\\"]MSWNET:["SMS_SITE=DEX"]\\\'; Flags=([System.UInt32[]]0) } -Verbose
VERBOSE: Performing the operation "Invoke-CimMethod: AddSiteSystem" on target "SMS_BoundaryGroup (GroupID = 16777217)".
VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''instance' = SMS_BoundaryGroup (GroupID =
16777217),'methodName' = AddSiteSystem,'namespaceName' = root/sms/site_dex'.

                                                ReturnValue PSComputerName
                                                ----------- --------------
                                                          0 DexSCCM
VERBOSE: Operation 'Invoke CimMethod' complete. 

Voila that worked after few attempts. Would like to point out that based on the documentation of the class the arguments you pass to the method in above are type-cast to an array....If you have any doubts grasping that drop me a comment would take time to explain that.

So what we have achieved so far is evident from the Screenshots below:

#See the Boundary Group has a member Boundary added 

#See below that we now have the Content Location set for the Boundary group

Distribute the Config Mgr Client Package to the DP Group

Phewww!!! Now the last piece of Puzzle is to distribute the ConfigMgr Client Package to our DP Group.
#Distribute the Config Mgr Client Package to ensure that the Package resides there
Start-CMContentDistribution -PackageName "Configuration Manager Client Package" -DistributionPointGroupName "Dex LAB DP group"

Now we can see the changes reflect in the Console too.

In the above screenshot there is a way to  verify the integrity of the package on a Distribution Point. Let's do it using PowerShell ...the class which is of interest is SMS_DistributionPoint have a look at the methods on this one:
PS DEX:\> Get-CimClass -ClassName SMS_DistributionPoint |  Select -ExpandProperty Cimclassmethods

Name                                             ReturnType Parameters                    Qualifiers
----                                             ---------- ----------                    ----------
VerifyPackage                                        SInt32 {PackageId, NALPath}          {implemented, static}
CancelDistribution                                   SInt32 {PackageId, NALPath}          {implemented, static}

Note that the method exists on the SMS_DistributionPoint Class.
Look closely that we need NAL Path which can be found on the instance of the SMS_DistributionPoint (as every package that is stored in the DP is an instance of this class) so we are going to store it in a variable first and use it later.
PS DEX:\> $package = Get-CimInstance -ClassName SMS_DistributionPoint -Filter 'PackageID="DEX00002"'
PS DEX:\> Invoke-CimMethod -ClassName SMS_DistributionPoint -Name VerifyPackage -Arguments @{PackageID=$package.PackageI
D;NALPath=$package.ServerNALPath} -ComputerName dexsccm -Namespace root/sms/site_DEX -Verbose
VERBOSE: Performing the operation "Invoke-CimMethod: VerifyPackage" on target "SMS_DistributionPoint".
VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = VerifyPackage,'className' =
SMS_DistributionPoint,'namespaceName' = root/sms/site_DEX'.

                                                ReturnValue PSComputerName
                                                ----------- --------------
                                                          0 dexsccm
VERBOSE: Operation 'Invoke CimMethod' complete.

Returns 0 which means the integrity is validated.

So that's it we have successfully configured Client Push settings in our Environment.

For all the code samples below is the gist:

Till next post

Sunday, March 02, 2014

PowerShell + SCCM 2012 R2 : Adding Roles & Client Settings (Device & User)

Post #3 in the PowerShell with ConfigMgr 2012 series.

Trying to do the things mentioned in this post --> @Windows-Noob

Before I begin, I had an interesting discovery.
While trying out the Cmdlets in the ConfigurationManager Module, I had been facing a lot of in the Discovery Post few weeks ago was not able to use the -ActiveDirectoryContainer parameter. So had to resolve to WMI way.

When I was trying out things mentioned in this post with PowerShell I faced a lot of Errors and asked around, finally was able to get a workaround.

I have ConfigMgr 12 R2 Preview installed on the server and the Eval version is out. So I downloaded the ConfigMgr 12 R2 Eval version and just did a re-install of the ConfigMgr Admin Console on one of my machines.

Now the downside of this is the GUI won't connect to the Site Server because of the version mismatch but the PowerShell module works fine (this is what I was after). I still have old ConfigMgr Admin Console installed on the Site Server so it's just a workaround until I build my Lab again.

Step 1. Add the Application Catalog Web Service Point [LogFile : awebsvcMSI.log and Application Catalog Website Point Roles [LogFile : awebsctl.log]

Well I found out that the ConfigurationModule has two cmdlets Add-CMApplicationCatalogWebServicePoint and Add-CMApplicationCatalogWebsitePoint exactly for this task. So the code is below :

Step 2 : Configure Custom Client Device Settings

Now as per the Windows-Noob guide we will be adding the Custom Device Client setting and customizing the  Client Policy, Computer Agent and Software Updates. There are already cmdlets by the name New-CMClientSetting which can create Custom (Device/User) Client setting. Once this is done we can customize the settings using the Set-CMClientSetting cmdlet.

If you browse to the online help page for the Set-CMClientSetting page then you will notice that it applies to SCCM 2012 R2 and that to customize each setting we have a parameter set for the cmdlet, this will be evident from below screenshot:

Now walkthrough the Code :

#First create the New Client settings 

New-CMClientSetting -Name "Custom Client Device Settings" -Type Device -Description "Custom Client settings: following Windows-Noob --> DexterPOSH"

The above expression will create a new Device Client Settings:

Now start configuring various settings under these new Device Client Settings:

#Now start configuring the Client Policy, Computer Agent (skippingSoftware Updates setting for now)
Set-CMClientSetting -Name "Custom Client Device Settings" -PolicyPollingInterval 5  -EnableUserPolicyPolling $true -EnableUserPolicyOnInternet $false

# 2. Configure the Computer Agent
Set-CMClientSetting -Name "Custom Client Device Settings" -PowerShellExecutionPolicy Bypass -InitialReminderHoursInterval 48 -InterimReminderHoursInterval 4 -FinalReminderMinutesInterval 15  -PortalUrl "" -AddPortalToTrustedSiteList $true -AllowPortalToHaveElevatedTrust $true -BrandingTitle "Dexter's LAB" -InstallRestriction AllUsers -DisplayNewProgramNotification $true

Now once you run above cmdlet then you can see the changes reflecting in the console too:

Now you can go an verify individual settings you below screenshot is the Client Policy settings:

Skipping the Software Updates settings for now, not yet configured it in my Lab.

Step 3 : Deploy our Custom Device Client Settings

To deploy our Client settings we would be using : Start-CMClientSettingDeployment cmdlet.
Read the help of this and you will come to know that you need to supply the client settings specify by Name, Id or Object which you want to deploy to a Collection (specify Name, Id or Object)

Notice that before this the deployment tab for the settings is empty:

Now run the below cmdlet and see wonders happen:
Start-CMClientSettingDeployment -ClientSettingName "Custom Client Device Settings" -CollectionName "All Systems"

See the changes reflect in the ConfigMgr console (hit refresh a couple of times)

Step 4 : Configure Custom Client User Settings (WMI Way)

I wanted to do this using WMI/CIM just for I want to know what's happening in which class. So I started playing around figuring things.

First we need to get the class we want to work with it's SMS_ClientSettings WMI Class. Now we have to create a instance of it and pass to it require properties while doing so...for creating Client User Settings we have to pass the Type = 2.

#create the new instance of the Class pass to it relevant properties like Name, Type , Priority, Description
$ClientUserSetting = New-CimInstance -ClassName SMS_ClientSettings -Property @{Name="Custom Client User Settings";Type=2;Priority=2;Description="Custom Client User Settings -- WMI Way"} -Namespace root/sms/site_DEX -ComputerName DexSCCM -verbose

#Now we need to add the User Device Affinity to the class we created as one of the AgentConfgurations

$ClientUserSetting.AgentConfigurations += New-CimInstance -ClassName SMS_TargetingAgentConfig -Property @{AgentID=10;AllowUserAffinity=1} -Namespace root/sms/Site_DEX -ComputerName DexSCCM -Verbose

#Now once done set back the property to the ConfigMgr Server 

Set-CimInstance -InputObject $ClientUserSetting 
#deploy the above settings to the Collection
Start-CMClientSettingDeployment -ClientSettingName "Custom Client User Settings" -CollectionName "All Systems"

I tried using Set-CMClientSetting cmdlet to set the above User Device Affinity Settings but it behaved rather oddly...So I went for the WMI way as now I know by sure that the Cmdlets shipped with Configuration Manager may have bugs but the WMI way is the one which will bail you out at the end.

Finally, Have created a Gist for all the cmdlets used above.
That's it for today's post.