Powershell: Set-StrictMode -Version Latest

Set-StrictMode is a wonderful Cmdlet that you should use in every script you write.  It will save you hours of debugging and frustration from things like fat-fingered variable and property names.  Here is what version 2 of Set-StrictMode does:

-- Prohibits references to uninitialized variables (including uninitialized variables in strings).
-- Prohibits references to non-existent properties of an object.
-- Prohibits function calls that use the syntax for calling methods.
-- Prohibits a variable without a name (${}).

If you apply Set-StrictMode to some of your old scripts, you'll be surprised at how many new errors it will throw at you. This is good though, because it encourages you to write safer, cleaner code with less bugs.  Number 2 on the list above was giving me fits today, however.  Let me give you an example.

$Users = Get-ADUser -Filter * -Properties *

Now let's say that I am interested in the EmployeeId attribute of the users. Even though I specified -Properties *, the user objects in the collection may or may not have a property called EmployeeId. The user object will not have an EmployeeId property that is blank or null.  If the attribute is not populated in Active Directory, the Cmdlet omits the property entirely.  So in a script without Strict Mode, I could just do

Foreach($User In $Users) { If($User.EmployeeId) { ... } }

And it would function as expected, running the code block if the user had an EmployeeId, and skip it otherwise. But with Strict Mode, you'll see a lot of this:

Property 'EmployeeId' cannot be found on this object; make sure it exists.
At line:1 char:9
+ $User. <<<< EmployeeId
    + CategoryInfo          : InvalidOperation: (.:OperatorToken) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFoundStrict

Even if I did

$Users = Get-ADUser -Filter { EmployeeId -NE $Null } -Properties *

or

$Users = Get-ADUser -Filter * -Properties * | Where-Object { $_.EmployeeId -NE $Null }

I would still get the errors when running through the Foreach loop, even though that should have given me only the users with EmployeeIDs. So I need a way to test for the existence of an object property, rather than just assuming that a null value returned when the property is referenced is good enough.

If($User -NE $Null -AND $User.PSObject.Properties.Match('EmployeeId').Count) { ... }

This works. The -AND operator in Powershell works like a "short circuit" && operator in most programming languages, meaning that if the first expression does not satisfy the requirements for entering the code block, then the second expression is not evaluated. This is perfect, because if I accidentally feed a null user object to the code above, I would have gotten the same error about the $User object not containing a PSObject property.

Alright, back to scripting!

Tell Me Which Active Directory Security Groups Are Not Applying Inheritable Permissions

I encounter many Active Directory forests that were built and maintained for years by other organizations, and then through mergers or acquisitions or business reorgs, I need to help bring them into the fold with the rest of my portfolio of AD forests.

AD permissions can be a deep rabbit hole, especially when sitting down to a new directory sight unseen. Administrators make subtle changes to AD objects over the years and a lot of entropy happens.  Entropy that's not always easy to see or keep track of.

In this particular instance, we had an issue where a delegation of control was not working correctly and/or consistently.  It was the common IT task of delegating the ability to reset passwords and unlock accounts (but nothing else) to a special "help desk" sort of security group. It was allowing members of the "help desk" group to reset the passwords of certain users, but not others.  None of them were administrative users, or members of the Domain Admins group, the Account Operators group, etc. etc.

Turns out, the problem was that some security groups were not including inheritable permissions from the domain root object, so users who were members of these certain groups were immune to the effects of the delegation. 

Advanced Permissions

At first I actually thought to go and click on every single security group in the domain, checking on whether they were applying inheritable permissions or not.  Then a few seconds I realized, "Don't be an idiot Ryan. The GUI is not your friend! Don't succumb to its siren song!"

After glancing at this excellent SDDL reference here for about 5 minutes, I whipped this up:

Foreach($_ In Get-ADGroup -Filter *)
{
	[bool]$Inherits = $($(Get-ADGroup $_ -Properties *).nTSecurityDescriptor.Sddl.Contains('OI'))
	If($Inherits -EQ $False)
	{
		$_
	}
}

 And presto - a list of all security groups in the domain that are not applying inheritable permission from their parents.

PS - I admit, I still have to use a cheat sheet when reading SDDL.  :P

Finding Locked Out Domain Accounts, Or At Least, How Not To

I hadn't posted in a little while, so I thought I'd do a two-fer today.

You might see some advice on the internet about using the userAccountControl attribute to identify locked out domain accounts.  More specifically, the following LDAP filter:

(&(objectCategory=User)(userAccountControl:1.2.840.113556.1.4.803:=16))

The =16 part should mean "locked out", per the attribute's documentation, keeping in mind that 0x10 in hex is 16 in decimal.

DON'T USE IT.  It doesn't work. I don't think it has ever worked. Apparently it was just an idea that some person on the AD design team had that never got implemented. If anyone has any history on this bit, and if it has ever worked in the past, I would love to hear about it. All I know is that it does not work now.

You can easily verify for yourself that it doesn't work with Powershell:

Get-ADUser -LDAPFilter "(&(objectCategory=User)(userAccountControl:1.2.840.113556.1.4.803:=16))"

