In this post, I am going to demonstrate Office 365 Site Access Governance using the approval process.
Here I have leveraged the below tools to achieve the functionality,
- Power apps/SPFx web part to take the request from the user
- SharePoint list to store the requested information
- Microsoft Flow will trigger after item creation and send to Azure function
- An azure function in which the business logic has been implemented to provide access to the requested site collection.
1. Create a Power App/SPFx web part to configure on the SharePoint page
Ok, I will start with the user request form, here it’s completely based on the client requirement which fields should be displayed on the user request form. you have to design the form accordingly.
2. Create a SharePoint list to store the requested information
3. Create Approval Workflow using Microsoft Flow
Open the URL https://flow.microsoft.com/ and choose the Blank template under the New button.
After selecting the template to choose When an item is Created and provide the name of the flow,
Now you should provide the Site Collection URL where the SharePoint list and web parts are configured, remember that the below template message I have added a custom message please don’t get confused that the template selection with the name.
After establishing the valid connection click on the New Step button and select the template as Initialize variable,
Provide the variable name and type as a string,
After creating the variable the next step is to click on the New step and select the template Apply to each, inside the template on the top Select output from previous steps, select the SharePoint list field which is having all access requested user email values.
Under the Apply to each control-click Add an action again add Compose variable control. after adding the click on the Inputs control, now it will open a popup there click Expression tab under the add the below expression and click OK.
concat(items(‘Apply_to_each’)?[‘Email’],’,’,variables(‘AccessForRequesteUsers’))
Now click on the Add an action with in the same Apply to each control, add a variable with control type: Set variable
From the dropdown select the variable which already initialized variable and assign the output.
Finally, after completing all configuration the Apply to each control looks like,
By using the above control the flow is concatenated and stored the requested user values in a single variable, now we will use the HTTP request to send the requested item information to Azure function, to configure this click on New step and type HTTP from the search box and select the HTTP control,
After selecting the control need to configure the values which need to be sent to Azure function in JSON format.
Here I have used only 4 properties to send the requested item information to Azure function, but you can as many required as per the business requirements.
Method: POST
URI: The URL needs to copy from the Azure function I will explain in the Azure function creation step.
Body: Here I used the JSON format to send the dynamic values that are populating from the current item and AccessrequestedUsers are taking the resulting form the variable which we already Compose and stored the final result in a variable.
The next step select Initialize variable control to store the response values from the Azure function,
To get the response from Azure function with specific property use the below syntax,
body(‘HTTP‘)[‘SiteColAdmins’]
Note: The value HTTP should be the same as before the HTTP Control selection name( on top), if not it will not work.
The next step would be to Initialize the variable which is used to compare the null values.
Now add one condition to validate the requested site collection administrators are not null,
After adding the above condition you will get two conditions IF Yes, IF No.
In this post, I am going to use multiple conditions, for a better explanation I will use numbers for IF conditions.
Condition 1: IF Yes
Under IF Yes condition click Add an action and again add one more condition to compare if the requested site is Private or Public or any other site template.
Condition 1: IF No: Here I am not adding any functionality
Again under the same condition, there will be two actions IF Yes, IF No.
Condition 2: IF Yes
Under IF Yes, click Add an action and add Start and wait for an approval control,
After adding the control do the below configuration,
After the workflow execution click Add an action, add the Condition and compare with the Responses value.
I have observed some times from the Approval flow Outcome is getting the value please check the below condition as well,
Under the above condition, there will be two actions IF Yes, IF No
Condition 3: IF Yes
Under If Yes condition adds one more HTTP request with Owner Approved Status.
After any one of the owners approved the Azure function will provide access to the requested site. Finally, it will send an email to the Owners and requested users.
Condition 2: IF No
Under IF No, Add an action add Sent an email control, because as per the scenario if the site belongs to the Private or any other secure templates the approval process will execute else it will automatically provide the access and send an email to the requested users and site collection administrators.
Condition 3: IF No
IF No: This scenario will execute if the owner of the site is Rejected the request, then it will send a rejected email to requested users and Site collection administrators, this will happen very rarely if it happens to need to contacts directly to the owners of the site.
4. Create an Azure Function using PnP PowerShell.
In this blog post, I am not going to explain detailed steps on how to create Azure function, because I have already explained all the steps on one of my previous blog post please use the link to create.
In my earlier post, I have used the Queue trigger template to create an azure function, but in the current scenario, I have used the HTTP trigger with the PowerShell template.
After creating the HTTP trigger we need to copy URL, which needs to be configured on the MS Flow.
Finally, the script will be,
#region [ Configuration values ]
$requestBody = Get-Content $req -Raw | ConvertFrom-Json
$RequestedSiteURL = $requestBody.SiteUrl
$ID = $requestBody.ID
$AccessRquestedEmails = $requestBody.AccessForRequesteUsers
$ApprovalStatus = $requestBody.Status
#Values are coming form Azure function properties/ if required we can configure directly
#$TenantUrl = $env:SPO_TenantUrl
#$UserName = $env:SPO_UserName
#$PassWord = $env:SPO_PassWord
$TenantURL = ‘Add Tenant URL’
$ListUpdateSiteURL = ‘Site collection URL : Where List created to store/update the requested item’
$UserName = ‘User email’
$PassWord = ‘Pass word’
$UserEmailValues = $AccessRquestedEmails.Substring(0,$AccessRquestedEmails.LastIndexOf(‘,’))
[string[]] $UserEmails = $UserEmailValues -split ‘,’
$Pwd = ConvertTo-SecureString $PassWord -AsPlainText -Force
$Creds = New-Object System.Management.Automation.PSCredential($UserName,$Pwd)
$ListName = ‘List Name’ #provide the list name
$ClassicTeamSiteTemplate = ‘STS’
$CommunicationSiteTemplate = ‘SITEPAGEPUBLISHING’
$CommunicationSiteType = ‘Modern Communication Site’
$ModernTeamSiteTemplate = ‘GROUP’
$AllAdminUsers = “”
$ExceptionMessage = “”
$IsUserfound = “True”;
$SiteType=”Team Site”
$OwnerApprovalStatus = ‘Owner Approved’
#endregion
Connect-PnPOnline -Url $RequestedSiteURL -Credentials $Creds
$WebTemp = Get-PnPWeb -Includes “WebTemplate” | Select-Object -Property WebTemplate
Write-Output “Web Template is ” $WebTemp.WebTemplate
if (($webTemp.WebTemplate -eq $ClassicTeamSiteTemplate) -OR ($webTemp.WebTemplate -eq $CommunicationSiteTemplate)) {
if ($webTemp.WebTemplate -eq $CommunicationSiteTemplate) {
$SiteType = $CommunicationSiteType
}
$AdminEmails = @()
Write-Output “Condition to validate the approval status if it’s approved or not ”
if ($ApprovalStatus -ne $OwnerApprovalStatus) {
$SiteCollectionAdmins = Get-PnPSiteCollectionAdmin
foreach ($AdminUser in $SiteCollectionAdmins) {
if($AdminUser.Email -ne “”){
$AdminEmails += $AdminUser.Email
}
}
Connect-PnPOnline -url $ListUpdateSiteURL -Credentials $Creds
foreach ($Sitecoladmin in $AdminEmails) {
try {
Write-Output “Updated site col admins values to SharePoint list” $WebTemp.WebTemplate
Set-PnPListItem -List $ListName -Identity $ID -Values @{“SiteColAdmins” = $Sitecoladmin; “SiteType” = $SiteType } -ErrorAction Stop
}
catch {
$IsUserfound = “False”
$ExceptionMessage += $($_.Exception.Message) +”and owners are $($admins)”
Write-Output “Exception Message: $($ExceptionMessage) and owners are $($admins)”
Set-PnPListItem -List $ListName -Identity $ID -Values @{“SiteType” = $SiteType; “Error_x0020_Detail” = $($ExceptionMessage); “OwnerStatus” = “Closed” }
}
}
if ($IsUserfound -eq “True”) {
Set-PnPListItem -List $ListName -Identity $ID -Values @{“SiteColAdmins” = $AdminEmails; “SiteType” = $SiteType }
}
# Adding one more condition if the site belongs to the Communication site directly gives access.If there are any other site templates we can wait till the owner approves
if ($SiteType -eq $CommunicationSiteType) {
Write-Output “Given the access to the users”
Connect-PnPOnline -url $RequestedSiteURL -Credentials $Creds
Add-PnPSiteCollectionAdmin -Owners @($UserEmails)
Connect-PnPOnline -url $ListUpdateSiteURL -Credentials $Creds
Set-PnPListItem -List $ListName -Identity $ID -Values @{“SiteType” = $SiteType; “Status” = “Closed”; “OwnerStatus” = “Updated Owner” }
}
} else {
try {
#This part will execute when the owner approved request through MS flow
Write-Output “Started approval process after owner approved”
Connect-PnPOnline -Url $ListUpdateSiteURL -Credentials $Creds
$GetSiteCol=(Get-PnPListItem -List $ListName -Id $ID -Fields “SiteColAdmins”).FieldValues
$ContainsSiteCol=$GetSiteCol[“SiteColAdmins”].Email
if($ContainsSiteCol){
Connect-PnPOnline -Url $RequestedSiteURL -Credentials $Creds
Add-PnPSiteCollectionAdmin -Owners @($UserEmails)
Connect-PnPOnline -url $ListUpdateSiteURL -Credentials $Creds
Write-Output “Access given to requested users after the owener approval ”
Set-PnPListItem -List $ListName -Identity $ID -Values @{“SiteType” = $SiteType; “Status” = “Closed”; “OwnerStatus” = “Owner Approved” }
} else {
Connect-PnPOnline -url $ListUpdateSiteURL -Credentials $Creds
Write-Output “Exception Message: $($ExceptionMessage)”
Set-PnPListItem -List $ListName -Identity $ID -Values @{“SiteType” = $SiteType; “Status” = “Closed”; “OwnerStatus” = “Error” }
}
}
catch {
Connect-PnPOnline -url $ListUpdateSiteURL -Credentials $Creds
$ExceptionMessage += $($_.Exception.Message)
Write-Output “Exception Message: $($ExceptionMessage)”
Set-PnPListItem -List $ListName -Identity $ID -Values @{“SiteType” = $SiteType; “Error_x0020_Detail” = $($ExceptionMessage); “Status” = “Error” ; “OwnerStatus” = “Error”}
}
}
Disconnect-PnPOnline
foreach ($UserEmail in $AdminEmails) {
$AllAdminUsers += $UserEmail + ‘;’
}
Write-Output “All Admin users ” $AllAdminUsers
} else {
#region [ Execute the below functionality for Modern team sites: We can apply the same approval process for Modern private sites as well depends on Business requirement ]
if(($webTemp.WebTemplate -eq $ModernTeamSiteTemplate)) {
try {
Connect-PnPOnline -Url $TenantURL -Credentials $Creds
$OgroupSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $Creds -Authentication Basic -AllowRedirection
Import-PSSession -AllowClobber $OgroupSession
Write-Output “Connected to exchange online”
Connect-PnPOnline -Url $RequestedSiteURL -Credentials $Creds
$OfficeGroupId = (Get-PnPSite -Includes GroupId).GroupId.ToString()
$AccessType = Get-UnifiedGroup -Identity $OfficeGroupId | Select-Object -Property AccessType
Write-Output “Access type is ” $AccessType.AccessType ” and Office group id is “$OfficeGroupId
$SiteType=$AccessType.AccessType
$Owners = Get-UnifiedGroup -Identity $OfficeGroupId | Get-UnifiedGroupLinks -LinkType Owner | Select-Object -Property WindowsLiveId
$O365GroupOwners = $Owners.WindowsLiveID
Write-Output “Owners are ” $O365GroupOwners
$GroupOwners = @()
foreach ($Owner in $O365GroupOwners) {
$GroupOwners += $Owner
}
Write-Output “Site Owners ” $GroupOwners
Connect-PnPOnline -Url $RequestedSiteURL -Credentials $Creds
if ($SiteType -eq “Public”) {
$MembershipOwnerType = ‘Owner’
$MembershipMemberType = ‘Member’
foreach ($reqowner in $ToEmails) {
if ($reqowner) {
Write-Output “Started Adding user to office 365 owner gorup ” $reqowner
Add-UnifiedGroupLinks -Identity $OfficeGroupId -LinkType $MembershipMemberType -Links $reqowner
Add-UnifiedGroupLinks -Identity $OfficeGroupId -LinkType $MembershipOwnerType -Links $reqowner
}
}
}
Connect-PnPOnline -url $ListUpdateSiteURL -Credentials $Creds
Write-Output “Update the Sharepoit list after providing the access to modern site”
Set-PnPListItem -List $ListName -Identity $ID -Values @{“SiteType” = “Modern Team Site – ” + $accessType.AccessType; “Status” = “Closed”; “OwnerStatus” = “Updated Owner” }
} catch{
Connect-PnPOnline -url $ListUpdateSiteURL -Credentials $Creds
$ExceptionMessage += $($_.Exception.Message)
Write-Output “Exception Message: $($ExceptionMessage)”
$updatedItem=Set-PnPListItem -List $ListName -Identity $ID -Values @{“SiteType” = “Modern Team Site – ” + $accessType.AccessType; “Error_x0020_Detail” = $($ExceptionMessage); “Status” = “Error”; “OwnerStatus” = “Error” }
}
foreach ($UserEmail in $O365GroupOwners) {
$AllAdminUsers += $UserEmail + ‘;’
}
Write-Output “All Admin users ” $AllAdminUsers
#endregion
}
}
$body = @{
ID=$ID
AccessFor=$UserEmails
SiteType=$SiteType
SiteColAdmins=$AllAdminUsers
} | ConvertTo-Json
Out-File -Encoding Ascii -FilePath $res -inputObject “$body”
From the above script if you observe that the last @body tag properties will send back the response to the MS Flow, here I used only 4 properties if you want you can send the other properties as per the business requirement.
Hope this blog will help you if you get similar kind requirement 🙂 🙂