We recently ran into a request to "archive project sites after the project has been completed" and this started to make me think of how we can automate as much as possible behind the scenes. Before we could do anything, we first had to define what "archive" meant to the end users, site owners, and us (the SharePoint team). After a few conversations, we all landed on the following:
- An archived site retains current permission structure, but is reduced down to read at the site level.
- All users will have access to the site, for historical reference, at a read level.
- The site will be "stamped" with an expiration date, upon which the site will be reviewed and deleted.
- Any content on the site may need to be migrated to team sites (for operational guides).
With these key points in mind, we can begin to really understand how to approach this. I am going to go over the first two steps here, and the others at a later time.
I do want to say that this will perform the actions at the top level of the site. Meaning, that if a user or group is applied directly to a library or a list, this process will not change the permissions that were applied through breaking inheritance.
Let's dive in!
Before we get to the good stuff, let us go over what we need.
I did this in a UDA because we have heard similar asks from other departments, so in preparation I went ahead and did it this way. You can easily do this in a site workflow and feed it from a list.
Retaining Current Structure Reduced to Read
Honestly, at first we suggested to remove all user permissions and then grant everyone read via AD. This accomplishes the same thing, but we lose the permissions structure that was in-place. We also avoid any issues with orphaned user groups, or even removing user groups that were applied on other project sites.
There are a lot of articles out there regarding permission masks and what each of them are.
Next, we need to get the applied groups and update their permissions. We will use Web Service calls to get the group XML so that we can iterate through the data and perform our actions.
We can use the GetGroupCollectionFromWeb service call from usergroup.asmx to set our varGroupXML. We will go after the data from the site that is provided by the user or workflow that we require as a parameter (inputSiteURL). You will have to put in an actual site URL to generate a list of web methods, but you can then replace the URL with your variable once you are setup.
Now that we have the data, we need only to get the specific user group names. So of course we will throw a Query XML action in there and query our variable (varGroupXML). I would recommend testing the web service and seeing the XML so that you understand the structure. It helps determine the XPath needed to get the desired elements. For this, we will use the following for the XPath to get to the Name element: /defaultNS:GetGroupCollectionFromWeb/defaultNS:Groups/defaultNS:Group/@Name
It is time to do the actual updates! We will use a For each loop, and look at the varPermissionIDColl. For each varPermissionID in that collection, we want to call a web service to update the permissions. We will use the UpdatePermission web service in permissions.asmx. Just as before, we will use the inputSiteURL variable to target the specific site. We will need a bit more information on this step:
That is it for the groups. If you were to run this now, all user groups on the target site would be updated to Read permissions.
While we updated all the groups, what about the users? In a perfect world, everyone would be in a group so we would not need to worry about random users being applied outside of a group...but alas we do not live there and random users are granted access outside of user groups. So to accomplish the same thing for users, you will run through the same process, but this time go after users, not groups.
Just like in the first web service call, you will want to use a method from usergroup.asmx, but this time you will want to call the GetUserCollectionFromWeb method to set our varUserXML.
Again, using a Query XML action, get a collection of names to perform the updates on. This time we will want to get login names since we are dealing with users. The XPath for this will be something like: /defaultNS:GetUserCollectionFromWeb/defaultNS:Users/defaultNS:User/@LoginName
I stored it into the varPermissionIDColl variable (you could create another variable if wanted or needed for debugging) and looped through it in the same way. This time, in the web service call, we need to make a slight change since we are dealing with users:
That covers all user groups and users that are applied to the site level. Last piece is to apply all users to the site with Read permissions.
We accomplish this with a simple web service call using the method AddPermission from permissions.asmx. Here is what it will look like:
This process is by no means one-size-fits-all, but it does accomplish some common asks (at least from what we have encountered). If you need to reduce all users and groups down to read, this is a straightforward way of doing things. Also, you could elevate permissions using the same process; simply change the permission mask. I have been toying with the idea of adding some logic to this to allow for permission level selection and evaluate the desired level within the workflow. This would allow for both reducing permissions as well as elevating them all within one UDA.
Also, as I said at the beginning, this only updates users and groups at the top site level. Any areas with broken inheritance will not be affected by this. I would like to explore how to "re-inherit" throughout the site, or possibly, find the areas with broken inheritance. This way no one slips through the cracks with different permissions.
Let me know what you have done or how you would approach/improve this!
Until next time.