Funny PSCustomObject behavior (ADSI)

Hi All,
I finished making this custom object with data pulled from ADSI, but it’s acting kind of funny.

$EnabledADComputers = New-Object system.Data.DataTable
$EnabledADComputers.Columns.Add('Computer')  | Out-Null
$EnabledADComputers.Columns.Add('OSName')    | Out-Null
$EnabledADComputers.Columns.Add('AD_OU')     | Out-Null
$EnabledADComputers.Columns.Add('LastLogon') | Out-Null
$EnabledADComputers.Columns.Add('ADCreated') | Out-Null

$adsi = $null
$adsi = [adsisearcher]"objectcategory=computer"
# To return only the enabled computer objects, use '!userAccountControl:1.2.840.113556.1.4.803:=2'
$adsi.filter = "(&(objectClass=Computer)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
$EnabledADComputersADSI = $adsi.FindAll()

Foreach ($E in $EnabledADComputersADSI){

        [string](($ -replace '^CN=[\w\d-_]+,\w\w=','' -replace ',OU=','/' -replace ',DC=.*'),
    ) | Out-Null

Write-Host "$($EnabledADComputers.count) objects returned from ADSI search."
Write-Host "$($ objects returned from ADSI search."

The last two lines, I’d expect both of them to return the same count, but only the second one returns anything, the first one nothing (not even 0 or 1). I want to make sure this object is solid before continuing with this script. I’ll be using it to do a network device audit at work.

You are not creating a PSObject, it’s a DataTable. A datatable doesn’t have a count property, it’s:


Additionally, you should take a look at this article to only get the properties from ADSI that you need as well as some other options for getting data from ADSI. If you do want to have a PSObject, you can do something like this:

$adsi = $null
$adsi = [adsisearcher]"objectcategory=computer"
# To return only the enabled computer objects, use '!userAccountControl:1.2.840.113556.1.4.803:=2'
$adsi.filter = "(&(objectClass=Computer)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
$EnabledADComputersADSI = $adsi.FindAll()

$EnabledADComputers = Foreach ($E in $EnabledADComputersADSI){
   $obj = $E.Properties
    $props = @{
        Computer  = [string]$
        OSName    = [string]$obj.operatingsystem
        AD_OU     = [string](($obj.distinguishedname) -replace '^CN=[\w\d-_]+,\w\w=','' -replace ',OU=','/' -replace ',DC=.*')
        LastLogon = (([datetime]::FromFileTime([string]$obj.lastlogon)).ToShortDateString())
        ADCreated = ($obj.whencreated).ToShortDateString()

    New-Object -TypeName PSObject -Property $props


Insightful, thank you. I don’t think I actually need a PS object for this info, now that I think about it. I appreciate the link for the ADSI queries. Do you have any good resources for different objects I can use in PS? …how to find the appropriate one for any given situation. I’ve never fully grasped the .Net landscape.

Typically, Powershell uses a PSObject, which is an array of hashtables. There are many ways to generate PSObjects, however, there are considerations such as the version of Powershell that the code will be executing on. I typically write almost all of my code to be compatible with Powershell V2 just because I’ve been burned by clients not being able to update systems.

Most cmdlets in Powershell produce a PSObject or accept input from the pipeline utilizing a PSObject. Additionally, most of your analytics such as basic .Count or Group-Object, Measure-Object, etc. also consume a PSObject.

There are certain situations when you would use hash tables, data tables or other objects to hold data, but if you are trying to learn Powershell you should stick to using the PSObject so that you can pass information to cmdlets in Powershell and leverage the pipeline. Also, is there a reason you are using ADSI versus Get-ADComputer (installed with RSAT tools - ActiveDirectory Powershell Module) or even 3rd party modules like Quest (Dell) Active Directory module (Get-QADComputer). If you are only doing reporting from your workstation, it will much easier and efficient to leverage those methods to query AD as they return a PSObject.

I’m with you on V2 scripts, I try to do that as well. I manage a lot of different environments, which makes writing scripts that work universally very challenging. Which leads to your next question…

I am using ADSI because the script I am running won’t always be on a DC, or I can’t guarantee that RSAT will be loaded on the target devices. The script will be used to audit compliance for access using my company’s MSP platform: tests for ping, GPO, admin$, winrm, WUA version, state of cache folders, etc. It will be run on a given device (it could even be a domain workstation) the client chooses for us to use as a monitoring hub (point of ‘attack’ for our team to get into the network using automation).

Only reporting at first, but I’ll also incorporate an agent deployment function for devices found to be un-managed or out-dated. This would download an exe and deploy, most likely using psexec. I have this deploy function working already, looking to start from scratch with better reporting (with email including excel and html attachments) as the priority.