Wednesday, August 24, 2022

Powerapps form - Control field visibility using Sharepoint Groups

We in general create Sharepoint groups for any specific roles in a process and utilize them across the process for visibility / notifications etc.  As there is no direct way to check is a user is member of any specific Sharepoint group, we can follow below approach. 

1. Create a Sharepoint group and add required users for access. 

2. Create a Sharepoint List - PowerAppsAdmin and add Description column, along with existing Title

3. Add an Item to this list, 

      Title: "Custom App Admin Access" (Any relative name for easy reference)

      Description: Used in Powerapps form for Admin access fields (some details to understand easily on there its used)

4. Now remove  permissions on this list item and provide read access only to the above created sharepoint group, along with Owners group, if they are super admins.

5. In the PowerApps form, Add another data source  - above created Sharepoint List "PowerAppsAdmin"

Now the list and the data is accessible across the PowerApps. We can use this to control access or d isplay mode of fields / data cards using filter expressions, as in below example

6. Select Visible Property of a data card and write in below expression

not(IsEmpty(Filter(PowerAppsAdmin, Title = "Custom App Admin Access")))

The above expresssion is fitlering on the Admin lsit for an item with title we provided. If it is not empty, that means the logged in user has access to the list item and it implies the owner is an admin. So, the card will be visible to the user.

7. Same way, we can also control DisplayMode of the form be selecting Display Mode of the card and writing below expression

If(IsEmpty(Filter(PowerAppsAdmin, Title = "Custom App Admin Access")),DisplayMode.View,Parent.DisplayMode)

The above expression ensures if no record fetched, its always in view mode and for admins, the mode of field depends on form.






Wednesday, April 20, 2022

Azure Function to Update DLP Policy of a Sharepoint Site

In order to share an Online Sharepoint site with external users at restricted level, we can setup different Data loss Prevention (DLP) policies and include / exclude the specific Site in the policy. 

In this article, will walk through automate the process of  updating DLP policies of a Sharepoint site using Azure Function which can be referred from a Microsoft Flow or any external client like Service NOW. 

For DLP policy updates, we use Exchange Online Management module in Powershell. The only way to connect to exchange online / IPPS session is  using User account. So, we create an admin account in azure with MFA disabled (  Use Conditional Access to exclude ) and provide "Compliance Administrator" role.

Login to portal.azure.com and access Function App.  


Click on Create on top left. Select an existing Subscription. You may create a new Resource Group or choose from an existing ( this is used to group multiple resources together ). Provide Function App name, choose Powershell 7.0 as runtime stack and Click on Review + create. 

It may take a while to deploy. Once its ready, access Configuration on left nav of the app to store username and Password of admin account. Best practice is to refer from Key Vault to keep them secure.

Access, functions on left nav, Create new function.

Choose, HTTP trigger template for the function. We will be able to call this service from Flow or any external client.

After Creation of function, fetch the URL using "Get Function URL" available in top nav. This will be the API url to consume from external systems. If we want to secure it further, can configure in API Management tool.

As we need Exchange Online Management Module, we can save the module to local machine and upload to the Azure function using FTP.  Once its available in server, use Import-module by referring it from server location. 

Now we should be able to write the code in browser by clicking on Code + Test on left navigation.

using namespace System.Net
# Input bindings are passed in via param block.
param($Request$TriggerMetadata)
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

# Interact with the body of the request.
$inputSite = $Request.Body.sharepointSite
$accessType = $Request.Body.accessType
#sample set
#$inputSite = "https://luckyenv.sharepoint.com/sites/SampleSite"
#$accessType = "Site Level"

#admin center access is used to check if provided site exists in the tenant
$AdminCenterURL = "https://luckyenv-admin.sharepoint.com/"

Write-Host "provided site is $inputSite"
try {
    #make sure this account has MFA disabled to work in azure functions automated way.
    $user = $env:admin-username
    $pw = $env:admin-password | ConvertTo-SecureString -AsPlainText

    $cred = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $user$pw
    Write-Host "creds created."

    #connect to sharepoint tenant to validate site url
    Connect-PnPOnline -URL $AdminCenterURL -Credential $cred
    #Check if site exists
    $Site = Get-PnPTenantSite | Where { $_.Url -eq $inputSite }
    If ($Site -ne $null) {
        #disconnect from tenant
        Disconnect-PnPOnline
        try {
            # we need to import the 
            Import-Module "D:\Home\site\wwwroot\updatedlppolicy\modules\ExchangeOnlineManagement\2.0.5\ExchangeOnlineManagement.psd1"
            #Import-Module ExchangeOnlineManagement
    
            #Connect to the session
            Connect-IPPSSession -Credential $cred
            Write-Host "dlp policy session connected."
    
            #Exclude the site from default policy
            Set-DlpCompliancePolicy "Sharing Outside of Org" -AddSharePointLocationException $inputSite -ErrorAction Stop
            Write-Host "Site excluded from default policy"
    
            #We have 2 policies based on Access type pased to API
            if ( $accessType -eq "Site Level") {
                $dlpPolicy = "External Collaboration Entire Site"
            }
            else {
                $dlpPolicy = "External COllaboration Subset"
            }
            #Include the site to a specific policy
            Set-DlpCompliancePolicy $dlpPolicy  -AddSharePointLocation $inputSite -ErrorAction Stop
            Write-Host "dlp policies updated."
    
            #Disconnect ipps session. we need to disconnect, limited sessions allowed at a time
            Disconnect-ExchangeOnline -Confirm:$false -InformationAction Ignore -ErrorAction SilentlyContinue
    
            #Prepare response 
            $status = "Success"
            $body = "successfully updated the DLP policy."

        }
        catch {
            Write-host "Error caught and handled in catch."
            Write-Error $_
            Write-Error $_.ScriptStackTrace

            #Prepare response 
            $status = "Failed"
            $body = "An error occurred that could not be resolved. $_.Exception.Message"
    
            #Disconnect ipps session.
            Disconnect-ExchangeOnline -Confirm:$false -InformationAction Ignore -ErrorAction SilentlyContinue
    
        }
    }
    Else {
        #Prepare response 
        $status = "Failed"
        $body = "Provided Sharepoint site doesn't exist in the tenant."
        Disconnect-PnPOnline
    }
}
catch {
    Write-host "Error caught on high level and handled in catch."
    Write-Error $_
    Write-Error $_.ScriptStackTrace
    
    #Prepare response 
    $status = "Failed"
    $body = "An error occurred in connecting to system and could not be resolved. $_.Exception.Message"

    #Disconnect from tenant, if connected
    Disconnect-PnPOnline
    
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
        StatusCode = [HttpStatusCode]::OK
        Body       = @{Status = $statusMessage = $body } | ConvertTo-Json -Compress
    })


Updated comments for each line of the code to understand it in detail. In this API, we are passing 2 parameters, one is site url and other is access type, could be at site level / doc level. Based on Access type, we choose the DLP policy of the site to be updated to.

Happy coding :-)