Using PowerShell to fake converting ConfigMgr IPSubnet to IPRange boundaries

Over the years that I have done work ConfigMgr, there was a back and forth on what type of boundaries to use between IPRange and IPSubnet. I have worked in places that were one or the other as well as mixed. Normally, what was used was determined by the suggestion of a Microsoft PFE. Now, I want to convert my IPSubnet boundaries to IPRange boundaries.

Personally, I do not like IPSubnet boundaries in ConfigMgr because the subnet mask information is not stored in the database. This means I can tell what the first IP address covered in the boundary is but I do not know what the last IP will be.

In most cased for my environment, the IPSubnet will just cover a single full class C range. For the range of 10.10.10.0-10.10.10.255, it would have Network of 10.10.10.0, Subnet mask of 255.255.255.0, and have a subnet ID of 10.10.10.0 in ConfigMgr.

Now I have seen a few cases in other environments where the range is 10.10.10.0-10.10.11.255. This would have Network of 10.10.10.0, Subnet mask of 255.255.254.0, and end up with a subnet ID of 10.10.10.0 in ConfigMgr. This is the same SubnetID even though the subnet mask is different.

In my environment, we have recently set the standard to use IP ranges only, but what to do with the existing SubnetIP based boundaries? We need to switch them over to ranges, but that is not a fun thing to do manually. Luckily, there is PowerShell to the rescue.

Unfortunately, I quickly learned that SET-CMBOUNDARY is great for changing the name of the boundary but you cannot change the type and give it a new value. I have to create new boundaries.

Here is what I needed to do:
1) Figure out the IP range from the Subnet ID (Only worrying about X.X.X.0 SubnetIDs and assuming the subnet masks are all 255.255.255.0)
2) Create the new range based boundary with the same name as the old subnetID boundary
3) Add the new boundaries to same boundary groups as the old ones
4) Remove the old subnetID boundary, if desired
5) Repeat over 1000 times

My solution is ugly and probably not the greatest because it loops through a CSV file and each boundary has multiple lines because each boundary is a member of multiple boundary groups and I need better SQL skills. This leads to many errors as it tries to create the boundary after creating it and tries to delete the old boundary after deleting it.

The first step was to get all the info I needed and SQL had it all.

Using the following query, I got the name of the boundaries, their ID, took the subnet value and converted it to a range, and got the boundary groups IDs of the groups the boundaries are part of.

SELECT
vSMS_Boundary.DisplayName, vSMS_Boundary.BoundaryID AS id,
STUFF(vSMS_Boundary.value, LEN(vSMS_Boundary.value), 1, '1') + '-' + STUFF(vSMS_Boundary.value, LEN(vSMS_Boundary.value), 1, '254') AS range,
vSMS_BoundaryGroup.GroupID AS bgid
FROM vSMS_Boundary INNER JOIN
vSMS_BoundaryGroupMembers ON vSMS_Boundary.BoundaryID = vSMS_BoundaryGroupMembers.BoundaryID INNER JOIN
vSMS_BoundaryGroup ON vSMS_BoundaryGroupMembers.GroupID = vSMS_BoundaryGroup.GroupID
WHERE vSMS_Boundary.BoundaryType = '0' AND vSMS_Boundary.Value LIKE '%.0'

I took the output the copied it with headers into Excel to create a CSV file. This will be the input for the script. As mentioned before, these ranges are created with the assumption that are full class C ranges with 255.255.255.0 or /24 subnet. If you have subnets that are /22 or /13, for example, the SQL is still a great start and you can just edit the third octet of second half of the range within the CSV file to create a properly sized IPRange boundary.

The input CSV file can be manually created as well. The column headers are displayname, id, range, and bgid.

Displayname – The name of the boundary
Id – BoundaryID of the out IPSubnet boundaryRange – IP range for the boundary
Bgid – Boundary Group ID to add boundary. Each boundary group add does

require a separate line in the CSV

I then passed the information in the CVS through my PowerShell script. The script created the IPRange boundaries, associated boundary groups to the boundary, and then deleted the old IPSubnet boundary.

