다음을 통해 공유


데이터 정책에서 차단된 Power Automate 흐름 식별

커넥터 작업 제어의 일부로 트리거를 포함하는 등 새로운 기능이 출시됨에 따라 기존 데이터 정책을 위반하는 흐름을 신속하게 식별하는 것이 중요해질 수 있습니다. 다음 스크립트는 검토를 위해 이러한 흐름의 인벤토리를 식별하고 제공하는 데 도움이 됩니다. 또한 스크립트는 런타임에 실패하지 않도록 흐름에 대한 정책을 업데이트하는 데 도움이 될 수 있습니다.

차단된 트리거가 있는 재고 흐름

다음 PowerShell 스크립트를 사용하여 영향을 받는 흐름 CSV 파일을 만들 수 있습니다. 테넌트의 모든 데이터 정책을 쿼리하여 범위 내에 있는 적용 가능한 환경을 찾습니다. 모든 환경을 재귀하고 현재 차단된 트리거 사용으로 인해 해당 정책을 위반하는 Power Automate 흐름을 찾습니다.

중요

다음 스크립트를 실행하려면 Power Platform 관리자여야 합니다. 관리자는 테넌트의 모든 정책, 환경 및 흐름에 액세스할 수 있습니다.

# Prompt user to capture TenantId
$TenantId = Read-Host "Please provide your Microsoft Entra TenantId Guid"
if($TenantId -eq "")
{
    Write-Host "TenantId is required. Exiting..." -ForegroundColor Red
    Exit;
}

function Is-Guid {
    param (
        [string]$Guid
    )
    try {
        [Guid]::Parse($Guid) | Out-Null
        return $true
    } catch {
        return $false
    }
}

if(Is-Guid -Guid $TenantId)
{
    ##We can proceed
}
else
{
    Write-Host "TenantId is not a valid Guid format. Exiting..." -ForegroundColor Red
    Exit;
}

# Load Power Platform Administration PowerShell module
Import-Module Microsoft.PowerApps.Administration.PowerShell -ErrorAction Stop
Import-Module Microsoft.PowerApps.PowerShell -ErrorAction Stop

Add-PowerAppsAccount

# Initialize output
$output = @()

Write-Host "Fetching all data policies in the tenant..." -ForegroundColor Cyan

try {
    # Fetch all data policies
    $dlpPolicies = Get-AdminDlpPolicy
    $totalPolicies = $dlpPolicies.Count
    if($totalPolicies -eq $null)
    {
        $totalPolicies = 1
    }

    Write-Host "Done. Fetching all Environments in the tenant..." -ForegroundColor Cyan
    $allEnvironments = Get-AdminPowerAppEnvironment
    Write-Host "Done."
} catch {
    Write-Host "Failed to fetch data policies: $_" -ForegroundColor Red
    exit
}

