Groups and their Membership

I’m working with a new client and this client is a “wholly owned subsidiary” of another company and is getting ready to be spun off as their own company.

One of the things we are doing is configuring their network to be standalone and not part of the corporate Active Directory. That involves creating users, group, moving computers, installing a new Exchange organization, etc. etc.

But sometimes it’s the simple things that bite you…

We’d gotten an export of the OU for the subsidiary out of corporate in a CSV file – every object, every attribute. That was a mess in-and-of itself, but at least all the data was there.

So we started importing the data. And it broke.

Huh? We’d done this before and no problem. What was going on here?

Like I said – the simple things…

This set of users and groups were using embedded groups. Sometimes up to five levels deep. (That is, a group within a group within a group within a group within a group.)

Well, you can’t assign a group membership to a group that doesn’t exist! Seems obvious, in retrospect.

The solution? Go through and harvest all groups FIRST – just create the groups in Active Directory. Then, go back through and assign membership to the groups.

Here is a somewhat sanitized version of the code we used. On the first pass, it creates groups. On the second pass, it will assign membership to the groups. This shows a number of PowerShell techniques in group management – including creating groups, checking for membership in a group, and adding members to a group.

Also note: there are several places where this script could be optimized. But I was more interested in clarity (since, as a consultant, I won’t be there forever!).

I hope you find this useful for your scripts!

set-variable ADS_GROUP_TYPE_DOMAIN_LOCALGROUP 1          
set-variable ADS_GROUP_TYPE_GLOBAL_GROUP      2          
set-variable ADS_GROUP_TYPE_LOCAL_GROUP       4          
set-variable ADS_GROUP_TYPE_UNIVERSAL_GROUP   8          
set-variable ADS_GROUP_TYPE_SECURITY_ENABLED -2147483648 

$groupType = $ADS_GROUP_TYPE_GLOBAL_GROUP –bor $ADS_GROUP_TYPE_SECURITY_ENABLED

$csv = import-csv GroupsOnly.csv
#
# Each line contains the dn, the name, the description if one exists, and the member list
# with the dn of each group member. The member list has each member separated by a semi-colon.
#
foreach ($line in $csv)
{
	$dn = $line.dn
	$adsPath = "LDAP://" + $dn
	$objGroup = [ADSI]$adsPath
	if ($objGroup.Name)
	{
		# the above verifies that the group exists. Note that 
		# ADSI is "smart". It doesn't actually access AD and 
		# fill the property cache until you read an attribute
		# from the object.
		"Exists: " + $line.Name
		if ($line.member.Length -gt 0)
		{
			$i = 0
			$members = $line.member.Split(";")
			$lower   = $dn.ToLower()
			foreach ($member in $members)
			{
				$objMember = [ADSI]("LDAP://" + $member)
				if ($objMember.Name)
				{
					# check to see if member is already in group
					[bool]$already = $false
					$userGroups = $objMember.memberOf.Value
					if ($userGroups)
					{
						foreach ($usergroup in $userGroups)
						{
							if ($userGroup.ToLower() -eq $lower)
							{
								$already = $true
								break
							}
						}
					}
					$objMember = $null
					if ($already)
					{
						"`tAlready in group: $member"
					}
					else
					{
						$objGroup.Add(("LDAP://" + $member))
						if ($?)
						{
							"`tAdded to group: $member"
							$i++
						}
						else
						{
							# should be an error displayed on the PowerShell window
							"`tCouldn't add to group: $member"
						}
					}
				}
				else
				{
					# member specified in CSV doesn't exist in Active Directory
					"`tObject doesn't exist: $member"
				}
			}
			"`tadded $i members"
		}
		else
		{
			# members element of the CSV was empty
			"`tno members specified for group"
		}
		$objGroup = $null
	}
	else 
	{
		"Creating " + $line.Name

		$parent = "LDAP://" + $dn.SubString(1 + $dn.IndexOf(","))
		$object = [ADSI]$parent
		$child  = $object.Create("group", "CN=" + $line.Name)

		$child.Put("groupType",      $groupType)
		$child.Put("sAMAccountName", $line.name)
		$child.Put("name",           $line.name)

		if ($line.description.length -gt 0)
		{
			$child.Put("description",    $line.description)
		}

		$child.SetInfo()
		$child = $null

	}

	sleep -milli 500
}

$csv = $null

Until next time…

As always, if there are items you would like me to talk about, please drop me a line and let me know!


Follow me on twitter: @EssentialExch

I Have a Logo!

In case anyone besides me is counting, I’ve been at this (consulting, writing, speaking, etc.) for 10 months now. I’m having a blast. I wish I’d done this years ago.

Two months ago I took on a partner and we formed Smith Consulting d/b/a The Essential Exchange as a legal entity (as opposed to the sole proprietership I was previously).

After several weeks of back-and-forth, we finally came up with a logo today, and I’m very pleased with it. It will replace the generic icons you see on this blog fairly soon (image manipulation is not my strong suit). Here it is:


Follow me on twitter: @EssentialExch