Skip to main content

Converting Configuration Manager Direct Membership Collections

·
Microsoft Configuration Manager Windows 10 and later Collections PowerShell Administration Automation
Author
Nick Benton
Principal Cloud Endpoint Consultant | Intune Blogger
Table of Contents

You may be using Direct Membership Rules in your Microsoft Configuration Manager environment, but should you really for critical production collections?

No is the answer, there I said it. Mainly because they require actual effort and overhead to maintain, and secondly because there have been times where these memberships just plain disappear, for many different reasons, but primarily if the ConfigMgr Client is reinstalled on the device.

So what do we do? Aggressive PowerShell scripts obviously.

The Approach
#

We’ll need to connect to the Configuration Manager PowerShell module, which is easy enough from the Primary Site server, then carry out the following actions:

  • Get the Device Collections with Direct Members
  • Capture the Direct Members
  • Create a new Query Membership Rule with the members
  • Remove the existing Direct Members
  • Logging?

So let’s get at it…

Connecting via PowerShell
#

From the Configuration Manager server we can leverage the PowerShell Module, import it and then connect to the PSDrive where we get to run all the Configuration Manager commands

Import-module ($Env:SMS_ADMIN_UI_PATH.Substring(0, $Env:SMS_ADMIN_UI_PATH.Length - 5) + '\ConfigurationManager.psd1')
$SiteCode = Get-PSDrive -PSProvider CMSITE
Set-location $SiteCode":"

Getting the Collections
#

I thought I’d be kind and give a couple of options to the Collection gathering, attack all of the Device Collections or, which is probably more sensible, bring up a list of collections to work with.

We’re doing this through a selection option at the start of the script and using Get-CMDeviceCollection:

if ($Choice_Number -eq '1') {
    Write-Host "Getting Device Collections with Direct Membership Rules..." -ForegroundColor Yellow
    $Collections = @(Get-CMDeviceCollection | Where-Object { $_.CollectionRules -like '*SMS_CollectionRuleDirect*' } | Select-Object Name, CollectionID, CollectionRules | Out-GridView -PassThru -Title 'Wait for all Collections to load, then select the Device Collections you want to convert. Use The ENTER Key or Mouse \ OK Button.')
}
if ($Choice_Number -eq '2') {
    Write-Host "Getting Device Collections with Direct Membership Rules..." -ForegroundColor Yellow
    $Collections = Get-CMDeviceCollection | Where-Object { $_.CollectionRules -like '*SMS_CollectionRuleDirect*' } | Select-Object Name, CollectionID, CollectionRules
}

We need to now identify whether these collection have Direct Members, so for each collection we can look at the CollectionRules attribute, and see if it contains SMS_CollectionRuleDirect:

foreach ($Collection in $Collections) {
    if ($Collection.CollectionRules -like "*SMS_CollectionRuleDirect*") {
    }
}

Now we know this collection has Direct Members, let’s get them using Get-CMDeviceCollectionDirectMembershipRule:

$DirectMembers = Get-CMDeviceCollectionDirectMembershipRule -CollectionName $Collection.Name

Cool, easy part done.

Building the Query
#

An easy replacement for the Direct Membership Rule is to use a List of Values Query based on System Name, so we’re going to use that, we’re going to use this as our basis for the new query once we’ve captured the Direct Members in a suitable format.

CCM Client Computer Name
#

select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_SYSTEM on SMS_G_System_SYSTEM.ResourceId = SMS_R_System.ResourceId where SMS_G_System_SYSTEM.Name in ("COMPUTER1", "COMPUTER2")

AD Client Computer Name
#

select SMS_R_System.ResourceId, SMS_R_System.ResourceType, SMS_R_System.Name, SMS_R_System.SMSUniqueIdentifier, SMS_R_System.ResourceDomainORWorkgroup, SMS_R_System.Client from  SMS_R_System where SMS_R_System.Name in ("COMPUTER1", "COMPUTER2")

We also need to get the names of the Direct Members, throw them into an Array as part of the foreach loop, then squish the Array to a String and combine the template query and the members:

$RuleName = "Direct Membership Replacement Query"
$QueryPart = "select SMS_R_System.ResourceId, SMS_R_System.ResourceType, SMS_R_System.Name, SMS_R_System.SMSUniqueIdentifier, SMS_R_System.ResourceDomainORWorkgroup, SMS_R_System.Client from  SMS_R_System where SMS_R_System.Name in ("

if ($Collection.CollectionRules -like "*SMS_CollectionRuleDirect*") {
    $DirectMembers = Get-CMDeviceCollectionDirectMembershipRule -CollectionName $Collection.Name
    $MembersArray = @()
    foreach ($DirectMember in $DirectMembers) {
        $MembersArray += $DirectMember.RuleName
    }
    $Members = '"{0}"' -f ($MembersArray -join '","')
    $QueryExpression = $QueryPart + $Members + ")"
}

This gives us the complete query string which we can use later on to create the Query Membership Rule.

Updating the Rules
#

Using Add-CMDeviceCollectionQueryMembershipRule we can create the new rule in the Collection using the variables we created earlier:

Add-CMDeviceCollectionQueryMembershipRule -CollectionName $Collection.Name -QueryExpression $QueryExpression -RuleName $RuleName

And after successfully creating the rule we can use Remove-CMDeviceCollectionDirectMembershipRule to loop through the existing Direct Members and remove them:

foreach ($DirectMember in $DirectMembers) {
    Try {
        Remove-CMDeviceCollectionDirectMembershipRule -CollectionName $Collection.Name -ResourceID $DirectMember.ResourceID -Force
        }
    Catch {

    }
}

Running the Script
#

Now we have all the bits we needed in place (cough logging cough), we can launch the script and use the option to select a Collection first as a test before we run it across the entire environment.

#Load Configuration Manager PowerShell Module
Import-Module ($Env:SMS_ADMIN_UI_PATH.Substring(0, $Env:SMS_ADMIN_UI_PATH.Length - 5) + '\ConfigurationManager.psd1')

#Get SiteCode
$SiteCode = Get-PSDrive -PSProvider CMSITE
Set-Location $SiteCode":"
Clear-Host

$RuleName = 'Direct Membership Replacement Query'
# CCM Client Name
#$QueryPart = 'select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_SYSTEM on SMS_G_System_SYSTEM.ResourceId = SMS_R_System.ResourceId where SMS_G_System_SYSTEM.Name in ('
# AD Name
$QueryPart = 'select SMS_R_System.ResourceId, SMS_R_System.ResourceType, SMS_R_System.Name, SMS_R_System.SMSUniqueIdentifier, SMS_R_System.ResourceDomainORWorkgroup, SMS_R_System.Client from  SMS_R_System where SMS_R_System.Name in ('
$Merged = New-Object System.Collections.ArrayList

Write-Host '********************************************************************************'

Write-Host '**** Welcome to the Direct Membership Device Collection Converter Tool     ****' -ForegroundColor Green
Write-Host '**** This Script will convert Direct Memberships to Query Based Membership ****' -ForegroundColor Cyan

Write-Host '*******************************************************************************'

Write-Host

Write-Host ' Please Choose one of the options below: ' -ForegroundColor Yellow
Write-Host
Write-Host ' (1) Manually select the Device Collections your want to convert... ' -ForegroundColor Green
Write-Host
Write-Host ' (2) Run the Script on all Device Collection in your environment... ' -ForegroundColor Green
Write-Host
Write-Host ' (E) EXIT SCRIPT ' -ForegroundColor Red
Write-Host
$Choice_Number = ''
$Choice_Number = Read-Host -Prompt 'Based on which option you want to run, please type 1, 2 or E to exit the test, then click enter '

while ( !($Choice_Number -eq '1' -or $Choice_Number -eq '2' -or $Choice_Number -eq 'E')) {

    $Choice_Number = Read-Host -Prompt 'Invalid Option, Based on which option you want to run, please type 1, 2 or E to exit the test, then click enter '

}

if ($Choice_Number -eq 'E') {
    Break
}
if ($Choice_Number -eq '1') {
    Write-Host "Getting Device Collections with Direct Membership Rules..." -ForegroundColor Yellow
    $Collections = @(Get-CMDeviceCollection | Where-Object { $_.CollectionRules -like '*SMS_CollectionRuleDirect*' } | Select-Object Name, CollectionID, CollectionRules | Out-GridView -PassThru -Title 'Wait for all Collections to load, then select the Device Collections you want to convert. Use The ENTER Key or Mouse \ OK Button.')
}
if ($Choice_Number -eq '2') {
    Write-Host "Getting Device Collections with Direct Membership Rules..." -ForegroundColor Yellow
    $Collections = Get-CMDeviceCollection | Where-Object { $_.CollectionRules -like '*SMS_CollectionRuleDirect*' } | Select-Object Name, CollectionID, CollectionRules
}

if (!$Collections) {
    Write-Host 'No Collections selected, please run the script again...' -ForegroundColor Red
    Break
}


