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: http://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 (5)

  1. Brandon Lockhart says:

    Do you have a copy of this script without the line numbering?

  2. It seems the code references a function Get-XADSite which isn't defined in provided code. Can you please provide missing code?

  3. Kelvin Wong says:

    Do you have the script to be downloaded? The codes are clipped off in the blog.

  4. 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.aspx

    Here 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; + $siteContainerDN
    
    return 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.

  5. Coco says:

    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)

        }