/\/\o\/\/ PowerShelled

This blog has moved to http://ThePowerShellGuy.com Greetings /\/\o\/\/
$AtomFeed = ("Atom.xml")
$PreviousItems = (" TechEd RoundUp "," A big hurray for the Scripting Guy ! "," Teched "," PowerShell Tab Completion Part 4 "," The DFO Show - Introducing Windows PowerShell "," PowerShell Tab Completion Part 3 of more ? "," PowerShell Tab Completion Part 2 "," The Microsoft Code "," Powershell links and free E-book "," PowerShell Tab Completion "," ")

Friday, June 23, 2006

 


PowerShel and Active Directory Part 1



In my 200th post already (as I did just see) as promised here the start of the Active Directory Management series.

In this first Part I will show how to make a Active Directory Object in Powershell,
connect to a domain, list Properties and Methods of the Object, and how to get the child Objects and store them in a Variable again.We will do all of this interactive from the commandline,

there are no CMDlets yet for Active directory management in PowerShell for version 1,
but as we can use .NET objects from powershell we are still able to manage Activedirectory from Powershell.

the .NET Classes we need are in the namespace system.directoryservices..
(This naming is because as you can connect not only to AD but to an other directoryservices using LDAP as well. )
in .net 2.0 we also have a activedirectory namespace for infrastructure management but more about that later in the Series.

The object we are going to use for this is called a Directoryentry.

So let's start by making a connection to Active Directory. by creating an DirectoryEntry object,
as you might have seen before, to get a .NET object we need the command new-object :


*Warning* As you have RC2 Installed all my AD posts will not work anymore, it is verry confusing, but fixable if you read explaination here PowerShell RC2 and Active Directory :


As I did switch back on all my work PC's (I did call all my colleagues not to upgrade as all our AD tools are messed up, I will not update them, as I would not recommend to upgrade to RC2 of you work with AD a lot.


PoSH> New-Object System.DirectoryServices.DirectoryEntry

distinguishedName                                                                                                     
-----------------                                                                                                     
{DC=mow,DC=local}


and as you can see as a result we get back the distinguishedName of the domain,

there are 2 things to note here,

you can see that the directoryentry constructed without parameters defaults to the default naming context, that is the current domain, in this case mow.local .

second thing to note is that as we just throw the object to the pipeline, so it gets the default formatting and after that its gone.

as I want to use it a bit more, I will put it into a variable so we can keep using it. I just use the arrow-up to get the previous line again, and Home to get to the beginning of the line and type '[variablename] = '. in front of it.
I like to work this way to first check if I get back the object, and then if it is OK I put it into a variable, otherwise I need to check the variable everytime to see if the command succeeded.

as this is connecting us to the root namespace of the domain, I will call the variable $root.

PoSH>$root = New-Object System.DirectoryServices.DirectoryEntry
PoSH>$root

distinguishedName                                                                                                     
-----------------                                                                                                     
{DC=mow,DC=local}


You can see we now have the Object in a Variable and can start using it

next, as the default formatter only displays the distinguishedName of the directoryentry object.

I use Format-List * ( by the Alias FL )to show all properties.

# Get all Properties 

$root | fl *


you can also get the Methods and Properties of the Object by Using Get-Member :
so next lets ask the directoryEntry Object what more it can do :

# Get All Members 

$root  | get-Member

# Or to get only the Methods :

PoSH> $root | get-member -MemberType method


   TypeName: System.DirectoryServices.DirectoryEntry

Name                      MemberType Definition                                                                       
----                      ---------- ----------                                                                       
add_Disposed              Method     System.Void add_Disposed(EventHandler value)                                     
Close                     Method     System.Void Close()                                                              
CommitChanges             Method     System.Void CommitChanges()                                                      
CopyTo                    Method     System.DirectoryServices.DirectoryEntry CopyTo(DirectoryEntry newParent), Syste...
CreateObjRef              Method     System.Runtime.Remoting.ObjRef CreateObjRef(Type requestedType)                  
DeleteTree                Method     System.Void DeleteTree()                                                         
Dispose                   Method     System.Void Dispose()                                                            
Equals                    Method     System.Boolean Equals(Object obj)                                                
get_AuthenticationType    Method     System.DirectoryServices.AuthenticationTypes get_AuthenticationType()            
get_Children              Method     System.DirectoryServices.DirectoryEntries get_Children() 
.....
.....


and hmmm, this get_children method looks handy,
let's try : $root.get_Children()

PoSH> $root.get_Children()

distinguishedName                                                                                                     
-----------------                                                                                                     
{CN=Builtin,DC=mow,DC=local}                                                                                          
{CN=Computers,DC=mow,DC=local}                                                                                        
{OU=Domain Controllers,DC=mow,DC=local}                                                                               
{CN=ForeignSecurityPrincipals,DC=mow,DC=local}                                                                        
{CN=Infrastructure,DC=mow,DC=local}                                                                                   
{CN=LostAndFound,DC=mow,DC=local}                                                                                     
{OU=MowOu,DC=mow,DC=local}                                                                                            
{CN=NTDS Quotas,DC=mow,DC=local}                                                                                      
{CN=Program Data,DC=mow,DC=local}                                                                                     
{CN=System,DC=mow,DC=local}                                                                                           
{CN=Users,DC=mow,DC=local}


Nice just what I needed as there is not much to do in the root of the ActiveDirectory, so lets look if we can connect to one of the SubOU's
You would expext that you can just use the indexer to get to the wanted OU from the Array returned like this :

PoSH> $root.get_Children()[6]
Unable to index into an object of type System.DirectoryServices.DirectoryEntries.
At line:1 char:22


This does not work, you can see the reason for this in the Errormessage,
we did not get back a collection of DirectoryEntry Objects, but one instance of another .NET class : System.DirectoryServices.DirectoryEntries
You can also check on this by asking it for it's type :

# Check the type 

$root.get_Children().GetType()


Note that normaly GM (Get_member) will not show this as it does enumerate the Object ( ?? and you just said that it was not a collection.. nope it is not and it has no indexer but it has a GetEnumerator() Method, so the pipeline of PowerShell does Enumerate on it, and get-member will show the members of the Items that got enumerated, but will show them only one for every type thrown to the pipeline)

PoSH> $root.get_Children() | gm

   TypeName: System.DirectoryServices.DirectoryEntry

Name                      MemberType Definition                                                                       
----                      ---------- ----------                                                                       
add_Disposed              Method     System.Void add_Disposed(EventHandler value)                                     
Close                     Method     System.Void Close()                                                              
CommitChanges             Method     System.Void CommitChanges()


you can also give the object as a parameter to get-member to avoid this
Get-Member -inputObject $root

or a workaround I like is put a comma before the object (again as I can use "Arrow Up" to get the last line this change is much easier in interactive use when you need members of the collection not the instances):

,$root.get_Children() | gm


   TypeName: System.DirectoryServices.DirectoryEntries

Name             MemberType Definition                                                                                
----             ---------- ----------                                                                                
Add              Method     System.DirectoryServices.DirectoryEntry Add(String name, String schemaClassName)          
Equals           Method     System.Boolean Equals(Object obj)                                                         
Find             Method     System.DirectoryServices.DirectoryEntry Find(String name), System.DirectoryServices.Dire...
get_SchemaFilter Method     System.DirectoryServices.SchemaNameCollection get_SchemaFilter()                          
GetEnumerator    Method     System.Collections.IEnumerator GetEnumerator()                                            
GetHashCode      Method     System.Int32 GetHashCode()                                                                
GetType          Method     System.Type GetType()                                                                     
Remove           Method     System.Void Remove(DirectoryEntry entry)                                                  
ToString         Method     System.String ToString()                                                                  
SchemaFilter     Property   System.DirectoryServices.SchemaNameCollection SchemaFilter {get;} 



Now we can see its not an array, but we can workaround this lack of an by explicitly making it an array by casting it like this :

@($root.get_Children())[6]

distinguishedName                                                                                                     
-----------------                                                                                                     
{OU=MowOu,DC=mow,DC=local}


but as you also might have seen in the get-member output, the directoryentries class also has a find method, that has a name as a parameter and.
that will find and return a specific child

$root.get_Children().find('ou=mowOu')

distinguishedName                                                                                                     
-----------------                                                                                                     
{OU=MowOu,DC=mow,DC=local}

# and store in a variable again for later usage

$mowOU = $root.get_Children().find('ou=mowOu')

# and list all properties again.

Posh> $MowOu | fl *


objectClass          : {top, organizationalUnit}
ou                   : {MowOu}
distinguishedName    : {OU=MowOu,DC=mow,DC=local}
instanceType         : {4}
whenCreated          : {5/18/2006 7:15:13 PM}
whenChanged          : {5/18/2006 7:15:13 PM}
uSNCreated           : {System.__ComObject}
uSNChanged           : {System.__ComObject}
name                 : {MowOu}
objectGUID           : {139 153 183 252 115 3 24 73 145 12 14 36 64 30 237 202}
objectCategory       : {CN=Organizational-Unit,CN=Schema,CN=Configuration,DC=mow,DC=local}
nTSecurityDescriptor : {System.__ComObject}


note that this is also a DirectoryEntry Object but it will have other properties as it as a OU Object not the domain.

and from here we can use the some method to list the objects (Users / Computers / Sub-OU's)

$mowou.get_children()

# and for example get a user into a variable to edit it etc :

$mow = $mowou.get_children().find('cn=mow')


note that this is not the only way to get to the sub-OU, If you know the LDAP Path you can pass it in the constructor and connect to the OU directly :
(also note that you can leave away the system namespace as PowerShell will append it)

# connect directly to an LDAP path :

$mowOu = New-Object DirectoryServices.DirectoryEntry("LDAP://OU=MowOu,DC=mow,DC=local")


In the next part of this series we will create a User, and see how to create a loop to make more as 1 user at a time,
and fill some more properties.

later in this series we will use the DirectorySearcher to do searches in AD

But for now by creating one .NET Object and only the 3 most basic PowerShell Cmdlets (new-object / format-(List/Table) / get-member ) we already can :

Connect to AD
List properties of AD Objects
List methods of AD Objects
Walk to the Domain structure to wanted OU
Connect directly to SubOU's
List members of OU's

and the only thing we have to remember is that we need a directoryServices.DirectoryEntry Object.
It will find out the domain / LDAP path for us if we do not know it, and we can use get-member to remember / learn us what the object can do.and to explore the objects we get back.

So as you see it is not as hard as i might seem being without Cmdlets.

More in next post, till then try walking to different parts of AD this way and look at Computer / Users Objects,and do Format-List * and get-Member on the returned objects, to get used to the DirectoryObject and learn for the output you get back,
(for a preview of the next part Creating a user as in the Demo, note the Add() method on the directoryEntries class)
Feel free to leave remarks or questions in the remarks.

Enjoy,

Greetings /\/\o\/\/
Tags :


Comments:
Anonymous Anonymous
Great stuff MOW! Thanks again for the awesome TechEd Demo.

http://blogs.msdn.com/powershell/archive/2006/06/24/645876.aspx

Jeffrey Snover
Windows PowerShell Architect
 
Anonymous Anonymous
Great post! One remark, one question:

- typo in your title
- I heard that Powershell will offer first-class support for ADSI in the near future. What's your idea about that?
 
Blogger /\/\o\/\/
Wkasdo,

1) type in the Title, Yes I did notice that, onlyits hard to change as the link will change.

2) You might have a look here :
in this post Jeffrey Snover gives some info about the changes in WMI support for RC2.

http://blogs.msdn.com/powershell/archive/2006/06/26/647038.aspx

Greetings /\/\o\/\/
 
Anonymous Anonymous
That was very clear, MOW. However, another question - how would I connect to another forest where I have domain admin rights to the top level domain?
 
Blogger /\/\o\/\/
@ sluice,

if you have a trust it should be just using the correct path.


Greetings /\/\o\/\/
 
Anonymous Anonymous
/\/\o\/\/,
Through MSH, can you change a user's terminal services profile directory address and drive letter?
 
Blogger /\/\o\/\/
Yes, you can do it.

That was for part 3 but here a preview :

$mow is my userobject

PoSH>$mow.InvokeGet('TerminalservicesHomeDrive')
J:
PoSH>$mow.InvokeSet('TerminalservicesHomeDrive',"z:")
PoSH>$mow.CommitChanges()
PoSH>$mow.InvokeGet('TerminalservicesHomeDrive')
Z:

PoSH>$mow.InvokeGet('TerminalservicesProfilePath')
c:\user\mow

Greetings /\/\o\/\/
 
Anonymous Anonymous
MoW,
How can I enumerate all the sub OUs and subsubOUs in a domain? Is there a way to recurse the get_children() method?
 
Blogger /\/\o\/\/
sluice,

You can use a directorysearcher for this :

$Searcher = New-Object DirectoryServices.DirectorySearcher

$searcher.filter = "objectClass=organizationalUnit"
$Searcher.findall()

More about that in a later post in the series,I allready wanted to post it before but my computer at home is broken at the moment so it will take a bit more time.

Greetings /\/\o\/\/


Greetings /\/\o\/\/
 
Post a Comment



<< Home

Archives

October 2005   November 2005   December 2005   January 2006   February 2006   March 2006   April 2006   May 2006   June 2006   July 2006   August 2006   September 2006   October 2006   November 2006   December 2006  

$Links = ("PowerShell RC1 Docs"," PowerShell RC1 X86"," PowerShell RC1 X64"," Monad GettingStarted guide"," Monad Progamming Guide"," Monad SDK"," Monad videos on Channel 9"," MSH Community Workspace"," scripts.readify.net "," MonadSource"," www.reskit.net"," PowerShell Blog"," Under The Stairs"," computerperformance powershell Home"," proudlyserving"," MSH on wikipedia"," MSHWiki Channel 9"," Keith Hill's Blog"," Precision Computing"," PowerShell for fun"," MSH Memo (Japanese)"," monadblog")

find-blog -about "PowerShell","Monad" | out-Technorati.
find-blog -contains "","" | out-Technorati.
Google
 
Web mow001.blogspot.com

This page is powered by Blogger. Isn't yours?