foreach ($Collection in $Collections) {

    if ($Collection.CollectionRules -like '*SMS_CollectionRuleDirect*') {

        $Output = New-Object -Type PSCustomObject
        $Output | Add-Member -type NoteProperty -Name ID -Value $Collection.CollectionID
        $Output | Add-Member -type NoteProperty -Name Collection -Value $Collection.Name

        Write-Host "The Collection $($Collection.Name) contains Direct Members..." -ForegroundColor Cyan
        Write-Host

        $DirectMembers = Get-CMDeviceCollectionDirectMembershipRule -CollectionName $Collection.Name
        Write-Host "Getting direct members for Collection $($Collection.Name)..." -ForegroundColor Cyan
        Write-Host

        $MembersArray = @()

        foreach ($DirectMember in $DirectMembers) {
            Write-Host "Direct Member $($DirectMember.RuleName) found." -ForegroundColor Yellow
            $MembersArray += $DirectMember.RuleName
        }

        $Members = '"{0}"' -f ($MembersArray -join '","')
        $Output | Add-Member -type NoteProperty -Name Members -Value $Members

        $QueryExpression = $QueryPart + $Members + ')'

        Try {
            Write-Host
            Write-Host "Adding Query based rule to Collection $($Collection.Name) to replace direct membership..." -ForegroundColor Cyan
            Write-Host
            Add-CMDeviceCollectionQueryMembershipRule -CollectionName $Collection.Name -QueryExpression $QueryExpression -RuleName $RuleName
            Write-Host 'Successfully added the query.' -ForegroundColor Green
            Write-Host
            $Output | Add-Member -type NoteProperty -Name Success -Value True
            Write-Host 'Removing Direct Membership Rules' -ForegroundColor Cyan
            Write-Host
            foreach ($DirectMember in $DirectMembers) {
                Try {
                    Remove-CMDeviceCollectionDirectMembershipRule -CollectionName $Collection.Name -ResourceID $DirectMember.ResourceID -Force
                    Write-Host "Successfully removed $($Directmember.RuleName)." -ForegroundColor Green
                    Write-Host

                }
                Catch {
                    Write-Host "Failed to remove $($Directmember.RuleName)." -ForegroundColor Red
                    Write-Host

                }
            }
        }
        Catch {
            Write-Host "Failed to convert Direct Membership to query for Collection $($Collection.Name)" -ForegroundColor Red
            $Output | Add-Member -type NoteProperty -Name Success -Value False
        }

        $Merged.Add($Output) | Out-Null

    }

}

Write-Host
Write-Host 'Results of the Collection Conversion...' -ForegroundColor Green
$Merged

Let’s run it on a test collection we know has direct members:

Device Collection
A Configuration Manager collection with direct members.

Run the script from an Elevated PowerShell prompt on your Primary Site Server:

Set-CollectionDirectToQueryMembership.ps1

Oh the Choices
#

Doing so will prompt you with options, oooo exciting, here we’ll select Option 1:

Script Options
The PowerShell window with script options.

Doing so will open a grid view of the collections in your Configuration Manager environment, this may take a while to load, and you should wait for them to load before selecting and confirming your Collections:

PowerShell Grid
The PowerShell script grid view of Configuration Manager collections.

Search for the Collection you want to update and select OK:

PowerShell Selection
The PowerShell script grid view results of a search for a Configuration Manager collection.

Converting the Rules
#

Now the magic happens, including a nice little output table of the results of the conversion:

Script Output
The PowerShell window with the results of the script.

The Results Speak for Themselves
#

Now the script has completed, we can go and check on the results on the Device Collection itself, look no Direct Members any more:

Collection Results
A Configuration Manager collection with query based members.

With the new Query in place:

Collection Query
A query rule of a Configuration Manager collection.

Summary
#

With this small but powerful script, you can remove all Direct Membership rules and use the more sustainable (and less likely to cause you problems), Query Rule on either Collections you know have Direct Members, or if you’re feeling brave, your entire set of Device Collections.

This saves you having to manually create the Rules, or even the collections, which would mean re-deploying configuration, updates, applications etc. to a new Collection.

What a time saver.

Related

Retrofitting Windows Autopilot Group Tags
Microsoft Intune Windows 10 and later Windows Autopilot Device Enrolment Graph API PowerShell Automation
Windows Operating System Compliance Updates
Microsoft Intune Windows 10 and later Compliance Software Updates Security Graph API PowerShell Automation
Bulk Adding Device Notes to Enrolled Devices
Microsoft Intune Administration PowerShell Graph API Device Enrolment Automation