
Microsoft Development
- 13 installs
- 7 repo stars
- Updated August 2, 2026
- practicalswan/agent-skills
microsoft-development is a Claude Code skill for ai & agent building.
About
microsoft-development is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- microsoft-development
- AI & Agent Building
- AI-coding skill
Microsoft Development by the numbers
- 13 all-time installs (skills.sh)
- Ranked #11,409 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/practicalswan/agent-skills --skill microsoft-developmentAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 13 |
|---|---|
| repo stars | ★ 7 |
| Last updated | August 2, 2026 |
| Repository | practicalswan/agent-skills ↗ |
How do I helps with ai & agent building tasks.?
Helps with ai & agent building tasks.
Who is it for?
Best when you're working on ai & agent building and need structured help with microsoft development.
Skip if: Teams with no ai & agent building needs, or anyone wanting a generic chat assistant without this specific workflow.
When should I use this skill?
When you need to helps with ai & agent building tasks., or when microsoft-development is a claude code skill for ai & agent building.
What you get
Structured output aligned to microsoft-development: microsoft-development, AI & Agent Building.
Files
Microsoft Development
Optimized for current Microsoft Graph, Entra ID, PowerShell 7.x, and Microsoft 365 integration workflows.
Use this skill when the answer should come from Microsoft documentation rather than memory or third-party summaries.
- Leverage native parallel subagent dispatch and 200k+ context windows where available.
Current MCP Reality
Microsoft's Learn Docs MCP server is publicly documented and currently exposes these core tools:
microsoft_docs_searchmicrosoft_docs_fetchmicrosoft_docs_extract_code_examplesmicrosoft_docs_search_by_product
Microsoft's getting-started docs also describe installation through npx -y @microsoft/learn-docs-mcp.
Activation Conditions
Use symptom -> action triggers: when one matches, apply this skill and verify with the protocol below.
- Verifying Azure SDK usage, limits, or configuration
- Looking up .NET, Graph, Windows, or Microsoft 365 APIs
- Pulling official code examples before implementation
- Checking product-specific guidance for Azure, Power BI, or Power Platform
Recommended Workflow
1. Search first with microsoft_docs_search. 2. Narrow by product with microsoft_docs_search_by_product when results are broad. 3. Fetch the relevant page with microsoft_docs_fetch for details. 4. Extract code examples with microsoft_docs_extract_code_examples if the user needs working snippets. 5. Prefer official code and limits over recollection.
Query Patterns
"Azure Container Apps scale rules""BlobClient UploadAsync Azure.Storage.Blobs"product="power-bi" query="row level security dax"product="microsoft-graph" query="send mail application permissions"
Guardrails
- Use Microsoft Learn MCP for Microsoft-specific answers before browsing elsewhere.
- Treat package versions, quotas, and service capabilities as time-sensitive.
- If Learn MCP is unavailable in the current client, use the included scripts and references as local fallbacks, then browse official docs.
Anti-Patterns
- Changing infrastructure before inspecting the current state: Cloud drift and hidden dependencies make blind edits risky.
- Hardcoding credentials or environment assumptions: Rollouts stop being reproducible and secrets become harder to rotate.
- Skipping rollback, observability, or validation planning: You only notice the missing safeguards after the deployment is already live.
Verification Protocol
Before claiming "skill applied successfully":
1. Pass/fail: The Microsoft Development implementation names the target runtime, framework version, and affected files. 2. Pass/fail: Build, lint, test, or equivalent local validation is run for the changed surface. 3. Pass/fail: Edge cases for errors, dependency drift, and environment differences are addressed or explicitly out of scope. 4. Pressure-test scenario: Apply the workflow to a change that passes happy-path tests but fails one boundary condition. 5. Success metric: Zero untested success claims; every implementation claim maps to a command or artifact.
Before and After Example
# Before
az deployment group create `
--resource-group app-rg `
--template-file main.bicep
# After
$deploymentName = "api-$(Get-Date -Format yyyyMMddHHmmss)"
az deployment group create `
--name $deploymentName `
--resource-group app-rg `
--template-file main.bicep `
--parameters environment=prod `
--what-ifMoves from an opaque deployment command to a traceable and reviewable workflow with named deployments, explicit parameters, and a preflight diff.
Common Pitfalls
- Treating vendor examples as drop-in production code: Official snippets usually prove an API, not your project’s retry, logging, or auth requirements.
- Skipping service-specific limits and permissions review: Azure and Microsoft 365 failures often come from quotas or scopes rather than syntax.
- Relying on memory instead of current docs: Microsoft SDK behavior and surface area change often enough that stale recall is risky.
References & Resources
Documentation
- Azure Services Quick Reference - Common Azure services, SDK packages, and decision points
- .NET Patterns - Practical .NET design and dependency-injection patterns
- Microsoft Learn MCP - Current tool names, install command, and query workflow
Scripts
- Azure Health Check - Validate Azure login and inspect common resource health in a resource group
Examples
- Azure Function API Example - Example serverless API workflow tied back to official Microsoft docs
<!-- PORTABILITY:START -->
Cross-Client Portability
This skill is written to stay usable across GitHub Copilot, Claude Code, Codex, and Gemini CLI.
- GitHub Copilot: keep the folder in a Copilot-visible skill or plugin path, or wrap the workflow as project instructions if the host does not support portable skill folders directly.
- Claude Code: keep the folder in a local skills directory or a compatible plugin or marketplace source.
- Codex: install or sync the folder into
$CODEX_HOME/skills/<skill-name>and restart Codex after major changes. - Gemini CLI: this repository generates a project command named
/skills:microsoft-developmentfrom this skill. Rebuild commands withpython scripts/export-gemini-skill.py microsoft-developmentand then run/commands reloadinside Gemini CLI.
<!-- PORTABILITY:END -->
<!-- MCP:START -->
MCP Availability And Fallback
Preferred MCP Server: Microsoft Learn Docs MCP
- Fallback prompt: "Use the Microsoft Development skill without MCP. Rely on the local
SKILL.md, bundled references or scripts, and manual verification. Show the exact commands, evidence, and final checks you used before concluding." - Use Microsoft Learn in a browser and local SDK or CLI documentation when the docs MCP server is unavailable.
- Verify generated commands or samples with the native toolchain (
dotnet,az, PowerShell, etc.) before shipping them.
<!-- MCP:END -->
Related Skills
- azure-integrations: Use it when the workflow also needs Azure deployment and infrastructure automation.
- powerbi-modeling: Use it when the workflow also needs Power BI semantic model design and DAX work.
- sql-development: Use it when the workflow also needs SQL query, schema, and performance tuning work.
- documentation-authoring: Use it when the workflow also needs drafting structured technical or product documents.
Changelog
[2026-04-25] - Version 1.2 Verification Protocol Refresh
Added
- Added a
Verification Protocolsection with skill-specific pass/fail checks, one pressure-test scenario, and a measurable success metric. - Added guidance to leverage native parallel subagent dispatch and 200k+ context windows where available.
Changed
- Updated
SKILL.mdfrontmatter toversion: "1.2"andlast_updated: 2026-04-25. - Reframed activation guidance toward symptom -> action triggers and standardized two-stage review wording where applicable.
[2026-04-24] - Version 1.1 Refresh
Changed
- Updated the SKILL frontmatter version to
1.1for the 2026-04-24 catalog refresh. - Added an "Optimized for ..." note at the top so the guidance is anchored to current platform versions.
All notable changes to this skill will be documented in this file.
[2026-04-24] - Verification Follow-Up
Fixed
- Added the missing before-and-after deployment example and Common Pitfalls section so the framework and language style requirements are satisfied.
[2026-04-24] - Skill Refresh
Changed
- Standardized the SKILL frontmatter with version metadata, last-updated date, tags, and a concise catalog description.
- Reformatted the portability and MCP guidance with a preferred server line, a copy-paste fallback prompt, and consistent bullet lists.
- Added a catalog-standard Anti-Patterns section and refreshed the Related Skills links at the end of the skill.
[2026-04-04] - Cross-Client Portability Refresh
Changed
- Added a standard portability note covering GitHub Copilot, Claude Code, Codex, and Gemini CLI.
- Documented the preferred MCP server surface for this skill and a local no-MCP fallback workflow.
Tested
- Validated
SKILL.mdfrontmatter, portability sections, and Gemini export readiness withpython scripts/validate-skills.py.
[2026-03-09] - Workspace Modernization
Changed
- Rewrote the skill around the current Microsoft Learn Docs MCP workflow and documented the currently published tool names
Added
references/microsoft-learn-mcp.mdwith the current install command and tool summary
Fixed
- Replaced
scripts/azure-health-check.ps1with a valid PowerShell health-check script
[2026-02-28] — Description Rewrite & Cross-References
Changed
- Rewrote skill description to ~200 characters with clear, specific activation keywords
- Improved keyword specificity to reduce overlap with related skills
Added
## Related Skillscross-reference table with 2-4 related skills and "Use When" guidance
Azure Functions REST API Example
Complete Azure Functions v4 (isolated process) HTTP API with Cosmos DB, authentication, OpenAPI, and CI/CD.
---
Project Structure
RecipeApi/
RecipeApi.csproj
Program.cs
host.json
local.settings.json
Models/
Recipe.cs
CreateRecipeRequest.cs
UpdateRecipeRequest.cs
Functions/
RecipeFunctions.cs
Services/
IRecipeService.cs
CosmosRecipeService.cs
.github/
workflows/
deploy.yml---
Configuration Files
RecipeApi.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.22.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.4" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.3.2" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.CosmosDB" Version="4.8.0" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.39.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.OpenApi" Version="1.5.1" />
<PackageReference Include="Microsoft.Identity.Web" Version="2.19.0" />
</ItemGroup>
</Project>Program.cs
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using RecipeApi.Services;
var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureServices((context, services) =>
{
services.AddApplicationInsightsTelemetryWorkerService();
services.ConfigureFunctionsApplicationInsights();
// Cosmos DB client — singleton for connection pooling
services.AddSingleton(sp =>
{
var connectionString = context.Configuration["CosmosDb:ConnectionString"];
return new CosmosClient(connectionString, new CosmosClientOptions
{
SerializerOptions = new CosmosSerializationOptions
{
PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase
}
});
});
services.AddSingleton<IRecipeService>(sp =>
{
var client = sp.GetRequiredService<CosmosClient>();
var databaseName = context.Configuration["CosmosDb:DatabaseName"] ?? "RecipeDb";
var containerName = context.Configuration["CosmosDb:ContainerName"] ?? "Recipes";
return new CosmosRecipeService(client, databaseName, containerName);
});
})
.Build();
host.Run();host.json
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
},
"enableLiveMetricsFilters": true
},
"logLevel": {
"default": "Information",
"Host.Results": "Error",
"Function": "Information"
}
},
"extensions": {
"http": {
"routePrefix": "api"
}
}
}local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
"CosmosDb:ConnectionString": "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
"CosmosDb:DatabaseName": "RecipeDb",
"CosmosDb:ContainerName": "Recipes"
}
}---
Models
Models/Recipe.cs
using System.Text.Json.Serialization;
namespace RecipeApi.Models;
public class Recipe
{
[JsonPropertyName("id")]
public string Id { get; set; } = Guid.NewGuid().ToString();
[JsonPropertyName("title")]
public string Title { get; set; } = string.Empty;
[JsonPropertyName("description")]
public string Description { get; set; } = string.Empty;
[JsonPropertyName("ingredients")]
public List<string> Ingredients { get; set; } = [];
[JsonPropertyName("instructions")]
public List<string> Instructions { get; set; } = [];
[JsonPropertyName("prepTimeMinutes")]
public int PrepTimeMinutes { get; set; }
[JsonPropertyName("difficulty")]
public string Difficulty { get; set; } = "Easy";
[JsonPropertyName("tags")]
public List<string> Tags { get; set; } = [];
[JsonPropertyName("authorId")]
public string AuthorId { get; set; } = string.Empty;
[JsonPropertyName("createdAt")]
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
[JsonPropertyName("updatedAt")]
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}Models/CreateRecipeRequest.cs
namespace RecipeApi.Models;
public class CreateRecipeRequest
{
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public List<string> Ingredients { get; set; } = [];
public List<string> Instructions { get; set; } = [];
public int PrepTimeMinutes { get; set; }
public string Difficulty { get; set; } = "Easy";
public List<string> Tags { get; set; } = [];
}Models/UpdateRecipeRequest.cs
namespace RecipeApi.Models;
public class UpdateRecipeRequest
{
public string? Title { get; set; }
public string? Description { get; set; }
public List<string>? Ingredients { get; set; }
public List<string>? Instructions { get; set; }
public int? PrepTimeMinutes { get; set; }
public string? Difficulty { get; set; }
public List<string>? Tags { get; set; }
}---
Services
Services/IRecipeService.cs
using RecipeApi.Models;
namespace RecipeApi.Services;
public interface IRecipeService
{
Task<IEnumerable<Recipe>> GetAllAsync(int limit = 50, string? continuationToken = null);
Task<Recipe?> GetByIdAsync(string id);
Task<Recipe> CreateAsync(Recipe recipe);
Task<Recipe?> UpdateAsync(string id, UpdateRecipeRequest request);
Task<bool> DeleteAsync(string id);
Task<IEnumerable<Recipe>> SearchAsync(string query);
}Services/CosmosRecipeService.cs
using Microsoft.Azure.Cosmos;
using RecipeApi.Models;
namespace RecipeApi.Services;
public class CosmosRecipeService : IRecipeService
{
private readonly Container _container;
public CosmosRecipeService(CosmosClient client, string databaseName, string containerName)
{
_container = client.GetContainer(databaseName, containerName);
}
public async Task<IEnumerable<Recipe>> GetAllAsync(int limit = 50, string? continuationToken = null)
{
var query = new QueryDefinition("SELECT * FROM c ORDER BY c.createdAt DESC OFFSET 0 LIMIT @limit")
.WithParameter("@limit", limit);
var results = new List<Recipe>();
using var iterator = _container.GetItemQueryIterator<Recipe>(query);
while (iterator.HasMoreResults)
{
var response = await iterator.ReadNextAsync();
results.AddRange(response);
}
return results;
}
public async Task<Recipe?> GetByIdAsync(string id)
{
try
{
var response = await _container.ReadItemAsync<Recipe>(id, new PartitionKey(id));
return response.Resource;
}
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return null;
}
}
public async Task<Recipe> CreateAsync(Recipe recipe)
{
recipe.Id = Guid.NewGuid().ToString();
recipe.CreatedAt = DateTime.UtcNow;
recipe.UpdatedAt = DateTime.UtcNow;
var response = await _container.CreateItemAsync(recipe, new PartitionKey(recipe.Id));
return response.Resource;
}
public async Task<Recipe?> UpdateAsync(string id, UpdateRecipeRequest request)
{
var existing = await GetByIdAsync(id);
if (existing is null) return null;
if (request.Title is not null) existing.Title = request.Title;
if (request.Description is not null) existing.Description = request.Description;
if (request.Ingredients is not null) existing.Ingredients = request.Ingredients;
if (request.Instructions is not null) existing.Instructions = request.Instructions;
if (request.PrepTimeMinutes.HasValue) existing.PrepTimeMinutes = request.PrepTimeMinutes.Value;
if (request.Difficulty is not null) existing.Difficulty = request.Difficulty;
if (request.Tags is not null) existing.Tags = request.Tags;
existing.UpdatedAt = DateTime.UtcNow;
var response = await _container.ReplaceItemAsync(existing, id, new PartitionKey(id));
return response.Resource;
}
public async Task<bool> DeleteAsync(string id)
{
try
{
await _container.DeleteItemAsync<Recipe>(id, new PartitionKey(id));
return true;
}
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return false;
}
}
public async Task<IEnumerable<Recipe>> SearchAsync(string query)
{
var sqlQuery = new QueryDefinition(
"SELECT * FROM c WHERE CONTAINS(LOWER(c.title), @query) OR CONTAINS(LOWER(c.description), @query)")
.WithParameter("@query", query.ToLower());
var results = new List<Recipe>();
using var iterator = _container.GetItemQueryIterator<Recipe>(sqlQuery);
while (iterator.HasMoreResults)
{
var response = await iterator.ReadNextAsync();
results.AddRange(response);
}
return results;
}
}---
Functions
Functions/RecipeFunctions.cs
using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using RecipeApi.Models;
using RecipeApi.Services;
namespace RecipeApi.Functions;
public class RecipeFunctions
{
private readonly IRecipeService _recipeService;
private readonly ILogger<RecipeFunctions> _logger;
public RecipeFunctions(IRecipeService recipeService, ILogger<RecipeFunctions> logger)
{
_recipeService = recipeService;
_logger = logger;
}
[Function("GetRecipes")]
[OpenApiOperation(operationId: "GetRecipes", tags: ["Recipes"])]
[OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json",
bodyType: typeof(IEnumerable<Recipe>), Description: "List of recipes")]
public async Task<HttpResponseData> GetRecipes(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "recipes")] HttpRequestData req)
{
_logger.LogInformation("Getting all recipes");
var recipes = await _recipeService.GetAllAsync();
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(recipes);
return response;
}
[Function("GetRecipeById")]
[OpenApiOperation(operationId: "GetRecipeById", tags: ["Recipes"])]
[OpenApiParameter(name: "id", In = ParameterLocation.Path, Required = true, Type = typeof(string))]
[OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json",
bodyType: typeof(Recipe), Description: "Recipe details")]
[OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NotFound, Description: "Recipe not found")]
public async Task<HttpResponseData> GetRecipeById(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "recipes/{id}")] HttpRequestData req,
string id)
{
_logger.LogInformation("Getting recipe {RecipeId}", id);
var recipe = await _recipeService.GetByIdAsync(id);
if (recipe is null)
{
return req.CreateResponse(HttpStatusCode.NotFound);
}
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(recipe);
return response;
}
[Function("CreateRecipe")]
[OpenApiOperation(operationId: "CreateRecipe", tags: ["Recipes"])]
[OpenApiSecurity("bearer_auth", SecuritySchemeType.Http, Scheme = OpenApiSecuritySchemeType.Bearer)]
[OpenApiRequestBody(contentType: "application/json", bodyType: typeof(CreateRecipeRequest), Required = true)]
[OpenApiResponseWithBody(statusCode: HttpStatusCode.Created, contentType: "application/json",
bodyType: typeof(Recipe), Description: "Created recipe")]
[OpenApiResponseWithoutBody(statusCode: HttpStatusCode.BadRequest, Description: "Validation error")]
public async Task<HttpResponseData> CreateRecipe(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "recipes")] HttpRequestData req)
{
var request = await req.ReadFromJsonAsync<CreateRecipeRequest>();
if (request is null || string.IsNullOrWhiteSpace(request.Title))
{
var badRequest = req.CreateResponse(HttpStatusCode.BadRequest);
await badRequest.WriteAsJsonAsync(new { error = "Title is required" });
return badRequest;
}
var recipe = new Recipe
{
Title = request.Title,
Description = request.Description,
Ingredients = request.Ingredients,
Instructions = request.Instructions,
PrepTimeMinutes = request.PrepTimeMinutes,
Difficulty = request.Difficulty,
Tags = request.Tags,
AuthorId = GetUserId(req)
};
var created = await _recipeService.CreateAsync(recipe);
_logger.LogInformation("Created recipe {RecipeId}: {Title}", created.Id, created.Title);
var response = req.CreateResponse(HttpStatusCode.Created);
response.Headers.Add("Location", $"/api/recipes/{created.Id}");
await response.WriteAsJsonAsync(created);
return response;
}
[Function("UpdateRecipe")]
[OpenApiOperation(operationId: "UpdateRecipe", tags: ["Recipes"])]
[OpenApiSecurity("bearer_auth", SecuritySchemeType.Http, Scheme = OpenApiSecuritySchemeType.Bearer)]
[OpenApiParameter(name: "id", In = ParameterLocation.Path, Required = true, Type = typeof(string))]
[OpenApiRequestBody(contentType: "application/json", bodyType: typeof(UpdateRecipeRequest), Required = true)]
[OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json",
bodyType: typeof(Recipe), Description: "Updated recipe")]
[OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NotFound, Description: "Recipe not found")]
public async Task<HttpResponseData> UpdateRecipe(
[HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = "recipes/{id}")] HttpRequestData req,
string id)
{
var request = await req.ReadFromJsonAsync<UpdateRecipeRequest>();
if (request is null)
{
return req.CreateResponse(HttpStatusCode.BadRequest);
}
var updated = await _recipeService.UpdateAsync(id, request);
if (updated is null)
{
return req.CreateResponse(HttpStatusCode.NotFound);
}
_logger.LogInformation("Updated recipe {RecipeId}", id);
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(updated);
return response;
}
[Function("DeleteRecipe")]
[OpenApiOperation(operationId: "DeleteRecipe", tags: ["Recipes"])]
[OpenApiSecurity("bearer_auth", SecuritySchemeType.Http, Scheme = OpenApiSecuritySchemeType.Bearer)]
[OpenApiParameter(name: "id", In = ParameterLocation.Path, Required = true, Type = typeof(string))]
[OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Description: "Recipe deleted")]
[OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NotFound, Description: "Recipe not found")]
public async Task<HttpResponseData> DeleteRecipe(
[HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = "recipes/{id}")] HttpRequestData req,
string id)
{
var deleted = await _recipeService.DeleteAsync(id);
if (!deleted)
{
return req.CreateResponse(HttpStatusCode.NotFound);
}
_logger.LogInformation("Deleted recipe {RecipeId}", id);
return req.CreateResponse(HttpStatusCode.NoContent);
}
[Function("SearchRecipes")]
[OpenApiOperation(operationId: "SearchRecipes", tags: ["Recipes"])]
[OpenApiParameter(name: "q", In = ParameterLocation.Query, Required = true, Type = typeof(string))]
[OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json",
bodyType: typeof(IEnumerable<Recipe>), Description: "Search results")]
public async Task<HttpResponseData> SearchRecipes(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "recipes/search")] HttpRequestData req)
{
var query = System.Web.HttpUtility.ParseQueryString(req.Url.Query).Get("q");
if (string.IsNullOrWhiteSpace(query))
{
var badRequest = req.CreateResponse(HttpStatusCode.BadRequest);
await badRequest.WriteAsJsonAsync(new { error = "Query parameter 'q' is required" });
return badRequest;
}
var results = await _recipeService.SearchAsync(query);
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(results);
return response;
}
private static string GetUserId(HttpRequestData req)
{
// In production, extract from JWT claims
// req.Headers.TryGetValues("Authorization", out var authHeaders);
return "anonymous";
}
}---
Local Development
Prerequisites
# Install Azure Functions Core Tools
npm install -g azure-functions-core-tools@4
# Install Cosmos DB Emulator (Windows) or use Docker
# Windows: Download from https://aka.ms/cosmosdb-emulator
# Docker:
docker run -p 8081:8081 -p 10250-10255:10250-10255 mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulatorCreate Database and Container
# Using Azure CLI (for cloud Cosmos DB)
az cosmosdb sql database create \
--account-name mycosmosaccount \
--resource-group my-rg \
--name RecipeDb
az cosmosdb sql container create \
--account-name mycosmosaccount \
--resource-group my-rg \
--database-name RecipeDb \
--name Recipes \
--partition-key-path "/id" \
--throughput 400Run Locally
# Restore and build
dotnet restore
dotnet build
# Start function app
func start
# Test endpoints
curl http://localhost:7071/api/recipes
curl -X POST http://localhost:7071/api/recipes \
-H "Content-Type: application/json" \
-d '{"title":"Test Pasta","description":"Delicious","ingredients":["pasta","sauce"],"instructions":["boil","mix"],"prepTimeMinutes":20}'
# OpenAPI/Swagger UI
# Navigate to http://localhost:7071/api/swagger/ui---
Deployment
Application Settings (Azure Portal / CLI)
az functionapp config appsettings set \
--name my-recipe-api \
--resource-group my-rg \
--settings \
"CosmosDb:ConnectionString=AccountEndpoint=https://mycosmosaccount.documents.azure.com:443/;AccountKey=..." \
"CosmosDb:DatabaseName=RecipeDb" \
"CosmosDb:ContainerName=Recipes"GitHub Actions Workflow
.github/workflows/deploy.yml
name: Deploy Azure Functions
on:
push:
branches: [main]
workflow_dispatch:
env:
AZURE_FUNCTIONAPP_NAME: my-recipe-api
DOTNET_VERSION: 8.0.x
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Publish
run: dotnet publish --configuration Release --output ./publish --no-build
- name: Deploy to Azure Functions
uses: Azure/functions-action@v1
with:
app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
package: ./publish
publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }}
- name: Smoke test
run: |
sleep 30
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" https://${{ env.AZURE_FUNCTIONAPP_NAME }}.azurewebsites.net/api/recipes)
if [ "$RESPONSE" != "200" ]; then
echo "Smoke test failed with status $RESPONSE"
exit 1
fi
echo "Smoke test passed"---
API Endpoints Summary
| Method | Route | Auth | Description |
|---|---|---|---|
| GET | /api/recipes | Anonymous | List all recipes |
| GET | /api/recipes/{id} | Anonymous | Get recipe by ID |
| POST | /api/recipes | Bearer | Create new recipe |
| PUT | /api/recipes/{id} | Bearer | Update recipe |
| DELETE | /api/recipes/{id} | Bearer | Delete recipe |
| GET | /api/recipes/search?q= | Anonymous | Search recipes |
| GET | /api/swagger/ui | Anonymous | OpenAPI docs |
MIT License
Copyright (c) 2026 Sithu Win San
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.Azure Services Quick Reference
Organized by category with descriptions, use cases, pricing hints, and SDK package names.
---
Compute
App Service
Web app hosting with managed platform, auto-scale, deployment slots, and custom domains.
- Use cases: Web APIs, SPAs, full-stack apps, containerized web apps
- Pricing hint: Free (F1), Basic (B1 ~$13/mo), Standard (S1 ~$73/mo), Premium (P1v3 ~$138/mo)
- SDK packages:
- JS:
@azure/arm-appservice - Python:
azure-mgmt-web - .NET:
Azure.ResourceManager.AppService
Azure Functions
Serverless event-driven compute — pay only for execution time.
- Use cases: HTTP APIs, scheduled jobs, event processing, webhooks, background tasks
- Pricing hint: Consumption plan: 1M executions free/month, ~$0.20 per additional million
- SDK packages:
- JS:
@azure/functions(v4 programming model) - Python:
azure-functions - .NET:
Microsoft.Azure.Functions.Worker
Container Apps
Serverless containers with built-in scaling, Dapr integration, and revision management.
- Use cases: Microservices, API gateways, background workers, event-driven apps
- Pricing hint: Consumption: ~$0.000012/vCPU-second, 180K vCPU-seconds free/month
- SDK packages:
- JS:
@azure/arm-appcontainers - Python:
azure-mgmt-appcontainers - .NET:
Azure.ResourceManager.AppContainers
Azure Kubernetes Service (AKS)
Managed Kubernetes with integrated CI/CD, monitoring, and governance.
- Use cases: Complex microservice architectures, multi-container apps, ML workloads
- Pricing hint: Control plane free; pay for VMs (e.g., Standard_D2s_v5 ~$70/mo)
- SDK packages:
- JS:
@azure/arm-containerservice - Python:
azure-mgmt-containerservice - .NET:
Azure.ResourceManager.ContainerService
Virtual Machines
Full IaaS with Linux/Windows VMs, SSH/RDP, and custom images.
- Use cases: Legacy apps, custom OS needs, GPU workloads, development/test
- Pricing hint: B1s (1 vCPU, 1GB) ~$7.50/mo; D2s_v5 (2 vCPU, 8GB) ~$70/mo
- SDK packages:
- JS:
@azure/arm-compute - Python:
azure-mgmt-compute - .NET:
Azure.ResourceManager.Compute
---
Storage
Blob Storage
Object storage for unstructured data — files, images, videos, backups.
- Use cases: Static file hosting, media storage, data lake, backup/archive
- Pricing hint: Hot: ~$0.018/GB/mo, Cool: ~$0.01/GB/mo, Archive: ~$0.002/GB/mo
- SDK packages:
- JS:
@azure/storage-blob - Python:
azure-storage-blob - .NET:
Azure.Storage.Blobs
Table Storage
NoSQL key-value store for semi-structured data at massive scale.
- Use cases: User profiles, device metadata, IoT data, config storage
- Pricing hint: ~$0.045/GB/mo storage, ~$0.00036/10K transactions
- SDK packages:
- JS:
@azure/data-tables - Python:
azure-data-tables - .NET:
Azure.Data.Tables
Queue Storage
Simple message queueing between application components.
- Use cases: Task queues, decoupled processing, load leveling
- Pricing hint: ~$0.004/10K operations
- SDK packages:
- JS:
@azure/storage-queue - Python:
azure-storage-queue - .NET:
Azure.Storage.Queues
Azure Files
SMB/NFS file shares in the cloud, mountable on Windows/Linux/macOS.
- Use cases: Shared app storage, file migration, hybrid file serving
- Pricing hint: Hot: ~$0.06/GB/mo (premium SSD ~$0.16/GB/mo)
- SDK packages:
- JS:
@azure/storage-file-share - Python:
azure-storage-file-share - .NET:
Azure.Storage.Files.Shares
Data Lake Storage Gen2
Hierarchical namespace on top of Blob Storage — optimized for analytics.
- Use cases: Big data analytics, Hadoop/Spark workloads, data lakehouse
- Pricing hint: Similar to Blob (Hot: ~$0.018/GB/mo) with hierarchical namespace surcharge
- SDK packages:
- JS:
@azure/storage-file-datalake - Python:
azure-storage-file-datalake - .NET:
Azure.Storage.Files.DataLake
---
Database
Cosmos DB
Globally distributed, multi-model (SQL, MongoDB, Cassandra, Gremlin, Table) database.
- Use cases: Global apps, real-time personalization, IoT, gaming leaderboards
- Pricing hint: Serverless from ~$0.25/1M RUs; Provisioned 400 RU/s ~$24/mo
- SDK packages:
- JS:
@azure/cosmos - Python:
azure-cosmos - .NET:
Microsoft.Azure.Cosmos
Azure SQL Database
Managed SQL Server with built-in intelligence, scaling, and high availability.
- Use cases: Relational apps, SaaS, reporting, ERP backends
- Pricing hint: Basic (5 DTU) ~$5/mo; S0 (10 DTU) ~$15/mo; serverless from ~$0.514/vCore-hour
- SDK packages:
- JS:
mssqlortedious - Python:
pyodbc,azure-mgmt-sql - .NET:
Microsoft.Data.SqlClient
Azure Database for PostgreSQL
Managed PostgreSQL with Flexible Server and Citus (distributed).
- Use cases: Web apps, geospatial, JSON workloads, multi-tenant SaaS
- Pricing hint: Burstable B1ms ~$12/mo; General Purpose D2s ~$125/mo
- SDK packages:
- JS:
pg - Python:
psycopg2,azure-mgmt-rdbms - .NET:
Npgsql
Azure Cache for Redis
In-memory data store for caching, session management, and real-time analytics.
- Use cases: Session cache, output cache, rate limiting, pub/sub messaging
- Pricing hint: Basic C0 (250MB) ~$16/mo; Standard C1 (1GB) ~$80/mo
- SDK packages:
- JS:
ioredisorredis - Python:
redis,azure-mgmt-redis - .NET:
StackExchange.Redis
---
Networking
Azure Front Door
Global load balancer with WAF, CDN, and intelligent routing.
- Use cases: Global web apps, multi-region HA, DDoS protection, SSL offloading
- Pricing hint: Standard ~$35/mo base + per-request pricing
- SDK packages:
- JS:
@azure/arm-frontdoor - Python:
azure-mgmt-frontdoor - .NET:
Azure.ResourceManager.FrontDoor
Azure CDN
Content delivery network for static assets and media streaming.
- Use cases: Static site hosting, media delivery, API acceleration
- Pricing hint: Standard Microsoft: ~$0.081/GB (first 10TB)
- SDK packages:
- JS:
@azure/arm-cdn - Python:
azure-mgmt-cdn - .NET:
Azure.ResourceManager.Cdn
Application Gateway
Regional L7 load balancer with WAF, URL routing, and SSL termination.
- Use cases: Internal web apps, API routing, SSL offloading, WAF protection
- Pricing hint: Standard_v2 ~$175/mo + capacity unit charges
- SDK packages:
- JS:
@azure/arm-network - Python:
azure-mgmt-network - .NET:
Azure.ResourceManager.Network
---
Identity
Microsoft Entra ID (formerly Azure AD)
Cloud identity and access management — SSO, MFA, conditional access.
- Use cases: User authentication, SSO, B2B collaboration, app registration
- Pricing hint: Free tier includes basic AAD; P1 ~$6/user/mo, P2 ~$9/user/mo
- SDK packages:
- JS:
@azure/identity,@azure/msal-browser,@azure/msal-node - Python:
azure-identity,msal - .NET:
Azure.Identity,Microsoft.Identity.Web
MSAL (Microsoft Authentication Library)
Client libraries for authenticating users and acquiring tokens.
- Use cases: SPA login, Node.js API auth, desktop app auth, daemon apps
- Pricing hint: Free (part of Entra ID)
- SDK packages:
- JS:
@azure/msal-browser(SPA),@azure/msal-node(server) - Python:
msal - .NET:
Microsoft.Identity.Client
---
Messaging
Service Bus
Enterprise message broker with queues, topics, and subscriptions.
- Use cases: Microservice communication, ordered messaging, transaction processing
- Pricing hint: Basic ~$0.05/1M operations; Standard ~$10/mo + per-message
- SDK packages:
- JS:
@azure/service-bus - Python:
azure-servicebus - .NET:
Azure.Messaging.ServiceBus
Event Grid
Reactive event routing with per-event push delivery model.
- Use cases: Resource change notifications, serverless triggers, event-driven architectures
- Pricing hint: ~$0.60/1M operations (first 100K free/month)
- SDK packages:
- JS:
@azure/eventgrid - Python:
azure-eventgrid - .NET:
Azure.Messaging.EventGrid
Event Hubs
Big data streaming platform — millions of events per second.
- Use cases: Telemetry ingestion, log streaming, real-time analytics, Kafka replacement
- Pricing hint: Basic ~$11/TU/mo; Standard ~$22/TU/mo
- SDK packages:
- JS:
@azure/event-hubs - Python:
azure-eventhub - .NET:
Azure.Messaging.EventHubs
---
AI & Machine Learning
Azure OpenAI Service
GPT-4, GPT-4o, DALL-E, Whisper — enterprise-grade with content filtering.
- Use cases: Chatbots, content generation, document analysis, code assistance
- Pricing hint: GPT-4o ~$2.50/1M input tokens, $10/1M output tokens (varies by model)
- SDK packages:
- JS:
openai(with Azure config) or@azure/openai - Python:
openai(with Azure config) orazure-ai-openai - .NET:
Azure.AI.OpenAI
Azure AI Services (formerly Cognitive Services)
Pre-built AI models — vision, speech, language, decision.
- Use cases: OCR, image classification, speech-to-text, sentiment analysis, translation
- Pricing hint: Free tiers available; pay-per-transaction (e.g., Vision: ~$1/1K transactions)
- SDK packages:
- JS:
@azure/ai-text-analytics,@azure/ai-vision-image-analysis,@azure/ai-form-recognizer - Python:
azure-ai-textanalytics,azure-ai-vision,azure-ai-formrecognizer - .NET:
Azure.AI.TextAnalytics,Azure.AI.Vision.ImageAnalysis,Azure.AI.FormRecognizer
Azure Machine Learning
MLOps platform — model training, deployment, monitoring, and pipelines.
- Use cases: Custom model training, AutoML, model registry, batch inference
- Pricing hint: Free workspace; pay for compute (e.g., NC6s_v3 GPU ~$3.06/hr)
- SDK packages:
- JS:
@azure/arm-machinelearning - Python:
azure-ai-ml,azureml-core - .NET:
Azure.ResourceManager.MachineLearning
.NET Common Patterns Reference
Essential patterns for modern .NET development with C# code examples.
---
Dependency Injection
.NET's built-in DI container in Microsoft.Extensions.DependencyInjection.
Service Registration
var builder = WebApplication.CreateBuilder(args);
// Transient — new instance per request
builder.Services.AddTransient<IEmailService, SmtpEmailService>();
// Scoped — one per HTTP request
builder.Services.AddScoped<IRecipeRepository, RecipeRepository>();
// Singleton — shared across all requests
builder.Services.AddSingleton<ICacheService, MemoryCacheService>();
// Factory registration
builder.Services.AddScoped<INotificationService>(sp =>
{
var config = sp.GetRequiredService<IOptions<NotificationOptions>>();
return new NotificationService(config.Value.Provider);
});Constructor Injection
public class RecipeService
{
private readonly IRecipeRepository _repository;
private readonly ILogger<RecipeService> _logger;
public RecipeService(IRecipeRepository repository, ILogger<RecipeService> logger)
{
_repository = repository;
_logger = logger;
}
public async Task<Recipe?> GetByIdAsync(int id)
{
_logger.LogInformation("Fetching recipe {RecipeId}", id);
return await _repository.GetByIdAsync(id);
}
}---
Options Pattern (IOptions)
Strongly typed configuration binding.
Configuration Classes
public class DatabaseOptions
{
public const string SectionName = "Database";
public string ConnectionString { get; set; } = string.Empty;
public int MaxRetryCount { get; set; } = 3;
public int CommandTimeoutSeconds { get; set; } = 30;
}
public class JwtOptions
{
public const string SectionName = "Jwt";
public string Secret { get; set; } = string.Empty;
public string Issuer { get; set; } = string.Empty;
public string Audience { get; set; } = string.Empty;
public int ExpirationMinutes { get; set; } = 60;
}Registration and Usage
// In Program.cs
builder.Services.Configure<DatabaseOptions>(
builder.Configuration.GetSection(DatabaseOptions.SectionName));
builder.Services.Configure<JwtOptions>(
builder.Configuration.GetSection(JwtOptions.SectionName));
// In a service — use IOptions<T> for singleton, IOptionsSnapshot<T>
// for scoped (reloads on change), IOptionsMonitor<T> for real-time
public class RecipeRepository
{
private readonly DatabaseOptions _dbOptions;
public RecipeRepository(IOptions<DatabaseOptions> options)
{
_dbOptions = options.Value;
}
}appsettings.json
{
"Database": {
"ConnectionString": "Server=localhost;Database=RecipeDb;Trusted_Connection=true;",
"MaxRetryCount": 3,
"CommandTimeoutSeconds": 30
},
"Jwt": {
"Secret": "your-secret-key-at-least-32-characters-long",
"Issuer": "KitchenOdyssey",
"Audience": "KitchenOdysseyUsers",
"ExpirationMinutes": 60
}
}---
Middleware Pipeline
Request/response processing pipeline.
Custom Middleware
public class RequestTimingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestTimingMiddleware> _logger;
public RequestTimingMiddleware(RequestDelegate next, ILogger<RequestTimingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
var stopwatch = Stopwatch.StartNew();
context.Response.OnStarting(() =>
{
stopwatch.Stop();
context.Response.Headers["X-Response-Time"] = $"{stopwatch.ElapsedMilliseconds}ms";
return Task.CompletedTask;
});
await _next(context);
_logger.LogInformation(
"{Method} {Path} responded {StatusCode} in {Elapsed}ms",
context.Request.Method,
context.Request.Path,
context.Response.StatusCode,
stopwatch.ElapsedMilliseconds);
}
}
// Extension method for clean registration
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseRequestTiming(this IApplicationBuilder app)
=> app.UseMiddleware<RequestTimingMiddleware>();
}Pipeline Order
var app = builder.Build();
// Order matters — each middleware wraps the next
app.UseExceptionHandler("/error");
app.UseHsts();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("AllowFrontend");
app.UseRequestTiming(); // Custom
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();---
Minimal APIs
Concise endpoint definitions without controllers.
Basic CRUD
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IRecipeRepository, RecipeRepository>();
var app = builder.Build();
var recipes = app.MapGroup("/api/recipes").WithTags("Recipes");
recipes.MapGet("/", async (IRecipeRepository repo) =>
Results.Ok(await repo.GetAllAsync()));
recipes.MapGet("/{id:int}", async (int id, IRecipeRepository repo) =>
await repo.GetByIdAsync(id) is { } recipe
? Results.Ok(recipe)
: Results.NotFound());
recipes.MapPost("/", async (CreateRecipeRequest request, IRecipeRepository repo) =>
{
var recipe = await repo.CreateAsync(request);
return Results.Created($"/api/recipes/{recipe.Id}", recipe);
});
recipes.MapPut("/{id:int}", async (int id, UpdateRecipeRequest request, IRecipeRepository repo) =>
await repo.UpdateAsync(id, request)
? Results.NoContent()
: Results.NotFound());
recipes.MapDelete("/{id:int}", async (int id, IRecipeRepository repo) =>
await repo.DeleteAsync(id)
? Results.NoContent()
: Results.NotFound());
app.Run();Request Validation with Filters
recipes.MapPost("/", async (CreateRecipeRequest request, IRecipeRepository repo) =>
{
var recipe = await repo.CreateAsync(request);
return Results.Created($"/api/recipes/{recipe.Id}", recipe);
})
.AddEndpointFilter(async (context, next) =>
{
var request = context.GetArgument<CreateRecipeRequest>(0);
if (string.IsNullOrWhiteSpace(request.Title))
return Results.ValidationProblem(
new Dictionary<string, string[]> { ["Title"] = ["Title is required"] });
return await next(context);
});---
Entity Framework Core
DbContext
public class RecipeDbContext : DbContext
{
public RecipeDbContext(DbContextOptions<RecipeDbContext> options) : base(options) { }
public DbSet<Recipe> Recipes => Set<Recipe>();
public DbSet<Ingredient> Ingredients => Set<Ingredient>();
public DbSet<User> Users => Set<User>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Recipe>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Title).HasMaxLength(200).IsRequired();
entity.Property(e => e.Description).HasMaxLength(2000);
entity.HasMany(e => e.Ingredients).WithOne(i => i.Recipe);
entity.HasOne(e => e.Author).WithMany(u => u.Recipes);
entity.HasIndex(e => e.Title);
});
}
}Registration
builder.Services.AddDbContext<RecipeDbContext>(options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString("Default"),
sqlOptions => sqlOptions.EnableRetryOnFailure(3)));Migrations
# Create migration
dotnet ef migrations add InitialCreate
# Apply migration
dotnet ef database update
# Generate SQL script
dotnet ef migrations script --idempotent -o migration.sqlLINQ Queries
public class RecipeRepository : IRecipeRepository
{
private readonly RecipeDbContext _context;
public RecipeRepository(RecipeDbContext context) => _context = context;
public async Task<List<Recipe>> SearchAsync(string query, int page = 1, int pageSize = 20)
{
return await _context.Recipes
.Include(r => r.Ingredients)
.Include(r => r.Author)
.Where(r => r.Title.Contains(query) || r.Description.Contains(query))
.OrderByDescending(r => r.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.AsNoTracking()
.ToListAsync();
}
public async Task<Recipe?> GetByIdAsync(int id)
{
return await _context.Recipes
.Include(r => r.Ingredients)
.Include(r => r.Author)
.FirstOrDefaultAsync(r => r.Id == id);
}
}---
Configuration
Sources (loaded in order, last wins)
var builder = WebApplication.CreateBuilder(args);
// Default sources (auto-configured):
// 1. appsettings.json
// 2. appsettings.{Environment}.json
// 3. User secrets (Development only)
// 4. Environment variables
// 5. Command-line arguments
// Custom sources
builder.Configuration.AddJsonFile("custom-config.json", optional: true, reloadOnChange: true);
builder.Configuration.AddAzureKeyVault(new Uri("https://myvault.vault.azure.net/"), new DefaultAzureCredential());User Secrets (Development)
# Initialize user secrets
dotnet user-secrets init
# Set values
dotnet user-secrets set "Jwt:Secret" "my-dev-secret-key-12345678901234567890"
dotnet user-secrets set "Database:ConnectionString" "Server=localhost;Database=DevDb;"Environment Variables
# Convention: use __ (double underscore) for nested keys
Database__ConnectionString="Server=prod;Database=ProdDb;"
Jwt__Secret="production-secret"---
Logging (ILogger)
public class RecipeService
{
private readonly ILogger<RecipeService> _logger;
public RecipeService(ILogger<RecipeService> logger) => _logger = logger;
public async Task<Recipe?> GetByIdAsync(int id)
{
_logger.LogDebug("Fetching recipe {RecipeId}", id);
try
{
var recipe = await _repository.GetByIdAsync(id);
if (recipe is null)
{
_logger.LogWarning("Recipe {RecipeId} not found", id);
return null;
}
_logger.LogInformation("Retrieved recipe {RecipeId}: {Title}", id, recipe.Title);
return recipe;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to fetch recipe {RecipeId}", id);
throw;
}
}
}Configuration in appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
}
}---
Health Checks
builder.Services.AddHealthChecks()
.AddSqlServer(builder.Configuration.GetConnectionString("Default")!, name: "database")
.AddRedis(builder.Configuration["Redis:ConnectionString"]!, name: "redis")
.AddUrlGroup(new Uri("https://api.external.com/health"), name: "external-api");
var app = builder.Build();
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json";
var result = new
{
status = report.Status.ToString(),
checks = report.Entries.Select(e => new
{
name = e.Key,
status = e.Value.Status.ToString(),
duration = e.Value.Duration.TotalMilliseconds
})
};
await context.Response.WriteAsJsonAsync(result);
}
});---
Background Services (IHostedService)
public class RecipeCleanupService : BackgroundService
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly ILogger<RecipeCleanupService> _logger;
private readonly TimeSpan _interval = TimeSpan.FromHours(6);
public RecipeCleanupService(
IServiceScopeFactory scopeFactory,
ILogger<RecipeCleanupService> logger)
{
_scopeFactory = scopeFactory;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Recipe cleanup service started");
while (!stoppingToken.IsCancellationRequested)
{
try
{
using var scope = _scopeFactory.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<RecipeDbContext>();
var cutoff = DateTime.UtcNow.AddDays(-30);
var drafts = await context.Recipes
.Where(r => r.IsDraft && r.UpdatedAt < cutoff)
.ToListAsync(stoppingToken);
if (drafts.Count > 0)
{
context.Recipes.RemoveRange(drafts);
await context.SaveChangesAsync(stoppingToken);
_logger.LogInformation("Cleaned up {Count} stale drafts", drafts.Count);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during recipe cleanup");
}
await Task.Delay(_interval, stoppingToken);
}
}
}
// Registration
builder.Services.AddHostedService<RecipeCleanupService>();---
Authentication & Authorization
JWT Bearer Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
var jwtOptions = builder.Configuration.GetSection("Jwt").Get<JwtOptions>()!;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtOptions.Issuer,
ValidateAudience = true,
ValidAudience = jwtOptions.Audience,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(jwtOptions.Secret)),
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
options.AddPolicy("RecipeOwner", policy =>
policy.Requirements.Add(new RecipeOwnerRequirement()));
});Endpoint Authorization
// Minimal API
recipes.MapPost("/", CreateRecipe).RequireAuthorization();
recipes.MapDelete("/{id}", DeleteRecipe).RequireAuthorization("AdminOnly");
// Controller
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class RecipesController : ControllerBase
{
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> GetAll() { /* ... */ }
[HttpPost]
public async Task<IActionResult> Create(CreateRecipeRequest request) { /* ... */ }
[HttpDelete("{id}")]
[Authorize(Policy = "AdminOnly")]
public async Task<IActionResult> Delete(int id) { /* ... */ }
}Microsoft Learn MCP
This reference summarizes the currently documented Microsoft Learn Docs MCP server workflow as of March 2026.
Install
npx -y @microsoft/learn-docs-mcpPublicly Documented Tool Names
microsoft_docs_searchmicrosoft_docs_fetchmicrosoft_docs_extract_code_examplesmicrosoft_docs_search_by_product
Suggested Usage Order
1. microsoft_docs_search for broad discovery 2. microsoft_docs_search_by_product when the product family matters 3. microsoft_docs_fetch for the full document 4. microsoft_docs_extract_code_examples when implementation examples are needed
Good Product Filters
azuredotnetpower-bimicrosoft-graphwindowsmicrosoft-365
Practical Tips
- Include version numbers when relevant, for example
.NET 9,Azure Functions v4, orASP.NET Core 9. - Search for exact class names when verifying SDK usage.
- Use the fetched page when limits, pricing, or configuration details matter.
<#
.SYNOPSIS
Report the health of common Azure resources in a resource group.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ResourceGroup,
[string]$SubscriptionId
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
function Write-State {
param(
[string]$Name,
[string]$State,
[string]$Detail = ""
)
$color = switch ($State) {
"Healthy" { "Green" }
"Warning" { "Yellow" }
"Missing" { "DarkYellow" }
"Error" { "Red" }
default { "White" }
}
$suffix = if ($Detail) { " - $Detail" } else { "" }
Write-Host ("[{0}] {1}{2}" -f $State, $Name, $suffix) -ForegroundColor $color
}
function Assert-Command {
param([string]$Name)
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
throw "Required command '$Name' was not found in PATH."
}
}
Assert-Command az
az account show *> $null
if ($LASTEXITCODE -ne 0) {
throw "Azure CLI is not logged in. Run 'az login' first."
}
if ($SubscriptionId) {
az account set --subscription $SubscriptionId | Out-Null
}
$exists = az group exists --name $ResourceGroup
if ($exists -eq "false") {
Write-State -Name $ResourceGroup -State "Missing" -Detail "resource group does not exist"
exit 1
}
Write-State -Name $ResourceGroup -State "Healthy" -Detail "resource group found"
Write-Host ""
$webapps = az webapp list --resource-group $ResourceGroup | ConvertFrom-Json
if (-not $webapps) {
Write-State -Name "Web Apps" -State "Missing"
} else {
foreach ($app in $webapps) {
$state = if ($app.state -eq "Running") { "Healthy" } else { "Warning" }
Write-State -Name $app.name -State $state -Detail "$($app.state) $($app.defaultHostName)"
}
}
$functions = az functionapp list --resource-group $ResourceGroup | ConvertFrom-Json
if (-not $functions) {
Write-State -Name "Function Apps" -State "Missing"
} else {
foreach ($app in $functions) {
$state = if ($app.state -eq "Running") { "Healthy" } else { "Warning" }
Write-State -Name $app.name -State $state -Detail "$($app.state) $($app.defaultHostName)"
}
}
$storageAccounts = az storage account list --resource-group $ResourceGroup | ConvertFrom-Json
if (-not $storageAccounts) {
Write-State -Name "Storage Accounts" -State "Missing"
} else {
foreach ($account in $storageAccounts) {
$state = if ($account.provisioningState -eq "Succeeded") { "Healthy" } else { "Warning" }
Write-State -Name $account.name -State $state -Detail "$($account.kind) $($account.sku.name)"
}
}
$sqlServers = az sql server list --resource-group $ResourceGroup | ConvertFrom-Json
if (-not $sqlServers) {
Write-State -Name "SQL Servers" -State "Missing"
} else {
foreach ($server in $sqlServers) {
Write-State -Name $server.name -State "Healthy" -Detail $server.fullyQualifiedDomainName
}
}
$cosmosAccounts = az cosmosdb list --resource-group $ResourceGroup | ConvertFrom-Json
if (-not $cosmosAccounts) {
Write-State -Name "Cosmos DB" -State "Missing"
} else {
foreach ($account in $cosmosAccounts) {
$state = if ($account.provisioningState -eq "Succeeded") { "Healthy" } else { "Warning" }
Write-State -Name $account.name -State $state -Detail $account.kind
}
}
Related skills
FAQ
What does microsoft-development do?
microsoft-development is a Claude Code skill for ai & agent building.
When should I use microsoft-development?
When you need to helps with ai & agent building tasks., or when microsoft-development is a claude code skill for ai & agent building.
What are the main capabilities?
microsoft-development; AI & Agent Building; AI-coding skill.