Stored credentials in Power Automate connections are a security liability and an operational time bomb. This lesson shows you how to implement service principal bindings and environment-scoped connection references so your production flows authenticate through Azure managed identities — no passwords, no expiry surprises, and no human identity embedded in your automation.

Here's a scenario that plays out in enterprises everywhere: a critical Power Automate flow stops working at 2 AM. The on-call engineer traces the failure to an expired password — the one belonging to a service account someone set up two years ago, buried in a connection that's been powering seventeen flows across three environments. The fix takes four hours because nobody documented which connections used which account, and rotating the credential means updating connections manually in every environment.
This is the credential sprawl problem, and it's not just an operational nuisance — it's a security liability. Stored credentials in Power Automate connections represent exactly the kind of static secret that attackers hunt for, auditors flag, and security frameworks like Zero Trust explicitly prohibit. When you hard-code a user's identity into a production integration, you've made that account a single point of failure and a permanent attack surface.
Managed identity authentication solves this at the architectural level. Instead of storing credentials that authenticate as a user, you bind your flows to Azure service principals backed by Azure Active Directory managed identities — identities that have no passwords, rotate their own tokens automatically, and can be scoped with precise RBAC permissions. By the end of this lesson, you'll know exactly how to implement this pattern end-to-end, including the service principal setup in Azure, connection reference configuration in Power Platform solutions, and how to promote these bindings cleanly through dev, test, and production environments without ever touching a stored password.
What you'll learn:
You should be comfortable with the following before working through this lesson:
Before we touch any configuration, let's be precise about what we're replacing and why the existing approach breaks down at scale.
When a Power Automate maker creates a standard Azure connector action — say, reading a blob from Azure Storage, posting a message to Service Bus, or querying an Azure SQL database — the platform prompts them to create a connection. That connection stores an OAuth token or username/password combination tied to a specific identity: usually the maker's own account, or if they were diligent, a shared service account.
That connection is then wrapped in a connection reference when the flow is solution-aware, which lets you remap the underlying connection per environment. But here's the critical gap: the connection reference still points to a connection that stores credentials. Those credentials belong to a person or a service account with a rotating password. When that password expires, every flow using that connection fails simultaneously. When that person leaves the organization, the account gets disabled and you're chasing broken connections across your entire environment estate.
The deeper issue is that stored credentials in connections are, by definition, static secrets. They persist in the Power Platform datastore, they appear in connection metadata, and they're visible to anyone with admin access to that environment. This violates the zero-trust principle of using just-in-time, just-enough access with no long-lived secrets.
Key insight
Managed identity authentication doesn't store credentials at all. The service principal authenticates through short-lived tokens issued by Azure AD on demand, using cryptographic certificates that Azure manages and rotates automatically. There is no password to expire, no token to leak, and no human identity to disable.
The architecture we're building uses an Entra ID App Registration to define a service principal identity, grants that identity RBAC permissions on specific Azure resources, and then configures Power Automate connection references to authenticate using that app registration's client ID and certificate (or client secret for lower-security scenarios). For production, we'll use certificate-based authentication.
Start in the Azure portal at Azure Active Directory > App registrations > New registration. The name matters for auditability — use a convention that identifies the system and environment, something like svc-powerautomate-prod-integrations rather than a generic PA Service Account. Set the supported account types to Accounts in this organizational directory only (single tenant). You don't need a redirect URI for this flow type.
After registration, you land on the app's overview page. Copy the Application (client) ID and Directory (tenant) ID — you'll need both when configuring the Power Automate connection.
Navigate to Certificates & secrets > Certificates > Upload certificate. For production, you should be uploading a certificate rather than creating a client secret. Here's why: client secrets are strings that can be copied and exfiltrated. Certificate private keys never leave the certificate store — only the public key is registered with Azure AD, and authentication happens through a cryptographic challenge-response that proves possession of the private key without ever transmitting it.
If you're generating a self-signed certificate for internal use, PowerShell makes this straightforward:
# Generate a self-signed certificate for the service principal
$certName = "svc-powerautomate-prod-integrations"
$cert = New-SelfSignedCertificate `
-Subject "CN=$certName" `
-CertStoreLocation "Cert:\CurrentUser\My" `
-KeyExportPolicy Exportable `
-KeySpec Signature `
-KeyLength 2048 `
-KeyAlgorithm RSA `
-HashAlgorithm SHA256 `
-NotAfter (Get-Date).AddYears(2)
# Export the public certificate (.cer) to upload to Azure AD
Export-Certificate `
-Cert $cert `
-FilePath ".\$certName.cer"
# Export the full certificate with private key (.pfx) for the connection
$password = ConvertTo-SecureString -String "YourSecurePassphrase" -Force -AsPlainText
Export-PfxCertificate `
-Cert $cert `
-FilePath ".\$certName.pfx" `
-Password $password
Upload the .cer file to the app registration. Store the .pfx file and its passphrase in Azure Key Vault — which connects to a broader secrets management strategy covered in Integrating Power Automate with Azure Key Vault and Managed Identities.
Warning
Never store the .pfx file in source control or SharePoint. It goes directly into Key Vault, and access to the Key Vault secret should be restricted to the pipeline identity that needs to retrieve it during deployment — not every maker in your organization.
This step is where precision matters most. The service principal should have the minimum permissions required for the specific connector actions your flows execute — not Contributor on the entire subscription.
For a flow that reads from Azure Blob Storage and writes to Azure Service Bus, you would navigate to each resource and assign roles at the appropriate scope:
Azure Blob Storage — assign on the Storage Account or specific container:
svc-powerautomate-prod-integrationsAzure Service Bus — assign on the namespace or specific queue:
The rationale for scoping at the resource level rather than the subscription is defense-in-depth. If the service principal credentials were somehow compromised, the blast radius is limited to the specific resources you've granted access to, not everything in the subscription.
Tip
Use Azure's Role assignments tab at the subscription level, filtered by your service principal's object ID, to get a consolidated view of every permission that principal holds. Run this audit monthly to catch permission creep before it becomes a governance problem.
With the service principal configured in Azure, you now need to wire it into Power Platform through connection references that are properly scoped per environment.
A connection reference in a Power Platform solution is a named pointer to an underlying connection. The key insight for managed identity patterns is that the connection reference logical name stays the same across all environments, but the underlying connection it points to is different in each environment. In dev, it points to a connection using your dev service principal. In production, it points to a production service principal with tighter permissions.
This is exactly the portability model you want for ALM — your flow logic never hardcodes which connection to use, it references a named connection reference, and environment promotion just means updating what that reference points to. For a deeper treatment of how this promotion works in practice, Deploying and Managing Power Automate Solutions Across Environments walks through the full pipeline mechanics.
In the target environment's Power Automate portal, navigate to Data > Connections > New connection. Search for the connector you're setting up — for example, Azure Blob Storage.
When the authentication dialog appears, look for the option to authenticate using a Service Principal. The exact label varies by connector, but you're looking for fields that ask for:
Note
Not every Azure connector in Power Automate supports service principal authentication today. The ones that do include Azure Blob Storage, Azure SQL Database, Azure Service Bus, Azure Key Vault, Azure AD, and most of the core Azure data connectors. HTTP with Azure AD is the escape hatch when a specific connector doesn't support it natively — you can call any Azure REST API directly using the HTTP connector with Azure AD authentication configured to your service principal. This pattern is covered in depth in Advanced Power Automate: Custom Connectors and HTTP Actions for Production Integration.
After creating the connection, go into your solution and create a Connection Reference that points to it. Navigate to your solution > New > Connection reference, select the connector type, and select the connection you just created. Give the connection reference a logical name that describes its purpose and environment neutrally: cr_azureblobstorage_financialreports rather than cr_azureblobstorage_prod.
For connectors where the resource endpoint itself varies by environment (different storage account names in dev vs. production, different Service Bus namespaces), combine your connection reference with environment variables. The connection reference handles authentication; an environment variable handles the endpoint URL.
In your solution, create a text environment variable named something like ev_blobstorage_accountname. In dev, its value is devfinancialreportssa. In production, it's prodfinancialreportssa. Your flow action reads the account name from the environment variable at runtime, and the connection reference provides the credential binding.
This keeps your flow definition completely environment-agnostic. Neither credentials nor endpoint configuration are hardcoded. This pattern connects to a broader configuration strategy outlined in Defining and Enforcing Environment Variable Strategies in Power Automate Solutions.
This is where managed identity patterns get seriously valuable — and where most teams get it wrong the first time.
For enterprise ALM with managed identity bindings, structure your solution components across three layers:
Layer 1 — Platform Configuration (managed, deployed first): Contains connection reference definitions (not the connections themselves), environment variable definitions (the schema, not the values), and any shared component metadata. This layer is managed and deployed to all environments from your pipeline.
Layer 2 — Environment Configuration (unmanaged or managed with environment-specific overrides): Contains the actual connections and environment variable values for that specific environment. This layer is not deployed through your main ALM pipeline — it's configured in each environment by an admin or through environment-specific pipeline steps. This is where the production service principal connection lives.
Layer 3 — Business Logic (managed, deployed through ALM): Contains your flows, which reference the connection references and environment variables defined in Layer 1. These flows can be promoted through dev, test, and production without modification because they never reference credentials directly.
Key insight
The separation between connection reference definitions (portable, in source control) and connection bindings (environment-specific, configured by admins) is the architectural move that makes managed identity work in ALM. If you conflate these two things, you'll spend every deployment manually remapping connections.
When you run your deployment pipeline (Azure DevOps or GitHub Actions), the pipeline needs to know which connection to bind to each connection reference in the target environment. This is configured in your deploymentSettings.json file, which the Power Platform Build Tools pick up during deployment:
{
"EnvironmentVariables": [
{
"SchemaName": "ev_blobstorage_accountname",
"Value": "prodfinancialreportssa"
},
{
"SchemaName": "ev_servicebus_namespace",
"Value": "prod-integrations-sb"
}
],
"ConnectionReferences": [
{
"LogicalName": "cr_azureblobstorage_financialreports",
"ConnectionId": "/providers/Microsoft.PowerApps/apis/shared_azureblob/connections/prod-blob-svcprincipal-conn",
"ConnectorId": "/providers/Microsoft.PowerApps/apis/shared_azureblob"
},
{
"LogicalName": "cr_servicebus_integrations",
"ConnectionId": "/providers/Microsoft.PowerApps/apis/shared_servicebus/connections/prod-servicebus-svcprincipal-conn",
"ConnectorId": "/providers/Microsoft.PowerApps/apis/shared_servicebus"
}
]
}
The ConnectionId values here reference the production connections that an admin pre-created using the production service principal credentials. Your pipeline doesn't create these connections — it references connections that already exist in the target environment. This is the correct separation: infrastructure (the connections, the service principals, the RBAC assignments) is provisioned by infrastructure processes; application logic (the flows) is deployed by application pipelines.
Tip
Store separate deploymentSettings.json files for each target environment in your repository — deploymentSettings.dev.json, deploymentSettings.test.json, deploymentSettings.prod.json. Each file references the environment-appropriate connection IDs. Your pipeline selects the correct file based on the deployment target stage.
Let's make this concrete with a realistic scenario. You have an existing Power Automate flow called Monthly Financial Report Generator that:
Currently, this flow uses connections tied to a service account svc-reports@contoso.com with a password that expires every 90 days. You're migrating it to managed identity authentication.
Before changing anything, map what you have. In Power Platform Admin Center, navigate to Environments > [Your Production Environment] > Resources > Connections and filter by the service account email. Document every connection, what connector it uses, and which flows reference it.
You can also do this programmatically with the Power Platform admin PowerShell module:
# Get all connections in the production environment
$environmentId = "your-prod-environment-id"
$connections = Get-AdminPowerAppConnection -EnvironmentName $environmentId
# Filter to connections owned by or created with the service account
$serviceAccountConnections = $connections | Where-Object {
$_.CreatedBy.email -eq "svc-reports@contoso.com"
}
# Output connection details
$serviceAccountConnections | Select-Object `
DisplayName, `
ConnectorName, `
ConnectionName, `
@{N="Owner"; E={$_.CreatedBy.email}} | Format-Table -AutoSize
This gives you the full picture before you touch anything. You should also run a flow audit to understand the blast radius — how many flows break if this service account's password rotates today?
Execute the Entra ID app registration steps from earlier, naming the app svc-powerautomate-prod-financial. Assign it:
prodfinancialreportssa storage accountprod-integrations-sb Service Bus queueFor the SQL assignment, you'll do this inside the database using Azure AD authentication:
-- Connect to the target database as an admin
CREATE USER [svc-powerautomate-prod-financial] FROM EXTERNAL PROVIDER;
-- Grant minimum required permissions
ALTER ROLE db_datareader ADD MEMBER [svc-powerautomate-prod-financial];
-- Only add write permissions if the flow needs to write data
-- ALTER ROLE db_datawriter ADD MEMBER [svc-powerautomate-prod-financial];
This grants your service principal read access to the database without giving it server-level permissions or access to other databases on the same server.
In each environment (starting with dev), create new connections for each connector type using the service principal credentials. For Azure Blob Storage:
Navigate to Power Automate portal > Data > Connections > New connection > Azure Blob Storage (Legacy or V2). Select Service Principal authentication. Enter:
your-tenant-idapp-client-id-from-entraName the connection descriptively: Dev - Blob Storage - Financial Reports - SvcPrincipal.
Repeat for Azure SQL Database and Azure Service Bus connectors.
In your development environment, open the solution containing the financial reporting flow. For each connection reference used by the flow, update it to point to the new service principal connection:
Navigate to the solution > select the connection reference > Edit > update the connection binding to the new service principal connection you just created.
After updating all three connection references, run the flow manually from the portal to verify it executes successfully end-to-end. Check each action's output — particularly that the Blob Storage read returns actual data and the Service Bus post completes without a 401 error.
Once dev is validated, repeat the connection creation in test, update connection references in that environment, and run your test suite against it. The flow definition itself hasn't changed — only the connection bindings. This is exactly the kind of change that should be low-risk and fast to validate.
For the production deployment, your pipeline picks up deploymentSettings.prod.json which references the production service principal connections that were pre-created in the production environment. The managed solution deploys, the connection references bind automatically, and the flow runs against Azure resources using the production service principal — with no password, no stored credential, and no human identity involved.
Warning
When you deprecate the old service account connections, don't delete them immediately. Run the new managed identity connections in parallel for at least one full billing cycle (or one full execution of your longest-running scheduled flows) before removing the old connections. Premature deletion is how you create an emergency incident.
Switching to managed identity authentication doesn't mean you can stop auditing — it means your audit surface changes and arguably improves.
With stored credentials, your audit questions were: Who has the service account password? When does it expire? Which connections use it? With managed identities, those questions go away and are replaced by better ones: What RBAC roles does the service principal hold? Who can modify those role assignments? Are the app registration certificate expiry dates tracked?
Navigate to Azure AD > App registrations > svc-powerautomate-prod-financial > Certificates & secrets and check the certificate expiry date. Set a calendar reminder or, better, an Azure Monitor alert that fires 60 days before expiry. Certificate rotation is planned maintenance, not an emergency.
For RBAC auditing, use Azure Policy to enforce that the service principal's role assignments stay within expected boundaries. If someone tries to grant Owner or Contributor to your automation service principal, that should trigger an alert — it means either an error or a potential privilege escalation attempt.
In Power Platform Admin Center, you can view which connections are bound to which connection references, and which flows are running against those connections. For compliance reporting, the Auditing and Governing Power Automate at Scale lesson covers how the CoE Toolkit extracts this metadata at scale.
You should also integrate your flow run logs with Azure Application Insights to capture which service principal identity is being used for each connector action at runtime. The Building a Power Automate Monitoring and Alerting System lesson shows how to push flow telemetry into Application Insights where you can run queries against it.
Note
Azure AD sign-in logs capture every authentication event from your service principal, including which resource was accessed, from what IP address, and whether the token was issued successfully. These logs are searchable in Log Analytics and are invaluable when investigating a security incident or a mysterious 401 error. Make sure they're being retained for at least 90 days (or longer, based on your compliance requirements).
Symptom: The connection appears to save successfully, but flows fail with 401 Unauthorized or AuthorizationFailed errors when they run.
Cause: Some connectors have both a legacy version and a V2 version, and they support service principal authentication differently. The legacy Azure Blob Storage connector may not honor service principal credentials the same way the V2 version does.
Fix: Check the connector version. For Azure Blob Storage, use the Azure Blob Storage (V2) connector. For Azure SQL, ensure you're using Azure AD authentication (not SQL authentication). If a connector genuinely doesn't support service principal auth, use the HTTP connector with Azure AD auth as described in Advanced Power Automate: Custom Connectors and HTTP Actions for Production Integration.
Symptom: The service principal can authenticate (no 401 errors), but gets 403 Forbidden when trying to access a specific resource.
Cause: The RBAC role was assigned at a different scope than expected — for example, assigned at the resource group level but the action targets a specific container within a storage account that has its own access control policy.
Fix: Check the role assignment in Azure Portal at the specific resource level (not the resource group). Use the Check access button on the IAM tab, enter the service principal's object ID, and verify it shows the expected role at the right scope. Also check if the resource has additional access control beyond RBAC — Azure Blob Storage containers can have their own access policies in addition to RBAC.
Symptom: After deploying a solution update through the pipeline, flows start failing because the connection reference is no longer bound to the correct connection.
Cause: When a managed solution is imported with updated components, connection reference bindings can be reset if the deployment pipeline's deploymentSettings.json file doesn't include all connection references. Any reference omitted from the settings file will fall back to default behavior, which may leave it unbound.
Fix: Ensure your deploymentSettings.json includes every connection reference in the solution, not just new ones. Treat the file as a complete specification of the environment's bindings, not an incremental patch.
Symptom: Flow actions that look up Azure AD users or groups fail with permission errors, even though the service principal has the right resource-level RBAC roles.
Cause: Azure AD connector actions (like getting group members or looking up user profiles) require Microsoft Graph API permissions on the app registration, not just Azure resource RBAC roles. These are configured differently — under API permissions on the app registration, not in the Azure resource IAM tab.
Fix: Navigate to the app registration > API permissions > Add a permission > Microsoft Graph > Application permissions. Add the minimum required permissions (e.g., User.Read.All or GroupMember.Read.All). Then click Grant admin consent — application permissions require admin consent before they take effect.
Symptom: Flows that were working fine suddenly start failing with authentication errors, with no code changes deployed.
Cause: The certificate uploaded to the app registration expired. Azure AD stops accepting authentication tokens signed with an expired certificate.
Fix: Rotate the certificate before it expires. Upload a new certificate to the app registration before removing the old one — Azure AD supports multiple active certificates simultaneously for exactly this reason. Update the connection in Power Automate to use the new certificate thumbprint, verify flows are working, then remove the expired certificate from the app registration.
Tip
Add certificate expiry as a monitored metric in your operations runbook. A simple Azure Logic App or Power Automate flow can query the app registration's certificate metadata via Microsoft Graph, compare the expiry dates to today, and send an alert if any certificate expires within 60 days. Automate the reminder so the rotation never catches you off guard.
You've built the complete managed identity authentication pattern for Power Automate: from Entra ID app registration and certificate-based service principal setup, through RBAC scoping at the resource level, to environment-scoped connection reference configuration that promotes cleanly through ALM pipelines. The payoff is a production environment where:
The architectural principles here — separation of connection definitions from connection bindings, least-privilege RBAC scoping, certificate over client secret — compose well with the broader governance and security patterns on this learning path.
Where to go next:
If your flows need to access secrets or certificates dynamically at runtime (rather than through connection bindings), Integrating Power Automate with Azure Key Vault and Managed Identities extends the patterns you've learned here into runtime secret retrieval.
For flows that run as part of a larger enterprise integration architecture, Implementing Event-Driven Automation with Power Automate and Azure Service Bus shows how to apply service principal authentication to high-volume queue-based patterns where the credential story is especially critical.
To complete your ALM picture and understand how the deploymentSettings.json fits into a full CI/CD pipeline, Building CI/CD for Power Automate with Azure DevOps and the Power Platform Build Tools walks through the full pipeline configuration from export to production deployment.
The investment in managed identity authentication pays dividends every time a password rotation doesn't become a midnight incident — which, once you've lived through one of those, is motivation enough.