An ASDI interface and Method question.

Ask your Windows PowerShell-related questions, including questions on cmdlet development!
Forum rules
Do not post any licensing information in this forum.

Any code longer than three lines should be added as code using the 'Select Code' dropdown menu or attached as a file.
Locked
User avatar
EBrant
Posts: 99
Joined: Thu Feb 02, 2012 1:51 am

An ASDI interface and Method question.

Post by EBrant » Wed Mar 20, 2013 6:34 am

Hello

Can someone please help me with the following question? In some respects it is a little unusual and quirky. I understand what went wrong and how to fix it, but there is an element I am a bit unclear on (the interesting bit), I have a hunch but not certain, help most appreciated.

I am dealing with Microsoft Active Directory and the [ADSI] DirectoryServices.DirectoryEntry and [ADSISEARCHER] DirectoryServices.DirectorySearcher interfaces to AD.

Now let’s say I have

AD Domain called MyDomain.net

Three OU’s called under MyDoamin (Windows 2003 R2)

OU1
OU2
Groups

Under OU1 there are two users

User1
User2

Under OU2 there are two users

User3
User4

Under the Groups OU there are two three groups

Group1
Group2
Group3

Now let’s say the following users are members of the following groups

Group1 members

User1 under OU1
User2 under OU1
User3 under OU2

Therefore the key here is that User3 under OU2 is a member of Group1 alongside Users1 and 2 from OU1

Now I want to remove any users from Group1 “as long as the user is under OU1 and no other OU”, therefore the result should be that Group1 ends up with just User3.

I realise I can do this with the Quest AD cmdlets, but I want to focus on how I initially did it using [ADSI] why it went wrong and how I resolved it, and therein my question (coming later).

So my initial code was

$OU = "LDAP://OU=OU1,DC=MyDomain,DC=net"
$ObjectType="(&(objectCategory=Person)(objectClass=User))"
$Searcher = [DirectoryServices.DirectorySearcher]""
$Searcher.SearchRoot=$OU
$Searcher.Filter=$ObjectType
$Users = $Searcher.FindAll()

foreach ($user in $Users) {

$GroupNames = ($user.PSBase.Properties.memberof | where {$_ -match "CN=Group1"})

foreach ($GroupName in $GroupNames) {

if ($user.PSBase.Properties.memberof | where {$_ -match "CN=Group1"})

{
$GroupObj=[ADSI]"LDAP://$GroupName"
$GroupObj.Remove("LDAP://$user")

}
}
}

First off, if you look at the result of $Users
It returns only the users accounts under OU1 as designed, i.e. it returns a collection of object of type System.DirectoryServices.SearchResult which I know are cut down objects and if you want the actual object you need to use the GetDirectoryEntry() method, but I will come to this later.

If I run the above script the result will be that all users (including User3 under OU2) will be removed from the group, and not just the collection of users I am working on in the foreach loop i.e. foreach ($user in $Users) as we know $Users only contains search results from users under OU1, so I am thinking why did it process a user (user3) under OU2 when it was not even in the collection of $Users.

So I am thinking perhaps the following line

$GroupObj.Remove("LDAP://$user")

i.e. the $User part is acting on the Class ‘user’ rather than the particular instance of the class e.g. the user in question under OU1 which is why it removed all users. However I do not see why as the LDAP://$user should pick out and attach to a particular instance as each instance of $user represents a specific user in my collection

So then I take a closer look at $user it returns the following to the console


Path
----
LDAP://CN=User1,OU=OU1,DC=mydomain,DC=net

Properties
----------
{blah blah }

Path
----
LDAP://CN=User2,OU=OU1,DC=mydomain,DC=net

Properties
----------
{blah blah }

That looks OK, again no mention at all of User3
So then I do the following
$OU = "LDAP://OU=Users,OU=OU1,DC=MyDomain,DC=net"
$ObjectType="(&(objectCategory=Person)(objectClass=User))"
$Searcher = [DirectoryServices.DirectorySearcher]""
$Searcher.SearchRoot=$OU
$Searcher.Filter=$ObjectType
$Users = $Searcher.FindAll()

foreach ($user in $Users) {

$GroupNames = ($user.PSBase.Properties.memberof | where {$_ -match "CN=Group1"})

foreach ($GroupName in $GroupNames) {

if ($user.PSBase.Properties.memberof | where {$_ -match "CN=Group1"})

{
$UserObj = $user | % {$_.GetDirectoryEntry()} | % {$_.distinguishedName
}


$GroupObj=[ADSI]"LDAP://$GroupName"
$GroupObj.Remove("LDAP://$UserObj")

}
}
}

This time the script works as expected and only removes Users under OU1 (User1 and User1) from Group1 and not users under OU2 (User3).

I realise with the second example I am getting the actual user object (as opposed to a cut down searcher result) from AD with the GetDirectoryEntry() method, but I did not think this was necessary; as event before invoking the GetDirectoryEntry() method each $user only represents one user and always under OU1 (event if is only a System.DirectoryServices.SearchResult object)
Perhaps without using GetDirectoryEntry() the following might work

$GroupObj.Remove("$($User.path)")

Not tested that one yet.

Any advice on the mechanics of the above and how/why in the first example all users are remove from the group would be most welcome.

Thanks in advance
Ernie

User avatar
jvierra
Posts: 13798
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: An ASDI interface and Method question.

Post by jvierra » Wed Mar 20, 2013 7:33 am

Can you please just state what it is you are trying to do. The long explanation is tedious and at no point does it state what the problem is.

My guess is that you want to remove items from a group based on the parent OU. Use the 'parent' property to determine which objects to remove - match parent distinguishedName to the desired container membership. This is not difficult at all which is why ther is a 'parent' property.
Last edited by jvierra on Wed Mar 20, 2013 7:53 am, edited 1 time in total.

User avatar
jvierra
Posts: 13798
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: An ASDI interface and Method question.

Post by jvierra » Wed Mar 20, 2013 7:36 am

Ernie -

Just a hint but your technical guess is not correct. ADSI does not and cannot work that way.

'Remove' works on an object path only. It does NOT work on a class. Thjis means that your logic has a flaw right from the start.

User avatar
EBrant
Posts: 99
Joined: Thu Feb 02, 2012 1:51 am

Re: An ASDI interface and Method question.

Post by EBrant » Wed Mar 20, 2013 10:18 am

Hello Jim

Thanks very much for taking the time to reply, I will check out the parent property, and thanks for the clarification on the class

Sorry for the long explaintion, thought it would help :)

What I cannot explain is this bit

if ($user.PSBase.Properties.memberof | where {$_ -match "CN=Group1"})

{
$GroupObj=[ADSI]"LDAP://$GroupName"
$GroupObj.Remove("LDAP://$user")
}

Removes ALL users from the group, even the Users not in the collection (as described above)

I just do not get it, I would have thought either the code would fail completely, or remove the users in the collection. I cannot see a locical reason why if removed All users.

I would be grateful if you could give me a possible reason for this.

Thank you again
Ernie

User avatar
jvierra
Posts: 13798
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: An ASDI interface and Method question.

Post by jvierra » Wed Mar 20, 2013 10:48 am

Think about it. Put in trace statements. The 'if' is always true because of the construct. It is just a poor use of logic.

Run this t see what it returns:
$user.PSBase.Properties.memberof | where {$_ -match "CN=Group1"}

Locked