SharePoint Site Assistant: Part II
Katerina Chernevskaya
????Principal Microsoft Cloud Architect (Power Platform & Azure AI) ||?? AI unlocks opportunities, low-code provides tools, I transform them into business impact. ||??BizApps MVP || ??MCT || ??Speaker
This is the second article in our series that focuses on developing a robust solution for users, facilitating an effortless request for a new SharePoint site via a streamlined, user-centric interface.
Part I - Developing an intuitive Canvas App screen to gather user preferences for new SharePoint sites.
Part II - Automate the site creation process, incorporating invitations for external users.
Part IV - Evaluating user behavior and assessing application performance.?(coming soon)
Part V - The Next Steps.?(coming soon)
?
A standout advantage of this solution is its ability to simplify the process, providing a one-screen form that requesters can complete conveniently. It also automates all necessary steps, significantly enhancing efficiency.
It's important to point out that SharePoint Site Assistant enables the requester to effortlessly provide a list of colleagues and external users who will be automatically added as members to the new site.
In this particular article, we will delve more deeply into automation. We will capture all the specifications provided by the requester, clean the list of users set to be added as members, invite external users, and create dedicated SharePoint groups with the necessary access levels. To wrap up the process, we will notify both the members and the requester about the new SharePoint site and provide them with the link.
?
Let's go! ??
?
Automate the site creation process, incorporating invitations for external users
Create additional variables
Go to your solution, click New -> More -> Environment variable.
Create the following 5 variables:
Name - siteAddress
Value - the root SharePoint site address, for example: https<YOUR DOMAIN>.sharepoint.com
?
Name - siteOwner
Value - the app's admin email that will be used to send email notifications after the process will be completed (in both success or failed cases), as well this email will be used to assign the owner role of the created SharePoint site
Name - SharePointAssistantTenant
Value - empty for now
Name - SharePointAssistantClient
Value - empty for now
?Name - SharePointAssistantKey
Value - empty for now
Trigger
Every flow initiates with a trigger. In our solution, we have the flexibility to choose between two options:
Option 1: The flow is triggered once a new record in Dataverse is created.
Option 2: The flow is triggered from Power Apps.
?
Option 1 is feasible because we store all prerequisites for a new SharePoint site in the Dataverse. However, there is always a caveat: in this scenario, you must ensure that the appropriate license is acquired for the Cloud Flow. Considering that we plan to use a Premium connector in this flow (to invite external users), if you select this option, you must have a Power Automate per flow, per user, or PAYG subscription.
?
Option 2 appears more appropriate for our solution because we aim to automate the steps of SharePoint site creation immediately after a user successfully submits their requirements. This option allows us to initiate the flow from the Power Apps, which implies that the flow will be triggered in the Power Apps Canvas App context. As such, the Power Apps Standalone license will suffice for this option.
?
Let's create our flow with the Power Apps trigger. Later on, we'll connect this flow to our Canvas App.
?
Open the solution that you created previously. Click on New -> Automation -> Cloud flow -> Automated.
Skip the prompted window, and in the Designer search for the trigger PowerApps. Add this trigger.
?
Later on, we will add an action that will take information from our Power Apps Canvas app.
?
Get a row from Dataverse with requirements
After setting up the trigger, we are prepared to configure the actions within the flow. The first action should retrieve all the necessary information for creating a new SharePoint site. In this phase, we have flexibility. You can directly transfer the required data from the Power Apps Canvas app. However, my preference lies in obtaining just the ID of the Dataverse record where all the necessary prerequisites are stored.
?
In the initial article of this series, we set up the Submit button to patch information to the Dataverse. We embedded the Patch function within the Set function, enabling us to fetch the ID of the newly created Dataverse row. So, we can use Get a row by ID action in our flow and pass to this action ID from the Canvas app.
Click on + New step button in the flow, search for the Get a row by ID action and add it.
Select your Dataverse table in the Table name field. For the Row ID field open the Dynamic content and select Ask in PowerApps.
?
And there you have it. The action is now configured. Once we finalize the flow, we'll integrate it with the Canvas App. During this integration, we will set up the passing of the ID of the row to the flow.
?
Variables
We need to initialize two variables type Array for storing the list of users: one for external users and one for internal (additional). We need two separate variables due to the different processing of these users.
When you add 2 Initialize variable actions and configure them, you should have the following two actions in your flow:
?
Convert the list of external users into array
In the Canvas app, the requester is required to provide at least one external user. This stipulation is a product of my configuration of the app, under the assumption that this is a mandatory field (feel free to configure the mandatory of this field based on your requirements). To enhance the user experience, we've offered the requester flexibility in choosing a more convenient separator - comma, semicolon, or space - to delimit the external users' emails. The requester's input is stored in the Dataverse as a string.
Given these conditions, we have two tasks to perform during the verification process before converting the string into an array:
1) Determine if we have a single external user or multiple external users
2) Standardize the separator by replacing commas and spaces with semicolons
?
Now, let's proceed with these tasks.
Click on + New step and add Scope action (it is located under Control group).
In the Scope add a Condition.
The conditional statement might seem a bit complex, but let's break down what's happening. Essentially, we have the following expression: if the string from Dataverse contains a semicolon, then the string comprises multiple external users; otherwise, the string has just one external user. Straightforward, right? However, our first step is to sanitize the string retrieved from Dataverse by replacing spaces and commas with semicolons. Users may inadvertently mix delimiters, creating delimiter combinations such as ', ' (coma and space) or '; ' (semicolon and space). Keeping this in mind, we will sequentially replace everything with a comma and finally replace the comma with a semicolon.
So, put the following code in the condition statement:
replace(
replace(
replace(
replace(
replace(
outputs('Get_a_row_by_ID')?['body/kch_externalusers'],
',?',
','
),
';?',
','
),
'?',
','
),
',,',
','
),
',',
';'
)
The operator is contains.
The value is ;
In the If yes branch add the Set variable action. In the Name field select the variable externalUsers. In the Value field add the following code:
split(
replace(
replace(
replace(
replace(
replace(
outputs('Get_a_row_by_ID')?['body/kch_externalusers'],
',?',
','
),
';?',
','
),
'?',
','
),
',,',
','
),
',',
';'
),
';'
)
With this code we again clean the string and additionally split all items by delimiter ; to create an array.
?
In the If now branch, add the Append to array variable action. In the Name field select the variable externalUsers. In the Value field select the Dataverse field where external user is stored.
?
Please take note: In the 'If no' branch, we are unable to utilize the 'Set variable' action because the variable type is an array, and therefore, the 'Set variable' action can only be used to store an array. A single email does not constitute an array, hence we need to append this value to an array.
?
The completed scope should looks as the following:
Convert the list of additional users into array
The steps for sanitizing additional users are quite similar. However, before initiating these steps, we must verify whether the requester has entered any additional users. Since the "Additional Users" field is not mandatory, we should take preventive steps to validate whether this field is empty or not, to avoid any errors in our flow.
?
Add a new Scope action as a parallel branch. Add a Conditional where check if the field from Dataverse that should store additional users is equal to null.
Please take a note: null should be entered as expression, not as a plain text.
Leave If yes branch empty.
In the If no branch repeat steps that we did for external users, just replace the Dataverse column you are using in expressions.
?
Merge users in one array
Alright, we now have two arrays for external and internal users (referred to as "Additional" within the app's context). Our next step is to merge these two arrays, simplifying their utilization in subsequent steps.
?
Add a Compose action and add the following code in the action to merge two arrays in one:
union(variables('externalUsers'),variables('additionalUsers'))
For now we have the following steps (scops are collapsed):
Create a SharePoint site
We are going to use Send an HTTP request to SharePoint to create a new site. Before we make this request let's create the URL for the new site. The structure of the link is following:
- Root address (we stored this information in the environment variable siteAddress)
- Static part sites/
- The site name that the requestor provided. Here I suggest you to remove all spaces in the name.
?
We can simply concatenate all parts and store the result in a variable. Add a new action Initialize variable, add the variable's name siteURL, type - String and add the following code into the Value field:
concat(
parameters('siteAddress?(kch_siteAddress)'),
'sites/',
replace(
outputs('Get_a_row_by_ID')?['body/kch_sitename'],
'?',
''
)
)
?
Now add Send an HTTP request to SharePoint action and configure it as following:
Site Address: environment variable siteAddress
Method: POST
Uri: _api/SPSiteManager/create
Headers:
{
?"accept": "application/json;odata=verbose",
?"content-type": "application/json;odata=verbose"
}
Body:
{
"request": {
"Title": "<VALUE FROM DATAVERSE>",
"Url": "<VARIABLE siteURL>",
"Description": "<VALUE FROM DATAVERSE>",
"Owner": "ENVIRONMENT VARIABLE siteOwner",
"Lcid": <VALUE FROM DATAVERSE>,
"WebTemplate": "SITEPAGEPUBLISHING#0",
领英推荐
"SiteDesignId": "<VALUE FROM DATAVERSE>",
"ShareByEmailEnabled": true
}
}
?
Please take all double quotes. Only the Lcid and ShareByEmailEnabled values are without double quotes.
?
Both action should looks as on the following screenshot:
Check if the site was created
The successful SharePoint site creation should return SiteStatus value 2. You can create Try-Catch logic in your convenient way. Below is a simple example:
- Parse body of the last action
- Check if the StatusCode equal to 2
- If no - send email notification to the admin, save the result to the Dataverse and stop the flow
Create SharePoint groups
SharePoint sites inherently have their own groups for Owners, Members, and Visitors, which we could utilize to add users. However, to do so, we would need to know their respective IDs. Although these IDs are usually as follows: 3 for Owners, 4 for Visitors, and 5 for Members, this isn't a strict rule and the IDs could be different. Such variations can potentially complicate our flow and lead to errors. To circumvent this, we plan on creating our own groups, thereby maintaining the process's full automation.
?
To create a new group add a new action Send an HTTP request to SharePoint and configure it as following:
Site Address: SiteURL that we got by parsing the body of the site creation action
Method: POST
Uri: _api/web/SiteGroups
Headers:
{
?"accept": "application/json;odata=verbose",
?"content-type": "application/json;odata=verbose"
}
Body:
{?
"__metadata": {?
"type":"SP.Group"?
},?
"Title": "<THE GROUP NAME>",?
"Description": "The group for a requestor and employees"?
}
For the group name I'm using the following code:
concat(replace(outputs('Get_a_row_by_ID')?['body/kch_sitename'],' ',''),' - Owners permissions')
?
After this action add the Parse action to parse the body of the previous step.
Hint: to get the JSON schema for the Parse action you can save and run flow, and cope to body of the output of the particular step.
?
After Parse add the Send an HTTP request to SharePoint action again. This time we will add permission to the created group. Configure this step as following:
Site Address: SiteURL that we got by parsing the body of the site creation action
Method: POST
Uri: /_api/web/roleassignments/addroleassignment(principalid=<ID OF THE GROUP FROM THE PREVIOUS PARSE>, roledefid=<ID OF REQUIRED PERMISSION>)
Headers:
{
?"accept": "application/json;odata=verbose",
?"content-type": "application/json;odata=verbose"
}
?
Options for the <ID OF REQUIRED PERMISSION>:
1073741829 - Full control
1073741827 - Contribute
1073741826 - Read
Repeat these 3 actions (create a group, parse, grant permissions) if you need to create more groups.
?
Invite external users
We can't add external users to the SharePoint directly until these external users don't have identity in out AD (Entra ID). To avoid any errors in the flow let's invite external users. For this step we will need Azure App Registration. Please follow the instruction on my GitHub to create the app registration in your tenant: https://github.com/Katerina-Chernevskaya/SharePoint-Site-Assistant#create-the-app-registration.
Once the app registration will be created please update your environment variables SharePointAssistantTenant, SharePointAssistantClient, SharePointAssistantKey.
?
Add a new action - HTTP and configure it as following:
Method: POST
Headers:
{
?"Content-type": "application/json"
}
Body:
{
?"invitedUserEmailAddress": "<VARIABLE externalUsers>",
?"inviteRedirectUrl": "<SiteURL - DYNAMIC CONTENT>",
?"sendInvitationMessage": false,
?"invitedUserMessageInfo": {
???"customizedMessageBody": "Hey! Join this site!"
?}
}
Authentication: Active Directory Oauth
Tenant: <ENVIRONMENT VARIABLE SharePointAssistantTenant>
Client ID: <ENVIRONMENT VARIABLE SharePointAssistantClient>
Secret: <ENVIRONMENT VARIABLE SharePointAssistantKey>
?
Please take a note: once you add the variable with external users this HTTP action will be automatically put into the loop.
?
Add users to the member group
This is one more Send an HTTP request to SharePoint action with the following configuration:
Site Address: SiteURL that we got by parsing the body of the site creation action
Method: POST
Uri: _api/web/sitegroups(<GROUP ID>)?['d']?['Id']})/users
Headers:
{
?"accept": "application/json;odata=verbose",
?"content-type": "application/json;odata=verbose"
}
Body:
{"__metadata":{"type":"SP.User"},"LoginName":"i:0#.f|membership|<FULL LIST OF MEMBERS>"}
Final steps
In the end of our flow we will update the Dataverse record and send email notification to all members, requester and owner.
Members we put in the BCC. This field accept string value. We convert the array with thelist of all members into a string using the following formula:
join(outputs('Final_Users_List'),';')
Integrate the flow with the Canvas App
Open the Canvas app we've created in the previous article. Go to the Power Automate tab, click Add flow and add your new flow.
?
Select the Submit button, open OnSelect property and after this code
// Navigate to the next screen
???Navigate(Done);
add the following code:
//Run flow
'SharePointAssistant-Sitecreation'.Run(_newRequestRecord.'SharePoint site requests');
Accomplishment Unlocked: A Comprehensive Flow for SharePoint Site Creation
Woohoo! ?? Congratulations! ?? We have successfully created a comprehensive flow that considers all facets of SharePoint site creation.
This milestone signifies a substantial leap in our journey to automate and streamline the process, ensuring an effective and user-centric system for both site creators and users. Let's give ourselves a round of applause for this remarkable achievement!
?
In our upcoming article, we'll be introducing additional screens to our Canvas App. Keep an eye out for updates!
??Want a quick start?
Want to install a whole solution right now? Feel free to download it from my GitHub and follow the step-by-step installation guidelines!
Here's the download link: https://github.com/Katerina-Chernevskaya/SharePoint-Site-Assistant.