Archive for September, 2009

Background Jobs in PowerGUI

Here’s a quick tutorial on how you can do asynchronous script execution in PowerGUI admin console.

Suppose you want to have a node in PowerGUI which would show computers in your office which are currently online. The script (using Quest AD cmdlets and Test-Connection cmdlet from PowerShell v2) could look like:

Get-QADComputer -Location 'EMEA/RU/St.Petersburg' |
    ForEach-Object {
        if (Test-Connection -ComputerName $_.Name -Quiet)
            { $_  }
    }

You will probably have a different Location parameter in your environment. Please change it here and in all samples below.

And it seems to work but is taking awfully long time to execute (sure, we could have optimized it a bit – but that’s not the point ;) ). And you kind of have to sit there and watch it executing because if you click another node in PowerGUI admin console it will abort the operation…

The easy fix is to turn it into background job by taking the script, adding Add-PSSnapin (because background jobs start in their own runspace) and keeping the job handle in a global variable (so we don’t lose the job when we leave the node):

$global:OnlineComputersJob = Start-Job {
Add-PSSnapin 'Quest.ActiveRoles.ADManagement'
Get-QADComputer -Location 'EMEA/RU/St.Petersburg' |
    ForEach-Object {
        if (Test-Connection -ComputerName $_.Name -Quiet)
            { $_  }
    }
}

This will start the job and it will keep running in the background while you can use PowerGUI for other tasks.

Now I will just change it a little bit to add actual output both to the grid and Output window. I will add yet another global variable $global:OnlineComputersResults and use it to keep all the results.

# if no job started yet, start a new one, else output old results
if ( $global:OnlineComputersJob -eq $null ) {

    # this is the collection in which we will keep job results
    $global:OnlineComputersResults = @()

    $global:OnlineComputersJob = Start-Job {
    Add-PSSnapin 'Quest.ActiveRoles.ADManagement'
    Get-QADComputer -Location 'EMEA/RU/St.Petersburg' |
        ForEach-Object {
            if (Test-Connection -ComputerName $_.Name -Quiet)
                { $_  }
        }
    }
} else {
    $global:OnlineComputersResults
}

# Now let's keep looping and adding new results to the grid
while ( $global:OnlineComputersJob.JobStateInfo.State -ne 'Completed' ) {
    # get new results, add them to old ones, and output them to the grid
    # @() required to make sure PowerShell knows it is a collection
    $results = @(Receive-Job $global:OnlineComputersJob)
    $global:OnlineComputersResults += $results
    $results
    Write-Host "Added $($results.count) records. More on the way."
    Start-Sleep -Seconds 5
}

# When the job is completed, output whatever remains
$results = Receive-Job $global:OnlineComputersJob
$global:OnlineComputersResults += $results
$results
Write-Host "Added $results records."
Write-Host "Job completed."

That is it. This node will start running and adding new results to the grid as they appear.

Now let’s make it really shine by adding an action to reset the computer list, and make other computer-related actions show-up in the right-hand pane.

To add the reset action, we first add a category: right-click the right-hand pane and select New / Category – then name it something – e.g. “Job Control”.

After that, right click the new category and select New / Script Action – then create a new action called Reset computer list with the following code:

$global:OnlineComputersResults = $null
Remove-Job $global:OnlineComputersJob -Force
$global:OnlineComputersJob = $null

When you click this action the computer list gets recompiled from scratch.

Finally, let’s modify the original node list to tell PowerGUI that the objects in the grid can be used as computer objects. We need to do this because background jobs return not real objects but their deserialized versions (only data fields) – hence the type is also changed to “Deserialized.Quest.ActiveRoles.ArsPowerShellSnapIn.Data.ArsComputerObject”.

If we want to tell PowerGUI that these objects can be treated as regular computer objects we have two options to do that:
1. (Safer) Go through the regular actions displayed for computers and, for the ones which are not using any methods, add ‘Deserialized.Quest.ActiveRoles.ArsPowerShellSnapIn.Data.ArsComputerObject’ in Properties / Display Configuration / Associate the action with these types.
2. (Easier) Modify the original node code so that the first element we output contains the regular computer type name: ‘Quest.ActiveRoles.ArsPowerShellSnapIn.Data.ArsComputerObject’.

So with that easier option, the node code will look like:

# if no job started yet, start a new one, else output old results
if ( $global:OnlineComputersJob -eq $null ) {

    # this is the collection in which we will keep job results
    $global:OnlineComputersResults = @()

    $global:OnlineComputersJob = Start-Job {
    Add-PSSnapin 'Quest.ActiveRoles.ADManagement'
    Get-QADComputer -Location 'EMEA/RU/St.Petersburg' |
        ForEach-Object {
            if (Test-Connection -ComputerName $_.Name -Quiet)
                { $_  }
        }
    }
} else {
    $global:OnlineComputersResults
}

