In this opportunity I am going to provide a solution to manage sites in your Active Directory (AD) forest by extending Active Directory PowerShell by implementing functions that allow creation, retrieval, update (moving to a site link, renaming) and deletion of sites.

First of all we need to understand what an AD site is made of. We can say that an AD site is represented by three directory objects and a reference in an attribute in a different object (i.e. the Site Link object). In detail an AD site is a compound of:

1. The site object (objectClass=site) which is an object under the sites object in the configuration naming context.

2. The NTDS site settings object (objectClass=nTDSSiteSettings) a child object under the site object

3. The servers containers object (objectClass=serversContainer) a child object under the site object

4. A value in the multi-valued attribute siteList in the site link object.

In the first place we need to know the sites container distinguished name (DN). This object has a relative distinguished name (RDN) with value Sites RDN under the configuration naming context. The following shows how to build the DN string for the sites container:

      $configNCDN = (Get-ADRootDSE).ConfigurationNamingContext

      $sitesContainerDN = ("CN=Sites," + $configNCDN)

The new site object will be an object under the sites container object:

      $newSiteDN = ("CN=" + $newSiteName +"," + $sitesContainerDN)

We can use the New-ADObject cmdlet to create the Site, NTDS Site Settings and Servers container objects under the Sites container object:

      New-ADObject -Name $newSiteName -Path $sitesContainerDN -Type site

      New-ADObject -Name "NTDS Site Settings" -Path $newSiteDN -Type nTDSSiteSettings

      New-ADObject -Name "Servers" -Path $newSiteDN -Type serversContainer

Now to add the site to the site link we will need a reference to the site link object. The site link objects are located under the given inter-site transport object (IP or SMTP). For example for DEFAULTIPSITELINK the site link container location will be:

      $siteLinkContainer = ("CN=DEFAULTIPSITELINK,CN=IP,CN=Inter-Site Transports,CN=Sites," + $configNCDN)

The last step is to add the site to the site link by including the site name in the siteList attribute in the respective site link object. To modify the siteList attribute in the site link object we will need to get the site link object first with the siteList attribute using using the Get-ADObject cmdlet to create an instance and then modify it using the Set-ADObject cmdlet:

      $siteLink = Get-ADObject $siteLinkContainer -Properties siteList

      $siteLink.siteList.Add($newSiteDN)

      Set-ADObject -Instance $siteLink

And that will be it.

I have created the function below called New-XADSite which receives the site name and the site link name (IP based, you can do a small modification to add SMTP based site links). Also it contains special cases validations which provide you a more reliable experience if you want to use the function in your AD forests. In future posts I will show you how to extend AD PowerShell to include functions to get the site names of the forest, moving a site to a different site link, renaming a site and removing a site in the forest. Notice that the script uses the function Test-XADObject which is implemented in a previous posting.

   1:  function New-XADSite() {
   2:     [CmdletBinding(ConfirmImpact="Low")]
   3:     Param (
   4:        [Parameter(Mandatory=$true,
   5:                   Position=0,
   6:                   ValueFromPipeline=$true,
   7:                   HelpMessage="Identity of the site to be created. Name is the only supported identity for this version of the script."
   8:                  )]
   9:        [Object] $Identity,
  10:        [Parameter(Mandatory=$false,
  11:                   Position=1,
  12:                   ValueFromPipeline=$false,
  13:                   HelpMessage="Site link name to which the site will belong."
  14:                  )]
  15:        [Object] $SiteLinkName
  16:     )
  17:   
  18:     BEGIN {
  19:   
  20:     }
  21:   
  22:     PROCESS {
  23:   
  24:        # In this version of the script the only identity supported is the site name
  25:        # Replace the line below to support other identities
  26:        $newSiteName = $Identity
  27:   
  28:        if ([String]::IsNullOrEmpty($newSiteName)) {
  29:           throw New-Object System.Management.Automation.PSArgumentException("Site name cannot be an empty string, please try again.")
  30:        }
  31:   
  32:        if ($SiteLinkName -eq $null) {
  33:           $SiteLinkName = "DEFAULTIPSITELINK"
  34:        }
  35:   
  36:        # Get the configuration partition DN, the sites container and build the new site DN
  37:        $configNCDN = (Get-ADRootDSE).ConfigurationNamingContext
  38:        $sitesContainerDN = ("CN=Sites," + $configNCDN)
  39:        $newSiteDN = ("CN=" + $newSiteName +"," + $sitesContainerDN)
  40:   
  41:        $siteLinkContainerDN = ("CN=" + $SiteLinkName + ",CN=IP,CN=Inter-Site Transports,CN=Sites," + $configNCDN)
  42:        # Verify if the site link exists
  43:        $siteLinkExists = Test-XADObject -Identity $siteLinkContainerDN
  44:        if ($siteLinkExists -eq $false) {
  45:           throw New-Object System.Management.Automation.RuntimeException("The site link you provided does not exist. Please check the name and try again.")
  46:        }
  47:   
  48:        # Verify if the site already exists
  49:        $siteExists = Test-XADObject -Identity $newSiteDN
  50:        if ($siteExists) {
  51:           throw New-Object System.Management.Automation.RuntimeException("Site already exists. Please check the name and try again.")
  52:        }
  53:        else {
  54:           $siteObjectCreated = $false
  55:   
  56:           trap [Exception] {
  57:              # Output the error to the pipeline
  58:              Write-Host $_
  59:   
  60:              # Remove any objects created for consistency
  61:              if ($siteObjectCreated -eq $true) {
  62:                 Remove-ADObject -Identity $newSiteDN -Recursive -Confirm:$false
  63:              }
  64:   
  65:              # Throw a new exception with the trapped exception as inner exception
  66:              throw New-Object System.Management.Automation.RuntimeException("An exception occurred when creating the site. See inner exceptions for more details.", $_.Exception)
  67:           }
  68:   
  69:           # Create Site, NTDS Site Settings and Server objects
  70:           New-ADObject -Name $newSiteName -Path $sitesContainerDN -Type site
  71:           $siteObjectCreated = $true
  72:           New-ADObject -Name "NTDS Site Settings" -Path $newSiteDN -Type nTDSSiteSettings
  73:           New-ADObject -Name "Servers" -Path $newSiteDN -Type serversContainer
  74:   
  75:           $siteLink = Get-ADObject $siteLinkContainerDN -Properties siteList
  76:           $numberOfSitesInSiteLink = $siteLink.siteList.Add($newSiteDN)
  77:           Set-ADObject -Instance $siteLink
  78:   
  79:           # Return the directory objects that make the AD site
  80:           Get-ADObject -Identity $newSiteDN
  81:           Get-ADObject -Identity ("CN=NTDS Site Settings," + $newSiteDN)
  82:           Get-ADObject -Identity ("CN=Servers," + $newSiteDN)
  83:        }
  84:   
  85:      }
  86:   
  87:      END {
  88:   
  89:      }
  90:  }

Enjoy,

Jairo Cadena

Active Directory