Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Read "userAccountControl" property to determine if account is disabled?

89 views
Skip to first unread message

psmit...@gmail.com

unread,
Dec 21, 2007, 12:06:19 PM12/21/07
to
I have a PS script that retrieves an object's LastLogonTimeStamp and
it's working fine (thanks to some VBS code from the MS ScriptCenter
and a post by Shay in a previous thread on calling the VBS code from
inside a PS function).

I'd like to also check the status of the account and add some
indicator in the output if the account is disabled. From some basic
Google-fu, I've found that the disabled flag is stored in the second
bit of the userAccountControl property. I've found plenty of examples
how to add a binary "and" to the LDAP query string to return a list of
enabled/disabled accounts, but I can't find anything on how to
programmatically read the second bit directly once a user object has
been obtained.

My script takes a list of names, loops through them and uses the
DirectorySearcher to retrieve the object with this query:
(&(objectCategory=User)(sAMAccountName=$name))

I then use the FindOne() method of the DirectorySearcher to get the
specific object. Once I have the object is there a way to just read
the second bit of the userAccountControl property or do I need to
execute another search and add that filter to the query? Something
like this:
(&(objectCategory=User)(sAMAccountName=$name)(userAccountControl:
1.2.840.113556.1.4.803:=2))

If that query returns a result then the account is disabled, but it
seems like a second AD query is a waste of time if that attribute can
be read directly.

The script is below, if it helps to see it in context. I removed the
"DisplayUsage" function from the listing to save space so ignore the
call to it.

Thanks,
Paul


param (
[string[]] $names,
[string] $inputFile
)

function Get-LastLogonTimeStamp($ldapPath) {
$vbsCode = @"
On Error Resume Next
Set objUser = getObject(`"$ldapPath`")
Set objLastLogon = objUser.Get("lastLogonTimeStamp")
intLastLogonTime = objLastLogon.HighPart * (2^32) +
objLastLogon.LowPart
intLastLogonTime = intLastLogonTime / (60 * 10000000)
intLastLogonTime = intLastLogonTime / 1440
result = intLastLogonTime + `#1/1/1601`#
"@

$vbs = new-object -com MSScriptControl.ScriptControl
$vbs.language = 'vbscript'
$vbs.ExecuteStatement($vbsCode)
$lastLogonTimeStamp = $vbs.Eval("result")
if ($lastLogonTimeStamp -eq "1/1/1601 12:00:00 AM") {
"Never"
}
else {
$lastLogonTimeStamp
}
}

if ($args -or (!$names -and !$inputFile)) {
DisplayUsage
exit
}

if ($inputFile) {
$names += get-content $inputFile
}

if ($names) {
$results = @{}
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
foreach ($name in $names) {
$blnUser = $true
$strFilter = "(&(objectCategory=User)(sAMAccountName=$name))"
$objSearcher.Filter = $strFilter
$objSearchResult = $objSearcher.FindOne()
if (!$objSearchResult) {
$blnUser = $false
$strFilter = "(&(objectCategory=Computer)(Name=$name))"
$objSearcher.Filter = $strFilter
$objSearchResult = $objSearcher.FindOne()
if (!$objSearchResult) {
$displayName = $name.ToUpper()
if (!$results.ContainsKey($displayName)) {
$results += @{$displayName = "Object Not Found"}
}
}
}
if ($objSearchResult) {
$object = $objSearchResult.GetDirectoryEntry()
if ($blnUser) {
$displayName = ([string]$object.sAMAccountName).ToUpper()
}
else {
$displayName = ([string]$object.Name).ToUpper()
}
$lastLogon = Get-LastLogonTimeStamp($objSearchResult.Path)
if (!$results.ContainsKey($displayName)) {
$results += @{$displayName = $lastlogon}
}
}
}
}

if ($results) {
$results.GetEnumerator()
}

Bob Butler

unread,
Dec 21, 2007, 12:11:19 PM12/21/07
to
<psmit...@gmail.com> wrote in message
news:05c46ff3-6535-42d3...@l32g2000hse.googlegroups.com...

>I have a PS script that retrieves an object's LastLogonTimeStamp and
> it's working fine (thanks to some VBS code from the MS ScriptCenter
> and a post by Shay in a previous thread on calling the VBS code from
> inside a PS function).
>
> I'd like to also check the status of the account and add some
> indicator in the output if the account is disabled. From some basic
> Google-fu, I've found that the disabled flag is stored in the second
> bit of the userAccountControl property.

