PowerShell for Windows Admins

February 21, 2012  2:24 PM

Win32_AutocheckSetting class

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I stumbled on this class

Get-WmiObject Win32_AutochkSetting

The useful output is

Caption          :
Description      :
SettingID        : Microsoft Windows 7 Ultimate |C:\Windows|\Device\Harddisk0\Partition2
UserInputDelay   : 10


Be careful with the documentation because it states that SettingID holds the date and time of installation! 

I’ve made the output a bit easier to understand

$acs = Get-WmiObject -Class Win32_AutochkSetting             
$setting = $acs.SettingID -split "\|"            
New-Object -TypeName PSObject -Property @{            
  OS = $setting[0]            
  Path = $setting[1]            
  Disk = $setting[2]            
  'Delay(s)' = $acs.UserInputDelay            

February 20, 2012  4:35 PM

UG meeting reminder

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

First reminder for the UG meeting on 28February – PowerShell and SQL Server

details from


February 18, 2012  2:05 PM

Count of files in a folder

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I was recently left a comment of a post


asking about the how to get the count of files in a folder

There are a number of solutions including dropping back to the FileSystem object from VBscript

If we want just a PowerShell option

function filecount {            
param (            
 if (-not (Test-Path $path)){Throw "Path: $path not found"}            
 $count = 0            
 $count = Get-ChildItem -Path $path |             
          where {!$_.PSIsContainer} |             
          Measure-Object |            
          select -ExpandProperty count            
 Get-Item -Path $path |             
 select PSDrive,             
 @{N="Parent"; E={($_.PSParentPath -split "FileSystem::")[1]}},            
 @{N="FileCount"; E={$count}}            
 Get-ChildItem -Path $path |             
 where {$_.PSIsContainer} |            
 foreach {            
   filecount $($_.Fullname)            
filecount "c:\scripts"

Supply a path to the top level folder for the filecount function

It will test if the folder exists & complain if it doesn’t.

We can then count the files in the folder using PSISContainer to filter out any subfolders and measure-object to perform the count.

Get-item is used on the path and piped into select where we split out the parent path and add the file count (that count be done with add-member as well

Get-ChildItem is used on the path and only folders are passed. The path of each subfolder is passed to the filecount function. 

A function calling itself like this known as recursion

February 18, 2012  4:58 AM

LDAP filter issues

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I have been using LDAP filters a lot recently. One thing that can cause subtle errors that are difficult to track down are LDAP filters.

As an example consider this code which discovers all objects with the creatorSID attribute set and then resolves that SID to discover the user who created the object. The creatorSID attribute is not set if  a domain admin creates the object

function Resolve-SIDtoUser {                        
 param ([byte[]]$sid)                        
  $sb = New-Object -TypeName System.Text.StringBuilder                        
  for ($i=0; $i -lt $sid.Length; $i++){                        
   $sb.AppendFormat("\{0}",  $sid[$i].ToString("X2")) | Out-Null                        
  $strsid = $sb.ToString()                        
  $root = [ADSI]""                        
  $search = [adsisearcher]$root                        
  $search.Filter = "(objectSID=$strsid)"                        
  $search.FindOne() | foreach {                        
    return $_.Properties.name                        
$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()                        
$domain = $dom.GetDirectoryEntry()                        
$dn = $($domain.distinguishedname)                         
$root = [ADSI]""                        
$search = [adsisearcher]$root                        
$search.Filter = "(mS-DS-CreatorSID=*)"                         
$search.SizeLimit = 3000                        
$search.FindAll() | foreach {                        
 $obj = $_.GetDirectoryEntry()                        
 $user = Resolve-SIDtoUser $obj.Properties."mS-DS-CreatorSID".value                        
 $obj | select  @{N="ObjectClass"; E={$_.SchemaClassName}},                         
 @{N="Name"; E={$_.name}},                         
 @{N="DistinguishedName"; E={$_.distinguishedname}},                        
 @{N="Creator"; E={$user}}                        
 } | Format-Table -AutoSize

In this we have to LDAP filters. The one at the bottom

$search.Filter = "(mS-DS-CreatorSID=*)" 

fires first and checks for all objects that have the ms-DS-CreatorSID attribute set.

What would happen if this was changed to

$search.Filter = "(mS-DS-CreatorSID=* )"

Looks the same at first glance but actually you would get nothing back! In the original version * indicates that the attribute has a value. You are dealing with strings in the filter to the change looks for ‘* ‘ i.e. something followed by a space!


$search.Filter = "(mS-DS-CreatorSID= *)"

does work because the * is still by itself.

This also is an issue if you are using variable substitution to create the filter. Consider the filter in the resolve-sidtouser function

$search.Filter = "(objectSID=$strsid)" 

On the face of it changing this to

$search.Filter = "(objectSID=$strsid )" 

shouldn’t matter but its the same case as above the space counts.

These rules also apply if you are using an LDAP filter with the Microsoft cmdlets, Quest cmdlets or the provider

The bottom line with LDAP filters be careful of spaces and don’t leave any after the filter definition.

February 15, 2012  12:23 PM

My first script

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

PowerShell is an incredible tool with the ability to automate a load of your mundane tasks. A frequent question is where do I start?

Now the obvious starting point is to learn a little PowerShell – PowerShell in a Month of Lunches is ideal if you are a complete beginner otherwise jump straight to PowerShell in Practice. Both are available from www.manning.com and all good book shops.

The next thing is picking the tasks to automate. What is causing you grief? What is being done inconsistently in your organisation? What takes up a lot of time and effort?

A few things come to mind:

  • creating AD user accounts and mailboxes – easy enough to create 1 in the GUI but it quickly gets tedious if you need to do many of them.  Also  the user names can be standardised. When I audit AD environments I see lots of examples where the names aren’t consistent – firstname lastname; lastname firstname;  lastname,firstname.  I really hate the last one – using a comma causes problems when creating/using distinguished names – don’t do it!
  • removing expired AD accounts – its easy to test when a user last logged on – set a date and disable accounts that haven’t been used
  • server configuration – most organisations have a requirement to create configuration documentation. Most admins don’t do it because its boring. Automate it with a set of WMI calls and you have instant documentation.  I recently refreshed the documentation for a couple hundred servers over a few nights
  • reporting e.g. disk usage, mailbox sizes, allocation of CPU and memory on virtual machines to test over commitment


Pick something that will be of immediate benefit to automate and do it. You may not get it all first pass but even taking away some of the manual work will free up a bit more time. The more you can speed up the mundane tasks the more time you have to devote to the interesting stuff

February 13, 2012  12:16 PM

String replacement

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A question of the forum involved changing the file path for a folder. The original path was like this

$folder = "C:\FolderName\NextFolder\ThirdFolder"


The first attempt uses the –replace operator

PS> $newfolder = $folder -replace "C:\FolderName\", "D:\"
Invalid regular expression pattern: C:\FolderName\.
At line:1 char:30
+ $newfolder = $folder -replace <<<<  "C:\FolderName\", "D:\"
    + CategoryInfo          : InvalidOperation: (C:\FolderName\:String) [], RuntimeException
    + FullyQualifiedErrorId : InvalidRegularExpression


The way to get round this is to escape the \ with another \ to keep the regular expressions happy

PS> $newfolder = $folder -replace "C:\\FolderName\\", "D:\"
PS> $newfolder


Alternatively if you don’t care about happy regex then use the Replace() method of the string class

PS> $newfolder = $folder.Replace("C:\FolderName\", "D:\")
PS> $newfolder


Its simpler than dealing with regex and escaping characters to meet its quirks

February 12, 2012  4:31 AM

Domain Controller Service Health–revisited

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A bit more digging as a follow up to the previous post shows that the NTDS service is shown when PowerShell is run with elevated privileges i.e. Run as Administrator. That means we want to be able to test is PowerShell is running in that mode

The test-dcservicehealth function becomes

function test-dcServiceHealth {            
param (            
PROCESS {            
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()            
if (! (New-Object Security.Principal.WindowsPrincipal $currentUser).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)){            
  Write-Warning "Must be run as administrator"            
"ADWS", "Dfs",  "DFSR", "DNS", "IsmServ", "kdc", "Netlogon", "NTDS", "NtFrs", "W32Time", "WinRM" |            
foreach {            
 Get-WmiObject -Class Win32_Service -Filter "Name = '$($_)'"-ComputerName $computername |            
 select Name, DisplayName, State,            

I’ve added advanced function parameter attributes so the function accepts pipeline input, added NTDS to the list of services and added a test to see if PowerShell is running as administrator – if it isn’t it returns with a warning


which means we can do this

"dc02", "server02" | test-dcServiceHealth | ft -GroupBy DC –AutoSize


It would be better to get the domain controllers automatically so

function get-DomainControllerNames {            
 $dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()            
 $dom.FindAllDomainControllers() | select -ExpandProperty Name            

which then means we do

get-DomainControllerNames | test-dcServiceHealth | ft -GroupBy DC –AutoSize

and we get a nicely formatted report

February 11, 2012  2:36 PM

Domain Controller Service Health

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

The correct functioning of our AD domains depends on the services that are running on our domain controllers

A quick test for the services that could affect AD

function test-dcServiceHealth {            
param (            
"ADWS", "Dfs",  "DFSR", "DNS", "IsmServ", "kdc", "Netlogon", "NtFrs", "W32Time", "WinRM" |            
foreach {            
Get-WmiObject -Class Win32_Service -Filter "Name = '$($_)'"-ComputerName $computername |            
select Name, DisplayName, State,            

One service that is missing is the AD Domain Service for Windows 2008 R2.  That doesn’t seem to show through WMI or Get-Service. I’m still investigating though if DNS and kdc are running they are both dependent on NTDS so it has to be running

February 11, 2012  5:53 AM

Changing folder creation date

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A question on the forum asked about setting creation date on folders after they have been copied to match the source folder.

I created a source folder with three folders and modified the creation dates

Set-ItemProperty -Path c:\testsource\folder1 -Name CreationTime -Value ((get-date).adddays(-90))
Set-ItemProperty -Path c:\testsource\folder2 -Name CreationTime -Value ((get-date).adddays(-127))
Set-ItemProperty -Path c:\testsource\folder3 -Name CreationTime -Value ((get-date).adddays(-192))


I then created a file

dir c:\scripts |out-file -FilePath test.txt

and copied it into each folder


The copy of the folders and their contents is achieved like this

Copy-Item -Path c:\testsource\* -Destination c:\testtarget -Force –Recurse


Our source looks like this

PS> Get-ChildItem -Path c:\testsource | where {$_.PSIsContainer} | ft  Fullname, CreationTime -a

FullName                             CreationTime
——–                                    ————
C:\testsource\Folder1   13/11/2011 11:26:35
C:\testsource\Folder2   07/10/2011 11:27:12
C:\testsource\Folder3   03/08/2011 11:27:27


and the target looks like this

PS> Get-ChildItem -Path c:\testtarget | where {$_.PSIsContainer} | ft  Fullname, CreationTime -a

FullName                           CreationTime
——–                                  ————
C:\testtarget\Folder1   11/02/2012 11:34:25
C:\testtarget\Folder2   11/02/2012 11:34:25
C:\testtarget\Folder3   11/02/2012 11:34:25


So our job is to change the creationtime property on the target folders


Get-ChildItem -Path c:\testsource -Recurse |             
where {$_.PSIsContainer} |            
foreach {            
 $newfolder = $_.Fullname -replace "Testsource", "Testtarget"            
 Set-ItemProperty -Path $newfolder -Name CreationTime -Value $($_.CreationTime)            


Just loop through the source folders – change the path to match the target and set the CreationTime


We can then test the results

PS> Get-ChildItem -Path c:\testtarget | where {$_.PSIsContainer} | ft  Fullname, CreationTime -a

FullName                           CreationTime

——–                                  ————

C:\testtarget\Folder1   13/11/2011 11:26:35

C:\testtarget\Folder2   07/10/2011 11:27:12

C:\testtarget\Folder3   03/08/2011 11:27:27


Job done

February 11, 2012  5:09 AM

Tools for administering Active Directory

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

It was pointed out in a comment that in my series of posts on administering Active Directory (started with http://msmvps.com/blogs/richardsiddaway/archive/2012/01/03/get-ad-users-in-an-ou-tree.aspx and the posts coming forward)

I hadn’t actually discussed the tools I was using in the posts – in the spirit of better now then never I’ll put that right.

There are two areas of Active Directory administration we should think about:

  • Data – users, groups, OUs, computers (possibly plus domains and forests)
  • Service – sites, subnets, site links, replication, schema

Up to now I have concentrated on the data – mainly user administration. I will expand to other areas as we proceed.

Any one who has spent any time administering AD soon becomes familiar with AD Users and Computers and the associated tools. These are great for doing the odd ad hoc job but for bulk investigation or processing we need scripts. In years gone by this would have been VBScript but the world has moved on and PowerShell is the scripting tool of choice for the savvy Windows administrator.

If you need to get started with PowerShell look a the books available at www.manning.com. PowerShell in a month of lunches is a great starter. PowerShell in Practice and PowerShell and WMI will extend that knowledge to actually using PowerShell in the real world.

Now that we’ve decided PowerShell is our admin tool of choice – how do we work with AD. The starting point is that there are no AD admin cmdlets built into PowerShell v2 (or v3 for that matter). However, we do have access to a number of tools that we can use through PowerShell.

The Quest AD cmdlets have been available for a number of years. First issued in 2007  they are a free download in 32 and 64 bit versions from http://www.quest.com/powershell/activeroles-server.aspx. A pdf manual is also available.  They install as a snapin on your workstation.

These cmdlets have a good coverage of AD data administration and also include PKI and Quest’s Active Roles administration.  They have a  number of advantages – especially the fact that you don’t have to install anything on your domain controllers and that they work with AD versions from Windows 2003 to Windows 2008 R2 out of the box. The main draw back is that they are non-Microsoft which is a big negative in some organisations.

Microsoft introduced a set of cmdlets for administering AD and a provider with Windows 2008 R2. If your domain controllers are running an earlier version of WIndows you can download versions of the cmdlets for Windows 2003 and Windows 2008 from the links provided at ttp://blogs.msdn.com/b/adpowershell/archive/2009/09/18/active-directory-management-gateway-service-released-to-web-manage-your-windows-2003-2008-dcs-using-ad-powershell.aspx.

The Microsoft cmdlets work through a web service that runs on the domain controllers. This is installed by default on Windows 2008 R2 but needs a specific install for older versions. I find this a drawback for legacy versions as I like to keep my domain controllers as clean as possible.  If your domain is Windows 2008 R2 then install the RSAT tools on your workstation and you will get the AD cmdlets and provider.

Having looked at the Microsoft provider in a fair amount of detail recently I have to admit that it is better than I thought. I don’t like the navigational requirement to use ou = xxx  to determine path but it is liveable with especially in scripts.

Scripting has a venerable tradition for AD administration. In PowerShell we use the [adsi] type accelerator which is a shortcut to System.DirectoryServices.DirectoryEntry. The class is a wrapper for standard ADSI access to AD. ADSI is COM based to add another level of complexity.  All of these wrappers modify the  objects returned to a greater or lesser extent. This can create some confusion as the methods you are used to from VBScript are available but not visible. You need to know how to use these objects to get the most from them – which is where the posts come in.

Searching AD in VBScript was painful but in PowerShell we get [adsisearcher] which is a type accelerator for System.DirectoryServices.DirectorySearcher. We have seen this in a number of posts – and will see it in the future.

Also available in the .NET fold for access through scripts is the System.DirectoryServices.ActiveDirectory namespace. This provides access to a number of classes that make it easier to deal with the service side – sites etc as we will see later.  The latest addition with .NET 3.5 (needed for ISE and out-gridview) is System.DirectoryServices.AccountManagement. This namespace provides access to users and groups. The syntax is more complex but it supplies easy access to a number of pieces of functionality that we can’t do using other .NET classes.

Finally the System.DirectoryServices.Protocols supplies access to some deep level aspects of AD – for instance we can use it to return an object from being tombstoned. This namespace is not well documented and is not easy to decipher its usage.


I still turn to the Quest cmdlets – I’ve been using them since I was involved with the original beta testing. if you have Windows 2008 R2 you have the Microsoft cmdlets which provide analogous functionality. I would recommend using one (or better still both) sets of cmdlets. Use scripting to fill in the gaps and leave the provider alone except for specific jobs – it is very good for bulk creation of OUs!



I have been asked if I will be pulling all of the code I’m publishing on AD into a book.  I hadn’t thought about it up to now.  Is there enough interest for such a book?

Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: