Richard's profileRichard Siddaway's BlogPhotosBlogLists Tools Help

Blog


    February 19

    Accessing AD with PowerShell

    With the introduction of Windows 2000 and Active Directory seven years ago Microsoft began a process of increasing the emphasis placed on the command line for administration. There are many tasks that it is more convenient to perform from the command line when administering Active Directory. Microsoft has introduced a set of command line tools dsquery etc. specifically for querying and modifying Active Directory. These tools enable the commonest tasks to be performed but for real low level access to the Active Directory the recommended approach for administrators has been via scripting – using VBScript. The examples in the various books that have been produced since 2000 and the collection of scripts on the TechNet script center practically all use VBScript.

    The following script can be used to create an OU

    Set objDomain = GetObject("LDAP://dc=manticore,dc=org")

    Set objOU = objDomain.Create("organizationalUnit", "ou=Sphinx")

    objOU.SetInfo

    The first line creates an ADSI object bound to the Active Directory domain. The second line creates the OU and the third line performs the action that actually creates a permanent instance of the object in the Active Directory. Until SetInfo is called the object is not actually created.

    ADSI is short for Active Directory Services Interface. It is a COM object that can be used through any programming language that can instantiate a COM object. The ADSI SDK can be accessed online at http://msdn2.microsoft.com/en-us/library/aa772170.aspx. ADSI can be used to access other directory stores including:

    • Windows NT SAM
    • Novell Directory Services
    • Novell Bindery
    • IIS

    With the introduction of PowerShell that leaves us the question of how do we access Active Directory with PowerShell. At the moment there are three main ways of achieving this goal.

    • Use [ADSI] constructor gives access to the same ADSI methods and properties that we are used to using with VBScript
    • Use System.DirectoryServices which is a .NET wrapper for the ADSI object. This functionality was introduced in .NET 1.0
    • Use System.DirectoryServices.ActiveDirectory which is a set of .NET classes that either build on System.DirectoryServices or access the specific APIs Microsoft have provided for accessing Active Directory. This functionality was introduced in .NET 2.0.

    This is all a little confusing especially for a beginner to PowerShell or someone who is not familiar with the .NET framework. I will provide examples of how these various options are used. In order to demonstrate how all of these options are used we need to change to creating a site object. The System.DirectoryServices.ActiveDirectory Namespace only supports a subset of the Active Directory objects as it is intended to be used for the administration of Active Directory rather than accessing data.

    The following VBScript to create a site will be used as a reference in the discussion.

    ' get sites container

    set objRootDSE = GetObject("LDAP://RootDSE")

    set objSitesCont = GetObject("LDAP://cn=sites," & objRootDSE.Get("configurationNamingContext"))

    'Create site

    set objSite = objSitesCont.Create("site","cn=MyNewSite")

    objSite.SetInfo

    'Create licensing site settings object

    set objLicensing = objSite.Create("licensingSiteSettings", "cn=Licensing Site Settings")

    objLicensing.SetInfo

    'Create NTDS Site Settings object

    set objNTDS = objSite.Create("nTDSSiteSettings", "cn=NTDS Site Settings")

    objNTDS.SetInfo

    'Create servers container

    set objServersCont = objSite.Create("serversContainer", "cn=Servers")

    objServersCont.SetInfo

     

    If we start by using the [ADSI] constructor we end up with the following script.

    # get sites container

    $Root = [ADSI]"LDAP://RootDSE"

    $SitesCont = [ADSI]("LDAP://cn=sites," + $Root.Get("configurationNamingContext"))

    #Create site

    $Site = $SitesCont.Create("site","cn=MyNewSite2")

    $Site.SetInfo()

    #Create licensing site settings object

    $Licensing = $Site.Create("licensingSiteSettings", "cn=Licensing Site Settings")

    $Licensing.SetInfo()

    #Create NTDS Site Settings object

    $NTDS = $Site.Create("nTDSSiteSettings", "cn=NTDS Site Settings")

    $NTDS.SetInfo()

    #Create servers container

    $ServersCont = $Site.Create("serversContainer", "cn=Servers")

    $ServersCont.SetInfo()

     

    Notice how the methods and syntax are so similar to the VBScript version. This is by design and allows a fairly straight forward method of migrating from using VBScript to access Active Directory to using PowerShell. The [ADSI] construction in effect gives the effect as Get-Object in VBScript. There is one major issue with using this approach if you create the $Site object for instance and perform a get-member on it the methods and properties such as SetInfo that you accessed through VBScript do NOT show. Get-member returns $site as a directory entry object and shows the .NET methods and properties as discussed below. The COM type methods accessed through VBScript are effectively hidden and need referencing via the ADSI documentation.

    The next stage is to look at using the .NET System.DirectoryServices Namespace which is a .NET wrapper for ADSI. We can take our script and rewrite it to give the following code.

    # get sites container

    $Root = [System.DirectoryServices.DirectoryEntry] "LDAP://RootDSE"

    $SitesCont = [System.DirectoryServices.DirectoryEntry]("LDAP://cn=sites," + $Root.Get("configurationNamingContext"))

    #Create site

    $Site = $SitesCont.Create("site","cn=MyNewSite3")

    $Site.psbase.CommitChanges()

    #Create licensing site settings object

    $Licensing = $Site.Create("licensingSiteSettings", "cn=Licensing Site Settings")

    $Licensing.psbase.CommitChanges()

    #Create NTDS Site Settings object

    $NTDS = $Site.Create("nTDSSiteSettings", "cn=NTDS Site Settings")

    $NTDS.psbase.CommitChanges()

    #Create servers container

    $ServersCont = $Site.Create("serversContainer", "cn=Servers")

    $ServersCont.psbase.CommitChanges()

     

    The major changes here are that we use [System.DirectoryServices.DirectoryEntry] instead of [ADSI] and that we use CommitChanges() instead of SetInfo(). Note that the .NET method CommitChanges() is just a wrapper for the COM SetInfo method. The other thing to notice is the use of psbase in the lines that call CommitChanges(). A PowerShell object is not always a pure .NET object and will often have a PowerShell created wrapper around it. If you put the latest version of our site creation code in a script and run it as . ./script.ps1 i.e. dot source it so that it runs in the same context as your main PowerShell process you will find that the objects used within the script are still available. Try the following commands

    $Site | get-member

    $Site.psbase | get-member

    The first command will return the properties of the Active Directory object referenced by $Site whereas the second returns the methods and properties of the .NET object $Site. More on PowerShell objects and their properties another time. In order to access the .NET methods and properties we need to use psbase to reference the base object.

    The third and final method of accessing Active Directory is by using System.DirectoryServices.ActiveDirectory which will make life a lot easier for us but does not provide everything we need as it is intended for administering Active Directory not the data held within Active Directory.

    ## get current forest and set context

    $for = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()

    $fortyp = [System.DirectoryServices.ActiveDirectory.DirectoryContexttype]"forest"

    $forcntxt = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext($fortyp, $for)

    ## create the site

    $site = new-object System.DirectoryServices.ActiveDirectory.ActiveDirectorySite($forcntxt, "MyNewSite4")

    $site.Save()

    By using this approach we can greatly simplify some administrative tasks. For some of its tasks System.DirectoryServices.ActiveDirectory will bypass ADSI and use other APIs which gives in effect a .NET approach to manipulating Active Directory. By getting a direct reference to the forest and its context we are performing the same steps as in earlier code where we bind to RootDSE and the configurationNamingContext. It is then a simple matter to create the site and save the object back to Active Directory. Note that the associated Servers, NTDS and Licensing objects are all created automatically.

    We have seen how to use PowerShell to access Active Directory. Until we have a set of cmdlets for accessing and manipulating Active Directory similar to those available for Exchange 2007 we need to code our scripts using one of the methods described above. All three methods will work – it is really a matter of picking the one that you feel comfortable with and hopefully making the best use of the available options.  

    Comments

    Please wait...
    Sorry, the comment you entered is too long. Please shorten it.
    You didn't enter anything. Please try again.
    Sorry, we can't add your comment right now. Please try again later.
    To add a comment, you need permission from your parent. Ask for permission
    Your parent has turned off comments.
    Sorry, we can't delete your comment right now. Please try again later.
    You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
    Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
    Complete the security check below to finish leaving your comment.
    The characters you type in the security check must match the characters in the picture or audio.

    To add a comment, sign in with your Windows Live ID (if you use Hotmail, Messenger, or Xbox LIVE, you have a Windows Live ID). Sign in


    Don't have a Windows Live ID? Sign up

    Trackbacks (1)

    The trackback URL for this entry is:
    http://richardsiddaway.spaces.live.com/blog/cns!43CFA46A74CF3E96!241.trak
    Weblogs that reference this entry