Scripts/ActivateFlows.ps1

#idea is to refactor SolutionDeploy into smaller scripts - to make maintenance easier


function Activate-Flows-Step {
    Param(
        [string] [Parameter(Mandatory = $true)] $PipelinePath,
        [string] [Parameter(Mandatory = $true)] $SolutionFolder,
        [System.Object] [Parameter(Mandatory = $true)] $CRMConn,
        [System.Object] [Parameter(Mandatory = $true)] $Deploy,
        [bool] [Parameter(Mandatory = $true)] $RunLocally
    )

    $FlowsToRetry = @()
    Write-Host "Establishing Connection References and Activating Flows" -ForegroundColor Green
    $ProgressPreference = "SilentlyContinue"
    # Activate Flows and Establish Connection References
    Write-Host "Getting Environment Id"
    $orgs = Get-CrmRecords -conn $CRMConn -EntityLogicalName organization
    if ($orgs.Count -gt 0) {
        $orgId = $orgs.CrmRecords[0].organizationid

        $Environment = Get-AdminPowerAppEnvironment | Where-Object OrganizationId -eq $orgId.Guid
        $EnvId = $Environment.EnvironmentName
        Write-Host "Environment Id - $EnvId"
        Write-Host "Checking if there are Connections References in the Solution that Need to be Wired Up"
        $solutions = Get-CrmRecords -conn $CRMConn -EntityLogicalName solution -FilterAttribute "uniquename" -FilterOperator "eq" -FilterValue "$($package.SolutionName)"
        $solutionId = $solutions.CrmRecords[0].solutionid
        $connRefs = (Get-CrmRecords -conn $CRMConn -EntityLogicalName connectionreference -FilterAttribute "solutionid" -FilterOperator eq -FilterValue $solutionid -Fields connectionreferencelogicalname, connectionid, connectorid, connectionreferenceid).CrmRecords
        $connRefs |  ForEach-Object {
            #Where-Object { $null -eq $_.connectionid } |
            $connectionType = $_.connectorid.Replace("/providers/Microsoft.PowerApps/apis/", "")
            Write-Host "Found Connection Reference $($_.connectionreferencelogicalname) "
            # without a Connection to $connectionType"
        
            Write-Host "Getting Connections in Environment"
            $connection = Get-AdminPowerAppConnection -EnvironmentName $EnvId | Select-Object -ExpandProperty Statuses -Property ConnectionName, DisplayName, ConnectorName, CreatedBy, CreatedTime | Where-Object { ($_.status -eq "Connected") -and ($_.ConnectorName -eq $connectionType) } | Sort-Object -Property CreatedTime
            #| Where-Object ConnectorName -eq $connectionType
            if ($connection) {
                # Get Dataverse systemuserid for the system user that maps to the aad user guid that created the connection
                $systemusers = Get-CrmRecords -conn $CRMConn -EntityLogicalName systemuser -FilterAttribute "azureactivedirectoryobjectid" -FilterOperator "eq" -FilterValue $connection[0].CreatedBy.id -Fields domainname
                if ($systemusers.Count -gt 0) {
                    Write-Host "Impersonating the Owner of the Connection - $($systemusers.CrmRecords[0].domainname)"
                    # Impersonate the Dataverse systemuser that created the connection when updating the connection reference
                    $impersonationCallerId = $systemusers.CrmRecords[0].systemuserid
                    $impersonationConn = $CRMConn
                    $impersonationConn.OrganizationWebProxyClient.CallerId = $impersonationCallerId 
                    Write-PPDOMessage "Setting Connection Reference to use $($connection[0].DisplayName)" -Type command -RunLocally $RunLocally
                    Set-CrmRecord -conn $impersonationConn -EntityLogicalName $_.logicalname -Id $_.connectionreferenceid -Fields @{"connectionid" = $connection[0].ConnectionName }                                    
                }
            }
            else {
                Write-PPDOMessage "No Connection has been set up of type $connectionType, some of your Flows may not Activate succesfully" -Type warning -RunLocally $RunLocally -LogWarning $true
            }
        
        }

        Write-Host "Checking if there are Flows that need to be Activated"
        if ($Deploy.Flows.ActivateFlows -eq $true) {
            if ($Deploy.Flows.OverrideFile) {
                Write-Host "Using $($Deploy.Flows.OverrideFile) for Flow Activation"
                try {
                    $FlowsToActivate = Get-Content -Path $PipelinePath\$SolutionFolder\$($Deploy.Flows.OverrideFile) -ErrorAction SilentlyContinue | ConvertFrom-Json    
                }
                catch {
                    
                }
                
            }
            else {
                Write-Host "Using Flows_Default.json for Flow Activation"
                try {
                    $FlowsToActivate = Get-Content -Path $PipelinePath\$SolutionFolder\Flows_Default.json -ErrorAction SilentlyContinue | ConvertFrom-Json 
                }
                catch {
                    $FlowsToActivate = $null
                }                                
            }
            Write-Host "There are $($FlowsToActivate.Count) Flows that need activating"
            $ErrorCount = 0
            if ($FlowsToActivate.Count -gt 0) {
                $FlowsToActivate | ForEach-Object {
                    $FlowStore = $_
                    $workflow = Get-CrmRecord -conn $CRMConn -EntityLogicalName workflow -Id $_.FlowId -Fields clientdata, category, statecode, name
                    if ($_.ActivateAsUser) {
                        Write-Host "ActivateAsUser defined and set to : $($_.ActivateAsUser), attempting to Active Flow as this user"
                        $systemuserResult = Get-CrmRecords -conn $CRMConn -EntityLogicalName systemuser -FilterAttribute "domainname" -FilterOperator "eq" -FilterValue $_.ActivateAsUser
                        if ($systemuserResult.Count -gt 0) {
                            $systemUserId = $systemuserResult.CrmRecords[0].systemuserid
                            #Activate the workflow using the owner.
                            if ($workflow.statecode -ne "Activated") {
                                $impersonationConn = $CRMConn
                                $impersonationCallerId = $systemUserId
                                $impersonationConn.OrganizationWebProxyClient.CallerId = $impersonationCallerId 
                                Write-PPDOMessage "Enabling Flow '$($workflow.name)'" -Type command -RunLocally $RunLocally
                                try {
                                    Set-CrmRecordState -conn $impersonationConn -EntityLogicalName workflow -Id $_.FlowId -StateCode Activated -StatusCode Activated    
                                }
                                catch {
                                    Write-PPDOMessage "There was an error activating the Flow, please confirm that the user exists and that the appropriate connections have been created as this user in the Environment" -Type warning -RunLocally $RunLocally -LogWarning $true
                                    Write-Host $_
                                    if ($_.ToString().Contains("ChildFlowNeverPublished")) {
                                        $FlowsToRetry += $FlowStore
                                    }
                                    else {
                                        $ErrorCount++    
                                    }
                                    
                                }
                            
                            }    
                        }
                        Write-PPDOMessage "User $($_.ActivateAsUser) was not found in $($Deploy.EnvironmentName)" -Type warning -RunLocally $RunLocally -LogWarning $true
                    }
                    else {
                        Write-Host "Checking if '$($workflow.name)' needs Activating..."
                        $solutions = Get-CrmRecords -conn $CRMConn -EntityLogicalName solution -FilterAttribute "uniquename" -FilterOperator "eq" -FilterValue "$($package.SolutionName)"
                        $solutionId = $solutions.CrmRecords[0].solutionid
                        $connRefs = (Get-CrmRecords -conn $CRMConn -EntityLogicalName connectionreference -FilterAttribute "solutionid" -FilterOperator eq -FilterValue $solutionid -Fields connectionreferencelogicalname, connectionid, connectorid, connectionreferenceid).CrmRecords
                        $connRefToUse = $connRefs | Where-Object { $null -ne $_.connectionid } | Select-Object -First 1 -ErrorAction SilentlyContinue
                        if ($null -ne $connRefToUse) {
                            Write-Host "---- Connection Ref Details ----"
                            Write-Host "Connectiod ID : " $connRefToUse.ConnectionId 
                            Write-Host $connRefToUse
                            Write-Host "--------------------------------"
                            $connection = Get-AdminPowerAppConnection -EnvironmentName $EnvId -Filter $connRefToUse.ConnectionId    
                        } 
                        else {
                            Write-PPDOMessage "There was an error getting a Connection to use, please confirm that the user exists and that the appropriate connections have been created as this user in the Environment" -Type warning -RunLocally $RunLocally -LogWarning $true
                            $ErrorCount++
                        }                                           
                        # Get Dataverse systemuserid for the system user that maps to the aad user guid that created the connection
                        if ($null -ne $connection) {
                            Write-Host "---- Connection Details -----"
                            Write-Host "Connection UserID : " $connection[0].CreatedBy.id
                            Write-Host $connection[0]
                            Write-Host "-----------------------------"
                            $systemusers = Get-CrmRecords -conn $CRMConn -EntityLogicalName systemuser -FilterAttribute "azureactivedirectoryobjectid" -FilterOperator "eq" -FilterValue $connection[0].CreatedBy.id
                        }                
                        else {
                            Write-PPDOMessage "There was an error getting the owner of the Connection to use, please confirm that the user exists and that the appropriate connections have been created as this user in the Environment" -Type warning -RunLocally $RunLocally -LogWarning $true
                            $ErrorCount++
                        }                            
                        if ($systemusers.Count -gt 0) {
                            # Impersonate the Dataverse systemuser that created the connection when updating the connection reference
                            $impersonationCallerId = $systemusers.CrmRecords[0].systemuserid
                            if ($workflow.statecode -ne "Activated") {
                                Write-PPDOMessage "Enabling Flow '$($workflow.name)' as Owner of Connection Reference" -Type command -RunLocally $RunLocally
                                $impersonationConn = $CRMConn
                                $impersonationConn.OrganizationWebProxyClient.CallerId = $impersonationCallerId 
                                try {
                                    Set-CrmRecordState -conn $impersonationConn -EntityLogicalName workflow -Id $_.FlowId -StateCode Activated -StatusCode Activated    
                                    Write-Host "...Activated" -ForegroundColor Green
                                }
                                catch {
                                    Write-PPDOMessage "There was an error activating the Flow, please confirm that the user exists and that the appropriate connections have been created as this user in the Environment" -Type warning -RunLocally $RunLocally -LogWarning $true
                                    Write-Host $_
                                    if ($_.ToString().Contains("ChildFlowNeverPublished")) {
                                        $FlowsToRetry += $FlowStore
                                    }
                                    else {
                                        $ErrorCount++    
                                    }


                                }
                                    
                            }

                        }                                          
                    }
                }  
            }
            ########################## - Retry Flow
            $FlowsToRetry | ForEach-Object {
                Write-Host
                Write-Host "Retrying Flows that failed due to Child Flows" -ForegroundColor Green
                $workflow = Get-CrmRecord -conn $CRMConn -EntityLogicalName workflow -Id $_.FlowId -Fields clientdata, category, statecode, name
                if ($_.ActivateAsUser) {
                    Write-Host "ActivateAsUser defined and set to : $($_.ActivateAsUser), attempting to Active Flow as this user"
                    $systemuserResult = Get-CrmRecords -conn $CRMConn -EntityLogicalName systemuser -FilterAttribute "domainname" -FilterOperator "eq" -FilterValue $_.ActivateAsUser
                    if ($systemuserResult.Count -gt 0) {
                        $systemUserId = $systemuserResult.CrmRecords[0].systemuserid
                        #Activate the workflow using the owner.
                        if ($workflow.statecode -ne "Activated") {
                            $impersonationConn = $CRMConn
                            $impersonationCallerId = $systemUserId
                            $impersonationConn.OrganizationWebProxyClient.CallerId = $impersonationCallerId 
                            Write-PPDOMessage "Enabling Flow '$($workflow.name)'" -Type command -RunLocally $RunLocally
                            try {
                                Set-CrmRecordState -conn $impersonationConn -EntityLogicalName workflow -Id $_.FlowId -StateCode Activated -StatusCode Activated    
                            }
                            catch {
                                Write-PPDOMessage "There was an error activating the Flow, please confirm that the user exists and that the appropriate connections have been created as this user in the Environment" -Type warning -RunLocally $RunLocally -LogWarning $true
                                Write-Host $_
                                $ErrorCount++    
                            
                                
                            }
                        
                        }    
                    }
                    Write-PPDOMessage "User $($_.ActivateAsUser) was not found in $($Deploy.EnvironmentName)" -Type warning -RunLocally $RunLocally -LogWarning $true
                }
                else {
                    Write-Host "Checking if '$($workflow.name)' needs Activating..."
                    
                    $solutions = Get-CrmRecords -conn $CRMConn -EntityLogicalName solution -FilterAttribute "uniquename" -FilterOperator "eq" -FilterValue "$($package.SolutionName)"
                    $solutionId = $solutions.CrmRecords[0].solutionid
                    $connRefs = (Get-CrmRecords -conn $CRMConn -EntityLogicalName connectionreference -FilterAttribute "solutionid" -FilterOperator eq -FilterValue $solutionid -Fields connectionreferencelogicalname, connectionid, connectorid, connectionreferenceid).CrmRecords
                    $connRefToUse = $connRefs | Where-Object { $null -ne $_.connectionid } | Select-Object -First 1 -ErrorAction SilentlyContinue
                    $connection = Get-AdminPowerAppConnection -EnvironmentName $EnvId -Filter $connRefToUse.ConnectionId
                    # Get Dataverse systemuserid for the system user that maps to the aad user guid that created the connection
                    $systemusers = Get-CrmRecords -conn $CRMConn -EntityLogicalName systemuser -FilterAttribute "azureactivedirectoryobjectid" -FilterOperator "eq" -FilterValue $connection[0].CreatedBy.id
                    if ($systemusers.Count -gt 0) {
                        # Impersonate the Dataverse systemuser that created the connection when updating the connection reference
                        $impersonationCallerId = $systemusers.CrmRecords[0].systemuserid
                        if ($workflow.statecode -ne "Activated") {
                            Write-PPDOMessage "Enabling Flow '$($workflow.name)' as Owner of Connection Reference" -Type command -RunLocally $RunLocally
                            $impersonationConn = $CRMConn
                            $impersonationConn.OrganizationWebProxyClient.CallerId = $impersonationCallerId 
                            try {
                                Set-CrmRecordState -conn $impersonationConn -EntityLogicalName workflow -Id $_.FlowId -StateCode Activated -StatusCode Activated    
                            }
                            catch {
                                Write-PPDOMessage "There was an error activating the Flow, please confirm that the user exists and that the appropriate connections have been created as this user in the Environment" -Type warning -RunLocally $RunLocally -LogWarning $true
                                Write-Host $_
                                $ErrorCount++    
                            }
                        }
                    }
                }
            }
            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                     
            }
        }
        else {
            Write-Host @"
No Flows were specified for activation. If you wish to include flows for activation, please add the following in deployPackages.json
"Flows":{
    "ActivateFlows": "true",
    "OverrideFile" : "",
    "FailonError" : "false"
}
"@

        }
    }
}