views:

256

answers:

2

So I pieced together this little script to dump all users in Active Directory into all the groups that they belong to. This part works great. But as in most organizations, the people in the sales force move around.

Now lets say I have 5 groups with one user in each group

  • Region 1 (Mark)
  • Region 2 (John)
  • Region 3 (Matt)
  • Region 4 (Liz)
  • Region 5 (Lucy)

Now I have these users in their respective group but lets say we have two people move regions. So lets say Mark and Liz switch. Now I run my script and now the groups look like this.

  • Region 1 (Mark, Liz)
  • Region 2 (John)
  • Region 3 (Matt)
  • Region 4 (Liz, Mark)
  • Region 5 (Lucy)

Now my script detected that the users have switched positions but has no way to detect that Mark is no longer in Region 1 and Liz is no longer in Region 2.

How can I check to see if a user is not supposed to be in a group and then delete it.

# Lets start with a clean slate :)
Clear

# Lets reference the assembly / GAC that we need for this
#region
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$SPSite = New-Object Microsoft.SharePoint.SPSite("https://extranet.something.com")
$OpenWeb = $SpSite.OpenWeb("/") 
#endregion

# Add some eye candy :)
#region
write-host "    _    ____       ____                   " -foregroundcolor Magenta
write-host "   / \  |  _ \     / ___| _   _ _ __   ___ " -foregroundcolor Magenta
write-host "  / _ \ | | | |____\___ \| | | | '_ \ / __|" -foregroundcolor Magenta
write-host " / ___ \| |_| |_____|__) | |_| | | | | (__ " -foregroundcolor Magenta
write-host "|_/   \_\____/     |____/ \__, |_| |_|\___|" -foregroundcolor Magenta
write-host "                          |___/            " -foregroundcolor Magenta
Write-Host "    Version 2.0" -foregroundcolor Red
Write-Host "    Build 2009 09-10 21:30" -foregroundcolor Red
Write-host "    Created by Mitchell J. Skurnik" -foregroundcolor Red
#endregion

# Create the stopwatch
#region
[System.Diagnostics.Stopwatch] $sw;
$sw = New-Object System.Diagnostics.StopWatch
$sw.Stop()
$sw.Start()
#endregion

# Function to control Adding groups
function creategroup
{
    param ([string] $siteurl = "https://extranet.something.com")
    $site = New-Object Microsoft.SharePoint.SPSite($siteurl)
    $web = $site.RootWeb;
    $group = $currentgroup;
    $perm = "Read";
    $owner = "jdoe";
    if ($owner -eq "") { $owner = $web.CurrentUser.LoginName }

    $exists = $web.SiteGroups | where { $_.Name -eq $group }
    if ($exists -eq $null)
    {
     # Create group
     $web.SiteGroups.Add($group, $web.EnsureUser($owner), $null, "");
     # Give permissions to the group
     $assign = New-Object Microsoft.SharePoint.SPRoleAssignment($web.SiteGroups[$group]);
     $assign.RoleDefinitionBindings.Add($web.RoleDefinitions[$perm])
     $web.RoleAssignments.Add($assign)
     Write-Host -ForegroundColor green "Creating sharepoint group - " $currentgroup;
    } 
    $site.Dispose();
}

