Flow: Set permissions using REST API

For a customer I am creating Microsoft Flows to support various business processes. One Flow controls the process of reviewing and approving vital business documents. These documents are so important that during the day to day business the reviewers and approvers are unable to edit these documents. Only when the documents are in review a select few are allowed to edit them.

Microsoft Flow contains many default actions for all sorts of tasks, but
during the writing of this blog post there is no default action that can set permissions or break the inheritance.

In this blog post I will explain how set custom permissions, break the inheritance and restore the inheritance using the REST API.

Register app and grant app permissions

For the Flow to work we will need to Register an App and grant it permissions. During the registration of the App we will receive various id’s we will use in the Flow.

  • Navigate to the app registration page on the SharePoint site.
https://<tenant>.sharepoint.com/_layouts/15/appregnew.aspx
  • Generate a Client ID and Client Secret.
  • Set the Title to Flow Web Service
  • Set App Domain to www.localhost.com
  • Set Redirect URI to https://www.localhost.com
  • Save the generated Client Id and Client Secret.
  • Navigate to the Grant permissions to an app page. This must be done by browsing to the appinv.aspx page of the site.
http://<hostname>/<the Site Collection>/_layouts/15/appinv.aspx
  • Fill in the Client Id and click on Lookup
  • Now we will grant the App full control access to the site collection.
  • Set the following XML as the Permission Request XML
<AppPermissionRequests>
    <AppPermissionRequest Scope="http://sharepoint/content/sitecollection/web" Right="FullControl" />
</AppPermissionRequests>
  • Click on Create.
  • A pop-up screen will appear asking to Trust the App, click on Trust It.
  • The App has been granted permissions and can be used in the Flow.

Creating the Flow

  • Create a Flow with the required trigger.
  • Add two initialize variable actions for the Token type and Access token.
  • Add the HTTP action to acquire the access token and type.
  • Set the name to HTTP – Request access token and type
  • Set the Method to POST.
  • The URI is different for every tenant, you will need to your tenant id.
  • https://accounts.accesscontrol.windows.net/<tenant ID>/tokens/OAuth/2
  • Set the Headers as follows.
  • The key is Content-Type
  • The value is application/x-www-form-urlencoded
  • For the body you will need the tenant ID, client_credentials, client_id and client_secret.
  • grant_type=client_credentials&client_id=
    <Client ID>@<Tenant ID&client_secret=<Encoded Client Secret>&resource=00000003-0000-0ff1-ce00-000000000000/<tenant>.sharepoint.com@<Tenant ID>
  • Add a Parse JSON action.
  • Set the content to the BODY (result) of the HTTP – Request access token and type action.
  • Use the following Schema to parse the JSON.
{
    "type": "object",
    "properties": {
        "token_type": {
            "type": "string"
        },
        "access_token": {
            "type": "string"
        }
    }
}
  • Set the variable Token type and Access token using the results of the parsed JSON.
  • Now you can break the inheritance, add a HTTP action.
  • Set the name to HTTP – break inheritance.
  • Set the Method to POST.
  • The URI is different for every item/list you want to change.
  • <site url>/_api/lists/getbytitle(‘<list display name>’)/items(<ITEM ID>)/breakroleinheritance(true)
  • Set the Headers as follows.
  • The key is Authorization
  • The value is the Token type and Access token variable. Make sure to add a space between the variables.
  • The key is Accept
  • The value is application/json;odata=verbose
  • Now you can grant an user permissions, I am granting the challenger (property of the item) contribution access.
  • Add a HTTP action, with this action we will get the users information.
  • Set the name to HTTP – Get User info
  • Set the Method to GET
  • The URI is different for every location where you want to get the information from.
    <site url>/ _api/web/siteusers/getbyemail(‘<Challengers email>’)
  • Set the same Headers as before.
  • Add the Parse JSON action, name is Parse JSON – Get User Principal ID
  • Set the content to the BODY (result) of the
    HTTP – Get User info action
  • Add the following Schema to the Use sample payload to generate the schema option.
  • Make sure to enter your own account details.
{
  "d": {
    "results": [
      {
        "Id": 9,
        "LoginName": "i:0#.f|membership|<youraccount>",
        "Title": "<your name>",
        "PrincipalType": 1,
        "Email": "<youremail>",
        "IsSiteAdmin": true
      },
      {
        "Id": 14,
        "LoginName": "i:0#.f|membership|<youraccount>",
        "Title": "Example Title",
        "PrincipalType": 1,
        "Email": "<youremail>",
        "IsSiteAdmin": false
      }
  
    ]
  }
}
  • Add a HTTP action, set the name to HTTP – Grant contribute permissions
  • Set the Method to POST
  • The URI is different for every item/list you want to change.
    <site url>/_api/lists/getbytitle(‘<list display name>’)/items(<ITEM ID>)//roleassignments/addroleassignment(principalid='<d.ID>’roleDefId=1073741827)
  • The id 1073741827 stands for contributor, in my blog post SharePoint: Get the Role ID you can read more about role id’s.
  • Set the same Headers as before.
  • Now you can reset the inheritance if needed, add a HTTP action
  • Set the name to HTTP – Reset Role Inheritance
  • Set the Method to POST
  • The URI is different for every item/list you want to change.
    <site url>/_api/lists/getbytitle(‘<list display name>’)/items(<ITEM ID>)/resetroleinheritance
  • Set the same Headers as before.
  • Your Flow will now look like this.

Flow: Start a flow with another flow

A flow was becoming very big and slow and this generated an undesirable situation, it also interfed with the overall duration of the process. The original flow looped through all the items of a SharePoint list and preformed several actions and manipulations, this look more than a hour. The solution was to created a flow that looped through all the items and a second flow that preformed all the actions and manipulations if required.

During the writing of this blog post there is no default action that can start another flow. But we can start a flow by using the action called HTTP (web service).

Flow being started

First we will need to create the flow that is being started. This is required because we will need the HTTP POST URL of this flow in the flow that starts a Flow. This flow will receive an id and then start on that item.

  • Create a flow with the trigger: When a HTTP request is recieved.
  • Add the action Parse JSON.
  • Add the Get Item (by ID).
  • Open the trigger action, the generated HTTP POST URL will be used in the other flow.
  • Set the Request Body JSON Schema to be able to receive an id value.
    {     "Type": "object",     "properties": {         "id": {             "type": "string"         }     },     "required": [         "id"     ] } 
  • Set the output of the trigger called body as the content (input) for the Parse JSON action.
  • Set the following schema
    {     "type": "object",     "properties": {         "id": {             "type": "string"         }     } } 
  • Set the action Get Item (by ID) to use the ID generated in the parse JSON action.
  • Now you can add all the required actions and manipulations you need to do on the item.

Flow that start a flow

When you start a flow on a item you will need to tell the flow on which item to start. In my example I am using a fixed id. In most cases you will need to created a more dynamic solution.

  • Create a flow that starts with your preferred trigger, in my example I used the manual trigger.
  • Add the HTTP action to the flow.
  • Set the method to POST.
  • Set the URI, copy it from the trigger of the flow that is being started.
  • Set the Body .
    {   "id": "60" } 

SharePoint: Get the Role ID

SharePoint uses role ID’s to create the permissions levels for SharePoint Groups. These role ID’s are a set of numbers that define the precise permissions level details.
The most used role ID are for Visitors (Read), Members (Contribute) and Owners (Full control). When you need other permission levels, creating the correct numbers can be difficult. With the help of the following steps it will be easy to create/find the role ID’s you need. This solution works for SharePoint Online and SharePoint 2013. The role ID’s are used when setting permissions using a rest call, see the blog post SharePoint 2013 Workflow: Changing Permissions with REST Calls for the details.

Permissions level  Role ID
Full Control 1073741829
Contribute 1073741827
Read 1073741826

Solution

  1. Open the SharePoint 2013 or Online site.
  2. Create the required Permission level on the Site Collection
  3. Go Site Settings – Site Permissions
  4. Open the Permission Levels
  5. Create a custom permission level, in this example I created a permission level called Custom Contribute.
  6. Now we need to find the corresponding role ID
  7. Create the following URL for your farm / site
    https://contoso.sharepoint.com/sites/dev/_api/web/roledefinitions
    
  8. This will open a page which looks like an RSS feed.
  9. Right click on the page to View Source, this will open the Source in Notepad.
  10. Now copy the code into your favorite XML editor.
  11. Search the XLM for the correct entry
  12. The Role ID is located in the link tag. The Role ID for my custom permission level is 1073741927
  13. The Role ID can now be used in web services, see the blog post SharePoint 2013 Workflow: Changing Permissions with REST Calls for the details.

SharePoint 2013 Workflow: Changing Permissions with REST Calls

SharePoint 2013 workflows do not have a default action to change item or list permissions. With the help of the call http web service action I was able to create multiple REST calls that can interact with the permission. In this blog post describes multiple workflows for specific interactions involving permissions. The solutions where build on SharePoint Online (Office 365) using SharePoint 2013 workflows.

Breaking the inheritance on current item

This workflow will break the inheritance of the item the workflow is running on. It is possible to change the regURL to break the inheritance of different items. If needed set the actions within an App step to make sure the workflow has the required permissions, see the blog post SharePoint 2013 Workflow: App step and App Permissions.

  1. Create a SharePoint 2013 workflow
  2. Create a dictionary variable called JSONRequestHeader
  3. Create a string variable called regURL
  4. Add the action Build Directory, select JSONRequestHeader as the variable
  5. Add Accept and content-type to the directory with the following code:
    application/json;odata=verbose
    

  6. Then store the following URL to the variable regURL

    [%Workflow Context:Current Site URL%]_api/lists/getbytitle('[%Worklfow Context:List Name%]')/items([%CurrentItem:ID%])/breakroleinheritance(true)
    

  7. Add the Call HTTP web service action to the workflow and set the “this” to the regURL
  8. Set RequestHeaders to Variable: JSONRequestHeader
  9. Set RequestType to HTTP Post
  10. The full workflow looks like this

Set permissions on current item

This workflow will set permissions on the item the workflow is running on. It is possible to change the regURL to set permission on different items. If needed set the actions within an App step to make sure the workflow has the required permissions, see the blog post SharePoint 2013 Workflow: App step and App Permissions.

  1. Create a SharePoint 2013 workflow
  2. Create a dictionary variable called JSONRequestHeader
  3. Create a string variable called regURL
  4. Add the action Build dictionary, select JSONRequestHeader as the variable
  5. Add Accept and content-type to the directory with the following code:
    application/json;odata=verbose
    

  6. In this example we will grant the default members group contribute permissions.
  7. Add an step in the workflow called: Set Role Members
  8. Then store the following URL to the variable regURL
    [%Workflow Context:Current Site URL%]_api/lists/getbytitle('[%Worklfow Context:List Name%]')/items([%CurrentItem:ID%])/roleassignments/addroleassignment(principalid='769',roleDefId=1073741826)
    
  9. The roleDefID sets the type off permissions
  10. The principalId is the ID of the permissions group, this is an unique id. You will need to find the principalId for your SharePoint Group, see the chapter Get SharePoint Groups principalId to learn how to find the principalId.
  11. Add the Call HTTP web service action to the workflow and set the “this” to the regURL
  12. Set RequestHeaders to Variable: JSONRequestHeader
  13. Set RequestType to HTTP Post
  14. The full workflow looks like this

Get SharePoint Groups principalId

  1. Open the SharePoint site where the SharePoint Groups are present
  2. Create the following URL
    [Current Site URL]/_api/lists/getbytitle('[List Name]')/items([Item ID])/roleassignments/
    
  3. In the source of the page you can find the principalId’s
  4. The principalId’s are located between the following tag
    <d:PrincipalId m:type="Edm.Int32">769</d:PrincipalId>

Remove all permissions on current item

This workflow will remove all permissions on the item the workflow is running on. It is possible to change the regURL to remove all permission on different items. If needed set the actions within an App step to make sure the workflow has the required permissions, see the blog post SharePoint 2013 Workflow: App step and App Permissions. With this workflow we will first break the inheritance, then get all permissions/roles on the item and then remove the roles. Only site collection administrator and farm admins will be able to access the item when the workflow has run.

  1. Create a SharePoint 2013 workflow
  2. Create a dictionary variable called JSONRequestHeader
  3. Create a dictionary variable called JSONDeleteHeader
  4. Create a dictionary variable called JSONResponse
  5. Create a dictionary called AllRoles
  6. Create a dictionary called RoleItem
  7. Create a string variable called regURL
  8. Create a integer called principalId
  9. Create a integer called Index
  10. Create a integer called countRoles
  11. Create a number called calc
  12. Add the action Build Dictionary, select JSONRequestHeader as the variable
  13. Add Accept and content-type to the directory with the following code:
    application/json;odata=verbose
    

  14. Add the action Build Dictionary, select JSONDeleteHeader as the variable
  15. Add X-HTTP-Method to the directory with the following code:
    DELETE
    

  16. The first step is to break the inheritance of the items, see above the chapter breaking the inheritance on current item for the steps.
  17. The second step is to get all SharePoint Groups (Roles) that have permissions on the item.
  18. Store the following URL to the variable regURL
    [%Workflow Context:Current Site URL%]_api/lists/getbytitle('[%Worklfow Context:List Name%]')/items([%CurrentItem:ID%])/roleassignments
  19. Add the Call HTTP web service to the workflow and set the “this” to the regURL
  20. Set RequestHeaders to Variable: JSONRequestHeader
  21. Set RequestType to HTTP Post
  22. Set ResponseContent to JSONResults
  23. Add the action Get an Item from a Dictionary, select from Variable: JSONResults, with output to Variable:AllRoles and the following code
    d/results

  24. Then add the action Count Items in a Dictionary, select from Variable:AllRoles with output Variable: countRoles
  25. The third step is to remove all the Roles
  26. Add a loop that runs repeatedly while: Variable:Index is less then Variable:countRoles
  27. Add the action Get an Item from a Dictionary, select from Variable: JSONResults, with output to Variable:roleItems and the following code
    d/results([%Variable:Index%])
  28. Add the action Get an Item from a Dictionary, select from Variable: roleItem, with output to Variable:principalId and the following code
    PrincipalId

  29. Then store the following URL to the variable regURL
    [%Workflow Context:Current Site URL%]_api/lists/getbytitle('[%Worklfow Context:List Name%]')/items([%CurrentItem:ID%])/roleassignments([%Variable:principalId%])
  30. Add the Call HTTP web service to the workflow and set the “this” to the regURL
  31. Set RequestHeaders to Variable: JSONDeletedHeader
  32. Set RequestType to HTTP Post
  33. Add the action Do Calculation select Variable:Index plus 1 and store the outcome in Variable: Calc
  34. Then Set Variable:Index to Variable:Calc
  35. The full workflow looks like this

SharePoint 2013 Workflow: App step and App Permissions

By default workflows run under the permissions of the user that starts the workflow, even is the workflow starts on a change or when a new item is created. The initiator does not always have the right permissions to complete all the actions within the workflow. For example when the workflow needs to delete an item, create a new items or use a web service. With SharePoint 2013 workflows it is possible to use the App step action to give the workflow its own permission set.

Solution part 1: Activate the feature

  1. The feature Workflows can use app permissions needs to be active.
  2. Open the site settings and click on Manage site features
  3. Activate the feature: Workflows can use app permissions

Solution part 2: Grant workflow app permissions

  1. Open the site settings and click on Site app permissions
  2. Copy the client section if the App Identifier. This is the identifier between the last “|” and the “@” sign, as shown in the figure.
  3. Navigate to the Grant permissions to an app page.This must be done by browsing to the appinv.aspx page of the site.
    http://}hostname}/{the Site Collection}/_layouts/15/appinv.aspx
    
  4. Fill in the App Id and click on Lookup
  5. The fields Title, App Domain and Redirect URL will be filled in automatically.
  6. Paste the following XML into the Permission Request XLM textbox to the grant the workflow App step full control
    <AppPermissionRequests>
        <AppPermissionRequest Scope="http://sharepoint/content/sitecollection/web" Right="FullControl" />
    </AppPermissionRequests>
    
  7. Click on Create and on Trust It

Solution part 3: Using the App step

  1. Create a SharePoint 2013 Workflow with SharePoint Designer.
  2. Click on App Step in the ribbon to add the step.
  3. Add actions within the App Step and publish the workflow, for example:

SharePoint 2013 Workflow: Start a workflow using a REST call

SharePoint 2013 workflows do not have a default action to start another 2013 workflow. There is an action to start 2010 workflows, but for my solution I required the more advanced actions only available with SharePoint 2013  workflows. With the help of the call http web service action I was able to create a REST call that started the workflow. The solution was build on SharePoint Online (Office 365) using a SharePoint 2013 workflow.

The REST call workflow consist of three major parts;

  1. The HTTP web service URL
  2. The SubscriptionID of the workflow that needs to be started
  3. The itemID of the item on which the workflows needs to be started.

A side note: The workflow that is being started needs to be configured to start manually. I could not find any reference on MSDN about this, but without this option I would get an error message.

Solution

  1. Create a SharePoint 2013 workflow
  2. Create a string variable called CurrentItemID
  3. Create a string variable called WorkflowGUID
  4. Create a string variable called regURL
    workflowvariables
  5. Now we need to find the SubscriptionID of the workflow we want to start. It is possible to use another REST call to find the SubscriptionID. But I prefer the following method.
  6. Navigate to the workflow settings of the list where the workflow is located
  7. Right-click on the workflow and open the properties
  8. Copy the URL and find the SubscriptionID within the URL
    getsubscriptionid
  9. Set the variable WorkflowGUID with the found SubscriptionID
  10. Set the variable CurrentID with the ItemID of the current item or item on which the workflow needs to start.
    setvariables
  11. Then store the following URL to the variable regURL
    [%workflow Context: Current Site URL%]_api/SP.WorkflowServices.WorkflowInstanceService.Current/StartWorkflowOnListItemBySubscriptionId(subscriptionId='[%Variable: WorkflowGUID%]',itemID='[%Variable: CurrentItemID%]')
    
  12. Add the Call HTTP web service to the workflow and set the “this” to the regURL
  13. Set the HTTP Method to HTTP POST
    setcallhttpwebservice
  14. The workflow will now look like this
    fullwfstartanotherwf
  15. The app step is used to make sure the workflow has the required permissions. More details about the App Stepp will be explained in another blog post.