Get the folder location of an object in ConfigMgr 2012 via PowerShell

This blog post will be about a question that I recently got with a big enterprise customer. They use many folders throughout the console to store their different objects and are able to find these objects by using the search option in the console. One thing the console doesn’t show, with the search results, is the folder location of the specific object. That folder location would make life a lot easier when adding packages to a task sequence.

Luckily, I already created once a small PowerShell function that does exactly that. It outputs the folder location of an object. This blog post will explain that PowerShell function and shows the required input per object type.

>> The complete function is available via download here on the TechNet Galleries! <<

Step 1: Get and set object folder

The first step, like with every other script, is getting and setting variables to work with. In this case, the first step is also the outline for the rest of the function. A good thing to know is that all folder information is stored in two classes in WMI, SMS_ObjectContainerNode and SMS_ObjectContainerItem. These two classes are the same for every object type. A simple join of these two classes will provide the information about the folder that the object is in. If this doesn’t return any information it means the object is in the root. The next steps will be a part of the if statement and are necessary to determine if there are any parent folders. This all together brings me to the following code (outline):

$ContainerNode = Get-WmiObject -Namespace root/SMS/site_$($SiteCode)` -ComputerName $SiteServer -Query "SELECT ocn.* FROM ` SMS_ObjectContainerNode AS ocn JOIN SMS_ObjectContainerItem ` AS oci ON ocn.ContainerNodeID=oci.ContainerNodeID WHERE ` oci.InstanceKey='$InstanceKey'" if ($ContainerNode -ne $null) { $ObjectFolder = $ContainerNode.Name {...Step 2 ... Step 3...} $ObjectFolder = "Root\" + $ObjectFolder Write-Output $ObjectFolder } else { $ObjectFolder = "Root" Write-Output $ObjectFolder }

Step 2: Decide if object folder has parent

The second step is to decide if the folder, that the object is in, has any parent folders. This can be simply done by checking the ParentContainerNodeID property. This property is set to 0 when there’s no parent folder. In case there is a parent folder, this property can be used to determine the complete path. This brings me to the following if-statement:

if ($ContainerNode.ParentContainerNodeID -eq 0) { $ParentFolder = $false } else { $ParentFolder = $true $ParentContainerNodeID = $ContainerNode.ParentContainerNodeID }

Step 3: Update object folder

The third step, and last, step is to find the name of the parent folder. Also, very important, this step is used to determine if the parent folder has a parent folder. This can be simply done by looping constantly until there’s no parent folder any more. In that case, the loop can stop and the complete path can be determined. This brings me to the following while-statement:

while ($ParentFolder -eq $true) { $ParentContainerNode = Get-WmiObject -Namespace ` root/SMS/site_$($SiteCode) -ComputerName $SiteServer ` -Query "SELECT * FROM SMS_ObjectContainerNode ` WHERE ContainerNodeID = '$ParentContainerNodeID'" $ObjectFolder = $ParentContainerNode.Name + "\" + $ObjectFolder if ($ParentContainerNode.ParentContainerNodeID -eq 0) { $ParentFolder = $false } else { $ParentContainerNodeID = ` $ParentContainerNode.ParentContainerNodeID } }

Extra information

Usually I like to end my posts with an example, but in this case I think it’s better to end with the different properties that are required as input for this function. Even though the function works with every object type, it differs per object type which property is required as input. The following table provides an overview, per object, which class and property contain the required information.

Object Class Property
Application SMS_ApplicationLatest ModelName
Package SMS_Package PackageID
Query SMS_Query QueryID
Software Metering Rule SMS_MeteredProductRule SecurityKey
Configuration Item SMS_ConfigurationItemLatest ModelName
Configuration Baseline SMS_ConfigurationBaselineInfo ModelName
Operating System Image SMS_OperatingSystemInstallPackage PackageID
Operating System Package SMS_ImagePackage PackageID
User State Migration SMS_StateMigration MigrationID
Boot Image SMS_BootImagePackage PackageID
Task Sequence SMS_TaskSequencePackage PackageID
Driver Package SMS_DriverPackage PackageID
Driver SMS_Driver ModelName
Software Update SMS_SoftwareUpdate ModelName
Collection SMS_Collection CollectionID

11 thoughts on “Get the folder location of an object in ConfigMgr 2012 via PowerShell”

  1. Hi Peter,

    First of all: Great post! I really like your way of writing the posts: Code + Information ==> Great job!
    I have just tried this code and in my case, it seems that it isn’t working correctly. Maybe you can help me troubleshoot this problem?

    I use the code at the bottom of this post to list all collections which have “Incremental Updates” enabled. The code works great untill it reaches the 2nd collection which has parent folders (except the root folder)

    After some testing, I came to the following conclusion: The script always keep looping in the following section “$ParentContainerNodeID = $ParentContainerNode.ParentContainerNodeID” and this for the 2nd time that it goes through this section. Appareantly “$ParentContainerNodeID” is empty so it keeps on looping..

    Is it possible to take a look into this? Thanks in advance!!

    Code:

    Param (
    [Parameter(Position=0)][string]$SiteServer
    )

    Function Get-SiteCode
    {
    $wqlQuery = “SELECT * FROM SMS_ProviderLocation”
    $a = Get-WmiObject -Query $wqlQuery -Namespace “root\sms” -ComputerName $SiteServer
    $a | ForEach-Object {
    if($_.ProviderForLocalSite)
    {
    $script:SiteCode = $_.SiteCode
    }
    }
    return $SiteCode
    }

    $SiteCode = Get-SiteCode

    function Get-ObjectLocation {
    param (
    [string]$InstanceKey
    )

    $ContainerNode = Get-WmiObject -Namespace root/SMS/site_$($SiteCode) -ComputerName $SiteServer -Query “SELECT ocn.* FROM SMS_ObjectContainerNode AS ocn JOIN SMS_ObjectContainerItem AS oci ON ocn.ContainerNodeID=oci.ContainerNodeID WHERE oci.InstanceKey=’$InstanceKey'”
    if ($ContainerNode -ne $null) {
    $ObjectFolder = $ContainerNode.Name
    if ($ContainerNode.ParentContainerNodeID -eq 0) {
    $ParentFolder = $false
    }
    else {
    $ParentFolder = $true
    $ParentContainerNodeID = $ContainerNode.ParentContainerNodeID
    }
    while ($ParentFolder -eq $true) {
    $ParentContainerNode = Get-WmiObject -Namespace root/SMS/site_$($SiteCode) -ComputerName $SiteServer -Query “SELECT * FROM SMS_ObjectContainerNode WHERE ContainerNodeID = ‘$ParentContainerNodeID'”
    $ObjectFolder = $ParentContainerNode.Name + “\” + $ObjectFolder
    if ($ParentContainerNode.ParentContainerNodeID -eq 0) {
    $ParentFolder = $false
    }
    else {
    $ParentContainerNodeID = $ParentContainerNode.ParentContainerNodeID
    }
    }
    $ObjectFolder = “Root\” + $ObjectFolder
    Write-Output $ObjectFolder
    }
    else {
    $ObjectFolder = “Root”
    Write-Output $ObjectFolder
    }
    }

    ##### Entry Point #####

    $d = 0
    $CountIncremental = 0

    # Get list of all collections on a given SiteServer for the given SiteCode
    $collectionQuery = Get-WmiObject `
    -Namespace “root\sms\Site_$SiteCode” `
    -Query “select Name,CollectionID,LastChangeTime,LastMemberChangeTime,LastRefreshTime from SMS_Collection where collectionid like’$SiteCode%'” `
    -ComputerName “$SiteServer”

    # Loop to process each collection
    foreach ($collection in $collectionQuery) {
    $d++
    Write-Progress -Activity “Processing Collections” `
    -Status “Processed: $d of $($collectionQuery.count) ” `
    -PercentComplete (($d / $collectionQuery.Count)*100)

    $coll = [wmi] $collection.__Path
    if ($coll.RefreshType -eq 6 -Or $coll.RefreshType -eq 4) {
    $ObjectLocation = Get-ObjectLocation -InstanceKey $coll.CollectionID
    write-host $ObjectLocation “`t” $coll.CollectionID “`t” $coll.Name
    $CountIncremental++
    }
    }

    write-host “Count Total Collections: $($collectionQuery.count)”
    write-host “Count Incremental: $CountIncremental”

    Reply
    • Hi Sven,
      I think you’re running into an issue in which the same InstanceKey exists for multiple objects. Like a Package ID that corresponds with a Collection ID. To work with that you can add an extra filter to the query used for the $ContainterNode variable. When you’re looking for collections you can add something like oci.ObjectTypeName=’SMS_Collection_Device’.
      Peter

      Reply
  2. Great stuff, works straight out of the box!

    I was surprised how MS missed such a simple thing.

    Thank you.

    Reply
  3. Hi Peter thank you to saved me hours of searching in the SCCM console 🙂
    I’ve also ran into to issue of duplicate instanceKey , I think you should add the fix in the article itself for better readability.
    Kind regards

    Reply
  4. function Get-ObjectLocation {
    param (
    [string]$Sitecode,
    [string]$SiteServer,
    [string]$InstanceKey
    )
    $ContainerNodes = Get-WmiObject -Namespace root/SMS/site_$($SiteCode) -ComputerName $SiteServer -Query “SELECT ocn.* FROM SMS_ObjectContainerNode AS ocn JOIN SMS_ObjectContainerItem AS oci ON ocn.ContainerNodeID=oci.ContainerNodeID WHERE oci.InstanceKey=’$InstanceKey'”
    foreach ($ContainerNode in $ContainerNodes){
    $ObjectFolder =””
    if ($ContainerNode.ObjectType -eq 20){
    $ObjectFolder = $ContainerNode.Name
    if ($ContainerNode.ParentContainerNodeID -eq 0) {
    $ParentFolder = $false
    }
    else {
    $ParentFolder = $true
    $ParentContainerNodeID = $ContainerNode.ParentContainerNodeID
    }
    while ($ParentFolder -eq $true) {
    $ParentContainerNode = Get-WmiObject -Namespace root/SMS/site_$($SiteCode) -ComputerName $SiteServer -Query “SELECT * FROM SMS_ObjectContainerNode WHERE ContainerNodeID = ‘$ParentContainerNodeID'”
    $ObjectFolder = $ParentContainerNode.Name + “\” + $ObjectFolder
    if ($ParentContainerNode.ParentContainerNodeID -eq 0) {
    $ParentFolder = $false
    }
    else {
    $ParentContainerNodeID = $ParentContainerNode.ParentContainerNodeID
    }
    }
    $ObjectFolder = “Root\” + $ObjectFolder
    Write-Output $ObjectFolder
    }

    }
    }

    And if you want get all the location of the task, for example to make a copy of the production task you can do it in this wall, i don’t write the export-cmtaskSequence

    $TS_PackagesID = Get-CMTaskSequence | Select-Object PackageID
    foreach ($TS_PackageID in $TS_PackagesID){Get-ObjectLocation -Sitecode XXX -SiteServer XXX -InstanceKey $TS_PackageID.PackageID}

    Reply
  5. HI Peter,

    I got multiple objects back from query “SELECT ocn.* FROM SMS_ObjectContainerNode AS ocn JOIN SMS_ObjectContainerItem AS oci ON ocn.ContainerNodeID=oci.ContainerNodeID WHERE oci.InstanceKey=’$InstanceKey’” for one collectionID

    I added the object type as follows now it seems to work:

    SELECT ocn.* FROM SMS_ObjectContainerNode AS ocn JOIN SMS_ObjectContainerItem AS oci ON ocn.ContainerNodeID=oci.ContainerNodeID WHERE oci.InstanceKey=’$InstanceKey’ and ocn.ObjectType=5000

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.