# Function to add users to the specified group
function addUser
{
    # Open a connection to the sharepoint  site and then select the sub site you want
    $themail = $prop.mail
    $thedisplay = $prop.displayname

    if ($themail -eq "")
    {
     $themail = "[email protected]"
    }
    if ($thedisplay -eq "")
    {
     $thedisplay = "Account, Test"
    }
    if ($themail -eq $null)
    {
     $themail = "[email protected]"
    }
    if ($thedisplay -eq $null)
    {
     $thedisplay = "Account, Test"
    }
    $TheNewGroup = $OpenWeb.SiteGroups | Where-Object {$_.Name -match $currentGroup}
    $TheNewGroup.AddUser("garr\" + $prop.samaccountname,$themail,$prop.displayname,"")
    #write-host "Added: " $thedisplay -foregroundcolor Red
}

# Function to remove people - be careful using this script :(
function removeUser
{
    #Might be able to pull this value from before...let's give it a try
    #$TheNewGroup = $OpenWeb.SiteGroups | Where-Object {$_.Name -match $currentGroup}
    #$TheNewGroup.AddUser("garr\" + $prop.samaccountname,$themail,$prop.displayname,"")
    $TheNewGroup.Remove("garr\" + $prop.samaccountname)
}

# Now onto the real stuff
Write-host "Searching for Groups" -foregroundcolor Green

# Clear out the existing text file so we have a clean slate
$file = New-Item -type file "C:\Powershell\allGroups.txt" -Force

# Execute the Group Dump Script
C:\Powershell\test.ps1 | Out-File -filepath "C:\PowerShell\allGroups.txt" -append

# Clean up the list by removing duplicates and sorting everything
$TextFile = $TextFile = "C:\Powershell\allGroups.txt" 
$NewTextFile = "C:\Powershell\allGroups - Sorted.txt"
GC $TextFile | Sort | GU > $NewTextFile

# Use LDAP to connect to Active Directory
#region
$Dom = 'LDAP://OU=yeah,OU=Users,OU=sdfsdfsdf,DC=something,DC=com'
$Root = New-Object DirectoryServices.DirectoryEntry $Dom 
#endregion

# Create a selector and start searching from the Root of AD
#region
$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = $root 
#endregion

# Integer to compare file length
$c=0

# Get the Group text file's length and write to scree and variable
$fileLength = [System.IO.File]::ReadAllText($NewTextFile).Split("`n").Count
Write-Host "Found " $fileLength "Groups in Active Directory" -foregroundcolor Magenta

# Integer for thumbing through 'memberOf' in active directory
$d = 0

# Integer for the amount of of users found
$f = 0

# Start a while loop where we read through the entire groups text file
while ($c -le $fileLength)
{
    # Increment the line number for the next pass through
    $c++

    # Grab the first line of text from the groups file (Really the 0th line) and then tell the user
    $currentGroup = (Get-Content $NewTextFile)[$c]

    # Create the group
    CreateGroup
    #Write-Host "Created Group: " $currentGroup -foregroundcolor Red

    #
    Write-host $c "/" $fileLength "`t" $currentGroup -foregroundcolor Red

    # Query Active directory and force some commands
    $adobj= $selector.findall() | where {$_.properties.objectcategory -match "CN=Person"} 
    foreach ($person in $adobj)
    { 
     # Variable for the different properties to reduce fatigue
     $prop=$person.properties

     # The Department
     $department = $prop.department

     # Sir Name
     $sn = $prop.sn

     # Given Name
     $gn = $prop.givenname

     $un = $prop.samaccountname

     # Assign the really long memberof to a variable
     $memberof = $person.properties["memberof"]

     # Length of memberof
     $memberofcount = $test.Count


     # Loop for each group the member is in
     while ($d -le $memberof.Count)
     {
      $blah = ForEach-Object{`
       $memberof[$d]`
       -replace "CN=",""`
       -replace ",OU=San Diego Office",""`
       -replace ",DC=something",""`
       -replace ",DC=com","" `
       -replace ",OU=LA Office","" 
      }
      # Incriment the d
      $d++

      # Is that user in the group?
      if ($blah -eq $currentGroup)
      {
       # Hey look we found somebody in that group :)
       Write-host "`t`t`t" $un -foregroundcolor Magenta
       addUser
       $f++
      }
      #elseif ($blah -ne $currentGroup)
      #{
      # removeUser
      #}

      else
      {
       # Oh noes...nobody is in that group...that is strange
      }
     }


     # Are we at the end of what the user has
     if ($d -ge $memberofs.Count)
     {
      # Looks like we are :)
      $d=0
     }
    }

    # Display amount of users found
    #Write-Host "`t`t`t" $f " user(s) found"
    $f = 0
}

# Stop Watch
$sw.Stop()

# Write the compact output to the screen
write-host "Updated in Time: ", $sw.Elapsed.ToString()


#This space is saved for future development
+1  A: 

To turn on auditing of security changes, go to the Site Settings page of the site collection root and select Editing users and permissions. Once auditing data has been collected you should see the reports under Audit log reports working.

You can then use SPAuditQuery to retrieve the data using the object model. See the SPAuditEventType enumeration to see what types of events you can filter on. Looks like you would need SecGroupMemberAdd and SecGroupMemberDel.

Finally, change group membership by using the SPRoleAssignment and SPRoleDefinition classes. This and this blog post should tell you everything you need to know about using these.

Alex Angas
Not sure how I could do that in powershell
Mitchell Skurnik
Your script already demonstrates use of .NET objects, and SPAuditQuery/SPRoleAssignment/etc... are just more of these. What in particular are you not sure of?
Alex Angas
I just see examples of C# and vb.net on those pages. Should the syntax be the same?
Mitchell Skurnik
The syntax is the only thing that won't be the same, everything else applies. If you need examples, there's a lot on CodePlex: http://www.codeplex.com/site/search?ProjectSearchText=SharePoint%20PowerShell You might also want to ask a question on this site about porting from C#/VB.NET to PowerShell. If I knew more PowerShell, I'd give you code samples! ;-)
Alex Angas
I already know how to delete the user but the above does not help me in determining if a user assigned to a variable lets say $user = "jdoe" is in a certain group. If I can do that, I think I can delete from a group using $TheNewGroup.Remove("garr\jdoe")
Mitchell Skurnik
SPRoleAssignmentCollection should tell you that. However it might be simpler to use SPGroup.Users http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spgroup.users.aspx or SPUser.Groups http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spuser.groups.aspx (again sorry I don't have PowerShell but see what you can find on CodePlex)
Alex Angas
Well I almost got it. The logic came to me in my sleep. I will post the answer later...thank you for your help
Mitchell Skurnik
A: 

This is not clean or anything but it works :)

Clear

#region Reference the assembly / GAC that we need for this
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$SPSite = New-Object Microsoft.SharePoint.SPSite("https://extranet.something.com")
$OpenWeb = $SpSite.OpenWeb("/") 
#endregion

#region Add some eye candy :)
write-host "    _    ____       ____                   " -foregroundcolor Magenta
write-host "   / \  |  _ \     / ___| _   _ _ __   ___ " -foregroundcolor Magenta
write-host "  / _ \ | | | |____\___ \| | | | '_ \ / __|" -foregroundcolor Magenta
write-host " / ___ \| |_| |_____|__) | |_| | | | | (__ " -foregroundcolor Magenta
write-host "|_/   \_\____/     |____/ \__, |_| |_|\___|" -foregroundcolor Magenta
write-host "                          |___/            " -foregroundcolor Magenta
Write-Host "    Version 2.0" -foregroundcolor Red
Write-Host "    Build 2009 09-30 14:00" -foregroundcolor Red
Write-host "    Created by Mitchell J. Skurnik" -foregroundcolor Red
#endregion

#region Create the stopwatch and some other things
[System.Diagnostics.Stopwatch] $sw;
$sw = New-Object System.Diagnostics.StopWatch
$sw.Stop()
$sw.Start()
$splist = "C:\Powershell\Users from SharePoint\"
$adlist = "C:\Powershell\Users in Groups\"
#endregion

#region Functions to add/delete users and groups
# Function to control Adding groups
function creategroup
{
    param ([string] $siteurl = "https://extranet.something.com")
    $site = New-Object Microsoft.SharePoint.SPSite($siteurl)
    $web = $site.RootWeb;
    $group = $currentgroup;
    $perm = "Read";
    $owner = "jdoe";
    if ($owner -eq "") { $owner = $web.CurrentUser.LoginName }

    $exists = $web.SiteGroups | where { $_.Name -eq $group }
    if ($exists -eq $null)
    {
     # Create group
     $web.SiteGroups.Add($group, $web.EnsureUser($owner), $null, "");
     # Give permissions to the group
     $assign = New-Object Microsoft.SharePoint.SPRoleAssignment($web.SiteGroups[$group]);
     $assign.RoleDefinitionBindings.Add($web.RoleDefinitions[$perm])
     $web.RoleAssignments.Add($assign)
     Write-Host -ForegroundColor green "Creating sharepoint group - " $currentgroup;
    } 
    $site.Dispose();
}

# Function to add users to the specified group
function addUser
{
    # Open a connection to the sharepoint  site and then select the sub site you want
    $themail = $prop.mail
    $thedisplay = $prop.displayname
    if ($themail -eq "") {$themail = "[email protected]"}
    if ($thedisplay -eq "") {$thedisplay = "Account, Test"}
    if ($themail -eq $null) {$themail = "[email protected]"}
    if ($thedisplay -eq $null) {$thedisplay = "Account, Test"}
    $TheNewGroup = $OpenWeb.SiteGroups | Where-Object {$_.Name -match $currentGroup}
    $TheNewGroup.AddUser("WRKGRP\" + $prop.samaccountname,$themail,$prop.displayname,"")
}

# Function to verify people
function verifyUsers
{
    param ([string] $verify_sitepath="https://extranet.something.com")
    $verify_site=new-object Microsoft.SharePoint.SPSite($verify_sitepath)
    $verify_web=$verify_site.Rootweb
    $verify_web.site.url
    $verify_groups = $verify_web.groups | ? {$_.Name -match "^.*$CurrentGroup" }
    foreach($verify_group in $verify_groups)
    {
     foreach($verify_user in $verify_group.users)
     {
      $verify_user = $verify_user -replace "WRKGRP\\",""
      Write-Output "$verify_user" | Out-File -filepath "$splist$currentGroup.txt" -append
     }
    }
    $strReference = get-Content "C:\Powershell\Users from SharePoint\$currentgroup.txt"
    $strDifference = get-Content "C:\Powershell\Users in groups\$currentgroup.txt"
    #Compare-Object $strReference $strDifference
    Compare-Object $strReference $strDifference | `
    Where-Object { $_.SideIndicator -eq "<=" } | `
    ForEach-Object
    {
     $TheNewGroup = $OpenWeb.SiteGroups | Where-Object {($_.Name -match $currentGroup)}
     $theuser = $verify_web.AllUsers.Item("WRKGRP\$_.InputObject")
     $TheNewGroup.RemoveUser($theuser)
     Write-host "Deleting user: {0} from $currentgroup" -f $_.InputObject -foregroundcolor Red
    }
}
#endregion

Write-host "Searching for Groups" -foregroundcolor Green

#region Create and delete some text files
# Clear out the existing text files so we have a clean slate
$file = New-Item -type file "C:\Powershell\allGroups.txt" -Force

#WARNING DO NOT CHANGE TO SAME DIRECTORY WHERE THE POWERSHELL SCRIPT IS
ls 'C:\Powershell\Users in groups' | remove-item
ls 'C:\Powershell\Users from SharePoint' | remove-item

# Execute the Group Dump Script
C:\Powershell\test.ps1 | Out-File -filepath "C:\PowerShell\allGroups.txt" -append

# Clean up the list by removing duplicates and sorting everything
$TextFile = $TextFile = "C:\Powershell\allGroups.txt" 
$NewTextFile = "C:\Powershell\allGroups - Sorted.txt"
GC $TextFile | Sort | GU > $NewTextFile
#endregion

#region Connect to LDAP and set up some variables
# Use LDAP to connect to Active Directory
$Dom = 'LDAP://OU=Sales Accounts,OU=Users,OU=Home Office,DC=something,DC=com'
$Root = New-Object DirectoryServices.DirectoryEntry $Dom 

# Create a selector and start searching from the Root of AD
$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = $root 

# Get the Group text file's length and write to scree and variable
$fileLength = [System.IO.File]::ReadAllText($NewTextFile).Split("`n").Count
Write-Host "Found " $fileLength "Groups in Active Directory" -foregroundcolor Magenta
$c = 0   # Integer to compare file length
$d = 0   # Integer for thumbing through 'memberOf' in active directory
$f = 0   # Integer for the amount of of users found
#endregion

# Start a while loop where we read through the entire groups text file
while ($c -le $fileLength)
{
    $c++           # Increment the line number for the next pass through
    $currentGroup = (Get-Content $NewTextFile)[$c] # Grab the first line of text from the groups file
    CreateGroup          # Create the group
    Write-host $c "/" $fileLength "`t" $currentGroup -foregroundcolor Red

    # Query Active directory and force some commands
    $adobj= $selector.findall() | where {$_.properties.objectcategory -match "CN=Person"} 
    foreach ($person in $adobj)
    { 
     $prop=$person.properties     # Variable for the different properties to reduce fatigue
     $department = $prop.department    # The Department
     $sn = $prop.sn        # Sir Name
     $gn = $prop.givenname      # Given Name
     $un = $prop.samaccountname     # Account Name
     $memberof = $person.properties["memberof"] # Assign the really long memberof to a variable
     $memberofcount = $test.Count     # Length of memberof

     # Loop for each group the member is in
     while ($d -le $memberof.Count)
     {
      $blah = ForEach-Object{`
       $memberof[$d]`
       -replace "CN=",""`
       -replace ",OU=Regional Sales",""`
       -replace ",DC=something",""`
       -replace ",DC=com","" `
       -replace ",OU=LA Offices","" 
      }
      $d++  # Incriment the d
      if ($blah -eq $currentGroup) # Is that user in the group?
      {
       Write-host "`t`t`t" $un -foregroundcolor Magenta
       $un | Out-File -filepath "C:\Powershell\Users in groups\$currentGroup.txt" -append
       adduser
       $f++
      }
     }
     if ($d -ge $memberofs.Count) { $d=0 }
    }
    verifyUsers  #Verify that the user is supposed to be in there :)
    #Write-Host "`t`t`t" $f " user(s) found"
    $f = 0
}

#region The End
# Stop Watch
$sw.Stop()
write-host "Updated in Time: ", $sw.Elapsed.ToString()
#endregion
Mitchell Skurnik
Glad you've been able to work this out!
Alex Angas
Came to me in the middle of the night but it took me 4 hours to impliment it this morning.What I did was output all the users in group A from LDAP into one file and then export the users from sharepoint into another and then I am comparing the two files. The users that are not in both get removed :). It takes about 30ish minutes for me to process 250+/- users and 300+/- groups
Mitchell Skurnik