Summary

Picture an agent called "Research-agent", the same agent from the last artcle, the one with an orchestration layer, a reasoning core, and tool connectors into SharePoint, a SQL database, and the web. By the time it reached production, it was also carrying three separate secrets: one for the SharePoint connector, one for the SQL connector, and one for the GitHub Actions pipeline that redeployed it every time someone pushed a change. None of those secrets was unreasonable on its own. Together they were three places a leak could hand an attacker standing access to everything "Research-agent" touches. This article walks through the four changes that take "Research-agent" from three stored secrets to zero: a managed identity instead of an API key, a federated pipeline instead of a stored secret, a conditional access policy restricting where its service principal can authenticate from, and a validation step that proves, in code, that no key is left anywhere to find.

Managed Identity

The SharePoint connector was the easiest of "Research-agent" three secrets to fix, because managed identity was built for exactly this case. Instead of storing a client secret that lets "Research-agent" authenticate as itself, Azure creates and rotates the credential automatically and ties it to the resource running the code, in this case the Azure Container App hosting its reasoning core. Behind the scenes, Azure registers this managed identity as a service principal in Microsoft Entra ID, so it behaves like any other application identity once a token is issued.

From that point on, nothing in "Research-agent" runtime ever reads a client secret for the SharePoint connector. When the code needs a token, it asks the local Instance Metadata Service instead, at http://169.254.169.254/metadata/identity/oauth2/token, and Azure hands back a token scoped to whatever the managed identity has been granted. There's no string in the container image, the environment variables, or the deployment pipeline that would let someone impersonate "Research-agent" if they got a copy of any of them.

Federated Pipeline

The second secret ran the deployment pipeline itself, the GitHub Actions workflow that rebuilt and redeployed "Research-agent" every time someone pushed to main. That one couldn't use a managed identity, because GitHub's runners aren't Azure resources. It could use federation instead, which solves the same problem a different way. Instead of Azure managing a credential "Research-agent" already had, GitHub issues a short-lived token for the workflow run, and Microsoft Entra ID trusts it without ever seeing a stored secret.

Image describes the Azure workflow for using authentication without a stored secret.
Diagram 1 - Describes the Azure workflow for using authentication without a stored secret.

That subject claim matters more than it looks. It scopes the trust to one repository and one branch, so a fork of "Research-agent" code, or a pull request from a branch that isn't main, can't present a token this trust will accept.

Conditional Access

Managed identity and federation both remove a secret. Neither one restricts where the resulting token can be used from, which is the gap conditional access for workload identities closes. "Research-agent" service principal, the identity Microsoft Entra ID actually issues tokens to, can be restricted to authenticate only from the network locations the workload is expected to run in.

This is a Microsoft Entra ID P2 feature, and it's the only one of the four steps that doesn't remove a credential. It assumes one will eventually leak anyway and limits what that leak is worth. A token stolen from "Research-agent" pipeline is far less useful to an attacker if it only works from the network ranges "Research-agent" already runs in.

Validate

None of the first three steps mean much without a way to prove they worked. The fourth step is a short script that acquires a token the same way "Research-agent" own code does, using the Azure Identity SDK's DefaultAzureCredential, and confirms there's no client secret anywhere in the chain:

Diagram 2 - Describes a short script that acquires a token with no client secret in the chain.
Diagram 2 - Describes a short script that acquires a token with no client secret in the chain.

DefaultAzureCredential tries a sequence of credential types in order, starting with the managed identity attached to the resource it's running on. If that succeeds, as it will for "Research-agent" once the identity is assigned, the SDK never falls back to reading a client secret from an environment variable. Running this script against the deployed agent, and reading its own source for the string client_secret, is a two-minute way to confirm all three original secrets are actually gone, not just unused.

Conclusion

"Research-agent" went from three stored secrets to zero without losing any of the access it actually needs. A managed identity replaces the credential for the SharePoint connector. A federated pipeline replaces the one running its GitHub Actions deployment. Conditional access limits what a token is worth even if one leaks anyway, and a short validation script proves the whole chain actually worked instead of just assuming it did. These four steps applied together, and checked, are the difference between an agent with three places to lose a secret and one with nothing left to steal.