PowerShell for Windows Admins


February 18, 2012  4:58 AM

LDAP filter issues



Posted by: Richard Siddaway
Active Directory, PowerShell

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)                         
                        
                        
"`nScript"                        
$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!

However

$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



Posted by: Richard Siddaway
PowerShell

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



Posted by: Richard Siddaway
PowerShell

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
D:\NextFolder\ThirdFolder

 

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
D:\NextFolder\ThirdFolder

 

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


February 12, 2012  4:31 AM

Domain Controller Service Health–revisited



Posted by: Richard Siddaway
Active Directory, PowerShell, WMI

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 {            
[CmdletBinding()]            
param (            
 [parameter(Position=0,            
   ValueFromPipeline=$true,             
   ValueFromPipelineByPropertyName=$true)]            
 [string]$computername=$env:COMPUTERNAME            
)            
            
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"            
  return            
}            
            
"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,            
 @{N="DC";E={$computername}}            
}            
}            
}

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



Posted by: Richard Siddaway
Active Directory, PowerShell v2, WMI

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 {            
[CmdletBinding()]            
param (            
 [string]$computername=$env:COMPUTERNAME            
)            
"ADWS", "Dfs",  "DFSR", "DNS", "IsmServ", "kdc", "Netlogon", "NtFrs", "W32Time", "WinRM" |            
foreach {            
            
Get-WmiObject -Class Win32_Service -Filter "Name = '$($_)'"-ComputerName $computername |            
select Name, DisplayName, State,            
@{N="DC";E={$computername}}            
}            
}

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



Posted by: Richard Siddaway
File System, PowerShell

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



Posted by: Richard Siddaway
Active Directory, PowerShell

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.

Recommendations:

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!

 

Question:

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?


February 9, 2012  1:48 PM

February PowerShell group meeting–SQL server and PowerShell



Posted by: Richard Siddaway
User Group


When: Tuesday, Feb 28, 2012 7:30 PM (GMT)


Where: Virtual

*~*~*~*~*~*~*~*~*~*

SQL Server forms the core of many of our systems – how can we administer and access it using PowerShell

Notes


Richard Siddaway has invited you to attend an online meeting using Live Meeting.
Join the meeting.
Audio Information
Computer Audio
To use computer audio, you need speakers and microphone, or a headset.
First Time Users:
To save time before the meeting, check your system to make sure it is ready to use Microsoft Office Live Meeting.
Troubleshooting
Unable to join the meeting? Follow these steps:

  1. Copy this address and paste it into your web browser:
    https://www.livemeeting.com/cc/usergroups/join
  2. Copy and paste the required information:
    Meeting ID: B4N68M
    Entry Code: 38-s+N7W4
    Location: https://www.livemeeting.com/cc/usergroups

If you still cannot enter the meeting, contact support

Notice
Microsoft Office Live Meeting can be used to record meetings. By participating in this meeting, you agree that your communications may be monitored or recorded at any time during the meeting.


February 6, 2012  6:28 AM

Inbound replication



Posted by: Richard Siddaway
Active Directory, PowerShell, WMI

Continuing round the MicrosoftActiveDirectory namespace we get to the MSAD_ReplCursor class which provides inbound replication state information about all replicas of a Naming Context

 

Get-WmiObject -Namespace root\MicrosoftActiveDirectory -Class MSAD_ReplCursor  |
Format-Table -GroupBy NamingContextDN -Property SourceDsaDN, SourceDsaInvocationID,
@{N="LastSuccessfulSync"; E={$_.ConvertToDateTime($_.TimeOfLastSuccessfulSync)}}, USNAttributeFilter –AutoSize

The interesting properties are the TimeofLastSuccessfulSync and the USNAttributeFilter.  The latter – to quote from the documentation  “gets the maximum update sequence number to which the destination server can indicate that it has recorded all changes originated by the given server at update sequence numbers less than, or equal to, this update sequence number. This property is used to filter changes that the destination server has already applied at replication source servers”.

 

Between the WMI classes we have the basis of a good test of replication  for our AD domain.  All we have to do is put it together in a coherent picture.


February 6, 2012  5:00 AM

Automating replication testing



Posted by: Richard Siddaway
Active Directory, PowerShell

Building on the recent post about testing replication I though a bit more automation was needed. Lets create a function to discover the domain controllers

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

 

We then use a simple pipeline to produce a nicely formatted report

 

get-DomainControllerNames |

foreach { test-replication -computername $_ } |

Format-Table -Property SourceDsACN, NamingContextDN, Last* -GroupBy DomainController –AutoSize 

 

   DomainController: DC02.Manticore.org

SourceDsaCN NamingContextDN                                LastSyncAttempt     LastSyncSuccess   
———– —————                                —————     —————   
SERVER02    DC=Manticore,DC=org                            06/02/2012 10:53:45 06/02/2012 10:53:45

SERVER02    CN=Configuration,DC=Manticore,DC=org           06/02/2012 10:01:46 06/02/2012 10:01:46

SERVER02    CN=Schema,CN=Configuration,DC=Manticore,DC=org 06/02/2012 10:01:46 06/02/2012 10:01:46

SERVER02    DC=DomainDnsZones,DC=Manticore,DC=org          06/02/2012 10:22:17 06/02/2012 10:22:17

SERVER02    DC=ForestDnsZones,DC=Manticore,DC=org          06/02/2012 10:07:02 06/02/2012 10:07:02


   DomainController: SERVER02.Manticore.org

SourceDsaCN NamingContextDN                                LastSyncAttempt     LastSyncSuccess   
———– —————                                —————     —————   
DC02        DC=Manticore,DC=org                            06/02/2012 10:03:17 06/02/2012 10:03:17

DC02        CN=Configuration,DC=Manticore,DC=org           06/02/2012 10:02:01 06/02/2012 10:02:01

DC02        CN=Schema,CN=Configuration,DC=Manticore,DC=org 06/02/2012 09:59:38 05/02/2012 19:48:03

DC02        DC=ForestDnsZones,DC=Manticore,DC=org          06/02/2012 10:07:17 06/02/2012 10:07:17

DC02        DC=DomainDnsZones,DC=Manticore,DC=org          06/02/2012 10:22:00 06/02/2012 10:22:00

 

If you have a very large number of domain controllers this may take a while to run. In that case split the domain controller list into a number of CSV files and work from those.


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: