Archive for the 'Knowledge Base' Category

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: , ,

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: , , , , , ,

Catch variable typos

Did you know that PowerShell has so called strict mode in which it can catch and report any use of uninitialized variables? I’ve started doing this while scripting and it made debugging scripts so much easier! Lot’s of typos and copy/paste issues simply get detected right away.

In PowerShell v1 you can turn this mode on by executing: Set-PSDebug –strict

In PowerShell v2 by doing: Set-StrictMode –version 1 or Set-StrictMode –version 2.

(See this great article explaining all the difference.)

To make this apply to all my debugging sessions, but not to command line and PowerGUI admin console, I did the following:

1. Opened my PowerShell profile:

Open-Profile

2. Added this code in there:

if ($host.Name -eq 'PowerGUIScriptEditorHost') {
    Set-StrictMode -Version 2.0
}

That’s it. Close the profile and restart the PowerGUI Script Editor.

P.S. Please forgive me the “if” workaround in common profile. In the future we will at some point start supporting a separate profile specifically for the script editor. This is the best practice and we’ll get there. ;)

Customize hotkeys in PowerGUI Script Editor

One of the hidden gems in PowerGUI Script Editor is the ability to fully customize its toolbars and hotkeys.

Say, you want to have a hotkey to collapse all regions in a PowerShell script – this could not be easier, just:

  1. On the Tools menu, click Customize.
  2. In the Customize dialog box, click the Keyboard tab.
  3. On the Keyboard tab, select the command you need.
  4. In the Press shortcut key(s), assign the hotkey you want.

Customize-Editor-Hotkeys

Other tabs of this dialog box are also useful:

  • Toolbars - lets you create your own toolbars.
  • Commands - lets you add buttons to existing toolbars by simply dragging them from the dialog box to the toolbar.

Hope this makes your PowerShell scripting even more enjoyable!

Clearing PowerShell runspace before debugging

Another PowerGUI Script Editor option I would like to highlight – is the ability to clear the PowerShell runspace each time you start debugging a script:

Editor_Reset_Runspace

Here’s the rule of thumb on picking the option you need:

Pick “Run all scripts in the same runspace” if you want the editor to behave like a PowerShell command line in which as you execute scripts and commands they leave the variables with their values assigned and available to whatever scripts you execute after that.

Pick “Reset PowerShell runspace each time debugging is started” if you want PowerGUI to completely clear PowerShell and (optionally) re-load your profile each time you start debugging. This basically emulates the situation when you start a new PowerShell window every time before executing a script – completely pristine environment with nothing left from whichever commands and scripts you executed before.

Use whatever model fits best and enjoy your scripting!

Tags: , , ,

Multiple PowerShell runspaces in PowerGUI Editor

By default, PowerGUI Script Editor is a single instance application – all scripts get opened as different tabs on the same window, and all the scripts and interactive commands in the same PowerShell session. They “see” the same variables and so on.

This however is very easy to change. Just go to Tools / Options and clear the Allow only one instance of the application to run – and you can have as many PowerGUI Script Editor windows as you like.

Editor_Single_Multiple_Runspace_Switch

Each window will have its own PowerShell runspace – totally isolated from other windows (but still shared within the tabs of the same window).

AD PowerPack now compatible with cmdlets 1.2

If you upgraded to AD cmdlets 1.2 and noticed that some links and actions in PowerGUI’s Active Directory and Network PowerPacks stopped working.

Now there are fixes available for both of the packs: just download and re-import them and you will get all the functionality back.

For those interested, the issue was related to AD cmdlets 1.2 losing Connection property in all their objects. This property was extensively used by our PowerPacks to provide for simultaneous work against multiple directories (e.g. test and production, AD and ADAM, or just multiple domains or forests).

AD cmdlets team is by the way working on a 1.2.1 patch release which would get the property back, but we thought we would provide a fix on PowerGUI side too.

As a free bonus AD PowerPack has a few other nice features:

  • Moved “Managed Domains” from the Network PowerPack to this PowerPack
  • Added top level Configuration node to allow you to set global settings that define what domain to connect to, what account to use, what properties to retrieve by default for each object type, what page size to use, what size limit to use and whether or not to perform searches across the entire forest or only the domain you connect to
  • Added several child nodes to provide fast access to common objects that users want to retrieve, including Locked Users, Disabled Users, Expired Users, Security Groups, Distribution Lists, Domain Controllers, and Exchange Servers
  • Fixed Empty Groups node such that it only returns truly empty groups (those which have no members and that aren’t set as primary group for any user or computer)
  • Added Unlock user action (this was overlooked in early releases)
  • Added Search… node to allow users to search their current Active Directory domain or the entire forest to which it belongs for objects by type and/or name

