Archive for October, 2007

Counting lines of source code with PowerShell

How do you count the lines of code your application has? As far as I know Visual Studio does not provide this functionality, so needless to say, we are doing that with PowerShell 😉

$num = 0
dir c:\root\ -include *.cs -Recurse | ForEach-Object { 
	$file=[array](Get-Content $_ ) 
	$num += $file.length 

You obviously need to change c:\root to your root folder path, and change the *.cs mask to the one applicable to your source code.

Tags: , ,

Date/Time User Properties

One of the enhancements of the 1.0.5 release is more attributes being exposed in their native formats. For example, AD cmdlets automatically convert the properties which should be date/time to the DateTime time so you don’t have to worry about the conversions and can just work with them.

Let’s see which Date/Time attributes my account has:

Get-QADUser "Dmitry Sotnikov" -IncludeAllProperties | Get-Member -MemberType NoteProperty | where {$_.Definition -like "*DateTime*" } | Format-List Name, Definition

Name : accountExpires
Definition : System.DateTime accountExpires=12/31/9999 11:59:59 PM

Name : badPasswordTime
Definition : System.DateTime badPasswordTime=10/29/2007 1:22:06 PM

Name : createTimeStamp
Definition : System.DateTime createTimeStamp=6/16/2004 3:59:22 PM

Name : lastLogoff
Definition : System.DateTime lastLogoff=1/1/1601 12:00:00 AM

Name : lastLogon
Definition : System.DateTime lastLogon=10/30/2007 12:04:22 PM

Name : lockoutTime
Definition : System.DateTime lockoutTime=1/1/1601 12:00:00 AM

Name : modifyTimeStamp
Definition : System.DateTime modifyTimeStamp=10/29/2007 12:20:24 AM

Name : pwdLastSet
Definition : System.DateTime pwdLastSet=8/27/2007 4:09:54 PM

Name : whenChanged
Definition : System.DateTime whenChanged=10/29/2007 12:20:24 AM

Name : whenCreated
Definition : System.DateTime whenCreated=6/16/2004 3:59:22 PM

Let’s see what I was doing in the command above:

  1. I retrieved my user object using Get-QADUser and supplying the name.
  2. Used –IncludeAllProperties to make the cmdlet retrieve all AD attributes and not just the default set (which would not have: createTimeStamp and modifyTimeStamp).
  3. Used Get-Member and Where to only leave Property members of the DateTime type.
  4. Formatted the output as a list with the names and definitions (type and value).

Note that you can operate DateTime values for filtering too.

For example, to see a list of accounts which never logged on this year you would do:

$threshold = (Get-Date).AddYears(-1)
Get-QADUser -IncludedProperties lastLogonTimestamp | where { $_.lastLogonTimestamp -le $threshold }

Pretty straight-forward, right? 😉


  • To have lastLogonTimestamp replicated between DCs the domain should be in Windows 2003 mode.
  • If your domain is still in Windows 2000 mode, you have to query for lastLogon from each DC (for every user), as lastLogon is a non-replicated attribute.
  • lastLogonTimestamp is updated each 14 days by default (in reality it is more often):

Tags: , , , , , ,

Presenting at OCS Launch in Moscow

I am presenting at the Office Communications Server 2007 launch in Moscow today. The topic is: Unified Reporting and Management for your Unified Communications – so there will definitely be plenty of PowerShell in the Exchange part of the talk.

By the way, Jeff Raikes is coming to personally do the keynote. This guy must be traveling a lot…

Tags: , ,

Using expressions in pipeline parameters

Often times you need the second cmdlet in a pipeline to not only accept the collection from the first one but also re-use some of the properties of the members. For example:

  • Renaming objects so the new name is the old one plus some prefix, or
  • Move objects to an OU based on some of objects’ attributes, or
  • Set password for a user to something containing his/her username.

I guess you got the idea. The problem with that is that in PowerShell if you just try to add this as an expression this will not work.

With AD cmdlets 1.0.5 the problem finally got a solution. Although PowerShell still does not allow to use expressions in the parameters you can use scriptblocks and AD cmdlets will handle the rest.

Just remember to use {} brackets instead of ():

[Update] This scriptblock approach works for Move/Rename but not for Set cmdlets:

# Add prefix to a set of user accounts:

Get-QADUser -SearchRoot ps64.local/test | Rename-QADObject -NewName {"Test_" + $_.Name}

# Restructure AD:

Get-QADUser -SearchRoot ps64.local/employees | Move-QADObject -to {"ps64.local/employees/" + $_.City}

For other cases you still need to use ForEach:

# Set password to username (note that your AD password restrictions might prevent you from setting passwords containing usernames)

Get-QADUser Test* | ForEach { Set-QADObject $_ -UserPassword {$_.sAMAccountName} }

#Set description including user properties

Get-QADUser | ForEach { Set-QADObject $_ -Description {$_.Title + " from " + $_.City + " " + $_.Department}}

Good luck with your bulk changes!

Tags: , , , , , ,

Creating test AD users (improved)

One of the kicks of PowerShell is taking someone else’s script and making it even shorter. 😉 This was exactly my first thought when I found this test AD user creation script by Austin (found via Bob).

Austin’s script is perfectly fine but you can make it shorter by getting rid of csv step and enabling the accounts on the fly.

So here’s how I would create 500 test user accounts:

1..500 | ForEach-Object {
New-QADUser -ParentContainer ps64.local/test -Name "testuser$_" -SamAccountName "testuser$_" -UserPrincipalName "testuser$" -FirstName "testUser$_" -LastName "example$_" -UserPassword "password_123" | Enable-QADUser

I think this is pretty self-describing. I am using the 1..500 cycle to get a collection of these 500 numbers, and then put them inside the string parameters (make sure you use double quotes!) in the ForEach-Object cycle. Finally the objects are passed to Enable-QADUser so the accounts get enabled.

Pretty cool, eh?

Tags: , , , , , , , ,

AD Group Membership Management

Manipulating AD group membership can definitely be one of the tasks you want to do in bulk and efficiently and PowerShell AD cmdlets are the best way of doing that. Below is my write-up on the main tasks you might need to perform and the PowerShell one-liners to do them.

The main tasks involve:

  • Populating groups based on user properties,
  • Copying group membership,
  • Comparing groups,
  • Restructuring groups.

Below is basically my PowerShell one-liners performing all of the task.

Let’s start with the simplest thing: create a group and populate it by users based on their attributes:

Get-QADUser -Department Accounting | Add-QADGroupMember DL.Accounting

Now, let’s copy the whole group membership to another group (I love this one-liner! Can you get simpler than that?):

Get-QADGroupMember DL.Accounting | Add-QADGroupMember AcctBackup

Let’s compare the two groups now to see whether the group membership is the same:
Compare-Object ( Get-QADGroupMember DL.Accounting) ( Get-QADGroupMember AcctBackup ) -IncludeEqual

InputObject                                                 SideIndicator
-----------                                                 -------------
CN=Jane Nikolsky,CN=Users,DC=domain1,DC=local               ==
CN=Chris Popolutsky,OU=Demo,DC=domain1,DC=local             ==
CN=Janus Demus,OU=Demo,DC=domain1,DC=local                  ==
CN=Kate Schmick,OU=Demo,DC=domain1,DC=local                 ==
CN=Natalia Karolli,OU=Demo,DC=domain1,DC=local              ==

Now, let’s split the accounting group into a few regional subgroups:

# Copy filtered group membership
Get-QADGroupMember DL.Accounting | where {$_.City -eq "Munich"} | Add-QADGroupMember DL.Munich.Accounting
Get-QADGroupMember DL.Accounting | where {$_.City -eq "Berlin"} | Add-QADGroupMember DL.Berlin.Accounting

# Nest the groups
Add-QADGroupMember -Identity DL.Accounting -Member domain1.local/demo/DL.Berlin.Accounting
Add-QADGroupMember -Identity DL.Accounting -Member domain1.local/demo/DL.Munich.Accounting

# Compare groups again
Compare-Object ( Get-QADGroupMember DL.Accounting) ( Get-QADGroupMember DL.Berlin.Accounting ) -IncludeEqual

InputObject                                                 SideIndicator
-----------                                                 -------------
CN=Janus Demus,OU=Demo,DC=domain1,DC=local                  ==
CN=Natalia Karolli,OU=Demo,DC=domain1,DC=local              ==
CN=Jane Nikolsky,CN=Users,DC=domain1,DC=local               <=
CN=Chris Popolutsky,OU=Demo,DC=domain1,DC=local             <=
CN=Kate Schmick,OU=Demo,DC=domain1,DC=local                 <=
CN=DL.Munich.Accounting,OU=Demo,DC=domain1,DC=local         <=
CN=DL.Berlin.Accounting,OU=Demo,DC=domain1,DC=local         <=

# Remove group member duplicates

Get-QADGroupMember DL.Berlin.Accounting | Remove-QADGroupMember DL.Accounting
Get-QADGroupMember DL.Munich.Accounting | Remove-QADGroupMember DL.Accounting

# Check if it worked
Compare-Object ( Get-QADGroupMember DL.Accounting) ( Get-QADGroupMember DL.Munich.Accounting ) -IncludeEqual

InputObject                                                 SideIndicator
-----------                                                 -------------
CN=Chris Popolutsky,OU=Demo,DC=domain1,DC=local             =>
CN=Kate Schmick,OU=Demo,DC=domain1,DC=local                 =>
CN=Jane Nikolsky,CN=Users,DC=domain1,DC=local               <=
CN=DL.Munich.Accounting,OU=Demo,DC=domain1,DC=local         <=
CN=DL.Berlin.Accounting,OU=Demo,DC=domain1,DC=local         <=

# No "Berlin" or "Munich" members left
Get-QADGroupMember DL.Accounting | ft Name, Type, City

Name                                    Type                                    City
----                                    ----                                    ----
Jane Nikolsky                           user                                    Orlando
DL.Munich.Accounting                    group
DL.Berlin.Accounting                    group

P.S. There are some known issues in AD cmdlets 1.0.5 which I worked around in the commands above. These will be fixed in later drops but I will mention them here just in case:

  • If you are adding members to a group not via a pipeline but by providing the -Member parameter, use canonical name for the new member.
  • If you have a group which has the same name as the beginning of another group name (e.g. AB and AB.CD) you might need to supply canonical name instead of name to disambiguate.

Tags: , , , , , , ,

Another chance to vote for AD cmdlets

If you love AD cmdlets and they make your AD administration easier, here’s another chance for you to give back. Please vote for the AD cmdlets’ parent product – ActiveRoles Server in SC Magazine:

This will help us make more money on ActiveRoles and continue paying the AD cmdlets development costs. So I guess this is one of those “will work for your votes” situations.

P.S. Other Quest products nominated include InTrust and Reporter. So if you are a fan of those feel free to cast your vote in the corresponding categories too.

Tags: , ,

PowerShell Highlight for your Blog

Just wanted to quickly blog about how I now manage to have nicely formatted PowerShell code snippets in my blog like this one:

for($i=0; $i -lt $csv.Count; $i++){   
   $command = "New-QADUser "

   $parameters | ForEach {

         $csvarg = Invoke-Expression ('$csv[$i].' + $_.Name)

         $arg = ' -' + $_.Name + ' "' + $csvarg + '"'

         $command = $command + $arg  
   Invoke-Expression $command

What I do, is using a nice little feature of the PowerGUI Script Editor: Save As HTML. I basically save my scripts from PowerGUI as html files and then copy/paste the html code I need into the blog. Makes the posts much more readable than they used to be!

[UPDATE] This is even easier now. With recent versions of PowerGUI Script Editor, all you need to do is select the PowerShell code you need, and then on the Edit menu, click Copy As / HTML.

Tags: ,

AD cmdlets go RC

As Bob and Richard posted in their blogs we have just released build 1.0.5 of AD cmdlets. This build is marked Release Candidate (RC) meaning we are feature complete for 1.0 release and 1.0.6 will hopefully become the 1.0 RTM to us.

1.0.5 has quite a few bugfixes and a few new features such as the Set-QADGroup cmdlet (to complement the Get-QADGroup which we had before), support for scriptblocks as parameters, better support for csv provisioning, and full support for ADAM (AD LDS) in all relevant cmdlets.

I will blog about each of the new features in more detail in the upcoming week.

On his blog Bob has the current list of cmdlets and quick descriptions for each of them.

Tags: , , , ,

Customizing syntax highlight

Cash has a post on his blog on customizing the way PowerGUI highlights PowerShell code. Check this out if you are not 100% satisfied with the default way. (Direct XML edits are dangerous, use with caution, back up, bla-bla-bla…)

Tags: , ,

My Recent Tweets


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 - WSO2 or anyone else for that matter. All trademarks acknowledged.

© 2007-2014 Dmitry Sotnikov

October 2007

%d bloggers like this: