SCCM Collections - Hash Table or Array? [1..10]

Hello I’m working on a script to add multiple collections to a master collection in SCCM. The catch is that I don’t want to add all of them at the same time. First, I get the master collection.

$CurrentMonth = Get-Date -UFormat %b

$MonthlyUpdatesCollection = (Get-CMDeviceCollection).Name | ? {$_ -like "*Sec.Updates*$CurrentMonth*Workstations*"}

Then I have a bunch of lines to add each collection that I want added.

Add-CMDeviceCollectionIncludeMembershipRule -CollectionName "$MonthlyUpdatesCollection" -IncludeCollectionId "XYZ00123"

Add-CMDeviceCollectionIncludeMembershipRule -CollectionName "$MonthlyUpdatesCollection" -IncludeCollectionId "XYZ00456"

Start-Sleep -Seconds 7200

Add-CMDeviceCollectionIncludeMembershipRule -CollectionName "$MonthlyUpdatesCollection" -IncludeCollectionId "XYZ00789"

Add-CMDeviceCollectionIncludeMembershipRule -CollectionName "$MonthlyUpdatesCollection" -IncludeCollectionId "XYZ00321"

So then I thought… instead of having a bunch of lines with the same thing basically… that I could create a hash table (maybe that’s my mistake, maybe I should just use a simple array?)

$CollectionIDs = @{

1="XYZ00123"

2="XYZ00456"

3="XYZ00789"

4="XYZ00321"

5="XYZ00654"

6="XYZ00987"

}

From here though I haven’t been able to use [1…3] for instance to just add the first 3 collections. I’ve tried



$CollectionIDs[1…3].Values | Foreach

$CollectionIDs.Values[1…3] | Foreach


I still consider myself a novice at PowerShell so maybe I’m completely off base and should use something totally different.

In a nutshell… I want to do a ForEach but not go through the entire hash table or array, I want to be able to select the first 3 and add them, then wait, then select the middle 3 and add them and then the last 3.

Let me know what you think. Thank you in advance!

 

 

 

Sorry for the formatting, I tried to fix it and failed!

The below code will do chunks of 3 and wait three minutes, and then do the next 3. If you want to change how many go at a time, you can simply change the " if ($i -eq 3)" line to If ($i -eq ).

This code uses a plain old array. It also assumes that you have a number in your array divisible by 3 and doesn’t do any error checking or validation before attempting the Add-CMDeviceCollectionIncludeMembershipRule command.

I would consider it a start- not the final solution.

Hopefully this helps.

$CurrentMonth = Get-Date -UFormat %b

$MonthlyUpdatesCollection = "MyUpdateCollection" #(Get-CMDeviceCollection).Name | ? {$_ -like "*Sec.Updates*$CurrentMonth*Workstations*"}

$arrCollectionIDs= @("XYZ00123", "XYZ00456", "XYZ00789", "XYZ00321", "XYZ00654", "XYZ00987", "XYZ01321", "XYZ01654", "XYZ01987")

$i = 1

ForEach ($ID in $arrCollectionIDs)
 {
  Add-CMDeviceCollectionIncludeMembershipRule -CollectionName "$MonthlyUpdatesCollection" -IncludeCollectionId "$ID"
  if ($i -eq 3)
   {
    Start-sleep -Seconds 180
    $i = 0
   }
  $i++
}

 

Weird, I tried to edit my post and I think it messed up a couple of your preformatted text examples.

The below code will do chucks of 3 and wait 3 minutes between. It assumes your array is divisible by 3, and will not validate that it has data before attempting to run the sccm command. You can change the number it does before pausing by changing the line " if ($i -eq 3)" to some other number.

I would consider this a start- not the complete code

$CurrentMonth = Get-Date -UFormat %b

$MonthlyUpdatesCollection = "MyUpdateCollection" #(Get-CMDeviceCollection).Name | ? {$_ -like "*Sec.Updates*$CurrentMonth*Workstations*"}

 

$arrCollectionIDs= @("XYZ00123", "XYZ00456", "XYZ00789", "XYZ00321", "XYZ00654", "XYZ00987", "XYZ01321", "XYZ01654", "XYZ01987")

 

$i = 1

ForEach ($ID in $arrCollectionIDs)

{

Add-CMDeviceCollectionIncludeMembershipRule -CollectionName "$MonthlyUpdatesCollection" -IncludeCollectionId "$ID"

 

if ($i -eq 3)

{

Start-sleep -Seconds 180

$i = 0

}

$i++

}

This maybe a copy paste issue, but the line spaces, Which I cleaned out will not allow this to work as planned
I am only use the format to make this easy to copy.

$CollectionIDs = @{
1="XYZ00123"
2="XYZ00456"
3="XYZ00789"
4="XYZ00321"
5="XYZ00654"
6="XYZ00987"
}

$CollectionIDs | ft -a


Name Value   
---- -----   
6    XYZ00987
5    XYZ00654
4    XYZ00321
3    XYZ00789
2    XYZ00456
1    XYZ00123

Note that the collection is last in is first and the output use the array is as expected

$CollectionIDs[1..3] | ft -a

XYZ00123
XYZ00456
XYZ00789

