Active Directory Powershell to manage Sites and Subnets – Part 2 (New-XADSubnet)

In an earlier post “Active Directory Powershell to manage sites – Part 1 (New-XADSite)” Jairo explained in detail about how to create a Site in Active Directory using AD Powershell. In today’s post I am going to discuss about how to create Subnets using AD Powershell.

Before going into details of creating a subnet object, first let us understand what is a Site and Subnet.

The following definition is from: https://technet.microsoft.com/en-us/library/cc782048(WS.10).aspx

In Active Directory, a site is a set of computers well-connected by a high-speed network, such as a local area network (LAN). All computers within the same site typically reside in the same building, or on the same campus network. A single site consists of one or more Internet Protocol (IP) subnets. Subnets are subdivisions of an IP network, with each subnet possessing its own unique network address. A subnet object in AD DS groups neighboring computers in much the same way that postal codes group neighboring postal addresses .

Sites and subnets are represented in Active Directory by site and subnet objects. Each site object is associated with one or more subnet objects. By associating a site with one or more subnets, you assign a set of IP addresses to the site.

NOTE: The term "subnet" in AD DS does not have the strict networking definition of the set of all addresses behind a single router. The only requirement for an AD DS subnet is that the address prefix conforms to the IP version 4 (IPv4) or IP version 6 (IPv6) format.

A subnet object (objectClass=subnet) in AD DS stores the prefix information in its name and the site information in attribute siteObject. It should be created under “CN=Subnets,CN=Sites,<Configuration-NC>”. For documentation on “how to determine the prefix of a subnet” bing for the phrase “Entering Address Prefixes”.

I have written a function below called New-XADSubnet that creates a new subnet. It accepts the subnet-prefix, site name, description and location of the subnet as input. The function also does some validation to provide a reliable experience, for example: it validates whether the specified subnet prefix length is correct or not. If a prefix length is not specified then this function will auto-generate the prefix length based on the number of trailing zero bits found in the prefix-ip-address.

