Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
161218 stories
·
33 followers

Microsoft Foundry Observability: How to Trace, Evaluate, Monitor, and Secure AI Agents

1 Share

The green check that hid a real problem

During a recent customer conversation, I was shown a customer-service refund agent that looked healthy by every traditional measure. Requests completed successfully. The endpoint returned HTTP 200. Latency was within the team's target, and there were no obvious exceptions in the application logs.

Then a customer asked a simple thing:

Please refund the duplicate $500 charge on invoice 83457.

The agent processed the refund and confirmed success. There was only one problem: in the failure case explored in this article, the agent calls the correct tool with the wrong parameter—refunding $5,000 instead of $500.

From the application's perspective, the request succeeded. From the business's perspective, the agent failed.

That moment changed the direction of our discussion. We were no longer asking, "Is the endpoint available?" We were asking:

  • Which tool did the agent call, and with what arguments?
  • Was the refund amount correct?
  • Did the agent honor the approval threshold?
  • How many other customers were affected?
  • How would we prove that the correction actually worked?

This is the gap that AI observability must close.

Traditional monitoring tells us whether a system is operating. Agent observability helps us determine whether the system is behaving as intended—and whether that behavior produces a trustworthy business outcome.

Below screenshot captures successful but incorrect action by agent. Later in this article, we will build this agent step by step and explore how observability helps us identify the failure.

Observability must extend beyond logs, metrics, and traces

Logs, metrics, and traces remain essential, but an AI application introduces another dimension: the application can be technically healthy and semantically wrong.

I frame this as five layers of AI observability. A refund that returns HTTP 200 can still fail on the layers that matter:

LayerQuestionRefund-scenario result
1. TechnicalIs it available, fast, healthy, and affordable?Pass — HTTP 200, normal latency, zero exceptions
2. Agent executionWhat did the agent actually do?Fail — wrong parameter passed to the tool
3. Safety / policyWas the behavior permitted?Fail — approval threshold bypassed
4. QualityWas the action correct?Fail — task executed incorrectly
5. BusinessDid it achieve the outcome?Fail — financial loss

These layers are related, but they are not interchangeable. A response can have low latency and high fluency while still passing the wrong amount to a payment tool. Traditional APM only covers Layer 1.

The Microsoft Foundry observability landscape

For this implementation, Microsoft Foundry was the development and evaluation experience, while Azure Monitor Application Insights became the telemetry store and investigation surface.

The responsibilities were intentionally separated:

  • Microsoft Foundry Traces gave the development team an ordered view of the agent run.
  • Foundry Evaluation measured quality, safety, and agent behavior.
  • The Foundry Monitor experience showed operational and evaluation trends for the deployed agent.
  • Application Insights Agent Observability connected runs, models, tools, token usage, latency, and failures.
  • Log Analytics supported customer-specific questions with Kusto Query Language.
  • Azure Monitor alerts turned important signals into an operational response.

OpenTelemetry provides the connective tissue. It gives us a standard trace model for agent, model, tool, and custom application spans instead of restricting observability to one application framework.

Step 1: Prepare the project

The sample uses Python 3.10 or later, a Microsoft Foundry project, a deployed model, and an Application Insights resource connected to the project. It does not require a preexisting repository or any additional source files. Create a new empty folder and run every snippet in this article in order.

Create and enter the working folder:

mkdir foundry-observability-demo cd foundry-observability-demo python -m venv .venv

Activate the virtual environment on Windows PowerShell:

.\.venv\Scripts\Activate.ps1

On macOS or Linux, use:

source .venv/bin/activate

Install the packages used by the article:

python -m pip install --upgrade pip python -m pip install \ "azure-ai-projects>=2.0.0" \ azure-identity \ azure-monitor-opentelemetry \ azure-core-tracing-opentelemetry \ opentelemetry-sdk \ python-dotenv

For local development, authenticate with the Azure CLI:

az login

Create a local .env file. Copy the endpoint and deployment name from the Foundry project rather than hard-coding them in source control.

FOUNDRY_ENDPOINT=<copy-from-the-Foundry-project-overview> FOUNDRY_MODEL=gpt-4o AZURE_TENANT_ID=<your-tenant-id> APPINSIGHTS_CONNECTION_STRING=<copy-from-Project-Tracing-Manage-data-source> LOG_ANALYTICS_WORKSPACE_ID=<workspace-guid>

Run Python from foundry-observability-demo. The following code loads .env from that folder and validates every value required by the later snippets

Load and validate the configuration:

import os from dotenv import load_dotenv # Load .env from the directory where Python or Jupyter was started. if not load_dotenv(dotenv_path=".env"): raise FileNotFoundError( "No .env file was found. Create it in the current working directory." ) # Validate the variables required by the article. required_variables = [ "FOUNDRY_ENDPOINT", "AZURE_TENANT_ID", "APPINSIGHTS_CONNECTION_STRING", ] missing_variables = [ name for name in required_variables if not os.environ.get(name) ] if missing_variables: raise ValueError( "Missing required .env values: " + ", ".join(missing_variables) ) foundry_endpoint = os.environ.get("FOUNDRY_ENDPOINT") tenant_id = os.environ.get("AZURE_TENANT_ID") model_deployment = os.environ.get("FOUNDRY_MODEL", "gpt-4o") app_insights_conn = os.environ.get("APPINSIGHTS_CONNECTION_STRING") log_analytics_workspace_id = os.environ.get("LOG_ANALYTICS_WORKSPACE_ID") print(f" Foundry endpoint : {foundry_endpoint[:50]}...") print(f"Model deployment : {model_deployment}") print(f"Tenant ID : {tenant_id[:8]}...") print(" Application Insights connection string loaded") print( " Log Analytics workspace ID loaded" if log_analytics_workspace_id else " LOG_ANALYTICS_WORKSPACE_ID is optional until the KQL section" )

Initialize the clients:

from azure.identity import AzureCliCredential from azure.ai.projects import AIProjectClient credential = AzureCliCredential(tenant_id=tenant_id) project_client = AIProjectClient(endpoint=foundry_endpoint, credential=credential) openai_client = project_client.get_openai_client() print(" AIProjectClient initialized") print("OpenAI client ready")

The identity running the sample needs permission to use the Foundry project. To query the resulting telemetry, it also needs appropriate access to Application Insights and its Log Analytics workspace. In production, assign access through Microsoft Entra groups and least-privilege roles.

Step 2: Create the refund agent and observe what it actually did

The agent is a function-tool agent named refund-agent-observability-demo. It exposes a single process_refund tool. The instructions are intentionally ambiguous about amount handling to demonstrate how an agent can misinterpret a parameter.

import json from azure.ai.projects.models import PromptAgentDefinition, FunctionTool, Tool # Define the refund tool process_refund_tool = FunctionTool( name="process_refund", description="Process a refund for a customer invoice. Returns confirmation with refund details.", parameters={ "type": "object", "properties": { "invoice_id": { "type": "string", "description": "The invoice ID to refund" }, "amount": { "type": "number", "description": "The refund amount in dollars" } }, "required": ["invoice_id", "amount"], "additionalProperties": False, }, strict=True, ) tools: list[Tool] = [process_refund_tool] print(" Refund tool defined") print(f" Tool: process_refund(invoice_id, amount)") print(f" Tool: process_refund(invoice_id, amount)") # Create the refund agent # NOTE: The instructions are intentionally ambiguous about amount handling # to demonstrate how agents can misinterpret parameters agent = project_client.agents.create_version( agent_name="refund-agent-observability-demo", definition=PromptAgentDefinition( model=model_deployment, instructions="""You are a customer service agent that processes refund requests. When a customer asks for a refund, use the process_refund tool. Extract the invoice ID and amount from the customer's message. Always confirm the refund was processed successfully. """, tools=tools, ), ) print(f" Agent created: {agent.name} (version {agent.version})")

Now send a refund request and inspect exactly what the agent proposed. The "Traditional Monitoring View" stays green while we independently check the tool argument:

# Send a refund request user_message = "Please refund the duplicate $500 charge on invoice 83457." print(f" User: {user_message}") print("─" * 60) response = openai_client.responses.create( model=model_deployment, instructions=agent.definition.instructions, tools=[{ "type": "function", "name": "process_refund", "description": "Process a refund for a customer invoice.", "parameters": { "type": "object", "properties": { "invoice_id": {"type": "string", "description": "The invoice ID"}, "amount": {"type": "number", "description": "The refund amount in dollars"} }, "required": ["invoice_id", "amount"], "additionalProperties": False, }, "strict": True, }], input=user_message, ) # Inspect what the agent did print("\n Traditional Monitoring View:") print(f" HTTP Status: 200") print(f" Exception Count: 0") print(f" Response Time: ~2-4s") print(f" Status: SUCCESS") print("\n Agent Output:") for item in response.output: if item.type == "function_call": args = json.loads(item.arguments) print(f"\n Tool Called: {item.name}") print(f" Invoice ID: {args.get('invoice_id')}") print(f" Amount: ${args.get('amount')}") # Check if the amount is correct expected_amount = 500.0 actual_amount = args.get('amount', 0) if actual_amount != expected_amount: print(f"\n PARAMETER ERROR DETECTED!") print(f" Expected: ${expected_amount}") print(f" Actual: ${actual_amount}") print(f" Loss: ${abs(actual_amount - expected_amount)}") else: print(f"\n Parameters correct") elif item.type == "message": print(f" Response: {item.content[0].text if item.content else 'N/A'}")

This is the whole point of observability: the HTTP status, exception count, and latency all look healthy, while the argument check is the only thing that reveals whether the refund amount is right.

The all-green moment:

Step 3: Classify the failure across the five layers

When the amount is wrong, the workshop classifies the same interaction across all five layers—so the "green check" and the real failure sit side by side:

# Visualize the five-layer assessment print("═" * 65) print(" FIVE-LAYER AI OBSERVABILITY ASSESSME

# Visualize the five-layer assessment print("═" * 65) print(" FIVE-LAYER AI OBSERVABILITY ASSESSMENT") print("═" * 65) print() layers = [ ("1. Technical", "PASS", "HTTP 200, 0 errors, 2.8s latency"), ("2. Agent Execution", "FAIL", "Wrong parameter: $5000 instead of $500"), ("3. Safety & Policy", "FAIL", "Approval threshold bypassed"), ("4. Quality", "FAIL", "Task executed incorrectly"), ("5. Business", "FAIL", "$4,500 financial loss"), ] for layer, status, detail in layers: print(f" {layer:<22} {status:<10} {detail}") print() print("─" * 65) print(" Traditional APM verdict: ALL GREEN") print(" AI Observability verdict: CRITICAL FAILURE") print("─" * 65)

Traditional monitoring only covers Layer 1. The remaining sections show how to detect, evaluate, monitor, and prevent the other four.

Step 4: Turn on tracing and read the run as a story

Server-side traces become available after Application Insights is connected to the project. Client-side tracing adds visibility into the application logic around the agent call. The workshop enables both with a few lines: it turns on content recording, configures Azure Monitor, and gets a tracer.

# Enable experimental GenAI tracing and content capture before instrumentation. os.environ["AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING"] = "true" os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = "true" from azure.ai.projects.telemetry import AIProjectInstrumentor from azure.monitor.opentelemetry import configure_azure_monitor from opentelemetry import trace configure_azure_monitor(connection_string=app_insights_conn) AIProjectInstrumentor().instrument( enable_content_recording=True, enable_trace_context_propagation=True, enable_baggage_propagation=False, ) tracer = trace.get_tracer("foundry-observability-blog") print("Azure Monitor configured") print("Foundry GenAI instrumentation enabled") print("Content recording enabled") print("Tracer initialized")

Wrapping an agent interaction in a custom parent span ties the model call and each tool call into one ordered trace. Here the workshop wraps a customer-service session, dispatches the tool the model requested, and returns the result with previous_response_id so the follow-up model call joins the same conversation:

from openai.types.responses.response_input_param import FunctionCallOutput # Run the agent with tracing with tracer.start_as_current_span("customer_service_session") as session_span: session_span.set_attribute("session.type", "customer_inquiry") session_span.set_attribute("customer.segment", "premium") # Initial model call response = openai_client.responses.create( model=model_deployment, instructions=agent.definition.instructions, tools=[{ "type": "function", "name": t.name, "description": t.description, "parameters": t.parameters, "strict": True, } for t in tools], input=user_message, ) # Process function calls, return tool output, then continue the conversation input_items = [] for item in response.output: if item.type == "function_call": result = {"status": "processed", "arguments": json.loads(item.arguments)} input_items.append( FunctionCallOutput( type="function_call_output", call_id=item.call_id, output=json.dumps(result), ) ) if input_items: final_response = openai_client.responses.create( model=model_deployment, instructions=agent.definition.instructions, input=input_items, previous_response_id=response.id, ) # Record the trace ID for investigation span_context = session_span.get_span_context() trace_id = format(span_context.trace_id, '032x') print(f" Trace ID: {trace_id}") print(f" View in Application Insights → Transaction Search → {trace_id}")

The resulting trace reads as a sequence, not a pile of logs:

Trace anatomy: 

Graph view of Conversation id: 

 End-to-end investigation in Application Insight

Application Insight- Agents (Preview)

Agents Preview — Models & Tools

Application Insight captures the platform where the agent lives (foundry,copilot studio etc)

Application Insight - failures

Step 5: Ask custom questions with KQL

The portal is an excellent starting point, but customers eventually ask questions specific to their business. The workshop's cookbook targets a Log Analytics workspace and uses the workspace tables (AppDependencies, AppEvents). The legacy aliases (dependencies, customEvents) only resolve in an Application Insights resource query context—use the schema that matches your query scope.

Tool failure rate:

AppDependencies | where DependencyType == "GenAI" | where Name == "ExecuteTool" | summarize Total = count(), Failed = countif(Success == false), FailPct = round(100.0 * countif(Success == false) / count(), 2) by bin(TimeGenerated, 1h) | render timechart

Token consumption over time:

AzureMetrics | where MetricName in ("InputTokens", "OutputTokens", "TotalTokens") | summarize Tokens = sum(Total) by MetricName, bin(TimeGenerated, 1h) | render timechart

Follow one trace end to end:

// Replace <operation-id> with the actual OperationId let TraceId = "<operation-id>"; AppDependencies | where OperationId == TraceId | project TimeGenerated, Name, DurationMs, Success, ResultCode, OperationId, ParentId, Properties | order by TimeGenerated asc

// Replace <operation-id> with the actual OperationId let TraceId = "<operation-id>"; AppDependencies | where OperationId == TraceId | project TimeGenerated, Name, DurationMs, Success, ResultCode, OperationId, ParentId, Properties | order by TimeGenerated asc

Correlate human/evaluation feedback with responses:

AppEvents | where Name == "gen_ai.evaluation.result" | extend responseId = tostring(Properties["gen_ai.response.id"]), score = todouble(Properties["gen_ai.evaluation.score.value"]), label = tostring(Properties["gen_ai.evaluation.score.label"]), source = tostring(Properties["microsoft.gen_ai.human_evaluation.source"]) | project TimeGenerated, responseId, score, label, source | order by TimeGenerated desc

AppEvents | where Name == "gen_ai.evaluation.result" | extend responseId = tostring(Properties["gen_ai.response.id"]), score = todouble(Properties["gen_ai.evaluation.score.value"]), label = tostring(Properties["gen_ai.evaluation.score.label"]), source = tostring(Properties["microsoft.gen_ai.human_evaluation.source"]) | project TimeGenerated, responseId, score, label, source | order by TimeGenerated desc

Telemetry schemas continue to evolve. Before using a query in production, inspect a representative span and confirm the table and attribute names emitted by the SDK version in use.

Custom KQL:

Step 6: Evaluate the process — did the agent pass the right parameters?

Tracing explains how an action happened. Evaluation measures whether it was correct, consistently. For a refund agent, the most direct check is process evaluation: did it select the right tool and pass the right arguments?

The following example runs builtin.tool_call_accuracy over a set of banking scenarios—including a process_refund case—using the Evals API:

import json # Banking tool definitions banking_tools = [ { "type": "function", "name": "get_account_balance", "description": "Retrieve the current balance for a customer account.", "parameters": { "type": "object", "properties": { "account_number": {"type": "string", "description": "Account number (e.g., CHK-12345)"} }, }, }, { "type": "function", "name": "process_refund", "description": "Process a refund to a customer account.", "parameters": { "type": "object", "properties": { "invoice_id": {"type": "string"}, "amount": {"type": "number"} }, }, }, ] # Test scenarios with expected tool calls # Note: ToolCallAccuracyEvaluator requires a `tool_call_id` on every tool_call item. scenarios = [ { "query": "What's the balance in account CHK-12345?", "tool_definitions": banking_tools, "tool_calls": [{ "type": "tool_call", "tool_call_id": "call_1", "name": "get_account_balance", "arguments": {"account_number": "CHK-12345"} }], }, { "query": "Please refund $75 on invoice INV-9876.", "tool_definitions": banking_tools, "tool_calls": [{ "type": "tool_call", "tool_call_id": "call_3", "name": "process_refund", "arguments": {"invoice_id": "INV-9876", "amount": 75} }], }, ] print(f"{len(scenarios)} test scenarios defined") import time # Prepare test data — file_content expects an array of {"item": {...}} objects test_content = [{"item": s} for s in scenarios] testing_criteria = [ { "type": "azure_ai_evaluator", "name": "tool_accuracy", "evaluator_name": "builtin.tool_call_accuracy", "initialization_parameters": {"deployment_name": model_deployment}, "data_mapping": { "query": "{{item.query}}", "tool_definitions": "{{item.tool_definitions}}", "tool_calls": "{{item.tool_calls}}", }, }, ] data_source_config = { "type": "custom", "item_schema": { "type": "object", "properties": { "query": {"type": "string"}, "tool_definitions": {"type": "array"}, "tool_calls": {"type": "array"}, }, "required": ["query", "tool_definitions", "tool_calls"], }, } eval_object = openai_client.evals.create( name="Banking Agent — Tool Call Accuracy", data_source_config=data_source_config, testing_criteria=testing_criteria, ) eval_run = openai_client.evals.runs.create( eval_id=eval_object.id, name="Tool Accuracy Run", data_source={ "type": "jsonl", "source": {"type": "file_content", "content": test_content}, }, ) print(f"Evaluation run started: {eval_run.id}") while eval_run.status not in ["completed", "failed"]: time.sleep(5) eval_run = openai_client.evals.runs.retrieve( run_id=eval_run.id, eval_id=eval_object.id ) print(f" Status: {eval_run.status}") print(f"\n{'' if eval_run.status == 'completed' else '' } Final: {eval_run.status}")

The workshop also runs a quality-and-safety suite (builtin.violence, builtin.fluency, builtin.task_adherence) against a registered agent using evals.create and azure_ai_target_completions. Together, these give both a process signal (right tool, right arguments) and a quality/safety signal.

One honest caveat for publication: in the workshop's saved run, the tool-call records are hand-authored fixtures, not calls captured from a live agent. For a production regression test, capture the agent's actual tool call and its tool_call_id, then evaluate that. For a refund, the exact-amount and approval checks belong in the transaction path as deterministic controls—an LLM judge should not be the only guard.

Evaluation

At first glance, this evaluation run appears healthy: the run completed successfully and achieved 80% tool accuracy. However, the final test case tells a different story. The agent failed the duplicate-refund scenario, receiving a tool-accuracy score of 2 and a result of 0/1. This illustrates why aggregate scores and completion status alone are insufficient—production observability must make individual failures easy to identify and investigate.

A completed evaluation run can still contain a critical failure. The final refund scenario failed tool-call accuracy despite an overall score of 80%.

Step 7: Red-team the agent before it ships

Quality evaluation asks whether expected tasks succeed. Red teaming asks whether deliberate adversarial pressure pushes the agent past its boundaries. The workshop runs the AI Red Teaming Agent in the cloud: it registers a Foundry agent target, generates a prohibited-actions taxonomy, wires up agentic evaluators, and submits a run with attack strategies.

from azure.ai.projects.models import ( AzureAIAgentTarget, AgentTaxonomyInput, EvaluationTaxonomy, RiskCategory, ) # Target descriptor referenced by both the taxonomy and the run. target = AzureAIAgentTarget(name=agent.name, version=agent.version) # Foundry generates the attack-prompt taxonomy from the agent's tools & instructions. taxonomy = project_client.beta.evaluation_taxonomies.create( agent.name, EvaluationTaxonomy( description="Taxonomy for banking agent red teaming", taxonomy_input=AgentTaxonomyInput( risk_categories=[RiskCategory.PROHIBITED_ACTIONS], target=target, ), ), ) taxonomy_file_id = taxonomy.id # The red team groups one or more runs and wires up the built-in agentic evaluators. red_team = openai_client.evals.create( name="Red Team — Banking Agent Safety", data_source_config={"type": "azure_ai_source", "scenario": "red_team"}, testing_criteria=[ { "type": "azure_ai_evaluator", "name": "Prohibited Actions", "evaluator_name": "builtin.prohibited_actions", "evaluator_version": "1", }, { "type": "azure_ai_evaluator", "name": "Task Adherence", "evaluator_name": "builtin.task_adherence", "evaluator_version": "1", "initialization_parameters": {"deployment_name": model_deployment}, }, { "type": "azure_ai_evaluator", "name": "Sensitive Data Leakage", "evaluator_name": "builtin.sensitive_data_leakage", "evaluator_version": "1", }, ], ) # Create the red-team run — it executes server-side in Foundry. eval_run = openai_client.evals.runs.create( eval_id=red_team.id, name="Banking Agent Red Team Run", data_source={ "type": "azure_ai_red_team", "item_generation_params": { "type": "red_team_taxonomy", "attack_strategies": ["Flip", "Base64", "IndirectJailbreak"], "num_turns": 5, "source": {"type": "file_id", "id": taxonomy_file_id}, }, "target": target.as_dict(), }, ) print(f"Run created: {eval_run.id} status={eval_run.status}") print(" View in Foundry → Build → Evaluations → Red team")

The strategies (Flip, Base64, IndirectJailbreak) select input transformations and multi-turn depth; they test whether the agent resists manipulation, stays on task, and does not leak data. Two things matter for an honest write-up:

  • Review the taxonomy before scanning. Confirm the generated prohibited behaviors reflect your policy. Run red teaming against an isolated target with synthetic accounts and no irreversible side effects.
  • Distinguish a security result from an infrastructure failure. A run that ends in failed is incomplete coverage, not a passing scan. Capture the run status honestly, and only present an attack-success-rate scorecard from a completed run.

Red-team run: Capture the run status and, when the run completes, the scorecard in Foundry → Build → Evaluations → Red team. If the run failed, caption it as incomplete coverage rather than a pass.

Step 8: Prove the fix and move to production monitoring

For the sample, the correction is a tightened prompt plus a deterministic amount/approval check in the transaction path—so the agent is not expected to reason its way out of receiving or emitting the wrong amount. After a fix, compare the two versions across the same evaluation set:

SignalBefore the fixAfter the fix
Request success100%100%
Correct refund amountFailPass
Approval threshold honoredFailPass
Tool-call accuracyFailPass

The operational success rate does not change. The business outcome does.

Once you know how to detect the failure, the next question is whether it is happening elsewhere. The Foundry Monitor experience brings operational metrics, evaluation results, and red-team results together for the selected agent; Application Insights adds deeper investigation across runs, models, tools, tokens, and errors. For this refund agent, I would monitor at least:

  • Run success rate and model or tool errors
  • P50 and P95 end-to-end latency
  • Input and output token consumption
  • Refund-amount and approval-threshold pass rate
  • Tool-call-accuracy distribution
  • Human escalation and customer correction rate
  • Safety and adversarial-testing findings

An alert should lead to an operational action:

SignalExample conditionAction
Wrong-amount refundAny run proposes an amount outside the approved rangeBlock the version and notify the finance/policy owner
Quality regressionTool-call-accuracy pass rate falls below the release thresholdStop promotion or roll back
Token anomalyTokens per run rise materially above baselineInspect context growth and repeated tool calls
LatencyP95 exceeds the agreed service objectiveInspect model, tool, and throttling spans
SafetyA scheduled red-team or production safety check failsRoute to the security and responsible-AI process

A KQL query like the tool-failure-rate example can become the signal for an Azure Monitor log alert. Configure the rule to trigger when the query returns results in the evaluation window, and route the notification through the customer's approved action group.

Continuous evaluation is the bridge between deployment and learning

A one-time evaluation protects one release. It does not protect the agent indefinitely. Production traffic changes, tools change, and models and prompts are revised. This makes observability a continuous engineering loop:

  1. Observe production behavior.
  2. Diagnose representative traces.
  3. Evaluate the failure mode.
  4. Add that failure to the regression dataset.
  5. Correct the prompt, tool, policy, or model configuration.
  6. Compare the candidate with the approved baseline.
  7. Promote only when the required quality, safety, operational, and business gates pass.

Microsoft Foundry supports recurring and continuous evaluation. When using live production traces, sample intentionally—random sampling gives coverage, while targeted or intelligent sampling can prioritize unusual, high-risk, failed, expensive, or low-quality interactions. Evaluation calls and telemetry have cost, privacy, and retention implications, so the sampling strategy should reflect business risk.

Security, privacy, and cost are part of observability design

Tracing can capture prompts, model outputs, tool arguments, and tool results. That visibility is valuable, but it creates responsibility:

  • Keep message-content recording disabled unless there is an approved need.
  • Never place credentials, tokens, or secrets in prompts or span attributes.
  • Avoid storing personal data when a pseudonymous transaction identifier is sufficient.
  • Redact or minimize sensitive content before telemetry is emitted.
  • Apply Azure RBAC to Application Insights and Log Analytics; set retention by environment and data classification.
  • Monitor ingestion and evaluation cost, not only model-token cost.
  • Validate preview capabilities against production requirements.

There is also a practical instrumentation lesson: do not add every available value to every span. Capture the dimensions needed to investigate reliability, behavior, quality, security, cost, and business outcomes. More telemetry is not automatically better telemetry.

What the customer gained

The most valuable outcome was not another dashboard. It was a shared way for developers, platform engineers, finance/policy owners, security teams, and business stakeholders to discuss the same agent run with evidence.

The developer could see the exact execution path. The platform team could see latency, errors, and token consumption. The policy owner could see the refund amount and whether the approval threshold held. The security team could verify how sensitive telemetry was handled. The business sponsor could see whether customers were refunded correctly.

The original request had a green check before the investigation, and it had a green check afterward. The difference was that after the fix, we could prove the refund amount was correct.

Final takeaway

An agent should not be considered healthy merely because it responds. A production-ready observability practice should be able to answer:

  • Did the agent complete the request?
  • What model, tool, and path did it use, and with what arguments?
  • Was its action relevant, grounded, safe, and correct?
  • Did it comply with the approved business process?
  • Can the team detect a regression before it affects more users?
  • Can the team prove that the remediation improved the outcome?

In the agentic era, observability is not the dashboard at the end of deployment. It is the evidence system connecting design decisions, production behavior, governance controls, and measurable business value.

References

Contributors:

This article is maintained by Microsoft. It was originally written by the following contributors.

Gaurav Bhardwaj | Senior Cloud Solution Architect

Read the whole story
alvinashcraft
29 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Windows, TLS 1.3 and Post Quantum Crypto FAQ

1 Share

Q01: What is the real threat?

A: The threat is that attackers can collect encrypted network data and then break the asymmetric cryptography (RSA/ECC/DH) used to protect bulk data encryption keys (AES) once cryptographically relevant quantum computers come online. This is known as Harvest Now, Decrypt Later (HNDL).

Q02: Do I need to use TLS 1.3 for PQ support?

A: Yes. Earlier versions of TLS, including TLS 1.0, TLS 1.1, and TLS 1.2, do not and will not support post-quantum key establishment.

Q03: Does enabling TLS 1.3 give me hybrid PQ support?

A: No. By default, you will get ‘classic’ crypto algorithms. You must enable the PQ algorithms; this is explained later.

Q04: What Windows OS version must I use to get TLS 1.3 and post-quantum support?

A: TLS Hybrid Key Exchange using ML-KEM groups is available on Windows 11 starting with update KB5089573 for 24H2 and 25H2 and KB5095091 for 26H1.

For Windows Server 2025 use the patch from July 14, 2026-KB5099536 (OS Build 26100.33158)

Q05: What is hybrid crypto in TLS 1.3?

A: Hybrid crypto establishes cryptographic keys by combining elliptic-curve cryptography with post-quantum cryptography, allowing the client and server to use both algorithms during the TLS 1.3 key establishment. It’s a hedge in case the PQ cryptography is broken.

Q06: What crypto is used in hybrid?

A: Like all crypto in TLS, this is flexible; however, the most common hybrid crypto for web browser-based key establishment is X25519_MLKEM768 which combines the classic X25519 Elliptic Curve with post-quantum ML-KEM.

Q07: What is ML-KEM?

A: ML‑KEM (Module-Lattice Key Encapsulation Mechanism) is the new quantum-resistant method for securely establishing cryptographic keys between hosts. It is defined in FIPS 203.

Q08: Is hybrid TLS 1.3 enabled in Windows today?

A: No not by default; you must enable it. If you use Group Policy, you can set the policy there. If the machine does not have GP, then you can use the following from an elevated PowerShell prompt:

Enable-TlsEccCurve -Name "X25519_MLKEM768" -Position 0

Note that -Position 0 is important as it places the hybrid group X25519_MLKEM768 at the top of the preferred group list. If you do not do this, you might not negotiate to the hybrid PQC group.

IMPORTANT: Note that Group Policy will override this setting, so don’t mix-n-match! If you see your group ordering change after calling the PS cmdlet, it's probably GP coming in and overriding the setting. 

Q09: In the prior answer, you used the word ‘group’ what is a group?

A: In TLS 1.3, a "group" is simply the method (or algorithm) that the client and server agree to use to securely establish keys during the connection. Examples include X25519 (the most common Elliptic Curve TLS 1.3 group) or the newer hybrid X25519_MLKEM.

As a side note, the word "group" isn't arbitrary - it comes from the underlying algebra (elliptic-curve groups, finite-field multiplicative groups). It's mathematically precise; it's just opaque to anyone who isn't thinking about group theory! However, ML-KEM isn't built on a group at all - its hardness comes, in part, from lattices.

Q10: Is a group the same as a ciphersuite in TLS 1.3?

A: No, a group is not the same as a ciphersuite in TLS 1.3. The group is how the client and server agree on secret keys. The ciphersuite is how they use secret keys to encrypt and protect the traffic. They work together in a TLS 1.3 handshake, but they are two separate parts

Q11: Are there other groups I should know about?

A: Yes. There are three common hybrid groups; you have already met X25519_MLKEM768, but there is also SecP256r1_MLKEM768 and SecP384r1_MLKEM1024.

Q12: What group should I use?

A: Follow your organization’s cryptographic policy and required assurance profile. For browser interoperability, prefer X25519_MLKEM768 where supported and place it ahead of other groups. For regulated environments, use a hybrid group and implementation permitted by the applicable policy and validated cryptographic module; this may require SecP256r1_MLKEM768 or SecP384r1_MLKEM1024 instead of X25519_MLKEM768.

Q13: If there is TLD 1.3 with hybrid crypto, is there a version that is NOT hybrid?

A: Yes, it’s called ‘pure’, and that is where rather than using ECC+PQC, you use just PQC; for example instead of X25519+MLKEM768, you use only MLKEM768 or MLKEM1024 if CNSA 2.0 compliance is in scope. Some customers may eventually require this. You can read about the MLKEM-only Windows schannel update here August 27, 2026—KB5120998 (OS Builds 26200.9278 and 26100.9278) Preview | Microsoft Support.

Q14: What’s CNSA 2.0?

A: CNSA 2.0, the Commercial National Security Algorithm Suite 2.0, is the NSA's set of quantum-resistant cryptographic algorithm requirements for U.S. National Security Systems. It updates CNSA 1.0 by introducing post-quantum algorithms intended to protect classified and other national-security-sensitive information against both classical and future quantum attacks. It is important because transitioning cryptographic infrastructure takes years, while adversaries can collect encrypted data now and attempt to decrypt it later. Although its formal scope is National Security Systems, CNSA 2.0 also provides vendors and other organizations with a concrete high-assurance target for planning, product development, and post-quantum migration.

The list of algorithms that affect TLS includes:

  • Key establishment: ML-KEM-1024 only (not 512, not 768)
  • Digital signatures: ML-DSA-87 only (not 44, not 65)
  • Hashing: SHA-384 or SHA-512 only
  • Symmetric encryption: AES-256 only

Q15: How do I test if my server supports TLS 1.3?

A: See Appendix A.

Q16: How do I test if my server supports TLS 1.3 and PQC?

A: See Appendix B.

Q17: How do I use Wireshark to determine if my server supports TLS 1.3 and PQC?

A: See Appendix C.

Q18: Do both the client and server need to support hybrid PQC TLS?

A: Yes.

Q19: What happens if one side does not support hybrid PQC TLS?

A: If one side does not support hybrid PQC TLS, the connection may still succeed using another mutually supported TLS 1.3 key establishment group, for example X25519, but it will not use hybrid PQC protection. The exact behavior depends on the client, server, and TLS configuration.

Q20: How do I know if my client, such as a browser, supports PQC TLS?

A: Point your application or browser at a tool like this https://pqc.ninja/api/browsertest/ it will output something like:

{ "negotiated_curve": "X25519MLKEM768", "offered_curves": "X25519MLKEM768:X25519:prime256v1:secp384r1", "negotiated_cipher": "TLS_AES_256_GCM_SHA384", "offered_ciphers": "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256", "alpn_protocol": "h3", "protocol": "TLSv1.3" }

You can see that the browser and server negotiated to use the X25519_MLKEM768 hybrid group and the browser supports this along with two classic (ie; non-PQC) curves, prime256v1 and secp384r1.

Q21: Are PQC (ML-DSA) certificates required for TLS 1.3?

A: No, not for hybrid PQC key establishment. PQC certificates are a separate part of the post quantum migration and relate to authentication and digital signatures. Hybrid key establishment protects the session key agreement; PQC certificates will protect the certificate signature and authentication path.

Q22: Following the previous question, why does the server still use a classic certificate if the key negotiation is post-quantum?

A: This is by design. We need to secure data in transit first and foremost, since that represents the most immediate quantum threat. An adversary can perform a harvest-now, decrypt-later attack by storing encrypted communications today and waiting until quantum computers are available to decrypt them - any data being transmitted currently needs quantum-safe key exchange to be secure in the future. Authentication, however, does not have that same window of exposure: to perform a successful spoof via certificate misuse, an attacker would need a cryptographically meaningful quantum computer at the time of the session - they do not get to use it later. As we don't have that ability currently, by securing key exchange first we mitigate the most imminent threat while the ecosystem around it (CAs, trust anchors, relying parties, etc.) works towards supporting PQC signatures.

Q23: Is there a performance impact from hybrid PQC TLS?

A: We will provide more stats as they become available, but current details look good; X25519 vs X25519-MLKEM768 is about a 3%-6% latency delta and less than 1% CPU hit using Azure Linux, nginx + OpenSSL + SymCrypt and similar stats using https.sys on Windows Server 2025.

Q24: Should I enable this on internet-facing services first or internal services first?

A: Start with controlled pilots, then prioritize services that protect long-lived or high-value confidential data. Internet-facing services may provide broader coverage, but internal services can be easier to test and control. The right rollout order should balance risk, compatibility, visibility, and operational readiness.

Q25: What logging or telemetry should I capture during testing?

A: Capture the client and server IP addresses, the negotiated key-establishment group, the negotiated cipher suite, and the TLS protocol version for every test connection. Also record whether the handshake succeeded or failed and correlate each result with a timestamp or connection identifier. This makes it possible to confirm that TLS 1.3 and the expected hybrid PQC group were negotiated.

Below is a screen shot from a tool I have on GitHub that shows most of this passively in Windows using schannel and pktmon. The code is here x509cert/schannel-cap.

 

Appendix A - Testing for TLS 1.3

You can use tools like OpenSSL, PowerShell or a modern browser to test a server to determine if it supports TLS 1.3.

Let’s look at each.

OpenSSL

Use the following from a Windows or Linux command-line, obviously replacing the IP address and port number for your target service.

openssl s_client -connect 192.168.1.1:443 -tls1_3 -brief

Success is when you see:

CONNECTION ESTABLISHED Protocol version: TLSv1.3

PowerShell 7+

Save the following as Test-Tls13.ps1.

param( [Parameter(Mandatory)][string]$IpAddress, [Parameter(Mandatory)][int]$Port ) $tcp = New-Object System.Net.Sockets.TcpClient $tcp.Connect($IpAddress, $Port) $tls = New-Object System.Net.Security.SslStream($tcp.GetStream(), $false) try { $tls.AuthenticateAsClient($IpAddress, $null, [System.Security.Authentication.SslProtocols]::Tls13, $false) Write-Host "Connected: $($tls.SslProtocol), cipher $($tls.NegotiatedCipherSuite)" -ForegroundColor Green } catch { Write-Host "TLS 1.3 handshake failed: $($_.Exception.Message)" -ForegroundColor Red } finally { $tls.Dispose() $tcp.Dispose() }

You can call this using positional syntax:

.\Test-Tls13.ps1 192.168.1.1 443

Or using parameters:

.\Test-Tls13.ps1 -IpAddress "192.168.1.1" -Port 443

Success is indicated by output like this:

Connected: Tls13, cipher TLS_AES_256_GCM_SHA384

Edge and Chrome Browsers

Current versions of Edge and Chrome support hybrid TLS 1.3. After you make a connection to the server, click the ellipsis in the top right (…) -> More Tools -> Developer Tools -> Security.

If you don’t see the security option:

Then click on the + symbol and add the Security tab.

Now you will see the connection details, if you see TLS 1.3, then the server and client are connected with TLS 1.3. In the example below, the connection is also using X25519MLKEM, so the connection is not just using TLS 1.3, it’s using TLS 1.3 in PQ hybrid.

 

Appendix B – Testing for TLS 1.3 and PQC

The simplest and most reliable way to test a server to determine if it supports TLS 1.3 and PQC, is to use the following OpenSSL command-line:

.\openssl s_client -connect 127.0.0.1:8443 -tls1_3 -brief

You will see output like this:

Connecting to 127.0.0.1 CONNECTION ESTABLISHED Protocol version: TLSv1.3 Ciphersuite: TLS_AES_256_GCM_SHA384 Peer certificate: CN=localhost Hash used: SHA256 Negotiated TLS1.3 group: X25519MLKEM768

 

The group information, in this case X25519MLKEM is at the bottom, this indicates that hybrid PQC is in place for this connection.

Appendix C – Wireshark Filtering

Wireshark is commonly used to determine what data is travelling across a network. You can determine if a connection uses hybrid groups using the following steps:

  • Start Wireshark,
  • Perform some sample network connections (like make an API call or load a page in a browser),
  • Stop the collection
  • Enter the following in the filter window: tls.handshake.extensions_key_share_group
  • Click on the packet of interest and scroll to the Extension: key_share line.

You will see something like this:

The line in this example shows that the connection uses X25519MLKEM768 which is a hybrid PQC group.

Note, you will often see TLS 1.2 used as the protocol version, Wireshark explains why:

The TLS Version field is a deprecated field, so ignore it!

Thanks!

As usual, a big thanks to the people who helped write and edit this document:

  • Jessica Krynitsky - Windows Security
  • Andrei Popov - Windows Security
  • Aabha Thipsay - Windows Security
  • Vick Mukherjeee - Azure Security
Read the whole story
alvinashcraft
29 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

I'm worried about the web

1 Share

I'm worried about the web.

I don't want to live in a world where most users of internet-connected devices consume their information and accomplish their tasks via a chatbot interface.

I don't want these interactions to be filtered, watered-down, reformatted, and ultimately decided by a few.

I want the web that I grew up with to remain: direct linking, independent publishing, view-source culture, and the ability to publish and browse without permission from an authority. I want its openness, its diversity, its weirdness. All of it.

A chatbot-mediated web comes with risks: less creativity, less uniqueness, less discovery. It's a web that makes it harder to attribute content to its original creators, and not as rewarding for those who contribute original work.

I'm not yet worried about the web platform, though perhaps I will be soon.

HTML, CSS, JS, and related standards constitute the best platform there is to build on. Whether the web as we know it continues to exist doesn't diminish these technologies' merit.

But, over time, what's the incentive for browser vendors to keep improving the platform when AI agents increasingly write much of the code?

AI agents are perfectly happy writing code whatever the platform provides, which reduces the need for human web developers to ask for improvements.

Also, what's the incentive for vendors to improve the platform when end users consume it via a chatbot? Does a chatbot-mediated web require capabilities we don't yet have? I hope so, for the sake of the platform. But, for now at least, I don't think it does.

Are you worried too?

Read the whole story
alvinashcraft
30 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

DevRel Field Notes: Build Review Into the Work

1 Share

This week’s examples point to a useful DevRel habit: make rehearsal, evaluation, and honest reporting part of the work instead of treating them as final checks.

Publishing is visible. Review usually is not. That can make it tempting to protect the production schedule first and squeeze testing, rehearsal, and follow-up into whatever time remains.

I think that order is backwards. The review work is where a team discovers whether a tutorial can be followed, whether a live demo is ready, and whether an explanation matches what the product actually does. If those checks happen late, they become a gate. If they happen throughout the work, they improve the content.

For this edition, I looked at three examples from the week ending September 11. They come from different parts of the developer ecosystem, and I am not presenting them as a measured industry trend. Together, however, they offer a useful way to think about managing content quality.

Rehearsal should be an ordinary part of live content

YouTube’s creator update page now describes Live Practice Mode, a private space in its mobile app where creators can rehearse their setup and content, then begin the live stream when they are ready. Read the YouTube creator update.

The product feature is new, but the management lesson is familiar. A technical livestream deserves rehearsal time in the plan. That time should cover more than microphones and screen sharing. The presenter should run the demo from a clean state, identify the moments that need explanation, and decide what to do if a service or sample fails.

I would also use rehearsal as coaching, not merely inspection. The goal is to help the presenter find the simplest route through the material while keeping their own voice. A manager can watch for assumptions that an expert no longer notices: an unexplained tool, an account that is already configured, or a command whose result arrives too quickly to follow.

A private run also gives the team a chance to decide whether live is the right format. If the useful part is a precise sequence of steps, a written guide may be easier to revisit. If the value is seeing someone diagnose a surprise and answer questions, live video may be exactly right.

Evaluate the behavior you actually care about

On September 9, the Google Developers Blog published The Anatomy of Harness Engineering. Its summary argues for small behavioral evaluations that check discrete actions, such as tool calls or file changes, alongside broader end-to-end benchmarks. Read the Google Developers post.

That idea transfers well to developer education. A team can evaluate an article by asking whether it exists, whether the links work, and whether the sample builds. Those checks matter, but they do not tell us whether a developer understands when to use the approach or can recover from a common mistake.

For each important piece, I would define a few observable behaviors before production. Can a reader find the prerequisite? Can a viewer pause at a meaningful point and reproduce the step? Can someone explain the tradeoff after finishing? These are small checks tied to the job of the content.

This changes the editorial conversation. Instead of debating whether a draft “feels clear,” the writer and reviewer can look at where a test reader hesitated. The evidence will still be limited, especially with a small review group, but it gives the team a concrete problem to fix.

Trust grows when the difficult result is included

GitHub published its August availability report on September 9 and stated that five incidents caused degraded performance during the month. See GitHub’s latest posts and the availability report.

An availability report is operational communication rather than a tutorial, but it belongs in a broader developer content strategy. Developers form an opinion of a platform through its documentation, examples, support, release notes, and incident communication. A polished launch post cannot carry trust by itself.

For a DevRel manager, this means maintaining a relationship with the teams responsible for documentation, support, product communication, and reliability. DevRel should not invent the technical account of an incident. It can help surface the questions developers are asking, identify terms that need explanation, and make sure useful follow-up reaches the same audience that saw the disruption.

The same principle applies at a smaller scale. If a tutorial relies on a preview feature, say so. If a workaround has a cost, include it. If a demo only covers the happy path, tell the viewer where to look next. Completeness does not require documenting every possible failure. It requires being honest about the boundaries that affect the reader’s decision.

Plan for learning, not just output

These examples push me toward a simple operating model for a DevRel content team. Every substantial piece should have a short quality plan before production begins:

  1. Name the developer outcome.
  2. Rehearse or test the path from a clean starting point.
  3. Record the assumptions, limitations, and likely failure points.
  4. Decide who will watch questions and feedback after publication.
  5. Bring what the team learns into the next brief.

This requires allocation choices. A calendar with every hour assigned to creating new assets leaves no room to improve them. I would reserve explicit capacity for technical review, rehearsal, accessibility, and post-publication follow-up. I would also protect creators from the idea that finding a flaw during review means they failed. Finding it before the audience does is the review process working.

The metrics should reflect that goal. Alongside reach and engagement, I would track corrections, repeated questions, sample failures, and the time it takes to answer a meaningful issue. I would look for patterns across several pieces rather than drawing a conclusion from one comment or one week.

My experiment for the coming week would be to take one planned tutorial and write three behavioral checks before drafting it. Give the finished piece to one developer who was not involved in production. Watch where those checks pass or fail, then revise the content before publishing.

Where in your content process does useful review happen today, and what would make it happen earlier?

Read the whole story
alvinashcraft
30 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Protecting File Access in the wwwroot Folder in ASP.NET

1 Share
ASP.NET Core treats files in wwwroot as public static content, but occasionally applications create files there that should only be available to authorized users. In this post I look at several ways to protect those files and show a small middleware solution that lets selectively access files easily.
Read the whole story
alvinashcraft
31 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Top 10 Essential Visual Studio 2026 Extensions for Modern C♯ and .NET 10 Developers

1 Share

Visual Studio 2026 delivers a noticeably faster 64-bit inner loop and robust multi-core parallel build scaling right out of the box, but the true superpower of Microsoft's flagship IDE lies in its extension ecosystem. Equipping your development environment with the right plugins bridges the gap between raw compiler power and effortless day-to-day coding velocity.

 

Before curating your extension portfolio, ensure you are running the latest build from our dedicated Visual Studio 2026 download hub, and check out our guide on how to accelerate Visual Studio 2026 build times. In this practical guide, we break down the top 10 essential extensions every modern C# and .NET 10 developer should install to boost productivity, catch subtle bugs, and maintain pristine code quality.

 

Top 10 Essential Visual Studio 2026 Extensions
Supercharge your development environment: the top 10 essential Visual Studio 2026 extensions for C# 14 and .NET 10 developer productivity.

 

Table of Contents

 

  • Instant Developer Velocity: Automates repetitive refactorings, boilerplate typing, and branch tracking so you stay locked in flow state.
  • C# 14 & .NET 10 Ready: All ten curated extensions run natively on 64-bit out-of-process architecture without stalling the IDE UI thread.
  • Proactive Quality Gates: Catches security vulnerabilities, memory leaks, and anti-patterns right inside the editor before code ever reaches a pull request.
  • Git & Team Collaboration: Delivers inline commit authoring blame, visual branch histories, and multi-repo tracking directly inside Solution Explorer.
  • Marketplace Verified: All extensions are maintained and available directly through the official Visual Studio Marketplace.

 

Why Visual Studio 2026 Extensions Matter for Everyday Velocity

Software development involves countless micro-actions: writing property accessors, navigating nested solution folders, resolving git conflicts, and reading compiler logs. While each task takes only a few seconds, context-switching across fifty microservices quickly drains your mental focus.

 

As documented in official development manuals on managing extensions for Visual Studio, Visual Studio 2026 isolates extension execution in dedicated worker processes. This means you can install powerful analyzers and workspace assistants without causing keyboard latency or editor freezes.

 

If you recently explored modern language syntax in our tutorial on C# 14 nameof with unbound generic types in .NET 10, you know that keeping your tooling updated ensures you get real-time syntax highlighting, refactoring suggestions, and instant compiler feedback.

 

 

Top 10 Essential Extensions for C# 14 and .NET 10 Development

Here are the ten most valuable extensions available on the official Visual Studio Marketplace that will elevate your daily engineering workflow:

 

1. GitHub Copilot for Visual Studio

GitHub Copilot has transformed from a simple autocomplete utility into an agentic software engineering partner. In Visual Studio 2026, it analyzes your entire solution context, predicts multi-file edits, and offers inline chat to explain legacy algorithms or draft comprehensive unit tests in seconds.

 

2. Roslynator 2026

Roslynator is an indispensable collection of over 500 analyzers, refactorings, and code fixes for C#. It automatically guides you toward modern language idioms—such as converting legacy loops into clean LINQ expressions, simplifying pattern matching, and eliminating redundant type specifications.

 

3. SonarLint for Visual Studio 2026

Think of SonarLint as an instant security and code quality spell-checker. As you write C# and .NET code, SonarLint flags potential SQL injections, null reference traps, and concurrency deadlocks directly in your editor margin with clear remediation guidance.

 

4. GitLens for Visual Studio

Bringing one of the most beloved tools from VS Code into Visual Studio, GitLens supercharges your version control. It adds subtle inline blame annotations showing who touched a line of code and when, alongside interactive visual branch graphs and commit comparisons.

 

5. Markdown Editor v2

Modern enterprise repositories rely heavily on Markdown for documentation, architecture decision records (ADRs), and pull request templates. Markdown Editor v2 provides full syntax highlighting, table formatting shortcuts, and a synchronized live HTML preview pane directly inside Visual Studio.

 

6. Visual Studio Spell Checker

Typos in public API method names, JSON property strings, and user-facing error messages look unprofessional and break serialization contracts. This extension checks spelling across code comments, string literals, and XML documentation while respecting camelCase and PascalCase identifiers.

 

7. File Icons 2026

When working across sprawling enterprise solutions with hundreds of C# classes, JSON configs, Dockerfiles, and Razor components, identifying file types at a glance matters. File Icons injects crisp, colorful icons into Solution Explorer, speeding up visual file scanning significantly.

 

8. Output Enhancer

Visual Studio's default build output window is a wall of monochrome text where critical compilation errors get lost in hundreds of informational lines. Output Enhancer color-codes warnings in yellow, errors in vibrant red, and build successes in green, allowing you to spot broken builds in milliseconds.

 

9. Trailing Whitespace Visualizer

Accidental trailing spaces dirty git diffs and lead to annoying code review nitpicks. This lightweight utility highlights stray trailing whitespaces in soft red and automatically strips them whenever you save a file, keeping your git history clean.

 

10. ReSharper / Rider Tools for Visual Studio

For enterprise developers managing legacy monoliths, JetBrains ReSharper remains a gold standard for solution-wide architectural analysis, automated dependency refactoring, and advanced unit testing diagnostics across massive .NET codebases.

 

 

Summary Comparison Table - Focus Area, Benefits, and Marketplace Tier

To help you prioritize which extensions to install first on your workstation, here is a quick reference breakdown:

 

Extension Name Primary Category Key Developer Benefit Licensing Tier
GitHub Copilot AI Coding Assistant Solution-wide multi-file edits and automated unit test generation Subscription / Free Tier
Roslynator 2026 Code Analysis & Refactoring 500+ automated diagnostics for modern C# 14 clean syntax Free / Open Source
SonarLint Static Code Security Real-time vulnerability scanning and anti-pattern detection Free / Community
GitLens Version Control & Git Inline commit blame annotations and visual branch history graphs Freemium
Markdown Editor v2 Documentation Tooling Live synchronized preview for READMEs and release documentation Free
VS Spell Checker Code Hygiene Catches typos in string literals, comments, and XML doc summaries Free / Open Source
File Icons 2026 UI Customization Crisp, distinctive file icons across Solution Explorer trees Free
Output Enhancer Build Diagnostics Color-coded MSBuild terminal output for instant error recognition Free
Trailing Whitespace Git Hygiene Auto-strips trailing spaces on file save to keep PR diffs clean Free
ReSharper Enterprise Architecture Advanced memory profiling and deep solution-wide refactorings Commercial / Trial

 

 

How to Manage and Update Extensions Safely in Visual Studio 2026

Installing and updating extensions in Visual Studio 2026 is straightforward, but keeping your IDE lean ensures optimal inner-loop speed:

  • Use the Extensions Manager: Open Visual Studio, click Extensions > Manage Extensions, and browse the Online marketplace tab to install new tools with one click.
  • Enable Automatic Updates: Visual Studio 2026 can automatically update extensions in the background when the IDE is closed, ensuring you always have the latest Roslyn diagnostics.
  • Audit Extension Load Times: If you ever experience sluggishness, navigate to Help > Manage Visual Studio Performance to review how many milliseconds each extension adds to solution load times.

 

For more architectural insights on modern Microsoft tooling, check out our deep-dive into Visual Studio 2026 GitHub Copilot multi-file edits for practical real-world workflows.

 

 

Frequently Asked Questions (FAQ)

  1. Are these extensions compatible with Visual Studio 2026 RTM and Preview channels?
    Yes. All ten extensions highlighted in this guide have been verified for Visual Studio 2026 64-bit architecture and install seamlessly across both stable RTM and isolated Preview channels.
  2.  

  3. Does installing many extensions slow down Visual Studio 2026 startup times?
    Visual Studio 2026 runs extensions asynchronously out-of-process, which prevents UI thread locking. However, installing excessive heavy analyzers can increase background CPU usage; stick to essential tools to maintain instant startup speeds.
  4.  

  5. How does Roslynator differ from built-in Visual Studio analyzers?
    While Visual Studio includes essential C# refactorings, Roslynator adds over 500 specialized diagnostics and code fixes specifically tuned for modern syntax, pattern matching, LINQ performance, and null-safety.
  6.  

  7. Is GitHub Copilot included free with Visual Studio 2026?
    The GitHub Copilot extension can be installed from the marketplace for free, but active access requires a personal GitHub Copilot subscription, a GitHub Enterprise plan, or an eligible student/open-source developer account.
  8.  

  9. Can SonarLint replace my team's continuous integration security scanners?
    SonarLint acts as an immediate on-the-fly 'in-IDE spell checker' for bugs and security vulnerabilities, catching flaws before you commit. It complements rather than replaces centralized CI/CD quality gates like SonarQube.
  10.  

  11. Where are Visual Studio 2026 extensions downloaded from?
    Extensions are securely fetched directly from the official Visual Studio Marketplace through the Extensions > Manage Extensions dialog inside the IDE.
  12.  

  13. Does GitLens for Visual Studio support multi-repository enterprise solutions?
    Yes. It tracks Git blame, file revision histories, and branch graph trees across multiple nested repositories simultaneously within large Visual Studio solutions.
  14.  

  15. How do I temporarily disable an extension if I encounter an issue?
    Navigate to Extensions > Manage Extensions, select the Installed tab, find the specific extension, and click Disable. Visual Studio will preserve your settings while keeping the extension inactive upon restart.
  16.  

  17. Does Visual Studio Spell Checker inspect code comments and string literals?
    Yes. It intelligently parses camelCase and PascalCase identifiers, checking spelling across XML doc summaries, inline code comments, and localized string literals without flagging programming keywords.
  18.  

  19. Where can I download the latest release of Visual Studio 2026?
    You can download the latest production RTM installer or side-by-side preview bits directly through our dedicated Visual Studio 2026 download hub.

 

 

End Note

Customizing Visual Studio 2026 with the right extensions transforms the IDE into a finely tuned workstation tailored to your exact engineering needs. From catching subtle security flaws with SonarLint to writing idiomatic C# 14 syntax with Roslynator, these tools save countless hours of manual debugging and code review back-and-forth.

 

Tomorrow, we will continue our Visual Studio and developer productivity series with a complete setup guide for building a modern Windows 11 developer workstation with WSL 2, Dev Home, and custom Git configurations, followed by gaming performance tweaks.

 

Which Visual Studio extensions do you rely on every single day, and is there a hidden gem plugin that your team cannot live without? Share your favorite extension picks and productivity tips in the comments below!

 

Top 10 Essential Visual Studio 2026 Extensions
Supercharge your development environment: the top 10 essential Visual Studio 2026 extensions for C# 14 and .NET 10 developer productivity.

 

Read the whole story
alvinashcraft
31 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories