Saturday, April 19, 2014

PowerShell + SCCM 2012 R2: Get the Device/ User Info WMI Way

This is in continuation of my last post on Using PowerShell to get client information  
In this post will be exploring on the ways to do the same thing using WMI/CIM, Why ?
With WQL syntax we can ask for only the properties we need rather than getting everything back.

The post will cover 3 topics:
  1. Query individual Device in CM using WMI/CIM
  2. Query Members of a Device Collection using WMI/CIM
  3. Query Users in a User Collection using WMI/CIM
To get the number of properties got back from a call to Get-CMDevice use Get-Member and Measure-Object like below:

Most of the properties returned back may be empty depending on how you are using ConfigMgr e.g if you use endpoint protection than those attributes in the class will be populated else will be empty.

Now if you see the output of Get-CMDevice is actually is a result of the SMS_CombinedDeviceResources

Let me tell you a little secret :-$ ...if you are looking forward to explore WMI part of ConfigMgr and have a doubt on which Class to look for, then piping the output of the Cmdlets with the ConfigurationManager Module to Get-Member gives you a hint.

The cmdlets shipped with (written in C#) the Module essentially makes WMI calls and then expose the results back to us that's why IResultObject in the Typename (above screenshot) :-B

So let's explore a bit the SMS_CombinedDeviceResources class...using the Get-CIMClass cmdlet.

Note - will be using the $PSDefaultParameters as mentioned in my earlier posts to set the Computername and Namespace parameter to point to the ConfigMgr Server.

We can see a lot of properties defined in this class:

Query individual Device using WMI/CIM

To get this information for a client can be a simple WMI call where you specify a filter for the Name property like below:

Get-CimInstance -ClassName SMS_CombinedDeviceResources -Filter 'Name="DEXTERDC"'

But the good thing about using the WMI/ CIM cmdlets directly is that we can use the WQL query to select only the properties we want (see below):

Get-CimInstance -Query "Select Name,LastMPServerName,deviceos from SMS_CombinedDeviceResources where Name='DexterDC'"

Note that all the extra properties show up as blank (other than those specified with Select statement), this will save the network bandwidth and will be efficient when we are processing say hundreds or thousands of clients.

Query Members of a Device Collection using WMI/CIM

If you have read few of my earlier posts then you would know that all the collections we create are an instance of the class SMS_Collection. So let's go ahead and do a WMI query to get the object back for the "All Systems" collection.

 Get-CimInstance -ClassName SMS_Collection -Filter 'Name="All Systems"'

Now out of all the properties listed the one which stands aside is the property named 'MemberClassName'. If you query this Class mentioned in the property MemberClassName then you get back all the members of the collection..I think you figured that out already ;)

Below is the value of the property MemberClassName for my "All Systems" collection
PS> (Get-CimInstance -ClassName SMS_Collection -Filter 'Name="All Systems"').MemberClassName


So let's do a WMI Call for this class:

You can see it got all the machines which were member of the 'All Systems' Collection

Now using the WQL query to optimize this a bit :

Get-CimInstance -query 'Select Name,IsClient,LastMPServerName,DeviceOS from SMS_CM_RES_COLL_SMS00001' | select -Property Name,IsClient,LastMPServerName,DeviceOS

I pipe it to the Select-Object cmdlet to get the properties I wanted as rest every property is empty.

To give an idea on the efficiency part see below:

PS> Measure-Command {  Get-CMDeployment -CollectionName 'All Systems'}

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 1
Milliseconds      : 55
Ticks             : 10554314
TotalDays         : 1.22156412037037E-05
TotalHours        : 0.000293175388888889
TotalMinutes      : 0.0175905233333333
TotalSeconds      : 1.0554314
TotalMilliseconds : 1055.4314

PS> Measure-Command  {Get-CimInstance -query 'Select Name,IsClient,LastMPServerName,DeviceOS from SMS_CM_RES_COLL_SMS000
01' }

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 173
Ticks             : 1738179
TotalDays         : 2.01178125E-06
TotalHours        : 4.828275E-05
TotalMinutes      : 0.002896965
TotalSeconds      : 0.1738179
TotalMilliseconds : 173.8179

This is pretty explanatory right ! This would be pretty significant difference if there are a lot of members in the Collection.

Query Users in a User Collection using WMI/CIM

Now there are User Collections too so how do we query the members in it. Well easy the same way we did it with Device Collections. :P

Well the difference is in the type of collection:
PS> (Get-CimInstance -ClassName SMS_Collection -Filter 'Name="All Systems"').CollectionType
PS> (Get-CimInstance -ClassName SMS_Collection -Filter 'Name="All Users"').CollectionType
So if we wanted to get the members of the collection "All Users" then below is a one liner to do that:

Get-CimInstance -ClassName  (Get-CimInstance -ClassName SMS_Collection -Filter 'Name="All Users"').MemberClassName |

 Select Name,SMSID

You can see the Class to which a User in CM belongs is SMS_CombinedUserResources which has few properties defined in it. 

[UPDATE} I was writing a function which will do all of the above things shown for me..there is a switch by the name $PropertySelect (also when I tried using $SelectProperty it gave me an error saying that 'The parameter name "SelectProperty" is reserved for future use.' ) which will give an option to select properties in Out-Gridview Window and these properties will only be retrieved ..pretty cool B-)
That's it for today's post. :) :)

Thursday, April 10, 2014

PowerShell + SCCM 2012 R2: Get the Client Info Easy Way

One of my friend Krishna called and asked me on how to get the report for the  Management Point to which SCCM clients (part of a Collection) report to.

This is normal day to day task in a ConfigMgr Admin's life and most of us would go and use Reports in Configuration Manager . But he wanted to do this using PowerShell...... hmmm :-?  Now you are talking :-b

If you are using Configuration Manager 2012 and haven't started using PowerShell then you as missing one of the Coolest things that can make your job easy.

In ConfigMgr 2007 we had an option to export the list of members from a Collection , see below:

This was convenient but sadly it's not there now.

But we can do a lot more if we choose to use PowerShell here.

This is not a very complex thing to do if you are comfortable with PowerShell, so I am going to do it 2 ways:

  1. Easy Way : using cmdlet Get-CMDevice
  2. WMI Way :P (next Post)

 Easy Way : using cmdlet Get-CMDevice

On your ConfigMgr Console top left corner Click and select "Connect via Windows PowerShell" (this is the last time am gonna put this in my post :) )

After you have your Console opened the ConfigurationManager PowerShell module will be loaded and you can now use the cmdlet Get-CMDevice. Note - When the console opens you will be placed in the drive named after your SiteCode something like DEX:\ in my case. If we want to run cmdlets shipped with ConfigMgr then that we need to do from inside this PSProvider.

Go ahead and open the help for the cmdlet and see that there are a lot of parameters and parameter sets but looking for the marked one here:

Well you can use this cmdlet in a lot many ways but as per my requirement I want to get the Devices in a specific collection and generate report. 

Let me take the "All Systems" as the collection here as I don't have many machines in my LAB. Below is a shot of my current members of it:

Now you can see I have already gone ahead and added the Management Point and Operating System property to the Column view (just have to right click on it and select these).

So below cmdlet will give you information about all the devices, the properties are overwhelming in number so going to save it in a variable here:

$Members = Get-CMDevice -CollectionName "All Systems"

To know what all properties the members of the Collection have, we can use :

$Members | Get-Member -MemberType Property

If you scroll down enough you will see there is a property named lastmpservername and deviceos , so go ahead and do this

$Members | select name,lastmpservername,deviceos

But strangely enough nothing gets returned back, Why ? What am I doing wrong here ?  :-/

Well I had mentioned in my first post that the Property references in here are case-sensitive and I don't know why it is the way it is. #-o

Work Around is put the property names exactly as they appear when we did this $Members | Get-Member  (Note below the proper case of the Property Names)

This issue has been fixed in the new R2 CU1  :-$

Now back to what we are doing. With PowerShell there are lot of things which you can do like sort on basis of LastMPServerName property and then group the devices having same LastMPServerName property :

Endless possibilities -- You can create a CSV of this report, an HTML report etc.

The only problem with this cmdlet is it doesn't allow me to specify a subset of the properties I want back, it just gives me back the whole set. So if there are lot of members in a collection it might take a while to run the cmdlet. Here is where the WMI way will be a bit efficient.

Using WMI/CIM we can specify the properties we want back using the WQL Query hence more efficient. I will be showing this in the next post as I have observed my posts length is increasing day by day...need to break it in pieces.

Till then /bye

Monday, April 07, 2014

PowerShell + SCCM 2012 R2 : Deploy PowerShell Scripts

[UPDATE] Looking for more #PowerShell + #ConfigMgr awesomeness , Check out my compiled list of posts -->

Earlier I blogged about using PowerShell to create an Application from MSI but wait am just warming up to what Configuration Manager has to offer with PowerShell :D
In this post we will be creating an Application having a deployment type which will run PowerShell Scripts (Install / Uninstall). Detect if the Script needs to be run on the Client (using PowerShell code) this is something called Script Detection Method in ConfigMgr.

Let me put this in simple layman terms we will be deploying Scripts to clients and executing them using Configuration Manager Infrastructure.


If you haven't configured Virtual Memory (Page File Size) manually then the System manages it for you. So is the case with all the Systems in my Lab.
So I want to deploy a PS1 Script which will set this (install) and another PS1 Script which will revert this setting to System managed if required (uninstall).

Well that's not all.....just to be clear will be doing all of this using PowerShell (Aah! you knew that already :P !) So let's get the party started <:-p<:-p <:-p <:-p

The Application creation & deployment process will follow the same Steps of :

  1. Creating the Application
  2. Adding a Deployment Type
  3. Distributing the Content to the DP Group
  4. Creating a Collection & Add members to it
  5. Deploy the Application to the Collection

The PowerShell Script:

Well I already have the Script from Technet Gallery which serves my requirement (why re-invent the wheel, right ?? ) It's a good idea to search Technet Gallery for any Scripts before you start, maybe you won't find the exact one but it will serve as a good starting point.

This Script is a PowerShell Module which has 2 functions defined inside it.
Set-OSCVirtualMemory and Set-PageFileSize . 

One needs to test the Scripts before using them in Applications because it may not work sometime. I have tested it locally on my Machine and after knowing that it works am ready to setup an Application and finally deploy it. 

By default the OS manages the PageFile for the System which is evident by the property AutomaticManagedPageFile on WMI Class Win32_ComputerSystem

Below is what I see on my machine right now which means the System manages page file on my machine (DexterDC).

PS> (Get-CimInstance -ClassName Win32_ComputerSystem).AutomaticManagedPageFile

The Script takes above into account disables it and sets the PageFile Size which you specify and also later you can revert back to the setting where System manages it.

This is a cool script and if you are looking for something similar to do in your environment then please go through it , I am sure you will learn quite a few bits from it. I know I did :)

Apart from the Module I need 2 more Scripts to Set the VM size and Unset it. I know I could have copied functions from the Module in these 2 PS1 and gone that way too. But let's stick with the Module Approach. Store all three (2 PS1 + 1 PSM1 files in a Shared path probably where all your Packages go in SCMM )

Second file : Unset_VM_size.ps1

Create Application

Now once we have the Scripts & the Module ready. Let's go ahead with the Application creation. We will go with the same steps which were taken to create a MSI application ....but we will have to specify a lot of information manually here in this case.

Create the Application Object

New-CMApplication -Name "PowerShell Set PageFile" -Description "The Script will set the  Page File Size to Initial 1024 and 2048 maximum" -SoftwareVersion "1.0" -AutoInstall $true 

This will create the Application you can set whole lot of properties on this Object using Set-CMApplication cmdlet. I am gonna move forward. For sake of convenience let me store the Application name in a variable do this:
$Applicationname = "PowerShell Set PageFile"

Now with ConfigMgr 2012 we have various detection methods which detect if the application is already installed. In this case let's define a here-string containing our PS1 code which detects if the Virtual Memory is already configured as per our needs. Before you go on and criticize usage of write-host see this post here .

$scriptDetection = @'
$pagefile = Get-WmiObject -Class Win32_PageFileSetting
$AutomaticManagePageFile = Get-WmiObject -Class Win32_ComputerSystem 
if (($pagefile.InitialSize -eq 1024) -and ($pagefile.MaximumSize -eq 2048) -and ($AutomaticManagePageFile.AutomaticManagedPagefile -eq $false))
    Write-host "Installed"


So now we have a Script code which will detect if the PowerShell Script which we deploy is at all needed to run on the machine to configure the Virtual Memory.

After this let's create the deployment type, it's a very big expression so am gonna use splatting. If you don't know about splatting then you have to do some reading :-B read the about_Splatting article in PowerShell.

Below is my splatting variable  and the Key Values come from the various parameter names in the cmdlet Add-CMDeployment type ...see the comments in the same line too which attempt to explain it.
Note - the above $ScriptDetection variable is used below.

$DeploymentTypeHash = @{
                    ManualSpecifyDeploymentType = $true #Yes we are going to manually specify the Deployment type
                    Applicationname = "$ApplicationName" #Application Name 
                    DeploymentTypeName = "POSH Set PageFile"    #Name given to the Deployment Type
                    DetectDeploymentTypeByCustomScript = $true # Yes deployment type will use a custom script to detect the presence of this 
                    ScriptInstaller = $true # Yes this is a Script Installer
                    ScriptType = 'PowerShell' # yep we will use PowerShell Script
                    ScriptContent =$scriptDetection  # Use the earlier defined here string
                    AdministratorComment = "This will set and reset the VM(pagefile) size" 
                    ContentLocation = "\\dexsccm\Packages\PS1_SetVMSize"  # NAL path to the package
                    InstallationProgram ='powershell.exe -file ".\Set_VM_Size.ps1"'  #Command line to Run for install
                    UninstallProgram ='powershell.exe -file ".\Unset_VM_size.ps1"'  #Command line to Run for un-Install
                    RequiresUserInteraction = $false  #Don't let User interact with this
                    InstallationBehaviorType = 'InstallForSystem' # Targeting Devices here
                    InstallationProgramVisibility = 'Hidden'  # Hide the PowerShell Console

Is it me or these are a lot of parameters /sweat 

Once we have parameters Now time to execute the code...we will use our Splatting variable here:

Add-CMDeploymentType @DeploymentTypeHash 

Now after this you should be seeing a Application created in the ConfigMgr Console and the deployment type too.

We can go ahead and verify all the properties in the ConfigMgr Console but to show why we used here-string above to set up the Script detection method earlier, below is a Screenshot showing the Detection Method for the Deployment Type. 
When we click on the "Edit" button the Script Editor opens up which has our code  B-)

After this let me move this Application under the PowerShell folder. You can read my earlier post on using Folders to organize in SCCM on how this works.

$Application  = Get-CMApplication -Name $Applicationname 
$POSHFolder = Get-CimInstance -ClassName "SMS_ObjectContainerNode" -Filter "Name='PowerShell'" -ComputerName DexSCCM -Namespace root/SMS/Site_DEX -Verbose

Invoke-CimMethod -ClassName SMS_ObjectContainerItem -MethodName MoveMembersEx -Arguments @{InstanceKeys=[string[]]$Application.ModelName;ContainerNodeID=[System.UInt32]0;TargetContainerNodeID=[System.UInt32]($POSHFolder.ContainerNodeID);ObjectTypeName="SMS_ApplicationLatest"} -Namespace root/sms/site_DEX -ComputerName DexSCCM -Verbose
So now we are ready with our Application and the deployment type, let's distribute the content to the Boundary Group in my LAB.

#Distribute the Content to the DP Group
Start-CMContentDistribution -ApplicationName "$ApplicationName" -DistributionPointGroupName "Dex LAB DP group" -Verbose

After this let me create a Device Collection to hold the Members for the deployment of this Application.

#create the Device Collection

New-CMDeviceCollection -Name "$ApplicationName" -Comment "All the Machines where $ApplicationName is sent to" -LimitingCollectionName "All Systems"  -RefreshType Periodic -RefreshSchedule (New-CMSchedule -Start (get-date) -RecurInterval Days -RecurCount 7) 

Now to stress the importance of Folders a bit in ConfigMgr, I had gone and created Folders below the "Device Collections" using PowerShell ;) to reflect the same structure as that under Applications making it easy for me :


You can move the newly created Device Collection under the PowerShell folder too to be consistent with what we are doing.

Enough :D Let's add a member to the Collection and deploy the Application to it.

#Add the Direct Membership Rule to add a Resource as a member to the Collection
Add-CMDeviceCollectionDirectMembershipRule -CollectionName "$ApplicationName"  -Resource (Get-CMDevice -Name "DexterDC") -Verbose

#start the Deployment
Start-CMApplicationDeployment -CollectionName "$ApplicationName" -Name "$ApplicationName" -DeployAction Install -DeployPurpose Available -UserNotification DisplayAll -AvaliableDate (get-date) -AvaliableTime (get-date) -TimeBaseOn LocalTime  -Verbose

After this wait for some time or refresh machine policy on the client (can use Invoke-CMClientNotification cmdlet ) to see the Application appear up in the Software Center.

But even after waiting for a while it doesn't shows up then..... ???

There's a gotcha here if you haven't set the Execution Policy (for PowerShell) in the Client Settings then you would see something like this in the AppDiscovery.log on the Client side.

Bottom line is-->  To use PowerShell Script as a detection method we need to have deployed proper Client Settings on the Clients to set the Execution Policy.

So you should either create a Custom Client settings and deploy it to the Collection with the Members where the Application using Script Detection will be used or change your default client settings (not advised). 
In ConfigMgr 2012 you can create Custom Client Settings and deploy those to Collections. I did blog about doing that with PowerShell too Check it out  Here

So once the appropriate Client settings are deployed to the Client, you need to initiate the Application manager Machine Policy Action so that the Application gets discovered. You can do this with PowerShell to on the Client:

Invoke-CimMethod -ClassName SMS_Client -MethodName TriggerSchedule -Arguments @{sScheduleID='{00000000-0000-0000-0000-000000000121}'}  -Namespace root/ccm

After this you will see in the AppDiscovery.log that the Script Detection works fine and the application shows up in Software Center

Click on Install and it gets installed.

Let's verify that the System doesn't manage Page file anymore and the Page File Settings did get changed.

Note : after Clicking on Install you can see the AppEnforce.log for some interesting entries ;)

That's it for the first part. Will be back with more awesomeness in next one.

/wahaha /bye

Tuesday, April 01, 2014

Remotely Set State and StartMode of a Service using PowerShell

Well as the name suggests this is a very simple and straightforward post.
Most of the PowerShell Geeks out there already know that we can do it in various ways:
  1. Using Set-Service
  2. Using PSRemoting
  3. Using WMI (Win32_Service)
In our environment at office we don't have PSRemoting yet enabled, but it's okey we can come around that. In addition would like it to be PowerShell v2 compatible you never know who asks you for this Script running on v2.

We can use Set-Service which has -ComputerName parameter, in case you haven't checked. But it fails to start/ stop a remote service if there are dependent services.

C:\> gsv -Name bits -ComputerName DexterClient1

Status   Name               DisplayName
------   ----               -----------
Running  bits               Background Intelligent Transfer Ser...

C:\> Set-Service -Name BITS -Status Stopped -ComputerName DexterClient1 -Verbose
VERBOSE: Performing the operation "Set-Service" on target "Background Intelligent Transfer Service (BITS)".
Set-Service : Cannot stop service 'Background Intelligent Transfer Service (BITS)' because it has dependent services.
At line:1 char:1
+ Set-Service -Name BITS -Status Stopped -ComputerName DexterClient1 -Verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.ServiceProcess.ServiceController:ServiceController) [Set-Servi
   ce], ServiceCommandException
    + FullyQualifiedErrorId : ServiceHasDependentServicesNoForce,Microsoft.PowerShell.Commands.SetServiceCommand

