Azure Logic Apps and SharePoint Permission management
For a while, I have been playing in SharePoint land. While there, I have been making good use of Power Automate and Azure Logic Apps.
Primarily I have been using Azure Logic Apps as a means of integration and orchestration using it to overcome limitations of a customer-specific workflow engine as well as a means of integration with on-premise applications.
Whilst the environment does come with a bunch of pre-canned SharePoint actions and triggers there are times where you need to go beyond standard steps and make use of the SharePoint REST API.
One of those use cases is definitely permission management — in this, I want to cover off an example use case:
- Create a folder in a document library
- Apply permissions based on a group name
- Then remove permissions for that group from the created folder
Like my other article where I cover setting up an Azure WebApp, I found it frustrating that there is no one place that attempts to take you end-to-end through the process — so the hope here is that I can take you end-to-end.
What tools are available
Before we start, let’s at least call out the alternative approaches that exist.
PowerShell
If you are:
- Happy to develop your own PowerShell Scripts
- Have a server to run said scripts
- And have Tenent based administrator rights
Then PowerShell might be a good alternative to Logic Apps.
Other Logic App / Power Automate actions
The marketplace for actions and triggers is growing — and some provide a bunch of the functionality already. An example of one would be Plum Sail.
It is a subscription service, you will need to add another subscription. And for some that might be a deal-breaker — especially when you can do some actions with your existing tooling.
The Setup
So let’s set some things up. Before we start I am going to set up the scenario — for this we are creating the following:
A Target Folder that we will use for our examples
And a Temp Group
This is very standard SharePoint to set up so I won't dwell long
Finally, we will set up the base permission on our folder (this should inherit down)
Create an Azure Logic App
Now there are several ways to get your logic app running, you can set it to trigger on a SharePoint list item being created, or a message hitting a message queue — but for illustrative purposes, we are just creating one logic app that will use an HTTP request.
Note: If you are using HTTP requests you will need to secure this request by limiting the IP address that can access this endpoint, alternatives can also include using the Azure API Management.
The Logic App will perform the following actions (depending on the mode selected):
- Create a Folder
- Add Permissions to that Folder (for a Specific Group)
- Remove Permissions to that Folder
We will use a combination of standard SharePoint actions as well as the API SharePoint action to show how you can leverage the existing SharePoint Rest API in a Logic App
To start create your Azure Logic App resource from the Azure Portal, and create a blank Logic App
Select the Request Trigger
Next, click on the ‘Use sample payload to generate schema’ link and enter the schema. To begin we are just using a simple message to get us underway.
{
"mode":"createfolder",
"name": "sampleFolder"
}
As we progress we will update this.
Enter the sample JSON message and click Done and you will be left with a schema that you can use to start building a flow.
Finally, click save and this will give you an HTTP endpoint that we can use to develop against.
Create A Folder
Logic Apps requires you to declare your variables upfront. For this action, we will need the declare the following:
- BaseFolder — which is the base folder in the Document Library where we are creating our folders
- FolderName — the name of the folder
Both variables will be strings
Next, we will create a switch statement — we will use this to switch to the different operations
For the switch action, we will be switching on the mode
Next, we add a case for the createfolder
mode.
In this case, we will create our logic to create a folder using the standard actions the are provided making sure to set the FolderName variable on the way through.
Save this, and when we run this using a client Postman we get a response.
Apply permissions based on a group name
Now things get a little interesting. For this case, we will extend the existing Logic App and add the ability to pass in a group name and have that used to grant access to the folder.
To get started we need to add another property to the HTTP Request call.
{ "properties": {
"mode": {
"type": "string"
},
"name": {
"type": "string"
},
"groupname": {
"type": "string"
}
},
"type": "object"
}
Next, add another case to the switch statement.
Next, we need to get the ID of the folder for this SharePoint has an action called Get folder metadata
. But first, we set the variable name up
Next, we can get the folder metadata.
With the ID — we can now Grant access to the folder — however in this case it is the itemid that you need. Now, this is a pretty good call as you can just add the Group Name in Text and it will apply for the permissions.
Add an HTTP request to respond (forgot to do this initially, but I also added a path to capture errors granting access).
And now we are ready to test using PostMan.
And all things going right — you are now able to see that Temporary Permission Group has access to our folder.
Remove Permissions
Now comes the fun part. For this we are going to need to perform the following actions:
- Accept the Group Name and fetch the group ID
- Get the Permissions on the Folder
- From the permissions, determine the ID of the permission that relate to the group's permission
- Update Permissions on the folder
Now, there may be a more efficient way to do this — this way you get to see each step on the way through.
To start we will need to add a couple more variables to the logic app to handle this scenario. The first is removepermissions
— an array-based variable. And the second is groupid
— an integer-based variable. Add these above the switch statement.
Then we will need to add a new condition to our switch statement
Like the other scenarios, we will set our folder name variable.
Next, we need to get the Group ID of the SharePoint Group. For this will need to make an API call to Sharepoint. From the SharePoint actions select Send an HTTP Request to SharePoint
Because we are using the Send an HTTP request to SharePoint call, the Logic App is not going to know the shape of the data coming back. To overcome this we will use the Parse JSON step.
{
"properties": {
"d": {
"properties": {
"AllowMembersEditMembership": {
"type": "boolean"
},
"AllowRequestToJoinLeave": {
"type": "boolean"
},
"AutoAcceptRequestToJoinLeave": {
"type": "boolean"
},
"Description": {
"type": "string"
},
"Id": {
"type": "integer"
},
"IsHiddenInUI": {
"type": "boolean"
},
"LoginName": {
"type": "string"
},
"OnlyAllowMembersViewMembership": {
"type": "boolean"
},
"OwnerTitle": {
"type": "string"
},
"PrincipalType": {
"type": "integer"
},
"RequestToJoinLeaveEmailSetting": {},
"Title": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
}
This is a large object to get back in response, and most of it we will not need. So to streamline things a little I adjust this back so that I just get the elements of the response I need.
{
"properties": {
"d": {
"properties": {
"Id": {
"type": "integer"
}
},
"type": "object"
}
},
"type": "object"
}
With our group id returned, we will need to set the groupid variable.
The next step we need the get the permissions on the folder. We will use the Send an HTTP Request to SharePoint
action again. But this time we will use the Role Assignments API. For this will hit the following endpoint:
_api/web/getFolderByServerRelativeUrl(‘/Shared Documents/@{variables(‘BaseFolder’)}@{variables(‘FolderName’)}/’)/ListItemAllFields/RoleAssignments?$expand=RoleDefinitionBindings`
Note: I am using a Document Library at the root of my SharePoint site for illustrative purposes. If you are using sites, you may need to adjust the path that you use when using this call.
As with the group id API call we will get back a large object. For our purposes, I have trimmed the object back to the bare essentials.
{
"properties": {
"d": {
"properties": {
"results": {
"items": {
"properties": {
"PrincipalId": {
"type": "integer"
},
"RoleDefinitionBindings": {
"type": "object"
}
},
"required": [
"RoleDefinitionBindings",
"PrincipalId"
],
"type": "object"
},
"type": "array"
}
},
"type": "object"
}
},
"type": "object"
}
This will bring back an array of elements, from this we will need to match the PrincipalID
with the group id. For this:
- Create a For Each loop
- Check if the
PrincipleID
is equal to thegroupid
- If
PrincipleID
andgroupid
variable are equal; append the Current Item to theremovepermission
array-based variable.
With the removepermission
array populated, we now need to use this to make the API call to remove the SharePoint permissions.
Create a For Each step and use the removepermission
variable.
Next — for each object in that array we will need to map out the ID of the permissions. For this, we use a Parse JSON step again.
For this step, we are using the following schema
{
"type": "object",
"properties": {
"RoleDefinitionBindings": {
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"Id": {
"type": "integer"
}
},
"required": [
"Id"
]
}
}
}
},
"PrincipalId": {
"type": "integer"
}
}
}
Note: This Parse JSON step can be combined with the one above — however, you will end up with two nodes called results (!!) which will make it hard to determine which one to use (again — we are aiming for obvious)
Within the same For Each look, create a new For Each loop that will process the array of permissions.
And within the for each loop we will make our remove permission API call using the groupid
variable, the role definition ID from our previous API call.
_api/web/getFolderByServerRelativeUrl('/Shared Documents/@{variables('BaseFolder')}@{variables('FolderName')}/')/ListItemAllFields/RoleAssignments/removeroleassignment(principalid=@{variables('groupid')},roleDefId=@{items('Remove_Access:_Go_through_each_to_remove_permission')?['Id']})
Finish this off with a response, and we are done.
Finally, we do a little test to ensure everything is working.
Wrap Up Time
Beyond the next level silliness involved in removing the permissions of a user — this was a straight forward process. Given the low code and flexible nature of the Azure Logic App / Power Automate environment means that you can do a lot without laying down a lot of code.
There are traps here that I have not covered here:
- Scale, especially when you start to get into extremely large Azure Logic App — As you scale you might want to look at splitting off functions.
- And security — this ties into the above point. Once you start passing off data to other Logic App you might want to look at implementing API management or other mechanisms to limit the access