Finding disabled computers in AD with PoSh

By YellowOnline on Friday 25 February 2011 10:48 - Comments (3)
Category: Powershell, Views: 3.354

In between all of my (Dutch) posts about sex, politics and travel I finally make a short post about PowerShell. My aim is to provide short and to the point code examples, explaining also how the code works.

---------------

For today: how can you find disabled computers in AD with PoSh if you can't use the AD-cmdlets, eg. because the necessary services are unavailable?

A sample script

PowerShell:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$DomainName = "www.tweakers.net"
$OU = "TweakBlogs"
$DCNotation = "DC=" + $DomainName.Replace(".",",DC=")
$PropertiesToLoad = @("name","useraccountcontrol","distinguishedname","operatingsystem","objectclass")
$SearchDomain = "$DomainName/OU=$OU,$DCNotation"
$DomainRoot = New-Object DirectoryServices.DirectoryEntry "LDAP://$SearchDomain"
$Selector = New-Object DirectoryServices.DirectorySearcher
ForEach ($Property in $PropertiesToLoad) 
    {
    $Selector.PropertiesToLoad.Add($Property)
    }
$Selector.SearchRoot = $DomainRoot
$Selector.SearchScope = "SubTree"
[array]$Disabled = $Selector.FindAll() | Where-Object {($_.Properties.objectclass -Match ("Computer")) -And ([Convert]::ToString($($_.Properties.useraccountcontrol),2) -bAnd 0x2)}



Let's break it up into parts

PowerShell:
1
2
$DomainName = "www.tweakers.net"
$OU = "TweakBlogs"


In this simplified version we have our domain and a single OU. Both are put inside of a variable so they can be changed easily. You could also hard-code them, of course, but why would you?


PowerShell:
1
$DCNotation = "DC=" + $DomainName.Replace(".",",DC=")


Next, the notation of the domain name is changed in a way that is easier to understand in the world of LDAP (also, I had other reasons for doing it like this in the script I cut this from).


PowerShell:
1
$PropertiesToLoad = @("name","useraccountcontrol","distinguishedname","operatingsystem","objectclass")


By default a query takes all common properties of an object in AD. In a small domain this doesn't really matter, but in a big domain (here: 25 000 computers) it can make quite a difference in performance to grab only the properties you need. For practical reasons, I fill an array with the properties I will need. This will be easier to add or remove properties later than, again, hardcoding them.


PowerShell:
1
$SearchDomain = "$DomainName/OU=$OU,$DCNotation"


We define where exactly we will execute our query in the AD hierarchy and explicitly declare which DC needs to be used. The latter part can be left away in most circumstances, but in a multidomain forest without trusts (again: like my environment) this is a must.


PowerShell:
1
$DomainRoot = New-Object DirectoryServices.DirectoryEntry "LDAP://$SearchDomain"


Here's a new object containing our connection string to the LDAP-database.


PowerShell:
1
2
Get-Credential
$DomainRoot = New-Object DirectoryServices.DirectoryEntry "LDAP://$SearchDomain",$Credentials.Username,$Credentials.GetNetworkCredential().Password


This is an alternative in case you need alternate credentials, whether because your current credentials have insufficient rights, whether because you're querying on a different domain.


PowerShell:
1
$Selector = New-Object DirectoryServices.DirectorySearcher


Next we create our search-object, commonly known as selector.


PowerShell:
1
2
3
4
ForEach ($Property in $PropertiesToLoad) 
    {
    $Selector.PropertiesToLoad.Add($Property)
    }


Here we load our earlier created array of properties into the selector.


PowerShell:
1
$Selector.SearchRoot = $DomainRoot


Where our selector needs to start looking


PowerShell:
1
$Selector.SearchScope = "SubTree"


How deep the selector should look.


PowerShell:
1
[array]$Disabled = $Selector.FindAll() | Where-Object {($_.Properties.objectclass -Match ("Computer")) -And ([Convert]::ToString($($_.Properties.useraccountcontrol),2) -bAnd 0x2)}


Finally, the important bit: with the FindAll-method we look for the earlier defined properties in the given domain/OU and we only return the objects that are a computer - so we don't get other objects back - and on which the second bit from the right is switched on. The latter is what makes the difference between a disabled and an enabled computer.

Two remarks:
  • PoSh usually knows what type a variable is, but here we need to declare the array explicitly because if only 1 object would be returned we don't have an array. This can be annoying if you want to use an array method like .Count. Without explicit declaration, if $Disabled would contain 2 objects a $Disabled.Count would return 2. With 1 object, however, $Disabled.Count wouldn't return anything - because it wouldn't be an array but just a string. That's why it is a good idea to add the [array]-part here.
  • The value returned from userAccountControl is decimal. Trying a binary AND on it directly did not seem to work, so I convert the decimal number to binary. The other side of the bAnd does not seem to care too much about notation though: in stead of 0x2 I could have well used "10".
And a final remark about AD-properties on the whole. Don't ask me why, but the exact properties from AD should be written in lowercase. If you watch in ADSIedit, you'll notice that everywhere camelCase is used. Nevertheless, any script fails if I try it and works perfectly if I use lowercase. For example: Properties.userAccountControl fails, Properties.useraccountcontrol works perfectly. I'll be happy to hear why if anyone knows.
---------------

I hope this post can be helpful for your own AD queries with PowerShell.

Volgende: Taboes in bed deel II: Remmingen van de vrouw - een gastblogpost 02-'11 Taboes in bed deel II: Remmingen van de vrouw - een gastblogpost
Volgende: Taboes in bed deel I: Praten over seks 02-'11 Taboes in bed deel I: Praten over seks

Comments


By Tweakers user Jejking, Friday 25 February 2011 11:57

I don't understand a sikkepit of it but I still like to make you a compliment for your efforts on teh blogs lately! :)

By Tweakers user Qwerty-273, Friday 25 February 2011 12:17

Get-ADComputer -LDAPFilter "(useraccountcontrol=10)" -SearchBase "CN=sub,DC=domeintje,DC=holloooo"

Of welk ldap filter je ook wilt gebruiken :)

[Comment edited on Friday 25 February 2011 12:18]


By Tweakers user YellowOnline, Friday 25 February 2011 12:48

Qwerty-273 wrote on Friday 25 February 2011 @ 12:17:
Get-ADComputer -LDAPFilter "(useraccountcontrol=10)" -SearchBase "CN=sub,DC=domeintje,DC=holloooo"

Of welk ldap filter je ook wilt gebruiken :)
I forgot to mention that I meant to do it without using the AD-cmdlets, e.g. in a situation when the PoSh-services aren't running or installed on the DC. If they do, life is a lot easier of course and you can just use the example you gave.

In order to comment on this post you need to be logged in. Use this link to log in when you are already a registered user. If you don't have an account you can create one here.