Here are the links to read more and download the updated Active Directory and Network PowerPacks.

PowerGUI and CTP3

PowerShell team has just posted the latest pre-release drop of the upcoming version 2 – and they did in on track with the timeline they announced back in November.

The current version of PowerGUI (1.5.3) which you can download from PowerGUI.org is in general compatible with CTP3 but does have a few issues. For example, the editor works fine and you can get syntax highlight and intellisense for new cmdlets, but step-by-step debugging might leave awkward yellow marking on previous steps. ;)

These issues are obviously fully fixed in the upcoming 1.6 release which is coming out literally in a matter of days now. Stay tuned. :)

Tags: , , , , ,

$input gotchas

$input (a.k.a. “dollar input” or “input variable”) is one of those esoteric parts of the PowerShell language that create a lot of confusion. In fact just today there was a discussion on how it actually works on the PowerShell MVP mailing list. We even had to read the documentation to figure it out. ;)

Anyways, basically $input in an enumerator which provides access to the pipeline you have.

So basically if you have a function which sums up the elements from the pipeline you can have something like:

Function Sum {
    $sum = 0
    $input |  foreach { $sum += $_ }
    $sum
}

1, 2, 3 | Sum

Easy. In fact if you have ever added any script actions or links in the PowerGUI admin console, this is basically what you use to get access to the selection from the central grid.

Now, there are a couple not so obvious gotchas here:

1. Enumerator is not an array

Suppose you want to know how many objects you get from the pipeline – a totally valid question. Maybe your function is supposed to only get one.

You just do $input.Count and… get nothing. Such property does not exist. This is an enumerator and it simply does not have such a property.

OK, you say, let’s wrap it into an array and we’ll learn the size:

@($input).Count

This works… Kind of… If you modify our example above to:

Function Sum {
    "Number of elements: " + @($input).Count
    $sum = 0
    $input |  foreach { $sum += $_ }
    "Sum is: " + $sum
}

1, 2, 3 | Sum

You get:

Number of elements: 3
Sum is: 0

The first line is correct – we had 3 elements. But why the heck is the sum 0 now?

Well, $input is an enumerator, and when you use it – you get to the next element. So once we used it to create a temporary array we got to its end. To fix it, simply reset it back:

Function Sum {
    "Number of elements: " + @($input).Count
    $input.Reset()

    $sum = 0
    $input |  foreach { $sum += $_ }
    "Sum is: " + $sum
}

1, 2, 3 | Sum

Now we are good again:

Number of elements: 3
Sum is: 6

2. Just using $input holds the pipeline till all objects are collected

This second one was spotted by Per here. He tried using $input and noticed that his function did not get executed until the whole pipeline was processed (that is his function was not invoked for each element one by one, but rather for the whole collection) – read his post for details.

This happens because according to PowerShell help:

“In the Process block of a function, $input contains the object currently in the pipeline… If the function does not have a Process block, the value of $input is available to the End block, and it contains all of the input to the function.”

So basically the reason for the code above to process the whole collection (rather than go item by item) is that we did not have a process block inside the function, so if you care about item by item processing – go with the process block, if not – feel free to use $input.

And by the way, inside the process block just stick to $_ like this:

Function Sum {
  begin { $sum = 0 }
  process { $sum += $_ }
  end { $sum }
}

1, 2, 3 | Sum

Using $input inside process is a hustle and Oisin promised a post on his blog on the reasons why. ;)

Tags: , , ,

New patch for AD cmdlets

There is a new patch available for AD cmdlets. It fixes two pretty important issues:

This patch was released as a maintenance release 1.1.2 and was published on September 8. If you downloaded the setup after that date you probably have the version already (just look up the Support information in Control Panel / Add/Remove Programs). If not – download the latest build and install on top of the version you are using – this will upgrade your installation to the patched version.

I want to say thanks to the team which quickly released the patch is demonstrated the commitment to fixing the issues for which they cannot provide a workaround – way to go!

Tags: , , , , , , ,

Next Page »


View Dmitry Sotnikov's profile on LinkedIn

Follow Dmitry Sotnikov at Twitter

My Recent Tweets

Archives

See you at:

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

 

November 2009
M T W T F S S
« Oct    
 1
2345678
9101112131415
16171819202122
23242526272829
30