Ad Space here

Welcome Guest! To enable all features please Login or Register.



Go to last post Go to first unread
Dustin Higgins  
#1 Posted : Saturday, November 02, 2019 7:33:32 PM(UTC)
Dustin Higgins

Rank: Advanced Member

Groups: Registered
Joined: 7/1/2018(UTC)
Posts: 64
_United States

Thanks: 1 times
Was thanked: 6 time(s) in 6 post(s)

PowerShell, Active Directory, Computer Lists, and a Point

This post is short and to the point.  Over the years I've have often been asked why I don't use the Active Directory module for Windows PowerShell.  I do use that module.  It is great!  Grabbing the last modified date for all GPOs is my absolute fav!

When I am wearing my Windows command line sys admin hat, I typically deal with big lists of servers or workstations.  There is the occasional need for user info such as email addresses and other user properties. While building tools (command line or GUI), I tend to use the ADSI Adapter over the much simpler Active Directory module. My answer typlically has been: "I just don't like having the extra dependency".  That has drawn a lot of shrugs and blank stares (obvious disagreement) over the years.

Today my answer is a little different and much more confident. Having the ADSI capability in my back pocket has allowed me to start working in a new environment and literally hit the ground running on my first day.  Without access to the Active Directory module I was able to break down all of the OUs/Computers by Operating System and quickly come up with a better understanding of my new home.  I honestly felt great about the progress that I made in the first couple days.

Here is an example of the computer query.  It is easily adapted to query users, license servers, etc.  The code is really nothing new.  I just wanted to share my point of why I continue to use it.  I really like adding the OU as a left to right string property.  IE: Group the OU property to quickly see counts of computers in each OU.


Function Get-AdComputerADSI {     Param(         [parameter(Mandatory=$true)]         [String]$Name     )     # Build the searcher     $Search = [adsisearcher]"(&(objectcategory=computer)(name=$Name))"     # Run the search     $Computers = $Search.FindAll()     # Loop through the results     foreach($Computer in $Computers) {         # Create a list for the OU items         $OuList = New-Object System.Collections.ArrayList         # Build the Computer Object and add the info from AD - Convert the time formats.         $ComputerObj = [PSCustomObject]@{             name = $Computer.Properties['name'][0]             adspath = $Computer.Properties['adspath'][0]             OU = ""             useraccountcontrol = $Computer.Properties['useraccountcontrol'][0]             whencreated = $Computer.Properties['whencreated'][0]             whenchanged = $Computer.Properties['whenchanged'][0]             lastlogon = [datetime]::FromFileTime($Computer.Properties['lastlogon'][0])             lastlogontimestamp = [datetime]::FromFileTime($Computer.Properties['lastlogontimestamp'][0])             description = $Computer.Properties['description'][0]             operatingsystem = $Computer.Properties['operatingsystem'][0]             operatingsystemversion = $Computer.Properties['operatingsystemversion'][0]         }         # Grab the OU information         foreach ($i in $ComputerObj.adspath.split(",")) {             [void]$OuList.Add($i.Split("=")[-1])         }         # Reverse it so it reads from left to right         $OuList.Reverse()         # Set the OU property of the Object by combining $Oulist with a ">" separator         $ComputerObj.OU = [String]::Join(">",$OuList.ToArray())         # Return the Object         $ComputerObj     } }

Feedback is welcome and follow Dustin Higgins on Twitter

#PowerShell #ActiveDirectory


Edited by user Sunday, November 03, 2019 1:18:37 AM(UTC)  | Reason: Not specified

Ad Space here
Mike Merola  
#2 Posted : Monday, November 04, 2019 3:22:36 PM(UTC)
Mike Merola

Rank: Administration

Groups: Administrators
Joined: 6/6/2018(UTC)
Posts: 19
_United States

Thanks: 2 times
Was thanked: 1 time(s) in 1 post(s)

I definitely agree with this stance. While I tend to use that module or even other ones personally, when writing scripts, especially those given to others, I try to go as basic/built-in as possible. When I write scripts that are going to be deployed/ran at a machine level, whether it is something deployed through SCCM, or ran via GPO, or a scheduled task I always have to assume that the extra modules are NOT installed.

One such example

At my current job we have SCCM deployed to manage machines and servers. When I started here 2.5 years ago, they were still using the older SCCM 2012 setup. There was also a VBS script deployed via GPO to auto install the SCCM client and perform a client health check that ran when the machine was joined to the domain and on reboot. This place did not have much in-house SCCM knowledge, they were using a system set up by a contractor back in 2013 and had been barely keeping it operational. The VBS script used was a horribly twisted and hacked older version of Jason Sandys well known and shared ConfigMgrStartup.vbs that was originally targeted for SCCM 2007. Now I am not knocking Jason Sandys' actual script. While VBS is not my script language of choice, it is still very well written and laid out and does its job very well. But this monstrosity was a very old version with a lot of very hacked together "tweaks" for the environment, frozen in time, and quite frankly it didn't work very well. ALL of the health checks were broken, it only installed a VERY old version of the client and tanked any machine that was Windows 10 entirely. Part of why I was hired was to revamp or replace the SCCM setup with the latest build. The method that I chose for transition was to build an entirely new SCCM setup (including new regional distribution points) in parallel to the old setup and build it up with everything that was needed and slowly transition clients over. This allowed the least user/production impact while giving us the most time to work on development and issues. I had both sets of SCCM servers publishing to Active Directory at the same time and the controlling divider was the boundaries (since AD Sites & Services was a total mess, I used IP range boundaries).


The plan was to move one or more boundaries out of the old system into the new system and let the clients switch on their own. For various reasons, deploying this through the old SCCM wasn't a good option, but I knew we had a GPO in place that ran an SCCM-centered script on startup, so I decided to rewrite that from scratch in PowerShell. Now SCCM Boundaries are published in Active Directory, that's how the clients can query AD to see what SITE they are assigned to. I wanted to use the same idea to manually switch (or install) the client. Since this was deployed to EVERY workstation in the environment I had to assume that the AD module was not available and chose to use ADSI LDAP queries. The relevant parts of the script are as follows:

#System Management Container in AD
$ADSysMgmtContainer = [ADSI]("LDAP://CN=System Management,CN=System,DC=MyDomain,DC=com")

#Getting a list of all AAA boundaries from Active Directory

$AdSearcher = [adsisearcher]"(&(Name=SMS-AAA-*)(objectClass=mSSMSRoamingBoundaryRange))"

$AdSearcher.SearchRoot = $ADSysMgmtContainer

#Following line allows for more than 1000 results from search, just in case

$AdSearcher.PageSize = 5000

TRY { $AAABoundaries = $AdSearcher.FindAll() } CATCH { $AAABoundaries = $NULL }

#Getting a list of all BBB boundaries from Active Directory

$AdSearcher = [adsisearcher]"(&(Name=SMS-BBB-*)(objectClass=mSSMSRoamingBoundaryRange))"

$AdSearcher.SearchRoot = $ADSysMgmtContainer

#Following line allows for more than 1000 results from search, just in case

$AdSearcher.PageSize = 5000

TRY { $BBBBoundaries = $AdSearcher.FindAll() } CATCH { $BBBBoundaries = $NULL }


Where AAA represents the old SCCM site code and BBB represents the new site code. Also replaced my domain name with MyDomain above.


Once those commands run, the script has a list of all the boundaries defined for those 2 sites in AD and then performs logic based on the results to switch Sites or install & configure the client.


The script was designed to stay in place long term and initial tests went so well that we were able to migrate all 10,000 machines from the old system to the new system in under a week (after the first 300 test machines sat for a week). The ADSI LDAP query method worked wonderously on all machines, Windows 7 with PowerShell 2.0 all the way to current Windows 10 builds.


I'm not trying to hijack this post, so I'm not going to go into any depth of what the script did or how any of it works, but if there is any interest I can put together my own post for that. I just wanted to share my relevant experience with the same mindset as Dustin here.



Rss Feed  Atom Feed
Users browsing this topic
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.