How to add computers listed in a text file to a collection

Using the new ConfigMgr 2012 PowerShell cmdlets, there are three different ways to add computers to a device collection.  Just like in the console, you can add computers via a direct membership rule (Add-CMDeviceCollectionMembershipRule), a query membership rule (Add-CMDeviceCollectionQueryMembershipRule) and also an include collection membership rule (Add-CMDeviceCollectionIncludeMembershipRule).  In order to use these cmdlets, the collection already needs to be created.  In my previous post How to quickly create Collections using New-CMDeviceCollection, I demonstrated one way of creating device collections based off of a for loop and using each loop as an index number in the collection name.

In this post, since we will be reading a list of computers from a text file, we will use the direct membership rule cmdlet Add-CMDeviceCollectionMembershipRule to add them to the collection.  Using this cmdlet, you can add a device resource object to one or more device collections.  Since collection names are unique in CM 12, it is easiest to reference the collection that you want to add objects to using the -CollectionName parameter.  For the device that we are trying to add, the easiest way is to get the resource ID and use the -ResourceId parameter.  We can get the resource ID of a device called PC01 using the Get-CMDevice cmdlet:

(Get-CMDevice -Name PC01).ResourceID

Using the Add-CMDeviceCollectionDirectMembershipRule, we can add PC01 to the collection called Collection_01 with the following command:

Add-CMDeviceCollectionDirectMembershipRule  -CollectionName Collection_01 -ResourceId $(get-cmdevice -Name PC01).ResourceID

Now we just need to read the computers in from a text file and add them to our collection. I created a text file called Collection_01.txt in D:\Collections and have added PC01, PC02, PC03 and PC99 (this one is not a valid client in my ConfigMgr lab). We can read these computers into a variable called $computers using the following command:

$computers = Get-Content "D:\Collections\Collection_01.txt"

If we add the above commands to the following loop, we can loop through the variable $computers and add each computer to the specified collection:

foreach($computer in $computers) {
   Add-CMDeviceCollectionDirectMembershipRule  -CollectionName Collection_01 -ResourceId $(get-cmdevice -Name $computer).ResourceID
}

If the computer name is already defined as a direct membership rule or it is not a valid client, it will produce an error. We can account for these errors by using try/catch functionality built into PowerShell. Inside of the for loop we can add:

foreach($computer in $computers) {
   try {
      Add-CMDeviceCollectionDirectMembershipRule  -CollectionName $collectionname -ResourceId $(get-cmdevice -Name $computer).ResourceID
       }
   catch {
      "Invalid client or direct membership rule may already exist: $computer" | Out-File "D:\Collections\Collection_01_error.log" -Append
       }
}

Now, wouldn’t it be great if we could create the collections based on the file names that contained a list of computers and automatically add the computers to the correct collection? You bet – the following will do just that. It will look in D:\Collections\ for all text files, create a collection based on the file names (minus the file extension), create direct membership rules for each computer name of the respective collection, and create a log file in the same directory with the collection name of any invalid collections, clients or rules.

#Create Collections and Membership Rules from Text Files
#Author: Mike Terrill
#Set path to collection directory
$collectiondir = "D:\Collections\"

#Pull only .TXT files into array
$filenames = Get-ChildItem $collectiondir* -include *.txt

for ($x=0; $x -lt ($filenames.Length); $x++) {
    $collectionname = $filenames.Name[$x].Split(".")[0]
    $collectionname
    #Add new collection based on the file name
    try {
        New-CMDeviceCollection -Name $collectionname -LimitingCollectionName "All Systems"
        }
    catch {
        "Error creating collection - collection may already exist: $collectionname" | Out-File "$collectiondir\$collectionname`_invalid.log" -Append
        }

    #Read list of computers from the text file
    $computers = Get-Content $filenames[$x]
    foreach($computer in $computers) {
        try {
            Add-CMDeviceCollectionDirectMembershipRule  -CollectionName $collectionname -ResourceId $(get-cmdevice -Name $computer).ResourceID
            }
        catch {
            "Invalid client or direct membership rule may already exist: $computer" | Out-File "$collectiondir\$collectionname`_invalid.log" -Append
            }
    }
}