So, you could simply brute force this, this way…

$CollectionIDs[1..3] | 
% {
    "Processing $_"
}

Start-sleep -Seconds 180

$CollectionIDs[4..6] | 
% {
    "Processing $_"
}

… or take a more elegant way, as this is what it sounds like you are trying to do.

https://www.petervanderwoude.nl/post/divide-a-collection-into-multiple-smaller-collections-in-configmgr-2012-via-powershell

Thank you for your help! I decided to go with a simple array as you did. I’m sure Hash Table has certain advantages but I just don’t know how to work well with them yet.

#Connect to SCCM Site
$SiteCode = "XYZ" # Site code
$ProviderMachineName = "XYZSCCM1.XYZ.local" # SMS Provider machine name
$initParams = @{}
if((Get-Module ConfigurationManager) -eq $null) {
Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1" @initParams
}
if((Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue) -eq $null) {
New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initParams
}
Set-Location "$($SiteCode):\" @initParams
#Sets Current Month Variable
$CurrentMonth = Get-Date -UFormat %b
$MonthlyUpdatesCollection = (Get-CMDeviceCollection).Name | ? {$_ -match "Test.Add.Collections"}
#Array of Collection IDs to be added to Monthly Collection
$CollectionIDs = @(
"XYZ007B5"       #0
"XYZ0078A"       #1
"XYZ007D8"       #2
"XYZ007F9"       #3
"XYZ007C0"       #4
"XYZ007B3"       #5
"XYZ007FA"       #6
"XYZ007C8"       #7
"XYZ007CD"       #8
"XYZ007C4"       #9
"XYZ007C4"       #10
"XYZ007B4"       #11
"XYZ00773"       #12
"XYZ007F0"       #13
"XYZ007E5"       #14
"XYZ007C5"       #15
"XYZ007C3"       #16
"XYZ007E4"       #17
"XYZ007F7"       #18
"XYZ0075D"       #19
)
$CollectionIDs[0..10] | ForEach {
Add-CMDeviceCollectionIncludeMembershipRule -CollectionName "$MonthlyUpdatesCollection" -IncludeCollectionId $($_)
}
Start-Sleep -Seconds 60
Invoke-CMDeviceCollectionUpdate -Name "$MonthlyUpdatesCollection"
Start-Sleep -Seconds 300
$CollectionIDs[11..14] | ForEach {
Add-CMDeviceCollectionIncludeMembershipRule -CollectionName "$MonthlyUpdatesCollection" -IncludeCollectionId $($_)
}
Start-Sleep -Seconds 60
Invoke-CMDeviceCollectionUpdate -Name "$MonthlyUpdatesCollection"
Start-Sleep -Seconds 300
$CollectionIDs[15..16] | ForEach {
Add-CMDeviceCollectionIncludeMembershipRule -CollectionName "$MonthlyUpdatesCollection" -IncludeCollectionId $($_)
}
Start-Sleep -Seconds 60
Invoke-CMDeviceCollectionUpdate -Name "$MonthlyUpdatesCollection"
Start-Sleep -Seconds 300
$CollectionIDs[17..19] | ForEach {
Add-CMDeviceCollectionIncludeMembershipRule -CollectionName "$MonthlyUpdatesCollection" -IncludeCollectionId $($_)
}
Start-Sleep -Seconds 60
Invoke-CMDeviceCollectionUpdate -Name "$MonthlyUpdatesCollection"

This is what I went with. The main point of this is because 2 months ago I added All Workstations at once and it screwed up WSUS for some reason.

My next question is… does anyone know if the Invoke-CMDeviceCollectionUpdate is good enough to add the members to the collection so that they will get policy and start downloading updates?

Invoke-CMDeviceCollectionUpdate -Name "$MonthlyUpdatesCollection"

 

Or should I add a RequestRefresh via WMI so that collection members get policy?

$CollectionQuery = Get-WmiObject -Namespace "Root\SMS\Site_$SiteCode" -Class SMS_Collection `
-ComputerName $ProviderMachineName -ErrorAction STOP -Filter "Name='$MonthlyUpdatesCollection'"
$CollectionQuery.RequestRefresh()

Understood, on taking the brute force way, but as to the rest of your query. It’s more about how WSUS and SCCM works that PS specifically. So, sure we can handle the PS proper stuff, but the moment you get into MS product / SKU specific features, all of which have there on modules / cmdlets, etc., unless we are SME’s (I’ve personally not touched SC since v2007) in those topics, that’s more of a challenge to answer, for obvious reasons.

Rule of thumb though. If you already have policies (SCCM / GPO, etc.) in play and working as designed, there is little reason to spend any cycles trying to out think it, unless there is a real reason to.

Invoke-CMDeviceCollectionUpdate is specifically an SCCM thing, not PS proper default cmdlet set.

I guess it really depends on if you want your clients to check in as quickly as possible or if you want them to do their regular randomized check in. If you are trying to get them to all check in for the maintenance window, you might want to do it so the machines get policy refreshes.

Depending on how many machines your SCCM environment manages, having all machines check in at the same time can really affect the performance (depending on your architecture, network, and, sometimes, which way the wind is blowing ) and make responses and downloads seem sluggish.

-m