Zoho Banner September 2011

Archive for the ‘PowerShell’ Category

I came across a weird error the other day while running Exchange Server 2013 CU6 setup with the /PrepareAD switch.  The error (from the Exchange setup logs) was this:

[12/02/2014 01:14:16.0727] [2] [ERROR] Could not find the Exchange Mailbox Administrators Universal Security Group through its well-known GUID 29a962c2-91d6-4ab7-9e06-8728f8f842ea.  Please make sure that Setup /prepareAD has been run.

I sat there scratching my head for a while because what (wtf?) is the Exchange Mailbox Administrators group and what does it have to do with Exchange 2013.  Anyway, after a journey of discovery, here’s what I found out.

For some time, Exchange has used an attribute named otherWellKnownObjects on the Exchange container in the Configuration partition of AD to store information about the location of certain Exchange objects, including the default groups.  One of these entries corresponds to the Organization Management group (as shown below).

attribute values

The strange-looking string next to the DN for the Organization Management group (C262A929D691B74A9E068728F8F842EA) is a Hex representation of a GUID.  To get to the string representation I found a handy function called Convert-OctetStringToGuid.  The result was as follows:

convert hex

The resulting GUID string (29a962c2-91d6-4ab7-9e06-8728f8f842ea) matches that shown in the original error.  So, basically the setup error was saying that it couldn’t find the Organization Management group.  The guff about the Exchange Mailbox Administrators group must be a legacy piece of code referencing an earlier version.  Once I had the new information I had a look to see if the group actually existed (it did).  I then concluded that maybe AD replication hadn’t finished by the time setup got to looking for the group.  I then made the executive decision to run setup with the /PrepareAD switch again.  Lo and behold it finished successfully.

I thought this write-up might help if you find yourself in the same boat.

A question came up in the forums today about how to use the AD Powershell cmdlets to find objects with attribute values that contain a single space. It’s a good question and relevant because often your results can be skewed by such values when searching for attributes that are not NULL (a space character is not a NULL). Anyway, here’s an example of how to do it using the LDAPFilter.

Get-ADUser -LDAPFilter "(telephonenumber=\20)"

The “\” is the standard escape character for use in LDAP searches and “20″ is the HEX representation of the space character.

The filter should also work with other LDAP clients (e.g. LDP.EXE).

It’s sometimes interesting to know how old your AD forest is and when the various domains were created.  I recently came across a really useful TechNet Blog with a Powershell snippet to do just this.  My version shown below just has some slightly different formatting.

# How old is the forest?
Get-ADObject -SearchBase (Get-ADForest).PartitionsContainer `
-LDAPFilter "(&(objectClass=crossRef)(systemFlags=3))" `
-pr dnsRoot, nETBIOSName, whenCreated | Sort whenCreated `
| select @{e={($_.DNSRoot)};l=”DomainFQDN”}, `
nETBIOSName, @{e={(get-date $_.whencreated -format dd/MM/yyyy)};l=”whenCreated”} `
| ft -AutoSize

The output should look something like this:

check-forestage

So how old is your forest?

If you haven’t heard that extended support for Windows XP ended earlier this year you’ve clearly been in a coma.  There are a number of well-publicised methods for finding out whether you still have XP machines in your environment.  Here is my own humble (and spectacularly over engineered) Powershell offering.

 

#########################################################
#
# Name: Find-XPStillActive.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 23/09/2014
# Comment: PowerShell 2.0 script to find active
# Windows XP computer accounts
#
#########################################################

## Define global variables
# Export file for storing results
$expfile = "c:\xp_still_active.csv"
# Define the header row for the CSV (we will create our own)
$header = "`"name`",`"os`",`"sp`",`"lastlogondate`""
# Consider any account logged on in the last x days to be active
$days = 60
$Today = Get-date
$SubtractDays = New-Object System.TimeSpan $days, 0, 0, 0, 0
$StartDate = $Today.Subtract($SubtractDays)
$startdate = $startdate.ToFiletime()
# LDAP filter settings
$filter = "(&(lastlogontimestamp>=$startDate)(operatingsystem=Windows XP*))"

## Functions
Function Format-ShortDate ($fdate)
{
        if ($fdate) {
            $day = $fdate.day
            $month = $fdate.month
            $year = $fdate.year
            "$day/$month/$year"
        } # end if

} # end function

## Start doing things
# Import the AD module
ipmo ActiveDirectory
# Tidy up any previous copies of the export file 
if (test-path $expfile) {Remove-Item $expfile}
# Add the header row to the export file
Add-Content -Value $header -Path $expfile
# Create an array of computer objects
$active = Get-ADComputer -LDAPFilter $filter -pr *
# loop through the array
foreach ($xp in $active) {
    # Grab the attribute values we need from the AD object
    $nm = $xp.name
    $os = $xp.operatingsystem
    $sp = $xp.operatingsystemservicepack
    $lt = Format-ShortDate $($xp.lastlogondate)
    $row = "`"$nm`",`"$os`",`"$sp`",`"$lt`""
    # Commit the row to the export file
    Add-Content -Value $row -Path $expfile
} # end foreach

## End script

Enjoy!

 

There was in interesting discussion the other day on the ActiveDir.org mailing list. Someone asked how many values can be stored within the proxyAddresses mutlivalued attribute in Active Directory. The responses were reasonably consistent, with most people indicating that in Windows 2000 the number was in the range of approximately 800 to 850 and from Windows 2000 the range is approximately 1200 to 1300.

This begs the question of why we can’t be specific about the number. Well, it comes down to how the data is stored within the Active Directory database (ntds.dit). Most of the attribute data for an individual object is stored within a single row in the Data Table within the database. I say “most” because linked attribute data (e.g. member/memberof, manager/directReports, etc.) is kept in a separate table (the Link Table). The AD schema determines how many attributes are available for a particular object and this obviously varies a lot from forest to forest, as does which of those possible attributes actually have populated values. There are also some overhead requirements that vary. All this combines to make it impossible to determine with any accuracy how many values an individual multivalued, nonlinked attribute can have.

Because I have an enquiring mind (some would say “nosey bastard”), I decided to try and hit the limit in my test lab. I did this by running a Powershell script to keep adding SMTP addresses to the proxyAddresses attribute for a user until an exception was thrown. I got to 1192 before I got the “The administrative limit for this request was exceeded” error (see below).

admin_limit

As Don Hacherl (former dev lead for AD at Microsoft) pointed out to me on the mailing list, the non-linked attribute limit is a limit across all non-linked attributes on the object. So for example, if I had added a telephone number before running the script then I would have only got to 1191 values on proxyAddresses.

Don also made is clear that under normal circumstances you shouldn’t need to be anywhere near the limit. In his words…

“The limit is supposed to be high enough that the only time you’ll hit it is when you have made an architectural error in your schema usage. Asking questions about the exact number of a limit that you have not yet hit is often a warning sign of your burning desire to make an architectural error in the future.”

I found a Powershell script on Experts Exchange that seemed to be useful for detecting errant objects that have a high number of values within an individual multivalued attribute. I’ve hacked with it a bit and have ensured that it now excludes linked values. Here it is for anyone that is interested.

#########################################################
#
# Name: Find-BloatedObjects.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 18/09/2014
# Comment: PowerShell 2.0 script to
# find objects with an unusually large number of values
# within a non-linked multi-valued attribute
# Credit: Parts of the script are from a solution posted by 
# footech on Experts Exchange here:
# http://tinyurl.com/md3cvzn
#
#########################################################

Import-Module ActiveDirectory
$queryCount = 1000 # adjust this if you need to
$snc = (Get-ADRootDSE).SchemaNamingContext
$dnc = (Get-ADRootDSE).DefaultNamingContext
$def = "Microsoft.ActiveDirectory.Management.ADPropertyValueCollection*"
$objs = Get-ADObject -Filter * -Properties * -SearchBase $dnc

ForEach ($obj in $objs) {
    #Write-Output "Looking at object $obj.distinguishedname"
    $ADOprops = $obj | gm -MemberType Property `
    | Where { $_.Definition -like $def } `
    | Select -ExpandProperty Name
        foreach ($prop in $ADOprops) {
            #Write-Output "Looking at property $prop"
            $fl = "(&(objectclass=attributeschema)(ldapdisplayname=$prop))"
            $linked = (Get-ADObject -LDAPFilter $fl -SearchBase $snc -pr linkid).linkid
            #Write-Output $linked
            If (($linked -eq $null) -and (($obj.$prop).count -gt $queryCount)) {
                Write-Output "----------------------------------------------"
                Write-Output "AD Object ""$($obj.DistinguishedName)"""
                Write-Output "has attribute ""$prop"" with a count of $($obj.$prop.count)"
            } # end if
        } # end foreach
} # end foreach

Feedback is, as always, very welcome!

 

When you install a new version of Exchange or apply a Cumulative Update certain AD attributes are updated to reflect the change.  The updates are made in three different directory partitions (also known as naming contexts): Schema, Configuration and Domain.  The following Microsoft TechNet article is a good reference for the different versions and the corresponding attribute values.

http://technet.microsoft.com/en-us/library/bb125224(v=exchg.150).aspx

You can check the values manually….or you could do it the easy way with Powershell.  Here’s a Powershell sample to give you values across the three partitions (assumes a single domain forest):

 

# Exchange Schema Version
$sc = (Get-ADRootDSE).SchemaNamingContext
$ob = "CN=ms-Exch-Schema-Version-Pt," + $sc
(Get-ADObject $ob -pr rangeUpper).rangeUpper

# Exchange Object Version (forest)
$cc = (Get-ADRootDSE).ConfigurationNamingContext
$fl = "(objectClass=msExchOrganizationContainer)"
(Get-ADObject -LDAPFilter $fl -SearchBase $cc -pr objectVersion).objectVersion

# Exchange Object Version (domain) - assumes single domain forest
$dc = (Get-ADRootDSE).DefaultNamingContext
$ob = "CN=Microsoft Exchange System Objects," + $dc
(Get-ADObject $ob -pr objectVersion).objectVersion

The output will looking something similar to screenshot shown below (showing the values for Exchange Server 2013 CU5):

Schema

I had a requirement recently to try and find out where and when a whole lot of mailboxes were hidden from the GAL.  Yes, fingering some poor sucker for the blame is an immensely satisfying task, isn’t it?  I’ve found that an effective way to do this is to query the AD replication metadata for the attribute concerned (in this case ‘msexchhidefromaddresslists’).  The replication metadata will provide you with the date/time for when the attribute value was last changed as well as the name of the DC where the last change was made.  From there you can search the Security Event Log on the DC in question for the audit events corresponding to the change.  This of course assumes that you have Audit Directory Service Changes switched on.

Typically, I would use the excellent Repadmin.exe command line tool to query the replication metadata, e.g. -

Repadmin /showobjmeta MyDC1 “CN=MyUser,OU=User Accounts,DC=contoso,DC=com”

However, in this case someone had already reversed most of the changes (i.e. unhidden the mailboxes) and I needed to query a large number of objects to find some others that were still hidden, hoping that some of them would have a common data/time stamp.  For this the Repadmin.exe would work but would be hopelessly inefficient.  And what (I said to myself) is the best method for performing bulk operations such as this?  Yes, that’s right:  Powershell to the rescue!

After some Googling, I found an excellent code snippet from Powershell MVP Brandon shell that hooks into the underlying .Net class to expose the replication metadata.  His is the clever bit (that’s why he’s paid the big bucks) – I’ve basically just done the wrapper to perform a bulk query and output the results to a CSV file.  Here’s the script for your enjoyment.

 

#########################################################
#
# Name: BulkReportReplicationMetadata.ps1
# Author: Tony Murray
# Version: 2.0
# Date: 27/03/2014
# Comment: PowerShell 2.0 script to find change times
# for an individual AD attribute using replication metadata
# 
# Some bits borrowed from: Get-ADObjectREplicationMetadata.ps1
# Brandon Shell (www.bsonposh.com)
#
#########################################################

# import the AD module
ipmo ActiveDirectory

# Define variables
$domain = (get-addomain).dnsroot # Use the current AD domain
$property = "msexchhidefromaddresslists" # This is the AD attribute we are interested in
$outfile = "c:\csv\outfile.csv" # CSV output file

# Blow away the existing file if it exists
if (test-path $outfile) {remove-item $outfile}

# We will build our own CSV rather than work with export-csv
$header = "samaccountname,modified,dc"
Add-Content -Value $header -Path $outfile

$sb = "OU=Standard User Accounts,DC=contoso,DC=com" # Search base for where our mailbox users live
$fl = "(&(homemdb=*)(msexchhidefromaddresslists=TRUE))" # LDAP filter to find our users
$users = Get-ADUser -LDAPFilter $fl -searchbase $sb

# Loop through our list of users
foreach ($user in $users) {

    $objectDN = $user.distinguishedname # used for finding the replication metadata
    $name = $user.samaccountname # Just for info
    # Sets Context to Domain for System.DirectoryServices.ActiveDirectory.DomainController
    $context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$domain)
    # .NET Class that returns a Domain Controller for Specified Context
    $dc = [System.DirectoryServices.ActiveDirectory.DomainController]::findOne($context)
    # GetReplicationMetadata returns metadata from the DC for the DN specified.
    $meta = $dc.GetReplicationMetadata($objectDN)
    # Get the last time the attribute value was changed
    $ctime = $meta | %{$_.$Property.LastOriginatingChangeTime}
    # Get the DC that the change was made on
    $dcon = $meta | %{$_.$Property.OriginatingServer}
    # Build the values to write to the output file
    $line =  "`"$name`",`"$ctime`",`"$dcon`""
    # Write the line to the output file
    Add-Content -Value $line -Path $outfile

} # end foreach

The shrewd amongst you would ask why I didn’t query the Exchange (2010 in this case) audit log for this information. The answer is that I did, but couldn’t find the relevant audit entries. The Exchange audit events are only captured if the Exchange tools (EMC/EMS/ECP) were used to perform the change. In my case the changes had been made in bulk, probably using the AD cmdlets.

Reading the post title you maybe wondering whether I’ve taken leave of my senses.  Why call Robocopy from Powershell to count files when Powershell has a perfectly good method of doing this natively? For example,

(Get-ChildItem e:\powershell -Recurse -force | ?{! $_.PSIsContainer} | Measure-Object).count

Well, the problem comes in when an individual file length exceeds 260 characters.  At this point Powershell throws an error similar to the one below.

Get-ChildItem : The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.

At E:\Powershell\CountFiles.ps1:243 char:32

+ $hcountpost = (Get-ChildItem e:\powershell -Recurse -force | Measure-Obj …

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : ReadError: (\\myserver\homed… – blah blah:String) [Get-ChildItem], PathTooLongException

+ FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand

The problem is not so much to do with Powershell as the underlying .NET APIs, as the following article succinctly explains.

http://blogs.msdn.com/b/bclteam/archive/2007/02/13/long-paths-in-net-part-1-of-3-kim-hamilton.aspx

This is where Robocopy comes in handy as it doesn’t suffer from the same limitations.  On the other hand Robocopy has its own output formatting that doesn’t play nicely with Powershell.  I came across a very handy code snippet from MVP Boe Prox that can help with this.  The only change I have made is to roll it up into a function (shown below for your pleasure).

Function Count-Files ($path) {
    $params = New-Object System.Collections.Arraylist
    $params.AddRange(@("/L","/S","/NJH","/BYTES","/FP","/NC","/NDL","/TS","/XJ","/R:0","/W:0"))
    $countPattern = "^\s{3}Files\s:\s+(?\d+).*"
    $sizePattern = "^\s{3}Bytes\s:\s+(?\d+(?:\.?\d+)).*"
    $return = robocopy $path NULL $params
    If ($return[-5] -match $countPattern) {
        $Count = $matches.Count
    } # end if
    else {
    $Count = 0
    } # end else
    Return $count
} # end Count-Files function

 

Enjoy.

 

Another short blog posting (brevity is obviously my thing for 2014).

I occasionally come across environments in which Strict Replication Consistency is not enabled.  This is invariably because these environment have forests that were created with Windows 2000 server, but which have subsequently been upgraded.  If you need to know more about Strict Replication Consitency and why it is important for your AD environment, I don’t plan to bore you with the details here as this topic is well covered elsewhere.  The best article I have found on the topic is this one.

http://blogs.technet.com/b/askds/archive/2010/02/15/strict-replication-consistency-myth-versus-reality.aspx

The article makes the essential point that if the environment does not have SRC enabled then you need to do two things:

  1. Ensure the required registry key is configured on each DC.  The quickest method to achieve this is using Repadmin as follows: repadmin /regkey * +strict
  2. Ensure any new DCs promoted within the forest automatically have SRC enabled by creating an operational GUID (actually a Container object in the Configuration Partition).  There are several ways to create the object, but as you know, I’m a fan of Powershell, so here’s the code to do it:

 

# Strict Consistency - add operational GUID
$cnc = (get-adrootdse).configurationnamingcontext
$path = "CN=Operations,CN=ForestUpdates," + $cnc
$obj = "94fdebc6-8eeb-4640-80de-ec52b9ca17fa"
New-ADObject -Name $obj -Type container -Path $path -OtherAttributes @{showInAdvancedViewOnly="TRUE"}

Enjoy!

Today’s blog entry is short and sweet.  You want to quickly find out whether the AD Recycle Bin feature is enabled or not.   There are a few ways to do this, but this is a quick Powershell method.

 

# Is the AD Recycle Bin enabled in my forest?
Import-Module ActiveDirectory
If ((Get-ADOptionalFeature -Identity "Recycle Bin Feature").EnabledScopes) {
    write-host "AD Recycle Bin is enabled"
    } # end if
Else {
    write-host "AD Recyle Bin is not yet enabled"
} # end else