As always, be sure to test any scripts or cmdlets in a test environment. Automation can be a great thing if used correctly and a very bad thing if used incorrectly.

Disclaimer:
Your use of these example scripts or cmdlets is at your sole risk. This information is provided “as-is”, without any warranty, whether express or implied, of accuracy, completeness, fitness for a particular purpose, title or non-infringement. I shall not be liable for any damages you may sustain by using these examples, whether direct, indirect, special, incidental or consequential.

Originally posted on https://miketerrill.net/

28 thoughts on “How to add computers listed in a text file to a collection

  1. I realize this blog post is old but hopefully still being monitored. I am trying to use this script to no avail. Not sure what I’m missing. Any help would be greatly appreciated.

    I am getting the following error:

    Method invocation failed because [System.Char] does not contain a method named ‘Split’.
    At \\server\Sources\Tools\DirectCollection\DirectCollection.ps1:10 char:5
    + $collectionname = $filenames.Name[$x].Split(“.”)[0]
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

    • Hi Mike, I think I know the problem. When I wrote the script I was testing with multiple text files. The problem is, if you are using a single text file, it will read the length of the filename instead of the number of items returned. So, you can either add a second (empty) text file in the directory, or use the following updated script that has a few minor changes to handle just one file:

      #Set path to collection directory
      $collectiondir = "D:\Collections\"

      #Pull only .TXT files into array
      $filenames = @(Get-ChildItem $collectiondir* -include *.txt -Name)

      for ($x=0; $x -lt ($filenames.Length); $x++) {
      $collectionname = $filenames[$x].Split(".")[0]
      $collectionname
      #Add new collection based on the file name
      try {
      New-CMDeviceCollection -Name $collectionname -LimitingCollectionName "All Systems"
      }
      catch {
      "Error creating collection - collection may already exist: $collectionname" | Out-File "$collectiondir\$collectionname`_invalid.log" -Append
      }

      #Read list of computers from the text file
      $filename = $filenames[$x]
      $computers = Get-Content $collectiondir$filename
      foreach($computer in $computers) {
      try {
      Add-CMDeviceCollectionDirectMembershipRule -CollectionName $collectionname -ResourceId $(get-cmdevice -Name $computer).ResourceID
      }
      catch {
      "Invalid client or direct membership rule may already exist: $computer" | Out-File "$collectiondir\$collectionname`_invalid.log" -Append
      }
      }
      }

      Good catch and sorry for the headache!

  2. Great post! I’m having a little trouble getting a successful outcome. The Collection is not getting created. The error message is “Error creating collection – collection may already exist:”
    Any idea what might be going wrong?

    Thank you

    • Sounds like you already have a Collection with the name that is read from the file name, or it created it on a previous run and you need to refresh the console to see it. Try running Get-CMDeviceCollection -Name ‘Name of your collection’ and see if it returns anything.

      • Mike,
        I got the script to run finally after a few additions. I’ll paste it below. I have one other issue that I can’t seem to figure out. Some machines that do exist in SCCM do not get added to the collection. How can I troubleshoot this issue? Here’s the script I’m running:

        #Import Configuration Manager Module
        import-module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5) + ‘\ConfigurationManager.psd1’)

        #Set SMS-SiteCode
        Set-Location CPT:

        #Set path to collection directory
        $collectiondir = “D:\Collections\”

        #Pull only .TXT files into array
        $filenames = @(Get-ChildItem $collectiondir* -include *.txt -Name)

        for ($x=0; $x -lt ($filenames.Length); $x++) {
        $collectionname = $filenames[$x].Split(“.”)[0]
        $collectionname

        #Add new collection based on the file name
        try {
        New-CMDeviceCollection -Name $collectionname -LimitingCollectionName “All Systems”
        }
        catch {
        “Error creating collection – collection may already exist: $collectionname” | Out-File “$collectiondir\$collectionname`_invalid.log” -Append
        }

        #Read and validate list of computers from the text file
        $filename = $filenames[$x]
        $computers = Get-Content $collectiondir$filename
        foreach($computer in $computers) {
        try {
        Add-CMDeviceCollectionDirectMembershipRule -CollectionName $collectionname -ResourceId $(get-cmdevice -Name $computer).ResourceID
        }
        catch {
        “Invalid client or direct membership rule may already exist: $computer” | Out-File “$collectiondir\$collectionname`_invalid.log” -Append
        }
        }
        }

        Thank you!

      • Hi Michael, thanks for the response and your updates. As for machines not being added, it could be a couple of things. If a machine is not part of the limiting collection, then it will not show up. Depending on the size of the environment, it may take some time for a system to show up if it is a part of the limiting collection.
        Thanks,
        Mike

  3. Hi Mike, thanks for the extremely useful script. I assume this creates a collection at the root level. Is there a way to specify a folder within devices collections as the default location?

  4. I’m having the same problem Michael Sweeting was having. I tried the code that he pasted in his reply and I get a syntax error. When I run your original powershell script all it says is the name of the text file and nothing else.

    • Sorry to hear that – without details about your environment it is a little difficult to understand what the problem could be. I would double check the script you are running to make sure there are not any rogue characters (like smart characters – i.e. double quotes that get changed to smart quotes). Also, make sure you are running under a session that already has the CM PowerShell module imported. In Michael Sweeting’s script, he imports the module and sets the PS drive to his site code of CPT – so you might double check that as well.

  5. Your script saved me time. I needed a limiting collection for devices to NOT software update and had names in a txt. All set.

  6. Pingback: 1E Blog / How to add computers listed in a text file to a device collection

  7. How can I use this script to add Ad Groups to a collection? I can add users and machines just fine but can’t seem to ad ad groups?

    • Hi Steve – have a look at the Add-CMUserCollectionDirectMembershipRule cmdlet. This should allow you to add an User Group Resource into a User Collection. Just keep in mind that Collections are either Device based or User/User Group based in CM 12 – no more mixing them in the same collections like in CM 07.

  8. If a system gets reinstalled (and keeps the same computername) it’s resource ID will change to a new value, which means that you have to re-run the script.

    • Hi Big Joe, CM 12 has gotten better about detecting newly installed clients and merging them into the existing resource ID. I am not saying it always works, but I just installed (wipe and load) a bunch of Win10 clients on existing VMs and they managed to keep their previous resource IDs and existing collection memberships.

  9. Pingback: Scheduled OS deployments - Page 2

  10. Great stuff, thank you so much for sharing this. I would have spent hours manually adding systems to collections without your valuable insights!

  11. Mike, Thank you for the useful script. When I ran your original scripts for single file, I got the same error with Mike D . Then I have run the script from Michael Sweeting to import the Configuration Manager Module, I got this error:

    PS C:\Users\temp\Desktop\scripts> .\CollectionFromSingleFile.ps1
    Get-ChildItem : Cannot retrieve the dynamic parameters for the cmdlet. Value cannot be null.
    Parameter name: drive
    At C:\Users\temp\Desktop\scripts\CollectionFromSingleFile.ps1:15 char:16
    + $filenames = @(Get-ChildItem $collectiondir* -include *.txt -Name)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
    + FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.PowerShell.Commands.GetChildItemCommand

    Please Help!

    • It could be that you do not have a psdrive for your site defined in your PowerShell session (like when you launch PowerShell from the CM Console). That could be the problem.
      -Mike

  12. Pingback: System Center | Everything To Be Virtual

  13. Mike I have one small issue, I want my device collection called
    “Great Program ver 1.6.7.9” the device collection that is made is called “Great Program ver 1”
    Is there any way to have periods in the device collection name?
    It works correctly if I name the text file device collection “Great Program ver 1_6_7_9.txt vs “Great Program ver 1.6.7.9.txt”
    -Dave

    • It can be done by changing some of the logic around ‘Split’. Maybe instead parse the file name to exclude the last four characters (the dot and file extension).

  14. Thank you, I did get it to work maybe not the best way but it works for me.
    I changed this:
    $collectionname = $filenames[$x].Split(“.”)[0]
    to
    $collectionname = $filenames[$x].Split(“_”)[0]

    I now end all my Text files with _.txt
    “Great Program ver 1.6.7.9_.txt”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s