The syntax is:

Switch-IPSubnet-to-IPRange-Boundaries.ps1 -inputfile file.csv -sc sitecode: <-del y>

The –del y switch is not mandatory and toggles the automatic deletion of the IPSubnet boundaries. Without the switch, they have to be deleted afterwards.

The script and SQL can be found GitHub at https://github.com/NecroMonkey/vault/tree/master/ConfigMgr-Scripts

&lt;#

.SYNOPSIS
The script takes an input CSV file of IPSUbnet boundaries and loops through it to create new IPRange boundaries

.SYNTAX
Switch-IPSubnet-to-IPRange-Boundaries.ps1 -inputfile file.csv -sc sitecode: &lt;-del y&gt;

.DESCRIPTION
The script takes an input CSV file and loops through it to create new IP range based ConfigMgr boundaries off IP subnt boundaries in the file, add it to desired boundary groups, and delete the old IPSubnet boundary.

.PARAMETER DisplayName
Name to be given to the new boundary created

.PARAMETER id
Boundary ID of the IPSubnet boundary being replaced by the new IPRange boundary

.PARAMETER range
IP Range to be used in creating the IPRange boundary. x.x.x.x-x.x.x.x format

.PARAMETER bgid
Boundary Group ID that the new boundary will be added

.PARAMETER del
Optional parameter to automatically remove the old IPSubnet boundary. Suggest not using and verify new boundarie and manually delete old. Only accepted pararmeter is y

.PARAMETER sc
ConfigMgr site code. FOrmat is site code followed by :

.PARAMETER InputFile
Path and name of input CSV file

.INPUTS
CSV file with the following column header names: displayname, id, range, and bgid. This is the input for the matching parameters. Each boundary group add does require a separate line

.NOTES
FileName: Switch-IPSubnet-to-IPRange-Boundaries.ps1
Author: Michael Schultz
Contact: mschultz@necro-monkey.com
Created: 20180825
Modified:
Version: 1.0.0

.EXAMPLE
.\recreateranges.ps1 -InputFile .\InputFile.csv -del y -sc sms:

#&gt;

#-----Parameters-----

param(
[Parameter(ParameterSetName=1,Mandatory=$true)]
[string]$DisplayName,
[Parameter(ParameterSetName=1,Mandatory=$true)]
[string]$range,
[Parameter(ParameterSetName=1,Mandatory=$true)]
[string]$bgid,
[Parameter(ParameterSetName=1,Mandatory=$true)]
[string]$id,
[Parameter(ParameterSetName=2,Mandatory=$false)]
[string]$del,
[Parameter(ParameterSetName=2,Mandatory=$true)]
[string]$sc,
[Parameter(ParameterSetName=2,Mandatory=$true)]
[string]$InputFile
)

#-----Initializations and Module Imports-----

#Get ConfigMgr Console Install Path to locate PS Module.

If (Test-Path "C:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1")
{
Import-Module "C:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1"
}
elseIf (Test-Path "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1")
{
Import-Module "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1"
}
elseif (test-path "d:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1")
{
Import-Module "d:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1"
}
elseIf (Test-Path "d:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1")
{
Import-Module "d:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1"
}
Else {
Write-output "Can't Find your ConfigMgr Module"
exit
}

#-----Variables-----

$List = Import-Csv -Path $InputFile

#-----Execution-----

#Change to ConfigMgr PSDrive
Set-Location $sc

#Create IPRange boundaries
Foreach ($item in $List)
{
New-CMBoundary -DisplayName $item.DisplayName -BoundaryType IPRange -Value $item.range
}

#Add new boundary to boundary groups
Foreach ($item in $List)
{
Add-CMBoundaryToGroup -BoundaryGroupID $item.bgid -BoundaryName $item.Displayname
}

#Delete old IPSubnet boundary if desired
if ($del -eq 'y')
{
Foreach ($item in $List)
{
Remove-CMBoundary -Id $item.id -Force
}
}