Monday, April 2, 2018

Powershellist - Adding multiple devices to a collection with partial name search

This Powershell script will add multiple devices to a specific device collection by partial name search, based on a file with pre-specified partial and/or full device names:

Get-Content "C:\path\Collection.txt" | foreach { Add-CMDeviceCollectionDirectMembershipRule -CollectionName "Collection Name" -ResourceID (Get-CMDevice -Name *$_*).ResourceID }


It returns a "Cannot bind argument to parameter 'ResourceId' because it is null' error if the partial name is not found.  Be sure to modify the path to the file (underlined/bold) as specified (which should be a list of partial device names, one per line).  This can come in handy if, for instance, you are given a list of serial numbers for devices to add to a collection, and since in our environment the device includes the serial number in the device name, it enables us to quickly add the list of serial numbers (partial device names) to the collection.

Thursday, March 22, 2018

Content Library Cleanup Tool explained

Just to expand on my previous post, here are some more of my findings on the Content Library Cleanup Tool.


  • Don't be afraid to delete content.  The content you are deleting with this tool is either invalid or orphaned, and is not the same as the content in your production environment.  It is perfectly safe to delete all content the tool finds in its scan - your DP will still contain the original content afterwards.
  • The tool may glitch every once in awhile.  I've seen a few times where the tool will keep showing "Analyzing local files... 99.92% complete" or close to that while appearing to still scan for content.  Pay attention to the prompts that come up between these same messages - you can press 'a' or 'y' on a separate line (yes, while it's still "scanning" at this same percentage repeatedly) and see if it accepts your input - if it does, it's in a buggy loop despite it still being able to process.  You can optionally check the free space of the drive the content is stored on to verify it's still being cleaned, or you can close the tool and re-run it again, your choice.
  • Be patient.  Occasionally you may notice the tool seems to 'lock up' at certain points.  I have yet to see the tool completely lock up without reason, so just be patient since whatever it's working on, chances are there's a lot of it to delete.  This is especially true of Endpoint Protection Definition Updates or any other content that gets refreshed consistently on the DP.
  • Troubleshooting. In case you ever receive the 'package is not fully installed' error, check the PackageID to validate if the content is being actively distributed to the DP.  If it isn't, the next step is to remove the content from the DP.  After doing so, run the tool again.  Then after the tool successfully runs, be sure to re-distribute the content you removed previously.  There are other errors that can happen with the tool indicating missing content, however, the tool can be re-ran to bypass these errors.
  • The tool may appear to delete 'too much'.  Depending on your environment/amount of content, the tool can easily free up hundreds of gigs of space on the content store's drive.  This is normal/expected behavior.

Monday, March 19, 2018

Scripting the automation of the Content Library Cleanup Tool

As of SCCM 1702's release, the Content Library Cleanup Tool is a nifty addition to cleanup your distribution points painlessly and effectively.  Since it requires command line parameters to run, I have created scripts to run the tool easier without having to remember the parameters.  There are two modes, 'what-if' and 'delete' modes for the tool, and I've created batch scripts for each mode and named them respectively.

For 'delete' mode, here is the batch script ("CLCT-Delete.cmd"):
@echo off
set /p dp="Please specify the DP hostname.  Hit Enter for %COMPUTERNAME%: "
if [%dp%] == [] ( goto default ) else (
"%~dp0ContentLibraryCleanup.exe" /dp %dp% /delete /log "%~dp0Logs\%dp%"
goto exit
)
:default
"%~dp0ContentLibraryCleanup.exe" /dp %COMPUTERNAME%.domain.com /delete /log "%~dp0Logs\%COMPUTERNAME%"
:exit
pause
exit 

For 'what-if' mode, here is the script ("CLCT-WhatIf.cmd"):

@echo off
setlocal EnableDelayedExpansion
set /p dp="Please specify the DP hostname.  Hit Enter for %COMPUTERNAME%: "
if [%dp%] == [] ( goto default ) else (
"%~dp0ContentLibraryCleanup.exe" /dp %dp% /log "%~dp0Logs\%dp%"
goto exit
)
:default
"%~dp0ContentLibraryCleanup.exe" /dp %COMPUTERNAME%.domain.com /log "%~dp0Logs\%COMPUTERNAME%"
:exit
pause
exit 


The script assumes "ContentLibraryCleanup.exe" is in the same folder as the script.  It will prompt for the hostname of the distribution point, and if none is entered, uses the current machine's hsotname as default.  It then writes everything to a log file in Logs\<hostname> folder as well (and opens it automatically when done).

Note: The Tool will crash if any content is currently being distributed to the DP (and it will do this after it's processed all content, which takes awhile).  Refer to the link above for more details if needed.

Wednesday, March 14, 2018

Fixing the "Task Sequence is locked" issue

Sometimes you'll edit a Task Sequence and either the console crashes, site goes down, or the TS is in use by another SCCM admin (but may not actually be in use anymore).


The normal solution is to wait 30 minutes after the TS is no longer in use and the lock will clear itself.  This doesn't always happen, and when it doesn't, this SQL query may prove to be useful:

SELECT * FROM SEDO_LockState WHERE AssignedUser = 'domain\username'
This query assumes you know the domain\username of the offending user who's no longer editing the TS.  If you don't know which user is, you can just run a simpler version - but be prepared to sift through a lot more info:

SELECT * FROM SEDO_LockState

Once you find the culprit record, run the query below to delete it:

DELETE FROM SEDO.LockState where ID = '555'
Replace 555 above with the actual number returned in the ID column of the record.

NOTE: I take no responsibility for deleted records due to misused queries.  Use at your own risk.  😀

Friday, January 19, 2018

Powershellist - Add Software Update Point as reference to all Boundary Groups

As of SCCM 1702, a Software Update Point must be specified as a reference to all relevant Boundary Groups - otherwise your endpoints will continue using MS Update despite the SUP being setup properly in all other aspects.  This Powershell script automatically adds the SUP to all Boundary Groups in one fell swoop, utilizing the CM cmdlets, of course.

$boundarygroups = Get-CMBoundaryGroup
foreach($BG in $boundarygroups)
 {
    Set-CMBoundaryGroup -name $BG.name -AddSiteSystemServerName supserver.fqdn.com
 }
Just change supserver.fqdn.com to the FQDN of your SUP and you're all set.

Friday, December 8, 2017

Query to search for MAC addresses with wildcard

Sometimes a machine doesn't want to PXE boot for imaging, doesn't show up in a hostname search, or has stale records in SCCM and we need to delete the object(s) to do what we want to do.  That's where a MAC address query can come in.  This query can also utilize wildcard searches using a "%" sign.  This can help if you are given a blurry phone picture of a MAC address and aren't able to determine if that's an '8' or a 'B', or any other characters.

select distinct SMS_R_System.MACAddresses, SMS_R_System.Name from  SMS_R_System where SMS_R_System.MACAddresses like ##PRM:SMS_R_System.MACAddresses## order by SMS_R_System.MACAddresses
Add this to your queries under Monitoring > Overview > Queries and run it as needed.


Managing IP Boundaries using IP Ranges

In our environment we consistently had IP boundary issues utilizing IP subnets, and recently discovered (via our network admins) that IP ranges would be considerably better than IP Subnets.  I had already been in the midst of converting the environment from IP Subnets to IP Ranges when I came across this article which proved the theory.  Therefore, here are some basic methods to start using IP ranges in your environment.

Convert SCCM Boundaries from IP Subnets to IP Ranges

First, you'll want to find a basic IP Calculator - there are many on Google although the one linked is what I used.  You'll also need access to the basic network layout of your environment - preferably acquired from your network admin.  The network layout should include either IP subnets with subnet masks (e.g. 255.255.240.0), or subnets with bit length (e.g. 10.81.64.0/20).  If you are given only subnets with subnet masks and not bit length, use a Subnet Mask Cheat Sheet to determine the bit length with subnets.  Then use the IP Calculator to determine the IP Range.  In SCCM the easiest way to convert is to go to the Properties of the Subnet Mask (under Administration > Hierarchy Configuration > Boundaries), change the Type dropdown to IP Address Range, then enter your IP ranges there. (If you create new IP Range entries alongside the IP Subnet entries, you'd still need to modify the new IP Ranges to add your Boundary Groups).

You will notice a pattern and probably remember the pattern as you do more subnets.  For me, it was that a /20 mask meant I would simply add 15 to the 3rd octet, so, for example, the complete range for a 10.50.32.0 subnet would be 10.50.32.1 to 10.50.47.254 (32+15=47).  Note that if you are doing /24 masks, it's pretty much pointless to convert since the range will be only that subnet anyway (e.g. 10.50.32.1 to 10.50.32.254).

Create a Device Collection based on IP Ranges in SCCM

If you'd like to create a collection based on IP Ranges, the best method is to use the subnet method with a range.  I have seen methods (i.e. here) which actually do not always work correctly since the way WQL works, it calculates only single digit numbers when sorting.  For instance, if your IP range is 10.50.96.1 to 10.50.111.254, and you setup the query as "greater than or equal to 10.50.96.1" and "less than or equal to 10.50.111.254", it will return 0 results, because WQL only reads the first digit in the 3rd octet (9) and compares that with the first number in the 3rd octet comparison (1).  So the method in that link will only work for specific IP ranges and not all of them.

Here's some examples on the correct way to use subnets with a range in your WQL query:

For a given IP subnet of 10.177.160.0/20:
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.IPSubnets like "10.177.16[0-9].0" or   SMS_R_System.IPSubnets like "10.177.17[0-5].0"
The actual range in the example is 10.177.160.1 through 10.177.175.254.  Since WQL works with single-digit numbers, this method must be used and can get a bit complicated.  

Here's another example for a multiple subnet entry of 10.124.0.0/20 and 10.124.16.0/21:
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.IPSubnets like "10.124.[0-9].0" or   SMS_R_System.IPSubnets like "10.124.1[0-9].0" or   SMS_R_System.IPSubnets like "10.124.2[0-3].0"
The ranges for this example are 10.124.0.1 through 10.124.15.254 and 10.124.16.1 through 10.124.23.254.  This can be combined to a range of 10.124.0.1 through 10.124.23.254, then is converted into the WQL above.

Note that it's not possible to just use 10.124.[10-19].0 in the above WQL query because of WQL's design to use single digits. 

Furthermore, if you have multiple ranges, you can go a bit further:
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.IPSubnets like "10.172.9[6-9].0" or SMS_R_System.IPSubnets like "10.172.1[0-1][0-9].0" or SMS_R_System.IPSubnets like "10.172.12[0-7].0"
With subnets of 10.172.96.0/20, 10.172.112.0/21, and 10.172.120.0/21, the subnets are converted and then shown in the above WQL.