MOSS MVP

I've moved my blog to http://blog.falchionconsulting.com!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Wednesday, August 6, 2008

Delete All Users from a Site Collection

I was chatting with my buddy and fellow MVP, Todd Klindt, the other day and he was asking if I had a custom command that would allow him to quickly delete all the users added to a site collection.  For his purposes he needed this to troubleshoot an issue he was having with a site collection that contained many thousands of users.

I figured this would be pretty easy to do - a simple while loop with a conditional check for site administrators to prevent them from being deleted so I quickly typed out the following code during our IM conversation:

using (SPSite site = new SPSite(url))
using (SPWeb web = site.OpenWeb())
{
    int offsetIndex = 0;
    while (web.SiteUsers.Count > offsetIndex)
    {
 
        if (web.SiteUsers[offsetIndex].IsSiteAdmin || web.SiteUsers[offsetIndex].ID == web.CurrentUser.ID)
        {
            offsetIndex++;
            continue;
        }
        web.SiteUsers.Remove(offsetIndex);
    }
}

We can't use a for loop for this because we're modifying the collection and that would result in an error so we need to use a while loop and just keep deleting until there's no more to delete.  But, we have to watch out for site administrators as we can't directly delete them in this fashion (and it's a good idea to keep them regardless) so we use an offset that gets incremented whenever we hit an administrator.  We also can't delete the current user so I check for that as well.

One thing to note is that if you have a lot of users (Todd was dealing with 17,000) then it will run real slow - he was seeing about 300 deleted per hour with progressively more as the size dwindled - so be patient :)

I figured since I had the code that I might as well turn it into a new stsadm command so I created gl-deleteallusers.  There's not really much to this command - it just takes in a single URL parameter which points to your site collection.  I thought about adjusting this so that you could delete users from a specific web that had broken inheritance but I didn't really have time.  Here's the help output:

C:\>stsadm -help gl-deleteallusers

stsadm -o gl-deleteallusers


Deletes all site collection users.  Will not delete site administrators.

Parameters:
        -url <site collection url>

The following tables summarizes the command and its parameters:

Command Name Availability Build Date
gl-deleteallusers WSS v3, MOSS 2007 7/23/2008

Parameter Name Short Form Required Description Example Usage
url   Yes The URL of the site collection of which the users will be deleted.  If a sub-site of the site collection is passed in it will only look at the site collection itself and ignore the sub-site information. -url http://portal/

Using the command is real simple - the following will delete all users, except site administrators, from the http://portal/sites/site1 site collection:

stsadm -o gl-deleteallusers -url http://portal/sites/site1

19 comments:

Todd said...

Thanks Gary. That command was a life saver.

tk

Anonymous said...

Does it work also for sub-sites???

Gary Lapointe said...

Nope - just site collection users.

Anonymous said...

Hi Gary, these commands are absolutely the best! Is it possible to delete all groups in a site collection?
We are using the "gl-convertsubsitetositecollection" command for a largish deployment. Our issue is that the converted sites bring in over 200 groups from the previous top level site.

Gary Lapointe said...

I currently don't have anything for the groups but you could easily delete them all with a simple powershell script that basically does what my code is doing for users but for groups instead.

Tamir said...

Hi. I am new to SharePoint. What file should I download, and do I make it availiable for me in the stsadm.exe ?

Gary Lapointe said...

If you have MOSS download the MOSS version otherwise download the WSS version (the WSP files). Then use stsadm's addsolution and deploysolution commands to deploy the WSP to your farm. The commands will then be available.

Tamir said...

Please Ignore my last post(!) after deploysolusion - it was there (told you I new here.. :) )

Thank you so much!!!
(Did some one said a life saver? that was true...)

AlexMondale said...

Gary,
I am a huge fan, like everyone else that posts here. One of my pet peeves is how many (17k doesn't really surprise me) of these users turn into dead wood in a normal-sized org. So, I humbly submit a small code project I undertook that extends your code here with a lookup into an AD to find objects that have been removed/disabled in AD. Take a look and use it if it looks interesting.


namespace WindowsApplication1
{

public partial class Form1 : Form
{
public ArrayList arrHold;

public Form1()
{
InitializeComponent();

}

private void button1_Click(object sender, EventArgs e)
{
lbUsers.Items.Clear();
using (SPSite site = new SPSite(txtSCURL.Text))
using (SPWeb web = site.OpenWeb()){
int offsetIndex = 0;
while (web.SiteUsers.Count > offsetIndex) {
if (web.SiteUsers[offsetIndex].IsSiteAdmin || web.SiteUsers[offsetIndex].ID == web.CurrentUser.ID){
offsetIndex++;
continue; }

string ThisUser = web.SiteUsers[offsetIndex].ToString();
if (ThisUser.StartsWith("DOMAIN\\")){
ThisUser = ThisUser.Substring(nn);
if (arrValidUser(ThisUser)==null)
lbUsers.Items.Add (web.SiteUsers[offsetIndex]);
}
offsetIndex++;
}
}
if (lbUsers.Items.Count < 1)
lbUsers.Items.Add("--All Users are Valid!--");
}

private string arrValidUser(string TheUser)
{
DirectoryEntry de = null;
DirectorySearcher ds = null;
SearchResult result = null;
//SearchResultCollection results = null;
string userInfo = String.Empty;
using (de = new DirectoryEntry(
"LDAP://dc=root,dc=domain,dc=foo",
null,
null,
AuthenticationTypes.Secure))
{

using (ds = new DirectorySearcher(de))
{
try
{
//ds.Filter = "(objectCategory=person)";
ds.PropertiesToLoad.Add("samAccountName");
ds.Filter ="(SamAccountName="+TheUser+")";
result = ds.FindOne();
if (null != result)
return "DOMAIN\\" + result.Properties["samAccountName"][0].ToString();
else
return null;
}

catch (Exception ex)
{
// log exception to the application event log
string sEvent = ex.Message;

}
return null;
}
}

}

private void button2_Click(object sender, EventArgs e)
{
using (SPSite site = new SPSite(txtSCURL.Text))
using (SPWeb web = site.OpenWeb()){
int offsetIndex = 0;

while (web.SiteUsers.Count > offsetIndex) {
// don't get to whack yourself! or an admin!
if (web.SiteUsers[offsetIndex].IsSiteAdmin || web.SiteUsers[offsetIndex].ID == web.CurrentUser.ID){
offsetIndex++;
continue; }

string ThisUser = web.SiteUsers[offsetIndex].ToString();
if (ThisUser.StartsWith("DOMAIN\\")){
ThisUser = ThisUser.Substring(nn);
if (arrValidUser(ThisUser) == null)
{
//MessageBox.Show("removing " + ThisUser);
web.SiteUsers.Remove(offsetIndex);
offsetIndex--;
}
}
offsetIndex++;
}
lbUsers.Items.Clear();
}
}
}
}

Gary Lapointe said...

Alex - thanks for posting your code - it is most appreciated!

Oskar said...

Has anyone tried the UserGroup.RemoveUserCollectionFromSite() web service method? Might it perform faster? 300 users per hr is too slow for our need...

Frank said...

Hi,

Can this be extended to remove users within a certain group from a site collection?

Gary Lapointe said...

It could but you'd probably be better just using PowerShell to achieve that.

brad.stickley said...

We have the need to delete all user profiles from a well intended, but overwhelming AD setting in our Shared Services Administration. All users in the AD were imported.... Is there a process that can take care of this? I don't want to delete the users from a site, but remove the imported profiles from our AD.

Thanks

Gary Lapointe said...

Adjust your import settings to remove the unnecessary accounts and then run a full import 3 times - that "should" automatically delete the missing profiles (I've seen cases where it didn't in which case we just manually deleted them via central admin by selecting the missing from import view).

Adam Preston said...

Gary,

This saved me HOURS of manual time! Thank you!

- Adam Preston

wolf992 said...

First off: Thanks Gary; your commands have helped us a ton.
Second: in reply to an anonymous post about deleting all groups in a site collection, this is the PowerShell command I used:
([xml](stsadm -o enumgroups -url http://siteurl/)).Groups.Group | foreach {stsadm -o deletegroup -url http://siteurl/ -name $_.Name }.

Not sure if it's the best way, but it's been working for me.

Chuck141 said...

First off I want to say Thanks to everyone on this thread. This is great stuff. I am currently trying to take AlexMondale code and scope it to the web application level. Has anyone done this already?
I am trying to accomplish
Goal:
The removal of deleted AD accounts from SharePoint entirely.

Our SharePoint Portal Contains a lot of site collections so I don't want to go through each one.

Kundan Ghimire said...

Alex,
I am trying to run this code . I am getting bunch of error
The name lbluser does not exist
The type or namsespace SpSite could not be found and so on other couple.
Can you please guide me

Thanks