So it goes out of the window as there is no way to force it too (-Force switch missing)

So using WMI is a better option here.

This Script was inspired by :
  1. Set-ServiceStartMode Script by Jason Morgan here 
  2. Article by Sriram Pamarthi here 
I tweaked the Script by Jason Morgan to start/ stop the Service in addition to setting up the desired start mode and did use the pointers in Sriram's blog to get interpret the WMI Return codes properly when invoking the ChangeStartMode() and StartService() or StopService() method on the instance of Win32_Service WMI Class Object. He was kind enough to give a second opinion too. Thanks :)

The Script logs any offline machines or any Exceptions thrown and will display any warnings if it's not able to set State or StartMode. we can use re-direction probably to capture the Errors if any though or modify the Script to fit one's need.

I have uploaded the Script on Technet and POSHCode too:
Give it a shot and let me know how I can improve this further. Below is the Gist:

PowerShell + SCCM 2012 R2 : Create Folders to organize

In the last post we took a look on how to create an Application from a MSI and deploy it using PowerShell :)

But if I take a look at the Application Root node at the moment everything gets listed here . Say if I have 200 applications then it seems a bit messy...Let's organize them in a better way using "Folders" in ConfigMgr 2012.

Applications Node:

Folders are used to better organize Objects and it can have Objects of one type only. We will come to appreciate this when we see the end result.

How do we create Folders ?
Well there are a couple of ways I know of:
  1. Manual through ConfigMgr Console
  2. Using PowerShell CMSite PSProvider (exposed through the ConfigMgr Module)
  3. Using WMI

Manual Way:

I would like to organize the Apps based on the Vendor, so that Apps from same vendor are easy to locate. For Ex: Quest AD Snapin will fall under "Quest" 7-zip will fall under "OpenSource" etc.

So go to the Applications node in the console and right-click it to get an option to create folder. Give it the name "Quest"

Give the name "Quest" and the folder is created

Now I was tempted to drag and drop the Application to the Folder but that is not supported as of now (I wonder why not). So go to your Application and right click Move and select the Folder where you want the Application moved.

Select the folder location


Note that once you move the Application it won't show up under the Root node but instead under the Folder you moved it.

Using PowerShell CMSite PSProvider 

When you click on "Connect via Windows PowerShell" in the ConfigMgr Console then you get a PowerShell Console. By default you are placed inside the CMSite location (same as the sitecode). One needs to run the Cmdlets shipped in with the PowerShell Module from this location only. Try doing a "ls" or Get-ChildItem there, you would see something familiar.

Once you are placed in the PowerShell Console you can traverse the CMSite provider using the same cmdlet you would have used to manage filesystem, registry etc. Note that some of the functionality might not be provided by the CMSite provider.. for ex. do a Get-ChildItem -Recurse

We were talking about creating folders, right.
So move under Applications node by using Set-Location DEX:\Application or cd .\Application.

Now we can create folders here in the same way we are used to using New-Item or Mkdir alias

Above have created a folder named "OpenSource" where the 7-zip application will be moved.

To move an application to the folder we just created we have a cmdlet named Move-CMObject, it takes the FolderPath and ObjectID or InputObject as a parameter...its straightforward if you go through the help for it.

Let's move it then using below:

$7zipApplication = Get-CMApplication -Name "7-zip"
Move-CMObject -FolderPath .\OpenSource -InputObject $7zipApplication -Verbose

See the Change in the Console:


Note - Only folders show up when you do a Get-ChildItem.

Using WMI

Using WMI to do this operation may be a perfect candidate when you don't want to depend on the ConfigMgr Console or the PowerShell Module shipped with it. This could be done from any machine with PowerShell installed on it.

I always use $PSDefaultparameters in my WMI Exploration ventures as it reduces passing Comuptername and namespace again and again:

$PSDefaultParameterValues =@{"get-cimclass:namespace"="Root\SMS\site_DEX";"get-cimclass:computername"="DexSCCM";"get-cimInstance:computername"="DexSCCM";"get-ciminstance:namespace"="Root\SMS\site_DEX";"get-wmiobject:namespace"="Root\SMS\site_DEX";"get-WMIObject:computername"="DexSCCM"}

First we need to get the class, something tells me it has something to do with the name container in the classname ;)
PS> Get-CimClass -ClassName *container*

The SMS_ObjectContainerNode looks like the one, Now if you go to the MSDN documentaion and look for the possible values for Objecttype you will come to know which all things an object can contain which in turn tells which all places folders can be used for Organizing stuff 

PS> Get-CimInstance -ClassName SMS_ObjectContainerNode

Notice for the Folders/ Containers we created to contain applications the MSDN documentation is not up to date. The objecttype for these type of folders is value 6000

These are the first 2 folders created in my environment . Could have filtered based on ObjectType too.

Now read the documentation and try to create the new Instance of this class using New-CIMInstance cmdlet 
$POSHFolder = New-CimInstance -ClassName SMS_ObjectContainerNode -Property @{Name="PowerShell";ObjectType=6000;ParentContainerNodeid=0;SourceSite="DEX"} -Namespace root/sms/site_DEX -ComputerName DexSCCM -Verbose

Note - ObjectType = 6000 tells it that it is a folder which will contain the Applications, ParentContainerNodeId = 0 tells it to create this folder under the Root (for Applications) can specify another Container ID to create nested folders. Stored it in a Folder as we will need it later.

Folder/ Container is created, Now time to move the Application by name "PowerShell Community Extensions" under it.

Yeah I know we can use the Move-CMObject now once the Folder is created....But we are doing it WMI way remember.

In the above Screenshot where we searched for WMI Classes with string container in can see a class by the name SMS_ObjectContainerItem , this class is our clue as it has 2 methods by the name MoveMembers & MoveMembersEx . Now if you go to the MSDN documentation it is not updated and for both it shows the same MOF definition. But we have another Tool sitting right in front of us.....any Guess on what it is ???

PowerShell ;)

Get-CimClass -ClassName SMS_ObjectContainerItem | select -ExpandProperty CimClassMethods | Format-List
This will show up the below:

As mentioned in the Screenshot the qualifier for the MoveMembers Static Method on the Class suggests its deprecated.

Let's go ahead and use the MoveMembersEx method and take note of the Parameters we are required to pass to this method - InstanceKeys , ContainerNodeId, TargetContainerNodeId, ObjectTypeName

But let's take a look at the before Screenshot of the ConfigMgr Console:

The PSCX Applicaiton shows up under the Root of Applications and the folder PowerShell created is empty.

After invoking the MoveMembersEx Method on the Class in below way:

$Application  = Get-CMApplication -Name "PowerShell Community Extensions"
Invoke-CimMethod -ClassName SMS_ObjectContainerItem -MethodName MoveMembersEx -Arguments @{InstanceKeys=[string[]]$Application.ModelName;ContainerNodeID=[System.UInt32]0;TargetContainerNodeID=[System.UInt32]($POSHFolder.ContainerNodeID);ObjectTypeName="SMS_ApplicationLatest"} -Namespace root/sms/site_DEX -ComputerName DexSCCM -Verbose

Note : In the above the Instance Keys and ObjectTypename values will change depending on what type of Objects Folder can contain.

The above should return a 0 value indicating success, if not then keep trying and cast the arguments passed so that it succeeds....I did it this way ;)

Now let's head back to the ConfigMgr Console Refresh the view and see the changes:

Now the important thing to note here is that we can create Folders in various places in the Console but a folder can contain Objects of a specific type only.

For Ex in the above case we created folders under Applications and they can only contain Applications inside them. Similarly we can organize collections, task sequences etc.

Folders are great way to organize stuff in ConfigMgr 2012, so go ahead and try the Fun route of creating these using PowerShell.

That's it for this post.
Till next post.