Zoho Banner September 2011

I fell foul of this one the other day and it took a while to figure out what to do about it. Here’s the scenario:

You deploy a new Group Policy Preferences (GPP) setting to create a folder on a workstation and specify the “Apply once and do not reapply” option. Unsurprisingly, this implies the GPP item will apply only once and will not run again!

gpp1

But what if you have a problem on a specific target computer where the computer thinks it has already applied the GPP setting once? You won’t get the setting to reapply using the traditional gpupdate /force option. Instead you must look in the registry of the affected computer and look for the RunOnce entries. These are located here:

HKEY_CURRENT_USER\Software\Microsoft\Group Policy\Client\RunOnce
HKEY_LOCAL_MACHINE\Software\Microsoft\Group Policy\Client\RunOnce

The registry string values (REG_SZ) shown correspond to the RunOnce settings. Instead of descriptive text these entries have (spectacularly unhelpful) GUID-style names.

gpp4

Ok, so armed with this information, how do you go about identifying the GUID-style name with your GPP setting? Don’t be led down the path of thinking the GUID-style name will match the GUID of your GPO – it doesn’t. That kind of make sense as a single GPO can have multiple GPP settings. The answer lies buried within the XML of the GPP setting. To get to the XML, right-click the GPP item within the GPO Editor and select All Tasks -> Display XML. Once you can see the XML look for the line starting with “FilterRunOnce” line. The value shown next to the “ID” is the one that corresponds to the registry entry.

 

gpp2

 

gpp3

To get the GPP setting to re-apply to the problem workstation, simply delete the relevant string value from the registry and force the policy to re-apply (gpupdate /force).

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 post is another short one.  It’s a Powershell one-liner to find all the Primary SMTP address suffixes in use by the mailboxes in your Exchange Org.

In this example I know that my default suffix is “contoso.com”, but I want to find out what others are being used as primary:

get-mailbox -ResultSize unlimited | ? {$_.primarySMTPaddress -notmatch "@contoso.com" } `
| select @{l="SMTPSuffix";expression={$_.primarysmtpaddress.tostring().Split("@")[1]}} –unique

The output (which will take a while as there is a lot of post-processing in the pipeline) looks like this:

SMTPSuffix
_______
fabrikam.com
northwindtraders.com
fouthcoffee.com

Enjoy!

 

Short answer:  No, AppLocker is not supported on Windows Server 2012 Server Core.

Slightly more long-winded answer:

My Google/Bing mojo failed to find a definitive answer to this question on-line.  In fact, I found two apparently conflicting sources of information.

This was the first one:

Windows PowerShell can used to manage AppLocker on Server Core installations using the AppLocker cmdlets and, if administered within a GPO, the Group Policy cmdlets. For more information, see the AppLocker PowerShell Command Reference.

http://technet.microsoft.com/en-us/library/hh831440.aspx

I tried to test this, but switching from “Server with a GUI” to Server Core removes the Application Identity service, which is required for enforcement of AppLocker rules.   The AppLocker event log is also removed.

This was the second one I found:

In Windows Server 2012 and Windows 8

AppLocker is supported on all Windows beta evaluation versions except the Server Core installation option.

http://technet.microsoft.com/en-us/library/ee619725(v=ws.10).aspx

Mmm, it only mentions the “beta evaluation” version, so a strong hint, but no definitive statement.

In the end I received a response from someone within Micrsoft to a Technet Forum post.  You can read the full thread here:

http://social.technet.microsoft.com/Forums/en-US/4d78ac57-df3d-444f-b6a4-9df892db8df8/applocker-on-server-core?forum=winservercore

 

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

I quite regularly come across Active Directory environments where users have been mistakenly added to groups protected by the AdminSDHolder and subsequently removed when the mistake has been realised.  This process creates “orphans” because the AdminSDHolder process doesn’t tidy up after itself.  Here’s what happens:

  1. User added to group protected by the AdminSDHolder (e.g. Account Operators)
  2. The AdminSDHolder process (actually a thread within LSASS.EXE on the PDC Emulator) sets the adminCount attribute value to 1 on the user object and replaces the user’s security descriptor with that of the AdminSDHolder container object.
  3. User is then removed from Account Operators.
  4. The adminCount value of 1 is not cleared and the security descriptor is not changed (i.e. remains the same as that of the AdminSDHolder).

It doesn’t make sense to leave these orphans as they are.  For one, it plays havoc with any delegation model that is in place.  It also makes it difficult to determine what objects are actually protected.  For example, searching for objects that have an adminCount of 1 will potentially give you misleading results.

So what can you do about it?  One option is to manually look at each object to determine whether it is genuinely a protected object and then clear the adminCount value and re-set the inheritance flag for any objects no longer protected.  This is likely to be incredibly time consuming as you will need to factor in nested group membership into your investigation.  Another option is to run a script that clears the adminCount value and resets the inheritance flag for *all* protected objects.  Any objects that should genuinely be protected will be re-protected (I just made that word up) when the AdminSDHolder next cycles (within 1 hour by default).

The second option sounds like the clear winner.  There are some small risks that you should be aware of with this approach.  Firstly, there might be some malware “lurking” in your environment that will take the opportunity to compromise privileged accounts when they are in an unprotected state (even for a short period).  Secondly, re-setting the inheritance flag on objects might cause unforeseen problems.  For example, a user object might have had the inheritance flag unchecked prior to being protected by the AdminSDHolder.

Basically, there is no silver bullet here.  Personally, I think the small risk associated with the scripted option generally outweighs the negative impact of AdminSDHolder orphans.  Here’s the script that I use:

#########################################################
#
# Name: Cleanup-AdminSDHolder.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 28/08/2013
# Comment: PowerShell 2.0 script to re-set inheritance
# flag for objects no longer protected by the 
# AdminSDHolder
#
########################################################### 

# Import the AD module
Import-Module ActiveDirectory

# Find objects that appear to be protected in AD
$probjs = Get-ADObject -LDAPFilter "(admincount=1)"
$bcount = $probjs.count
if ($bcount) {
    write-host "Protected object count before change is $bcount"
} # end if
else {
    write-host "No objects are currently protected - nothing to do"
    exit
} # end else

# Change to the AD drive
cd AD:

# Loop through protected objets to set inheritance flag
# and clear the adminCount value
foreach ($probj in $probjs) {
    $dn = $probj.distinguishedname
    $acl = get-acl $dn
    if ($acl.AreAccessRulesProtected) {
        write-host "$dn has inheritance blocked - we will remove the block"
        $acl.SetAccessRuleProtection($false, $false);
        set-acl -aclobject $acl $dn
        # We need to clear the adminCount attribute too
        Set-ADObject -Identity $dn -Clear adminCount
    } # end if
} # end foreach    

# Count the number of protected objects
$acount = (Get-ADObject -LDAPFilter "(admincount=1)").count
if ($acount) {
    write-host "Protected object count after change is $acount"
} # end if
else {
    write-host "No objects are currently protected"
} # end else

Please leave a comment if you have any thoughts on how to improve the script, or if you are aware of a better method of handling AdminSDHolder orphans.

You can download a copy of the script here: Cleanup-AdminSDHolder

 

Back in March 2010 when Powershell and I were on somewhat less friendly terms, I wrote an OU shadow script to populate group membership based on the contents of an OU. Since then, Powershell and I now at least acknowledge eachother when we pass in the corridor and I have updated the script with some improvements.

One common use for the script is populating group memberships for use with Fine-Grained Password Policy (FGPP).

Please leave a comment if you see any scope for improvement.  You can download a copy of the script here: OUBasedGroupMembership ps1

#########################################################
#
# Name: OUBasedGroupMembership.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 03/09/2013
# Comment: PowerShell 2.0 script to
# manage group membership based on OU contents
#
#########################################################

# Import the AD module
ipmo ActiveDirectory

# Define arrays to be used for matching
$arrou = @()
$arrgp = @()

# Domain controller to be used
$dc = (Get-ADRootDSE).dnshostname
write-host "Using DC $dc for all AD reads/writes"

# Specify the OU where the accounts are located
$OUdn = "OU=Admin Accounts,OU=AD Administration,DC=contoso,DC=com"
$OuUsrs = Get-ADUser -Filter * -SearchBase $oudn -Server $dc

# Specify the group to use
$grp = "de42112f-81d2-4849-900c-d6907c77d3f5" # "Service Accounts"
$grpusers = Get-ADGroupMember -Identity $grp -Server $dc

# Build arrays using the DN attribute value
$OuUsrs | % {$arrou += $_.distinguishedname}
$grpusers | % {$arrgp += $_.distinguishedname}

# Add to group membership (new user in OU)
foreach ($usr in $arrou) {
    if ($arrgp -contains $usr) {
        write-host "User $usr is a member of the group"
    }
    else {
        write-host "User $usr is not a member of the group - adding..."
        #Add-ADGroupMember -Identity $grp -Members $usr -Server $dc
    } # end else
    Remove-Variable -ErrorAction SilentlyContinue -Name usr    
} # end foreach

write-host "`n"

# Remove from group membership (no longer in OU or has been manually added to group)
# The assumption here is that the OU is authoritative for the group's membership
foreach ($mem in $arrgp) {
    if ($arrou -contains $mem) {
        write-host "User $mem is located in the OU.  Nothing to do"
    } # end if
    else {
        write-host "User $mem is not present in the OU.  Removing from membership..."
        #Remove-ADGroupMember -Identity $grp -Members $mem -Server $dc -Confirm:$false
    } # end else
    Remove-Variable -ErrorAction SilentlyContinue -Name mem
} # end foreach

I’ve recently been looking at Microsoft’s Security Compliance Manager 3.0.  SCM allows provides a rich set of server-role-based security baselines for deployment using either GPO or SCCM.  This latest version includes baselines for Windows Server 2012. 

After deploying the “WS2012 Domain Controller Security Compliance 1.0″ baseline settings via GPO into my lab environment I found RDP sessions to my Windows Server 2012 DCs to be horrendously slow – almost to the point of not being able to do anything.

My on-line searches for the cause revealed nothing official from Microsoft, but I did find some references to one specific setting being the probable cause.  The setting is “Use FIPS compliant algorithms for encryption, hashing, and signing” set to Enabled.

Computer Config->Policies->Windows Settings->Security Settings->Local Policies->Security Options->System Cryptography->Use FIPS compliant algorithms for encryption, hashing, and signing

After setting the value to Disabled and updating Group Policy on the DCs my RDP sessions returned immediately to normal speed.

I hope this information helps others who might come across the same behaviour.