/\/\o\/\/ PowerShelled

This blog has moved to http://ThePowerShellGuy.com Greetings /\/\o\/\/
$AtomFeed = ("Atom.xml")
$PreviousItems = (" PowerShell "," PowerShell : How Do I randomize a list, and remove... "," PowerShell : Can you do that less cryptic ? "," PowerShell : How can I tell whitch numbers are mis... "," PowerShell Active Directory Browser teaser "," Powershell, Has my Dell a dangerous battery Part 2... "," PowerShell, Has my Dell a dangerous battery ? "," PowerShell Adding -verbose switch to functions "," PowerShell and Active Directory Part 9 (nested gro... "," Powershell and hey Scripting guy ! "," ")

Tuesday, September 05, 2006

 


PowerShell : Active Directory Part 10 (AD Browser)



For this part 10 of this series, I'v made a GUI Active Directory Browser script in PowerShell.



*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.


*Edit 2* an RC2 version of this script you can find in the Comments, updated by Jakke, Thanks Jakke !

In the first post in this Active Directory series, PowerShell and Active Directory Part 1 , I showed how you could connect to the root of an Active Directory Domain,


$root = New-Object System.DirectoryServices.DirectoryEntry


Get a Sub OU :


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


Connect directly to an Active Directory Object : 


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


And how to use them on the CommandLine,


From there on, we amongst others,


Listed and created AD Objects, as OU's Users and Groups, Changed properties, Used methods, Exported and Imported/Created the Objects (Users) to and from a CSV file and did set ACL's to AD Objects.


But if you need to connect to a SubOU deep in the AD tree, it is hard to get at it this way, we or need a lot of Get_Children() and Find() Methods, or a long LDAP path.


In next post we will so how to search for Objects in Active Directory using a DirectorySearcher, and how you can connect to the Active Directory Object from the results,that also will solve part of this problem.


But most of the times  would also be handy to be be able to just browse through the AD Tree to the Object you need and then use it in PowerShell to perform some actions.


I Made a Script for this in PowerShell  : BrowseActiveDirectory.ps1


This Script will connect to the Root of the Domain or to custom root (a Subcontainer supplied as a DirectoryEntry Object, if you did follow the rest of the series, I hope this makes perfect sense ;-) ) and if this succeeds builds a Form that contains a TreeView Object, that you can use to browse to the AD object you  need, and then Returns it so you can use it in PowerShell.


This Form, (as you might have seen in my Teaser), Looks like this (click picture to enlarge)



In this case I walked to the OU : MowOU, I also used in former examples, when the Active Directory Browser will Startup you will only see the Root Object with the DN of the AD Object it represents.


for performance reasons, not the Whole AD-tree will be read when starting up, but only when you select a Node in the TreeView, the Children of the AD object it presents get enumerated and there DN's are added to the TreeView, we will use events from the TreeView to do this.


When you use the Select Button or hit Enter, the DirectoryEntry will be retrieved and Passed back to the pipeline so  you can Catch it in the PowerShell console and put it into a variable for further use.


I like this GUI form as a quick way to get to the Object I want in AD, it is much quicker then getting there from the commandline, or it also much easier to explore and look at the structure of the ActiveDirectory domain this way. 


As already mentioned in  PowerShell and Active Directory Part 8 (ACL's) , I like to just use "loose lines of Code" or functions, that you just can just past into the Commandline, so most of my examples are given that way


And I gave some more examples and explained a bit more about the difference with Scripts here :PowerShell, Has my Dell a dangerous battery ? ,


This script I made Hybrid, you can start it directly or start it to load the function, I did add this function as a Switch Parameter,


so Next to Using the script Directly like this :


 



PoSH>$De = .\ActiveDirectoryBrowser.Ps1
PoSH>$De = fl *
PoSH>$De = .\ActiveDirectoryBrowser.Ps1
PoSH>$De | fl *

objectClass : {top, organizationalUnit}
ou : {MowSubOu}
distinguishedName : {OU=MowSubOu,OU=MowOu,DC=mow,DC=local}
instanceType : {4}
whenCreated : {6/26/2006 6:59:59 PM}
whenChanged : {6/26/2006 6:59:59 PM}
uSNCreated : {System.__ComObject}
uSNChanged : {System.__ComObject}
name : {MowSubOu}
objectGUID : {162 13 61 122 139 39 201 72 161 216 129 101 53 217 180 114}
objectCategory : {CN=Organizational-Unit,CN=Schema,CN=Configuration,DC=mow,DC=local}
nTSecurityDescriptor : {System.__ComObject}

# Use this as new Root


PoSH>.\ActiveDirectoryBrowser.Ps1 $de


You can also use the Browse-ActiveDirectory Function that does all the work in the Script, by  Loading it into your current session by dotSourcing the ActiveDirectoryBrowser.Ps1 script and providing the -loadOnly Switch


then you can use the Browse-ActiveDirectory function interactively from then on, so you can also use this script in your profile



If you use the -LoadOnly Switch but do not DotSource it the script will warn you like this :


 



PoSH>.\ActiveDirectoryBrowser.Ps1 -l
WARNING: LoadOnly Switch is given but you also need to 'dotsource' the script to load the function in the global scope
To Start a script in the global scope (DotSource) put a dot and a space in front of path to the script
If the script is in the current directory this would look like this :
. .\ActiveDirectoryBrowser.Ps1
then :
The Browse-ActiveDirectory Function is loaded and can be used like this :
$de = Browse-ActiveDirectory

PoSH>. .\ActiveDirectoryBrowser.Ps1 -l
The Browse-ActiveDirectory Function is loaded and can be used like this :
$de = Browse-ActiveDirectory


If you use the script this way for loading the function, it also will Define the alias Bad for ease of use 


I hope this LoadOnly option example also shows a bit how you can use the different forms of starting things in PowerShell.


The Script looks like this :


 


 


# ActiveDirectoryBrowser.Ps1
#
# This script does show a GUI to browse ActiveDirectory in a Treeview
# and Returns the DirectoryEntry Selected for use in PowerShell
# or if LoadOnly Parameter is given it just loads the Browse-ActiveDirectory function, 
# and does set the alias bad, for loading the function for interactive use by dotsourcing the script
#
# /\/\o\/\/ 2006
#
# http://mow001.blogspot.com

param (
  [directoryservices.directoryEntry]$root = (new-object system.directoryservices.directoryEntry),
  [Switch]$LoadOnly
)

# the Main function that can be loaded or gets started at the end of the script
 
Function Browse-ActiveDirectory {
  param ([directoryservices.directoryEntry]$root = (new-object system.directoryservices.directoryEntry))

  # Try to connect to the Domain root

  &{trap {throw "$($_)"};[void]$Root.get_Name()}

  # Make the form

  $form = new-object Windows.Forms.form   
  $form.Size = new-object System.Drawing.Size @(800,600)   
  $form.text = "/\/\o\/\/'s PowerShell ActiveDirectory Browser"  

  # Make TreeView to hold the Domain Tree

  $TV = new-object windows.forms.TreeView
  $TV.Location = new-object System.Drawing.Size(10,30)  
  $TV.size = new-object System.Drawing.Size(770,470)  
  $TV.Anchor = "top, left, right"    

  # Add the Button to close the form and return the selected DirectoryEntry
 
  $btnSelect = new-object System.Windows.Forms.Button 
  $btnSelect.text = "Select"
  $btnSelect.Location = new-object System.Drawing.Size(710,510)  
  $btnSelect.size = new-object System.Drawing.Size(70,30)  
  $btnSelect.Anchor = "Bottom, right"  

  # If Select button pressed set return value to Selected DirectoryEntry and close form

  $btnSelect.add_Click({
    $script:Return = new-object system.directoryservices.directoryEntry("LDAP://$($TV.SelectedNode.text)"
    $form.close()
  })

  # Add Cancel button 

  $btnCancel = new-object System.Windows.Forms.Button 
  $btnCancel.text = "Cancel"
  $btnCancel.Location = new-object System.Drawing.Size(630,510)  
  $btnCancel.size = new-object System.Drawing.Size(70,30)  
  $btnCancel.Anchor = "Bottom, right"  

  # If cancel button is clicked set returnvalue to $False and close form

  $btnCancel.add_Click({$script:Return = $false ; $form.close()})

  # Create a TreeNode for the domain root found

  $TNRoot = new-object System.Windows.Forms.TreeNode("Root")
  $TNRoot.Name = $root.name
  $TNRoot.Text = $root.distinguishedName
  $TNRoot.tag = "NotEnumerated"

  # First time a Node is Selected, enumerate the Children of the selected DirectoryEntry

  $TV.add_AfterSelect({
    if ($this.SelectedNode.tag -eq "NotEnumerated") {

      $de = new-object system.directoryservices.directoryEntry("LDAP://$($this.SelectedNode.text)")

      # Add all Children found as Sub Nodes to the selected TreeNode

      $de.get_Children() | 
      foreach {
        $TN = new-object System.Windows.Forms.TreeNode
        $TN.Name = $_.name
        $TN.Text = $_.distinguishedName
        $TN.tag = "NotEnumerated"
        $this.SelectedNode.Nodes.Add($TN)
      }

      # Set tag to show this node is already enumerated
 
      $this.SelectedNode.tag = "Enumerated"
    }
  })

  # Add the RootNode to the Treeview

  [void]$TV.Nodes.Add($TNRoot)

  # Add the Controls to the Form
  
  $form.Controls.Add($TV)
  $form.Controls.Add($btnSelect ) 
  $form.Controls.Add($btnCancel )

  # Set the Select Button as the Default
 
  $form.AcceptButton =  $btnSelect
     
  $Form.Add_Shown({$form.Activate()})   
  [void]$form.showdialog() 

  # Return selected DirectoryEntry or $false as Cancel Button is Used
  Return $script:Return
}

# If used as a script start the function

Set-PSDebug -Strict:$false #  Otherwise Checking the Switch parmeter does fail (Bug ?)

if ($LoadOnly.IsPresent) {

  # Only load the Function for interactive use

  if (-not $MyInvocation.line.StartsWith('. ')) {
    Write-Warning "LoadOnly Switch is given but you also need to 'dotsource' the script to load the function in the global scope"
    Write-Host "To Start a script in the global scope (dotsource) put a dot and a space in front of path to the script"
    Write-Host "If the script is in the current directory this would look like this :"
    Write-Host ". .\ActiveDirectoryBrowser.Ps1"
    Write-Host "then :"
  }
  Write-Host "The Browse-ActiveDirectory Function is loaded and can be used like this :"
  Write-Host '$de = Browse-ActiveDirectory'
  Set-alias bad Browse-ActiveDirectory
}
Else {

  # start Function

  . Browse-ActiveDirectory $root

}

 


 


Most of this Code is straight forward I think with the # comments( if you followed the AD series, and have seen some of my other PowerShell GUI Examples) , just remember that the add_* methods are used to add scripts to events from the objects on the Form.


here some remark by the main points of interest in the script (sorry Numbers Gone) :


Line 24 : &{trap {throw "$($_)"};[void]$Root.get_Name()}


This line does test the ActiveDirectory Connection by getting the name Property and use a Trap block to catch it and throwing the Error again so the script will stop if this fails. 


Line 49,64 :$btnCancel.add_Click({$script:Return = $false ; $form.close()})


The add_Click methods handle the event when I button gets clicked you can give this funtion a Delegate ScriptBlock that gets executed every time the button get clicked.


Line 75-95 : $TV.add_AfterSelect({


This whole Scriptblock is also an eventhandler delegate, this one from the Treeview object and it is called every time a TreeNode is selected,


you can see there is a variable with the name $This, this will contain the TreeView object so we can get the treenode that is selected at that moment from that


Line 76 : if ($this.SelectedNode.tag -eq "NotEnumerated")


we check if we already did get the children of this node, otherwise we will collect them and add SubNodes to the TreeView for them.


Line 109 : $form.AcceptButton =  $btnSelect


Make The Select Button the Default so it will get a click event when [Enter] is pressed


Line 111 : $Form.Add_Shown({$form.Activate()})


Used to give the form focus


Line 120 : The logic to make the script "Hybrid"


For more info about the AD parts see the rest of the series,


For more info about building GUI's and placing and using Form elements on them in PowerShell See my Other GUI examples in former posts.


Using a DataGrid :


PowerShell out-DataGrid update and more DataSet utilities


Using a Property Grid (Object Viewer)


PowerShell-out-propertygrid-msh-view.html


about Focus the Form problem (updated version this script)


Simple Show-Image function for Monad


Adding menus etc ,


MSH Concentration (scripting games part 4)


MSH Minesweeper GUI game


Hosting PowerShell in it.


Hosting an MSH runspace from Monad


Use For EventWatcher workaround (STA Thread ), and how to use Popup balloons.


MSH directory watcher with popup-balloon


There are a bit more posts to find, if you search on GUI on my blog.


*Edit* as the lineNumbers do stay if you copy the Code Above and pasting it, So I'm posting the code another time without LineNumbers


Enjoy,


greetings /\/\o\/\/
Tags : Monad msh PowerShell


 



Repost code from Notepad,without LineNumbers hope this works as well LiveWrite as Blogger hangs, on toughing this post ;-(


Comments:
Blogger /\/\o\/\/
Bigger Picture here, or in teaser there clicking the picture workd:
http://photos1.blogger.com/blogger/8006/1759/1600/bad.jpg

all things did go wrong while posting, and formattings was bit messed up

gr /\/\o\/\/
 
Anonymous Jakke
I adapted the script so it is working in RC2


# ActiveDirectoryBrowser.Ps1
#
# This script does show a GUI to browse ActiveDirectory in a Treeview
# and Returns the DirectoryEntry Selected for use in PowerShell
# or if LoadOnly Parameter is given it just loads the Browse-ActiveDirectory function,
# and does set the alias bad, for loading the function for interactive use by dotsourcing the script
#
# /\/\o\/\/ 2006
#
# http://mow001.blogspot.com


# the Main function that can be loaded or gets started at the end of the script

Function Browse-ActiveDirectory {
$root=[ADSI]''

# Try to connect to the Domain root

&{trap {throw "$($_)"};[void]$Root.psbase.get_Name()}

# Make the form
# add a reference to the forms assembly
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

$form = new-object Windows.Forms.form
$form.Size = new-object System.Drawing.Size @(800,600)
$form.text = "/\/\o\/\/'s PowerShell ActiveDirectory Browser"

# Make TreeView to hold the Domain Tree

$TV = new-object windows.forms.TreeView
$TV.Location = new-object System.Drawing.Size(10,30)
$TV.size = new-object System.Drawing.Size(770,470)
$TV.Anchor = "top, left, right"

# Add the Button to close the form and return the selected DirectoryEntry

$btnSelect = new-object System.Windows.Forms.Button
$btnSelect.text = "Select"
$btnSelect.Location = new-object System.Drawing.Size(710,510)
$btnSelect.size = new-object System.Drawing.Size(70,30)
$btnSelect.Anchor = "Bottom, right"

# If Select button pressed set return value to Selected DirectoryEntry and close form

$btnSelect.add_Click({
$script:Return = new-object system.directoryservices.directoryEntry("LDAP://$($TV.SelectedNode.text)")
$form.close()
})

# Add Cancel button

$btnCancel = new-object System.Windows.Forms.Button
$btnCancel.text = "Cancel"
$btnCancel.Location = new-object System.Drawing.Size(630,510)
$btnCancel.size = new-object System.Drawing.Size(70,30)
$btnCancel.Anchor = "Bottom, right"

# If cancel button is clicked set returnvalue to $False and close form

$btnCancel.add_Click({$script:Return = $false ; $form.close()})

# Create a TreeNode for the domain root found

$TNRoot = new-object System.Windows.Forms.TreeNode("Root")
$TNRoot.Name = $root.name
$TNRoot.Text = $root.distinguishedName
$TNRoot.tag = "NotEnumerated"

# First time a Node is Selected, enumerate the Children of the selected DirectoryEntry

$TV.add_AfterSelect({
if ($this.SelectedNode.tag -eq "NotEnumerated") {

$de = [ADSI]"LDAP://$($this.SelectedNode.text)"

# Add all Children found as Sub Nodes to the selected TreeNode

$de.psBase.get_Children() |
foreach {
$TN = new-object System.Windows.Forms.TreeNode
$TN.Name = $_.name
$TN.Text = $_.distinguishedName
$TN.tag = "NotEnumerated"
$this.SelectedNode.Nodes.Add($TN)
}

# Set tag to show this node is already enumerated

$this.SelectedNode.tag = "Enumerated"
}
})

# Add the RootNode to the Treeview

[void]$TV.Nodes.Add($TNRoot)

# Add the Controls to the Form

$form.Controls.Add($TV)
$form.Controls.Add($btnSelect )
$form.Controls.Add($btnCancel )

# Set the Select Button as the Default

$form.AcceptButton = $btnSelect

$Form.Add_Shown({$form.Activate()})
[void]$form.showdialog()

# Return selected DirectoryEntry or $false as Cancel Button is Used
Return $script:Return
}

# If used as a script start the function

Set-PSDebug -Strict:$false # Otherwise Checking the Switch parmeter does fail (Bug ?)

if ($LoadOnly.IsPresent) {

# Only load the Function for interactive use

if (-not $MyInvocation.line.StartsWith('. ')) {
Write-Warning "LoadOnly Switch is given but you also need to 'dotsource' the script to load the function in the global scope"
Write-Host "To Start a script in the global scope (dotsource) put a dot and a space in front of path to the script"
Write-Host "If the script is in the current directory this would look like this :"
Write-Host ". .\ActiveDirectoryBrowser.Ps1"
Write-Host "then :"
}
Write-Host "The Browse-ActiveDirectory Function is loaded and can be used like this :"
Write-Host '$de = Browse-ActiveDirectory'
Set-alias bad Browse-ActiveDirectory
}
Else {

# start Function

. Browse-ActiveDirectory $root

}
 
Blogger /\/\o\/\/
Thanks,

I was first starting on the WMI posts (as I like the changes there ;-0 )

Greetings /\/\o\/\/
 
Anonymous Jakke
no prob bro; I needed the browser and was too layzy to remove RC2 ;-)
 
Post a Comment

Links to this post:

Create a Link



<< 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?