NOTE: The script below uses Test-XADObject function published in a previous post.

    1:  #
    2:  # Advanced Function to create a new subnet.
    3:  #
    4:  function New-XADSubnet() {
    5:     [CmdletBinding(ConfirmImpact="Low")]
    6:     Param (
    7:        [Parameter(Mandatory=$true,
    8:                   Position=0,
    9:                   ValueFromPipeline=$true,
   10:                   HelpMessage="Prefix of the subnet to be created. This should be a combination of IP Address followed by / and Prefix length. IPv4 example: 157.54.208.0/20  IPv6 example: 3FFE:FFFF:0:C000::/64 . NOTE: If the prefix length is not specified then this cmdlet will auto-generate the prefix length based on the number of trailing zero bits. For example: If supplied Prefix is 157.54.208.0 then the subnet created is 157.54.208.0/20. Another example: If supplied Prefix is 3FFE:FFFF:0:C000:: then the subnet created is 3FFE:FFFF:0:C000::/34"
   11:                  )]
   12:        [String] $Prefix,
   13:        [Parameter(Mandatory=$false,
   14:                   Position=1,
   15:                   ValueFromPipeline=$false,
   16:                   HelpMessage="Site to which the subnet will be applied. Accepts Site name, Guid, DN or ADObject representing the site"
   17:                  )]
   18:        [Object] $Site,
   19:        [Parameter(Mandatory=$false,
   20:                   ValueFromPipeline=$false,
   21:                   HelpMessage="Description"
   22:                  )]
   23:        [String] $Description,
   24:        [Parameter(Mandatory=$false,
   25:                   ValueFromPipeline=$false,
   26:                   HelpMessage="Location"
   27:                  )]
   28:        [String] $Location
   29:     )
   30:     PROCESS {
   31:   
   32:        if ([String]::IsNullOrEmpty($Prefix)) {
   33:           throw New-Object System.Management.Automation.PSArgumentException("Prefix name cannot be an empty string, please try again.")
   34:        }
   35:        
   36:        $newSubnetName = $Prefix
   37:        
   38:        if ($Prefix.Contains("/")) {
   39:            $subnetIPAddressStr,$prefixLengthStr = $newSubnetName.Split("/")
   40:            $subnetIPAddress = [System.Net.IPAddress]::Parse($subnetIPAddressStr)
   41:            $specifiedPrefixLength = [int]::Parse($prefixLengthStr)
   42:            
   43:            $ipAddressPrefixLength = GetIPAddressPrefixLength $subnetIPAddress
   44:            if ($ipAddressPrefixLength -gt $specifiedPrefixLength) {
   45:                throw New-Object System.Management.Automation.PSArgumentException("The subnet prefix length you specified is incorrect. Please check the prefix and try again.")
   46:            }
   47:            
   48:        } else {
   49:            $subnetIPAddress = [System.Net.IPAddress]::Parse($newSubnetName)
   50:            $prefixLength = GetIPAddressPrefixLength $subnetIPAddress
   51:            $newSubnetName = $newSubnetName + "/" + $prefixLength
   52:        }
   53:   
   54:        # Get the configuration partition DN, the sites container and build the new site DN
   55:        $configNCDN = (Get-ADRootDSE).ConfigurationNamingContext
   56:        $subnetContainerDN = ("CN=Subnets,CN=Sites," + $configNCDN)
   57:        $newSubnetDN = ("CN=" + $newSubnetName +"," + $subnetContainerDN)
   58:        $siteDN = $null
   59:        if ($Site -ne $null) {
   60:            $siteDN = (Get-XADSite $Site).DistinguishedName
   61:        }
   62:   
   63:        # Verify if the subnet already exists
   64:        $subnetExists = Test-XADObject -Identity $newSubnetDN
   65:        if ($subnetExists) {
   66:           throw New-Object System.Management.Automation.PSArgumentException("Subnet already exists. Please check the name and try again.")
   67:        }
   68:        
   69:        [Hashtable] $ht = new-object -type hashtable
   70:        if ($siteDN -ne $null) {
   71:            $ht.Add("siteObject", $siteDN)
   72:        }
   73:        if (-not [String]::IsNullOrEmpty($Description)) {
   74:            $ht.Add("description", $Description)
   75:        }
   76:        if (-not [String]::IsNullOrEmpty($Location)) {
   77:            $ht.Add("location", $Location)
   78:        }
   79:   
   80:   
   81:        # Create subnet object 
   82:        if ($ht.Count -eq 0) {
   83:            New-ADObject -Name $newSubnetName -Path $subnetContainerDN -Type subnet 
   84:        } else {
   85:            New-ADObject -Name $newSubnetName -Path $subnetContainerDN -Type subnet -OtherAttributes $ht
   86:        }
   87:   
   88:        # Fetch the subnet object
   89:        Get-ADObject $newSubnetDN -properties "siteObject", "description", "location"
   90:   
   91:      }
   92:  }
   93:   
   94:   
   95:   
   96:  #
   97:  # Internal utility function
   98:  # This function returns the number of trailing zeroes in the input byte
   99:  #
  100:  function GetNumberOfTrailingZeroes {
  101:  Param ([byte] $x)
  102:  $numOfTrailingZeroes = 0;
  103:  if ( $x -eq 0) {
  104:     return 8
  105:  }
  106:  if ( $x % 2 -eq 0) {
  107:     $numOfTrailingZeroes ++;
  108:     $numOfTrailingZeroes +=  GetNumberOfTrailingZeroes($x/2);
  109:  }
  110:  return $numOfTrailingZeroes
  111:  }
  112:   
  113:   
  114:   
  115:   
  116:  #
  117:  # Internal utility function
  118:  # This function returns the number of non-zero bits in an ip-address
  119:  #
  120:  function GetIPAddressPrefixLength {
  121:  Param ([System.Net.IPAddress] $ipAddress)
  122:  $byteArray = $ipAddress.GetAddressBytes()
  123:  $numOfTrailingZeroes = 0;
  124:  for ($i = $byteArray.Length - 1; $i -ge 0; $i--) {
  125:      $numOfZeroesInByte = GetNumberOfTrailingZeroes($byteArray[$i]);
  126:      if ($numOfZeroesInByte -eq 0) {
  127:         break;
  128:      }
  129:      $numOfTrailingZeroes += $numOfZeroesInByte;
  130:  }
  131:  (($byteArray.Length * 8) - $numOfTrailingZeroes)
  132:  }

