
Your organization has 400 Canvas apps. You know this because someone ran a Power Platform admin report last Tuesday and the number was sobering. What you don't know is which of those apps are connecting to SharePoint lists containing HR salary data, which ones are pulling from unapproved third-party APIs, or whether any of them are silently forwarding data to personal OneDrive accounts through flows that were built during a well-intentioned hackathon eighteen months ago. This is the governance debt that accumulates when Power Apps adoption outpaces policy.
This is not a theoretical problem. It's the exact situation that enterprises face after three to five years of organic Power Platform growth — what Microsoft sometimes calls "citizen developer sprawl." The platform is brilliantly designed to lower the barrier to app creation, which is precisely what makes it a governance challenge. A single maker with a Power Apps license and a creative weekend can wire together SharePoint, Outlook, Teams, and a random HTTP connector to an external SaaS tool before your IT team knows it happened. Multiply that by hundreds of makers across dozens of departments, and you have a data governance situation that would make your CISO uncomfortable.
By the end of this lesson, you'll understand how to design, implement, and operationalize a governance architecture for Power Apps at enterprise scale. We'll go deep on Data Loss Prevention policies — not just what they do but how they evaluate connectors, how policy layering works, and where the edge cases will bite you. We'll cover connector whitelisting strategies, environment strategies that balance maker autonomy with compliance controls, and how to use the Power Platform admin center, PowerShell, and the admin connector APIs to build automated compliance monitoring. You will walk away with a governance framework you can actually implement.
What you'll learn:
This lesson is written for experienced Power Platform administrators and architects. You should already be comfortable with:
If you're newer to Power Apps administration, work through the earlier modules in this learning path first. Governance architecture without connector fundamentals is just policy theater.
The single most common governance mistake is jumping straight into the Power Platform admin center and starting to move connectors around without understanding the underlying evaluation model. DLP policies are more nuanced than they appear, and the consequences of misunderstanding them range from breaking production apps to creating false confidence in a policy that isn't actually doing what you think.
DLP policies in Power Platform operate at two scope levels: tenant-wide and environment-specific. But the interaction between them is where most administrators get confused.
A tenant-level DLP policy applies to all environments in your tenant by default, but you can exclude specific environments from it. An environment-level DLP policy applies only to a named environment. When both exist, the more restrictive policy wins — but "more restrictive" is determined connector by connector, not policy by policy.
Here's what that actually means in practice. Suppose you have a tenant-level policy that puts SharePoint in the "Business" group and Gmail in the "Non-Business" group. Then you create an environment-level policy for your Development environment that puts both SharePoint and Gmail in the "Business" group (perhaps because your developers legitimately need to test Gmail integration). The effective policy for that environment is:
This surprises people. The environment-level policy cannot override a tenant-level restriction by moving a connector to a less restrictive group. It can only make things more restrictive, not less. This asymmetry is by design — it ensures that tenant admins retain ultimate control — but it means your environment policy is not the full story for any given environment.
Critical Understanding: Environment DLP policies can ADD restrictions but cannot REMOVE restrictions imposed by tenant-level policies. If you're building environment-level policies expecting them to grant connector access that the tenant policy blocks, you're wasting your time and creating a false sense of security.
Power Platform DLP uses three groups:
Business (formerly "Business data only") — Connectors in this group can share data with each other freely. Think of this as your "approved" pool. SharePoint, Dataverse, Azure SQL, and Teams typically live here in a well-governed tenant.
Non-Business (formerly "No business data allowed") — Connectors in this group can share data with each other, but NOT with connectors in the Business group. This is the key point: Non-Business connectors aren't blocked, they're isolated. A maker can still use a Non-Business connector in an app, but they can't combine it with a Business connector in the same app or flow.
Blocked — These connectors cannot be used at all in the policy scope. Period. No app using a blocked connector will save or run successfully within the policy boundary.
The isolation model of the Non-Business group is subtle and important. It means that if you put a personal Gmail connector in Non-Business, a maker can still build an app that reads Gmail. They just can't build an app that reads Gmail AND writes to SharePoint in the same application. This prevents data exfiltration paths — you can't read sensitive data from SharePoint and pipe it to personal Gmail in a single flow — without completely blocking personal email integration for use cases that don't touch sensitive data.
Standard connectors are the ones Microsoft maintains. Custom connectors are the wild west.
Custom connectors are HTTP wrappers that makers build to talk to arbitrary APIs. By default, in environments without specific DLP configuration, custom connectors are allowed and they default to the Non-Business group. That's actually reasonable behavior, but it's not sufficient for enterprise governance.
Starting with the admin center improvements from 2022 onward, you can now apply DLP policy rules to custom connectors by URL pattern matching. You define a pattern like https://api.internalfinancesystem.com/* and assign it to a group. This is powerful but requires ongoing maintenance — every new internal API your development team exposes becomes a governance question.
# Example: Adding a custom connector URL pattern to a DLP policy
# Requires the Microsoft.PowerApps.Administration.PowerShell module
# First, get your policy ID
$policies = Get-AdminDlpPolicy
$targetPolicy = $policies | Where-Object { $PolicyName -eq "Enterprise-Core-DLP" }
# The custom connector configuration requires working with the policy's connector groups
# This is done through the HTTP API since full PowerShell coverage is incomplete
$policyId = $targetPolicy.PolicyName
# Get current custom connector URL patterns
$customConnectorConfig = Get-AdminDlpCustomConnectorUrlPattern -PolicyName $policyId
# Add a new URL pattern for an internal finance API
New-AdminDlpCustomConnectorUrlPattern -PolicyName $policyId `
-ConnectorGroupType "hbi" `
-Rules @{
order = 1
customConnectorRuleClassification = "BusinessData"
pattern = "https://api.financesystem.contoso.com/*"
}
Warning: The
hbi(High Business Impact) group type maps to "Business" in the UI. Microsoft uses internal naming that differs from what you see in the admin center. The mapping is:hbi= Business,lbi= Non-Business,blocked= Blocked. Getting this wrong silently misconfigures your policy.
Before you start classifying connectors, you need a framework for making those decisions. Ad hoc classification leads to inconsistency, and inconsistency leads to either over-blocking (which kills adoption) or under-blocking (which creates real data risk).
Evaluate each connector across three dimensions:
Data Sensitivity Potential: Can this connector access or exfiltrate sensitive business data? A SharePoint connector absolutely can. A Weather service connector almost certainly cannot.
External Egress Risk: Does this connector send data outside your Microsoft 365 boundary? Connectors like Azure SQL (your own tenant) have low egress risk. Connectors like Salesforce, Slack, or arbitrary HTTP endpoints have high egress risk because data leaves Microsoft's infrastructure and goes to a third-party SaaS.
Authentication and Audit Transparency: Does this connector authenticate with your corporate identity (AAD)? Can you audit its usage in your security logs? AAD-authenticated connectors like SharePoint and Teams create audit trails in your Unified Audit Log. Many third-party connectors authenticate with API keys or OAuth to external identity providers — these create audit gaps.
Using these three dimensions, you can create a tiered classification:
| Tier | Business Group | Examples | Rationale |
|---|---|---|---|
| Tier 1 - Core | Business | SharePoint, Dataverse, Teams, Exchange, Azure SQL, Azure Blob | Microsoft-hosted, AAD-auth, full audit |
| Tier 2 - Approved External | Business | Salesforce, ServiceNow, SAP (with justification) | Common enterprise SaaS, vendor agreements |
| Tier 3 - Personal/Consumer | Non-Business | Gmail, Twitter, Dropbox, personal OneDrive | Consumer services, no enterprise controls |
| Tier 4 - Blocked | Blocked | Anonymous HTTP, specific high-risk connectors | No accountability, exfiltration risk |
The HTTP connector (and its sibling, the HTTP with Azure AD connector) is the single most consequential governance decision you'll make. The plain HTTP connector allows a maker to call any arbitrary URL on the internet — no predefined structure, no Microsoft audit trail, unlimited external egress.
If you put HTTP in the Non-Business group, a maker can build a non-business app that exfiltrates data by calling an external API you've never heard of. If you put it in Business, you've essentially put every external API in the same data-sharing group as your SharePoint tenant. If you Block it, you break a significant number of legitimate integration patterns, including many templates from AppSource that use HTTP for real business purposes.
The right answer for most enterprises is to Block the plain HTTP connector at the tenant level and use the HTTP with Azure AD connector in the Business group instead. The HTTP with AAD connector requires the target endpoint to be registered in your Azure AD tenant — it will only work against URLs you explicitly authorize through AAD App Registration. This gives you arbitrary HTTP capability without arbitrary HTTP risk.
# Checking which apps in your tenant use the HTTP connector
# This is critical before blocking it — you need to know the blast radius
$environments = Get-AdminEnvironment
foreach ($env in $environments) {
$apps = Get-AdminApp -EnvironmentName $env.EnvironmentName
foreach ($app in $apps) {
# Get connections used by each app
$appDetail = Get-AdminApp -EnvironmentName $env.EnvironmentName -AppName $app.AppName
# Check if the app uses HTTP connector
if ($appDetail.Internal.properties.connectionReferences) {
$connections = $appDetail.Internal.properties.connectionReferences |
Get-Member -MemberType NoteProperty |
Select-Object -ExpandProperty Name
foreach ($conn in $connections) {
$connRef = $appDetail.Internal.properties.connectionReferences.$conn
if ($connRef.api.name -like "*http*") {
[PSCustomObject]@{
Environment = $env.DisplayName
AppName = $app.DisplayName
ConnectorId = $connRef.api.name
AppOwner = $app.Owner.email
}
}
}
}
}
}
Run this audit before you implement any blocking policy. The output will tell you exactly which makers you need to contact before the policy goes live. Blocking the HTTP connector without this audit is how you become the admin who "broke 47 apps on a Tuesday morning" and then spent two weeks in incident reviews.
DLP policies don't exist in a vacuum. Their effectiveness is entirely determined by how your environment architecture is designed. A perfect DLP policy applied to the wrong environment structure is governance theater.
Most governance frameworks start with the concept of environment types:
Default Environment: Every Microsoft 365 tenant has exactly one default environment, and every licensed user can create apps in it by default. This is the environment that causes the most governance pain because it's open, it's where people experiment, and it tends to accumulate apps like a digital junk drawer.
Your default environment should have the most restrictive DLP policy of any environment. This is counterintuitive — you might think it should be permissive since it's for experimentation — but the default environment is also where the most unsophisticated makers work, and unsophisticated makers are the highest-risk users from a data governance perspective. Restrict the default environment aggressively.
Managed Production Environments: These are named environments with specific purposes — Finance Production, HR Production, Operations Production. Each should have tailored DLP policies that allow exactly the connectors those teams need and nothing else.
Developer/Sandbox Environments: These need more connector flexibility than production. But they should not share data with production systems. Isolate them from production data sources at the connector level.
# Creating a governed environment with proper settings
# This goes beyond DLP — environment settings matter for governance too
New-AdminPowerAppEnvironment `
-DisplayName "Finance-Production" `
-Location "unitedstates" `
-EnvironmentSku "Production" `
-SecurityGroupId "a1b2c3d4-..." ` # AAD group for Finance team
-Description "Finance department production environment - restricted access"
Important: The
-SecurityGroupIdparameter is how you prevent everyone in the organization from creating apps in your managed environments. If you don't set a security group, any licensed user can create apps in any environment they can access. This is the second most common governance failure after DLP misconfiguration.
The default environment requires special attention:
Rename it from "Contoso (default)" to something that signals its restricted nature: "General Use - Basic Connectors Only" or "Unmanaged - Limited Functionality."
Apply a strict tenant-level DLP that covers the default environment, allowing only low-sensitivity connectors: SharePoint (basic lists), Teams, Forms, and approved Microsoft 365 connectors.
Disable Dataverse in the default environment or restrict who can create tables. The default environment does have a Dataverse database, and makers can create tables that accumulate organizational data outside IT oversight.
Block app sharing to "Everyone" at the tenant level. This prevents a maker in the default environment from building a nominally personal app and then sharing it with the entire organization, effectively turning it into an unsanctioned production application.
# Restrict canvas app sharing to everyone
# This is a tenant-wide setting
$requestBody = @{
properties = @{
powerPlatform = @{
powerApps = @{
disableShareWithEveryone = $true
}
}
}
}
Set-TenantSettings -RequestBody $requestBody
Microsoft introduced Managed Environments as a premium governance feature in 2022, and if you're doing enterprise governance, you should be using it. Managed Environments unlock several controls that standard environments don't have:
Weekly digest emails — Admins receive automated reports on app usage, unused apps, and connector usage within the managed environment. This transforms governance from reactive to proactive.
Maker welcome content — You can inject a governance notice into the Power Apps maker studio for your environment, reminding makers of policies before they build.
Solution checker enforcement — You can require that all solutions pass the Solution Checker (which identifies performance and security issues) before they can be deployed to the environment.
Sharing limits — In a Managed Environment, you can cap how broadly a canvas app can be shared — for example, limiting sharing to security groups only, or capping the number of users an app can be shared with.
# Enable Managed Environment on an existing environment
# Requires the tenant admin role
$envId = "your-environment-id"
Enable-AdminManagedEnvironment `
-EnvironmentId $envId `
-MakerWelcomeMarkdownBody "# Welcome to Finance Production
This environment is governed by the Finance IT team.
All apps must comply with the Finance Data Governance Policy.
Contact: finance-it@contoso.com before creating production applications." `
-MakerOnboardingMarkdownUrl "https://intranet.contoso.com/power-platform-governance"
The admin center UI is fine for one or two policies. At enterprise scale, with dozens of environments and multiple policy layers, you need Infrastructure as Code for your DLP policies. PowerShell and the admin APIs are the only way to do this consistently.
# Install the required modules
Install-Module -Name Microsoft.PowerApps.Administration.PowerShell -Force
Install-Module -Name Microsoft.PowerApps.PowerShell -AllowClobber -Force
# Connect with a service principal for automation (preferred over user auth for CI/CD)
# You'll need an AAD app registration with Power Platform admin permissions
$clientId = "your-app-registration-client-id"
$tenantId = "your-tenant-id"
$clientSecret = "your-client-secret" # Use Key Vault in production, not hardcoded
Add-PowerAppsAccount `
-TenantID $tenantId `
-ApplicationId $clientId `
-ClientSecret (ConvertTo-SecureString $clientSecret -AsPlainText -Force)
Security Note: Never hardcode client secrets in scripts that go into source control. Use Azure Key Vault references or pipeline variable groups in your CI/CD system. A governance script with a hardcoded credential is an ironic security problem.
Rather than configuring policies manually, define them in structured data and apply them programmatically. This makes policies reviewable, version-controlled, and auditable.
# Define your connector classifications in a structured format
# This is your "policy as code" — version control this file
$enterpriseCorePolicy = @{
PolicyName = "Enterprise-Core-DLP-v2"
DisplayName = "Enterprise Core DLP Policy - All Environments"
Description = "Baseline DLP policy applied tenant-wide. Version 2.1 - Approved by CISO 2024-01-15"
# Environments to EXCLUDE from this policy (they'll have their own policies)
ExcludedEnvironments = @(
"development-sandbox-env-id",
"powerplatform-coe-env-id"
)
BusinessConnectors = @(
# Microsoft 365 Core
"/providers/Microsoft.PowerApps/apis/shared_sharepointonline",
"/providers/Microsoft.PowerApps/apis/shared_teams",
"/providers/Microsoft.PowerApps/apis/shared_office365",
"/providers/Microsoft.PowerApps/apis/shared_office365users",
"/providers/Microsoft.PowerApps/apis/shared_office365groups",
# Azure Data Services
"/providers/Microsoft.PowerApps/apis/shared_azureblob",
"/providers/Microsoft.PowerApps/apis/shared_sql",
"/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps",
# Approved Enterprise SaaS
"/providers/Microsoft.PowerApps/apis/shared_salesforce",
"/providers/Microsoft.PowerApps/apis/shared_servicenow",
# HTTP with AAD (controlled external calls)
"/providers/Microsoft.PowerApps/apis/shared_webcontents"
)
NonBusinessConnectors = @(
"/providers/Microsoft.PowerApps/apis/shared_gmail",
"/providers/Microsoft.PowerApps/apis/shared_dropbox",
"/providers/Microsoft.PowerApps/apis/shared_twitter",
"/providers/Microsoft.PowerApps/apis/shared_onedriveforbusiness"
# Note: OneDrive for Business in Non-Business is intentional
# It prevents OneDrive from being used as a data bridge
# Teams and SharePoint in Business handle document storage
)
BlockedConnectors = @(
"/providers/Microsoft.PowerApps/apis/shared_http", # Plain HTTP - too dangerous
"/providers/Microsoft.PowerApps/apis/shared_rss",
"/providers/Microsoft.PowerApps/apis/shared_ftp"
)
}
function Apply-DLPPolicy {
param($PolicyConfig)
# Check if policy already exists
$existingPolicies = Get-AdminDlpPolicy
$existing = $existingPolicies | Where-Object { $_.DisplayName -eq $PolicyConfig.DisplayName }
if ($existing) {
Write-Host "Policy exists: $($PolicyConfig.DisplayName). Updating..."
# For updates, you need to rebuild the connector groups object
# The API expects a specific format
} else {
Write-Host "Creating new policy: $($PolicyConfig.DisplayName)"
}
# Build the connector groups structure
$connectorGroups = @(
@{
classification = "BusinessData"
connectors = $PolicyConfig.BusinessConnectors | ForEach-Object {
@{ id = $_; name = ($_ -split "/")[-1] }
}
},
@{
classification = "NoBusinessData"
connectors = $PolicyConfig.NonBusinessConnectors | ForEach-Object {
@{ id = $_; name = ($_ -split "/")[-1] }
}
},
@{
classification = "Blocked"
connectors = $PolicyConfig.BlockedConnectors | ForEach-Object {
@{ id = $_; name = ($_ -split "/")[-1] }
}
}
)
# Create or update via the admin API
New-AdminDlpPolicy `
-DisplayName $PolicyConfig.DisplayName `
-ConnectorGroups $connectorGroups
}
DLP policies prevent new violations, but they don't clean up what already exists. And policies can be circumvented — not always maliciously, but through legitimate exemptions that then never get reviewed, or through environments that were created before policies were applied. Automated compliance monitoring is how you detect drift.
This script is designed to run on a schedule (weekly via Azure Automation or a Logic App) and produce a report that your governance committee can review:
# Enterprise Canvas App Compliance Audit
# Run weekly via Azure Automation Account
# Outputs to SharePoint list for tracking
function Get-ComplianceReport {
$report = @()
$auditDate = Get-Date -Format "yyyy-MM-dd"
# Get all environments
$environments = Get-AdminEnvironment
# Get all DLP policies for reference
$dlpPolicies = Get-AdminDlpPolicy
foreach ($env in $environments) {
$envName = $env.DisplayName
$envId = $env.EnvironmentName
# Check if environment has DLP policy coverage
$applicablePolicies = $dlpPolicies | Where-Object {
# Tenant-wide policies that don't exclude this environment
($_.environments.Count -eq 0 -and
$_.excludedEnvironments -notcontains $envId) -or
# Environment-specific policies
($_.environments -contains $envId)
}
if ($applicablePolicies.Count -eq 0) {
$report += [PSCustomObject]@{
Date = $auditDate
EnvironmentName = $envName
EnvironmentId = $envId
IssueType = "NoDLPCoverage"
Severity = "Critical"
Details = "Environment has no applicable DLP policy"
AppName = "N/A"
AppOwner = "N/A"
}
}
# Get all apps in the environment
$apps = Get-AdminApp -EnvironmentName $envId
foreach ($app in $apps) {
# Check for apps shared with 'Everyone'
$appPermissions = Get-AdminAppRoleAssignment `
-AppName $app.AppName `
-EnvironmentName $envId
$sharedWithAll = $appPermissions | Where-Object {
$_.RoleType -eq "CanView" -and
$_.PrincipalType -eq "Tenant"
}
if ($sharedWithAll) {
$report += [PSCustomObject]@{
Date = $auditDate
EnvironmentName = $envName
EnvironmentId = $envId
IssueType = "SharedWithEntireTenant"
Severity = "High"
Details = "Canvas app shared with all users in tenant"
AppName = $app.DisplayName
AppOwner = $app.Owner.email
}
}
# Check for orphaned apps (owner no longer in organization)
# This requires checking AAD for the owner's account status
$ownerEmail = $app.Owner.email
try {
$ownerUser = Get-AzADUser -UserPrincipalName $ownerEmail -ErrorAction Stop
if ($ownerUser.AccountEnabled -eq $false) {
$report += [PSCustomObject]@{
Date = $auditDate
EnvironmentName = $envName
EnvironmentId = $envId
IssueType = "OrphanedApp"
Severity = "Medium"
Details = "App owner account is disabled in AAD"
AppName = $app.DisplayName
AppOwner = $ownerEmail
}
}
} catch {
$report += [PSCustomObject]@{
Date = $auditDate
EnvironmentName = $envName
EnvironmentId = $envId
IssueType = "OrphanedApp"
Severity = "High"
Details = "App owner account not found in AAD"
AppName = $app.DisplayName
AppOwner = $ownerEmail
}
}
}
}
return $report
}
# Run the audit and export
$complianceReport = Get-ComplianceReport
# Export to CSV for governance committee
$complianceReport | Export-Csv -Path "PowerApps-Compliance-$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation
# Get critical issues for immediate alerting
$criticalIssues = $complianceReport | Where-Object { $_.Severity -eq "Critical" }
if ($criticalIssues.Count -gt 0) {
Write-Host "ALERT: $($criticalIssues.Count) critical compliance issues found"
# In production, send this via email or Teams notification
}
Before you build all of this from scratch, you should know that Microsoft's Center of Excellence Starter Kit implements much of this compliance monitoring for you. The CoE kit includes:
The CoE Starter Kit is not a replacement for custom governance work — it's a starting point. But implementing it saves you four to six weeks of custom development, and you can extend it with your own compliance logic on top of the inventory foundation it provides.
Architecture Advice: Deploy the CoE Starter Kit into a dedicated "CoE" environment that is excluded from your production DLP policies (the kit needs to use connectors that might be restricted elsewhere) and governed separately by the platform admin team. Don't let CoE components sprawl into your general-purpose environments.
No governance framework survives contact with reality without an exemption process. The question is whether your exemption process is thoughtful or whether it's a loophole that swallows the policy.
The worst exemption pattern is creating a "permissive" environment and putting people there when they ask for more connectors. This pattern is tempting because it's easy — just say "go build in the Dev environment, it has all the connectors." But it means:
Instead, implement connector exemptions at the policy level with a formal review process:
Step 1: Maker submits an exemption request via a Canvas app (you should absolutely build this in Power Apps — it's a great demonstration of the platform). The request captures: which connector they need, which environment, what business use case, what data will be accessed, and who approved the use case.
Step 2: Security review looks at the connector's authentication model, data egress implications, and whether the use case has a business justification.
Step 3: If approved, create a new environment-specific DLP policy that allows the connector, scoped to the specific environment for that team. Document the exemption in your governance register with a review date.
Step 4: Quarterly review of all exemptions to verify they're still needed and that the apps using them are still active.
# Creating a targeted environment DLP override for a specific exemption
# Scenario: Marketing team approved to use MailChimp connector in Marketing-Production env
$exemptionEnvironmentId = "marketing-production-env-id"
$exemptionConnector = "/providers/Microsoft.PowerApps/apis/shared_mailchimp"
$exemptionJustification = "Marketing automation use case, approved by CISO 2024-03-01, review due 2024-09-01"
# Get the existing environment policy or create a new one
# Note: This ADDS to the Business group for this environment
# The tenant policy still controls all other connectors
New-AdminDlpPolicy `
-DisplayName "Marketing-Production-MailChimp-Exemption" `
-PolicyScope "SingleEnvironment" `
-EnvironmentIds @($exemptionEnvironmentId) `
-ConnectorGroups @(
@{
classification = "BusinessData"
connectors = @(
@{ id = $exemptionConnector; name = "shared_mailchimp" }
)
}
)
# Log the exemption to your governance register
# In practice, this would write to a Dataverse table or SharePoint list
Write-Host "Exemption created: MailChimp connector enabled in Marketing-Production"
Write-Host "Justification: $exemptionJustification"
Let's put this together with a structured exercise you can actually run against your tenant.
You're the Power Platform admin for Contoso Corporation. You have 200+ employees and a growing Power Apps deployment. You need to implement a governance baseline that:
Step 1: Inventory your current state
Before touching any policies, run this inventory:
# Connect to your tenant
Add-PowerAppsAccount
# Export current environment inventory
$environments = Get-AdminEnvironment
$environments | Select-Object DisplayName, EnvironmentName, EnvironmentType, CreatedTime, CreatedBy |
Export-Csv "environment-inventory.csv" -NoTypeInformation
# Export existing DLP policies
$policies = Get-AdminDlpPolicy
Write-Host "Current DLP Policies:"
$policies | Select-Object DisplayName, CreatedTime | Format-Table
# Count apps per environment
foreach ($env in $environments) {
$appCount = (Get-AdminApp -EnvironmentName $env.EnvironmentName).Count
Write-Host "$($env.DisplayName): $appCount apps"
}
Step 2: Identify your default environment
$defaultEnv = $environments | Where-Object { $_.IsDefault -eq $true }
Write-Host "Default Environment: $($defaultEnv.DisplayName)"
Write-Host "Environment ID: $($defaultEnv.EnvironmentName)"
# Check current DLP coverage for default environment
# Look for policies that apply to it
Step 3: Design your policy structure on paper before implementing
Draw out (literally, on paper or in a diagram tool):
This design phase is where most governance projects fail. Rushing to implementation without the architecture documented leads to policies that conflict, gaps that persist, and a governance program that's impossible to explain to a new admin six months later.
Step 4: Implement your tenant-level baseline policy
Using the PowerShell patterns from earlier in this lesson, create your tenant-wide policy. Start with a generous Business group that includes all connectors your current apps use, then progressively restrict over 30 days as you validate no production apps break.
Step 5: Run a 30-day monitoring period
After implementing policies, monitor the compliance report weekly. Track:
Symptom: You apply a new tenant DLP policy and 30 apps stop working the next morning.
Solution: Always run the connector usage inventory first (the PowerShell script shown earlier). Cross-reference your intended policy against the connectors actually in use. For any connector you're planning to block or move to Non-Business, identify every app using it and notify the owners at least two weeks in advance.
DLP policies apply to both Canvas apps AND Power Automate flows. But the behavior differs. A flow that violates DLP policy will have its HTTP calls blocked, but the flow itself doesn't fail immediately — it depends on when the DLP check triggers. Always test policy changes with both apps and flows in your staging environment.
When apps use service principal connections (common in enterprise scenarios where you want a non-user-specific connection to SharePoint or SQL), those connections may bypass certain DLP checks that rely on user identity. Service principal connections should be audited separately and treated as high-privilege credentials.
# Finding service principal connections in your environments
$environments = Get-AdminEnvironment
foreach ($env in $environments) {
$connections = Get-AdminConnection -EnvironmentName $env.EnvironmentName
$spConnections = $connections | Where-Object {
$_.CreatedBy.id -ne $_.Properties.authenticatedUser.id -or
$_.Properties.connectionParametersSet.name -like "*service*principal*"
}
if ($spConnections.Count -gt 0) {
Write-Host "Service Principal connections in $($env.DisplayName):"
$spConnections | Select-Object DisplayName, ConnectorName, CreatedBy
}
}
DLP controls which connectors can be combined. Environment access controls determine who can create apps at all. These are different control planes and both are required. A DLP policy alone doesn't prevent an unauthorized user from creating an app in your production environment — only security group membership on the environment does that.
The CoE Starter Kit environment needs broad connector access to inventory your tenant. It's often set up with minimal DLP controls, which is appropriate for its function. But then it's forgotten in governance reviews. Your CoE environment should be explicitly documented as an exempted environment with a named admin owner, not just quietly excluded from policies.
This usually has one of three causes:
The app was last saved before the policy went live. DLP is checked at save time and at connection establishment. If the app already has an established connection to the blocked connector, it may continue to function until the connection is refreshed.
The environment has an environment-level policy that moves the connector to Business before the tenant policy can block it. Remember: environment policies can't override tenant blocking, BUT if the connector doesn't appear in the blocked connector list of the tenant policy (only in a non-tenant policy you thought was tenant-wide), it won't be blocked.
You're checking the wrong environment. Confirm which environment the app lives in and which policies actually apply to that environment.
# Diagnostic: Show all policies that apply to a specific environment
function Get-EffectivePoliciesForEnvironment {
param([string]$EnvironmentId)
$allPolicies = Get-AdminDlpPolicy
$applicablePolicies = @()
foreach ($policy in $allPolicies) {
# Check if it's tenant-wide and doesn't exclude this environment
if ($policy.environments.Count -eq 0) {
if ($policy.excludedEnvironments -notcontains $EnvironmentId) {
$applicablePolicies += $policy
}
}
# Check if it explicitly targets this environment
elseif ($policy.environments -contains $EnvironmentId) {
$applicablePolicies += $policy
}
}
return $applicablePolicies
}
$effectivePolicies = Get-EffectivePoliciesForEnvironment -EnvironmentId "your-env-id"
Write-Host "Policies applying to this environment: $($effectivePolicies.Count)"
$effectivePolicies | Select-Object DisplayName, CreatedTime | Format-Table
You've covered a lot of ground. Let's consolidate the architecture thinking before you walk away.
Enterprise Power Apps governance rests on four pillars that must work together:
DLP Policies — Your connector-level data boundaries. Design them hierarchically: a restrictive tenant-wide baseline, with environment-specific policies that add further restriction (never relaxation) for specialized environments. Get your connector classification right using the risk dimensioning framework, handle the HTTP connector with particular care, and manage custom connectors through URL pattern matching.
Environment Architecture — The structural container that DLP lives in. Harden the default environment aggressively. Create purpose-specific managed environments for production workloads. Use security groups to control who can create apps in each environment. Without good environment architecture, your DLP policies are protecting the wrong things.
Automated Monitoring — Governance is not a one-time implementation; it's an ongoing operational practice. Weekly compliance audits that surface orphaned apps, DLP gaps, and over-shared applications keep you ahead of drift. The CoE Starter Kit gives you a strong foundation; extend it with custom compliance logic for your specific requirements.
Exemption Process — Acknowledge that your policies will need exceptions and build a structured, audited process for handling them. The goal is not zero exemptions; it's zero unreviewed exemptions.
Run the inventory scripts in this lesson against your own tenant this week. You cannot govern what you haven't measured. The output will tell you where your risk actually lives.
Map your environments to the three-tier model. Which environments are production? Which are uncontrolled? Which have no DLP coverage at all?
Deploy the CoE Starter Kit to a dedicated environment if you haven't already. It takes a full day to set up properly, but it's the foundation for everything else.
Design your connector classification scheme using the risk dimensioning framework before touching any policy settings.
Build your exemption process before you need it. Governance programs fail when legitimate maker needs have no legitimate path to resolution.
From here, explore the Power Platform admin center's Analytics section to understand maker behavior and app usage patterns — this behavioral data informs your governance decisions better than any framework document. Also look at Azure Policy integration for governing the Azure resources that your Power Platform environments depend on, which is the next layer of the governance stack above what we've covered here.
Governance at scale is never finished. It's a continuous operational practice, and the organizations that do it well are the ones that treat it as a living program rather than a one-time implementation project.