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 :-)