Sample usage in my test environment:

PS AD:\>
PS AD:\> New-XADSubnet "10.10.0.0/16"  -Location "Redmond,WA" -Description "Redmond Subnet"

Description       : Redmond Subnet
DistinguishedName : CN=10.10.0.0/16,CN=Subnets,CN=Sites,CN=Configuration,DC=dsw
                    amipat-w7-vm1,DC=nttest,DC=microsoft,DC=com
location          : Redmond,WA
Name              : 10.10.0.0/16
ObjectClass       : subnet
ObjectGUID        : e4c924ff-ee39-47e3-8b92-71a8600188af

Cheers,

Swami

--

Swaminathan Pattabiraman

Developer – Active Directory Powershell Team

Comments

  • Anonymous
    October 29, 2010
    The comment has been removed
  • Anonymous
    July 26, 2011
    It seems the code references a function Get-XADSite which isn't defined in provided code. Can you please provide missing code?
  • Anonymous
    February 29, 2012
    The comment has been removed
  • Anonymous
    March 27, 2012
    I had to manually create the Get-XADSite function using code examples from the Part 3 of this series:blogs.msdn.com/.../active-directory-powershell-to-manage-sites-and-subnets-part-3-getting-site-and-subnets.aspxHere is the function that I put together:Get a specified Active Directory Site.    function Get-XADSite() {
    Param ([String] $siteName)$configNCDN = (Get-ADRootDSE).ConfigurationNamingContext$siteContainerDN = (&quot;CN=Sites,&quot; + $configNCDN)$siteDN = &quot;CN=&quot; + $siteName + &quot;,&quot; + $siteContainerDNreturn Get-ADObject -Identity $siteDN
    }I've tested this with the New-XADSite function and it works; just created 45 subnets and associated them with appropriate sites.
  • Anonymous
    May 14, 2012
    thancks  PS AD:>  PS AD:> New-XADSubnet "10.10.0.0/16"  -Location "Redmond,WA" -Description "Redmond Subnet"  Description       : Redmond Subnet  DistinguishedName : CN=10.10.0.0/16,CN=Subnets,CN=Sites,CN=Configuration,DC=dsw                      amipat-w7-vm1,DC=nttest,DC=microsoft,DC=com  location          : Redmond,WA  Name              : 10.10.0.0/16  ObjectClass       : subnet  ObjectGUID        : e4c924ff-ee39-47e3-8b92-71a8600188af    #      # Advanced Function to create a new subnet.      #      function New-XADSubnet() {         [CmdletBinding(ConfirmImpact="Low")]         Param (            [Parameter(Mandatory=$true,                       Position=0,                       ValueFromPipeline=$true,                       HelpMessage="Prefix of the subnet to be created. This should be a combination of IP Address followed by / and Prefix length. IPv4 example: 157.54.208.0/20  IPv6 example: 3FFE:FFFF:0:C000::/64 . NOTE: If the prefix length is not specified then this cmdlet will auto-generate the prefix length based on the number of trailing zero bits. For example: If supplied Prefix is 157.54.208.0 then the subnet created is 157.54.208.0/20. Another example: If supplied Prefix is 3FFE:FFFF:0:C000:: then the subnet created is 3FFE:FFFF:0:C000::/34"                      )]            [String] $Prefix,            [Parameter(Mandatory=$false,                       Position=1,                       ValueFromPipeline=$false,                       HelpMessage="Site to which the subnet will be applied. Accepts Site name, Guid, DN or ADObject representing the site"                      )]            [Object] $Site,            [Parameter(Mandatory=$false,                       ValueFromPipeline=$false,                       HelpMessage="Description"                      )]            [String] $Description,            [Parameter(Mandatory=$false,                       ValueFromPipeline=$false,                       HelpMessage="Location"                      )]            [String] $Location         )  30:     PROCESS {            if ([String]::IsNullOrEmpty($Prefix)) {               throw New-Object System.Management.Automation.PSArgumentException("Prefix name cannot be an empty string, please try again.")            }            $newSubnetName = $Prefix            if ($Prefix.Contains("/")) {                $subnetIPAddressStr,$prefixLengthStr = $newSubnetName.Split("/")                $subnetIPAddress = [System.Net.IPAddress]::Parse($subnetIPAddressStr)                $specifiedPrefixLength = [int]::Parse($prefixLengthStr)                $ipAddressPrefixLength = GetIPAddressPrefixLength $subnetIPAddress                if ($ipAddressPrefixLength -gt $specifiedPrefixLength) {                    throw New-Object System.Management.Automation.PSArgumentException("The subnet prefix length you specified is incorrect. Please check the prefix and try again.")                }            } else {                $subnetIPAddress = [System.Net.IPAddress]::Parse($newSubnetName)                $prefixLength = GetIPAddressPrefixLength $subnetIPAddress                $newSubnetName = $newSubnetName + "/" + $prefixLength            }            # Get the configuration partition DN, the sites container and build the new site DN            $configNCDN = (Get-ADRootDSE).ConfigurationNamingContext            $subnetContainerDN = ("CN=Subnets,CN=Sites," + $configNCDN)            $newSubnetDN = ("CN=" + $newSubnetName +"," + $subnetContainerDN)            $siteDN = $null            if ($Site -ne $null) {                $siteDN = (Get-XADSite $Site).DistinguishedName            }            # Verify if the subnet already exists          $subnetExists = Test-XADObject -Identity $newSubnetDN            if ($subnetExists) {               throw New-Object System.Management.Automation.PSArgumentException("Subnet already exists. Please check the name and try again.")            }            [Hashtable] $ht = new-object -type hashtable            if ($siteDN -ne $null) {                $ht.Add("siteObject", $siteDN)            }            if (-not [String]::IsNullOrEmpty($Description)) {                $ht.Add("description", $Description)            }            if (-not [String]::IsNullOrEmpty($Location)) {                $ht.Add("location", $Location)            }            # Create subnet object            if ($ht.Count -eq 0) {                New-ADObject -Name $newSubnetName -Path $subnetContainerDN -Type subnet            } else {                New-ADObject -Name $newSubnetName -Path $subnetContainerDN -Type subnet -OtherAttributes $ht            }            # Fetch the subnet object            Get-ADObject $newSubnetDN -properties "siteObject", "description", "location"          }      }      #      # Internal utility function      # This function returns the number of trailing zeroes in the input byte      # 100:  function GetNumberOfTrailingZeroes {    Param ([byte] $x)    $numOfTrailingZeroes = 0;    if ( $x -eq 0) {       return 8    }    if ( $x % 2 -eq 0) {       $numOfTrailingZeroes ++;       $numOfTrailingZeroes +=  GetNumberOfTrailingZeroes($x/2);    } 110:  return $numOfTrailingZeroes    }    #    # Internal utility function    # This function returns the number of non-zero bits in an ip-address    #    function GetIPAddressPrefixLength {    Param ([System.Net.IPAddress] $ipAddress)    $byteArray = $ipAddress.GetAddressBytes()    $numOfTrailingZeroes = 0; 124:  for ($i = $byteArray.Length - 1; $i -ge 0; $i--) {        $numOfZeroesInByte = GetNumberOfTrailingZeroes($byteArray[$i]);        if ($numOfZeroesInByte -eq 0) {           break;        }        $numOfTrailingZeroes += $numOfZeroesInByte;    }    (($byteArray.Length * 8) - $numOfTrailingZeroes)    }