Private/PowerAutomateFlows.ps1

function Invoke-FlowAction {
    Param(
        [Microsoft.Xrm.Tooling.Connector.CrmServiceClient] $CRMConn,
        [bool] [Parameter(Mandatory = $true)] $AnyImportSuccessful,
        [bool] [Parameter(Mandatory = $true)] $AlwaysTryActivate,
        [bool] [Parameter(Mandatory = $true)] $FailonError,
        [string] [Parameter(Mandatory = $true)] $SolutionName,
        [string] [Parameter(Mandatory = $true)] $SolutionFolder,
        [string] [Parameter(Mandatory = $true)] $PipelinePath,
        [bool] [Parameter(Mandatory = $false)] $RunLocally = $false,
        [System.Collections.Hashtable] [Parameter(Mandatory = $true)] $Deploy
    )

    if ($AnyImportSuccessful -or $AlwaysTryActivate -eq $true) {
        Write-Host "Activating Power Automate flows" -ForegroundColor Green
        $ProgressPreference = "SilentlyContinue"
        # Activate Flows and Establish Connection References
        Write-Host "Getting Environment Id"
        $orgs = Get-CrmRecords -conn $CRMConn -EntityLogicalName organization -TopCount 1 -Fields "organizationid"
        if ($orgs.Count -gt 0) {
            $orgId = $orgs.CrmRecords[0].organizationid

            $Environment = Get-AdminPowerAppEnvironment | Where-Object OrganizationId -eq $orgId.Guid
            if ($null -eq $Environment) {
                $PermissionsErrorMessage = "To use this function, the service principal needs registered as a Management Application in Azure. You can do so when running PPDO locally."
                if ($FailonError -eq $true) {
                    Write-PPDOMessage -Message $PermissionsErrorMessage -Type error -RunLocally $RunLocally -LogError $true
                } 
                else {
                    Write-PPDOMessage -Message $PermissionsErrorMessage -Type warning -RunLocally $RunLocally -LogWarning $true
                }
            }
            $EnvId = $Environment.EnvironmentName
            Write-Host "Environment Id - $EnvId"

            if ($Deploy.ConnectionReferences.SetConnections -eq $true) {
                Invoke-SetConnectionReferences -CRMConn $CRMConn -SolutionName $SolutionName -EnvId $EnvId -FailOnError $Deploy.ConnectionReferences.FailOnError -RunLocally $RunLocally
            }
            else {
                Write-PPDOMessage -Message "Skipping setting Connection References per configuration in deployPackages.json" -type "warning" -LogWarning $true -RunLocally $RunLocally
            }

            Invoke-FlowActivation -CRMConn $CRMConn -PipelinePath $PipelinePath -RunLocally $RunLocally -Deploy $Deploy -SolutionFolder $SolutionFolder -EnvId $EnvId -SolutionName $SolutionName
        }
        else {
            $NoOrganizationsError = "There are no organization records in CRM, unable to set Connection References or activate flows."
            if ($FailonError -eq $true) {
                Write-PPDOMessage -Message $NoOrganizationsError -Type error -RunLocally $RunLocally -LogError $true
            } 
            else {
                Write-PPDOMessage -Message $NoOrganizationsError -Type warning -RunLocally $RunLocally -LogWarning $true
            }
        }
    }
}

function Invoke-FlowActivation {
    Param(
        [Microsoft.Xrm.Tooling.Connector.CrmServiceClient] [Parameter(Mandatory = $true)] $CRMConn,
        [string] [Parameter(Mandatory = $true)] $PipelinePath,
        [string] [Parameter(Mandatory = $true)] $SolutionFolder,
        [bool] [Parameter(Mandatory = $false)] $RunLocally = $false,
        [System.Collections.Hashtable] [Parameter(Mandatory = $true)] $Deploy,
        [string] [Parameter(Mandatory = $true)] $EnvId,
        [string] [Parameter(Mandatory = $true)] $SolutionName
    )

    function Invoke-Activate {
        Param(
            [Microsoft.Xrm.Tooling.Connector.CrmServiceClient] [Parameter(Mandatory = $true)] $CRMConn,
            [System.Object] [Parameter(Mandatory = $true)] $FlowsToActivate,
            [ref] [Parameter(Mandatory = $true)] $ErrorCount,
            [switch] $CatchChildFlowActivationErrors
        )

        $FlowsToRetry = @()

        $FlowsToActivate | ForEach-Object {
            $FlowStore = $_
            try {
                $workflow = Get-CrmRecord -conn $CRMConn -EntityLogicalName "workflow" -Id $_.FlowId -Fields "statecode", "name"

                if ($workflow.statecode -ne "Activated") {
                    Write-Host "Workflow status is $($workflow.statecode), attempting to activate flow."
                    $activationConnection = $CRMConn

                    if ($_.ActivateAsUser) {
                        Write-Host "ActivateAsUser defined and set to: $($_.ActivateAsUser), attempting to activate flow as this user"
                        $systemuserResult = Get-CrmRecords -conn $CRMConn -EntityLogicalName "systemuser" -FilterAttribute "domainname" -FilterOperator "eq" -FilterValue $_.ActivateAsUser -TopCount 1 -Fields "systemuserid"
                        if ($systemuserResult.Count -gt 0) {
                            $activationConnection.OrganizationWebProxyClient.CallerId = $systemuserResult.CrmRecords[0].systemuserid
                        }
                        else {
                            Write-PPDOMessage -Message "$($_.Exception.Message)" -Type "error" -RunLocally $RunLocally
                            Throw "User $($_.ActivateAsUser) was not found in $($Deploy.EnvironmentName), unable to impersonate them to activate flow $($workflow.name)"
                        }
                    }
                    Write-PPDOMessage "Enabling Flow '$($workflow.name)'" -Type command -RunLocally $RunLocally
                    try {
                        Set-CrmRecordState -conn $activationConnection -EntityLogicalName "workflow" -Id $_.FlowId -StateCode "Activated" -StatusCode "Activated"
                        Write-Host "...Activated" -ForegroundColor Green
                    }
                    catch {
                        if ($_.ToString().Contains("ChildFlowNeverPublished") -and $CatchChildFlowActivationErrors) {
                            $FlowsToRetry += $FlowStore
                        }
                        else {
                            Throw $_
                        }
                    }
                }
            }
            catch {
                Write-PPDOMessage "$($_.Exception.Message)" -Type "error" -RunLocally $RunLocally
                $ErrorCount.Value++
            }
        }
        return $FlowsToRetry
    }
    
    Write-Host "Checking if there are Flows that need to be activated"
    if ($Deploy.Flows.ActivateFlows -ne $true) {
        Write-PPDOMessage -Message "Skipping flow activation, per 'ActivateFlows' flag being false in deployPackages.json" -RunLocally $RunLocally
        return
    }

    # Use Override File if it is set
    if ($Deploy.Flows.OverrideFile) {
        $FlowsJSONFilePath = "$PipelinePath\$SolutionFolder\$($Deploy.Flows.OverrideFile)"
    }
    else {
        $FlowsJSONFilePath = "$PipelinePath\$SolutionFolder\Flows_Default.json"
    }

    Write-PPDOMessage -Message "Using list of flows to activate at location '$FlowsJSONFilePath'" -RunLocally $RunLocally

    # Get list of flows to activate from JSON
    try {
        $FlowsToActivate = Get-Content -Path $FlowsJSONFilePath | ConvertFrom-Json    
    }
    catch {
        Write-PPDOMessage -Message "$($_.Exception.Message)" -Type "error" -LogError $true -RunLocally $RunLocally
        Throw "An error occurred when trying to get list of flows to activate at location '$FlowsJSONFilePath'."
    }

    Write-PPDOMessage -Message "There are $($FlowsToActivate.Count) Flows to activate." -RunLocally $RunLocally
    if ($FlowsToActivate.Count -le 0) {
        return
    }

    $ErrorCount = 0
    $FlowsToRetry = Invoke-Activate -CRMConn $CRMConn -ErrorCount ([ref]$ErrorCount) -FlowsToActivate $FlowsToActivate -CatchChildFlowActivationErrors
    
    if ($FlowsToRetry.Count -gt 0) {
        Invoke-Activate -CRMConn $CRMConn -ErrorCount ([ref]$ErrorCount) -FlowsToActivate $FlowsToRetry
    }

    if ($Deploy.Flows.FailOnError -eq $true -and $ErrorCount -gt 0) {
        Write-PPDOMessage "There were $ErrorCount Flow activation errors and FailOnError is set to True... exiting." -Type error -RunLocally $RunLocally -LogError $true
        exit 1
    }
}