$policyIndex = 0
foreach ($policy in $dlpPolicies) {

$policyIndex++
Write-Progress -Activity "Processing Policies" -Status "Processing $policyIndex of $totalPolicies" -PercentComplete (($policyIndex / $totalPolicies) * 100)

    try {
        Write-Host "Processing Data Policy: $($policy.DisplayName)" -ForegroundColor Cyan

        # Initialize actionBlockedConnectorIds
        $actionBlockedConnectorIds = @()

        # Retrieve connector configurations for the policy
        $connectorConfigurations = Get-PowerAppDlpPolicyConnectorConfigurations -TenantId $TenantId -PolicyName $policy.PolicyName

        if (-not $connectorConfigurations.connectorActionConfigurations) {
            Write-Host "Skipping policy $($policy.DisplayName) - No action control policy settings defined." -ForegroundColor Gray
            continue
        }

        foreach ($connectorConfig in $connectorConfigurations.connectorActionConfigurations) {
            $connectorId = $connectorConfig.connectorId

            # Check if the connector has actions blocked by default
            if ($connectorConfig.defaultConnectorActionRuleBehavior -eq 'Block') {
                Write-Host "Connector with default blocked actions found: $connectorId in Policy $($policy.DisplayName)" -ForegroundColor Yellow

                $actionBlockedConnectorIds += $connectorId
            }
        }

        # Determine environments impacted by the policy
        Write-Host "Determining environments impacted by the policy..." -ForegroundColor Cyan
        $environments = @()

        switch ($policy.FilterType) {
            "" {
                $environments = $allEnvironments
            }
            "include" {
                $environments = $policy.Environments | ForEach-Object { $_.name }
            }
            "exclude" {
                $policyEnvironmentNames = $policy.Environments | ForEach-Object { $_.name }
                $environments = $allEnvironments | Where-Object { $_.EnvironmentName -notin $policyEnvironmentNames }
            }
        }

        $environmentIndex = 0
        $environmentCount = $environments.Count
        if($environmentCount -eq $null)
        {
            $environmentCount = 1
        }
        foreach ($environment in $environments) {
            $environmentIndex++
            Write-Progress -Activity "Processing Environments" -Status "Environment $environmentIndex of $environmentCount" -PercentComplete (($environmentIndex / $environmentCount) * 100)

            $environmentId = if ($environment -is [string]) { $environment } else { $environment.EnvironmentName }

            $environmentDetails = Get-AdminPowerAppEnvironment -EnvironmentName $environmentId
            Write-Host "Processing environment: $environmentId - " + $environmentDetails.DisplayName -ForegroundColor Green

            # Capture Flow Management API URI
            $flowBaseUri = $environmentDetails.Internal.properties.runtimeEndpoints.'microsoft.Flow'

            # Fetch flows in the environment
            try {
                $flowsUri = $flowBaseUri + '/providers/Microsoft.ProcessSimple/scopes/admin/environments/' + $environmentId + '/v2/flows'
                $flowsOutput = InvokeAPi -Method Get -Route $flowsUri -Body ""
                        
            } catch {
                Write-Host "Failed to fetch flows for environment $environmentId : $_" -ForegroundColor Red
                continue
            }

            $flows = $flowsOutput
            $flowIndex = 0
            $flowsCount = $flows.value.Count
            if($flowsCount -eq $null)
            {
                $flowsCount = 1
            }
            foreach ($flow in $flows.value) {
                $flowIndex++
                Write-Progress -Activity "Processing Flows" -Status "Flow $flowIndex of $flowsCount " -PercentComplete (($flowIndex / $flowsCount) * 100)

                # Check if flow's trigger uses the restricted connector
                Write-Host "Flow display name: " $flow.properties.displayName 
                Write-Host "Flow status: " $flow.properties.state
                        
                $flowDetailsUri = $flowBaseUri + '/providers/Microsoft.ProcessSimple/scopes/admin/environments/' + $environmentId + '/flows/' + $flow.name
                $flowDetailsOutput = InvokeAPi -Method Get -Route $flowDetailsUri -Body ""

                $HasBlockedTriggers = "No"
                foreach($trig in $flowDetailsOutput.properties.definitionSummary.triggers)
                {
                    Write-Host $trig.type $trig.swaggerOperationId $trig.api
                    if($trig.api.id -in $actionBlockedConnectorIds)
                    {
                        Write-Host "Flow using restricted trigger connector found: $($flow.properties.displayName)" -ForegroundColor Yellow
                        $output += [PSCustomObject]@{
                            FlowId            = $flow.name
                            FlowName          = $flow.properties.displayName
                            EnvironmentId     = $environmentId
                            PolicyId          = $policy.PolicyName
                            ConnectorId       = $trig.api.id
                            TriggerOperationId = $trig.swaggerOperationId
                            PolicyUrl         = "https://admin.powerplatform.microsoft.com/policies/dlp/policyId/$($policy.PolicyName)"
                        }
                        $HasBlockedTriggers = "Yes"
                    }

                }

                if($HasBlockedTriggers -eq "No")
                {
                    Write-Host "No blocked triggers found."
                }
            }
        }
    } catch {
        Write-Host "Error processing policy $($policy.DisplayName): $_" -ForegroundColor Red
    }
}

# Final Output
Write-Host "`nResults Summary:" -ForegroundColor Cyan
$output | Format-Table -AutoSize

if($output.Count -gt 0)
{
    # Prompt user to save output as CSV
    $saveCsv = Read-Host "Do you want to save the results to ImpactedFlows.csv? (Y/N)"
    if ($saveCsv -eq 'Y') {
        $output | Export-Csv -Path "$(Get-Location)\ImpactedFlows.csv" -NoTypeInformation -Force
        Write-Host "Results saved to ImpactedFlows.csv" -ForegroundColor Green
    } else {
        Write-Host "Results not saved." -ForegroundColor Yellow
    }
}
else
{
    Write-Host "No triggers were found in violation of your data policies.  Please let Microsoft know via Support Ticket that you would like to be removed from runtime enforcement exclusion for data policies."
}

# Instructions for deep linking
Write-Host "`nTo review a data policy, navigate to:" -ForegroundColor Cyan
Write-Host "https://admin.powerplatform.microsoft.com/policies/dlp/policyId/{PolicyId}" -ForegroundColor Green
Write-Host "You may need to adjust the PPAC Admin URL for GCC, GCCH, Mooncake, DoD, and other clouds." -ForegroundColor Cyan

커넥터 작업 제어에 트리거를 포함하도록 정책을 업데이트합니다.

다음 PowerShell 스크립트를 사용하면 차단 위험이 있는 트리거를 포함하도록 데이터 정책을 업데이트할 수 있습니다. 이렇게 하면 이전 스크립트의 출력 영향을 받는 흐름 CSV 파일이 입력으로 사용됩니다. 이전 섹션에서 식별된 모든 트리거를 사용하지 않으려면 이 스크립트를 실행하기 전에 CSV 파일에서 해당 트리거를 제거할 수 있습니다.

# Prompt user to capture TenantId
$TenantId = Read-Host "Please provide your Microsoft Entra TenantId Guid"
if($TenantId -eq "")
{
    Write-Host "TenantId is required. Exiting..." -ForegroundColor Red
    Exit;
}

function Is-Guid {
    param (
        [string]$Guid
    )
    try {
        [Guid]::Parse($Guid) | Out-Null
        return $true
    } catch {
        return $false
    }
}

if (-not (Is-Guid -Guid $TenantId)) {
    Write-Host "TenantId is not a valid Guid format. Exiting..." -ForegroundColor Red
    Exit;
}

# Load Power Platform Administration PowerShell module
Import-Module Microsoft.PowerApps.Administration.PowerShell -ErrorAction Stop
Import-Module Microsoft.PowerApps.PowerShell -ErrorAction Stop

Add-PowerAppsAccount

# Prompt for CSV file path
$csvPath = Read-Host "Enter the path to ImpactedFlows.csv"
if (-not (Test-Path $csvPath)) {
    Write-Host "File not found at $csvPath. Exiting..." -ForegroundColor Red
    Exit;
}

# Import the CSV data
$impactedFlows = Import-Csv -Path $csvPath

# Group by PolicyId for efficient updates
$groupedPolicies = $impactedFlows | Group-Object -Property PolicyId

foreach ($policyGroup in $groupedPolicies) {
    $policyId = $policyGroup.Name
    $flowsInPolicy = $policyGroup.Group
    $policyNeedsUpdated = $false

    Write-Host "Processing Data Policy: $policyId" -ForegroundColor Cyan

    # Get existing connector configurations for the policy
    try {
        $connectorConfigurations = Get-PowerAppDlpPolicyConnectorConfigurations -TenantId $TenantId -PolicyName $policyId
    } catch {
        Write-Host "Failed to retrieve connector configurations for policy $policyId : $_" -ForegroundColor Red
        continue
    }

    foreach ($flow in $flowsInPolicy) {
        $connectorId = $flow.ConnectorId
        $triggerOperationId = $flow.TriggerOperationId

        # Check if the connector already has configurations
        $existingConfig = $connectorConfigurations.connectorActionConfigurations | Where-Object { $_.connectorId -eq $connectorId }

        if ($existingConfig) {

            # Check if the triggerOperationId already exists in the actionRules array
            $triggerExists = $existingConfig.actionRules | Where-Object { $_.actionId -eq $triggerOperationId } 
            if($triggerExists -ne $null)
            {
                Write-Host "TriggerOperationId $triggerOperationId already exists for connector $connectorId in policy $policyId" -ForegroundColor Yellow
            } else {
                # Add the new triggerOperationId to the existing configuration
                $triggerRule = [pscustomobject]@{
                    actionId = $triggerOperationId
                    behavior = "Allow"
                }
                $existingConfig.actionRules+= $triggerRule
                $policyNeedsUpdated = $true
                
                Write-Host "Adding TriggerOperationId $triggerOperationId to connector $connectorId in policy $policyId" -ForegroundColor Green
            }
        }
    }

    if($policyNeedsUpdated)
    {
        # Update the data policy with modified configurations
        try {
            Set-PowerAppDlpPolicyConnectorConfigurations -TenantId $TenantId -PolicyName $policyId -UpdatedConnectorConfigurations $connectorConfigurations
            Write-Host "Successfully updated Data Policy: $policyId" -ForegroundColor Cyan
        } catch {
            Write-Host "Failed to update Data Policy $policyId : $_" -ForegroundColor Red
        }
    }
}

Write-Host "All impacted flows processed and data policies updated." -ForegroundColor Cyan