You'll probably get 0, or some other very inaccurate value. (Other userAccountControl flags however do definitely work and can be used reliably. Just not this one.)

Here is another LDAP filter you will often see on the web for finding locked out accounts:

(&(objectCategory=Person)(objectClass=User)(lockoutTime>=1))

DON'T USE THAT EITHER. That will return too many results.  The reason why is that lockoutTime is not reset until the next time that person successfully logs in. So if an account is locked out, then their lockoutTime attribute gets set, then if the domain lockout duration expires, the account is no longer technically locked out, but lockoutTime remains populated until the next time that user logs in. Now if you're thinking that we should filter this list by only the users who have a lockoutTime that is less than [domain lockout duration] minutes in the past, then you're on the right track. Those would be the users who are still really locked out.

When I type  Search-ADAccount -LockedOut , however, I am given what seems to be an accurate number of users that are currently locked out. I should point out that if working in a large AD environment, I think it's best to point directly to your PDC-emulator whenever possible, because your PDC-emulator will always have the most up-to-date information about account lockouts. From a Microsoft article about urgent replication:

... account lockout is urgently replicated to the primary domain controller (PDC) emulator role owner and is then urgently replicated to the following:

• Domain controllers in the same domain that are located in the same site as the PDC emulator.

• Domain controllers in the same domain that are located in the same site as the domain controller that handled the account lockout.

• Domain controllers in the same domain that are located in sites that have been configured to allow change notification between sites (and, therefore, urgent replication) with the site that contains the PDC emulator or with the site where the account lockout was handled. These sites include any site that is included in the same site link as the site that contains the PDC emulator or in the same site link as the site that contains the domain controller that handled the account lockout.

In addition, when authentication fails at a domain controller other than the PDC emulator, the authentication is retried at the PDC emulator. For this reason, the PDC emulator locks the account before the domain controller that handled the failed-password attempt if the bad-password-attempt threshold is reached.

If you follow my earlier instructions on how to peek inside the Search-ADAccount cmdlet itself, you'll see that Microsoft themselves is keying on the Account Lockout Time to perform this search:

Finally, I can reproduce the same behavior of  Search-ADAccount -LockedOut  with the following bit of Powershell, given that I know my domain's account lockout duration:

Get-ADUser -LDAPFilter "(&(objectCategory=Person)(objectClass=User)(lockoutTime>=1))" -Properties LockoutTime | 
Select Name, @{n="LockoutTime";e={[DateTime]::FromFileTime($_.LockoutTime)}} | 
Sort LockoutTime -Descending | ? { $_.LockoutTime -gt (Get-Date).AddMinutes($AccountLockoutDuration * -1) }

That gives the exact same results as  Search-ADAccount -LockedOut .

Accessing HKEY_USERS With Powershell

If you run  Get-PSDrive  in Powershell, you will see that you are given two registry providers: HKEY_LOCAL_MACHINE (HKLM) and HKEY_CURRENT_USER (HKCU).

Given that HKCU doesn't actually exist per se, but is rather just a "projection" or "mapped view" of HKEY_USERS\<Your SID>, that means that the HKCU provider can be problematic in certain remote registry or network logon situations, and of course it's no good at all if you want to view or manipulate the registry of another user.

So how do we access HKEY_USERS in Powershell?  Here are three ways that will work just fine:

#
Set-Location Registry::\HKEY_USERS
#
#
New-PSDrive HKU Registry HKEY_USERS
Set-Location HKU:
#

And in this last example, I will set the desktop wallpaper for my own user account to cats.jpg, without using HKCU. This way I can run it at system boot, before I ever even log on:

#
Set-ItemProperty -Path Microsoft.PowerShell.Core\Registry::HKEY_USERS\S-1-5-21-2381515279-1434780867-6353740611-13738\Software\Microsoft\Windows\CurrentVersion\Policies\System -Name Wallpaper -Value C:\Pictures\cats.jpg
#

Taking a Peek Inside Powershell Cmdlets

Have you ever wondered how a particular Powershell Cmdlet works under the hood?  Maybe you're trying to mimic a certain behavior of a Cmdlet, and you'd like to see how Microsoft did it.

Turns out, it's surprisingly easy. The first thing you need is a .NET decompiler. There are many to choose from, but I like DotPeek.

Next, pick a Cmdlet, such as  Get-ADUser . To find the DLL that the Cmdlet comes from, do this:

Cmdlet DLL

If you add a  | clip  on the end there, the output will go straight to your clipboard.

(Did you know the hexadecimal color code for the Powershell background color is 012456?)

Anyhow, now that we know in what DLL the Cmdlet resides, we need to find out what method(s) within that DLL the Cmdlet is actually calling.  We can do that with  Trace-Command :

Trace-Command

There's a little more output after that, but this last line here is what we want. Microsoft.ActiveDirectory.Management.Commands.GetADUser.

Now we know the actual .NET method being called, and which DLL it's in. All that's left to do is fire up your .NET decompiler and disassemble!

Get-ADUser DotPeek