if (flag -band 2) {
disabled
}
else {
enabled
}

Brandon Shell [MVP]

unread,
Dec 21, 2007, 12:29:31 PM12/21/07
to
A couple of things...

* This would be WAY eaiser using the FREE CMDlets from Quest
http://www.quest.com/activeroles-server/arms.aspx
* I would use DirectorySearcher for the LastLogonTimeStamp look here for
more info
http://bsonposh.com/modules/wordpress/?p=30
* There is a hidden GetEx() method you can use to get 'AccountDisabled'.
Returns $true or $false
Example: $user.psbase.invokeget('AccountDisabled')

Brandon Shell
---------------
Blog: http://www.bsonposh.com/
PSH Scripts Project: www.codeplex.com/psobject

psmit...@gmail.com

unread,
Dec 21, 2007, 1:38:40 PM12/21/07
to
On Dec 21, 11:29 am, Brandon Shell [MVP] <a_bshell.m...@hotmail.com>
wrote:

> A couple of things...
>
> * This would be WAY eaiser using the FREE CMDlets from Quest
>    http://www.quest.com/activeroles-server/arms.aspx
> * I would use DirectorySearcher for the LastLogonTimeStamp look here for
> more info
>    http://bsonposh.com/modules/wordpress/?p=30
> * There is a hidden GetEx() method you can use to get 'AccountDisabled'.
> Returns $true or $false
>    Example: $user.psbase.invokeget('AccountDisabled')
>
> Brandon Shell
> ---------------
> Blog:http://www.bsonposh.com/
> PSH Scripts Project:  www.codeplex.com/psobject
>

Yes, I know the Quest tools would simplify this. The script started
as an exercise to get to know the DirectoryServices class a little
better, before it morphed into something I thought would actually be
useful. Then it became one of those "I know I can do this"
things. :)

The hidden GetEx() method works like a charm. Obviously I don't see
it in the get-member list for a DirectoryEntry object. How would I go
about finding hidden methods?

Also, thanks to Bob for his -band solution. It looks like that also
works, although I got a type convesion error when trying to use the
userAccountControl attribute directly:

PS C:\> $object.useraccountcontrol -band 2
Cannot convert "System.DirectoryServices.PropertyValueCollection" to
"System.Int32".
At line:1 char:33
+ $object.useraccountcontrol -band <<<< 2

I noticed if I converted it to a string first, the type conversion
would work:

PS C:\> $object.useraccountcontrol.tostring() -band 2
0

Thanks for both solutions. Binary arithmetic is still a bit over my
head. :)

Thanks,
Paul

Brandon Shell [MVP]

unread,
Dec 21, 2007, 1:45:27 PM12/21/07
to
I don't know of an authorative list of these "hidden" methods\properties.
I just know them from past experience.

Bob Butler

unread,
Dec 21, 2007, 2:52:58 PM12/21/07
to
<psmit...@gmail.com> wrote in message
news:1782989f-6e27-4efe...@t1g2000pra.googlegroups.com...

On Dec 21, 11:29 am, Brandon Shell [MVP] <a_bshell.m...@hotmail.com>
wrote:
>PS C:\> $object.useraccountcontrol -band 2
> Cannot convert "System.DirectoryServices.PropertyValueCollection" to
>"System.Int32".


$object.userAccountControl.Value -band 2

0 new messages