---
title: "Azure Human Approvals: Stop the Agent Before the Tool Fires"
description: "Build a real human approval gate in Microsoft Foundry Agent Service that suspends the run, verifies an Entra ID approver, and logs each decision."
canonical: "https://adamtheautomator.com/azure-agent-approval-gate/"
---

# Azure Human Approvals: Stop the Agent Before the Tool Fires

> Build a real human approval gate in Microsoft Foundry Agent Service that suspends the run, verifies an Entra ID approver, and logs each decision.

Source: https://adamtheautomator.com/azure-agent-approval-gate/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![Azure Human Approvals: Stop the Agent Before the Tool Fires](https://adamtheautomator.com/wp-content/uploads/publisher/4065d9c85b2b810f8ec2f16d43234514/30a1ebd88fe7d0a348fc74f3679ed0a07cd493411310d679fd9886586ec027dd.webp)

# Azure Human Approvals: Stop the Agent Before the Tool Fires

[![](https://secure.gravatar.com/avatar/d0b9d42e21e5622713f8b693aa5c0f9244d5f7dd200ed29b8398f52dee5de337?s=192&d=mm&r=g)Adam Bertram](https://adamtheautomator.com/author/adam-bertram/)10 November 202614 min. read

Categories: [AI Agents](/category/ai-agents/)

Tags:[Azure](/tag/azure/)[AI](/tag/ai/)[Security](/tag/security/)[Microsoft Entra](/tag/microsoft-entra/)

Table of Contents

*   [What a Human Approval Gate Has to Prove](#what-a-human-approval-gate-has-to-prove)
*   [The Three Tests](#the-three-tests)
*   [Prerequisites That Decide Whether the Gate Is Real](#prerequisites-that-decide-whether-the-gate-is-real)
*   [Turn Off Key Authentication First](#turn-off-key-authentication-first)
*   [Assign the Roles the Workflow Actually Uses](#assign-the-roles-the-workflow-actually-uses)
*   [Wrap the Tool the Model Cannot Call Alone](#wrap-the-tool-the-model-cannot-call-alone)
*   [Pause a Hosted Agent Without Losing the Thread](#pause-a-hosted-agent-without-losing-the-thread)
*   [What Survives a Restart](#what-survives-a-restart)
*   [Hand the Request to a Person](#hand-the-request-to-a-person)
*   [Decide Who May Call the Resume Endpoint](#decide-who-may-call-the-resume-endpoint)
*   [The Delegated Approval Gap](#the-delegated-approval-gap)
*   [Time Out the Pause Nobody Answers](#time-out-the-pause-nobody-answers)
*   [Retention Limits That End the Wait](#retention-limits-that-end-the-wait)
*   [Concurrency and Batch Resumes](#concurrency-and-batch-resumes)
*   [Build the Audit Record the Platform Leaves to You](#build-the-audit-record-the-platform-leaves-to-you)
*   [Write the Decision Record at Resume Time](#write-the-decision-record-at-resume-time)
*   [Test the Gate Before You Trust It](#test-the-gate-before-you-trust-it)
*   [Make the Approval Defensible](#make-the-approval-defensible)

Three teams can each say their Azure agentic workflow requires human approval and mean three different mechanisms. One has an adaptive card in Teams that no workflow is waiting on. One has a boolean in a request payload that the calling service fills in. One has a run that stops before a tool call executes and resumes only when an identified person makes a decision. Only the third is an approval gate, and the property that separates it from the other two is whether the run is genuinely suspended.

This guide builds that third kind on the [Microsoft Foundry Agent Service](https://learn.microsoft.com/en-us/azure/foundry/agents/overview) (formerly Azure AI Foundry Agent Service). It is written against the Microsoft Learn Foundry Agent Service and Agent Framework documentation as read on November 9, 2026. You’ll set the identity prerequisites, wrap a tool so the framework returns an approval request instead of calling it, resume the suspended chain with the approver’s decision, then add the timeout and the decision record the platform leaves to your application. Order matters, because a gate built before the identity work is one that anyone holding the endpoint key can pass.

## What a Human Approval Gate Has to Prove

A tool call is where an agent stops talking and starts changing something. A refund posts, a firewall rule opens. The gate belongs between the model’s decision to call that tool and the execution of the call, which rules out any design where the run answers the user and finishes its work in the background.

### The Three Tests

The difference between a gate and a notification is what you can prove about the moment of the call.

*   Did the run stop before the tool executed, and stay stopped across container restarts?
    
*   Was the stop attached to a named person with an Entra ID identity rather than a shared key?
    
*   Does a record show what that person saw before approving?
    

Microsoft’s procedure for [adding a human-in-the-loop approval step](https://learn.microsoft.com/en-us/azure/foundry/agents/how-to/add-human-in-the-loop) builds the first answer into the hosted agent runtime. Microsoft Learn states: “A long-running hosted agent can pause indefinitely for that reply without holding a request open or losing its place, because a multi-turn chain persists between turns.”

![Approval gate architecture](https://adamtheautomator.com/wp-content/uploads/publisher/0dbfaa24b25301d90748351c9d751fa9ca4665856296b51d66cb79580223496a.jpg)

Foundry has no approver role and no built-in approval record, so two of the three requirements (an approval tied to a named Entra ID identity, and a record of what that person saw) live in your application code. That is where most of the work in this guide goes.

## Prerequisites That Decide Whether the Gate Is Real

Start with authentication, because every later control assumes the caller is a known identity. Microsoft’s [role-based access control documentation for Foundry](https://learn.microsoft.com/en-us/azure/foundry/concepts/rbac-foundry) makes the consequence plain. “RBAC roles apply when you authenticate using Microsoft Entra ID. If you use key-based authentication instead, the key grants full access without role restrictions.”

A resume endpoint that accepts a shared key lets anyone who finds that key approve a refund. The gate only holds when the caller’s identity is checked.

### Turn Off Key Authentication First

The [disable local authentication guidance](https://learn.microsoft.com/en-us/azure/ai-services/disable-local-auth) covers every documented way to close the key path, from a subscription-wide policy down to a single resource:

*   A subscription or resource group policy named **Azure AI Services resources should have key access disabled (disable local authentication)**.
    
*   The `disableLocalAuth: true` property in a Bicep or ARM template.
    
*   `Set-AzCognitiveServicesAccount -DisableLocalAuth $true` for a single resource.
    

The change lands on the control plane immediately, but the gateway that enforces authentication can keep accepting previously valid keys until its cached configuration refreshes, typically for minutes and sometimes for hours. The documented test is to send a data-plane request with the old key and confirm an HTTP 401 with “Access denied due to invalid subscription key or wrong API endpoint” before treating the change as complete. A rollback is not instant either, for the same caching reason.

### Assign the Roles the Workflow Actually Uses

The minimum documented assignment is **Foundry User** on the Foundry resource for the approver’s user principal and the project’s managed identity. Anyone who needs to see quota also needs **Cognitive Services Usages Reader** at subscription scope. Both assignments are added automatically only when the project is created through the Foundry portal. Create the project from the CLI or an SDK and you make them yourself.

```bash
az role assignment create \
  --assignee "<approver-object-id>" \
  --role "Foundry User" \
  --scope "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.CognitiveServices/accounts/<account>"
```

Resist the urge to hand the resume service **Foundry Owner** because the first assignment came back wrong. **Foundry Agent Consumer** exists for a principal that only interacts with agent endpoints, and it can be scoped to one agent through that agent’s resource URI. Assignment at agent scope is evaluated only for agent endpoint access, so that scope cannot give an approver visibility into the project.

* * *

_**Warning: A role assignment that looks correct on the project can still be missing on the resource for the project’s managed identity. Check both scopes before you test the approval path.**_

* * *

## Wrap the Tool the Model Cannot Call Alone

Tool-level approval fits when one function carries the risk and the rest of the agent is harmless. The [Agent Framework tool approval guidance](https://learn.microsoft.com/en-us/agent-framework/agents/tools/tool-approval) handles it at the function boundary. In Python you set an approval mode on the decorator:

```python
from agent_framework import tool

@tool(approval_mode="always_require")
def execute_refund(transaction_id: str, amount_cents: int) -> str:
    """Issue a refund against a settled transaction."""
    return f"refund issued for {transaction_id}"
```

In C# you wrap the function object instead.

```text
AIFunction refund = AIFunctionFactory.Create(ExecuteRefund);
AIFunction gatedRefund = new ApprovalRequiredAIFunction(refund);
```

Pass the wrapped function into the agent’s tool list and the run changes shape. In .NET the run ends without a final answer and returns `ToolApprovalRequestContent` instances, and the decision goes back through `CreateResponse(bool)`: `true` authorizes the call and `false` refuses it. Your application reads each request’s tool call to get the function name and the exact arguments the model intended to pass, which is what the approver needs to see.

In Python the request arrives as a `Content` item whose `type` is `function_approval_request`, with the call and its arguments on `.function_call`. Answer it with `request.to_function_approval_response(True)`, or `False` to refuse, and send the returned `function_approval_response` item back inside a `Message(role="user", contents=[...])` on the same `AgentSession`. A handler that resumes on a fresh session loses the pending tool call, so the model either asks again or reconstructs arguments from conversation text. Keep the session object stored with the approval record.

* * *

_**Pro Tip: Send the approver the arguments, not a summary of them. The tool call carries the amount, the account, and the target, and those values are what the signature covers.**_

* * *

## Pause a Hosted Agent Without Losing the Thread

When the whole agent needs to stop, including the reasoning around several tool calls, use the hosted agent’s multi-turn task chain. A `@multi_turn_task` keeps the execution context alive between turns under one `task_id`, and the [long-running agent API reference](https://learn.microsoft.com/en-us/azure/foundry/agents/concepts/long-running-agent-reference) documents the task context fields, including `entry_mode` and its `fresh`, `resumed`, and `recovered` values.

Durable tasks are opt-in. Call `set_resilient_tasks_enabled(True)` at import time, which runs before host startup and lets the host construct its task manager. Declaring a `@multi_turn_task` handler does not enable the subsystem, and `approve.run()` raises `TaskManagerNotInitialized` until that call runs.

```python
from azure.ai.agentserver.core.tasks import (
    multi_turn_task,
    set_resilient_tasks_enabled,
    TaskContext,
)
from azure.ai.agentserver.core.storage import FoundryStateStore

set_resilient_tasks_enabled(True)

@multi_turn_task(name="refund-approval")
async def approve(ctx: TaskContext[dict]) -> dict:
    store = await FoundryStateStore.get_or_create(f"workflows/{ctx.task_id}")
    if ctx.entry_mode == "resumed":
        decision = ctx.input["decision"]
        if decision == "approved":
            item = await store.get_item("approval-metadata")
            await issue_refund(item.value["transaction_id"])
            return {"status": "refunded"}
        return {"status": "declined"}

    transaction = await load_transaction(ctx.input["transaction_id"])
    await store.set_item("approval-metadata", {"transaction_id": transaction.id})
    return {"status": "awaiting_approval", "summary": transaction.summary}
```

The first turn writes the transaction ID into the task’s own state store item and returns. On `azure-ai-agentserver-core` 2.3.0 the `TaskContext` object exposes `task_id`, `input`, `entry_mode`, `retry_attempt`, and the cancellation hooks, with no metadata mapping. The linked API reference documents a `metadata` field (a `TaskMetadata` mapping), but no published build of `azure-ai-agentserver-core` exposes it: 2.3.0 and the 2.3.1b1 prerelease both lack the attribute. The watermark therefore has to live in `workflows/<task_id>`, and `ctx.metadata["transaction_id"]` raises `AttributeError` on the first turn. Microsoft Learn’s human-in-the-loop sample still carries that `ctx.metadata` line, so the sample inherits a documentation defect. The chain moves to a suspended state instead of ending. When a person finally decides, your application calls `approve.run(task_id="refund-7821", input={"decision": "approved"})` and the handler re-enters with `ctx.entry_mode` set to `"resumed"` and reads the item back before it acts.

### What Survives a Restart

Compute does not survive, so nothing important may live in memory. The [durable state store for hosted agents](https://learn.microsoft.com/en-us/azure/foundry/agents/concepts/agent-state-store) persists JSON across container evictions, with two limits that shape design. Each item is capped at 1 MB, and the store’s idle window removes items after 30 days by default. Writes renew that window and reads do not, so a review that only gets polled will still expire.

Keep full requests, transcripts, and generated artifacts in Blob storage or Cosmos DB, with a reference in the state store. A suspended chain that resumes into an empty checkpoint is worse than a rejected approval, because the run continues with no memory of why it stopped.

## Hand the Request to a Person

A gate needs a place where the decision happens, and for most Azure shops that place is Teams. [Automating Foundry agents with Logic Apps](https://learn.microsoft.com/en-us/azure/logic-apps/automate-foundry-agents-with-workflows) is the supported route, because the Foundry Agent Service connector is native to Logic Apps. Microsoft publishes that connector beyond Logic Apps as well. The [connector reference](https://learn.microsoft.com/en-us/connectors/azureagentservice/) lists it for Copilot Studio, Logic Apps, Power Apps, and Power Automate, all Standard class and in commercial regions only. The connector is in Preview, so its support terms are those of a preview service. Logic Apps stays the route here, because the flow needs a managed identity and a workflow that can wait on an approval output.

A Logic App watches for the approval output, builds an adaptive card, and posts it to the approver’s channel or direct message. The card earns its place when it carries the proposed action in plain text, the arguments the model intended, links to the source records, and three actions rather than two. Approve and reject cover the easy cases. Request changes stops an approver from rejecting a good action over a bad parameter, which sends the agent back to planning instead of ending the workflow.

When the approver submits, the Logic App calls the resume path with the `task_id` and the decision. That call needs the same identity check as any other resume, so give the Logic App a [managed identity](https://adamtheautomator.com/secure-azure-managed-identities/) with **Foundry Agent Consumer** on the agent rather than a service principal secret in a connection.

## Decide Who May Call the Resume Endpoint

Approval is an application action: your handler is re-entered by calling `approve.run` with a task ID and a decision payload. No Azure role states who may approve which action, so the entitlement check belongs in your resume endpoint.

| Participant | Role and scope | What it permits |
| --- | --- | --- |
| Approver | Foundry User on the Foundry resource | Read the project and the run under review |
| Resume service | Foundry Agent Consumer at agent scope | Call this one agent’s endpoint |
| Project administrator | Foundry Project Manager | Manage projects and assign Foundry User |

![Approver identity checks](https://adamtheautomator.com/wp-content/uploads/publisher/66646f0b475114fda48029109f9aef4c2c7d68f07573de142b935bea80fb1bd7.jpg)

### The Delegated Approval Gap

Whether a second person can approve a run that someone else started is the open question in this design. A [Microsoft Q&A thread on shared approval with the same execution context](https://learn.microsoft.com/en-us/answers/questions/5962649/azure-ai-foundry-workflow-hitl-shared-approval-wit) asks exactly that and does not settle on a built-in mechanism, and the published approval procedure demonstrates the same caller resuming the run it started. Treat cross-user approval as a feature you build and test until Microsoft documents one.

The [agent identity documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/concepts/agent-identity) adds a constraint that catches people out. Under delegated access the agent acts for the signed-in user, so both the agent’s delegated permissions and that user’s access to the target resource have to hold at execution time. A validation that runs once at approval time will pass and then fail when the tool fires.

```python
async def resume_with_decision(task_id: str, approver, decision: str):
    requester_id = await load_requester(task_id)
    if approver.object_id == requester_id:
        raise PermissionError("requester cannot approve their own action")
    if not await is_authorized_approver(approver, task_id):
        raise PermissionError("caller is not an approver for this workflow")
    return await approve.run(
        task_id=task_id,
        input={"decision": decision, "approver": approver.object_id},
    )
```

The `resume_with_decision` function is application code, not an SDK call. Writing the requesting identity into the same `workflows/<task_id>` state store item on the first turn is what makes the comparison possible later.

## Time Out the Pause Nobody Answers

A suspended multi-turn chain stays active until your application deletes it or its retention period expires. One-shot task records clean themselves up when they finish, and the chain a person never answers is the one your application still has to delete.

### Retention Limits That End the Wait

The 15-minute idle timeout is the shortest clock in an approval’s life. Compute is deprovisioned, and state is restored on the next request, so an approver who opens the card the next morning still has a run to resume.

| What expires | Default | Effect on a slow approval |
| --- | --- | --- |
| Hosted agent session idle timeout | 15 minutes, configurable from 2 to 240 | Compute is deprovisioned and state is restored on the next request |
| Session deletion | 30 days of inactivity | The run cannot be resumed at all |
| State store item | 30 days idle unless configured otherwise | The checkpoint is gone even if the session survives |
| Suspended approval chain | Until the application deletes it, or its retention period expires | Your application still has to delete chains whose decisions were abandoned |

The idle timeout only ends compute; it leaves the approval waiting. The runtime keeps a chain active until it reaches that retention period, and the checkpoint that lets it resume lives in the state store, so the record and the checkpoint are both yours to end. Set the store’s item lifetime when the store is created, because the service ignores the option on a store that already exists:

```python
from azure.ai.agentserver.core.storage import FoundryStateStore

# 14 days in seconds, applied only on first creation
store = await FoundryStateStore.get_or_create(
    f"workflows/{task_id}",
    item_ttl_seconds=1209600,
)
```

For the chain that has to go now, delete its state by hand. The state-store CLI pages through the stores your agent created and deletes an item permanently:

```bash
azd ai agent state-stores list
azd ai agent state-stores items list --store "workflows/refund-7821"
azd ai agent state-stores items delete "approval" --store "workflows/refund-7821" --yes --no-prompt
```

List again after the delete. An item that survives the command is a chain whose next resume still finds its checkpoint.

### Concurrency and Batch Resumes

Foundry applies a per-subscription, per-region limit on concurrent hosted agent sessions of 2,000 in Canada Central, East US 2, Japan East, North Central US, South Africa North, Southeast Asia, and Sweden Central, and 1,000 in other regions. An idle session does not count against the limit, but resuming one needs available quota, and a batch of approvals cleared at the end of a shift is exactly the pattern that consumes it. The [limits, quotas, and regional support page](https://learn.microsoft.com/en-us/azure/foundry/agents/concepts/limits-quotas-regions) is the reference to plan against. Agent Service enforces limits at two levels: its own agent and session limits, and the model deployment’s rate limits. Overrunning the concurrent session quota returns HTTP 429 with `session_quota_exceeded`, or `regional_session_quota_exceeded` when regional capacity is the constraint, while model deployment throttling returns 429 `rate_limit_exceeded`. Backoff with jitter clears the regional-capacity and throttling cases; a `session_quota_exceeded` overrun is your own subscription’s session count, so the fix is to stop sessions that don’t need active compute, delete sessions whose state is no longer needed, or request a limit increase.

Define an approval SLA with a named escalation path, and delete chains whose decisions were abandoned. An approval that nobody owns is an orphaned run holding a quota slot.

## Build the Audit Record the Platform Leaves to You

Microsoft’s [diagnostic logging for Foundry](https://learn.microsoft.com/en-us/azure/foundry/how-to/diagnostic-logging) gives you the platform half of the evidence. Turn on **Audit Logs**, **Request and Response Logs**, **Azure OpenAI Request Usage**, and **AllMetrics**, and send them to both a storage account and Log Analytics. Records land as JSON blobs in per-category containers named `insights-logs-<category>`, organized by resource ID and timestamp.

Foundry’s diagnostic logs can take up to two hours to become available for query, so a control that reads its own audit output minutes after an approval reads nothing. And the task record is deliberately small, holding an ID and a step number, with the request, history, and generated artifacts left in your own storage.

The approval record therefore has to be assembled in your application. These are the fields worth capturing at the moment of decision.

| Field | Where it comes from | What it answers |
| --- | --- | --- |
| Trace ID | Generated at run start | Ties the prompt, tool calls, and decision together |
| Task ID | The suspended chain | Identifies which run resumed |
| Requester object ID | State store item from the first turn | Who asked for the action |
| Approver object ID | The validated Entra ID caller | Who authorized it |
| Tool name and arguments | The approval request payload | What was actually approved |
| Decision and timestamp | Resume input | When and how it was answered |

### Write the Decision Record at Resume Time

Write the trace ID, task ID, approver, decision, and timestamp in one record at resume time. The state store is a durable home for that record, and `create_item` writes an append-only key, so a retried resume cannot overwrite the record the first attempt wrote:

```python
from azure.ai.agentserver.core.storage import FoundryStateStore

async def write_decision_record(task_id: str, record: dict) -> None:
    store = await FoundryStateStore.get_or_create(f"records/{task_id}")
    await store.create_item(f"decisions/{record['trace_id']}", record)
```

Read the item back once the resume returns. A record whose trace ID matches the run is the one an auditor can follow to the executed call, and an auditor who has to reconstruct that from three systems will ask for it again next year.

## Test the Gate Before You Trust It

Run these checks in a test subscription before an approval gate guards anything real. Each one fails quietly in production if you skip it.

1.  Send a request with the old key after disabling local authentication, and expect the [documented 401 response](https://learn.microsoft.com/en-us/azure/ai-services/disable-local-auth) instead of a successful call. Repeat the check after the refresh window rather than five minutes later.
    
2.  Give a test account no Foundry User assignment and confirm it cannot open the run under review. A gate that only hides a button still allows the API call.
    
3.  Resume the same `task_id` twice with the same decision and confirm the refund posts once. Idempotency lives in your tool, and the platform will happily resume a chain twice.
    
4.  Approve something older than the retention window and confirm the failure is legible. An empty checkpoint should produce a clear error, not a partial run.
    
5.  Resume fifty approvals at once and watch for 429 responses, then check that the backoff path actually waits.
    

* * *

_**Warning: Test the rejection path with the same care as the approval path. A refusal that the model reads as a retryable error will send the same tool call back for approval in a loop.**_

* * *

## Make the Approval Defensible

Of the three designs (a Teams card that no workflow waits on, a boolean in the request payload, and a run suspended before the tool executes), only the suspended run lets a team answer an auditor’s question about a specific refund. That team can name the person who approved it, show the arguments that person saw, and produce the timestamp and trace ID that connect the decision to the executed call. The other two teams have a card and a payload field, and neither survives a follow-up question.

Building that gate takes four pieces of work. Turn off key authentication, assign Foundry User and Foundry Agent Consumer to the right principals, wrap the risky tool or suspend the hosted run so it can resume, then write the timeout and the decision record yourself. The last two are the ones Microsoft’s documentation leaves open, so design them before the first approval request reaches a person. Retrofitting an audit trail onto a run that already fired means reassembling the decision from the application record and logs that can run two hours behind.

Microsoft’s [resilience guidance for long-running hosted agents](https://learn.microsoft.com/en-us/azure/foundry/agents/concepts/long-running-agent-resilience) is the next document to read if your approvals routinely outlive a session.

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fazure-agent-approval-gate%2F&text=Azure%20Human%20Approvals%3A%20Stop%20the%20Agent%20Before%20the%20Tool%20Fires)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fazure-agent-approval-gate%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fazure-agent-approval-gate%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/publisher/4065d9c85b2b81bf9605cb37d967f788/a9812525518a5125a43d2341858253da973f79467a0eba39ef631295b5a8807b.png)

### [Entra Agent IDs: How to Stop AI Privilege Escalation](/entra-agent-id-privilege-escalation/)

Map the four objects behind a Microsoft Entra Agent ID, follow four documented paths to tenant control, and apply eight controls in risk order.

![](https://adamtheautomator.com/wp-content/uploads/publisher/4065d9c85b2b81cb8cd5e63895d6d603/a290484691de461d47cf56c4c5e9178e3574a1bff6e7fc0a813e3b45039f936a.webp)

### [5 Ways to Stop Azure AI Agents from Blowing Your Budget](/azure-ai-agent-budget-guardrails/)

Cap Azure AI agent spend with an API Management token limit, Application Insights telemetry, semantic caching, loop caps, and budget alerts.

![](https://adamtheautomator.com/wp-content/uploads/publisher/4065d9c85b2b81caa866d0f71bf0002b/66051cfb556b73d2e1c3b14eb006283e65cc77f3781dce13b5a0a0ec58ca99f0.webp)

### [Eliminate Azure DevOps Secrets with Workload Identity](/azure-devops-workload-identity/)

Retire Azure DevOps client secrets and PATs for workload identity federation, then prove a stolen credential fails before production cutover.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
