ShareFlows.ps1

#
# ShareFlows.ps1
#
[CmdletBinding()]

param(
    [string]$TenantId , #The tenant Id where your instance resides
    [string]$ApplicationId , #The application Id used for the connection
    [string]$ApplicationSecret, #The application secret used for connection
    [string]$EnvironmentUrl,
    [string]$FlowsToShareJson,
    [string]$MSALModulePath,
    [string]$MGUsersModulePath,
    [string]$MGGroupsModulePath,
    [string]$PowerAppsModulePath,
    [string]$PowerAppsAdminModulePath,
    [string]$CrmConnectorModulePath
)

$ErrorActionPreference = "Stop"

Write-Verbose 'Entering ShareFlows.ps1'

#Script Location
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
Write-Verbose "Script Path: $scriptPath"

#Import Modules

$xrmCIToolkit = $scriptPath + "\Xrm.Framework.CI.PowerShell.Cmdlets.dll"
Write-Verbose "Importing: $xrmCIToolkit"
Import-Module $xrmCIToolkit

Write-Verbose "Import Crm Connector: $CrmConnectorModulePath"
Import-module "$CrmConnectorModulePath\Microsoft.Xrm.Tooling.CrmConnector.PowerShell.psd1"

Write-Verbose "Importing MSAL Module" 
Import-Module "$MSALModulePath\MSAL.PS.psd1"

Write-Verbose "Importing Microsoft Graph Users Module" 
Import-Module "$MGUsersModulePath\Microsoft.Graph.Users.psd1"

Write-Verbose "Importing Microsoft Graph Groups Module" 
Import-Module "$MGGroupsModulePath\Microsoft.Graph.Groups.psd1"

Write-Verbose "Importing PowerApps Admin Module: $PowerAppsAdminModulePath"
Import-module "$PowerAppsAdminModulePath\Microsoft.PowerApps.Administration.PowerShell.psd1"

#Connect

Write-Verbose "Connecting to Microsoft Graph"
$MsalToken = Get-MsalToken -TenantId $TenantId -ClientId $ApplicationId -ClientSecret ($ApplicationSecret | ConvertTo-SecureString -AsPlainText -Force)
Connect-Graph -AccessToken $MsalToken.AccessToken

Write-Verbose "Connecting to PowerApps Endpoint"
Add-PowerAppsAccount -TenantID $TenantId -ApplicationId $ApplicationId -ClientSecret $ApplicationSecret -Endpoint prod

#Environment

#$EnvironmentName = (Get-AdminPowerAppEnvironment "$EnvironmentDisplayName").EnvironmentName
#if ($EnvironmentName)
#{
# Write-Verbose "Environment found with Id: $EnvironmentName"
#}
#else
#{
# throw "Evironment: $EnvironmentDisplayName could not be found"
#}


$CrmConnectionString = "AuthType=ClientSecret;url=$EnvironmentUrl;ClientId=$ApplicationId;ClientSecret=$ApplicationSecret"


$CRMConn = Get-CrmConnection -ConnectionString $CrmConnectionString
$EnvironmentId = $CRMConn.EnvironmentId

#Azure AD

#$securePassword = ConvertTo-SecureString $ApplicationSecret -AsPlainText -Force
#$psCred = New-Object System.Management.Automation.PSCredential($ApplicationId , $securePassword)
#Connect-AzureAD -Credential $psCred -TenantId $TenantId

#Share Apps

$FlowsToShare = ConvertFrom-Json $FlowsToShareJson

foreach ($flow in $FlowsToShare.Flows)
{
    $FlowName = $($flow.FlowName)
    Write-Verbose "Locating Flow: $FlowName"

    #$AppId = (Get-AdminPowerApp $($app.AppName) -EnvironmentName $EnvironmentName).AppName
    #If ($AppId)
    #{
    # Write-Verbose "PowerApp $AppName found with Id: $AppId"
    #}
    #else
    #{
    # throw "$AppName could not be found"
    #}
    
    $records = Get-XrmEntities -ConnectionString $CrmConnectionString -FetchXml "<fetch top='50'><entity name='workflow'><attribute name='workflowid'/><attribute name='name'/><attribute name='category'/><attribute name='workflowidunique'/><attribute name='ownerid' /><filter type='and' ><condition attribute='name' operator='eq' value='$FlowName' /><condition attribute='category' operator='eq' value='5' /></filter></entity></fetch>"

    if ($records.Count -eq 1)
    {
        $FlowId = $records[0].workflowidunique
    }
    elseif ($records.Count -gt 1)
    {
        throw "Found mutiple Flows with name: $FlowName"
    }
    else
    {
        throw "Flow with name: $FlowName could not be found"
    }

    foreach($shareWith in $flow.ShareWith)
    {
        $principalType = $shareWith.PrincipalType
        $principal = $shareWith.Principal
        $roleName = $shareWith.RoleName

        if ($principalType -eq "User")
        {
            $principalId = (Get-MgUser -UserId $principal | select Id).Id
            $users = Get-XrmEntities -ConnectionString $CrmConnectionString -FetchXml "<fetch top='50' ><entity name='systemuser' ><attribute name='azureactivedirectoryobjectid' /><attribute name='domainname' /><attribute name='systemuserid' /><filter><condition attribute='domainname' operator='eq' value='$principal' /></filter></entity></fetch>"

            $userId = $users[0].Id
        }
        elseif ($shareWith.PrincipalType -eq "Group")
        {
            $principalId = (Get-MgGroup -Filter "DisplayName eq '$principal'" | select Id).Id
        }
        else
        {
            $principalId = $TenantId
        }

        if ($roleName -eq "Owner")
        {
            Write-Host "Assigning Flow '$FlowName' [$FlowId] with $principalType $principal [$principalId] access $roleName in Environment: $EnvironmentUrl [$EnvironmentId]"

            if ($records[0].ownerid.Id -ne $userId)
            {
                $assign = New-XrmEntity -EntityName 'workflow'
                $assign.Id = $records[0].Id
                $owner = New-Object -TypeName Microsoft.Xrm.Sdk.EntityReference -ArgumentList 'systemuser'
                $owner.LogicalName = 'systemuser'
                $owner.Id = $records[0].ownerid.Id
                $assign.Attributes.add('ownerid', $owner);
                Set-XrmEntity -ConnectionString $CrmConnectionString -EntityObject ($assign)

                Write-Host "Changed Flow Owner"
            }
            else
            {
                Write-Host "Skipped as current Owner is $($records[0].ownerid.Id)"
            }
        }
        else
        {
            Write-Host "Sharing Flow '$FlowName' [$FlowId] with $principalType $principal [$principalId] access $roleName in Environment: $EnvironmentUrl [$EnvironmentId]"

            $response = Set-AdminFlowOwnerRole -FlowName $FlowId -EnvironmentName $EnvironmentId -RoleName $roleName -PrincipalType $principalType -PrincipalObjectId $principalId

            Write-Verbose "Invoked Set-AdminFlowOwnerRole"

            $response

            if ($response.Error -or ($response.Code -ne '' -and $response.Code -ge 400))
            {
                throw "Flow Sharing Failed: $response"
            }
        }

        Write-Host "Shared Flow"
    }
}