# Now let's keep looping and adding new results to the grid
while ( $global:OnlineComputersJob.JobStateInfo.State -ne 'Completed' ) {
    # get new results, add them to old ones, and output them to the grid
    # @() required to make sure PowerShell knows it is a collection
    $results = @(Receive-Job $global:OnlineComputersJob)

    #for the first object in the collection, set object type
    if (( $global:OnlineComputersResults.Count -eq 0 ) -and
        ( $results.count -gt 0 )) {
        $results[0].PSObject.TypeNames.Insert(0,`
        'Quest.ActiveRoles.ArsPowerShellSnapIn.Data.ArsComputerObject')
    }

    $global:OnlineComputersResults += $results
    $results
    Write-Host "Added $($results.count) records. More on the way."
    Start-Sleep -Seconds 5
}

# When the job is completed, output whatever remains
$results = Receive-Job $global:OnlineComputersJob
$global:OnlineComputersResults += $results
$results
Write-Host "Added $results records."
Write-Host "Job completed."

That is it. Now you have your Online Computers node which:
A. Execute in the background asynchronously,
B. Outputs results as they get in,
C. Has all the actions for its results plus a new one to restart it.

This is how your console would probably look like:

background

And, yes, PowerGUI at some point will probably have a lot of this just built into the framework. Meanwhile, I hope that you find this workaround useful. :)

Big thanks to Karl for the idea of this blog post.

More on AD Recycle Bin

I’ve blogged about object recovery functionality in new Active Directory 2008 R2, and previous versions (free and commercial).

However, Jonathan took the discussion to the whole new level with his incredibly detailed Simple-Talk post: The Active Directory Recycle Bin in Windows Server 2008 R2.

Definitely worth checking out and bookmarking for future use!

Skype for administrators

Call any user in Active Directory or Exchange right from your administrative console, with a single click dial into any conference call (with participant passcode dialed for you ;) ) – all of that is possible with the latest extension for PowerGUI.

PowerGUI Skype PowerPack adds Call buttons for all user accounts and mailboxes, as well as people in your corporate organizational chart. In addition to this you can obviously call any of your Skype contacts or any conference call appointments listed in your Outlook.

Check out this 7-minute demo here (highly recommended to watch in HD and full screen):

As always, all the code behind any nodes and actions can be found in their properties, so you can learn how this actually works, modify the pack to suite your needs or extend it to add calling capabilities to whichever other systems you are managing!

This blog post by Shafqat Ahmed helped me a lot when I started working on the PowerPack. My scripts in the pack are basically enhancements built on top of his excellent post.

Download Skype PowerPack here and let us know what you think.

Tags: , , , , , , ,

Microsoft’s AD cmdlets available for Win 2003 and later

Microsoft has just released to the web final bits of their Active Directory Web Services. This is the service you need to install on your domain controller or ADAM/AD LDS server to make them accessible for Microsoft’s AD cmdlets.

As you can see from the diagram above, unlike Quest AD cmdlets, Microsoft’s ones require Active Directory Web Services (ADWS) on the server/DC you manage. These web services ship with Windows Server 2008 R2. Now they are also available as a free download for Windows Server 2003 SP2 and later.

This means that as long as you are ready to install this additional software on your DCs, you can start using these new cmdlets from Microsoft. There is no need to upgrade DCs to 2008 R2 or change the forest functional level.

More choices for PowerShell management of AD – better for everyone. :)

Tags: , ,

Set -WhatIf ON by default

Making PowerShell safe by default is trivial. Ulf here suggested that “socially responsible” PowerShell needs to use -WhatIf by default for all operations, and actually perform the actions only if explicitly asked to do so.

To make PowerShell operate in this mode, all you need to do is set $WhatIfPreference variable to $true, and use -WhatIf:$false when you do want something to be executed. (colon is mandatory by the way – because this is a switch parameter).

Here’s my session (copy/pasted from PowerShell Console pane of PowerGUI Script Editor):

C:\> $WhatIfPreference
False
C:\> $WhatIfPreference = $true
C:\> $WhatIfPreference
True
C:\> Get-Process notepad

Handles  NPM(K)  PM(K)   WS(K)  Id  ProcessName
-------  ------  -----   -----  --  -----------
     63       3   2372    6668  66      notepad


C:\> Get-Process notepad | Stop-Process
What if: Performing operation "Stop-Process" on Target "notepad (4708)".
C:\> Get-Process notepad | Stop-Process -WhatIf:$false

Tags: , ,

Adding Direct Reports action

Here’s my quick addition to Kamran’s “How to export data from Active Directory using the free PowerGUI tool“:

If you might need to find users managed by someone more than once, there are a couple of ways you can make this simpler for your self:

A. Download and install free Org Chart powerpack, or

B. Follow these instructions to add a Direct Reports button to PowerGUI admin console:

1. Start PowerGUI admin console and browse to Active Directory / Users.

2. In the right-hand pane, right-click the category to which you want to add the action and pick New / Script Action from the shortcut menu.

new-script-action

3. In the New Action dialog box, type in the action name (e.g. Direct Reports), and script:

$input | ForEach-Object {
    Get-QADUser -Manager $_
}

(The script is quite trivial: for each user account currently selected in the PowerGUI grid, we get the list of AD users which have this object specified as Manager).

direct-reports

3. On that same dialog box, click the Display Configuration button, and then in the Display results group, select the Display the results in a nested view option.

This tells PowerGUI that your action outputs objects to the grid (which it does – we need to see the reports selected user has!)

nested-view

4. That’s it! Click OK on both dialog boxes, select the user account whose reports you need to see, and click your newly added Direct Reports action.

Big thanks to Kamran for posting his original tip and video.

Tags: , , , , , ,

Fastest way to retrieve AD objects

DontUseDefaultIncludedProperties is the AD cmdlets parameter you need when you want to get AD objects fast without extra properties you do not need. For example, I have just used it in my script to locate the largest groups in our Active Directory. Let’s talk about how effective it really is and how it works under the covers.

First, of all, let me prove that it is indeed very efficient. Here’s the same Get-QADUser run 100 times with and without the parameter:

[PS] C:\>Measure-Command { for ($i=0;$i -lt 100;$i++) { $a = Get-QADUser -SamAccountName dsotnikov -DontUseDefaultIncludedProperties } }

...
Seconds : 3
Milliseconds : 951
...


[PS] C:\>Measure-Command { for ($i=0;$i -lt 100;$i++) { $a = Get-QADUser -SamAccountName dsotnikov } }

...
Seconds : 7
Milliseconds : 526
...

That’s twice as fast with the parameter than it is without it!

Why? Because DontUseDefaultIncludedProperties makes the cmdlet only retrieve 2 attributes: distinguishedName и objectClass, whereas the cmdlet without it will go get quite a few other properties.

You can easily see which attributes got retrieved by running:
$a = Get-QADUser -SamAccountName dsotnikov -DontUseDefaultIncludedProperties
$a.Cache.AttributesInCache

The interesting thing is that the cmdlet is even smarter with subsequent use of the object. For most properties (to be specific, for all regular .NET properties of the object but not PowerShell dynamic NoteProperties), we will go and retrieve the property once you request it later on. E.g. this will actually work and give you the account description:

$a = Get-QADUser -SamAccountName dsotnikov -DontUseDefaultIncludedProperties
$a.Description

And this will retrieve a whole bunch of attributes:
Get-QADUser -SamAccountName dsotnikov -DontUseDefaultIncludedProperties | Format-List *

You can obviously keep using $a.Cache.AttributesInCache to check which ones we retrieve.

Pretty cool, isn’t it?

The only other thing I would note is the difference between:

Get-QADUser -SamAccountName dsotnikov -DontUseDefaultIncludedProperties

and

Get-QADUser dsotnikov -DontUseDefaultIncludedProperties

The former is way more efficient than the latter because the -SamAccountName parameter (or any other parameter besides the generic implied -Identity) lets us optimize the query specifically to search by that attribute rather than do the Ambiguous Name Resolution which we use otherwise. Be specific in your parameters and we will give you the fastest results!

Tags: , , , , , ,

Find large AD groups

Here’s a quick script which I wrote yesterday for one of our professional services engineers. Basically they wanted to give customer a report for large groups, i.e. groups with more than certain number of user accounts including the ones in nested groups.

This turned out to be very straight-forward with QAD cmdlets.

The script is below:

function Get-LargeADGroup {
param($limit = 75)

 Get-QADGroup | Foreach-Object {
   $members = $_ |
           Get-QADGroupMember -Indirect -Type 'user' `
              -DontUseDefaultIncludedProperties `
              -SizeLimit ($limit+1) -WarningAction SilentlyContinue
   if ( ($members -ne $null) -and
        ($members.gettype().Name -eq 'Object[]') -and
        ($members.Count -ge $limit)) {
     $_
   }
 }
}

#Usage
Get-LargeADGroup -limit 75 | Select Name, DN | Export-Csv c:\largegroups.csv

One trick worth pointing out is the use of -SizeLimit to not retrieve all members of the group – after all I just need to know if there is more than certain number of them.

Hope this helps! :)

PowerShell Script Templates

Adding standard header with disclaimer, copyright, and so on to your scripts is very easy. If you would like to (or have to – because of your company’s policies) use a certain template for your scripts – just define is as a code snippet in PowerGUI Script Editor.

Once you do that, the template is just a click- (or Ctrl-I-) away:

header_snippet(Note that snippets are alphabetically sorted so using underscore to get your ahead of others is a valid trick.)

And then, once you select the snippet you would probably get something like:

header_addedHopefully, this will save you a few extra seconds a day wasted on meaningless copy/paste. :)

Detailed instructions on adding your code snippets can be found here.

PowerGUI Wallpaper

There’s a new fall/autumn edition of our PowerGUI desktop wallpaper available.

Just go to our downloads page and get the size you need:

Downloads page also has our 2008 wallpaper for you to compare and pick the one which fits your seasonal mood better. :)


My Recent Tweets

Archives

Legal

The posts on this blog are provided “as is” with no warranties and confer no rights. The opinions expressed on this site are mine and mine alone, and do not necessarily represent those of my employer Quest Software or anyone else for that matter. All trademarks acknowledged.

© 2007 Dmitry Sotnikov

Pages

 

September 2009
M T W T F S S
« Aug   Oct »
 123456
78910111213
14151617181920
21222324252627
282930  

Follow

Get every new post delivered to your Inbox.

Join 44 other followers