Case of the Slow PowerShell Logon Script

A logon script was taking 30-40 seconds to run at logon.

$domain = "contoso" $Searcher = New-Object System.DirectoryServices.DirectorySearcher("domain",$domain) $Searcher.Filter = ("(sAMAccountName=$env:username)") $user=$searcher.FindOne() $ugroups=$User.GetDirectoryEntry().memberOf $ugroups -split '=,' | ForEach-Object { $Searcher.Filter = ("(distinguishedName=$_)") $groups=$searcher.FineOne() $nested=$groups.GetDirectoryEntry().memberOf if($nested -like "CN=*") { $ugroups = $ugroups + " " + $nested } } $ugroups -split '=,' | ForEach-Object { if ($_ -Like "CN=Group_Prefix_*") { $Searcher.Filter = ("(distinguishedName=$_)") $fgroups=$searcher.FindAll() ForEach ($group in $fgroups) { $info=$group.GetDirectoryEntry().info $url=$group.GetDirectoryEntry().url $att3=$group.GetDirectoryEntry().ExtensionAttribute3 $att4=$group.GetDirectoryEntry().ExtensionAttribute4 $att5=$group.GetDirectoryEntry().ExtensionAttribute5 # do something with the values } } }

 

Some flaws in this script:

  • Searching Active Directory to find current user’s group membership, this is unnecessary.
  • Once Distinguished Names (DNs) for Groups were obtained, instead of opening them directly, they are split up to find the Group Name, and AD is searched again, for every single group the user is a member of.

The optimised script ran in under 1 second. This performs 0 Active Directory searches, and so avoids the potentially expensive operation. It takes advantage of the fact all groups are in a known OU, so they can be opened directly using New-Object System.DirectoryServices.DirectoryEntry.

In the updated script we retrieve user’s groups from System.Security.Principal.WindowsPrincipal for the logged on user, this is almost instant. In addition one we have the Group Distinguished name, in format LDAP://CN=Name,OU=Some OU,DC=domain,DC=COM” we access the object directly with  $de = New-Object System.DirectoryServices.DirectoryEntry($groupDN)

This updated script does have some limitation:

  • The group membership will only update at user logon – which is OK because this script only runs at logon
  • If you are going to use special characters like commas in your group names, the script will need to be updated with necessary escape codes for DN format
#groups will always be in this OU $groupOU = "OU=Groups,DC=contoso,DC=local" $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent() $WindowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal($currentUser) $groups = $WindowsPrincipal.Identity.Groups| Foreach-Object { $_.Translate([Security.Principal.NTAccount]) } ForEach ($group in $groups) { if ($group.Value.Contains("\")) { $domain = $group.Value.Split("\")[0] $groupName = $group.Value.Split("\")[1] if ($groupName.StartsWith("Group_Prefix")) { $groupDN = [string]::Format("LDAP://CN={0},{1}",$groupName,$groupOu) $de = New-Object System.DirectoryServices.DirectoryEntry($groupDN) $info=$de.info $url=$de.url $att3=$de.ExtensionAttribute3 $att4=$de.ExtensionAttribute4 $att5=$de.ExtensionAttribute5 # do something with the values } } }

About chentiangemalc

specializes in end-user computing technologies. disclaimer 1) use at your own risk. test any solution in your environment. if you do not understand the impact/consequences of what you're doing please stop, and ask advice from somebody who does. 2) views are my own at the time of posting and do not necessarily represent my current view or the view of my employer and family members/relatives. 3) over the years Microsoft/Citrix/VMWare have given me a few free shirts, pens, paper notebooks/etc. despite these gifts i will try to remain unbiased.
This entry was posted in Performance, PowerShell, Scripting and tagged . Bookmark the permalink.

Leave a comment