feat: Add Official Microsoft & Gemini Skills (845+ Total)
🚀 Impact Significantly expands the capabilities of **Antigravity Awesome Skills** by integrating official skill collections from **Microsoft** and **Google Gemini**. This update increases the total skill count to **845+**, making the library even more comprehensive for AI coding assistants. ✨ Key Changes 1. New Official Skills - **Microsoft Skills**: Added a massive collection of official skills from [microsoft/skills](https://github.com/microsoft/skills). - Includes Azure, .NET, Python, TypeScript, and Semantic Kernel skills. - Preserves the original directory structure under `skills/official/microsoft/`. - Includes plugin skills from the `.github/plugins` directory. - **Gemini Skills**: Added official Gemini API development skills under `skills/gemini-api-dev/`. 2. New Scripts & Tooling - **`scripts/sync_microsoft_skills.py`**: A robust synchronization script that: - Clones the official Microsoft repository. - Preserves the original directory heirarchy. - Handles symlinks and plugin locations. - Generates attribution metadata. - **`scripts/tests/inspect_microsoft_repo.py`**: Debug tool to inspect the remote repository structure. - **`scripts/tests/test_comprehensive_coverage.py`**: Verification script to ensure 100% of skills are captured during sync. 3. Core Improvements - **`scripts/generate_index.py`**: Enhanced frontmatter parsing to safely handle unquoted values containing `@` symbols and commas (fixing issues with some Microsoft skill descriptions). - **`package.json`**: Added `sync:microsoft` and `sync:all-official` scripts for easy maintenance. 4. Documentation - Updated `README.md` to reflect the new skill counts (845+) and added Microsoft/Gemini to the provider list. - Updated `CATALOG.md` and `skills_index.json` with the new skills. 🧪 Verification - Ran `scripts/tests/test_comprehensive_coverage.py` to verify all Microsoft skills are detected. - Validated `generate_index.py` fixes by successfully indexing the new skills.
This commit is contained in:
334
skills/official/microsoft/dotnet/compute/botservice/SKILL.md
Normal file
334
skills/official/microsoft/dotnet/compute/botservice/SKILL.md
Normal file
@@ -0,0 +1,334 @@
|
||||
---
|
||||
name: azure-mgmt-botservice-dotnet
|
||||
description: |
|
||||
Azure Resource Manager SDK for Bot Service in .NET. Management plane operations for creating and managing Azure Bot resources, channels (Teams, DirectLine, Slack), and connection settings. Triggers: "Bot Service", "BotResource", "Azure Bot", "DirectLine channel", "Teams channel", "bot management .NET", "create bot".
|
||||
package: Azure.ResourceManager.BotService
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.BotService (.NET)
|
||||
|
||||
Management plane SDK for provisioning and managing Azure Bot Service resources via Azure Resource Manager.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.BotService
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Versions**: Stable v1.1.1, Preview v1.1.0-beta.1
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
# For service principal auth (optional)
|
||||
AZURE_TENANT_ID=<tenant-id>
|
||||
AZURE_CLIENT_ID=<client-id>
|
||||
AZURE_CLIENT_SECRET=<client-secret>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.BotService;
|
||||
|
||||
// Authenticate using DefaultAzureCredential
|
||||
var credential = new DefaultAzureCredential();
|
||||
ArmClient armClient = new ArmClient(credential);
|
||||
|
||||
// Get subscription and resource group
|
||||
SubscriptionResource subscription = await armClient.GetDefaultSubscriptionAsync();
|
||||
ResourceGroupResource resourceGroup = await subscription.GetResourceGroups().GetAsync("myResourceGroup");
|
||||
|
||||
// Access bot collection
|
||||
BotCollection botCollection = resourceGroup.GetBots();
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
ArmClient
|
||||
└── SubscriptionResource
|
||||
└── ResourceGroupResource
|
||||
└── BotResource
|
||||
├── BotChannelResource (DirectLine, Teams, Slack, etc.)
|
||||
├── BotConnectionSettingResource (OAuth connections)
|
||||
└── BotServicePrivateEndpointConnectionResource
|
||||
```
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Create Bot Resource
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.BotService;
|
||||
using Azure.ResourceManager.BotService.Models;
|
||||
|
||||
// Create bot data
|
||||
var botData = new BotData(AzureLocation.WestUS2)
|
||||
{
|
||||
Kind = BotServiceKind.Azurebot,
|
||||
Sku = new BotServiceSku(BotServiceSkuName.F0),
|
||||
Properties = new BotProperties(
|
||||
displayName: "MyBot",
|
||||
endpoint: new Uri("https://mybot.azurewebsites.net/api/messages"),
|
||||
msaAppId: "<your-msa-app-id>")
|
||||
{
|
||||
Description = "My Azure Bot",
|
||||
MsaAppType = BotMsaAppType.MultiTenant
|
||||
}
|
||||
};
|
||||
|
||||
// Create or update the bot
|
||||
ArmOperation<BotResource> operation = await botCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"myBotName",
|
||||
botData);
|
||||
|
||||
BotResource bot = operation.Value;
|
||||
Console.WriteLine($"Bot created: {bot.Data.Name}");
|
||||
```
|
||||
|
||||
### 2. Configure DirectLine Channel
|
||||
|
||||
```csharp
|
||||
// Get the bot
|
||||
BotResource bot = await resourceGroup.GetBots().GetAsync("myBotName");
|
||||
|
||||
// Get channel collection
|
||||
BotChannelCollection channels = bot.GetBotChannels();
|
||||
|
||||
// Create DirectLine channel configuration
|
||||
var channelData = new BotChannelData(AzureLocation.WestUS2)
|
||||
{
|
||||
Properties = new DirectLineChannel()
|
||||
{
|
||||
Properties = new DirectLineChannelProperties()
|
||||
{
|
||||
Sites =
|
||||
{
|
||||
new DirectLineSite("Default Site")
|
||||
{
|
||||
IsEnabled = true,
|
||||
IsV1Enabled = false,
|
||||
IsV3Enabled = true,
|
||||
IsSecureSiteEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create or update the channel
|
||||
ArmOperation<BotChannelResource> channelOp = await channels.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
BotChannelName.DirectLineChannel,
|
||||
channelData);
|
||||
|
||||
Console.WriteLine("DirectLine channel configured");
|
||||
```
|
||||
|
||||
### 3. Configure Microsoft Teams Channel
|
||||
|
||||
```csharp
|
||||
var teamsChannelData = new BotChannelData(AzureLocation.WestUS2)
|
||||
{
|
||||
Properties = new MsTeamsChannel()
|
||||
{
|
||||
Properties = new MsTeamsChannelProperties()
|
||||
{
|
||||
IsEnabled = true,
|
||||
EnableCalling = false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await channels.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
BotChannelName.MsTeamsChannel,
|
||||
teamsChannelData);
|
||||
```
|
||||
|
||||
### 4. Configure Web Chat Channel
|
||||
|
||||
```csharp
|
||||
var webChatChannelData = new BotChannelData(AzureLocation.WestUS2)
|
||||
{
|
||||
Properties = new WebChatChannel()
|
||||
{
|
||||
Properties = new WebChatChannelProperties()
|
||||
{
|
||||
Sites =
|
||||
{
|
||||
new WebChatSite("Default Site")
|
||||
{
|
||||
IsEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await channels.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
BotChannelName.WebChatChannel,
|
||||
webChatChannelData);
|
||||
```
|
||||
|
||||
### 5. Get Bot and List Channels
|
||||
|
||||
```csharp
|
||||
// Get bot
|
||||
BotResource bot = await botCollection.GetAsync("myBotName");
|
||||
Console.WriteLine($"Bot: {bot.Data.Properties.DisplayName}");
|
||||
Console.WriteLine($"Endpoint: {bot.Data.Properties.Endpoint}");
|
||||
|
||||
// List channels
|
||||
await foreach (BotChannelResource channel in bot.GetBotChannels().GetAllAsync())
|
||||
{
|
||||
Console.WriteLine($"Channel: {channel.Data.Name}");
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Regenerate DirectLine Keys
|
||||
|
||||
```csharp
|
||||
var regenerateRequest = new BotChannelRegenerateKeysContent(BotChannelName.DirectLineChannel)
|
||||
{
|
||||
SiteName = "Default Site"
|
||||
};
|
||||
|
||||
BotChannelResource channelWithKeys = await bot.GetBotChannelWithRegenerateKeysAsync(regenerateRequest);
|
||||
```
|
||||
|
||||
### 7. Update Bot
|
||||
|
||||
```csharp
|
||||
BotResource bot = await botCollection.GetAsync("myBotName");
|
||||
|
||||
// Update using patch
|
||||
var updateData = new BotData(bot.Data.Location)
|
||||
{
|
||||
Properties = new BotProperties(
|
||||
displayName: "Updated Bot Name",
|
||||
endpoint: bot.Data.Properties.Endpoint,
|
||||
msaAppId: bot.Data.Properties.MsaAppId)
|
||||
{
|
||||
Description = "Updated description"
|
||||
}
|
||||
};
|
||||
|
||||
await bot.UpdateAsync(updateData);
|
||||
```
|
||||
|
||||
### 8. Delete Bot
|
||||
|
||||
```csharp
|
||||
BotResource bot = await botCollection.GetAsync("myBotName");
|
||||
await bot.DeleteAsync(WaitUntil.Completed);
|
||||
```
|
||||
|
||||
## Supported Channel Types
|
||||
|
||||
| Channel | Constant | Class |
|
||||
|---------|----------|-------|
|
||||
| Direct Line | `BotChannelName.DirectLineChannel` | `DirectLineChannel` |
|
||||
| Direct Line Speech | `BotChannelName.DirectLineSpeechChannel` | `DirectLineSpeechChannel` |
|
||||
| Microsoft Teams | `BotChannelName.MsTeamsChannel` | `MsTeamsChannel` |
|
||||
| Web Chat | `BotChannelName.WebChatChannel` | `WebChatChannel` |
|
||||
| Slack | `BotChannelName.SlackChannel` | `SlackChannel` |
|
||||
| Facebook | `BotChannelName.FacebookChannel` | `FacebookChannel` |
|
||||
| Email | `BotChannelName.EmailChannel` | `EmailChannel` |
|
||||
| Telegram | `BotChannelName.TelegramChannel` | `TelegramChannel` |
|
||||
| Telephony | `BotChannelName.TelephonyChannel` | `TelephonyChannel` |
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ArmClient` | Entry point for all ARM operations |
|
||||
| `BotResource` | Represents an Azure Bot resource |
|
||||
| `BotCollection` | Collection for bot CRUD |
|
||||
| `BotData` | Bot resource definition |
|
||||
| `BotProperties` | Bot configuration properties |
|
||||
| `BotChannelResource` | Channel configuration |
|
||||
| `BotChannelCollection` | Collection of channels |
|
||||
| `BotChannelData` | Channel configuration data |
|
||||
| `BotConnectionSettingResource` | OAuth connection settings |
|
||||
|
||||
## BotServiceKind Values
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `BotServiceKind.Azurebot` | Azure Bot (recommended) |
|
||||
| `BotServiceKind.Bot` | Legacy Bot Framework bot |
|
||||
| `BotServiceKind.Designer` | Composer bot |
|
||||
| `BotServiceKind.Function` | Function bot |
|
||||
| `BotServiceKind.Sdk` | SDK bot |
|
||||
|
||||
## BotServiceSkuName Values
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `BotServiceSkuName.F0` | Free tier |
|
||||
| `BotServiceSkuName.S1` | Standard tier |
|
||||
|
||||
## BotMsaAppType Values
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `BotMsaAppType.MultiTenant` | Multi-tenant app |
|
||||
| `BotMsaAppType.SingleTenant` | Single-tenant app |
|
||||
| `BotMsaAppType.UserAssignedMSI` | User-assigned managed identity |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always use `DefaultAzureCredential`** — supports multiple auth methods
|
||||
2. **Use `WaitUntil.Completed`** for synchronous operations
|
||||
3. **Handle `RequestFailedException`** for API errors
|
||||
4. **Use async methods** (`*Async`) for all operations
|
||||
5. **Store MSA App credentials securely** — use Key Vault for secrets
|
||||
6. **Use managed identity** (`BotMsaAppType.UserAssignedMSI`) for production bots
|
||||
7. **Enable secure sites** for DirectLine channels in production
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
var operation = await botCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
botName,
|
||||
botData);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("Bot already exists");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"ARM Error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.ResourceManager.BotService` | Bot management (this SDK) | `dotnet add package Azure.ResourceManager.BotService` |
|
||||
| `Microsoft.Bot.Builder` | Bot Framework SDK | `dotnet add package Microsoft.Bot.Builder` |
|
||||
| `Microsoft.Bot.Builder.Integration.AspNet.Core` | ASP.NET Core integration | `dotnet add package Microsoft.Bot.Builder.Integration.AspNet.Core` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.ResourceManager.BotService |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.resourcemanager.botservice |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/botservice/Azure.ResourceManager.BotService |
|
||||
| Azure Bot Service Docs | https://learn.microsoft.com/azure/bot-service/ |
|
||||
377
skills/official/microsoft/dotnet/compute/durabletask/SKILL.md
Normal file
377
skills/official/microsoft/dotnet/compute/durabletask/SKILL.md
Normal file
@@ -0,0 +1,377 @@
|
||||
---
|
||||
name: azure-resource-manager-durabletask-dotnet
|
||||
description: |
|
||||
Azure Resource Manager SDK for Durable Task Scheduler in .NET. Use for MANAGEMENT PLANE operations: creating/managing Durable Task Schedulers, Task Hubs, and retention policies via Azure Resource Manager. Triggers: "Durable Task Scheduler", "create scheduler", "task hub", "DurableTaskSchedulerResource", "provision Durable Task", "orchestration scheduler".
|
||||
package: Azure.ResourceManager.DurableTask
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.DurableTask (.NET)
|
||||
|
||||
Management plane SDK for provisioning and managing Azure Durable Task Scheduler resources via Azure Resource Manager.
|
||||
|
||||
> **⚠️ Management vs Data Plane**
|
||||
> - **This SDK (Azure.ResourceManager.DurableTask)**: Create schedulers, task hubs, configure retention policies
|
||||
> - **Data Plane SDK (Microsoft.DurableTask.Client.AzureManaged)**: Start orchestrations, query instances, send events
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.DurableTask
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Versions**: Stable v1.0.0 (2025-11-03), Preview v1.0.0-beta.1 (2025-04-24)
|
||||
**API Version**: 2025-11-01
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
AZURE_RESOURCE_GROUP=<your-resource-group>
|
||||
# For service principal auth (optional)
|
||||
AZURE_TENANT_ID=<tenant-id>
|
||||
AZURE_CLIENT_ID=<client-id>
|
||||
AZURE_CLIENT_SECRET=<client-secret>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.DurableTask;
|
||||
|
||||
// Always use DefaultAzureCredential
|
||||
var credential = new DefaultAzureCredential();
|
||||
var armClient = new ArmClient(credential);
|
||||
|
||||
// Get subscription
|
||||
var subscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
|
||||
var subscription = armClient.GetSubscriptionResource(
|
||||
new ResourceIdentifier($"/subscriptions/{subscriptionId}"));
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
ArmClient
|
||||
└── SubscriptionResource
|
||||
└── ResourceGroupResource
|
||||
└── DurableTaskSchedulerResource
|
||||
├── DurableTaskHubResource
|
||||
└── DurableTaskRetentionPolicyResource
|
||||
```
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### 1. Create Durable Task Scheduler
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.DurableTask;
|
||||
using Azure.ResourceManager.DurableTask.Models;
|
||||
|
||||
// Get resource group
|
||||
var resourceGroup = await subscription
|
||||
.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
// Define scheduler with Dedicated SKU
|
||||
var schedulerData = new DurableTaskSchedulerData(AzureLocation.EastUS)
|
||||
{
|
||||
Properties = new DurableTaskSchedulerProperties
|
||||
{
|
||||
Sku = new DurableTaskSchedulerSku(DurableTaskSchedulerSkuName.Dedicated)
|
||||
{
|
||||
Capacity = 1 // Number of instances
|
||||
},
|
||||
// Optional: IP allowlist for network security
|
||||
IPAllowlist = { "10.0.0.0/24", "192.168.1.0/24" }
|
||||
}
|
||||
};
|
||||
|
||||
// Create scheduler (long-running operation)
|
||||
var schedulerCollection = resourceGroup.Value.GetDurableTaskSchedulers();
|
||||
var operation = await schedulerCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-scheduler",
|
||||
schedulerData);
|
||||
|
||||
DurableTaskSchedulerResource scheduler = operation.Value;
|
||||
Console.WriteLine($"Scheduler created: {scheduler.Data.Name}");
|
||||
Console.WriteLine($"Endpoint: {scheduler.Data.Properties.Endpoint}");
|
||||
```
|
||||
|
||||
### 2. Create Scheduler with Consumption SKU
|
||||
|
||||
```csharp
|
||||
// Consumption SKU (serverless)
|
||||
var consumptionSchedulerData = new DurableTaskSchedulerData(AzureLocation.EastUS)
|
||||
{
|
||||
Properties = new DurableTaskSchedulerProperties
|
||||
{
|
||||
Sku = new DurableTaskSchedulerSku(DurableTaskSchedulerSkuName.Consumption)
|
||||
// No capacity needed for consumption
|
||||
}
|
||||
};
|
||||
|
||||
var operation = await schedulerCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-serverless-scheduler",
|
||||
consumptionSchedulerData);
|
||||
```
|
||||
|
||||
### 3. Create Task Hub
|
||||
|
||||
```csharp
|
||||
// Task hubs are created under a scheduler
|
||||
var taskHubData = new DurableTaskHubData
|
||||
{
|
||||
// Properties are optional for basic task hub
|
||||
};
|
||||
|
||||
var taskHubCollection = scheduler.GetDurableTaskHubs();
|
||||
var hubOperation = await taskHubCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-taskhub",
|
||||
taskHubData);
|
||||
|
||||
DurableTaskHubResource taskHub = hubOperation.Value;
|
||||
Console.WriteLine($"Task Hub created: {taskHub.Data.Name}");
|
||||
```
|
||||
|
||||
### 4. List Schedulers
|
||||
|
||||
```csharp
|
||||
// List all schedulers in subscription
|
||||
await foreach (var sched in subscription.GetDurableTaskSchedulersAsync())
|
||||
{
|
||||
Console.WriteLine($"Scheduler: {sched.Data.Name}");
|
||||
Console.WriteLine($" Location: {sched.Data.Location}");
|
||||
Console.WriteLine($" SKU: {sched.Data.Properties.Sku?.Name}");
|
||||
Console.WriteLine($" Endpoint: {sched.Data.Properties.Endpoint}");
|
||||
}
|
||||
|
||||
// List schedulers in resource group
|
||||
var schedulers = resourceGroup.Value.GetDurableTaskSchedulers();
|
||||
await foreach (var sched in schedulers.GetAllAsync())
|
||||
{
|
||||
Console.WriteLine($"Scheduler: {sched.Data.Name}");
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Get Scheduler by Name
|
||||
|
||||
```csharp
|
||||
// Get existing scheduler
|
||||
var existingScheduler = await schedulerCollection.GetAsync("my-scheduler");
|
||||
Console.WriteLine($"Found: {existingScheduler.Value.Data.Name}");
|
||||
|
||||
// Or use extension method
|
||||
var schedulerResource = armClient.GetDurableTaskSchedulerResource(
|
||||
DurableTaskSchedulerResource.CreateResourceIdentifier(
|
||||
subscriptionId,
|
||||
"my-resource-group",
|
||||
"my-scheduler"));
|
||||
var scheduler = await schedulerResource.GetAsync();
|
||||
```
|
||||
|
||||
### 6. Update Scheduler
|
||||
|
||||
```csharp
|
||||
// Get current scheduler
|
||||
var scheduler = await schedulerCollection.GetAsync("my-scheduler");
|
||||
|
||||
// Update with new configuration
|
||||
var updateData = new DurableTaskSchedulerData(scheduler.Value.Data.Location)
|
||||
{
|
||||
Properties = new DurableTaskSchedulerProperties
|
||||
{
|
||||
Sku = new DurableTaskSchedulerSku(DurableTaskSchedulerSkuName.Dedicated)
|
||||
{
|
||||
Capacity = 2 // Scale up
|
||||
},
|
||||
IPAllowlist = { "10.0.0.0/16" } // Update IP allowlist
|
||||
}
|
||||
};
|
||||
|
||||
var updateOperation = await schedulerCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-scheduler",
|
||||
updateData);
|
||||
```
|
||||
|
||||
### 7. Delete Resources
|
||||
|
||||
```csharp
|
||||
// Delete task hub first
|
||||
var taskHub = await scheduler.GetDurableTaskHubs().GetAsync("my-taskhub");
|
||||
await taskHub.Value.DeleteAsync(WaitUntil.Completed);
|
||||
|
||||
// Then delete scheduler
|
||||
await scheduler.DeleteAsync(WaitUntil.Completed);
|
||||
```
|
||||
|
||||
### 8. Manage Retention Policies
|
||||
|
||||
```csharp
|
||||
// Get retention policy collection
|
||||
var retentionPolicies = scheduler.GetDurableTaskRetentionPolicies();
|
||||
|
||||
// Create or update retention policy
|
||||
var retentionData = new DurableTaskRetentionPolicyData
|
||||
{
|
||||
Properties = new DurableTaskRetentionPolicyProperties
|
||||
{
|
||||
// Configure retention settings
|
||||
}
|
||||
};
|
||||
|
||||
var retentionOperation = await retentionPolicies.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"default", // Policy name
|
||||
retentionData);
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ArmClient` | Entry point for all ARM operations |
|
||||
| `DurableTaskSchedulerResource` | Represents a Durable Task Scheduler |
|
||||
| `DurableTaskSchedulerCollection` | Collection for scheduler CRUD |
|
||||
| `DurableTaskSchedulerData` | Scheduler creation/update payload |
|
||||
| `DurableTaskSchedulerProperties` | Scheduler configuration (SKU, IPAllowlist) |
|
||||
| `DurableTaskSchedulerSku` | SKU configuration (Name, Capacity, RedundancyState) |
|
||||
| `DurableTaskSchedulerSkuName` | SKU options: `Dedicated`, `Consumption` |
|
||||
| `DurableTaskHubResource` | Represents a Task Hub |
|
||||
| `DurableTaskHubCollection` | Collection for task hub CRUD |
|
||||
| `DurableTaskHubData` | Task hub creation payload |
|
||||
| `DurableTaskRetentionPolicyResource` | Retention policy management |
|
||||
| `DurableTaskRetentionPolicyData` | Retention policy configuration |
|
||||
| `DurableTaskExtensions` | Extension methods for ARM client |
|
||||
|
||||
## SKU Options
|
||||
|
||||
| SKU | Description | Use Case |
|
||||
|-----|-------------|----------|
|
||||
| `Dedicated` | Fixed capacity with configurable instances | Production workloads, predictable performance |
|
||||
| `Consumption` | Serverless, auto-scaling | Development, variable workloads |
|
||||
|
||||
## Extension Methods
|
||||
|
||||
The SDK provides extension methods on `SubscriptionResource` and `ResourceGroupResource`:
|
||||
|
||||
```csharp
|
||||
// On SubscriptionResource
|
||||
subscription.GetDurableTaskSchedulers(); // List all in subscription
|
||||
subscription.GetDurableTaskSchedulersAsync(); // Async enumerable
|
||||
|
||||
// On ResourceGroupResource
|
||||
resourceGroup.GetDurableTaskSchedulers(); // Get collection
|
||||
resourceGroup.GetDurableTaskSchedulerAsync(name); // Get by name
|
||||
|
||||
// On ArmClient
|
||||
armClient.GetDurableTaskSchedulerResource(id); // Get by resource ID
|
||||
armClient.GetDurableTaskHubResource(id); // Get task hub by ID
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `WaitUntil.Completed`** for operations that must finish before proceeding
|
||||
2. **Use `WaitUntil.Started`** when you want to poll manually or run operations in parallel
|
||||
3. **Always use `DefaultAzureCredential`** — never hardcode keys
|
||||
4. **Handle `RequestFailedException`** for ARM API errors
|
||||
5. **Use `CreateOrUpdateAsync`** for idempotent operations
|
||||
6. **Delete task hubs before schedulers** — schedulers with task hubs cannot be deleted
|
||||
7. **Use IP allowlists** for network security in production
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
var operation = await schedulerCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed, schedulerName, schedulerData);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("Scheduler already exists");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 404)
|
||||
{
|
||||
Console.WriteLine("Resource group not found");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"ARM Error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.DurableTask;
|
||||
using Azure.ResourceManager.DurableTask.Models;
|
||||
using Azure.ResourceManager.Resources;
|
||||
|
||||
// Setup
|
||||
var credential = new DefaultAzureCredential();
|
||||
var armClient = new ArmClient(credential);
|
||||
|
||||
var subscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID")!;
|
||||
var resourceGroupName = Environment.GetEnvironmentVariable("AZURE_RESOURCE_GROUP")!;
|
||||
|
||||
var subscription = armClient.GetSubscriptionResource(
|
||||
new ResourceIdentifier($"/subscriptions/{subscriptionId}"));
|
||||
var resourceGroup = await subscription.GetResourceGroupAsync(resourceGroupName);
|
||||
|
||||
// Create scheduler
|
||||
var schedulerData = new DurableTaskSchedulerData(AzureLocation.EastUS)
|
||||
{
|
||||
Properties = new DurableTaskSchedulerProperties
|
||||
{
|
||||
Sku = new DurableTaskSchedulerSku(DurableTaskSchedulerSkuName.Dedicated)
|
||||
{
|
||||
Capacity = 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var schedulerCollection = resourceGroup.Value.GetDurableTaskSchedulers();
|
||||
var schedulerOp = await schedulerCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed, "my-scheduler", schedulerData);
|
||||
var scheduler = schedulerOp.Value;
|
||||
|
||||
Console.WriteLine($"Scheduler endpoint: {scheduler.Data.Properties.Endpoint}");
|
||||
|
||||
// Create task hub
|
||||
var taskHubData = new DurableTaskHubData();
|
||||
var taskHubOp = await scheduler.GetDurableTaskHubs().CreateOrUpdateAsync(
|
||||
WaitUntil.Completed, "my-taskhub", taskHubData);
|
||||
var taskHub = taskHubOp.Value;
|
||||
|
||||
Console.WriteLine($"Task Hub: {taskHub.Data.Name}");
|
||||
|
||||
// Cleanup
|
||||
await taskHub.DeleteAsync(WaitUntil.Completed);
|
||||
await scheduler.DeleteAsync(WaitUntil.Completed);
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.ResourceManager.DurableTask` | Management plane (this SDK) | `dotnet add package Azure.ResourceManager.DurableTask` |
|
||||
| `Microsoft.DurableTask.Client.AzureManaged` | Data plane (orchestrations, activities) | `dotnet add package Microsoft.DurableTask.Client.AzureManaged` |
|
||||
| `Microsoft.DurableTask.Worker.AzureManaged` | Worker for running orchestrations | `dotnet add package Microsoft.DurableTask.Worker.AzureManaged` |
|
||||
| `Azure.Identity` | Authentication | `dotnet add package Azure.Identity` |
|
||||
| `Azure.ResourceManager` | Base ARM SDK | `dotnet add package Azure.ResourceManager` |
|
||||
|
||||
## Source Reference
|
||||
|
||||
- [GitHub: Azure.ResourceManager.DurableTask](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/durabletask/Azure.ResourceManager.DurableTask)
|
||||
- [NuGet: Azure.ResourceManager.DurableTask](https://www.nuget.org/packages/Azure.ResourceManager.DurableTask)
|
||||
297
skills/official/microsoft/dotnet/compute/playwright/SKILL.md
Normal file
297
skills/official/microsoft/dotnet/compute/playwright/SKILL.md
Normal file
@@ -0,0 +1,297 @@
|
||||
---
|
||||
name: azure-resource-manager-playwright-dotnet
|
||||
description: |
|
||||
Azure Resource Manager SDK for Microsoft Playwright Testing in .NET. Use for MANAGEMENT PLANE operations: creating/managing Playwright Testing workspaces, checking name availability, and managing workspace quotas via Azure Resource Manager. NOT for running Playwright tests - use Azure.Developer.MicrosoftPlaywrightTesting.NUnit for that. Triggers: "Playwright workspace", "create Playwright Testing workspace", "manage Playwright resources", "ARM Playwright", "PlaywrightWorkspaceResource", "provision Playwright Testing".
|
||||
package: Azure.ResourceManager.Playwright
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.Playwright (.NET)
|
||||
|
||||
Management plane SDK for provisioning and managing Microsoft Playwright Testing workspaces via Azure Resource Manager.
|
||||
|
||||
> **⚠️ Management vs Test Execution**
|
||||
> - **This SDK (Azure.ResourceManager.Playwright)**: Create workspaces, manage quotas, check name availability
|
||||
> - **Test Execution SDK (Azure.Developer.MicrosoftPlaywrightTesting.NUnit)**: Run Playwright tests at scale on cloud browsers
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.Playwright
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Versions**: Stable v1.0.0, Preview v1.0.0-beta.1
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
# For service principal auth (optional)
|
||||
AZURE_TENANT_ID=<tenant-id>
|
||||
AZURE_CLIENT_ID=<client-id>
|
||||
AZURE_CLIENT_SECRET=<client-secret>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.Playwright;
|
||||
|
||||
// Always use DefaultAzureCredential
|
||||
var credential = new DefaultAzureCredential();
|
||||
var armClient = new ArmClient(credential);
|
||||
|
||||
// Get subscription
|
||||
var subscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
|
||||
var subscription = armClient.GetSubscriptionResource(
|
||||
new ResourceIdentifier($"/subscriptions/{subscriptionId}"));
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
ArmClient
|
||||
└── SubscriptionResource
|
||||
├── PlaywrightQuotaResource (subscription-level quotas)
|
||||
└── ResourceGroupResource
|
||||
└── PlaywrightWorkspaceResource
|
||||
└── PlaywrightWorkspaceQuotaResource (workspace-level quotas)
|
||||
```
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### 1. Create Playwright Workspace
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.Playwright;
|
||||
using Azure.ResourceManager.Playwright.Models;
|
||||
|
||||
// Get resource group
|
||||
var resourceGroup = await subscription
|
||||
.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
// Define workspace
|
||||
var workspaceData = new PlaywrightWorkspaceData(AzureLocation.WestUS3)
|
||||
{
|
||||
// Optional: Configure regional affinity and local auth
|
||||
RegionalAffinity = PlaywrightRegionalAffinity.Enabled,
|
||||
LocalAuth = PlaywrightLocalAuth.Enabled,
|
||||
Tags =
|
||||
{
|
||||
["Team"] = "Dev Exp",
|
||||
["Environment"] = "Production"
|
||||
}
|
||||
};
|
||||
|
||||
// Create workspace (long-running operation)
|
||||
var workspaceCollection = resourceGroup.Value.GetPlaywrightWorkspaces();
|
||||
var operation = await workspaceCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-playwright-workspace",
|
||||
workspaceData);
|
||||
|
||||
PlaywrightWorkspaceResource workspace = operation.Value;
|
||||
|
||||
// Get the data plane URI for running tests
|
||||
Console.WriteLine($"Data Plane URI: {workspace.Data.DataplaneUri}");
|
||||
Console.WriteLine($"Workspace ID: {workspace.Data.WorkspaceId}");
|
||||
```
|
||||
|
||||
### 2. Get Existing Workspace
|
||||
|
||||
```csharp
|
||||
// Get by name
|
||||
var workspace = await workspaceCollection.GetAsync("my-playwright-workspace");
|
||||
|
||||
// Or check if exists first
|
||||
bool exists = await workspaceCollection.ExistsAsync("my-playwright-workspace");
|
||||
if (exists)
|
||||
{
|
||||
var existingWorkspace = await workspaceCollection.GetAsync("my-playwright-workspace");
|
||||
Console.WriteLine($"Workspace found: {existingWorkspace.Value.Data.Name}");
|
||||
}
|
||||
```
|
||||
|
||||
### 3. List Workspaces
|
||||
|
||||
```csharp
|
||||
// List in resource group
|
||||
await foreach (var workspace in workspaceCollection.GetAllAsync())
|
||||
{
|
||||
Console.WriteLine($"Workspace: {workspace.Data.Name}");
|
||||
Console.WriteLine($" Location: {workspace.Data.Location}");
|
||||
Console.WriteLine($" State: {workspace.Data.ProvisioningState}");
|
||||
Console.WriteLine($" Data Plane URI: {workspace.Data.DataplaneUri}");
|
||||
}
|
||||
|
||||
// List across subscription
|
||||
await foreach (var workspace in subscription.GetPlaywrightWorkspacesAsync())
|
||||
{
|
||||
Console.WriteLine($"Workspace: {workspace.Data.Name}");
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Update Workspace
|
||||
|
||||
```csharp
|
||||
var patch = new PlaywrightWorkspacePatch
|
||||
{
|
||||
Tags =
|
||||
{
|
||||
["Team"] = "Dev Exp",
|
||||
["Environment"] = "Staging",
|
||||
["UpdatedAt"] = DateTime.UtcNow.ToString("o")
|
||||
}
|
||||
};
|
||||
|
||||
var updatedWorkspace = await workspace.Value.UpdateAsync(patch);
|
||||
```
|
||||
|
||||
### 5. Check Name Availability
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.Playwright.Models;
|
||||
|
||||
var checkRequest = new PlaywrightCheckNameAvailabilityContent
|
||||
{
|
||||
Name = "my-new-workspace",
|
||||
ResourceType = "Microsoft.LoadTestService/playwrightWorkspaces"
|
||||
};
|
||||
|
||||
var result = await subscription.CheckPlaywrightNameAvailabilityAsync(checkRequest);
|
||||
|
||||
if (result.Value.IsNameAvailable == true)
|
||||
{
|
||||
Console.WriteLine("Name is available!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Name unavailable: {result.Value.Message}");
|
||||
Console.WriteLine($"Reason: {result.Value.Reason}");
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Get Quota Information
|
||||
|
||||
```csharp
|
||||
// Subscription-level quotas
|
||||
await foreach (var quota in subscription.GetPlaywrightQuotasAsync(AzureLocation.WestUS3))
|
||||
{
|
||||
Console.WriteLine($"Quota: {quota.Data.Name}");
|
||||
Console.WriteLine($" Limit: {quota.Data.Limit}");
|
||||
Console.WriteLine($" Used: {quota.Data.Used}");
|
||||
}
|
||||
|
||||
// Workspace-level quotas
|
||||
var workspaceQuotas = workspace.Value.GetAllPlaywrightWorkspaceQuota();
|
||||
await foreach (var quota in workspaceQuotas.GetAllAsync())
|
||||
{
|
||||
Console.WriteLine($"Workspace Quota: {quota.Data.Name}");
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Delete Workspace
|
||||
|
||||
```csharp
|
||||
// Delete (long-running operation)
|
||||
await workspace.Value.DeleteAsync(WaitUntil.Completed);
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ArmClient` | Entry point for all ARM operations |
|
||||
| `PlaywrightWorkspaceResource` | Represents a Playwright Testing workspace |
|
||||
| `PlaywrightWorkspaceCollection` | Collection for workspace CRUD |
|
||||
| `PlaywrightWorkspaceData` | Workspace creation/response payload |
|
||||
| `PlaywrightWorkspacePatch` | Workspace update payload |
|
||||
| `PlaywrightQuotaResource` | Subscription-level quota information |
|
||||
| `PlaywrightWorkspaceQuotaResource` | Workspace-level quota information |
|
||||
| `PlaywrightExtensions` | Extension methods for ARM resources |
|
||||
| `PlaywrightCheckNameAvailabilityContent` | Name availability check request |
|
||||
|
||||
## Workspace Properties
|
||||
|
||||
| Property | Description |
|
||||
|----------|-------------|
|
||||
| `DataplaneUri` | URI for running tests (e.g., `https://api.dataplane.{guid}.domain.com`) |
|
||||
| `WorkspaceId` | Unique workspace identifier (GUID) |
|
||||
| `RegionalAffinity` | Enable/disable regional affinity for test execution |
|
||||
| `LocalAuth` | Enable/disable local authentication (access tokens) |
|
||||
| `ProvisioningState` | Current provisioning state (Succeeded, Failed, etc.) |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `WaitUntil.Completed`** for operations that must finish before proceeding
|
||||
2. **Use `WaitUntil.Started`** when you want to poll manually or run operations in parallel
|
||||
3. **Always use `DefaultAzureCredential`** — never hardcode keys
|
||||
4. **Handle `RequestFailedException`** for ARM API errors
|
||||
5. **Use `CreateOrUpdateAsync`** for idempotent operations
|
||||
6. **Navigate hierarchy** via `Get*` methods (e.g., `resourceGroup.GetPlaywrightWorkspaces()`)
|
||||
7. **Store the DataplaneUri** after workspace creation for test execution configuration
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
var operation = await workspaceCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed, workspaceName, workspaceData);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("Workspace already exists");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 400)
|
||||
{
|
||||
Console.WriteLine($"Bad request: {ex.Message}");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"ARM Error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Integration with Test Execution
|
||||
|
||||
After creating a workspace, use the `DataplaneUri` to configure your Playwright tests:
|
||||
|
||||
```csharp
|
||||
// 1. Create workspace (this SDK)
|
||||
var workspace = await workspaceCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed, "my-workspace", workspaceData);
|
||||
|
||||
// 2. Get the service URL
|
||||
var serviceUrl = workspace.Value.Data.DataplaneUri;
|
||||
|
||||
// 3. Set environment variable for test execution
|
||||
Environment.SetEnvironmentVariable("PLAYWRIGHT_SERVICE_URL", serviceUrl.ToString());
|
||||
|
||||
// 4. Run tests using Azure.Developer.MicrosoftPlaywrightTesting.NUnit
|
||||
// (separate package for test execution)
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.ResourceManager.Playwright` | Management plane (this SDK) | `dotnet add package Azure.ResourceManager.Playwright` |
|
||||
| `Azure.Developer.MicrosoftPlaywrightTesting.NUnit` | Run NUnit Playwright tests at scale | `dotnet add package Azure.Developer.MicrosoftPlaywrightTesting.NUnit --prerelease` |
|
||||
| `Azure.Developer.Playwright` | Playwright client library | `dotnet add package Azure.Developer.Playwright` |
|
||||
|
||||
## API Information
|
||||
|
||||
- **Resource Provider**: `Microsoft.LoadTestService`
|
||||
- **Default API Version**: `2025-09-01`
|
||||
- **Resource Type**: `Microsoft.LoadTestService/playwrightWorkspaces`
|
||||
|
||||
## Documentation Links
|
||||
|
||||
- [Azure.ResourceManager.Playwright API Reference](https://learn.microsoft.com/en-us/dotnet/api/azure.resourcemanager.playwright)
|
||||
- [Microsoft Playwright Testing Overview](https://learn.microsoft.com/en-us/azure/playwright-testing/overview-what-is-microsoft-playwright-testing)
|
||||
- [Quickstart: Run Playwright Tests at Scale](https://learn.microsoft.com/en-us/azure/playwright-testing/quickstart-run-end-to-end-tests)
|
||||
250
skills/official/microsoft/dotnet/data/cosmosdb/SKILL.md
Normal file
250
skills/official/microsoft/dotnet/data/cosmosdb/SKILL.md
Normal file
@@ -0,0 +1,250 @@
|
||||
---
|
||||
name: azure-resource-manager-cosmosdb-dotnet
|
||||
description: |
|
||||
Azure Resource Manager SDK for Cosmos DB in .NET. Use for MANAGEMENT PLANE operations: creating/managing Cosmos DB accounts, databases, containers, throughput settings, and RBAC via Azure Resource Manager. NOT for data plane operations (CRUD on documents) - use Microsoft.Azure.Cosmos for that. Triggers: "Cosmos DB account", "create Cosmos account", "manage Cosmos resources", "ARM Cosmos", "CosmosDBAccountResource", "provision Cosmos DB".
|
||||
package: Azure.ResourceManager.CosmosDB
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.CosmosDB (.NET)
|
||||
|
||||
Management plane SDK for provisioning and managing Azure Cosmos DB resources via Azure Resource Manager.
|
||||
|
||||
> **⚠️ Management vs Data Plane**
|
||||
> - **This SDK (Azure.ResourceManager.CosmosDB)**: Create accounts, databases, containers, configure throughput, manage RBAC
|
||||
> - **Data Plane SDK (Microsoft.Azure.Cosmos)**: CRUD operations on documents, queries, stored procedures execution
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.CosmosDB
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Versions**: Stable v1.4.0, Preview v1.4.0-beta.13
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
# For service principal auth (optional)
|
||||
AZURE_TENANT_ID=<tenant-id>
|
||||
AZURE_CLIENT_ID=<client-id>
|
||||
AZURE_CLIENT_SECRET=<client-secret>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.CosmosDB;
|
||||
|
||||
// Always use DefaultAzureCredential
|
||||
var credential = new DefaultAzureCredential();
|
||||
var armClient = new ArmClient(credential);
|
||||
|
||||
// Get subscription
|
||||
var subscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
|
||||
var subscription = armClient.GetSubscriptionResource(
|
||||
new ResourceIdentifier($"/subscriptions/{subscriptionId}"));
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
ArmClient
|
||||
└── SubscriptionResource
|
||||
└── ResourceGroupResource
|
||||
└── CosmosDBAccountResource
|
||||
├── CosmosDBSqlDatabaseResource
|
||||
│ └── CosmosDBSqlContainerResource
|
||||
│ ├── CosmosDBSqlStoredProcedureResource
|
||||
│ ├── CosmosDBSqlTriggerResource
|
||||
│ └── CosmosDBSqlUserDefinedFunctionResource
|
||||
├── CassandraKeyspaceResource
|
||||
├── GremlinDatabaseResource
|
||||
├── MongoDBDatabaseResource
|
||||
└── CosmosDBTableResource
|
||||
```
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### 1. Create Cosmos DB Account
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.CosmosDB;
|
||||
using Azure.ResourceManager.CosmosDB.Models;
|
||||
|
||||
// Get resource group
|
||||
var resourceGroup = await subscription
|
||||
.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
// Define account
|
||||
var accountData = new CosmosDBAccountCreateOrUpdateContent(
|
||||
location: AzureLocation.EastUS,
|
||||
locations: new[]
|
||||
{
|
||||
new CosmosDBAccountLocation
|
||||
{
|
||||
LocationName = AzureLocation.EastUS,
|
||||
FailoverPriority = 0,
|
||||
IsZoneRedundant = false
|
||||
}
|
||||
})
|
||||
{
|
||||
Kind = CosmosDBAccountKind.GlobalDocumentDB,
|
||||
ConsistencyPolicy = new ConsistencyPolicy(DefaultConsistencyLevel.Session),
|
||||
EnableAutomaticFailover = true
|
||||
};
|
||||
|
||||
// Create account (long-running operation)
|
||||
var accountCollection = resourceGroup.Value.GetCosmosDBAccounts();
|
||||
var operation = await accountCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-cosmos-account",
|
||||
accountData);
|
||||
|
||||
CosmosDBAccountResource account = operation.Value;
|
||||
```
|
||||
|
||||
### 2. Create SQL Database
|
||||
|
||||
```csharp
|
||||
var databaseData = new CosmosDBSqlDatabaseCreateOrUpdateContent(
|
||||
new CosmosDBSqlDatabaseResourceInfo("my-database"));
|
||||
|
||||
var databaseCollection = account.GetCosmosDBSqlDatabases();
|
||||
var dbOperation = await databaseCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-database",
|
||||
databaseData);
|
||||
|
||||
CosmosDBSqlDatabaseResource database = dbOperation.Value;
|
||||
```
|
||||
|
||||
### 3. Create SQL Container
|
||||
|
||||
```csharp
|
||||
var containerData = new CosmosDBSqlContainerCreateOrUpdateContent(
|
||||
new CosmosDBSqlContainerResourceInfo("my-container")
|
||||
{
|
||||
PartitionKey = new CosmosDBContainerPartitionKey
|
||||
{
|
||||
Paths = { "/partitionKey" },
|
||||
Kind = CosmosDBPartitionKind.Hash
|
||||
},
|
||||
IndexingPolicy = new CosmosDBIndexingPolicy
|
||||
{
|
||||
Automatic = true,
|
||||
IndexingMode = CosmosDBIndexingMode.Consistent
|
||||
},
|
||||
DefaultTtl = 86400 // 24 hours
|
||||
});
|
||||
|
||||
var containerCollection = database.GetCosmosDBSqlContainers();
|
||||
var containerOperation = await containerCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-container",
|
||||
containerData);
|
||||
|
||||
CosmosDBSqlContainerResource container = containerOperation.Value;
|
||||
```
|
||||
|
||||
### 4. Configure Throughput
|
||||
|
||||
```csharp
|
||||
// Manual throughput
|
||||
var throughputData = new ThroughputSettingsUpdateData(
|
||||
new ThroughputSettingsResourceInfo
|
||||
{
|
||||
Throughput = 400
|
||||
});
|
||||
|
||||
// Autoscale throughput
|
||||
var autoscaleData = new ThroughputSettingsUpdateData(
|
||||
new ThroughputSettingsResourceInfo
|
||||
{
|
||||
AutoscaleSettings = new AutoscaleSettingsResourceInfo
|
||||
{
|
||||
MaxThroughput = 4000
|
||||
}
|
||||
});
|
||||
|
||||
// Apply to database
|
||||
await database.CreateOrUpdateCosmosDBSqlDatabaseThroughputAsync(
|
||||
WaitUntil.Completed,
|
||||
throughputData);
|
||||
```
|
||||
|
||||
### 5. Get Connection Information
|
||||
|
||||
```csharp
|
||||
// Get keys
|
||||
var keys = await account.GetKeysAsync();
|
||||
Console.WriteLine($"Primary Key: {keys.Value.PrimaryMasterKey}");
|
||||
|
||||
// Get connection strings
|
||||
var connectionStrings = await account.GetConnectionStringsAsync();
|
||||
foreach (var cs in connectionStrings.Value.ConnectionStrings)
|
||||
{
|
||||
Console.WriteLine($"{cs.Description}: {cs.ConnectionString}");
|
||||
}
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ArmClient` | Entry point for all ARM operations |
|
||||
| `CosmosDBAccountResource` | Represents a Cosmos DB account |
|
||||
| `CosmosDBAccountCollection` | Collection for account CRUD |
|
||||
| `CosmosDBSqlDatabaseResource` | SQL API database |
|
||||
| `CosmosDBSqlContainerResource` | SQL API container |
|
||||
| `CosmosDBAccountCreateOrUpdateContent` | Account creation payload |
|
||||
| `CosmosDBSqlDatabaseCreateOrUpdateContent` | Database creation payload |
|
||||
| `CosmosDBSqlContainerCreateOrUpdateContent` | Container creation payload |
|
||||
| `ThroughputSettingsUpdateData` | Throughput configuration |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `WaitUntil.Completed`** for operations that must finish before proceeding
|
||||
2. **Use `WaitUntil.Started`** when you want to poll manually or run operations in parallel
|
||||
3. **Always use `DefaultAzureCredential`** — never hardcode keys
|
||||
4. **Handle `RequestFailedException`** for ARM API errors
|
||||
5. **Use `CreateOrUpdateAsync`** for idempotent operations
|
||||
6. **Navigate hierarchy** via `Get*` methods (e.g., `account.GetCosmosDBSqlDatabases()`)
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
var operation = await accountCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed, accountName, accountData);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("Account already exists");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"ARM Error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Reference Files
|
||||
|
||||
| File | When to Read |
|
||||
|------|--------------|
|
||||
| [references/account-management.md](references/account-management.md) | Account CRUD, failover, keys, connection strings, networking |
|
||||
| [references/sql-resources.md](references/sql-resources.md) | SQL databases, containers, stored procedures, triggers, UDFs |
|
||||
| [references/throughput.md](references/throughput.md) | Manual/autoscale throughput, migration between modes |
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Microsoft.Azure.Cosmos` | Data plane (document CRUD, queries) | `dotnet add package Microsoft.Azure.Cosmos` |
|
||||
| `Azure.ResourceManager.CosmosDB` | Management plane (this SDK) | `dotnet add package Azure.ResourceManager.CosmosDB` |
|
||||
338
skills/official/microsoft/dotnet/data/fabric/SKILL.md
Normal file
338
skills/official/microsoft/dotnet/data/fabric/SKILL.md
Normal file
@@ -0,0 +1,338 @@
|
||||
---
|
||||
name: azure-mgmt-fabric-dotnet
|
||||
description: |
|
||||
Azure Resource Manager SDK for Fabric in .NET. Use for MANAGEMENT PLANE operations: provisioning, scaling, suspending/resuming Microsoft Fabric capacities, checking name availability, and listing SKUs via Azure Resource Manager. Triggers: "Fabric capacity", "create capacity", "suspend capacity", "resume capacity", "Fabric SKU", "provision Fabric", "ARM Fabric", "FabricCapacityResource".
|
||||
package: Azure.ResourceManager.Fabric
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.Fabric (.NET)
|
||||
|
||||
Management plane SDK for provisioning and managing Microsoft Fabric capacity resources via Azure Resource Manager.
|
||||
|
||||
> **Management Plane Only**
|
||||
> This SDK manages Fabric *capacities* (compute resources). For working with Fabric workspaces, lakehouses, warehouses, and data items, use the Microsoft Fabric REST API or data plane SDKs.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.Fabric
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Version**: 1.0.0 (GA - September 2025)
|
||||
**API Version**: 2023-11-01
|
||||
**Target Frameworks**: .NET 8.0, .NET Standard 2.0
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
# For service principal auth (optional)
|
||||
AZURE_TENANT_ID=<tenant-id>
|
||||
AZURE_CLIENT_ID=<client-id>
|
||||
AZURE_CLIENT_SECRET=<client-secret>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.Fabric;
|
||||
|
||||
// Always use DefaultAzureCredential
|
||||
var credential = new DefaultAzureCredential();
|
||||
var armClient = new ArmClient(credential);
|
||||
|
||||
// Get subscription
|
||||
var subscription = await armClient.GetDefaultSubscriptionAsync();
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
ArmClient
|
||||
└── SubscriptionResource
|
||||
└── ResourceGroupResource
|
||||
└── FabricCapacityResource
|
||||
```
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Create Fabric Capacity
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.Fabric;
|
||||
using Azure.ResourceManager.Fabric.Models;
|
||||
using Azure.Core;
|
||||
|
||||
// Get resource group
|
||||
var resourceGroup = await subscription.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
// Define capacity configuration
|
||||
var administration = new FabricCapacityAdministration(
|
||||
new[] { "admin@contoso.com" } // Capacity administrators (UPNs or object IDs)
|
||||
);
|
||||
|
||||
var properties = new FabricCapacityProperties(administration);
|
||||
|
||||
var sku = new FabricSku("F64", FabricSkuTier.Fabric);
|
||||
|
||||
var capacityData = new FabricCapacityData(
|
||||
AzureLocation.WestUS2,
|
||||
properties,
|
||||
sku)
|
||||
{
|
||||
Tags = { ["Environment"] = "Production" }
|
||||
};
|
||||
|
||||
// Create capacity (long-running operation)
|
||||
var capacityCollection = resourceGroup.Value.GetFabricCapacities();
|
||||
var operation = await capacityCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-fabric-capacity",
|
||||
capacityData);
|
||||
|
||||
FabricCapacityResource capacity = operation.Value;
|
||||
Console.WriteLine($"Created capacity: {capacity.Data.Name}");
|
||||
Console.WriteLine($"State: {capacity.Data.Properties.State}");
|
||||
```
|
||||
|
||||
### 2. Get Fabric Capacity
|
||||
|
||||
```csharp
|
||||
// Get existing capacity
|
||||
var capacity = await resourceGroup.Value
|
||||
.GetFabricCapacityAsync("my-fabric-capacity");
|
||||
|
||||
Console.WriteLine($"Name: {capacity.Value.Data.Name}");
|
||||
Console.WriteLine($"Location: {capacity.Value.Data.Location}");
|
||||
Console.WriteLine($"SKU: {capacity.Value.Data.Sku.Name}");
|
||||
Console.WriteLine($"State: {capacity.Value.Data.Properties.State}");
|
||||
Console.WriteLine($"Provisioning State: {capacity.Value.Data.Properties.ProvisioningState}");
|
||||
```
|
||||
|
||||
### 3. Update Capacity (Scale SKU or Change Admins)
|
||||
|
||||
```csharp
|
||||
var capacity = await resourceGroup.Value
|
||||
.GetFabricCapacityAsync("my-fabric-capacity");
|
||||
|
||||
var patch = new FabricCapacityPatch
|
||||
{
|
||||
Sku = new FabricSku("F128", FabricSkuTier.Fabric), // Scale up
|
||||
Properties = new FabricCapacityUpdateProperties
|
||||
{
|
||||
Administration = new FabricCapacityAdministration(
|
||||
new[] { "admin@contoso.com", "newadmin@contoso.com" }
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
var updateOperation = await capacity.Value.UpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
patch);
|
||||
|
||||
Console.WriteLine($"Updated SKU: {updateOperation.Value.Data.Sku.Name}");
|
||||
```
|
||||
|
||||
### 4. Suspend and Resume Capacity
|
||||
|
||||
```csharp
|
||||
// Suspend capacity (stop billing for compute)
|
||||
await capacity.Value.SuspendAsync(WaitUntil.Completed);
|
||||
Console.WriteLine("Capacity suspended");
|
||||
|
||||
// Resume capacity
|
||||
var resumeOperation = await capacity.Value.ResumeAsync(WaitUntil.Completed);
|
||||
Console.WriteLine($"Capacity resumed. State: {resumeOperation.Value.Data.Properties.State}");
|
||||
```
|
||||
|
||||
### 5. Delete Capacity
|
||||
|
||||
```csharp
|
||||
await capacity.Value.DeleteAsync(WaitUntil.Completed);
|
||||
Console.WriteLine("Capacity deleted");
|
||||
```
|
||||
|
||||
### 6. List All Capacities
|
||||
|
||||
```csharp
|
||||
// In a resource group
|
||||
await foreach (var cap in resourceGroup.Value.GetFabricCapacities())
|
||||
{
|
||||
Console.WriteLine($"- {cap.Data.Name} ({cap.Data.Sku.Name})");
|
||||
}
|
||||
|
||||
// In a subscription
|
||||
await foreach (var cap in subscription.GetFabricCapacitiesAsync())
|
||||
{
|
||||
Console.WriteLine($"- {cap.Data.Name} in {cap.Data.Location}");
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Check Name Availability
|
||||
|
||||
```csharp
|
||||
var checkContent = new FabricNameAvailabilityContent
|
||||
{
|
||||
Name = "my-new-capacity",
|
||||
ResourceType = "Microsoft.Fabric/capacities"
|
||||
};
|
||||
|
||||
var result = await subscription.CheckFabricCapacityNameAvailabilityAsync(
|
||||
AzureLocation.WestUS2,
|
||||
checkContent);
|
||||
|
||||
if (result.Value.IsNameAvailable == true)
|
||||
{
|
||||
Console.WriteLine("Name is available!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Name unavailable: {result.Value.Reason} - {result.Value.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
### 8. List Available SKUs
|
||||
|
||||
```csharp
|
||||
// List all SKUs available in subscription
|
||||
await foreach (var skuDetails in subscription.GetSkusFabricCapacitiesAsync())
|
||||
{
|
||||
Console.WriteLine($"SKU: {skuDetails.Name}");
|
||||
Console.WriteLine($" Resource Type: {skuDetails.ResourceType}");
|
||||
foreach (var location in skuDetails.Locations)
|
||||
{
|
||||
Console.WriteLine($" Location: {location}");
|
||||
}
|
||||
}
|
||||
|
||||
// List SKUs available for an existing capacity (for scaling)
|
||||
await foreach (var skuDetails in capacity.Value.GetSkusForCapacityAsync())
|
||||
{
|
||||
Console.WriteLine($"Can scale to: {skuDetails.Sku.Name}");
|
||||
}
|
||||
```
|
||||
|
||||
## SKU Reference
|
||||
|
||||
| SKU Name | Capacity Units (CU) | Power BI Equivalent |
|
||||
|----------|---------------------|---------------------|
|
||||
| F2 | 2 | - |
|
||||
| F4 | 4 | - |
|
||||
| F8 | 8 | EM1/A1 |
|
||||
| F16 | 16 | EM2/A2 |
|
||||
| F32 | 32 | EM3/A3 |
|
||||
| F64 | 64 | P1/A4 |
|
||||
| F128 | 128 | P2/A5 |
|
||||
| F256 | 256 | P3/A6 |
|
||||
| F512 | 512 | P4/A7 |
|
||||
| F1024 | 1024 | P5/A8 |
|
||||
| F2048 | 2048 | - |
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ArmClient` | Entry point for all ARM operations |
|
||||
| `FabricCapacityResource` | Represents a Fabric capacity instance |
|
||||
| `FabricCapacityCollection` | Collection for capacity CRUD operations |
|
||||
| `FabricCapacityData` | Capacity creation/read data model |
|
||||
| `FabricCapacityPatch` | Capacity update payload |
|
||||
| `FabricCapacityProperties` | Capacity properties (administration, state) |
|
||||
| `FabricCapacityAdministration` | Admin members configuration |
|
||||
| `FabricSku` | SKU configuration (name and tier) |
|
||||
| `FabricSkuTier` | Pricing tier (currently only "Fabric") |
|
||||
| `FabricProvisioningState` | Provisioning states (Succeeded, Failed, etc.) |
|
||||
| `FabricResourceState` | Resource states (Active, Suspended, etc.) |
|
||||
| `FabricNameAvailabilityContent` | Name availability check request |
|
||||
| `FabricNameAvailabilityResult` | Name availability check response |
|
||||
|
||||
## Provisioning and Resource States
|
||||
|
||||
### Provisioning States (`FabricProvisioningState`)
|
||||
- `Succeeded` - Operation completed successfully
|
||||
- `Failed` - Operation failed
|
||||
- `Canceled` - Operation was canceled
|
||||
- `Deleting` - Capacity is being deleted
|
||||
- `Provisioning` - Initial provisioning in progress
|
||||
- `Updating` - Update operation in progress
|
||||
|
||||
### Resource States (`FabricResourceState`)
|
||||
- `Active` - Capacity is running and available
|
||||
- `Provisioning` - Being provisioned
|
||||
- `Failed` - In failed state
|
||||
- `Updating` - Being updated
|
||||
- `Deleting` - Being deleted
|
||||
- `Suspending` - Transitioning to suspended
|
||||
- `Suspended` - Suspended (not billing for compute)
|
||||
- `Pausing` - Transitioning to paused
|
||||
- `Paused` - Paused
|
||||
- `Resuming` - Resuming from suspended/paused
|
||||
- `Scaling` - Scaling to different SKU
|
||||
- `Preparing` - Preparing resources
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `WaitUntil.Completed`** for operations that must finish before proceeding
|
||||
2. **Use `WaitUntil.Started`** when you want to poll manually or run operations in parallel
|
||||
3. **Always use `DefaultAzureCredential`** — never hardcode credentials
|
||||
4. **Handle `RequestFailedException`** for ARM API errors
|
||||
5. **Use `CreateOrUpdateAsync`** for idempotent operations
|
||||
6. **Suspend when not in use** — Fabric capacities bill for compute even when idle
|
||||
7. **Check provisioning state** before performing operations on a capacity
|
||||
8. **Use appropriate SKU** — Start small (F2/F4) for dev/test, scale up for production
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
var operation = await capacityCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed, capacityName, capacityData);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("Capacity already exists or conflict");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 400)
|
||||
{
|
||||
Console.WriteLine($"Invalid configuration: {ex.Message}");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 403)
|
||||
{
|
||||
Console.WriteLine("Insufficient permissions or quota exceeded");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"ARM Error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Capacity names must be globally unique** — Fabric capacity names must be unique across all Azure subscriptions
|
||||
2. **Suspend doesn't delete** — Suspended capacities still exist but don't bill for compute
|
||||
3. **SKU changes may require downtime** — Scaling operations can take several minutes
|
||||
4. **Admin UPNs must be valid** — Capacity administrators must be valid Azure AD users
|
||||
5. **Location constraints** — Not all SKUs are available in all regions; use `GetSkusFabricCapacitiesAsync` to check
|
||||
6. **Long provisioning times** — Capacity creation can take 5-15 minutes
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.ResourceManager.Fabric` | Management plane (this SDK) | `dotnet add package Azure.ResourceManager.Fabric` |
|
||||
| `Microsoft.Fabric.Api` | Data plane operations (beta) | `dotnet add package Microsoft.Fabric.Api --prerelease` |
|
||||
| `Azure.ResourceManager` | Core ARM SDK | `dotnet add package Azure.ResourceManager` |
|
||||
| `Azure.Identity` | Authentication | `dotnet add package Azure.Identity` |
|
||||
|
||||
## References
|
||||
|
||||
- [Azure.ResourceManager.Fabric NuGet](https://www.nuget.org/packages/Azure.ResourceManager.Fabric)
|
||||
- [GitHub Source](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/fabric/Azure.ResourceManager.Fabric)
|
||||
- [Microsoft Fabric Documentation](https://learn.microsoft.com/fabric/)
|
||||
- [Fabric Capacity Management](https://learn.microsoft.com/fabric/admin/service-admin-portal-capacity-settings)
|
||||
392
skills/official/microsoft/dotnet/data/mysql/SKILL.md
Normal file
392
skills/official/microsoft/dotnet/data/mysql/SKILL.md
Normal file
@@ -0,0 +1,392 @@
|
||||
---
|
||||
name: azure-resource-manager-mysql-dotnet
|
||||
description: |
|
||||
Azure MySQL Flexible Server SDK for .NET. Database management for MySQL Flexible Server deployments. Use for creating servers, databases, firewall rules, configurations, backups, and high availability. Triggers: "MySQL", "MySqlFlexibleServer", "MySQL Flexible Server", "Azure Database for MySQL", "MySQL database management", "MySQL firewall", "MySQL backup".
|
||||
package: Azure.ResourceManager.MySql
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.MySql (.NET)
|
||||
|
||||
Azure Resource Manager SDK for managing MySQL Flexible Server deployments.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.MySql
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Version**: v1.2.0 (GA)
|
||||
**API Version**: 2023-12-30
|
||||
|
||||
> **Note**: This skill focuses on MySQL Flexible Server. Single Server is deprecated and scheduled for retirement.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
AZURE_RESOURCE_GROUP=<your-resource-group>
|
||||
AZURE_MYSQL_SERVER_NAME=<your-mysql-server>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.MySql;
|
||||
using Azure.ResourceManager.MySql.FlexibleServers;
|
||||
|
||||
ArmClient client = new ArmClient(new DefaultAzureCredential());
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
Subscription
|
||||
└── ResourceGroup
|
||||
└── MySqlFlexibleServer # MySQL Flexible Server instance
|
||||
├── MySqlFlexibleServerDatabase # Database within the server
|
||||
├── MySqlFlexibleServerFirewallRule # IP firewall rules
|
||||
├── MySqlFlexibleServerConfiguration # Server parameters
|
||||
├── MySqlFlexibleServerBackup # Backup information
|
||||
├── MySqlFlexibleServerMaintenanceWindow # Maintenance schedule
|
||||
└── MySqlFlexibleServerAadAdministrator # Entra ID admin
|
||||
```
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Create MySQL Flexible Server
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.MySql.FlexibleServers;
|
||||
using Azure.ResourceManager.MySql.FlexibleServers.Models;
|
||||
|
||||
ResourceGroupResource resourceGroup = await client
|
||||
.GetDefaultSubscriptionAsync()
|
||||
.Result
|
||||
.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
MySqlFlexibleServerCollection servers = resourceGroup.GetMySqlFlexibleServers();
|
||||
|
||||
MySqlFlexibleServerData data = new MySqlFlexibleServerData(AzureLocation.EastUS)
|
||||
{
|
||||
Sku = new MySqlFlexibleServerSku("Standard_D2ds_v4", MySqlFlexibleServerSkuTier.GeneralPurpose),
|
||||
AdministratorLogin = "mysqladmin",
|
||||
AdministratorLoginPassword = "YourSecurePassword123!",
|
||||
Version = MySqlFlexibleServerVersion.Ver8_0_21,
|
||||
Storage = new MySqlFlexibleServerStorage
|
||||
{
|
||||
StorageSizeInGB = 128,
|
||||
AutoGrow = MySqlFlexibleServerEnableStatusEnum.Enabled,
|
||||
Iops = 3000
|
||||
},
|
||||
Backup = new MySqlFlexibleServerBackupProperties
|
||||
{
|
||||
BackupRetentionDays = 7,
|
||||
GeoRedundantBackup = MySqlFlexibleServerEnableStatusEnum.Disabled
|
||||
},
|
||||
HighAvailability = new MySqlFlexibleServerHighAvailability
|
||||
{
|
||||
Mode = MySqlFlexibleServerHighAvailabilityMode.ZoneRedundant,
|
||||
StandbyAvailabilityZone = "2"
|
||||
},
|
||||
AvailabilityZone = "1"
|
||||
};
|
||||
|
||||
ArmOperation<MySqlFlexibleServerResource> operation = await servers
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-mysql-server", data);
|
||||
|
||||
MySqlFlexibleServerResource server = operation.Value;
|
||||
Console.WriteLine($"Server created: {server.Data.FullyQualifiedDomainName}");
|
||||
```
|
||||
|
||||
### 2. Create Database
|
||||
|
||||
```csharp
|
||||
MySqlFlexibleServerResource server = await resourceGroup
|
||||
.GetMySqlFlexibleServerAsync("my-mysql-server");
|
||||
|
||||
MySqlFlexibleServerDatabaseCollection databases = server.GetMySqlFlexibleServerDatabases();
|
||||
|
||||
MySqlFlexibleServerDatabaseData dbData = new MySqlFlexibleServerDatabaseData
|
||||
{
|
||||
Charset = "utf8mb4",
|
||||
Collation = "utf8mb4_unicode_ci"
|
||||
};
|
||||
|
||||
ArmOperation<MySqlFlexibleServerDatabaseResource> operation = await databases
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "myappdb", dbData);
|
||||
|
||||
MySqlFlexibleServerDatabaseResource database = operation.Value;
|
||||
Console.WriteLine($"Database created: {database.Data.Name}");
|
||||
```
|
||||
|
||||
### 3. Configure Firewall Rules
|
||||
|
||||
```csharp
|
||||
MySqlFlexibleServerFirewallRuleCollection firewallRules = server.GetMySqlFlexibleServerFirewallRules();
|
||||
|
||||
// Allow specific IP range
|
||||
MySqlFlexibleServerFirewallRuleData ruleData = new MySqlFlexibleServerFirewallRuleData
|
||||
{
|
||||
StartIPAddress = System.Net.IPAddress.Parse("10.0.0.1"),
|
||||
EndIPAddress = System.Net.IPAddress.Parse("10.0.0.255")
|
||||
};
|
||||
|
||||
ArmOperation<MySqlFlexibleServerFirewallRuleResource> operation = await firewallRules
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "allow-internal", ruleData);
|
||||
|
||||
// Allow Azure services
|
||||
MySqlFlexibleServerFirewallRuleData azureServicesRule = new MySqlFlexibleServerFirewallRuleData
|
||||
{
|
||||
StartIPAddress = System.Net.IPAddress.Parse("0.0.0.0"),
|
||||
EndIPAddress = System.Net.IPAddress.Parse("0.0.0.0")
|
||||
};
|
||||
|
||||
await firewallRules.CreateOrUpdateAsync(WaitUntil.Completed, "AllowAllAzureServicesAndResourcesWithinAzureIps", azureServicesRule);
|
||||
```
|
||||
|
||||
### 4. Update Server Configuration
|
||||
|
||||
```csharp
|
||||
MySqlFlexibleServerConfigurationCollection configurations = server.GetMySqlFlexibleServerConfigurations();
|
||||
|
||||
// Get current configuration
|
||||
MySqlFlexibleServerConfigurationResource config = await configurations
|
||||
.GetAsync("max_connections");
|
||||
|
||||
// Update configuration
|
||||
MySqlFlexibleServerConfigurationData configData = new MySqlFlexibleServerConfigurationData
|
||||
{
|
||||
Value = "500",
|
||||
Source = MySqlFlexibleServerConfigurationSource.UserOverride
|
||||
};
|
||||
|
||||
ArmOperation<MySqlFlexibleServerConfigurationResource> operation = await configurations
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "max_connections", configData);
|
||||
|
||||
// Common configurations to tune
|
||||
string[] commonParams = { "max_connections", "innodb_buffer_pool_size", "slow_query_log", "long_query_time" };
|
||||
```
|
||||
|
||||
### 5. Configure Entra ID Administrator
|
||||
|
||||
```csharp
|
||||
MySqlFlexibleServerAadAdministratorCollection admins = server.GetMySqlFlexibleServerAadAdministrators();
|
||||
|
||||
MySqlFlexibleServerAadAdministratorData adminData = new MySqlFlexibleServerAadAdministratorData
|
||||
{
|
||||
AdministratorType = MySqlFlexibleServerAdministratorType.ActiveDirectory,
|
||||
Login = "aad-admin@contoso.com",
|
||||
Sid = Guid.Parse("<entra-object-id>"),
|
||||
TenantId = Guid.Parse("<tenant-id>"),
|
||||
IdentityResourceId = new ResourceIdentifier("/subscriptions/.../userAssignedIdentities/mysql-identity")
|
||||
};
|
||||
|
||||
ArmOperation<MySqlFlexibleServerAadAdministratorResource> operation = await admins
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "ActiveDirectory", adminData);
|
||||
```
|
||||
|
||||
### 6. List and Manage Servers
|
||||
|
||||
```csharp
|
||||
// List servers in resource group
|
||||
await foreach (MySqlFlexibleServerResource server in resourceGroup.GetMySqlFlexibleServers())
|
||||
{
|
||||
Console.WriteLine($"Server: {server.Data.Name}");
|
||||
Console.WriteLine($" FQDN: {server.Data.FullyQualifiedDomainName}");
|
||||
Console.WriteLine($" Version: {server.Data.Version}");
|
||||
Console.WriteLine($" State: {server.Data.State}");
|
||||
Console.WriteLine($" SKU: {server.Data.Sku.Name} ({server.Data.Sku.Tier})");
|
||||
}
|
||||
|
||||
// List databases in server
|
||||
await foreach (MySqlFlexibleServerDatabaseResource db in server.GetMySqlFlexibleServerDatabases())
|
||||
{
|
||||
Console.WriteLine($"Database: {db.Data.Name}");
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Backup and Restore
|
||||
|
||||
```csharp
|
||||
// List available backups
|
||||
await foreach (MySqlFlexibleServerBackupResource backup in server.GetMySqlFlexibleServerBackups())
|
||||
{
|
||||
Console.WriteLine($"Backup: {backup.Data.Name}");
|
||||
Console.WriteLine($" Type: {backup.Data.BackupType}");
|
||||
Console.WriteLine($" Completed: {backup.Data.CompletedOn}");
|
||||
}
|
||||
|
||||
// Point-in-time restore
|
||||
MySqlFlexibleServerData restoreData = new MySqlFlexibleServerData(AzureLocation.EastUS)
|
||||
{
|
||||
CreateMode = MySqlFlexibleServerCreateMode.PointInTimeRestore,
|
||||
SourceServerResourceId = server.Id,
|
||||
RestorePointInTime = DateTimeOffset.UtcNow.AddHours(-2)
|
||||
};
|
||||
|
||||
ArmOperation<MySqlFlexibleServerResource> operation = await servers
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-mysql-restored", restoreData);
|
||||
```
|
||||
|
||||
### 8. Stop and Start Server
|
||||
|
||||
```csharp
|
||||
MySqlFlexibleServerResource server = await resourceGroup
|
||||
.GetMySqlFlexibleServerAsync("my-mysql-server");
|
||||
|
||||
// Stop server (saves costs when not in use)
|
||||
await server.StopAsync(WaitUntil.Completed);
|
||||
|
||||
// Start server
|
||||
await server.StartAsync(WaitUntil.Completed);
|
||||
|
||||
// Restart server
|
||||
await server.RestartAsync(WaitUntil.Completed, new MySqlFlexibleServerRestartParameter
|
||||
{
|
||||
RestartWithFailover = MySqlFlexibleServerEnableStatusEnum.Enabled,
|
||||
MaxFailoverSeconds = 60
|
||||
});
|
||||
```
|
||||
|
||||
### 9. Update Server (Scale)
|
||||
|
||||
```csharp
|
||||
MySqlFlexibleServerResource server = await resourceGroup
|
||||
.GetMySqlFlexibleServerAsync("my-mysql-server");
|
||||
|
||||
MySqlFlexibleServerPatch patch = new MySqlFlexibleServerPatch
|
||||
{
|
||||
Sku = new MySqlFlexibleServerSku("Standard_D4ds_v4", MySqlFlexibleServerSkuTier.GeneralPurpose),
|
||||
Storage = new MySqlFlexibleServerStorage
|
||||
{
|
||||
StorageSizeInGB = 256,
|
||||
Iops = 6000
|
||||
}
|
||||
};
|
||||
|
||||
ArmOperation<MySqlFlexibleServerResource> operation = await server
|
||||
.UpdateAsync(WaitUntil.Completed, patch);
|
||||
```
|
||||
|
||||
### 10. Delete Server
|
||||
|
||||
```csharp
|
||||
MySqlFlexibleServerResource server = await resourceGroup
|
||||
.GetMySqlFlexibleServerAsync("my-mysql-server");
|
||||
|
||||
await server.DeleteAsync(WaitUntil.Completed);
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `MySqlFlexibleServerResource` | Flexible Server instance |
|
||||
| `MySqlFlexibleServerData` | Server configuration data |
|
||||
| `MySqlFlexibleServerCollection` | Collection of servers |
|
||||
| `MySqlFlexibleServerDatabaseResource` | Database within server |
|
||||
| `MySqlFlexibleServerFirewallRuleResource` | IP firewall rule |
|
||||
| `MySqlFlexibleServerConfigurationResource` | Server parameter |
|
||||
| `MySqlFlexibleServerBackupResource` | Backup metadata |
|
||||
| `MySqlFlexibleServerAadAdministratorResource` | Entra ID admin |
|
||||
| `MySqlFlexibleServerSku` | SKU (compute tier + size) |
|
||||
| `MySqlFlexibleServerStorage` | Storage configuration |
|
||||
| `MySqlFlexibleServerHighAvailability` | HA configuration |
|
||||
| `MySqlFlexibleServerBackupProperties` | Backup settings |
|
||||
|
||||
## SKU Tiers
|
||||
|
||||
| Tier | Use Case | SKU Examples |
|
||||
|------|----------|--------------|
|
||||
| `Burstable` | Dev/test, light workloads | Standard_B1ms, Standard_B2s |
|
||||
| `GeneralPurpose` | Production workloads | Standard_D2ds_v4, Standard_D4ds_v4 |
|
||||
| `MemoryOptimized` | High memory requirements | Standard_E2ds_v4, Standard_E4ds_v4 |
|
||||
|
||||
## High Availability Modes
|
||||
|
||||
| Mode | Description |
|
||||
|------|-------------|
|
||||
| `Disabled` | No HA (single server) |
|
||||
| `SameZone` | HA within same availability zone |
|
||||
| `ZoneRedundant` | HA across availability zones |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Flexible Server** — Single Server is deprecated
|
||||
2. **Enable zone-redundant HA** — For production workloads
|
||||
3. **Use DefaultAzureCredential** — Prefer over connection strings
|
||||
4. **Configure Entra ID authentication** — More secure than SQL auth
|
||||
5. **Enable auto-grow storage** — Prevents out-of-space issues
|
||||
6. **Set appropriate backup retention** — 7-35 days based on compliance
|
||||
7. **Use private endpoints** — For secure network access
|
||||
8. **Tune server parameters** — Based on workload characteristics
|
||||
9. **Monitor with Azure Monitor** — Enable metrics and logs
|
||||
10. **Stop dev/test servers** — Save costs when not in use
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
ArmOperation<MySqlFlexibleServerResource> operation = await servers
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-mysql", data);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("Server already exists");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 400)
|
||||
{
|
||||
Console.WriteLine($"Invalid configuration: {ex.Message}");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Azure error: {ex.Status} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Connection String
|
||||
|
||||
After creating the server, connect using:
|
||||
|
||||
```csharp
|
||||
// ADO.NET connection string
|
||||
string connectionString = $"Server={server.Data.FullyQualifiedDomainName};" +
|
||||
"Database=myappdb;" +
|
||||
"User Id=mysqladmin;" +
|
||||
"Password=YourSecurePassword123!;" +
|
||||
"SslMode=Required;";
|
||||
|
||||
// With Entra ID token (recommended)
|
||||
var credential = new DefaultAzureCredential();
|
||||
var token = await credential.GetTokenAsync(
|
||||
new TokenRequestContext(new[] { "https://ossrdbms-aad.database.windows.net/.default" }));
|
||||
|
||||
string connectionString = $"Server={server.Data.FullyQualifiedDomainName};" +
|
||||
"Database=myappdb;" +
|
||||
$"User Id=aad-admin@contoso.com;" +
|
||||
$"Password={token.Token};" +
|
||||
"SslMode=Required;";
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.ResourceManager.MySql` | MySQL management (this SDK) | `dotnet add package Azure.ResourceManager.MySql` |
|
||||
| `Azure.ResourceManager.PostgreSql` | PostgreSQL management | `dotnet add package Azure.ResourceManager.PostgreSql` |
|
||||
| `MySqlConnector` | MySQL data access | `dotnet add package MySqlConnector` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.ResourceManager.MySql |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.resourcemanager.mysql |
|
||||
| Product Documentation | https://learn.microsoft.com/azure/mysql/flexible-server/ |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/mysql/Azure.ResourceManager.MySql |
|
||||
432
skills/official/microsoft/dotnet/data/postgresql/SKILL.md
Normal file
432
skills/official/microsoft/dotnet/data/postgresql/SKILL.md
Normal file
@@ -0,0 +1,432 @@
|
||||
---
|
||||
name: azure-resource-manager-postgresql-dotnet
|
||||
description: |
|
||||
Azure PostgreSQL Flexible Server SDK for .NET. Database management for PostgreSQL Flexible Server deployments. Use for creating servers, databases, firewall rules, configurations, backups, and high availability. Triggers: "PostgreSQL", "PostgreSqlFlexibleServer", "PostgreSQL Flexible Server", "Azure Database for PostgreSQL", "PostgreSQL database management", "PostgreSQL firewall", "PostgreSQL backup", "Postgres".
|
||||
package: Azure.ResourceManager.PostgreSql
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.PostgreSql (.NET)
|
||||
|
||||
Azure Resource Manager SDK for managing PostgreSQL Flexible Server deployments.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.PostgreSql
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Version**: v1.2.0 (GA)
|
||||
**API Version**: 2023-12-01-preview
|
||||
|
||||
> **Note**: This skill focuses on PostgreSQL Flexible Server. Single Server is deprecated and scheduled for retirement.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
AZURE_RESOURCE_GROUP=<your-resource-group>
|
||||
AZURE_POSTGRESQL_SERVER_NAME=<your-postgresql-server>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.PostgreSql;
|
||||
using Azure.ResourceManager.PostgreSql.FlexibleServers;
|
||||
|
||||
ArmClient client = new ArmClient(new DefaultAzureCredential());
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
Subscription
|
||||
└── ResourceGroup
|
||||
└── PostgreSqlFlexibleServer # PostgreSQL Flexible Server instance
|
||||
├── PostgreSqlFlexibleServerDatabase # Database within the server
|
||||
├── PostgreSqlFlexibleServerFirewallRule # IP firewall rules
|
||||
├── PostgreSqlFlexibleServerConfiguration # Server parameters
|
||||
├── PostgreSqlFlexibleServerBackup # Backup information
|
||||
├── PostgreSqlFlexibleServerActiveDirectoryAdministrator # Entra ID admin
|
||||
└── PostgreSqlFlexibleServerVirtualEndpoint # Read replica endpoints
|
||||
```
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Create PostgreSQL Flexible Server
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.PostgreSql.FlexibleServers;
|
||||
using Azure.ResourceManager.PostgreSql.FlexibleServers.Models;
|
||||
|
||||
ResourceGroupResource resourceGroup = await client
|
||||
.GetDefaultSubscriptionAsync()
|
||||
.Result
|
||||
.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
PostgreSqlFlexibleServerCollection servers = resourceGroup.GetPostgreSqlFlexibleServers();
|
||||
|
||||
PostgreSqlFlexibleServerData data = new PostgreSqlFlexibleServerData(AzureLocation.EastUS)
|
||||
{
|
||||
Sku = new PostgreSqlFlexibleServerSku("Standard_D2ds_v4", PostgreSqlFlexibleServerSkuTier.GeneralPurpose),
|
||||
AdministratorLogin = "pgadmin",
|
||||
AdministratorLoginPassword = "YourSecurePassword123!",
|
||||
Version = PostgreSqlFlexibleServerVersion.Ver16,
|
||||
Storage = new PostgreSqlFlexibleServerStorage
|
||||
{
|
||||
StorageSizeInGB = 128,
|
||||
AutoGrow = StorageAutoGrow.Enabled,
|
||||
Tier = PostgreSqlStorageTierName.P30
|
||||
},
|
||||
Backup = new PostgreSqlFlexibleServerBackupProperties
|
||||
{
|
||||
BackupRetentionDays = 7,
|
||||
GeoRedundantBackup = PostgreSqlFlexibleServerGeoRedundantBackupEnum.Disabled
|
||||
},
|
||||
HighAvailability = new PostgreSqlFlexibleServerHighAvailability
|
||||
{
|
||||
Mode = PostgreSqlFlexibleServerHighAvailabilityMode.ZoneRedundant,
|
||||
StandbyAvailabilityZone = "2"
|
||||
},
|
||||
AvailabilityZone = "1",
|
||||
AuthConfig = new PostgreSqlFlexibleServerAuthConfig
|
||||
{
|
||||
ActiveDirectoryAuth = PostgreSqlFlexibleServerActiveDirectoryAuthEnum.Enabled,
|
||||
PasswordAuth = PostgreSqlFlexibleServerPasswordAuthEnum.Enabled
|
||||
}
|
||||
};
|
||||
|
||||
ArmOperation<PostgreSqlFlexibleServerResource> operation = await servers
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-postgresql-server", data);
|
||||
|
||||
PostgreSqlFlexibleServerResource server = operation.Value;
|
||||
Console.WriteLine($"Server created: {server.Data.FullyQualifiedDomainName}");
|
||||
```
|
||||
|
||||
### 2. Create Database
|
||||
|
||||
```csharp
|
||||
PostgreSqlFlexibleServerResource server = await resourceGroup
|
||||
.GetPostgreSqlFlexibleServerAsync("my-postgresql-server");
|
||||
|
||||
PostgreSqlFlexibleServerDatabaseCollection databases = server.GetPostgreSqlFlexibleServerDatabases();
|
||||
|
||||
PostgreSqlFlexibleServerDatabaseData dbData = new PostgreSqlFlexibleServerDatabaseData
|
||||
{
|
||||
Charset = "UTF8",
|
||||
Collation = "en_US.utf8"
|
||||
};
|
||||
|
||||
ArmOperation<PostgreSqlFlexibleServerDatabaseResource> operation = await databases
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "myappdb", dbData);
|
||||
|
||||
PostgreSqlFlexibleServerDatabaseResource database = operation.Value;
|
||||
Console.WriteLine($"Database created: {database.Data.Name}");
|
||||
```
|
||||
|
||||
### 3. Configure Firewall Rules
|
||||
|
||||
```csharp
|
||||
PostgreSqlFlexibleServerFirewallRuleCollection firewallRules = server.GetPostgreSqlFlexibleServerFirewallRules();
|
||||
|
||||
// Allow specific IP range
|
||||
PostgreSqlFlexibleServerFirewallRuleData ruleData = new PostgreSqlFlexibleServerFirewallRuleData
|
||||
{
|
||||
StartIPAddress = System.Net.IPAddress.Parse("10.0.0.1"),
|
||||
EndIPAddress = System.Net.IPAddress.Parse("10.0.0.255")
|
||||
};
|
||||
|
||||
ArmOperation<PostgreSqlFlexibleServerFirewallRuleResource> operation = await firewallRules
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "allow-internal", ruleData);
|
||||
|
||||
// Allow Azure services
|
||||
PostgreSqlFlexibleServerFirewallRuleData azureServicesRule = new PostgreSqlFlexibleServerFirewallRuleData
|
||||
{
|
||||
StartIPAddress = System.Net.IPAddress.Parse("0.0.0.0"),
|
||||
EndIPAddress = System.Net.IPAddress.Parse("0.0.0.0")
|
||||
};
|
||||
|
||||
await firewallRules.CreateOrUpdateAsync(WaitUntil.Completed, "AllowAllAzureServicesAndResourcesWithinAzureIps", azureServicesRule);
|
||||
```
|
||||
|
||||
### 4. Update Server Configuration
|
||||
|
||||
```csharp
|
||||
PostgreSqlFlexibleServerConfigurationCollection configurations = server.GetPostgreSqlFlexibleServerConfigurations();
|
||||
|
||||
// Get current configuration
|
||||
PostgreSqlFlexibleServerConfigurationResource config = await configurations
|
||||
.GetAsync("max_connections");
|
||||
|
||||
// Update configuration
|
||||
PostgreSqlFlexibleServerConfigurationData configData = new PostgreSqlFlexibleServerConfigurationData
|
||||
{
|
||||
Value = "500",
|
||||
Source = "user-override"
|
||||
};
|
||||
|
||||
ArmOperation<PostgreSqlFlexibleServerConfigurationResource> operation = await configurations
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "max_connections", configData);
|
||||
|
||||
// Common PostgreSQL configurations to tune
|
||||
string[] commonParams = {
|
||||
"max_connections",
|
||||
"shared_buffers",
|
||||
"work_mem",
|
||||
"maintenance_work_mem",
|
||||
"effective_cache_size",
|
||||
"log_min_duration_statement"
|
||||
};
|
||||
```
|
||||
|
||||
### 5. Configure Entra ID Administrator
|
||||
|
||||
```csharp
|
||||
PostgreSqlFlexibleServerActiveDirectoryAdministratorCollection admins =
|
||||
server.GetPostgreSqlFlexibleServerActiveDirectoryAdministrators();
|
||||
|
||||
PostgreSqlFlexibleServerActiveDirectoryAdministratorData adminData =
|
||||
new PostgreSqlFlexibleServerActiveDirectoryAdministratorData
|
||||
{
|
||||
PrincipalType = PostgreSqlFlexibleServerPrincipalType.User,
|
||||
PrincipalName = "aad-admin@contoso.com",
|
||||
TenantId = Guid.Parse("<tenant-id>")
|
||||
};
|
||||
|
||||
ArmOperation<PostgreSqlFlexibleServerActiveDirectoryAdministratorResource> operation = await admins
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "<entra-object-id>", adminData);
|
||||
```
|
||||
|
||||
### 6. List and Manage Servers
|
||||
|
||||
```csharp
|
||||
// List servers in resource group
|
||||
await foreach (PostgreSqlFlexibleServerResource server in resourceGroup.GetPostgreSqlFlexibleServers())
|
||||
{
|
||||
Console.WriteLine($"Server: {server.Data.Name}");
|
||||
Console.WriteLine($" FQDN: {server.Data.FullyQualifiedDomainName}");
|
||||
Console.WriteLine($" Version: {server.Data.Version}");
|
||||
Console.WriteLine($" State: {server.Data.State}");
|
||||
Console.WriteLine($" SKU: {server.Data.Sku.Name} ({server.Data.Sku.Tier})");
|
||||
Console.WriteLine($" HA: {server.Data.HighAvailability?.Mode}");
|
||||
}
|
||||
|
||||
// List databases in server
|
||||
await foreach (PostgreSqlFlexibleServerDatabaseResource db in server.GetPostgreSqlFlexibleServerDatabases())
|
||||
{
|
||||
Console.WriteLine($"Database: {db.Data.Name}");
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Backup and Point-in-Time Restore
|
||||
|
||||
```csharp
|
||||
// List available backups
|
||||
await foreach (PostgreSqlFlexibleServerBackupResource backup in server.GetPostgreSqlFlexibleServerBackups())
|
||||
{
|
||||
Console.WriteLine($"Backup: {backup.Data.Name}");
|
||||
Console.WriteLine($" Type: {backup.Data.BackupType}");
|
||||
Console.WriteLine($" Completed: {backup.Data.CompletedOn}");
|
||||
}
|
||||
|
||||
// Point-in-time restore
|
||||
PostgreSqlFlexibleServerData restoreData = new PostgreSqlFlexibleServerData(AzureLocation.EastUS)
|
||||
{
|
||||
CreateMode = PostgreSqlFlexibleServerCreateMode.PointInTimeRestore,
|
||||
SourceServerResourceId = server.Id,
|
||||
PointInTimeUtc = DateTimeOffset.UtcNow.AddHours(-2)
|
||||
};
|
||||
|
||||
ArmOperation<PostgreSqlFlexibleServerResource> operation = await servers
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-postgresql-restored", restoreData);
|
||||
```
|
||||
|
||||
### 8. Create Read Replica
|
||||
|
||||
```csharp
|
||||
PostgreSqlFlexibleServerData replicaData = new PostgreSqlFlexibleServerData(AzureLocation.WestUS)
|
||||
{
|
||||
CreateMode = PostgreSqlFlexibleServerCreateMode.Replica,
|
||||
SourceServerResourceId = server.Id,
|
||||
Sku = new PostgreSqlFlexibleServerSku("Standard_D2ds_v4", PostgreSqlFlexibleServerSkuTier.GeneralPurpose)
|
||||
};
|
||||
|
||||
ArmOperation<PostgreSqlFlexibleServerResource> operation = await servers
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-postgresql-replica", replicaData);
|
||||
```
|
||||
|
||||
### 9. Stop and Start Server
|
||||
|
||||
```csharp
|
||||
PostgreSqlFlexibleServerResource server = await resourceGroup
|
||||
.GetPostgreSqlFlexibleServerAsync("my-postgresql-server");
|
||||
|
||||
// Stop server (saves costs when not in use)
|
||||
await server.StopAsync(WaitUntil.Completed);
|
||||
|
||||
// Start server
|
||||
await server.StartAsync(WaitUntil.Completed);
|
||||
|
||||
// Restart server
|
||||
await server.RestartAsync(WaitUntil.Completed, new PostgreSqlFlexibleServerRestartParameter
|
||||
{
|
||||
RestartWithFailover = true,
|
||||
FailoverMode = PostgreSqlFlexibleServerFailoverMode.PlannedFailover
|
||||
});
|
||||
```
|
||||
|
||||
### 10. Update Server (Scale)
|
||||
|
||||
```csharp
|
||||
PostgreSqlFlexibleServerResource server = await resourceGroup
|
||||
.GetPostgreSqlFlexibleServerAsync("my-postgresql-server");
|
||||
|
||||
PostgreSqlFlexibleServerPatch patch = new PostgreSqlFlexibleServerPatch
|
||||
{
|
||||
Sku = new PostgreSqlFlexibleServerSku("Standard_D4ds_v4", PostgreSqlFlexibleServerSkuTier.GeneralPurpose),
|
||||
Storage = new PostgreSqlFlexibleServerStorage
|
||||
{
|
||||
StorageSizeInGB = 256,
|
||||
Tier = PostgreSqlStorageTierName.P40
|
||||
}
|
||||
};
|
||||
|
||||
ArmOperation<PostgreSqlFlexibleServerResource> operation = await server
|
||||
.UpdateAsync(WaitUntil.Completed, patch);
|
||||
```
|
||||
|
||||
### 11. Delete Server
|
||||
|
||||
```csharp
|
||||
PostgreSqlFlexibleServerResource server = await resourceGroup
|
||||
.GetPostgreSqlFlexibleServerAsync("my-postgresql-server");
|
||||
|
||||
await server.DeleteAsync(WaitUntil.Completed);
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `PostgreSqlFlexibleServerResource` | Flexible Server instance |
|
||||
| `PostgreSqlFlexibleServerData` | Server configuration data |
|
||||
| `PostgreSqlFlexibleServerCollection` | Collection of servers |
|
||||
| `PostgreSqlFlexibleServerDatabaseResource` | Database within server |
|
||||
| `PostgreSqlFlexibleServerFirewallRuleResource` | IP firewall rule |
|
||||
| `PostgreSqlFlexibleServerConfigurationResource` | Server parameter |
|
||||
| `PostgreSqlFlexibleServerBackupResource` | Backup metadata |
|
||||
| `PostgreSqlFlexibleServerActiveDirectoryAdministratorResource` | Entra ID admin |
|
||||
| `PostgreSqlFlexibleServerSku` | SKU (compute tier + size) |
|
||||
| `PostgreSqlFlexibleServerStorage` | Storage configuration |
|
||||
| `PostgreSqlFlexibleServerHighAvailability` | HA configuration |
|
||||
| `PostgreSqlFlexibleServerBackupProperties` | Backup settings |
|
||||
| `PostgreSqlFlexibleServerAuthConfig` | Authentication settings |
|
||||
|
||||
## SKU Tiers
|
||||
|
||||
| Tier | Use Case | SKU Examples |
|
||||
|------|----------|--------------|
|
||||
| `Burstable` | Dev/test, light workloads | Standard_B1ms, Standard_B2s |
|
||||
| `GeneralPurpose` | Production workloads | Standard_D2ds_v4, Standard_D4ds_v4 |
|
||||
| `MemoryOptimized` | High memory requirements | Standard_E2ds_v4, Standard_E4ds_v4 |
|
||||
|
||||
## PostgreSQL Versions
|
||||
|
||||
| Version | Enum Value |
|
||||
|---------|------------|
|
||||
| PostgreSQL 11 | `Ver11` |
|
||||
| PostgreSQL 12 | `Ver12` |
|
||||
| PostgreSQL 13 | `Ver13` |
|
||||
| PostgreSQL 14 | `Ver14` |
|
||||
| PostgreSQL 15 | `Ver15` |
|
||||
| PostgreSQL 16 | `Ver16` |
|
||||
|
||||
## High Availability Modes
|
||||
|
||||
| Mode | Description |
|
||||
|------|-------------|
|
||||
| `Disabled` | No HA (single server) |
|
||||
| `SameZone` | HA within same availability zone |
|
||||
| `ZoneRedundant` | HA across availability zones |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Flexible Server** — Single Server is deprecated
|
||||
2. **Enable zone-redundant HA** — For production workloads
|
||||
3. **Use DefaultAzureCredential** — Prefer over connection strings
|
||||
4. **Configure Entra ID authentication** — More secure than SQL auth alone
|
||||
5. **Enable both auth methods** — Entra ID + password for flexibility
|
||||
6. **Set appropriate backup retention** — 7-35 days based on compliance
|
||||
7. **Use private endpoints** — For secure network access
|
||||
8. **Tune server parameters** — Based on workload characteristics
|
||||
9. **Use read replicas** — For read-heavy workloads
|
||||
10. **Stop dev/test servers** — Save costs when not in use
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
ArmOperation<PostgreSqlFlexibleServerResource> operation = await servers
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-postgresql", data);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("Server already exists");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 400)
|
||||
{
|
||||
Console.WriteLine($"Invalid configuration: {ex.Message}");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Azure error: {ex.Status} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Connection String
|
||||
|
||||
After creating the server, connect using:
|
||||
|
||||
```csharp
|
||||
// Npgsql connection string
|
||||
string connectionString = $"Host={server.Data.FullyQualifiedDomainName};" +
|
||||
"Database=myappdb;" +
|
||||
"Username=pgadmin;" +
|
||||
"Password=YourSecurePassword123!;" +
|
||||
"SSL Mode=Require;Trust Server Certificate=true;";
|
||||
|
||||
// With Entra ID token (recommended)
|
||||
var credential = new DefaultAzureCredential();
|
||||
var token = await credential.GetTokenAsync(
|
||||
new TokenRequestContext(new[] { "https://ossrdbms-aad.database.windows.net/.default" }));
|
||||
|
||||
string connectionString = $"Host={server.Data.FullyQualifiedDomainName};" +
|
||||
"Database=myappdb;" +
|
||||
$"Username=aad-admin@contoso.com;" +
|
||||
$"Password={token.Token};" +
|
||||
"SSL Mode=Require;";
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.ResourceManager.PostgreSql` | PostgreSQL management (this SDK) | `dotnet add package Azure.ResourceManager.PostgreSql` |
|
||||
| `Azure.ResourceManager.MySql` | MySQL management | `dotnet add package Azure.ResourceManager.MySql` |
|
||||
| `Npgsql` | PostgreSQL data access | `dotnet add package Npgsql` |
|
||||
| `Npgsql.EntityFrameworkCore.PostgreSQL` | EF Core provider | `dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.ResourceManager.PostgreSql |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.resourcemanager.postgresql |
|
||||
| Product Documentation | https://learn.microsoft.com/azure/postgresql/flexible-server/ |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/postgresql/Azure.ResourceManager.PostgreSql |
|
||||
356
skills/official/microsoft/dotnet/data/redis/SKILL.md
Normal file
356
skills/official/microsoft/dotnet/data/redis/SKILL.md
Normal file
@@ -0,0 +1,356 @@
|
||||
---
|
||||
name: azure-resource-manager-redis-dotnet
|
||||
description: |
|
||||
Azure Resource Manager SDK for Redis in .NET. Use for MANAGEMENT PLANE operations: creating/managing Azure Cache for Redis instances, firewall rules, access keys, patch schedules, linked servers (geo-replication), and private endpoints via Azure Resource Manager. NOT for data plane operations (get/set keys, pub/sub) - use StackExchange.Redis for that. Triggers: "Redis cache", "create Redis", "manage Redis", "ARM Redis", "RedisResource", "provision Redis", "Azure Cache for Redis".
|
||||
package: Azure.ResourceManager.Redis
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.Redis (.NET)
|
||||
|
||||
Management plane SDK for provisioning and managing Azure Cache for Redis resources via Azure Resource Manager.
|
||||
|
||||
> **⚠️ Management vs Data Plane**
|
||||
> - **This SDK (Azure.ResourceManager.Redis)**: Create caches, configure firewall rules, manage access keys, set up geo-replication
|
||||
> - **Data Plane SDK (StackExchange.Redis)**: Get/set keys, pub/sub, streams, Lua scripts
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.Redis
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Version**: 1.5.1 (Stable)
|
||||
**API Version**: 2024-11-01
|
||||
**Target Frameworks**: .NET 8.0, .NET Standard 2.0
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
# For service principal auth (optional)
|
||||
AZURE_TENANT_ID=<tenant-id>
|
||||
AZURE_CLIENT_ID=<client-id>
|
||||
AZURE_CLIENT_SECRET=<client-secret>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.Redis;
|
||||
|
||||
// Always use DefaultAzureCredential
|
||||
var credential = new DefaultAzureCredential();
|
||||
var armClient = new ArmClient(credential);
|
||||
|
||||
// Get subscription
|
||||
var subscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
|
||||
var subscription = armClient.GetSubscriptionResource(
|
||||
new ResourceIdentifier($"/subscriptions/{subscriptionId}"));
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
ArmClient
|
||||
└── SubscriptionResource
|
||||
└── ResourceGroupResource
|
||||
└── RedisResource
|
||||
├── RedisFirewallRuleResource
|
||||
├── RedisPatchScheduleResource
|
||||
├── RedisLinkedServerWithPropertyResource
|
||||
├── RedisPrivateEndpointConnectionResource
|
||||
└── RedisCacheAccessPolicyResource
|
||||
```
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Create Redis Cache
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.Redis;
|
||||
using Azure.ResourceManager.Redis.Models;
|
||||
|
||||
// Get resource group
|
||||
var resourceGroup = await subscription
|
||||
.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
// Define cache configuration
|
||||
var cacheData = new RedisCreateOrUpdateContent(
|
||||
location: AzureLocation.EastUS,
|
||||
sku: new RedisSku(RedisSkuName.Standard, RedisSkuFamily.BasicOrStandard, 1))
|
||||
{
|
||||
EnableNonSslPort = false,
|
||||
MinimumTlsVersion = RedisTlsVersion.Tls1_2,
|
||||
RedisConfiguration = new RedisCommonConfiguration
|
||||
{
|
||||
MaxMemoryPolicy = "volatile-lru"
|
||||
},
|
||||
Tags =
|
||||
{
|
||||
["environment"] = "production"
|
||||
}
|
||||
};
|
||||
|
||||
// Create cache (long-running operation)
|
||||
var cacheCollection = resourceGroup.Value.GetAllRedis();
|
||||
var operation = await cacheCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-redis-cache",
|
||||
cacheData);
|
||||
|
||||
RedisResource cache = operation.Value;
|
||||
Console.WriteLine($"Cache created: {cache.Data.HostName}");
|
||||
```
|
||||
|
||||
### 2. Get Redis Cache
|
||||
|
||||
```csharp
|
||||
// Get existing cache
|
||||
var cache = await resourceGroup.Value
|
||||
.GetRedisAsync("my-redis-cache");
|
||||
|
||||
Console.WriteLine($"Host: {cache.Value.Data.HostName}");
|
||||
Console.WriteLine($"Port: {cache.Value.Data.Port}");
|
||||
Console.WriteLine($"SSL Port: {cache.Value.Data.SslPort}");
|
||||
Console.WriteLine($"Provisioning State: {cache.Value.Data.ProvisioningState}");
|
||||
```
|
||||
|
||||
### 3. Update Redis Cache
|
||||
|
||||
```csharp
|
||||
var patchData = new RedisPatch
|
||||
{
|
||||
Sku = new RedisSku(RedisSkuName.Standard, RedisSkuFamily.BasicOrStandard, 2),
|
||||
RedisConfiguration = new RedisCommonConfiguration
|
||||
{
|
||||
MaxMemoryPolicy = "allkeys-lru"
|
||||
}
|
||||
};
|
||||
|
||||
var updateOperation = await cache.Value.UpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
patchData);
|
||||
```
|
||||
|
||||
### 4. Delete Redis Cache
|
||||
|
||||
```csharp
|
||||
await cache.Value.DeleteAsync(WaitUntil.Completed);
|
||||
```
|
||||
|
||||
### 5. Get Access Keys
|
||||
|
||||
```csharp
|
||||
var keys = await cache.Value.GetKeysAsync();
|
||||
Console.WriteLine($"Primary Key: {keys.Value.PrimaryKey}");
|
||||
Console.WriteLine($"Secondary Key: {keys.Value.SecondaryKey}");
|
||||
```
|
||||
|
||||
### 6. Regenerate Access Keys
|
||||
|
||||
```csharp
|
||||
var regenerateContent = new RedisRegenerateKeyContent(RedisRegenerateKeyType.Primary);
|
||||
var newKeys = await cache.Value.RegenerateKeyAsync(regenerateContent);
|
||||
Console.WriteLine($"New Primary Key: {newKeys.Value.PrimaryKey}");
|
||||
```
|
||||
|
||||
### 7. Manage Firewall Rules
|
||||
|
||||
```csharp
|
||||
// Create firewall rule
|
||||
var firewallData = new RedisFirewallRuleData(
|
||||
startIP: System.Net.IPAddress.Parse("10.0.0.1"),
|
||||
endIP: System.Net.IPAddress.Parse("10.0.0.255"));
|
||||
|
||||
var firewallCollection = cache.Value.GetRedisFirewallRules();
|
||||
var firewallOperation = await firewallCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"allow-internal-network",
|
||||
firewallData);
|
||||
|
||||
// List all firewall rules
|
||||
await foreach (var rule in firewallCollection.GetAllAsync())
|
||||
{
|
||||
Console.WriteLine($"Rule: {rule.Data.Name} ({rule.Data.StartIP} - {rule.Data.EndIP})");
|
||||
}
|
||||
|
||||
// Delete firewall rule
|
||||
var ruleToDelete = await firewallCollection.GetAsync("allow-internal-network");
|
||||
await ruleToDelete.Value.DeleteAsync(WaitUntil.Completed);
|
||||
```
|
||||
|
||||
### 8. Configure Patch Schedule (Premium SKU)
|
||||
|
||||
```csharp
|
||||
// Patch schedules require Premium SKU
|
||||
var scheduleData = new RedisPatchScheduleData(
|
||||
new[]
|
||||
{
|
||||
new RedisPatchScheduleSetting(RedisDayOfWeek.Saturday, 2) // 2 AM Saturday
|
||||
{
|
||||
MaintenanceWindow = TimeSpan.FromHours(5)
|
||||
},
|
||||
new RedisPatchScheduleSetting(RedisDayOfWeek.Sunday, 2) // 2 AM Sunday
|
||||
{
|
||||
MaintenanceWindow = TimeSpan.FromHours(5)
|
||||
}
|
||||
});
|
||||
|
||||
var scheduleCollection = cache.Value.GetRedisPatchSchedules();
|
||||
await scheduleCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
RedisPatchScheduleDefaultName.Default,
|
||||
scheduleData);
|
||||
```
|
||||
|
||||
### 9. Import/Export Data (Premium SKU)
|
||||
|
||||
```csharp
|
||||
// Import data from blob storage
|
||||
var importContent = new ImportRdbContent(
|
||||
files: new[] { "https://mystorageaccount.blob.core.windows.net/container/dump.rdb" },
|
||||
format: "RDB");
|
||||
|
||||
await cache.Value.ImportDataAsync(WaitUntil.Completed, importContent);
|
||||
|
||||
// Export data to blob storage
|
||||
var exportContent = new ExportRdbContent(
|
||||
prefix: "backup",
|
||||
container: "https://mystorageaccount.blob.core.windows.net/container?sastoken",
|
||||
format: "RDB");
|
||||
|
||||
await cache.Value.ExportDataAsync(WaitUntil.Completed, exportContent);
|
||||
```
|
||||
|
||||
### 10. Force Reboot
|
||||
|
||||
```csharp
|
||||
var rebootContent = new RedisRebootContent
|
||||
{
|
||||
RebootType = RedisRebootType.AllNodes,
|
||||
ShardId = 0 // For clustered caches
|
||||
};
|
||||
|
||||
await cache.Value.ForceRebootAsync(rebootContent);
|
||||
```
|
||||
|
||||
## SKU Reference
|
||||
|
||||
| SKU | Family | Capacity | Features |
|
||||
|-----|--------|----------|----------|
|
||||
| Basic | C | 0-6 | Single node, no SLA, dev/test only |
|
||||
| Standard | C | 0-6 | Two nodes (primary/replica), SLA |
|
||||
| Premium | P | 1-5 | Clustering, geo-replication, VNet, persistence |
|
||||
|
||||
**Capacity Sizes (Family C - Basic/Standard)**:
|
||||
- C0: 250 MB
|
||||
- C1: 1 GB
|
||||
- C2: 2.5 GB
|
||||
- C3: 6 GB
|
||||
- C4: 13 GB
|
||||
- C5: 26 GB
|
||||
- C6: 53 GB
|
||||
|
||||
**Capacity Sizes (Family P - Premium)**:
|
||||
- P1: 6 GB per shard
|
||||
- P2: 13 GB per shard
|
||||
- P3: 26 GB per shard
|
||||
- P4: 53 GB per shard
|
||||
- P5: 120 GB per shard
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ArmClient` | Entry point for all ARM operations |
|
||||
| `RedisResource` | Represents a Redis cache instance |
|
||||
| `RedisCollection` | Collection for cache CRUD operations |
|
||||
| `RedisFirewallRuleResource` | Firewall rule for IP filtering |
|
||||
| `RedisPatchScheduleResource` | Maintenance window configuration |
|
||||
| `RedisLinkedServerWithPropertyResource` | Geo-replication linked server |
|
||||
| `RedisPrivateEndpointConnectionResource` | Private endpoint connection |
|
||||
| `RedisCacheAccessPolicyResource` | RBAC access policy |
|
||||
| `RedisCreateOrUpdateContent` | Cache creation payload |
|
||||
| `RedisPatch` | Cache update payload |
|
||||
| `RedisSku` | SKU configuration (name, family, capacity) |
|
||||
| `RedisAccessKeys` | Primary and secondary access keys |
|
||||
| `RedisRegenerateKeyContent` | Key regeneration request |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `WaitUntil.Completed`** for operations that must finish before proceeding
|
||||
2. **Use `WaitUntil.Started`** when you want to poll manually or run operations in parallel
|
||||
3. **Always use `DefaultAzureCredential`** — never hardcode keys
|
||||
4. **Handle `RequestFailedException`** for ARM API errors
|
||||
5. **Use `CreateOrUpdateAsync`** for idempotent operations
|
||||
6. **Navigate hierarchy** via `Get*` methods (e.g., `cache.GetRedisFirewallRules()`)
|
||||
7. **Use Premium SKU** for production workloads requiring geo-replication, clustering, or persistence
|
||||
8. **Enable TLS 1.2 minimum** — set `MinimumTlsVersion = RedisTlsVersion.Tls1_2`
|
||||
9. **Disable non-SSL port** — set `EnableNonSslPort = false` for security
|
||||
10. **Rotate keys regularly** — use `RegenerateKeyAsync` and update connection strings
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
var operation = await cacheCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed, cacheName, cacheData);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("Cache already exists");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 400)
|
||||
{
|
||||
Console.WriteLine($"Invalid configuration: {ex.Message}");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"ARM Error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **SKU downgrades not allowed** — You cannot downgrade from Premium to Standard/Basic
|
||||
2. **Clustering requires Premium** — Shard configuration only available on Premium SKU
|
||||
3. **Geo-replication requires Premium** — Linked servers only work with Premium caches
|
||||
4. **VNet injection requires Premium** — Virtual network support is Premium-only
|
||||
5. **Patch schedules require Premium** — Maintenance windows only configurable on Premium
|
||||
6. **Cache name globally unique** — Redis cache names must be unique across all Azure subscriptions
|
||||
7. **Long provisioning times** — Cache creation can take 15-20 minutes; use `WaitUntil.Started` for async patterns
|
||||
|
||||
## Connecting with StackExchange.Redis (Data Plane)
|
||||
|
||||
After creating the cache with this management SDK, use StackExchange.Redis for data operations:
|
||||
|
||||
```csharp
|
||||
using StackExchange.Redis;
|
||||
|
||||
// Get connection info from management SDK
|
||||
var cache = await resourceGroup.Value.GetRedisAsync("my-redis-cache");
|
||||
var keys = await cache.Value.GetKeysAsync();
|
||||
|
||||
// Connect with StackExchange.Redis
|
||||
var connectionString = $"{cache.Value.Data.HostName}:{cache.Value.Data.SslPort},password={keys.Value.PrimaryKey},ssl=True,abortConnect=False";
|
||||
var connection = ConnectionMultiplexer.Connect(connectionString);
|
||||
var db = connection.GetDatabase();
|
||||
|
||||
// Data operations
|
||||
await db.StringSetAsync("key", "value");
|
||||
var value = await db.StringGetAsync("key");
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `StackExchange.Redis` | Data plane (get/set, pub/sub, streams) | `dotnet add package StackExchange.Redis` |
|
||||
| `Azure.ResourceManager.Redis` | Management plane (this SDK) | `dotnet add package Azure.ResourceManager.Redis` |
|
||||
| `Microsoft.Azure.StackExchangeRedis` | Azure-specific Redis extensions | `dotnet add package Microsoft.Azure.StackExchangeRedis` |
|
||||
319
skills/official/microsoft/dotnet/data/sql/SKILL.md
Normal file
319
skills/official/microsoft/dotnet/data/sql/SKILL.md
Normal file
@@ -0,0 +1,319 @@
|
||||
---
|
||||
name: azure-resource-manager-sql-dotnet
|
||||
description: |
|
||||
Azure Resource Manager SDK for Azure SQL in .NET. Use for MANAGEMENT PLANE operations: creating/managing SQL servers, databases, elastic pools, firewall rules, and failover groups via Azure Resource Manager. NOT for data plane operations (executing queries) - use Microsoft.Data.SqlClient for that. Triggers: "SQL server", "create SQL database", "manage SQL resources", "ARM SQL", "SqlServerResource", "provision Azure SQL", "elastic pool", "firewall rule".
|
||||
package: Azure.ResourceManager.Sql
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.Sql (.NET)
|
||||
|
||||
Management plane SDK for provisioning and managing Azure SQL resources via Azure Resource Manager.
|
||||
|
||||
> **⚠️ Management vs Data Plane**
|
||||
> - **This SDK (Azure.ResourceManager.Sql)**: Create servers, databases, elastic pools, configure firewall rules, manage failover groups
|
||||
> - **Data Plane SDK (Microsoft.Data.SqlClient)**: Execute queries, stored procedures, manage connections
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.Sql
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Versions**: Stable v1.3.0, Preview v1.4.0-beta.3
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
# For service principal auth (optional)
|
||||
AZURE_TENANT_ID=<tenant-id>
|
||||
AZURE_CLIENT_ID=<client-id>
|
||||
AZURE_CLIENT_SECRET=<client-secret>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.Sql;
|
||||
|
||||
// Always use DefaultAzureCredential
|
||||
var credential = new DefaultAzureCredential();
|
||||
var armClient = new ArmClient(credential);
|
||||
|
||||
// Get subscription
|
||||
var subscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
|
||||
var subscription = armClient.GetSubscriptionResource(
|
||||
new ResourceIdentifier($"/subscriptions/{subscriptionId}"));
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
ArmClient
|
||||
└── SubscriptionResource
|
||||
└── ResourceGroupResource
|
||||
└── SqlServerResource
|
||||
├── SqlDatabaseResource
|
||||
├── ElasticPoolResource
|
||||
│ └── ElasticPoolDatabaseResource
|
||||
├── SqlFirewallRuleResource
|
||||
├── FailoverGroupResource
|
||||
├── ServerBlobAuditingPolicyResource
|
||||
├── EncryptionProtectorResource
|
||||
└── VirtualNetworkRuleResource
|
||||
```
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### 1. Create SQL Server
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.Sql;
|
||||
using Azure.ResourceManager.Sql.Models;
|
||||
|
||||
// Get resource group
|
||||
var resourceGroup = await subscription
|
||||
.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
// Define server
|
||||
var serverData = new SqlServerData(AzureLocation.EastUS)
|
||||
{
|
||||
AdministratorLogin = "sqladmin",
|
||||
AdministratorLoginPassword = "YourSecurePassword123!",
|
||||
Version = "12.0",
|
||||
MinimalTlsVersion = SqlMinimalTlsVersion.Tls1_2,
|
||||
PublicNetworkAccess = ServerNetworkAccessFlag.Enabled
|
||||
};
|
||||
|
||||
// Create server (long-running operation)
|
||||
var serverCollection = resourceGroup.Value.GetSqlServers();
|
||||
var operation = await serverCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-sql-server",
|
||||
serverData);
|
||||
|
||||
SqlServerResource server = operation.Value;
|
||||
```
|
||||
|
||||
### 2. Create SQL Database
|
||||
|
||||
```csharp
|
||||
var databaseData = new SqlDatabaseData(AzureLocation.EastUS)
|
||||
{
|
||||
Sku = new SqlSku("S0") { Tier = "Standard" },
|
||||
MaxSizeBytes = 2L * 1024 * 1024 * 1024, // 2 GB
|
||||
Collation = "SQL_Latin1_General_CP1_CI_AS",
|
||||
RequestedBackupStorageRedundancy = SqlBackupStorageRedundancy.Local
|
||||
};
|
||||
|
||||
var databaseCollection = server.GetSqlDatabases();
|
||||
var dbOperation = await databaseCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-database",
|
||||
databaseData);
|
||||
|
||||
SqlDatabaseResource database = dbOperation.Value;
|
||||
```
|
||||
|
||||
### 3. Create Elastic Pool
|
||||
|
||||
```csharp
|
||||
var poolData = new ElasticPoolData(AzureLocation.EastUS)
|
||||
{
|
||||
Sku = new SqlSku("StandardPool")
|
||||
{
|
||||
Tier = "Standard",
|
||||
Capacity = 100 // 100 eDTUs
|
||||
},
|
||||
PerDatabaseSettings = new ElasticPoolPerDatabaseSettings
|
||||
{
|
||||
MinCapacity = 0,
|
||||
MaxCapacity = 100
|
||||
}
|
||||
};
|
||||
|
||||
var poolCollection = server.GetElasticPools();
|
||||
var poolOperation = await poolCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-elastic-pool",
|
||||
poolData);
|
||||
|
||||
ElasticPoolResource pool = poolOperation.Value;
|
||||
```
|
||||
|
||||
### 4. Add Database to Elastic Pool
|
||||
|
||||
```csharp
|
||||
var databaseData = new SqlDatabaseData(AzureLocation.EastUS)
|
||||
{
|
||||
ElasticPoolId = pool.Id
|
||||
};
|
||||
|
||||
await databaseCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"pooled-database",
|
||||
databaseData);
|
||||
```
|
||||
|
||||
### 5. Configure Firewall Rules
|
||||
|
||||
```csharp
|
||||
// Allow Azure services
|
||||
var azureServicesRule = new SqlFirewallRuleData
|
||||
{
|
||||
StartIPAddress = "0.0.0.0",
|
||||
EndIPAddress = "0.0.0.0"
|
||||
};
|
||||
|
||||
var firewallCollection = server.GetSqlFirewallRules();
|
||||
await firewallCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"AllowAzureServices",
|
||||
azureServicesRule);
|
||||
|
||||
// Allow specific IP range
|
||||
var clientRule = new SqlFirewallRuleData
|
||||
{
|
||||
StartIPAddress = "203.0.113.0",
|
||||
EndIPAddress = "203.0.113.255"
|
||||
};
|
||||
|
||||
await firewallCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"AllowClientIPs",
|
||||
clientRule);
|
||||
```
|
||||
|
||||
### 6. List Resources
|
||||
|
||||
```csharp
|
||||
// List all servers in subscription
|
||||
await foreach (var srv in subscription.GetSqlServersAsync())
|
||||
{
|
||||
Console.WriteLine($"Server: {srv.Data.Name} in {srv.Data.Location}");
|
||||
}
|
||||
|
||||
// List databases in a server
|
||||
await foreach (var db in server.GetSqlDatabases())
|
||||
{
|
||||
Console.WriteLine($"Database: {db.Data.Name}, SKU: {db.Data.Sku?.Name}");
|
||||
}
|
||||
|
||||
// List elastic pools
|
||||
await foreach (var ep in server.GetElasticPools())
|
||||
{
|
||||
Console.WriteLine($"Pool: {ep.Data.Name}, DTU: {ep.Data.Sku?.Capacity}");
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Get Connection String
|
||||
|
||||
```csharp
|
||||
// Build connection string (server FQDN is predictable)
|
||||
var serverFqdn = $"{server.Data.Name}.database.windows.net";
|
||||
var connectionString = $"Server=tcp:{serverFqdn},1433;" +
|
||||
$"Initial Catalog={database.Data.Name};" +
|
||||
"Persist Security Info=False;" +
|
||||
$"User ID={server.Data.AdministratorLogin};" +
|
||||
"Password=<your-password>;" +
|
||||
"MultipleActiveResultSets=False;" +
|
||||
"Encrypt=True;" +
|
||||
"TrustServerCertificate=False;" +
|
||||
"Connection Timeout=30;";
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ArmClient` | Entry point for all ARM operations |
|
||||
| `SqlServerResource` | Represents an Azure SQL server |
|
||||
| `SqlServerCollection` | Collection for server CRUD |
|
||||
| `SqlDatabaseResource` | Represents a SQL database |
|
||||
| `SqlDatabaseCollection` | Collection for database CRUD |
|
||||
| `ElasticPoolResource` | Represents an elastic pool |
|
||||
| `ElasticPoolCollection` | Collection for elastic pool CRUD |
|
||||
| `SqlFirewallRuleResource` | Represents a firewall rule |
|
||||
| `SqlFirewallRuleCollection` | Collection for firewall rule CRUD |
|
||||
| `SqlServerData` | Server creation/update payload |
|
||||
| `SqlDatabaseData` | Database creation/update payload |
|
||||
| `ElasticPoolData` | Elastic pool creation/update payload |
|
||||
| `SqlFirewallRuleData` | Firewall rule creation/update payload |
|
||||
| `SqlSku` | SKU configuration (tier, capacity) |
|
||||
|
||||
## Common SKUs
|
||||
|
||||
### Database SKUs
|
||||
|
||||
| SKU Name | Tier | Description |
|
||||
|----------|------|-------------|
|
||||
| `Basic` | Basic | 5 DTUs, 2 GB max |
|
||||
| `S0`-`S12` | Standard | 10-3000 DTUs |
|
||||
| `P1`-`P15` | Premium | 125-4000 DTUs |
|
||||
| `GP_Gen5_2` | GeneralPurpose | vCore-based, 2 vCores |
|
||||
| `BC_Gen5_2` | BusinessCritical | vCore-based, 2 vCores |
|
||||
| `HS_Gen5_2` | Hyperscale | vCore-based, 2 vCores |
|
||||
|
||||
### Elastic Pool SKUs
|
||||
|
||||
| SKU Name | Tier | Description |
|
||||
|----------|------|-------------|
|
||||
| `BasicPool` | Basic | 50-1600 eDTUs |
|
||||
| `StandardPool` | Standard | 50-3000 eDTUs |
|
||||
| `PremiumPool` | Premium | 125-4000 eDTUs |
|
||||
| `GP_Gen5_2` | GeneralPurpose | vCore-based |
|
||||
| `BC_Gen5_2` | BusinessCritical | vCore-based |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `WaitUntil.Completed`** for operations that must finish before proceeding
|
||||
2. **Use `WaitUntil.Started`** when you want to poll manually or run operations in parallel
|
||||
3. **Always use `DefaultAzureCredential`** — never hardcode passwords in production
|
||||
4. **Handle `RequestFailedException`** for ARM API errors
|
||||
5. **Use `CreateOrUpdateAsync`** for idempotent operations
|
||||
6. **Navigate hierarchy** via `Get*` methods (e.g., `server.GetSqlDatabases()`)
|
||||
7. **Use elastic pools** for cost optimization when managing multiple databases
|
||||
8. **Configure firewall rules** before attempting connections
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
var operation = await serverCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed, serverName, serverData);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("Server already exists");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 400)
|
||||
{
|
||||
Console.WriteLine($"Invalid request: {ex.Message}");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"ARM Error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Reference Files
|
||||
|
||||
| File | When to Read |
|
||||
|------|--------------|
|
||||
| [references/server-management.md](references/server-management.md) | Server CRUD, admin credentials, Azure AD auth, networking |
|
||||
| [references/database-operations.md](references/database-operations.md) | Database CRUD, scaling, backup, restore, copy |
|
||||
| [references/elastic-pools.md](references/elastic-pools.md) | Pool management, adding/removing databases, scaling |
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Microsoft.Data.SqlClient` | Data plane (execute queries, stored procedures) | `dotnet add package Microsoft.Data.SqlClient` |
|
||||
| `Azure.ResourceManager.Sql` | Management plane (this SDK) | `dotnet add package Azure.ResourceManager.Sql` |
|
||||
| `Microsoft.EntityFrameworkCore.SqlServer` | ORM for SQL Server | `dotnet add package Microsoft.EntityFrameworkCore.SqlServer` |
|
||||
@@ -0,0 +1,440 @@
|
||||
---
|
||||
name: microsoft-azure-webjobs-extensions-authentication-events-dotnet
|
||||
description: |
|
||||
Microsoft Entra Authentication Events SDK for .NET. Azure Functions triggers for custom authentication extensions. Use for token enrichment, custom claims, attribute collection, and OTP customization in Entra ID. Triggers: "Authentication Events", "WebJobsAuthenticationEventsTrigger", "OnTokenIssuanceStart", "OnAttributeCollectionStart", "custom claims", "token enrichment", "Entra custom extension", "authentication extension".
|
||||
---
|
||||
|
||||
# Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents (.NET)
|
||||
|
||||
Azure Functions extension for handling Microsoft Entra ID custom authentication events.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents
|
||||
```
|
||||
|
||||
**Current Version**: v1.1.0 (stable)
|
||||
|
||||
## Supported Events
|
||||
|
||||
| Event | Purpose |
|
||||
|-------|---------|
|
||||
| `OnTokenIssuanceStart` | Add custom claims to tokens during issuance |
|
||||
| `OnAttributeCollectionStart` | Customize attribute collection UI before display |
|
||||
| `OnAttributeCollectionSubmit` | Validate/modify attributes after user submission |
|
||||
| `OnOtpSend` | Custom OTP delivery (SMS, email, etc.) |
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Token Enrichment (Add Custom Claims)
|
||||
|
||||
Add custom claims to access or ID tokens during sign-in.
|
||||
|
||||
```csharp
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;
|
||||
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.TokenIssuanceStart;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
public static class TokenEnrichmentFunction
|
||||
{
|
||||
[FunctionName("OnTokenIssuanceStart")]
|
||||
public static WebJobsAuthenticationEventResponse Run(
|
||||
[WebJobsAuthenticationEventsTrigger] WebJobsTokenIssuanceStartRequest request,
|
||||
ILogger log)
|
||||
{
|
||||
log.LogInformation("Token issuance event for user: {UserId}",
|
||||
request.Data?.AuthenticationContext?.User?.Id);
|
||||
|
||||
// Create response with custom claims
|
||||
var response = new WebJobsTokenIssuanceStartResponse();
|
||||
|
||||
// Add claims to the token
|
||||
response.Actions.Add(new WebJobsProvideClaimsForToken
|
||||
{
|
||||
Claims = new Dictionary<string, string>
|
||||
{
|
||||
{ "customClaim1", "customValue1" },
|
||||
{ "department", "Engineering" },
|
||||
{ "costCenter", "CC-12345" },
|
||||
{ "apiVersion", "v2" }
|
||||
}
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Token Enrichment with External Data
|
||||
|
||||
Fetch claims from external systems (databases, APIs).
|
||||
|
||||
```csharp
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;
|
||||
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.TokenIssuanceStart;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
|
||||
public static class TokenEnrichmentWithExternalData
|
||||
{
|
||||
private static readonly HttpClient _httpClient = new();
|
||||
|
||||
[FunctionName("OnTokenIssuanceStartExternal")]
|
||||
public static async Task<WebJobsAuthenticationEventResponse> Run(
|
||||
[WebJobsAuthenticationEventsTrigger] WebJobsTokenIssuanceStartRequest request,
|
||||
ILogger log)
|
||||
{
|
||||
string? userId = request.Data?.AuthenticationContext?.User?.Id;
|
||||
|
||||
if (string.IsNullOrEmpty(userId))
|
||||
{
|
||||
log.LogWarning("No user ID in request");
|
||||
return new WebJobsTokenIssuanceStartResponse();
|
||||
}
|
||||
|
||||
// Fetch user data from external API
|
||||
var userProfile = await GetUserProfileAsync(userId);
|
||||
|
||||
var response = new WebJobsTokenIssuanceStartResponse();
|
||||
response.Actions.Add(new WebJobsProvideClaimsForToken
|
||||
{
|
||||
Claims = new Dictionary<string, string>
|
||||
{
|
||||
{ "employeeId", userProfile.EmployeeId },
|
||||
{ "department", userProfile.Department },
|
||||
{ "roles", string.Join(",", userProfile.Roles) }
|
||||
}
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static async Task<UserProfile> GetUserProfileAsync(string userId)
|
||||
{
|
||||
var response = await _httpClient.GetAsync($"https://api.example.com/users/{userId}");
|
||||
response.EnsureSuccessStatusCode();
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
return JsonSerializer.Deserialize<UserProfile>(json)!;
|
||||
}
|
||||
}
|
||||
|
||||
public record UserProfile(string EmployeeId, string Department, string[] Roles);
|
||||
```
|
||||
|
||||
### 3. Attribute Collection - Customize UI (Start Event)
|
||||
|
||||
Customize the attribute collection page before it's displayed.
|
||||
|
||||
```csharp
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;
|
||||
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
public static class AttributeCollectionStartFunction
|
||||
{
|
||||
[FunctionName("OnAttributeCollectionStart")]
|
||||
public static WebJobsAuthenticationEventResponse Run(
|
||||
[WebJobsAuthenticationEventsTrigger] WebJobsAttributeCollectionStartRequest request,
|
||||
ILogger log)
|
||||
{
|
||||
log.LogInformation("Attribute collection start for correlation: {CorrelationId}",
|
||||
request.Data?.AuthenticationContext?.CorrelationId);
|
||||
|
||||
var response = new WebJobsAttributeCollectionStartResponse();
|
||||
|
||||
// Option 1: Continue with default behavior
|
||||
response.Actions.Add(new WebJobsContinueWithDefaultBehavior());
|
||||
|
||||
// Option 2: Prefill attributes
|
||||
// response.Actions.Add(new WebJobsSetPrefillValues
|
||||
// {
|
||||
// Attributes = new Dictionary<string, string>
|
||||
// {
|
||||
// { "city", "Seattle" },
|
||||
// { "country", "USA" }
|
||||
// }
|
||||
// });
|
||||
|
||||
// Option 3: Show blocking page (prevent sign-up)
|
||||
// response.Actions.Add(new WebJobsShowBlockPage
|
||||
// {
|
||||
// Message = "Sign-up is currently disabled."
|
||||
// });
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Attribute Collection - Validate Submission (Submit Event)
|
||||
|
||||
Validate and modify attributes after user submission.
|
||||
|
||||
```csharp
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;
|
||||
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
public static class AttributeCollectionSubmitFunction
|
||||
{
|
||||
[FunctionName("OnAttributeCollectionSubmit")]
|
||||
public static WebJobsAuthenticationEventResponse Run(
|
||||
[WebJobsAuthenticationEventsTrigger] WebJobsAttributeCollectionSubmitRequest request,
|
||||
ILogger log)
|
||||
{
|
||||
var response = new WebJobsAttributeCollectionSubmitResponse();
|
||||
|
||||
// Access submitted attributes
|
||||
var attributes = request.Data?.UserSignUpInfo?.Attributes;
|
||||
|
||||
string? email = attributes?["email"]?.ToString();
|
||||
string? displayName = attributes?["displayName"]?.ToString();
|
||||
|
||||
// Validation example: block certain email domains
|
||||
if (email?.EndsWith("@blocked.com") == true)
|
||||
{
|
||||
response.Actions.Add(new WebJobsShowBlockPage
|
||||
{
|
||||
Message = "Sign-up from this email domain is not allowed."
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
// Validation example: show validation error
|
||||
if (string.IsNullOrEmpty(displayName) || displayName.Length < 3)
|
||||
{
|
||||
response.Actions.Add(new WebJobsShowValidationError
|
||||
{
|
||||
Message = "Display name must be at least 3 characters.",
|
||||
AttributeErrors = new Dictionary<string, string>
|
||||
{
|
||||
{ "displayName", "Name is too short" }
|
||||
}
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
// Modify attributes before saving
|
||||
response.Actions.Add(new WebJobsModifyAttributeValues
|
||||
{
|
||||
Attributes = new Dictionary<string, string>
|
||||
{
|
||||
{ "displayName", displayName.Trim() },
|
||||
{ "city", attributes?["city"]?.ToString()?.ToUpperInvariant() ?? "" }
|
||||
}
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Custom OTP Delivery
|
||||
|
||||
Send one-time passwords via custom channels (SMS, email, push notification).
|
||||
|
||||
```csharp
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;
|
||||
using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
public static class CustomOtpFunction
|
||||
{
|
||||
[FunctionName("OnOtpSend")]
|
||||
public static async Task<WebJobsAuthenticationEventResponse> Run(
|
||||
[WebJobsAuthenticationEventsTrigger] WebJobsOnOtpSendRequest request,
|
||||
ILogger log)
|
||||
{
|
||||
var response = new WebJobsOnOtpSendResponse();
|
||||
|
||||
string? phoneNumber = request.Data?.OtpContext?.Identifier;
|
||||
string? otp = request.Data?.OtpContext?.OneTimeCode;
|
||||
|
||||
if (string.IsNullOrEmpty(phoneNumber) || string.IsNullOrEmpty(otp))
|
||||
{
|
||||
log.LogError("Missing phone number or OTP");
|
||||
response.Actions.Add(new WebJobsOnOtpSendFailed
|
||||
{
|
||||
Error = "Missing required data"
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Send OTP via your SMS provider
|
||||
await SendSmsAsync(phoneNumber, $"Your verification code is: {otp}");
|
||||
|
||||
response.Actions.Add(new WebJobsOnOtpSendSuccess());
|
||||
log.LogInformation("OTP sent successfully to {PhoneNumber}", phoneNumber);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.LogError(ex, "Failed to send OTP");
|
||||
response.Actions.Add(new WebJobsOnOtpSendFailed
|
||||
{
|
||||
Error = "Failed to send verification code"
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static async Task SendSmsAsync(string phoneNumber, string message)
|
||||
{
|
||||
// Implement your SMS provider integration (Twilio, Azure Communication Services, etc.)
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Function App Configuration
|
||||
|
||||
Configure the Function App for authentication events.
|
||||
|
||||
```csharp
|
||||
// Program.cs (Isolated worker model)
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
var host = new HostBuilder()
|
||||
.ConfigureFunctionsWorkerDefaults()
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
```
|
||||
|
||||
```json
|
||||
// host.json
|
||||
{
|
||||
"version": "2.0",
|
||||
"logging": {
|
||||
"applicationInsights": {
|
||||
"samplingSettings": {
|
||||
"isEnabled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
"http": {
|
||||
"routePrefix": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
// local.settings.json
|
||||
{
|
||||
"IsEncrypted": false,
|
||||
"Values": {
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `WebJobsAuthenticationEventsTriggerAttribute` | Function trigger attribute |
|
||||
| `WebJobsTokenIssuanceStartRequest` | Token issuance event request |
|
||||
| `WebJobsTokenIssuanceStartResponse` | Token issuance event response |
|
||||
| `WebJobsProvideClaimsForToken` | Action to add claims |
|
||||
| `WebJobsAttributeCollectionStartRequest` | Attribute collection start request |
|
||||
| `WebJobsAttributeCollectionStartResponse` | Attribute collection start response |
|
||||
| `WebJobsAttributeCollectionSubmitRequest` | Attribute submission request |
|
||||
| `WebJobsAttributeCollectionSubmitResponse` | Attribute submission response |
|
||||
| `WebJobsSetPrefillValues` | Prefill form values |
|
||||
| `WebJobsShowBlockPage` | Block user with message |
|
||||
| `WebJobsShowValidationError` | Show validation errors |
|
||||
| `WebJobsModifyAttributeValues` | Modify submitted values |
|
||||
| `WebJobsOnOtpSendRequest` | OTP send event request |
|
||||
| `WebJobsOnOtpSendResponse` | OTP send event response |
|
||||
| `WebJobsOnOtpSendSuccess` | OTP sent successfully |
|
||||
| `WebJobsOnOtpSendFailed` | OTP send failed |
|
||||
| `WebJobsContinueWithDefaultBehavior` | Continue with default flow |
|
||||
|
||||
## Entra ID Configuration
|
||||
|
||||
After deploying your Function App, configure the custom extension in Entra ID:
|
||||
|
||||
1. **Register the API** in Entra ID → App registrations
|
||||
2. **Create Custom Authentication Extension** in Entra ID → External Identities → Custom authentication extensions
|
||||
3. **Link to User Flow** in Entra ID → External Identities → User flows
|
||||
|
||||
### Required App Registration Settings
|
||||
|
||||
```
|
||||
Expose an API:
|
||||
- Application ID URI: api://<your-function-app-name>.azurewebsites.net
|
||||
- Scope: CustomAuthenticationExtension.Receive.Payload
|
||||
|
||||
API Permissions:
|
||||
- Microsoft Graph: User.Read (delegated)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Validate all inputs** — Never trust request data; validate before processing
|
||||
2. **Handle errors gracefully** — Return appropriate error responses
|
||||
3. **Log correlation IDs** — Use `CorrelationId` for troubleshooting
|
||||
4. **Keep functions fast** — Authentication events have timeout limits
|
||||
5. **Use managed identity** — Access Azure resources securely
|
||||
6. **Cache external data** — Avoid slow lookups on every request
|
||||
7. **Test locally** — Use Azure Functions Core Tools with sample payloads
|
||||
8. **Monitor with App Insights** — Track function execution and errors
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
[FunctionName("OnTokenIssuanceStart")]
|
||||
public static WebJobsAuthenticationEventResponse Run(
|
||||
[WebJobsAuthenticationEventsTrigger] WebJobsTokenIssuanceStartRequest request,
|
||||
ILogger log)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Your logic here
|
||||
var response = new WebJobsTokenIssuanceStartResponse();
|
||||
response.Actions.Add(new WebJobsProvideClaimsForToken
|
||||
{
|
||||
Claims = new Dictionary<string, string> { { "claim", "value" } }
|
||||
});
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.LogError(ex, "Error processing token issuance event");
|
||||
|
||||
// Return empty response - authentication continues without custom claims
|
||||
// Do NOT throw - this would fail the authentication
|
||||
return new WebJobsTokenIssuanceStartResponse();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents` | Auth events (this SDK) | `dotnet add package Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents` |
|
||||
| `Microsoft.Identity.Web` | Web app authentication | `dotnet add package Microsoft.Identity.Web` |
|
||||
| `Azure.Identity` | Azure authentication | `dotnet add package Azure.Identity` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents |
|
||||
| Custom Extensions Overview | https://learn.microsoft.com/entra/identity-platform/custom-extension-overview |
|
||||
| Token Issuance Events | https://learn.microsoft.com/entra/identity-platform/custom-extension-tokenissuancestart-setup |
|
||||
| Attribute Collection Events | https://learn.microsoft.com/entra/identity-platform/custom-extension-attribute-collection |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/entra/Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents |
|
||||
339
skills/official/microsoft/dotnet/entra/azure-identity/SKILL.md
Normal file
339
skills/official/microsoft/dotnet/entra/azure-identity/SKILL.md
Normal file
@@ -0,0 +1,339 @@
|
||||
---
|
||||
name: azure-identity-dotnet
|
||||
description: |
|
||||
Azure Identity SDK for .NET. Authentication library for Azure SDK clients using Microsoft Entra ID. Use for DefaultAzureCredential, managed identity, service principals, and developer credentials. Triggers: "Azure Identity", "DefaultAzureCredential", "ManagedIdentityCredential", "ClientSecretCredential", "authentication .NET", "Azure auth", "credential chain".
|
||||
package: Azure.Identity
|
||||
---
|
||||
|
||||
# Azure.Identity (.NET)
|
||||
|
||||
Authentication library for Azure SDK clients using Microsoft Entra ID (formerly Azure AD).
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.Identity
|
||||
|
||||
# For ASP.NET Core
|
||||
dotnet add package Microsoft.Extensions.Azure
|
||||
|
||||
# For brokered authentication (Windows)
|
||||
dotnet add package Azure.Identity.Broker
|
||||
```
|
||||
|
||||
**Current Versions**: Stable v1.17.1, Preview v1.18.0-beta.2
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Service Principal with Secret
|
||||
```bash
|
||||
AZURE_CLIENT_ID=<application-client-id>
|
||||
AZURE_TENANT_ID=<directory-tenant-id>
|
||||
AZURE_CLIENT_SECRET=<client-secret-value>
|
||||
```
|
||||
|
||||
### Service Principal with Certificate
|
||||
```bash
|
||||
AZURE_CLIENT_ID=<application-client-id>
|
||||
AZURE_TENANT_ID=<directory-tenant-id>
|
||||
AZURE_CLIENT_CERTIFICATE_PATH=<path-to-pfx-or-pem>
|
||||
AZURE_CLIENT_CERTIFICATE_PASSWORD=<certificate-password> # Optional
|
||||
```
|
||||
|
||||
### Managed Identity
|
||||
```bash
|
||||
AZURE_CLIENT_ID=<user-assigned-managed-identity-client-id> # Only for user-assigned
|
||||
```
|
||||
|
||||
## DefaultAzureCredential
|
||||
|
||||
The recommended credential for most scenarios. Tries multiple authentication methods in order:
|
||||
|
||||
| Order | Credential | Enabled by Default |
|
||||
|-------|------------|-------------------|
|
||||
| 1 | EnvironmentCredential | Yes |
|
||||
| 2 | WorkloadIdentityCredential | Yes |
|
||||
| 3 | ManagedIdentityCredential | Yes |
|
||||
| 4 | VisualStudioCredential | Yes |
|
||||
| 5 | VisualStudioCodeCredential | Yes |
|
||||
| 6 | AzureCliCredential | Yes |
|
||||
| 7 | AzurePowerShellCredential | Yes |
|
||||
| 8 | AzureDeveloperCliCredential | Yes |
|
||||
| 9 | InteractiveBrowserCredential | **No** |
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Storage.Blobs;
|
||||
|
||||
var credential = new DefaultAzureCredential();
|
||||
var blobClient = new BlobServiceClient(
|
||||
new Uri("https://myaccount.blob.core.windows.net"),
|
||||
credential);
|
||||
```
|
||||
|
||||
### ASP.NET Core with Dependency Injection
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Microsoft.Extensions.Azure;
|
||||
|
||||
builder.Services.AddAzureClients(clientBuilder =>
|
||||
{
|
||||
clientBuilder.AddBlobServiceClient(
|
||||
new Uri("https://myaccount.blob.core.windows.net"));
|
||||
clientBuilder.AddSecretClient(
|
||||
new Uri("https://myvault.vault.azure.net"));
|
||||
|
||||
// Uses DefaultAzureCredential by default
|
||||
clientBuilder.UseCredential(new DefaultAzureCredential());
|
||||
});
|
||||
```
|
||||
|
||||
### Customizing DefaultAzureCredential
|
||||
|
||||
```csharp
|
||||
var credential = new DefaultAzureCredential(
|
||||
new DefaultAzureCredentialOptions
|
||||
{
|
||||
ExcludeEnvironmentCredential = true,
|
||||
ExcludeManagedIdentityCredential = false,
|
||||
ExcludeVisualStudioCredential = false,
|
||||
ExcludeAzureCliCredential = false,
|
||||
ExcludeInteractiveBrowserCredential = false, // Enable interactive
|
||||
TenantId = "<tenant-id>",
|
||||
ManagedIdentityClientId = "<user-assigned-mi-client-id>"
|
||||
});
|
||||
```
|
||||
|
||||
## Credential Types
|
||||
|
||||
### ManagedIdentityCredential (Production)
|
||||
|
||||
```csharp
|
||||
// System-assigned managed identity
|
||||
var credential = new ManagedIdentityCredential(ManagedIdentityId.SystemAssigned);
|
||||
|
||||
// User-assigned by client ID
|
||||
var credential = new ManagedIdentityCredential(
|
||||
ManagedIdentityId.FromUserAssignedClientId("<client-id>"));
|
||||
|
||||
// User-assigned by resource ID
|
||||
var credential = new ManagedIdentityCredential(
|
||||
ManagedIdentityId.FromUserAssignedResourceId("<resource-id>"));
|
||||
```
|
||||
|
||||
### ClientSecretCredential
|
||||
|
||||
```csharp
|
||||
var credential = new ClientSecretCredential(
|
||||
tenantId: "<tenant-id>",
|
||||
clientId: "<client-id>",
|
||||
clientSecret: "<client-secret>");
|
||||
|
||||
var client = new SecretClient(
|
||||
new Uri("https://myvault.vault.azure.net"),
|
||||
credential);
|
||||
```
|
||||
|
||||
### ClientCertificateCredential
|
||||
|
||||
```csharp
|
||||
var certificate = X509CertificateLoader.LoadCertificateFromFile("MyCertificate.pfx");
|
||||
var credential = new ClientCertificateCredential(
|
||||
tenantId: "<tenant-id>",
|
||||
clientId: "<client-id>",
|
||||
certificate);
|
||||
```
|
||||
|
||||
### ChainedTokenCredential (Custom Chain)
|
||||
|
||||
```csharp
|
||||
var credential = new ChainedTokenCredential(
|
||||
new ManagedIdentityCredential(),
|
||||
new AzureCliCredential());
|
||||
|
||||
var client = new SecretClient(
|
||||
new Uri("https://myvault.vault.azure.net"),
|
||||
credential);
|
||||
```
|
||||
|
||||
### Developer Credentials
|
||||
|
||||
```csharp
|
||||
// Azure CLI
|
||||
var credential = new AzureCliCredential();
|
||||
|
||||
// Azure PowerShell
|
||||
var credential = new AzurePowerShellCredential();
|
||||
|
||||
// Azure Developer CLI (azd)
|
||||
var credential = new AzureDeveloperCliCredential();
|
||||
|
||||
// Visual Studio
|
||||
var credential = new VisualStudioCredential();
|
||||
|
||||
// Interactive Browser
|
||||
var credential = new InteractiveBrowserCredential();
|
||||
```
|
||||
|
||||
## Environment-Based Configuration
|
||||
|
||||
```csharp
|
||||
// Production vs Development
|
||||
TokenCredential credential = builder.Environment.IsProduction()
|
||||
? new ManagedIdentityCredential("<client-id>")
|
||||
: new DefaultAzureCredential();
|
||||
```
|
||||
|
||||
## Sovereign Clouds
|
||||
|
||||
```csharp
|
||||
var credential = new DefaultAzureCredential(
|
||||
new DefaultAzureCredentialOptions
|
||||
{
|
||||
AuthorityHost = AzureAuthorityHosts.AzureGovernment
|
||||
});
|
||||
|
||||
// Available authority hosts:
|
||||
// AzureAuthorityHosts.AzurePublicCloud (default)
|
||||
// AzureAuthorityHosts.AzureGovernment
|
||||
// AzureAuthorityHosts.AzureChina
|
||||
// AzureAuthorityHosts.AzureGermany
|
||||
```
|
||||
|
||||
## Credential Types Reference
|
||||
|
||||
| Category | Credential | Purpose |
|
||||
|----------|------------|---------|
|
||||
| **Chains** | `DefaultAzureCredential` | Preconfigured chain for dev-to-prod |
|
||||
| | `ChainedTokenCredential` | Custom credential chain |
|
||||
| **Azure-Hosted** | `ManagedIdentityCredential` | Azure managed identity |
|
||||
| | `WorkloadIdentityCredential` | Kubernetes workload identity |
|
||||
| | `EnvironmentCredential` | Environment variables |
|
||||
| **Service Principal** | `ClientSecretCredential` | Client ID + secret |
|
||||
| | `ClientCertificateCredential` | Client ID + certificate |
|
||||
| | `ClientAssertionCredential` | Signed client assertion |
|
||||
| **User** | `InteractiveBrowserCredential` | Browser-based auth |
|
||||
| | `DeviceCodeCredential` | Device code flow |
|
||||
| | `OnBehalfOfCredential` | Delegated identity |
|
||||
| **Developer** | `AzureCliCredential` | Azure CLI |
|
||||
| | `AzurePowerShellCredential` | Azure PowerShell |
|
||||
| | `AzureDeveloperCliCredential` | Azure Developer CLI |
|
||||
| | `VisualStudioCredential` | Visual Studio |
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Use Deterministic Credentials in Production
|
||||
|
||||
```csharp
|
||||
// Development
|
||||
var devCredential = new DefaultAzureCredential();
|
||||
|
||||
// Production - use specific credential
|
||||
var prodCredential = new ManagedIdentityCredential("<client-id>");
|
||||
```
|
||||
|
||||
### 2. Reuse Credential Instances
|
||||
|
||||
```csharp
|
||||
// Good: Single credential instance shared across clients
|
||||
var credential = new DefaultAzureCredential();
|
||||
var blobClient = new BlobServiceClient(blobUri, credential);
|
||||
var secretClient = new SecretClient(vaultUri, credential);
|
||||
```
|
||||
|
||||
### 3. Configure Retry Policies
|
||||
|
||||
```csharp
|
||||
var options = new ManagedIdentityCredentialOptions(
|
||||
ManagedIdentityId.FromUserAssignedClientId(clientId))
|
||||
{
|
||||
Retry =
|
||||
{
|
||||
MaxRetries = 3,
|
||||
Delay = TimeSpan.FromSeconds(0.5),
|
||||
}
|
||||
};
|
||||
var credential = new ManagedIdentityCredential(options);
|
||||
```
|
||||
|
||||
### 4. Enable Logging for Debugging
|
||||
|
||||
```csharp
|
||||
using Azure.Core.Diagnostics;
|
||||
|
||||
using AzureEventSourceListener listener = new((args, message) =>
|
||||
{
|
||||
if (args is { EventSource.Name: "Azure-Identity" })
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
}, EventLevel.LogAlways);
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Security.KeyVault.Secrets;
|
||||
|
||||
var client = new SecretClient(
|
||||
new Uri("https://myvault.vault.azure.net"),
|
||||
new DefaultAzureCredential());
|
||||
|
||||
try
|
||||
{
|
||||
KeyVaultSecret secret = await client.GetSecretAsync("secret1");
|
||||
}
|
||||
catch (AuthenticationFailedException e)
|
||||
{
|
||||
Console.WriteLine($"Authentication Failed: {e.Message}");
|
||||
}
|
||||
catch (CredentialUnavailableException e)
|
||||
{
|
||||
Console.WriteLine($"Credential Unavailable: {e.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Key Exceptions
|
||||
|
||||
| Exception | Description |
|
||||
|-----------|-------------|
|
||||
| `AuthenticationFailedException` | Base exception for authentication errors |
|
||||
| `CredentialUnavailableException` | Credential cannot authenticate in current environment |
|
||||
| `AuthenticationRequiredException` | Interactive authentication is required |
|
||||
|
||||
## Managed Identity Support
|
||||
|
||||
Supported Azure services:
|
||||
- Azure App Service and Azure Functions
|
||||
- Azure Arc
|
||||
- Azure Cloud Shell
|
||||
- Azure Kubernetes Service (AKS)
|
||||
- Azure Service Fabric
|
||||
- Azure Virtual Machines
|
||||
- Azure Virtual Machine Scale Sets
|
||||
|
||||
## Thread Safety
|
||||
|
||||
All credential implementations are thread-safe. A single credential instance can be safely shared across multiple clients and threads.
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.Identity` | Authentication (this SDK) | `dotnet add package Azure.Identity` |
|
||||
| `Microsoft.Extensions.Azure` | DI integration | `dotnet add package Microsoft.Extensions.Azure` |
|
||||
| `Azure.Identity.Broker` | Brokered auth (Windows) | `dotnet add package Azure.Identity.Broker` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.Identity |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.identity |
|
||||
| Credential Chains | https://learn.microsoft.com/dotnet/azure/sdk/authentication/credential-chains |
|
||||
| Best Practices | https://learn.microsoft.com/dotnet/azure/sdk/authentication/best-practices |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/identity/Azure.Identity |
|
||||
406
skills/official/microsoft/dotnet/entra/keyvault/SKILL.md
Normal file
406
skills/official/microsoft/dotnet/entra/keyvault/SKILL.md
Normal file
@@ -0,0 +1,406 @@
|
||||
---
|
||||
name: azure-security-keyvault-keys-dotnet
|
||||
description: |
|
||||
Azure Key Vault Keys SDK for .NET. Client library for managing cryptographic keys in Azure Key Vault and Managed HSM. Use for key creation, rotation, encryption, decryption, signing, and verification. Triggers: "Key Vault keys", "KeyClient", "CryptographyClient", "RSA key", "EC key", "encrypt decrypt .NET", "key rotation", "HSM".
|
||||
package: Azure.Security.KeyVault.Keys
|
||||
---
|
||||
|
||||
# Azure.Security.KeyVault.Keys (.NET)
|
||||
|
||||
Client library for managing cryptographic keys in Azure Key Vault and Managed HSM.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.Security.KeyVault.Keys
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Version**: 4.7.0 (stable)
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
KEY_VAULT_NAME=<your-key-vault-name>
|
||||
# Or full URI
|
||||
AZURE_KEYVAULT_URL=https://<vault-name>.vault.azure.net
|
||||
```
|
||||
|
||||
## Client Hierarchy
|
||||
|
||||
```
|
||||
KeyClient (key management)
|
||||
├── CreateKey / CreateRsaKey / CreateEcKey
|
||||
├── GetKey / GetKeys
|
||||
├── UpdateKeyProperties
|
||||
├── DeleteKey / PurgeDeletedKey
|
||||
├── BackupKey / RestoreKey
|
||||
└── GetCryptographyClient() → CryptographyClient
|
||||
|
||||
CryptographyClient (cryptographic operations)
|
||||
├── Encrypt / Decrypt
|
||||
├── WrapKey / UnwrapKey
|
||||
├── Sign / Verify
|
||||
└── SignData / VerifyData
|
||||
|
||||
KeyResolver (key resolution)
|
||||
└── Resolve(keyId) → CryptographyClient
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### DefaultAzureCredential (Recommended)
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Security.KeyVault.Keys;
|
||||
|
||||
var keyVaultName = Environment.GetEnvironmentVariable("KEY_VAULT_NAME");
|
||||
var kvUri = $"https://{keyVaultName}.vault.azure.net";
|
||||
|
||||
var client = new KeyClient(new Uri(kvUri), new DefaultAzureCredential());
|
||||
```
|
||||
|
||||
### Service Principal
|
||||
|
||||
```csharp
|
||||
var credential = new ClientSecretCredential(
|
||||
tenantId: "<tenant-id>",
|
||||
clientId: "<client-id>",
|
||||
clientSecret: "<client-secret>");
|
||||
|
||||
var client = new KeyClient(new Uri(kvUri), credential);
|
||||
```
|
||||
|
||||
## Key Management
|
||||
|
||||
### Create Keys
|
||||
|
||||
```csharp
|
||||
// Create RSA key
|
||||
KeyVaultKey rsaKey = await client.CreateKeyAsync("my-rsa-key", KeyType.Rsa);
|
||||
Console.WriteLine($"Created key: {rsaKey.Name}, Type: {rsaKey.KeyType}");
|
||||
|
||||
// Create RSA key with options
|
||||
var rsaOptions = new CreateRsaKeyOptions("my-rsa-key-2048")
|
||||
{
|
||||
KeySize = 2048,
|
||||
HardwareProtected = false, // true for HSM-backed
|
||||
ExpiresOn = DateTimeOffset.UtcNow.AddYears(1),
|
||||
NotBefore = DateTimeOffset.UtcNow,
|
||||
Enabled = true
|
||||
};
|
||||
rsaOptions.KeyOperations.Add(KeyOperation.Encrypt);
|
||||
rsaOptions.KeyOperations.Add(KeyOperation.Decrypt);
|
||||
|
||||
KeyVaultKey rsaKey2 = await client.CreateRsaKeyAsync(rsaOptions);
|
||||
|
||||
// Create EC key
|
||||
var ecOptions = new CreateEcKeyOptions("my-ec-key")
|
||||
{
|
||||
CurveName = KeyCurveName.P256,
|
||||
HardwareProtected = true // HSM-backed
|
||||
};
|
||||
KeyVaultKey ecKey = await client.CreateEcKeyAsync(ecOptions);
|
||||
|
||||
// Create Oct (symmetric) key for wrap/unwrap
|
||||
var octOptions = new CreateOctKeyOptions("my-oct-key")
|
||||
{
|
||||
KeySize = 256,
|
||||
HardwareProtected = true
|
||||
};
|
||||
KeyVaultKey octKey = await client.CreateOctKeyAsync(octOptions);
|
||||
```
|
||||
|
||||
### Retrieve Keys
|
||||
|
||||
```csharp
|
||||
// Get specific key (latest version)
|
||||
KeyVaultKey key = await client.GetKeyAsync("my-rsa-key");
|
||||
Console.WriteLine($"Key ID: {key.Id}");
|
||||
Console.WriteLine($"Key Type: {key.KeyType}");
|
||||
Console.WriteLine($"Version: {key.Properties.Version}");
|
||||
|
||||
// Get specific version
|
||||
KeyVaultKey keyVersion = await client.GetKeyAsync("my-rsa-key", "version-id");
|
||||
|
||||
// List all keys
|
||||
await foreach (KeyProperties keyProps in client.GetPropertiesOfKeysAsync())
|
||||
{
|
||||
Console.WriteLine($"Key: {keyProps.Name}, Enabled: {keyProps.Enabled}");
|
||||
}
|
||||
|
||||
// List key versions
|
||||
await foreach (KeyProperties version in client.GetPropertiesOfKeyVersionsAsync("my-rsa-key"))
|
||||
{
|
||||
Console.WriteLine($"Version: {version.Version}, Created: {version.CreatedOn}");
|
||||
}
|
||||
```
|
||||
|
||||
### Update Key Properties
|
||||
|
||||
```csharp
|
||||
KeyVaultKey key = await client.GetKeyAsync("my-rsa-key");
|
||||
|
||||
key.Properties.ExpiresOn = DateTimeOffset.UtcNow.AddYears(2);
|
||||
key.Properties.Tags["environment"] = "production";
|
||||
|
||||
KeyVaultKey updatedKey = await client.UpdateKeyPropertiesAsync(key.Properties);
|
||||
```
|
||||
|
||||
### Delete and Purge Keys
|
||||
|
||||
```csharp
|
||||
// Start delete operation
|
||||
DeleteKeyOperation operation = await client.StartDeleteKeyAsync("my-rsa-key");
|
||||
|
||||
// Wait for deletion to complete (required before purge)
|
||||
await operation.WaitForCompletionAsync();
|
||||
Console.WriteLine($"Deleted key scheduled purge date: {operation.Value.ScheduledPurgeDate}");
|
||||
|
||||
// Purge immediately (if soft-delete is enabled)
|
||||
await client.PurgeDeletedKeyAsync("my-rsa-key");
|
||||
|
||||
// Or recover deleted key
|
||||
KeyVaultKey recoveredKey = await client.StartRecoverDeletedKeyAsync("my-rsa-key");
|
||||
```
|
||||
|
||||
### Backup and Restore
|
||||
|
||||
```csharp
|
||||
// Backup key
|
||||
byte[] backup = await client.BackupKeyAsync("my-rsa-key");
|
||||
await File.WriteAllBytesAsync("key-backup.bin", backup);
|
||||
|
||||
// Restore key
|
||||
byte[] backupData = await File.ReadAllBytesAsync("key-backup.bin");
|
||||
KeyVaultKey restoredKey = await client.RestoreKeyBackupAsync(backupData);
|
||||
```
|
||||
|
||||
## Cryptographic Operations
|
||||
|
||||
### Get CryptographyClient
|
||||
|
||||
```csharp
|
||||
// From KeyClient
|
||||
KeyVaultKey key = await client.GetKeyAsync("my-rsa-key");
|
||||
CryptographyClient cryptoClient = client.GetCryptographyClient(
|
||||
key.Name,
|
||||
key.Properties.Version);
|
||||
|
||||
// Or create directly with key ID
|
||||
CryptographyClient cryptoClient = new CryptographyClient(
|
||||
new Uri("https://myvault.vault.azure.net/keys/my-rsa-key/version"),
|
||||
new DefaultAzureCredential());
|
||||
```
|
||||
|
||||
### Encrypt and Decrypt
|
||||
|
||||
```csharp
|
||||
byte[] plaintext = Encoding.UTF8.GetBytes("Secret message to encrypt");
|
||||
|
||||
// Encrypt
|
||||
EncryptResult encryptResult = await cryptoClient.EncryptAsync(
|
||||
EncryptionAlgorithm.RsaOaep256,
|
||||
plaintext);
|
||||
Console.WriteLine($"Encrypted: {Convert.ToBase64String(encryptResult.Ciphertext)}");
|
||||
|
||||
// Decrypt
|
||||
DecryptResult decryptResult = await cryptoClient.DecryptAsync(
|
||||
EncryptionAlgorithm.RsaOaep256,
|
||||
encryptResult.Ciphertext);
|
||||
string decrypted = Encoding.UTF8.GetString(decryptResult.Plaintext);
|
||||
Console.WriteLine($"Decrypted: {decrypted}");
|
||||
```
|
||||
|
||||
### Wrap and Unwrap Keys
|
||||
|
||||
```csharp
|
||||
// Key to wrap (e.g., AES key)
|
||||
byte[] keyToWrap = new byte[32]; // 256-bit key
|
||||
RandomNumberGenerator.Fill(keyToWrap);
|
||||
|
||||
// Wrap key
|
||||
WrapResult wrapResult = await cryptoClient.WrapKeyAsync(
|
||||
KeyWrapAlgorithm.RsaOaep256,
|
||||
keyToWrap);
|
||||
|
||||
// Unwrap key
|
||||
UnwrapResult unwrapResult = await cryptoClient.UnwrapKeyAsync(
|
||||
KeyWrapAlgorithm.RsaOaep256,
|
||||
wrapResult.EncryptedKey);
|
||||
```
|
||||
|
||||
### Sign and Verify
|
||||
|
||||
```csharp
|
||||
// Data to sign
|
||||
byte[] data = Encoding.UTF8.GetBytes("Data to sign");
|
||||
|
||||
// Sign data (computes hash internally)
|
||||
SignResult signResult = await cryptoClient.SignDataAsync(
|
||||
SignatureAlgorithm.RS256,
|
||||
data);
|
||||
|
||||
// Verify signature
|
||||
VerifyResult verifyResult = await cryptoClient.VerifyDataAsync(
|
||||
SignatureAlgorithm.RS256,
|
||||
data,
|
||||
signResult.Signature);
|
||||
Console.WriteLine($"Signature valid: {verifyResult.IsValid}");
|
||||
|
||||
// Or sign pre-computed hash
|
||||
using var sha256 = SHA256.Create();
|
||||
byte[] hash = sha256.ComputeHash(data);
|
||||
|
||||
SignResult signHashResult = await cryptoClient.SignAsync(
|
||||
SignatureAlgorithm.RS256,
|
||||
hash);
|
||||
```
|
||||
|
||||
## Key Resolver
|
||||
|
||||
```csharp
|
||||
using Azure.Security.KeyVault.Keys.Cryptography;
|
||||
|
||||
var resolver = new KeyResolver(new DefaultAzureCredential());
|
||||
|
||||
// Resolve key by ID to get CryptographyClient
|
||||
CryptographyClient cryptoClient = await resolver.ResolveAsync(
|
||||
new Uri("https://myvault.vault.azure.net/keys/my-key/version"));
|
||||
|
||||
// Use for encryption
|
||||
EncryptResult result = await cryptoClient.EncryptAsync(
|
||||
EncryptionAlgorithm.RsaOaep256,
|
||||
plaintext);
|
||||
```
|
||||
|
||||
## Key Rotation
|
||||
|
||||
```csharp
|
||||
// Rotate key (creates new version)
|
||||
KeyVaultKey rotatedKey = await client.RotateKeyAsync("my-rsa-key");
|
||||
Console.WriteLine($"New version: {rotatedKey.Properties.Version}");
|
||||
|
||||
// Get rotation policy
|
||||
KeyRotationPolicy policy = await client.GetKeyRotationPolicyAsync("my-rsa-key");
|
||||
|
||||
// Update rotation policy
|
||||
policy.ExpiresIn = "P90D"; // 90 days
|
||||
policy.LifetimeActions.Add(new KeyRotationLifetimeAction
|
||||
{
|
||||
Action = KeyRotationPolicyAction.Rotate,
|
||||
TimeBeforeExpiry = "P30D" // Rotate 30 days before expiry
|
||||
});
|
||||
|
||||
await client.UpdateKeyRotationPolicyAsync("my-rsa-key", policy);
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `KeyClient` | Key management operations |
|
||||
| `CryptographyClient` | Cryptographic operations |
|
||||
| `KeyResolver` | Resolve key ID to CryptographyClient |
|
||||
| `KeyVaultKey` | Key with cryptographic material |
|
||||
| `KeyProperties` | Key metadata (no crypto material) |
|
||||
| `CreateRsaKeyOptions` | RSA key creation options |
|
||||
| `CreateEcKeyOptions` | EC key creation options |
|
||||
| `CreateOctKeyOptions` | Symmetric key options |
|
||||
| `EncryptResult` | Encryption result |
|
||||
| `DecryptResult` | Decryption result |
|
||||
| `SignResult` | Signing result |
|
||||
| `VerifyResult` | Verification result |
|
||||
| `WrapResult` | Key wrap result |
|
||||
| `UnwrapResult` | Key unwrap result |
|
||||
|
||||
## Algorithms Reference
|
||||
|
||||
### Encryption Algorithms
|
||||
| Algorithm | Key Type | Description |
|
||||
|-----------|----------|-------------|
|
||||
| `RsaOaep` | RSA | RSA-OAEP |
|
||||
| `RsaOaep256` | RSA | RSA-OAEP-256 |
|
||||
| `Rsa15` | RSA | RSA 1.5 (legacy) |
|
||||
| `A128Gcm` | Oct | AES-128-GCM |
|
||||
| `A256Gcm` | Oct | AES-256-GCM |
|
||||
|
||||
### Signature Algorithms
|
||||
| Algorithm | Key Type | Description |
|
||||
|-----------|----------|-------------|
|
||||
| `RS256` | RSA | RSASSA-PKCS1-v1_5 SHA-256 |
|
||||
| `RS384` | RSA | RSASSA-PKCS1-v1_5 SHA-384 |
|
||||
| `RS512` | RSA | RSASSA-PKCS1-v1_5 SHA-512 |
|
||||
| `PS256` | RSA | RSASSA-PSS SHA-256 |
|
||||
| `ES256` | EC | ECDSA P-256 SHA-256 |
|
||||
| `ES384` | EC | ECDSA P-384 SHA-384 |
|
||||
| `ES512` | EC | ECDSA P-521 SHA-512 |
|
||||
|
||||
### Key Wrap Algorithms
|
||||
| Algorithm | Key Type | Description |
|
||||
|-----------|----------|-------------|
|
||||
| `RsaOaep` | RSA | RSA-OAEP |
|
||||
| `RsaOaep256` | RSA | RSA-OAEP-256 |
|
||||
| `A128KW` | Oct | AES-128 Key Wrap |
|
||||
| `A256KW` | Oct | AES-256 Key Wrap |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Managed Identity** — Prefer `DefaultAzureCredential` over secrets
|
||||
2. **Enable soft-delete** — Protect against accidental deletion
|
||||
3. **Use HSM-backed keys** — Set `HardwareProtected = true` for sensitive keys
|
||||
4. **Implement key rotation** — Use automatic rotation policies
|
||||
5. **Limit key operations** — Only enable required `KeyOperations`
|
||||
6. **Set expiration dates** — Always set `ExpiresOn` for keys
|
||||
7. **Use specific versions** — Pin to versions in production
|
||||
8. **Cache CryptographyClient** — Reuse for multiple operations
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
KeyVaultKey key = await client.GetKeyAsync("my-key");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 404)
|
||||
{
|
||||
Console.WriteLine("Key not found");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 403)
|
||||
{
|
||||
Console.WriteLine("Access denied - check RBAC permissions");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Key Vault error: {ex.Status} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Required RBAC Roles
|
||||
|
||||
| Role | Permissions |
|
||||
|------|-------------|
|
||||
| Key Vault Crypto Officer | Full key management |
|
||||
| Key Vault Crypto User | Use keys for crypto operations |
|
||||
| Key Vault Reader | Read key metadata |
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.Security.KeyVault.Keys` | Keys (this SDK) | `dotnet add package Azure.Security.KeyVault.Keys` |
|
||||
| `Azure.Security.KeyVault.Secrets` | Secrets | `dotnet add package Azure.Security.KeyVault.Secrets` |
|
||||
| `Azure.Security.KeyVault.Certificates` | Certificates | `dotnet add package Azure.Security.KeyVault.Certificates` |
|
||||
| `Azure.Identity` | Authentication | `dotnet add package Azure.Identity` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.Security.KeyVault.Keys |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.security.keyvault.keys |
|
||||
| Quickstart | https://learn.microsoft.com/azure/key-vault/keys/quick-create-net |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/keyvault/Azure.Security.KeyVault.Keys |
|
||||
@@ -0,0 +1,337 @@
|
||||
---
|
||||
name: azure-ai-document-intelligence-dotnet
|
||||
description: |
|
||||
Azure AI Document Intelligence SDK for .NET. Extract text, tables, and structured data from documents using prebuilt and custom models. Use for invoice processing, receipt extraction, ID document analysis, and custom document models. Triggers: "Document Intelligence", "DocumentIntelligenceClient", "form recognizer", "invoice extraction", "receipt OCR", "document analysis .NET".
|
||||
package: Azure.AI.DocumentIntelligence
|
||||
---
|
||||
|
||||
# Azure.AI.DocumentIntelligence (.NET)
|
||||
|
||||
Extract text, tables, and structured data from documents using prebuilt and custom models.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.AI.DocumentIntelligence
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Version**: v1.0.0 (GA)
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
DOCUMENT_INTELLIGENCE_ENDPOINT=https://<resource-name>.cognitiveservices.azure.com/
|
||||
DOCUMENT_INTELLIGENCE_API_KEY=<your-api-key>
|
||||
BLOB_CONTAINER_SAS_URL=https://<storage>.blob.core.windows.net/<container>?<sas-token>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### Microsoft Entra ID (Recommended)
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.AI.DocumentIntelligence;
|
||||
|
||||
string endpoint = Environment.GetEnvironmentVariable("DOCUMENT_INTELLIGENCE_ENDPOINT");
|
||||
var credential = new DefaultAzureCredential();
|
||||
var client = new DocumentIntelligenceClient(new Uri(endpoint), credential);
|
||||
```
|
||||
|
||||
> **Note**: Entra ID requires a **custom subdomain** (e.g., `https://<resource-name>.cognitiveservices.azure.com/`), not a regional endpoint.
|
||||
|
||||
### API Key
|
||||
|
||||
```csharp
|
||||
string endpoint = Environment.GetEnvironmentVariable("DOCUMENT_INTELLIGENCE_ENDPOINT");
|
||||
string apiKey = Environment.GetEnvironmentVariable("DOCUMENT_INTELLIGENCE_API_KEY");
|
||||
var client = new DocumentIntelligenceClient(new Uri(endpoint), new AzureKeyCredential(apiKey));
|
||||
```
|
||||
|
||||
## Client Types
|
||||
|
||||
| Client | Purpose |
|
||||
|--------|---------|
|
||||
| `DocumentIntelligenceClient` | Analyze documents, classify documents |
|
||||
| `DocumentIntelligenceAdministrationClient` | Build/manage custom models and classifiers |
|
||||
|
||||
## Prebuilt Models
|
||||
|
||||
| Model ID | Description |
|
||||
|----------|-------------|
|
||||
| `prebuilt-read` | Extract text, languages, handwriting |
|
||||
| `prebuilt-layout` | Extract text, tables, selection marks, structure |
|
||||
| `prebuilt-invoice` | Extract invoice fields (vendor, items, totals) |
|
||||
| `prebuilt-receipt` | Extract receipt fields (merchant, items, total) |
|
||||
| `prebuilt-idDocument` | Extract ID document fields (name, DOB, address) |
|
||||
| `prebuilt-businessCard` | Extract business card fields |
|
||||
| `prebuilt-tax.us.w2` | Extract W-2 tax form fields |
|
||||
| `prebuilt-healthInsuranceCard.us` | Extract health insurance card fields |
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Analyze Invoice
|
||||
|
||||
```csharp
|
||||
using Azure.AI.DocumentIntelligence;
|
||||
|
||||
Uri invoiceUri = new Uri("https://example.com/invoice.pdf");
|
||||
|
||||
Operation<AnalyzeResult> operation = await client.AnalyzeDocumentAsync(
|
||||
WaitUntil.Completed,
|
||||
"prebuilt-invoice",
|
||||
invoiceUri);
|
||||
|
||||
AnalyzeResult result = operation.Value;
|
||||
|
||||
foreach (AnalyzedDocument document in result.Documents)
|
||||
{
|
||||
if (document.Fields.TryGetValue("VendorName", out DocumentField vendorNameField)
|
||||
&& vendorNameField.FieldType == DocumentFieldType.String)
|
||||
{
|
||||
string vendorName = vendorNameField.ValueString;
|
||||
Console.WriteLine($"Vendor Name: '{vendorName}', confidence: {vendorNameField.Confidence}");
|
||||
}
|
||||
|
||||
if (document.Fields.TryGetValue("InvoiceTotal", out DocumentField invoiceTotalField)
|
||||
&& invoiceTotalField.FieldType == DocumentFieldType.Currency)
|
||||
{
|
||||
CurrencyValue invoiceTotal = invoiceTotalField.ValueCurrency;
|
||||
Console.WriteLine($"Invoice Total: '{invoiceTotal.CurrencySymbol}{invoiceTotal.Amount}'");
|
||||
}
|
||||
|
||||
// Extract line items
|
||||
if (document.Fields.TryGetValue("Items", out DocumentField itemsField)
|
||||
&& itemsField.FieldType == DocumentFieldType.List)
|
||||
{
|
||||
foreach (DocumentField item in itemsField.ValueList)
|
||||
{
|
||||
var itemFields = item.ValueDictionary;
|
||||
if (itemFields.TryGetValue("Description", out DocumentField descField))
|
||||
Console.WriteLine($" Item: {descField.ValueString}");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Extract Layout (Text, Tables, Structure)
|
||||
|
||||
```csharp
|
||||
Uri fileUri = new Uri("https://example.com/document.pdf");
|
||||
|
||||
Operation<AnalyzeResult> operation = await client.AnalyzeDocumentAsync(
|
||||
WaitUntil.Completed,
|
||||
"prebuilt-layout",
|
||||
fileUri);
|
||||
|
||||
AnalyzeResult result = operation.Value;
|
||||
|
||||
// Extract text by page
|
||||
foreach (DocumentPage page in result.Pages)
|
||||
{
|
||||
Console.WriteLine($"Page {page.PageNumber}: {page.Lines.Count} lines, {page.Words.Count} words");
|
||||
|
||||
foreach (DocumentLine line in page.Lines)
|
||||
{
|
||||
Console.WriteLine($" Line: '{line.Content}'");
|
||||
}
|
||||
}
|
||||
|
||||
// Extract tables
|
||||
foreach (DocumentTable table in result.Tables)
|
||||
{
|
||||
Console.WriteLine($"Table: {table.RowCount} rows x {table.ColumnCount} columns");
|
||||
foreach (DocumentTableCell cell in table.Cells)
|
||||
{
|
||||
Console.WriteLine($" Cell ({cell.RowIndex}, {cell.ColumnIndex}): {cell.Content}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Analyze Receipt
|
||||
|
||||
```csharp
|
||||
Operation<AnalyzeResult> operation = await client.AnalyzeDocumentAsync(
|
||||
WaitUntil.Completed,
|
||||
"prebuilt-receipt",
|
||||
receiptUri);
|
||||
|
||||
AnalyzeResult result = operation.Value;
|
||||
|
||||
foreach (AnalyzedDocument document in result.Documents)
|
||||
{
|
||||
if (document.Fields.TryGetValue("MerchantName", out DocumentField merchantField))
|
||||
Console.WriteLine($"Merchant: {merchantField.ValueString}");
|
||||
|
||||
if (document.Fields.TryGetValue("Total", out DocumentField totalField))
|
||||
Console.WriteLine($"Total: {totalField.ValueCurrency.Amount}");
|
||||
|
||||
if (document.Fields.TryGetValue("TransactionDate", out DocumentField dateField))
|
||||
Console.WriteLine($"Date: {dateField.ValueDate}");
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Build Custom Model
|
||||
|
||||
```csharp
|
||||
var adminClient = new DocumentIntelligenceAdministrationClient(
|
||||
new Uri(endpoint),
|
||||
new AzureKeyCredential(apiKey));
|
||||
|
||||
string modelId = "my-custom-model";
|
||||
Uri blobContainerUri = new Uri("<blob-container-sas-url>");
|
||||
|
||||
var blobSource = new BlobContentSource(blobContainerUri);
|
||||
var options = new BuildDocumentModelOptions(modelId, DocumentBuildMode.Template, blobSource);
|
||||
|
||||
Operation<DocumentModelDetails> operation = await adminClient.BuildDocumentModelAsync(
|
||||
WaitUntil.Completed,
|
||||
options);
|
||||
|
||||
DocumentModelDetails model = operation.Value;
|
||||
|
||||
Console.WriteLine($"Model ID: {model.ModelId}");
|
||||
Console.WriteLine($"Created: {model.CreatedOn}");
|
||||
|
||||
foreach (var docType in model.DocumentTypes)
|
||||
{
|
||||
Console.WriteLine($"Document type: {docType.Key}");
|
||||
foreach (var field in docType.Value.FieldSchema)
|
||||
{
|
||||
Console.WriteLine($" Field: {field.Key}, Confidence: {docType.Value.FieldConfidence[field.Key]}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Build Document Classifier
|
||||
|
||||
```csharp
|
||||
string classifierId = "my-classifier";
|
||||
Uri blobContainerUri = new Uri("<blob-container-sas-url>");
|
||||
|
||||
var sourceA = new BlobContentSource(blobContainerUri) { Prefix = "TypeA/train" };
|
||||
var sourceB = new BlobContentSource(blobContainerUri) { Prefix = "TypeB/train" };
|
||||
|
||||
var docTypes = new Dictionary<string, ClassifierDocumentTypeDetails>()
|
||||
{
|
||||
{ "TypeA", new ClassifierDocumentTypeDetails(sourceA) },
|
||||
{ "TypeB", new ClassifierDocumentTypeDetails(sourceB) }
|
||||
};
|
||||
|
||||
var options = new BuildClassifierOptions(classifierId, docTypes);
|
||||
|
||||
Operation<DocumentClassifierDetails> operation = await adminClient.BuildClassifierAsync(
|
||||
WaitUntil.Completed,
|
||||
options);
|
||||
|
||||
DocumentClassifierDetails classifier = operation.Value;
|
||||
Console.WriteLine($"Classifier ID: {classifier.ClassifierId}");
|
||||
```
|
||||
|
||||
### 6. Classify Document
|
||||
|
||||
```csharp
|
||||
string classifierId = "my-classifier";
|
||||
Uri documentUri = new Uri("https://example.com/document.pdf");
|
||||
|
||||
var options = new ClassifyDocumentOptions(classifierId, documentUri);
|
||||
|
||||
Operation<AnalyzeResult> operation = await client.ClassifyDocumentAsync(
|
||||
WaitUntil.Completed,
|
||||
options);
|
||||
|
||||
AnalyzeResult result = operation.Value;
|
||||
|
||||
foreach (AnalyzedDocument document in result.Documents)
|
||||
{
|
||||
Console.WriteLine($"Document type: {document.DocumentType}, confidence: {document.Confidence}");
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Manage Models
|
||||
|
||||
```csharp
|
||||
// Get resource details
|
||||
DocumentIntelligenceResourceDetails resourceDetails = await adminClient.GetResourceDetailsAsync();
|
||||
Console.WriteLine($"Custom models: {resourceDetails.CustomDocumentModels.Count}/{resourceDetails.CustomDocumentModels.Limit}");
|
||||
|
||||
// Get specific model
|
||||
DocumentModelDetails model = await adminClient.GetModelAsync("my-model-id");
|
||||
Console.WriteLine($"Model: {model.ModelId}, Created: {model.CreatedOn}");
|
||||
|
||||
// List models
|
||||
await foreach (DocumentModelDetails modelItem in adminClient.GetModelsAsync())
|
||||
{
|
||||
Console.WriteLine($"Model: {modelItem.ModelId}");
|
||||
}
|
||||
|
||||
// Delete model
|
||||
await adminClient.DeleteModelAsync("my-model-id");
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `DocumentIntelligenceClient` | Main client for analysis |
|
||||
| `DocumentIntelligenceAdministrationClient` | Model management |
|
||||
| `AnalyzeResult` | Result of document analysis |
|
||||
| `AnalyzedDocument` | Single document within result |
|
||||
| `DocumentField` | Extracted field with value and confidence |
|
||||
| `DocumentFieldType` | String, Date, Number, Currency, etc. |
|
||||
| `DocumentPage` | Page info (lines, words, selection marks) |
|
||||
| `DocumentTable` | Extracted table with cells |
|
||||
| `DocumentModelDetails` | Custom model metadata |
|
||||
| `BlobContentSource` | Training data source |
|
||||
|
||||
## Build Modes
|
||||
|
||||
| Mode | Use Case |
|
||||
|------|----------|
|
||||
| `DocumentBuildMode.Template` | Fixed layout documents (forms) |
|
||||
| `DocumentBuildMode.Neural` | Variable layout documents |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use DefaultAzureCredential** for production
|
||||
2. **Reuse client instances** — clients are thread-safe
|
||||
3. **Handle long-running operations** — Use `WaitUntil.Completed` for simplicity
|
||||
4. **Check field confidence** — Always verify `Confidence` property
|
||||
5. **Use appropriate model** — Prebuilt for common docs, custom for specialized
|
||||
6. **Use custom subdomain** — Required for Entra ID authentication
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
var operation = await client.AnalyzeDocumentAsync(
|
||||
WaitUntil.Completed,
|
||||
"prebuilt-invoice",
|
||||
documentUri);
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Status} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.AI.DocumentIntelligence` | Document analysis (this SDK) | `dotnet add package Azure.AI.DocumentIntelligence` |
|
||||
| `Azure.AI.FormRecognizer` | Legacy SDK (deprecated) | Use DocumentIntelligence instead |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.AI.DocumentIntelligence |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.ai.documentintelligence |
|
||||
| GitHub Samples | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/documentintelligence/Azure.AI.DocumentIntelligence/samples |
|
||||
| Document Intelligence Studio | https://documentintelligence.ai.azure.com/ |
|
||||
| Prebuilt Models | https://aka.ms/azsdk/formrecognizer/models |
|
||||
455
skills/official/microsoft/dotnet/foundry/openai/SKILL.md
Normal file
455
skills/official/microsoft/dotnet/foundry/openai/SKILL.md
Normal file
@@ -0,0 +1,455 @@
|
||||
---
|
||||
name: azure-ai-openai-dotnet
|
||||
description: |
|
||||
Azure OpenAI SDK for .NET. Client library for Azure OpenAI and OpenAI services. Use for chat completions, embeddings, image generation, audio transcription, and assistants. Triggers: "Azure OpenAI", "AzureOpenAIClient", "ChatClient", "chat completions .NET", "GPT-4", "embeddings", "DALL-E", "Whisper", "OpenAI .NET".
|
||||
package: Azure.AI.OpenAI
|
||||
---
|
||||
|
||||
# Azure.AI.OpenAI (.NET)
|
||||
|
||||
Client library for Azure OpenAI Service providing access to OpenAI models including GPT-4, GPT-4o, embeddings, DALL-E, and Whisper.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.AI.OpenAI
|
||||
|
||||
# For OpenAI (non-Azure) compatibility
|
||||
dotnet add package OpenAI
|
||||
```
|
||||
|
||||
**Current Version**: 2.1.0 (stable)
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_OPENAI_ENDPOINT=https://<resource-name>.openai.azure.com
|
||||
AZURE_OPENAI_API_KEY=<api-key> # For key-based auth
|
||||
AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o-mini # Your deployment name
|
||||
```
|
||||
|
||||
## Client Hierarchy
|
||||
|
||||
```
|
||||
AzureOpenAIClient (top-level)
|
||||
├── GetChatClient(deploymentName) → ChatClient
|
||||
├── GetEmbeddingClient(deploymentName) → EmbeddingClient
|
||||
├── GetImageClient(deploymentName) → ImageClient
|
||||
├── GetAudioClient(deploymentName) → AudioClient
|
||||
└── GetAssistantClient() → AssistantClient
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### API Key Authentication
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
using Azure.AI.OpenAI;
|
||||
|
||||
AzureOpenAIClient client = new(
|
||||
new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!),
|
||||
new AzureKeyCredential(Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY")!));
|
||||
```
|
||||
|
||||
### Microsoft Entra ID (Recommended for Production)
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.AI.OpenAI;
|
||||
|
||||
AzureOpenAIClient client = new(
|
||||
new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!),
|
||||
new DefaultAzureCredential());
|
||||
```
|
||||
|
||||
### Using OpenAI SDK Directly with Azure
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using OpenAI;
|
||||
using OpenAI.Chat;
|
||||
using System.ClientModel.Primitives;
|
||||
|
||||
#pragma warning disable OPENAI001
|
||||
|
||||
BearerTokenPolicy tokenPolicy = new(
|
||||
new DefaultAzureCredential(),
|
||||
"https://cognitiveservices.azure.com/.default");
|
||||
|
||||
ChatClient client = new(
|
||||
model: "gpt-4o-mini",
|
||||
authenticationPolicy: tokenPolicy,
|
||||
options: new OpenAIClientOptions()
|
||||
{
|
||||
Endpoint = new Uri("https://YOUR-RESOURCE.openai.azure.com/openai/v1")
|
||||
});
|
||||
```
|
||||
|
||||
## Chat Completions
|
||||
|
||||
### Basic Chat
|
||||
|
||||
```csharp
|
||||
using Azure.AI.OpenAI;
|
||||
using OpenAI.Chat;
|
||||
|
||||
AzureOpenAIClient azureClient = new(
|
||||
new Uri(endpoint),
|
||||
new DefaultAzureCredential());
|
||||
|
||||
ChatClient chatClient = azureClient.GetChatClient("gpt-4o-mini");
|
||||
|
||||
ChatCompletion completion = chatClient.CompleteChat(
|
||||
[
|
||||
new SystemChatMessage("You are a helpful assistant."),
|
||||
new UserChatMessage("What is Azure OpenAI?")
|
||||
]);
|
||||
|
||||
Console.WriteLine(completion.Content[0].Text);
|
||||
```
|
||||
|
||||
### Async Chat
|
||||
|
||||
```csharp
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(
|
||||
[
|
||||
new SystemChatMessage("You are a helpful assistant."),
|
||||
new UserChatMessage("Explain cloud computing in simple terms.")
|
||||
]);
|
||||
|
||||
Console.WriteLine($"Response: {completion.Content[0].Text}");
|
||||
Console.WriteLine($"Tokens used: {completion.Usage.TotalTokenCount}");
|
||||
```
|
||||
|
||||
### Streaming Chat
|
||||
|
||||
```csharp
|
||||
await foreach (StreamingChatCompletionUpdate update
|
||||
in chatClient.CompleteChatStreamingAsync(messages))
|
||||
{
|
||||
if (update.ContentUpdate.Count > 0)
|
||||
{
|
||||
Console.Write(update.ContentUpdate[0].Text);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Chat with Options
|
||||
|
||||
```csharp
|
||||
ChatCompletionOptions options = new()
|
||||
{
|
||||
MaxOutputTokenCount = 1000,
|
||||
Temperature = 0.7f,
|
||||
TopP = 0.95f,
|
||||
FrequencyPenalty = 0,
|
||||
PresencePenalty = 0
|
||||
};
|
||||
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages, options);
|
||||
```
|
||||
|
||||
### Multi-turn Conversation
|
||||
|
||||
```csharp
|
||||
List<ChatMessage> messages = new()
|
||||
{
|
||||
new SystemChatMessage("You are a helpful assistant."),
|
||||
new UserChatMessage("Hi, can you help me?"),
|
||||
new AssistantChatMessage("Of course! What do you need help with?"),
|
||||
new UserChatMessage("What's the capital of France?")
|
||||
};
|
||||
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
||||
messages.Add(new AssistantChatMessage(completion.Content[0].Text));
|
||||
```
|
||||
|
||||
## Structured Outputs (JSON Schema)
|
||||
|
||||
```csharp
|
||||
using System.Text.Json;
|
||||
|
||||
ChatCompletionOptions options = new()
|
||||
{
|
||||
ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
|
||||
jsonSchemaFormatName: "math_reasoning",
|
||||
jsonSchema: BinaryData.FromBytes("""
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"steps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"explanation": { "type": "string" },
|
||||
"output": { "type": "string" }
|
||||
},
|
||||
"required": ["explanation", "output"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"final_answer": { "type": "string" }
|
||||
},
|
||||
"required": ["steps", "final_answer"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
"""u8.ToArray()),
|
||||
jsonSchemaIsStrict: true)
|
||||
};
|
||||
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(
|
||||
[new UserChatMessage("How can I solve 8x + 7 = -23?")],
|
||||
options);
|
||||
|
||||
using JsonDocument json = JsonDocument.Parse(completion.Content[0].Text);
|
||||
Console.WriteLine($"Answer: {json.RootElement.GetProperty("final_answer")}");
|
||||
```
|
||||
|
||||
## Reasoning Models (o1, o4-mini)
|
||||
|
||||
```csharp
|
||||
ChatCompletionOptions options = new()
|
||||
{
|
||||
ReasoningEffortLevel = ChatReasoningEffortLevel.Low,
|
||||
MaxOutputTokenCount = 100000
|
||||
};
|
||||
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(
|
||||
[
|
||||
new DeveloperChatMessage("You are a helpful assistant"),
|
||||
new UserChatMessage("Explain the theory of relativity")
|
||||
], options);
|
||||
```
|
||||
|
||||
## Azure AI Search Integration (RAG)
|
||||
|
||||
```csharp
|
||||
using Azure.AI.OpenAI.Chat;
|
||||
|
||||
#pragma warning disable AOAI001
|
||||
|
||||
ChatCompletionOptions options = new();
|
||||
options.AddDataSource(new AzureSearchChatDataSource()
|
||||
{
|
||||
Endpoint = new Uri(searchEndpoint),
|
||||
IndexName = searchIndex,
|
||||
Authentication = DataSourceAuthentication.FromApiKey(searchKey)
|
||||
});
|
||||
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(
|
||||
[new UserChatMessage("What health plans are available?")],
|
||||
options);
|
||||
|
||||
ChatMessageContext context = completion.GetMessageContext();
|
||||
if (context?.Intent is not null)
|
||||
{
|
||||
Console.WriteLine($"Intent: {context.Intent}");
|
||||
}
|
||||
foreach (ChatCitation citation in context?.Citations ?? [])
|
||||
{
|
||||
Console.WriteLine($"Citation: {citation.Content}");
|
||||
}
|
||||
```
|
||||
|
||||
## Embeddings
|
||||
|
||||
```csharp
|
||||
using OpenAI.Embeddings;
|
||||
|
||||
EmbeddingClient embeddingClient = azureClient.GetEmbeddingClient("text-embedding-ada-002");
|
||||
|
||||
OpenAIEmbedding embedding = await embeddingClient.GenerateEmbeddingAsync("Hello, world!");
|
||||
ReadOnlyMemory<float> vector = embedding.ToFloats();
|
||||
|
||||
Console.WriteLine($"Embedding dimensions: {vector.Length}");
|
||||
```
|
||||
|
||||
### Batch Embeddings
|
||||
|
||||
```csharp
|
||||
List<string> inputs = new()
|
||||
{
|
||||
"First document text",
|
||||
"Second document text",
|
||||
"Third document text"
|
||||
};
|
||||
|
||||
OpenAIEmbeddingCollection embeddings = await embeddingClient.GenerateEmbeddingsAsync(inputs);
|
||||
|
||||
foreach (OpenAIEmbedding emb in embeddings)
|
||||
{
|
||||
Console.WriteLine($"Index {emb.Index}: {emb.ToFloats().Length} dimensions");
|
||||
}
|
||||
```
|
||||
|
||||
## Image Generation (DALL-E)
|
||||
|
||||
```csharp
|
||||
using OpenAI.Images;
|
||||
|
||||
ImageClient imageClient = azureClient.GetImageClient("dall-e-3");
|
||||
|
||||
GeneratedImage image = await imageClient.GenerateImageAsync(
|
||||
"A futuristic city skyline at sunset",
|
||||
new ImageGenerationOptions
|
||||
{
|
||||
Size = GeneratedImageSize.W1024xH1024,
|
||||
Quality = GeneratedImageQuality.High,
|
||||
Style = GeneratedImageStyle.Vivid
|
||||
});
|
||||
|
||||
Console.WriteLine($"Image URL: {image.ImageUri}");
|
||||
```
|
||||
|
||||
## Audio (Whisper)
|
||||
|
||||
### Transcription
|
||||
|
||||
```csharp
|
||||
using OpenAI.Audio;
|
||||
|
||||
AudioClient audioClient = azureClient.GetAudioClient("whisper");
|
||||
|
||||
AudioTranscription transcription = await audioClient.TranscribeAudioAsync(
|
||||
"audio.mp3",
|
||||
new AudioTranscriptionOptions
|
||||
{
|
||||
ResponseFormat = AudioTranscriptionFormat.Verbose,
|
||||
Language = "en"
|
||||
});
|
||||
|
||||
Console.WriteLine(transcription.Text);
|
||||
```
|
||||
|
||||
### Text-to-Speech
|
||||
|
||||
```csharp
|
||||
BinaryData speech = await audioClient.GenerateSpeechAsync(
|
||||
"Hello, welcome to Azure OpenAI!",
|
||||
GeneratedSpeechVoice.Alloy,
|
||||
new SpeechGenerationOptions
|
||||
{
|
||||
SpeedRatio = 1.0f,
|
||||
ResponseFormat = GeneratedSpeechFormat.Mp3
|
||||
});
|
||||
|
||||
await File.WriteAllBytesAsync("output.mp3", speech.ToArray());
|
||||
```
|
||||
|
||||
## Function Calling (Tools)
|
||||
|
||||
```csharp
|
||||
ChatTool getCurrentWeatherTool = ChatTool.CreateFunctionTool(
|
||||
functionName: "get_current_weather",
|
||||
functionDescription: "Get the current weather in a given location",
|
||||
functionParameters: BinaryData.FromString("""
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string",
|
||||
"enum": ["celsius", "fahrenheit"]
|
||||
}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
"""));
|
||||
|
||||
ChatCompletionOptions options = new()
|
||||
{
|
||||
Tools = { getCurrentWeatherTool }
|
||||
};
|
||||
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(
|
||||
[new UserChatMessage("What's the weather in Seattle?")],
|
||||
options);
|
||||
|
||||
if (completion.FinishReason == ChatFinishReason.ToolCalls)
|
||||
{
|
||||
foreach (ChatToolCall toolCall in completion.ToolCalls)
|
||||
{
|
||||
Console.WriteLine($"Function: {toolCall.FunctionName}");
|
||||
Console.WriteLine($"Arguments: {toolCall.FunctionArguments}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `AzureOpenAIClient` | Top-level client for Azure OpenAI |
|
||||
| `ChatClient` | Chat completions |
|
||||
| `EmbeddingClient` | Text embeddings |
|
||||
| `ImageClient` | Image generation (DALL-E) |
|
||||
| `AudioClient` | Audio transcription/TTS |
|
||||
| `ChatCompletion` | Chat response |
|
||||
| `ChatCompletionOptions` | Request configuration |
|
||||
| `StreamingChatCompletionUpdate` | Streaming response chunk |
|
||||
| `ChatMessage` | Base message type |
|
||||
| `SystemChatMessage` | System prompt |
|
||||
| `UserChatMessage` | User input |
|
||||
| `AssistantChatMessage` | Assistant response |
|
||||
| `DeveloperChatMessage` | Developer message (reasoning models) |
|
||||
| `ChatTool` | Function/tool definition |
|
||||
| `ChatToolCall` | Tool invocation request |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Entra ID in production** — Avoid API keys; use `DefaultAzureCredential`
|
||||
2. **Reuse client instances** — Create once, share across requests
|
||||
3. **Handle rate limits** — Implement exponential backoff for 429 errors
|
||||
4. **Stream for long responses** — Use `CompleteChatStreamingAsync` for better UX
|
||||
5. **Set appropriate timeouts** — Long completions may need extended timeouts
|
||||
6. **Use structured outputs** — JSON schema ensures consistent response format
|
||||
7. **Monitor token usage** — Track `completion.Usage` for cost management
|
||||
8. **Validate tool calls** — Always validate function arguments before execution
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
ChatCompletion completion = await chatClient.CompleteChatAsync(messages);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 429)
|
||||
{
|
||||
Console.WriteLine("Rate limited. Retry after delay.");
|
||||
await Task.Delay(TimeSpan.FromSeconds(10));
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 400)
|
||||
{
|
||||
Console.WriteLine($"Bad request: {ex.Message}");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Azure OpenAI error: {ex.Status} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.AI.OpenAI` | Azure OpenAI client (this SDK) | `dotnet add package Azure.AI.OpenAI` |
|
||||
| `OpenAI` | OpenAI compatibility | `dotnet add package OpenAI` |
|
||||
| `Azure.Identity` | Authentication | `dotnet add package Azure.Identity` |
|
||||
| `Azure.Search.Documents` | AI Search for RAG | `dotnet add package Azure.Search.Documents` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.AI.OpenAI |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.ai.openai |
|
||||
| Migration Guide (1.0→2.0) | https://learn.microsoft.com/azure/ai-services/openai/how-to/dotnet-migration |
|
||||
| Quickstart | https://learn.microsoft.com/azure/ai-services/openai/quickstart |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/openai/Azure.AI.OpenAI |
|
||||
348
skills/official/microsoft/dotnet/foundry/projects/SKILL.md
Normal file
348
skills/official/microsoft/dotnet/foundry/projects/SKILL.md
Normal file
@@ -0,0 +1,348 @@
|
||||
---
|
||||
name: azure-ai-projects-dotnet
|
||||
description: |
|
||||
Azure AI Projects SDK for .NET. High-level client for Azure AI Foundry projects including agents, connections, datasets, deployments, evaluations, and indexes. Use for AI Foundry project management, versioned agents, and orchestration. Triggers: "AI Projects", "AIProjectClient", "Foundry project", "versioned agents", "evaluations", "datasets", "connections", "deployments .NET".
|
||||
package: Azure.AI.Projects
|
||||
---
|
||||
|
||||
# Azure.AI.Projects (.NET)
|
||||
|
||||
High-level SDK for Azure AI Foundry project operations including agents, connections, datasets, deployments, evaluations, and indexes.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.AI.Projects
|
||||
dotnet add package Azure.Identity
|
||||
|
||||
# Optional: For versioned agents with OpenAI extensions
|
||||
dotnet add package Azure.AI.Projects.OpenAI --prerelease
|
||||
|
||||
# Optional: For low-level agent operations
|
||||
dotnet add package Azure.AI.Agents.Persistent --prerelease
|
||||
```
|
||||
|
||||
**Current Versions**: GA v1.1.0, Preview v1.2.0-beta.5
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
PROJECT_ENDPOINT=https://<resource>.services.ai.azure.com/api/projects/<project>
|
||||
MODEL_DEPLOYMENT_NAME=gpt-4o-mini
|
||||
CONNECTION_NAME=<your-connection-name>
|
||||
AI_SEARCH_CONNECTION_NAME=<ai-search-connection>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.AI.Projects;
|
||||
|
||||
var endpoint = Environment.GetEnvironmentVariable("PROJECT_ENDPOINT");
|
||||
AIProjectClient projectClient = new AIProjectClient(
|
||||
new Uri(endpoint),
|
||||
new DefaultAzureCredential());
|
||||
```
|
||||
|
||||
## Client Hierarchy
|
||||
|
||||
```
|
||||
AIProjectClient
|
||||
├── Agents → AIProjectAgentsOperations (versioned agents)
|
||||
├── Connections → ConnectionsClient
|
||||
├── Datasets → DatasetsClient
|
||||
├── Deployments → DeploymentsClient
|
||||
├── Evaluations → EvaluationsClient
|
||||
├── Evaluators → EvaluatorsClient
|
||||
├── Indexes → IndexesClient
|
||||
├── Telemetry → AIProjectTelemetry
|
||||
├── OpenAI → ProjectOpenAIClient (preview)
|
||||
└── GetPersistentAgentsClient() → PersistentAgentsClient
|
||||
```
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Get Persistent Agents Client
|
||||
|
||||
```csharp
|
||||
// Get low-level agents client from project client
|
||||
PersistentAgentsClient agentsClient = projectClient.GetPersistentAgentsClient();
|
||||
|
||||
// Create agent
|
||||
PersistentAgent agent = await agentsClient.Administration.CreateAgentAsync(
|
||||
model: "gpt-4o-mini",
|
||||
name: "Math Tutor",
|
||||
instructions: "You are a personal math tutor.");
|
||||
|
||||
// Create thread and run
|
||||
PersistentAgentThread thread = await agentsClient.Threads.CreateThreadAsync();
|
||||
await agentsClient.Messages.CreateMessageAsync(thread.Id, MessageRole.User, "Solve 3x + 11 = 14");
|
||||
ThreadRun run = await agentsClient.Runs.CreateRunAsync(thread.Id, agent.Id);
|
||||
|
||||
// Poll for completion
|
||||
do
|
||||
{
|
||||
await Task.Delay(500);
|
||||
run = await agentsClient.Runs.GetRunAsync(thread.Id, run.Id);
|
||||
}
|
||||
while (run.Status == RunStatus.Queued || run.Status == RunStatus.InProgress);
|
||||
|
||||
// Get messages
|
||||
await foreach (var msg in agentsClient.Messages.GetMessagesAsync(thread.Id))
|
||||
{
|
||||
foreach (var content in msg.ContentItems)
|
||||
{
|
||||
if (content is MessageTextContent textContent)
|
||||
Console.WriteLine(textContent.Text);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
await agentsClient.Threads.DeleteThreadAsync(thread.Id);
|
||||
await agentsClient.Administration.DeleteAgentAsync(agent.Id);
|
||||
```
|
||||
|
||||
### 2. Versioned Agents with Tools (Preview)
|
||||
|
||||
```csharp
|
||||
using Azure.AI.Projects.OpenAI;
|
||||
|
||||
// Create agent with web search tool
|
||||
PromptAgentDefinition agentDefinition = new(model: "gpt-4o-mini")
|
||||
{
|
||||
Instructions = "You are a helpful assistant that can search the web",
|
||||
Tools = {
|
||||
ResponseTool.CreateWebSearchTool(
|
||||
userLocation: WebSearchToolLocation.CreateApproximateLocation(
|
||||
country: "US",
|
||||
city: "Seattle",
|
||||
region: "Washington"
|
||||
)
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
AgentVersion agentVersion = await projectClient.Agents.CreateAgentVersionAsync(
|
||||
agentName: "myAgent",
|
||||
options: new(agentDefinition));
|
||||
|
||||
// Get response client
|
||||
ProjectResponsesClient responseClient = projectClient.OpenAI.GetProjectResponsesClientForAgent(agentVersion.Name);
|
||||
|
||||
// Create response
|
||||
ResponseResult response = responseClient.CreateResponse("What's the weather in Seattle?");
|
||||
Console.WriteLine(response.GetOutputText());
|
||||
|
||||
// Cleanup
|
||||
projectClient.Agents.DeleteAgentVersion(agentName: agentVersion.Name, agentVersion: agentVersion.Version);
|
||||
```
|
||||
|
||||
### 3. Connections
|
||||
|
||||
```csharp
|
||||
// List all connections
|
||||
foreach (AIProjectConnection connection in projectClient.Connections.GetConnections())
|
||||
{
|
||||
Console.WriteLine($"{connection.Name}: {connection.ConnectionType}");
|
||||
}
|
||||
|
||||
// Get specific connection
|
||||
AIProjectConnection conn = projectClient.Connections.GetConnection(
|
||||
connectionName,
|
||||
includeCredentials: true);
|
||||
|
||||
// Get default connection
|
||||
AIProjectConnection defaultConn = projectClient.Connections.GetDefaultConnection(
|
||||
includeCredentials: false);
|
||||
```
|
||||
|
||||
### 4. Deployments
|
||||
|
||||
```csharp
|
||||
// List all deployments
|
||||
foreach (AIProjectDeployment deployment in projectClient.Deployments.GetDeployments())
|
||||
{
|
||||
Console.WriteLine($"{deployment.Name}: {deployment.ModelName}");
|
||||
}
|
||||
|
||||
// Filter by publisher
|
||||
foreach (var deployment in projectClient.Deployments.GetDeployments(modelPublisher: "Microsoft"))
|
||||
{
|
||||
Console.WriteLine(deployment.Name);
|
||||
}
|
||||
|
||||
// Get specific deployment
|
||||
ModelDeployment details = (ModelDeployment)projectClient.Deployments.GetDeployment("gpt-4o-mini");
|
||||
```
|
||||
|
||||
### 5. Datasets
|
||||
|
||||
```csharp
|
||||
// Upload single file
|
||||
FileDataset fileDataset = projectClient.Datasets.UploadFile(
|
||||
name: "my-dataset",
|
||||
version: "1.0",
|
||||
filePath: "data/training.txt",
|
||||
connectionName: connectionName);
|
||||
|
||||
// Upload folder
|
||||
FolderDataset folderDataset = projectClient.Datasets.UploadFolder(
|
||||
name: "my-dataset",
|
||||
version: "2.0",
|
||||
folderPath: "data/training",
|
||||
connectionName: connectionName,
|
||||
filePattern: new Regex(".*\\.txt"));
|
||||
|
||||
// Get dataset
|
||||
AIProjectDataset dataset = projectClient.Datasets.GetDataset("my-dataset", "1.0");
|
||||
|
||||
// Delete dataset
|
||||
projectClient.Datasets.Delete("my-dataset", "1.0");
|
||||
```
|
||||
|
||||
### 6. Indexes
|
||||
|
||||
```csharp
|
||||
// Create Azure AI Search index
|
||||
AzureAISearchIndex searchIndex = new(aiSearchConnectionName, aiSearchIndexName)
|
||||
{
|
||||
Description = "Sample Index"
|
||||
};
|
||||
|
||||
searchIndex = (AzureAISearchIndex)projectClient.Indexes.CreateOrUpdate(
|
||||
name: "my-index",
|
||||
version: "1.0",
|
||||
index: searchIndex);
|
||||
|
||||
// List indexes
|
||||
foreach (AIProjectIndex index in projectClient.Indexes.GetIndexes())
|
||||
{
|
||||
Console.WriteLine(index.Name);
|
||||
}
|
||||
|
||||
// Delete index
|
||||
projectClient.Indexes.Delete(name: "my-index", version: "1.0");
|
||||
```
|
||||
|
||||
### 7. Evaluations
|
||||
|
||||
```csharp
|
||||
// Create evaluation configuration
|
||||
var evaluatorConfig = new EvaluatorConfiguration(id: EvaluatorIDs.Relevance);
|
||||
evaluatorConfig.InitParams.Add("deployment_name", BinaryData.FromObjectAsJson("gpt-4o"));
|
||||
|
||||
// Create evaluation
|
||||
Evaluation evaluation = new Evaluation(
|
||||
data: new InputDataset("<dataset_id>"),
|
||||
evaluators: new Dictionary<string, EvaluatorConfiguration>
|
||||
{
|
||||
{ "relevance", evaluatorConfig }
|
||||
}
|
||||
)
|
||||
{
|
||||
DisplayName = "Sample Evaluation"
|
||||
};
|
||||
|
||||
// Run evaluation
|
||||
Evaluation result = projectClient.Evaluations.Create(evaluation: evaluation);
|
||||
|
||||
// Get evaluation
|
||||
Evaluation getResult = projectClient.Evaluations.Get(result.Name);
|
||||
|
||||
// List evaluations
|
||||
foreach (var eval in projectClient.Evaluations.GetAll())
|
||||
{
|
||||
Console.WriteLine($"{eval.DisplayName}: {eval.Status}");
|
||||
}
|
||||
```
|
||||
|
||||
### 8. Get Azure OpenAI Chat Client
|
||||
|
||||
```csharp
|
||||
using Azure.AI.OpenAI;
|
||||
using OpenAI.Chat;
|
||||
|
||||
ClientConnection connection = projectClient.GetConnection(typeof(AzureOpenAIClient).FullName!);
|
||||
|
||||
if (!connection.TryGetLocatorAsUri(out Uri uri) || uri is null)
|
||||
throw new InvalidOperationException("Invalid URI.");
|
||||
|
||||
uri = new Uri($"https://{uri.Host}");
|
||||
|
||||
AzureOpenAIClient azureOpenAIClient = new AzureOpenAIClient(uri, new DefaultAzureCredential());
|
||||
ChatClient chatClient = azureOpenAIClient.GetChatClient("gpt-4o-mini");
|
||||
|
||||
ChatCompletion result = chatClient.CompleteChat("List all rainbow colors");
|
||||
Console.WriteLine(result.Content[0].Text);
|
||||
```
|
||||
|
||||
## Available Agent Tools
|
||||
|
||||
| Tool | Class | Purpose |
|
||||
|------|-------|---------|
|
||||
| Code Interpreter | `CodeInterpreterToolDefinition` | Execute Python code |
|
||||
| File Search | `FileSearchToolDefinition` | Search uploaded files |
|
||||
| Function Calling | `FunctionToolDefinition` | Call custom functions |
|
||||
| Bing Grounding | `BingGroundingToolDefinition` | Web search via Bing |
|
||||
| Azure AI Search | `AzureAISearchToolDefinition` | Search Azure AI indexes |
|
||||
| OpenAPI | `OpenApiToolDefinition` | Call external APIs |
|
||||
| Azure Functions | `AzureFunctionToolDefinition` | Invoke Azure Functions |
|
||||
| MCP | `MCPToolDefinition` | Model Context Protocol tools |
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `AIProjectClient` | Main entry point |
|
||||
| `PersistentAgentsClient` | Low-level agent operations |
|
||||
| `PromptAgentDefinition` | Versioned agent definition |
|
||||
| `AgentVersion` | Versioned agent instance |
|
||||
| `AIProjectConnection` | Connection to Azure resource |
|
||||
| `AIProjectDeployment` | Model deployment info |
|
||||
| `AIProjectDataset` | Dataset metadata |
|
||||
| `AIProjectIndex` | Search index metadata |
|
||||
| `Evaluation` | Evaluation configuration and results |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `DefaultAzureCredential`** for production authentication
|
||||
2. **Use async methods** (`*Async`) for all I/O operations
|
||||
3. **Poll with appropriate delays** (500ms recommended) when waiting for runs
|
||||
4. **Clean up resources** — delete threads, agents, and files when done
|
||||
5. **Use versioned agents** (via `Azure.AI.Projects.OpenAI`) for production scenarios
|
||||
6. **Store connection IDs** rather than names for tool configurations
|
||||
7. **Use `includeCredentials: true`** only when credentials are needed
|
||||
8. **Handle pagination** — use `AsyncPageable<T>` for listing operations
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
var result = await projectClient.Evaluations.CreateAsync(evaluation);
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.AI.Projects` | High-level project client (this SDK) | `dotnet add package Azure.AI.Projects` |
|
||||
| `Azure.AI.Agents.Persistent` | Low-level agent operations | `dotnet add package Azure.AI.Agents.Persistent` |
|
||||
| `Azure.AI.Projects.OpenAI` | Versioned agents with OpenAI | `dotnet add package Azure.AI.Projects.OpenAI` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.AI.Projects |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.ai.projects |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.Projects |
|
||||
| Samples | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.Projects/samples |
|
||||
@@ -0,0 +1,339 @@
|
||||
---
|
||||
name: azure-search-documents-dotnet
|
||||
description: |
|
||||
Azure AI Search SDK for .NET (Azure.Search.Documents). Use for building search applications with full-text, vector, semantic, and hybrid search. Covers SearchClient (queries, document CRUD), SearchIndexClient (index management), and SearchIndexerClient (indexers, skillsets). Triggers: "Azure Search .NET", "SearchClient", "SearchIndexClient", "vector search C#", "semantic search .NET", "hybrid search", "Azure.Search.Documents".
|
||||
package: Azure.Search.Documents
|
||||
---
|
||||
|
||||
# Azure.Search.Documents (.NET)
|
||||
|
||||
Build search applications with full-text, vector, semantic, and hybrid search capabilities.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.Search.Documents
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Versions**: Stable v11.7.0, Preview v11.8.0-beta.1
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
SEARCH_ENDPOINT=https://<search-service>.search.windows.net
|
||||
SEARCH_INDEX_NAME=<index-name>
|
||||
# For API key auth (not recommended for production)
|
||||
SEARCH_API_KEY=<api-key>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
**DefaultAzureCredential (preferred)**:
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Search.Documents;
|
||||
|
||||
var credential = new DefaultAzureCredential();
|
||||
var client = new SearchClient(
|
||||
new Uri(Environment.GetEnvironmentVariable("SEARCH_ENDPOINT")),
|
||||
Environment.GetEnvironmentVariable("SEARCH_INDEX_NAME"),
|
||||
credential);
|
||||
```
|
||||
|
||||
**API Key**:
|
||||
```csharp
|
||||
using Azure;
|
||||
using Azure.Search.Documents;
|
||||
|
||||
var credential = new AzureKeyCredential(
|
||||
Environment.GetEnvironmentVariable("SEARCH_API_KEY"));
|
||||
var client = new SearchClient(
|
||||
new Uri(Environment.GetEnvironmentVariable("SEARCH_ENDPOINT")),
|
||||
Environment.GetEnvironmentVariable("SEARCH_INDEX_NAME"),
|
||||
credential);
|
||||
```
|
||||
|
||||
## Client Selection
|
||||
|
||||
| Client | Purpose |
|
||||
|--------|---------|
|
||||
| `SearchClient` | Query indexes, upload/update/delete documents |
|
||||
| `SearchIndexClient` | Create/manage indexes, synonym maps |
|
||||
| `SearchIndexerClient` | Manage indexers, skillsets, data sources |
|
||||
|
||||
## Index Creation
|
||||
|
||||
### Using FieldBuilder (Recommended)
|
||||
|
||||
```csharp
|
||||
using Azure.Search.Documents.Indexes;
|
||||
using Azure.Search.Documents.Indexes.Models;
|
||||
|
||||
// Define model with attributes
|
||||
public class Hotel
|
||||
{
|
||||
[SimpleField(IsKey = true, IsFilterable = true)]
|
||||
public string HotelId { get; set; }
|
||||
|
||||
[SearchableField(IsSortable = true)]
|
||||
public string HotelName { get; set; }
|
||||
|
||||
[SearchableField(AnalyzerName = LexicalAnalyzerName.EnLucene)]
|
||||
public string Description { get; set; }
|
||||
|
||||
[SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
|
||||
public double? Rating { get; set; }
|
||||
|
||||
[VectorSearchField(VectorSearchDimensions = 1536, VectorSearchProfileName = "vector-profile")]
|
||||
public ReadOnlyMemory<float>? DescriptionVector { get; set; }
|
||||
}
|
||||
|
||||
// Create index
|
||||
var indexClient = new SearchIndexClient(endpoint, credential);
|
||||
var fieldBuilder = new FieldBuilder();
|
||||
var fields = fieldBuilder.Build(typeof(Hotel));
|
||||
|
||||
var index = new SearchIndex("hotels")
|
||||
{
|
||||
Fields = fields,
|
||||
VectorSearch = new VectorSearch
|
||||
{
|
||||
Profiles = { new VectorSearchProfile("vector-profile", "hnsw-algo") },
|
||||
Algorithms = { new HnswAlgorithmConfiguration("hnsw-algo") }
|
||||
}
|
||||
};
|
||||
|
||||
await indexClient.CreateOrUpdateIndexAsync(index);
|
||||
```
|
||||
|
||||
### Manual Field Definition
|
||||
|
||||
```csharp
|
||||
var index = new SearchIndex("hotels")
|
||||
{
|
||||
Fields =
|
||||
{
|
||||
new SimpleField("hotelId", SearchFieldDataType.String) { IsKey = true, IsFilterable = true },
|
||||
new SearchableField("hotelName") { IsSortable = true },
|
||||
new SearchableField("description") { AnalyzerName = LexicalAnalyzerName.EnLucene },
|
||||
new SimpleField("rating", SearchFieldDataType.Double) { IsFilterable = true, IsSortable = true },
|
||||
new SearchField("descriptionVector", SearchFieldDataType.Collection(SearchFieldDataType.Single))
|
||||
{
|
||||
VectorSearchDimensions = 1536,
|
||||
VectorSearchProfileName = "vector-profile"
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Document Operations
|
||||
|
||||
```csharp
|
||||
var searchClient = new SearchClient(endpoint, indexName, credential);
|
||||
|
||||
// Upload (add new)
|
||||
var hotels = new[] { new Hotel { HotelId = "1", HotelName = "Hotel A" } };
|
||||
await searchClient.UploadDocumentsAsync(hotels);
|
||||
|
||||
// Merge (update existing)
|
||||
await searchClient.MergeDocumentsAsync(hotels);
|
||||
|
||||
// Merge or Upload (upsert)
|
||||
await searchClient.MergeOrUploadDocumentsAsync(hotels);
|
||||
|
||||
// Delete
|
||||
await searchClient.DeleteDocumentsAsync("hotelId", new[] { "1", "2" });
|
||||
|
||||
// Batch operations
|
||||
var batch = IndexDocumentsBatch.Create(
|
||||
IndexDocumentsAction.Upload(hotel1),
|
||||
IndexDocumentsAction.Merge(hotel2),
|
||||
IndexDocumentsAction.Delete(hotel3));
|
||||
await searchClient.IndexDocumentsAsync(batch);
|
||||
```
|
||||
|
||||
## Search Patterns
|
||||
|
||||
### Basic Search
|
||||
|
||||
```csharp
|
||||
var options = new SearchOptions
|
||||
{
|
||||
Filter = "rating ge 4",
|
||||
OrderBy = { "rating desc" },
|
||||
Select = { "hotelId", "hotelName", "rating" },
|
||||
Size = 10,
|
||||
Skip = 0,
|
||||
IncludeTotalCount = true
|
||||
};
|
||||
|
||||
SearchResults<Hotel> results = await searchClient.SearchAsync<Hotel>("luxury", options);
|
||||
|
||||
Console.WriteLine($"Total: {results.TotalCount}");
|
||||
await foreach (SearchResult<Hotel> result in results.GetResultsAsync())
|
||||
{
|
||||
Console.WriteLine($"{result.Document.HotelName} (Score: {result.Score})");
|
||||
}
|
||||
```
|
||||
|
||||
### Faceted Search
|
||||
|
||||
```csharp
|
||||
var options = new SearchOptions
|
||||
{
|
||||
Facets = { "rating,count:5", "category" }
|
||||
};
|
||||
|
||||
var results = await searchClient.SearchAsync<Hotel>("*", options);
|
||||
|
||||
foreach (var facet in results.Value.Facets["rating"])
|
||||
{
|
||||
Console.WriteLine($"Rating {facet.Value}: {facet.Count}");
|
||||
}
|
||||
```
|
||||
|
||||
### Autocomplete and Suggestions
|
||||
|
||||
```csharp
|
||||
// Autocomplete
|
||||
var autocompleteOptions = new AutocompleteOptions { Mode = AutocompleteMode.OneTermWithContext };
|
||||
var autocomplete = await searchClient.AutocompleteAsync("lux", "suggester-name", autocompleteOptions);
|
||||
|
||||
// Suggestions
|
||||
var suggestOptions = new SuggestOptions { UseFuzzyMatching = true };
|
||||
var suggestions = await searchClient.SuggestAsync<Hotel>("lux", "suggester-name", suggestOptions);
|
||||
```
|
||||
|
||||
## Vector Search
|
||||
|
||||
See [references/vector-search.md](references/vector-search.md) for detailed patterns.
|
||||
|
||||
```csharp
|
||||
using Azure.Search.Documents.Models;
|
||||
|
||||
// Pure vector search
|
||||
var vectorQuery = new VectorizedQuery(embedding)
|
||||
{
|
||||
KNearestNeighborsCount = 5,
|
||||
Fields = { "descriptionVector" }
|
||||
};
|
||||
|
||||
var options = new SearchOptions
|
||||
{
|
||||
VectorSearch = new VectorSearchOptions
|
||||
{
|
||||
Queries = { vectorQuery }
|
||||
}
|
||||
};
|
||||
|
||||
var results = await searchClient.SearchAsync<Hotel>(null, options);
|
||||
```
|
||||
|
||||
## Semantic Search
|
||||
|
||||
See [references/semantic-search.md](references/semantic-search.md) for detailed patterns.
|
||||
|
||||
```csharp
|
||||
var options = new SearchOptions
|
||||
{
|
||||
QueryType = SearchQueryType.Semantic,
|
||||
SemanticSearch = new SemanticSearchOptions
|
||||
{
|
||||
SemanticConfigurationName = "my-semantic-config",
|
||||
QueryCaption = new QueryCaption(QueryCaptionType.Extractive),
|
||||
QueryAnswer = new QueryAnswer(QueryAnswerType.Extractive)
|
||||
}
|
||||
};
|
||||
|
||||
var results = await searchClient.SearchAsync<Hotel>("best hotel for families", options);
|
||||
|
||||
// Access semantic answers
|
||||
foreach (var answer in results.Value.SemanticSearch.Answers)
|
||||
{
|
||||
Console.WriteLine($"Answer: {answer.Text} (Score: {answer.Score})");
|
||||
}
|
||||
|
||||
// Access captions
|
||||
await foreach (var result in results.Value.GetResultsAsync())
|
||||
{
|
||||
var caption = result.SemanticSearch?.Captions?.FirstOrDefault();
|
||||
Console.WriteLine($"Caption: {caption?.Text}");
|
||||
}
|
||||
```
|
||||
|
||||
## Hybrid Search (Vector + Keyword + Semantic)
|
||||
|
||||
```csharp
|
||||
var vectorQuery = new VectorizedQuery(embedding)
|
||||
{
|
||||
KNearestNeighborsCount = 5,
|
||||
Fields = { "descriptionVector" }
|
||||
};
|
||||
|
||||
var options = new SearchOptions
|
||||
{
|
||||
QueryType = SearchQueryType.Semantic,
|
||||
SemanticSearch = new SemanticSearchOptions
|
||||
{
|
||||
SemanticConfigurationName = "my-semantic-config"
|
||||
},
|
||||
VectorSearch = new VectorSearchOptions
|
||||
{
|
||||
Queries = { vectorQuery }
|
||||
}
|
||||
};
|
||||
|
||||
// Combines keyword search, vector search, and semantic ranking
|
||||
var results = await searchClient.SearchAsync<Hotel>("luxury beachfront", options);
|
||||
```
|
||||
|
||||
## Field Attributes Reference
|
||||
|
||||
| Attribute | Purpose |
|
||||
|-----------|---------|
|
||||
| `SimpleField` | Non-searchable field (filters, sorting, facets) |
|
||||
| `SearchableField` | Full-text searchable field |
|
||||
| `VectorSearchField` | Vector embedding field |
|
||||
| `IsKey = true` | Document key (required, one per index) |
|
||||
| `IsFilterable = true` | Enable $filter expressions |
|
||||
| `IsSortable = true` | Enable $orderby |
|
||||
| `IsFacetable = true` | Enable faceted navigation |
|
||||
| `IsHidden = true` | Exclude from results |
|
||||
| `AnalyzerName` | Specify text analyzer |
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
var results = await searchClient.SearchAsync<Hotel>("query");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 404)
|
||||
{
|
||||
Console.WriteLine("Index not found");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Search error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `DefaultAzureCredential`** over API keys for production
|
||||
2. **Use `FieldBuilder`** with model attributes for type-safe index definitions
|
||||
3. **Use `CreateOrUpdateIndexAsync`** for idempotent index creation
|
||||
4. **Batch document operations** for better throughput
|
||||
5. **Use `Select`** to return only needed fields
|
||||
6. **Configure semantic search** for natural language queries
|
||||
7. **Combine vector + keyword + semantic** for best relevance
|
||||
|
||||
## Reference Files
|
||||
|
||||
| File | Contents |
|
||||
|------|----------|
|
||||
| [references/vector-search.md](references/vector-search.md) | Vector search, hybrid search, vectorizers |
|
||||
| [references/semantic-search.md](references/semantic-search.md) | Semantic ranking, captions, answers |
|
||||
265
skills/official/microsoft/dotnet/foundry/voicelive/SKILL.md
Normal file
265
skills/official/microsoft/dotnet/foundry/voicelive/SKILL.md
Normal file
@@ -0,0 +1,265 @@
|
||||
---
|
||||
name: azure-ai-voicelive-dotnet
|
||||
description: |
|
||||
Azure AI Voice Live SDK for .NET. Build real-time voice AI applications with bidirectional WebSocket communication. Use for voice assistants, conversational AI, real-time speech-to-speech, and voice-enabled chatbots. Triggers: "voice live", "real-time voice", "VoiceLiveClient", "VoiceLiveSession", "voice assistant .NET", "bidirectional audio", "speech-to-speech".
|
||||
package: Azure.AI.VoiceLive
|
||||
---
|
||||
|
||||
# Azure.AI.VoiceLive (.NET)
|
||||
|
||||
Real-time voice AI SDK for building bidirectional voice assistants with Azure AI.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.AI.VoiceLive
|
||||
dotnet add package Azure.Identity
|
||||
dotnet add package NAudio # For audio capture/playback
|
||||
```
|
||||
|
||||
**Current Versions**: Stable v1.0.0, Preview v1.1.0-beta.1
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_VOICELIVE_ENDPOINT=https://<resource>.services.ai.azure.com/
|
||||
AZURE_VOICELIVE_MODEL=gpt-4o-realtime-preview
|
||||
AZURE_VOICELIVE_VOICE=en-US-AvaNeural
|
||||
# Optional: API key if not using Entra ID
|
||||
AZURE_VOICELIVE_API_KEY=<your-api-key>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### Microsoft Entra ID (Recommended)
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.AI.VoiceLive;
|
||||
|
||||
Uri endpoint = new Uri("https://your-resource.cognitiveservices.azure.com");
|
||||
DefaultAzureCredential credential = new DefaultAzureCredential();
|
||||
VoiceLiveClient client = new VoiceLiveClient(endpoint, credential);
|
||||
```
|
||||
|
||||
**Required Role**: `Cognitive Services User` (assign in Azure Portal → Access control)
|
||||
|
||||
### API Key
|
||||
|
||||
```csharp
|
||||
Uri endpoint = new Uri("https://your-resource.cognitiveservices.azure.com");
|
||||
AzureKeyCredential credential = new AzureKeyCredential("your-api-key");
|
||||
VoiceLiveClient client = new VoiceLiveClient(endpoint, credential);
|
||||
```
|
||||
|
||||
## Client Hierarchy
|
||||
|
||||
```
|
||||
VoiceLiveClient
|
||||
└── VoiceLiveSession (WebSocket connection)
|
||||
├── ConfigureSessionAsync()
|
||||
├── GetUpdatesAsync() → SessionUpdate events
|
||||
├── AddItemAsync() → UserMessageItem, FunctionCallOutputItem
|
||||
├── SendAudioAsync()
|
||||
└── StartResponseAsync()
|
||||
```
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### 1. Start Session and Configure
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.AI.VoiceLive;
|
||||
|
||||
var endpoint = new Uri(Environment.GetEnvironmentVariable("AZURE_VOICELIVE_ENDPOINT"));
|
||||
var client = new VoiceLiveClient(endpoint, new DefaultAzureCredential());
|
||||
|
||||
var model = "gpt-4o-mini-realtime-preview";
|
||||
|
||||
// Start session
|
||||
using VoiceLiveSession session = await client.StartSessionAsync(model);
|
||||
|
||||
// Configure session
|
||||
VoiceLiveSessionOptions sessionOptions = new()
|
||||
{
|
||||
Model = model,
|
||||
Instructions = "You are a helpful AI assistant. Respond naturally.",
|
||||
Voice = new AzureStandardVoice("en-US-AvaNeural"),
|
||||
TurnDetection = new AzureSemanticVadTurnDetection()
|
||||
{
|
||||
Threshold = 0.5f,
|
||||
PrefixPadding = TimeSpan.FromMilliseconds(300),
|
||||
SilenceDuration = TimeSpan.FromMilliseconds(500)
|
||||
},
|
||||
InputAudioFormat = InputAudioFormat.Pcm16,
|
||||
OutputAudioFormat = OutputAudioFormat.Pcm16
|
||||
};
|
||||
|
||||
// Set modalities (both text and audio for voice assistants)
|
||||
sessionOptions.Modalities.Clear();
|
||||
sessionOptions.Modalities.Add(InteractionModality.Text);
|
||||
sessionOptions.Modalities.Add(InteractionModality.Audio);
|
||||
|
||||
await session.ConfigureSessionAsync(sessionOptions);
|
||||
```
|
||||
|
||||
### 2. Process Events
|
||||
|
||||
```csharp
|
||||
await foreach (SessionUpdate serverEvent in session.GetUpdatesAsync())
|
||||
{
|
||||
switch (serverEvent)
|
||||
{
|
||||
case SessionUpdateResponseAudioDelta audioDelta:
|
||||
byte[] audioData = audioDelta.Delta.ToArray();
|
||||
// Play audio via NAudio or other audio library
|
||||
break;
|
||||
|
||||
case SessionUpdateResponseTextDelta textDelta:
|
||||
Console.Write(textDelta.Delta);
|
||||
break;
|
||||
|
||||
case SessionUpdateResponseFunctionCallArgumentsDone functionCall:
|
||||
// Handle function call (see Function Calling section)
|
||||
break;
|
||||
|
||||
case SessionUpdateError error:
|
||||
Console.WriteLine($"Error: {error.Error.Message}");
|
||||
break;
|
||||
|
||||
case SessionUpdateResponseDone:
|
||||
Console.WriteLine("\n--- Response complete ---");
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Send User Message
|
||||
|
||||
```csharp
|
||||
await session.AddItemAsync(new UserMessageItem("Hello, can you help me?"));
|
||||
await session.StartResponseAsync();
|
||||
```
|
||||
|
||||
### 4. Function Calling
|
||||
|
||||
```csharp
|
||||
// Define function
|
||||
var weatherFunction = new VoiceLiveFunctionDefinition("get_current_weather")
|
||||
{
|
||||
Description = "Get the current weather for a given location",
|
||||
Parameters = BinaryData.FromString("""
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state or country"
|
||||
}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
""")
|
||||
};
|
||||
|
||||
// Add to session options
|
||||
sessionOptions.Tools.Add(weatherFunction);
|
||||
|
||||
// Handle function call in event loop
|
||||
if (serverEvent is SessionUpdateResponseFunctionCallArgumentsDone functionCall)
|
||||
{
|
||||
if (functionCall.Name == "get_current_weather")
|
||||
{
|
||||
var parameters = JsonSerializer.Deserialize<Dictionary<string, string>>(functionCall.Arguments);
|
||||
string location = parameters?["location"] ?? "";
|
||||
|
||||
// Call external service
|
||||
string weatherInfo = $"The weather in {location} is sunny, 75°F.";
|
||||
|
||||
// Send response
|
||||
await session.AddItemAsync(new FunctionCallOutputItem(functionCall.CallId, weatherInfo));
|
||||
await session.StartResponseAsync();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Voice Options
|
||||
|
||||
| Voice Type | Class | Example |
|
||||
|------------|-------|---------|
|
||||
| Azure Standard | `AzureStandardVoice` | `"en-US-AvaNeural"` |
|
||||
| Azure HD | `AzureStandardVoice` | `"en-US-Ava:DragonHDLatestNeural"` |
|
||||
| Azure Custom | `AzureCustomVoice` | Custom voice with endpoint ID |
|
||||
|
||||
## Supported Models
|
||||
|
||||
| Model | Description |
|
||||
|-------|-------------|
|
||||
| `gpt-4o-realtime-preview` | GPT-4o with real-time audio |
|
||||
| `gpt-4o-mini-realtime-preview` | Lightweight, fast interactions |
|
||||
| `phi4-mm-realtime` | Cost-effective multimodal |
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `VoiceLiveClient` | Main client for creating sessions |
|
||||
| `VoiceLiveSession` | Active WebSocket session |
|
||||
| `VoiceLiveSessionOptions` | Session configuration |
|
||||
| `AzureStandardVoice` | Standard Azure voice provider |
|
||||
| `AzureSemanticVadTurnDetection` | Voice activity detection |
|
||||
| `VoiceLiveFunctionDefinition` | Function tool definition |
|
||||
| `UserMessageItem` | User text message |
|
||||
| `FunctionCallOutputItem` | Function call response |
|
||||
| `SessionUpdateResponseAudioDelta` | Audio chunk event |
|
||||
| `SessionUpdateResponseTextDelta` | Text chunk event |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always set both modalities** — Include `Text` and `Audio` for voice assistants
|
||||
2. **Use `AzureSemanticVadTurnDetection`** — Provides natural conversation flow
|
||||
3. **Configure appropriate silence duration** — 500ms typical to avoid premature cutoffs
|
||||
4. **Use `using` statement** — Ensures proper session disposal
|
||||
5. **Handle all event types** — Check for errors, audio, text, and function calls
|
||||
6. **Use DefaultAzureCredential** — Never hardcode API keys
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
if (serverEvent is SessionUpdateError error)
|
||||
{
|
||||
if (error.Error.Message.Contains("Cancellation failed: no active response"))
|
||||
{
|
||||
// Benign error, can ignore
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Error: {error.Error.Message}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Audio Configuration
|
||||
|
||||
- **Input Format**: `InputAudioFormat.Pcm16` (16-bit PCM)
|
||||
- **Output Format**: `OutputAudioFormat.Pcm16`
|
||||
- **Sample Rate**: 24kHz recommended
|
||||
- **Channels**: Mono
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.AI.VoiceLive` | Real-time voice (this SDK) | `dotnet add package Azure.AI.VoiceLive` |
|
||||
| `Microsoft.CognitiveServices.Speech` | Speech-to-text, text-to-speech | `dotnet add package Microsoft.CognitiveServices.Speech` |
|
||||
| `NAudio` | Audio capture/playback | `dotnet add package NAudio` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.AI.VoiceLive |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.ai.voicelive |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.VoiceLive |
|
||||
| Quickstart | https://learn.microsoft.com/azure/ai-services/speech-service/voice-live-quickstart |
|
||||
@@ -0,0 +1,328 @@
|
||||
---
|
||||
name: azure-mgmt-weightsandbiases-dotnet
|
||||
description: |
|
||||
Azure Weights & Biases SDK for .NET. ML experiment tracking and model management via Azure Marketplace. Use for creating W&B instances, managing SSO, marketplace integration, and ML observability. Triggers: "Weights and Biases", "W&B", "WeightsAndBiases", "ML experiment tracking", "model registry", "experiment management", "wandb".
|
||||
package: Azure.ResourceManager.WeightsAndBiases
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.WeightsAndBiases (.NET)
|
||||
|
||||
Azure Resource Manager SDK for deploying and managing Weights & Biases ML experiment tracking instances via Azure Marketplace.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.WeightsAndBiases --prerelease
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Version**: v1.0.0-beta.1 (preview)
|
||||
**API Version**: 2024-09-18-preview
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
AZURE_RESOURCE_GROUP=<your-resource-group>
|
||||
AZURE_WANDB_INSTANCE_NAME=<your-wandb-instance>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.WeightsAndBiases;
|
||||
|
||||
ArmClient client = new ArmClient(new DefaultAzureCredential());
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
Subscription
|
||||
└── ResourceGroup
|
||||
└── WeightsAndBiasesInstance # W&B deployment from Azure Marketplace
|
||||
├── Properties
|
||||
│ ├── Marketplace # Offer details, plan, publisher
|
||||
│ ├── User # Admin user info
|
||||
│ ├── PartnerProperties # W&B-specific config (region, subdomain)
|
||||
│ └── SingleSignOnPropertiesV2 # Entra ID SSO configuration
|
||||
└── Identity # Managed identity (optional)
|
||||
```
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Create Weights & Biases Instance
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.WeightsAndBiases;
|
||||
using Azure.ResourceManager.WeightsAndBiases.Models;
|
||||
|
||||
ResourceGroupResource resourceGroup = await client
|
||||
.GetDefaultSubscriptionAsync()
|
||||
.Result
|
||||
.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
WeightsAndBiasesInstanceCollection instances = resourceGroup.GetWeightsAndBiasesInstances();
|
||||
|
||||
WeightsAndBiasesInstanceData data = new WeightsAndBiasesInstanceData(AzureLocation.EastUS)
|
||||
{
|
||||
Properties = new WeightsAndBiasesInstanceProperties
|
||||
{
|
||||
// Marketplace configuration
|
||||
Marketplace = new WeightsAndBiasesMarketplaceDetails
|
||||
{
|
||||
SubscriptionId = "<marketplace-subscription-id>",
|
||||
OfferDetails = new WeightsAndBiasesOfferDetails
|
||||
{
|
||||
PublisherId = "wandb",
|
||||
OfferId = "wandb-pay-as-you-go",
|
||||
PlanId = "wandb-payg",
|
||||
PlanName = "Pay As You Go",
|
||||
TermId = "monthly",
|
||||
TermUnit = "P1M"
|
||||
}
|
||||
},
|
||||
// Admin user
|
||||
User = new WeightsAndBiasesUserDetails
|
||||
{
|
||||
FirstName = "Admin",
|
||||
LastName = "User",
|
||||
EmailAddress = "admin@example.com",
|
||||
Upn = "admin@example.com"
|
||||
},
|
||||
// W&B-specific configuration
|
||||
PartnerProperties = new WeightsAndBiasesPartnerProperties
|
||||
{
|
||||
Region = WeightsAndBiasesRegion.EastUS,
|
||||
Subdomain = "my-company-wandb"
|
||||
}
|
||||
},
|
||||
// Optional: Enable managed identity
|
||||
Identity = new ManagedServiceIdentity(ManagedServiceIdentityType.SystemAssigned)
|
||||
};
|
||||
|
||||
ArmOperation<WeightsAndBiasesInstanceResource> operation = await instances
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-wandb-instance", data);
|
||||
|
||||
WeightsAndBiasesInstanceResource instance = operation.Value;
|
||||
|
||||
Console.WriteLine($"W&B Instance created: {instance.Data.Name}");
|
||||
Console.WriteLine($"Provisioning state: {instance.Data.Properties.ProvisioningState}");
|
||||
```
|
||||
|
||||
### 2. Get Existing Instance
|
||||
|
||||
```csharp
|
||||
WeightsAndBiasesInstanceResource instance = await resourceGroup
|
||||
.GetWeightsAndBiasesInstanceAsync("my-wandb-instance");
|
||||
|
||||
Console.WriteLine($"Instance: {instance.Data.Name}");
|
||||
Console.WriteLine($"Location: {instance.Data.Location}");
|
||||
Console.WriteLine($"State: {instance.Data.Properties.ProvisioningState}");
|
||||
|
||||
if (instance.Data.Properties.PartnerProperties != null)
|
||||
{
|
||||
Console.WriteLine($"Region: {instance.Data.Properties.PartnerProperties.Region}");
|
||||
Console.WriteLine($"Subdomain: {instance.Data.Properties.PartnerProperties.Subdomain}");
|
||||
}
|
||||
```
|
||||
|
||||
### 3. List All Instances
|
||||
|
||||
```csharp
|
||||
// List in resource group
|
||||
await foreach (WeightsAndBiasesInstanceResource instance in
|
||||
resourceGroup.GetWeightsAndBiasesInstances())
|
||||
{
|
||||
Console.WriteLine($"Instance: {instance.Data.Name}");
|
||||
Console.WriteLine($" Location: {instance.Data.Location}");
|
||||
Console.WriteLine($" State: {instance.Data.Properties.ProvisioningState}");
|
||||
}
|
||||
|
||||
// List in subscription
|
||||
SubscriptionResource subscription = await client.GetDefaultSubscriptionAsync();
|
||||
await foreach (WeightsAndBiasesInstanceResource instance in
|
||||
subscription.GetWeightsAndBiasesInstancesAsync())
|
||||
{
|
||||
Console.WriteLine($"{instance.Data.Name} in {instance.Id.ResourceGroupName}");
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Configure Single Sign-On (SSO)
|
||||
|
||||
```csharp
|
||||
WeightsAndBiasesInstanceResource instance = await resourceGroup
|
||||
.GetWeightsAndBiasesInstanceAsync("my-wandb-instance");
|
||||
|
||||
// Update with SSO configuration
|
||||
WeightsAndBiasesInstanceData updateData = instance.Data;
|
||||
|
||||
updateData.Properties.SingleSignOnPropertiesV2 = new WeightsAndBiasSingleSignOnPropertiesV2
|
||||
{
|
||||
Type = WeightsAndBiasSingleSignOnType.Saml,
|
||||
State = WeightsAndBiasSingleSignOnState.Enable,
|
||||
EnterpriseAppId = "<entra-app-id>",
|
||||
AadDomains = { "example.com", "contoso.com" }
|
||||
};
|
||||
|
||||
ArmOperation<WeightsAndBiasesInstanceResource> operation = await resourceGroup
|
||||
.GetWeightsAndBiasesInstances()
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-wandb-instance", updateData);
|
||||
```
|
||||
|
||||
### 5. Update Instance
|
||||
|
||||
```csharp
|
||||
WeightsAndBiasesInstanceResource instance = await resourceGroup
|
||||
.GetWeightsAndBiasesInstanceAsync("my-wandb-instance");
|
||||
|
||||
// Update tags
|
||||
WeightsAndBiasesInstancePatch patch = new WeightsAndBiasesInstancePatch
|
||||
{
|
||||
Tags =
|
||||
{
|
||||
{ "environment", "production" },
|
||||
{ "team", "ml-platform" },
|
||||
{ "costCenter", "CC-ML-001" }
|
||||
}
|
||||
};
|
||||
|
||||
instance = await instance.UpdateAsync(patch);
|
||||
Console.WriteLine($"Updated instance: {instance.Data.Name}");
|
||||
```
|
||||
|
||||
### 6. Delete Instance
|
||||
|
||||
```csharp
|
||||
WeightsAndBiasesInstanceResource instance = await resourceGroup
|
||||
.GetWeightsAndBiasesInstanceAsync("my-wandb-instance");
|
||||
|
||||
await instance.DeleteAsync(WaitUntil.Completed);
|
||||
Console.WriteLine("Instance deleted");
|
||||
```
|
||||
|
||||
### 7. Check Resource Name Availability
|
||||
|
||||
```csharp
|
||||
// Check if name is available before creating
|
||||
// (Implement via direct ARM call if SDK doesn't expose this)
|
||||
try
|
||||
{
|
||||
await resourceGroup.GetWeightsAndBiasesInstanceAsync("desired-name");
|
||||
Console.WriteLine("Name is already taken");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 404)
|
||||
{
|
||||
Console.WriteLine("Name is available");
|
||||
}
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `WeightsAndBiasesInstanceResource` | W&B instance resource |
|
||||
| `WeightsAndBiasesInstanceData` | Instance configuration data |
|
||||
| `WeightsAndBiasesInstanceCollection` | Collection of instances |
|
||||
| `WeightsAndBiasesInstanceProperties` | Instance properties |
|
||||
| `WeightsAndBiasesMarketplaceDetails` | Marketplace subscription info |
|
||||
| `WeightsAndBiasesOfferDetails` | Marketplace offer details |
|
||||
| `WeightsAndBiasesUserDetails` | Admin user information |
|
||||
| `WeightsAndBiasesPartnerProperties` | W&B-specific configuration |
|
||||
| `WeightsAndBiasSingleSignOnPropertiesV2` | SSO configuration |
|
||||
| `WeightsAndBiasesInstancePatch` | Patch for updates |
|
||||
| `WeightsAndBiasesRegion` | Supported regions enum |
|
||||
|
||||
## Available Regions
|
||||
|
||||
| Region Enum | Azure Region |
|
||||
|-------------|--------------|
|
||||
| `WeightsAndBiasesRegion.EastUS` | East US |
|
||||
| `WeightsAndBiasesRegion.CentralUS` | Central US |
|
||||
| `WeightsAndBiasesRegion.WestUS` | West US |
|
||||
| `WeightsAndBiasesRegion.WestEurope` | West Europe |
|
||||
| `WeightsAndBiasesRegion.JapanEast` | Japan East |
|
||||
| `WeightsAndBiasesRegion.KoreaCentral` | Korea Central |
|
||||
|
||||
## Marketplace Offer Details
|
||||
|
||||
For Azure Marketplace integration:
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Publisher ID | `wandb` |
|
||||
| Offer ID | `wandb-pay-as-you-go` |
|
||||
| Plan ID | `wandb-payg` (Pay As You Go) |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use DefaultAzureCredential** — Supports multiple auth methods automatically
|
||||
2. **Enable managed identity** — For secure access to other Azure resources
|
||||
3. **Configure SSO** — Enable Entra ID SSO for enterprise security
|
||||
4. **Tag resources** — Use tags for cost tracking and organization
|
||||
5. **Check provisioning state** — Wait for `Succeeded` before using instance
|
||||
6. **Use appropriate region** — Choose region closest to your compute
|
||||
7. **Monitor with Azure** — Use Azure Monitor for resource health
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
ArmOperation<WeightsAndBiasesInstanceResource> operation = await instances
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-wandb", data);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("Instance already exists or name conflict");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 400)
|
||||
{
|
||||
Console.WriteLine($"Invalid configuration: {ex.Message}");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Azure error: {ex.Status} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Integration with W&B SDK
|
||||
|
||||
After creating the Azure resource, use the W&B Python SDK for experiment tracking:
|
||||
|
||||
```python
|
||||
# Install: pip install wandb
|
||||
import wandb
|
||||
|
||||
# Login with your W&B API key from the Azure-deployed instance
|
||||
wandb.login(host="https://my-company-wandb.wandb.ai")
|
||||
|
||||
# Initialize a run
|
||||
run = wandb.init(project="my-ml-project")
|
||||
|
||||
# Log metrics
|
||||
wandb.log({"accuracy": 0.95, "loss": 0.05})
|
||||
|
||||
# Finish run
|
||||
run.finish()
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.ResourceManager.WeightsAndBiases` | W&B instance management (this SDK) | `dotnet add package Azure.ResourceManager.WeightsAndBiases --prerelease` |
|
||||
| `Azure.ResourceManager.MachineLearning` | Azure ML workspaces | `dotnet add package Azure.ResourceManager.MachineLearning` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.ResourceManager.WeightsAndBiases |
|
||||
| W&B Documentation | https://docs.wandb.ai/ |
|
||||
| Azure Marketplace | https://azuremarketplace.microsoft.com/marketplace/apps/wandb.wandb-pay-as-you-go |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/weightsandbiases |
|
||||
494
skills/official/microsoft/dotnet/general/maps/SKILL.md
Normal file
494
skills/official/microsoft/dotnet/general/maps/SKILL.md
Normal file
@@ -0,0 +1,494 @@
|
||||
---
|
||||
name: azure-maps-search-dotnet
|
||||
description: |
|
||||
Azure Maps SDK for .NET. Location-based services including geocoding, routing, rendering, geolocation, and weather. Use for address search, directions, map tiles, IP geolocation, and weather data. Triggers: "Azure Maps", "MapsSearchClient", "MapsRoutingClient", "MapsRenderingClient", "geocoding .NET", "route directions", "map tiles", "geolocation".
|
||||
package: Azure.Maps.Search
|
||||
---
|
||||
|
||||
# Azure Maps (.NET)
|
||||
|
||||
Azure Maps SDK for .NET providing location-based services: geocoding, routing, rendering, geolocation, and weather.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Search (geocoding, reverse geocoding)
|
||||
dotnet add package Azure.Maps.Search --prerelease
|
||||
|
||||
# Routing (directions, route matrix)
|
||||
dotnet add package Azure.Maps.Routing --prerelease
|
||||
|
||||
# Rendering (map tiles, static images)
|
||||
dotnet add package Azure.Maps.Rendering --prerelease
|
||||
|
||||
# Geolocation (IP to location)
|
||||
dotnet add package Azure.Maps.Geolocation --prerelease
|
||||
|
||||
# Weather
|
||||
dotnet add package Azure.Maps.Weather --prerelease
|
||||
|
||||
# Resource Management (account management, SAS tokens)
|
||||
dotnet add package Azure.ResourceManager.Maps --prerelease
|
||||
|
||||
# Required for authentication
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Versions**:
|
||||
- `Azure.Maps.Search`: v2.0.0-beta.5
|
||||
- `Azure.Maps.Routing`: v1.0.0-beta.4
|
||||
- `Azure.Maps.Rendering`: v2.0.0-beta.1
|
||||
- `Azure.Maps.Geolocation`: v1.0.0-beta.3
|
||||
- `Azure.ResourceManager.Maps`: v1.1.0-beta.2
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_MAPS_SUBSCRIPTION_KEY=<your-subscription-key>
|
||||
AZURE_MAPS_CLIENT_ID=<your-client-id> # For Entra ID auth
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### Subscription Key (Shared Key)
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
using Azure.Maps.Search;
|
||||
|
||||
var subscriptionKey = Environment.GetEnvironmentVariable("AZURE_MAPS_SUBSCRIPTION_KEY");
|
||||
var credential = new AzureKeyCredential(subscriptionKey);
|
||||
|
||||
var client = new MapsSearchClient(credential);
|
||||
```
|
||||
|
||||
### Microsoft Entra ID (Recommended for Production)
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Maps.Search;
|
||||
|
||||
var credential = new DefaultAzureCredential();
|
||||
var clientId = Environment.GetEnvironmentVariable("AZURE_MAPS_CLIENT_ID");
|
||||
|
||||
var client = new MapsSearchClient(credential, clientId);
|
||||
```
|
||||
|
||||
### Shared Access Signature (SAS)
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
using Azure.Core;
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.Maps;
|
||||
using Azure.ResourceManager.Maps.Models;
|
||||
using Azure.Maps.Search;
|
||||
|
||||
// Authenticate with Azure Resource Manager
|
||||
ArmClient armClient = new ArmClient(new DefaultAzureCredential());
|
||||
|
||||
// Get Maps account resource
|
||||
ResourceIdentifier mapsAccountResourceId = MapsAccountResource.CreateResourceIdentifier(
|
||||
subscriptionId, resourceGroupName, accountName);
|
||||
MapsAccountResource mapsAccount = armClient.GetMapsAccountResource(mapsAccountResourceId);
|
||||
|
||||
// Generate SAS token
|
||||
MapsAccountSasContent sasContent = new MapsAccountSasContent(
|
||||
MapsSigningKey.PrimaryKey,
|
||||
principalId,
|
||||
maxRatePerSecond: 500,
|
||||
start: DateTime.UtcNow.ToString("O"),
|
||||
expiry: DateTime.UtcNow.AddDays(1).ToString("O"));
|
||||
|
||||
Response<MapsAccountSasToken> sas = mapsAccount.GetSas(sasContent);
|
||||
|
||||
// Create client with SAS token
|
||||
var sasCredential = new AzureSasCredential(sas.Value.AccountSasToken);
|
||||
var client = new MapsSearchClient(sasCredential);
|
||||
```
|
||||
|
||||
## Client Hierarchy
|
||||
|
||||
```
|
||||
Azure.Maps.Search
|
||||
└── MapsSearchClient
|
||||
├── GetGeocoding() → Geocode addresses
|
||||
├── GetGeocodingBatch() → Batch geocoding
|
||||
├── GetReverseGeocoding() → Coordinates to address
|
||||
├── GetReverseGeocodingBatch() → Batch reverse geocoding
|
||||
└── GetPolygon() → Get boundary polygons
|
||||
|
||||
Azure.Maps.Routing
|
||||
└── MapsRoutingClient
|
||||
├── GetDirections() → Route directions
|
||||
├── GetImmediateRouteMatrix() → Route matrix (sync, ≤100)
|
||||
├── GetRouteMatrix() → Route matrix (async, ≤700)
|
||||
└── GetRouteRange() → Isochrone/reachable range
|
||||
|
||||
Azure.Maps.Rendering
|
||||
└── MapsRenderingClient
|
||||
├── GetMapTile() → Map tiles
|
||||
├── GetMapStaticImage() → Static map images
|
||||
└── GetCopyrightCaption() → Copyright info
|
||||
|
||||
Azure.Maps.Geolocation
|
||||
└── MapsGeolocationClient
|
||||
└── GetCountryCode() → IP to country/region
|
||||
|
||||
Azure.Maps.Weather
|
||||
└── MapsWeatherClient
|
||||
├── GetCurrentWeatherConditions() → Current weather
|
||||
├── GetDailyForecast() → Daily forecast
|
||||
├── GetHourlyForecast() → Hourly forecast
|
||||
└── GetSevereWeatherAlerts() → Weather alerts
|
||||
```
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Geocoding (Address to Coordinates)
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
using Azure.Maps.Search;
|
||||
|
||||
var credential = new AzureKeyCredential(subscriptionKey);
|
||||
var client = new MapsSearchClient(credential);
|
||||
|
||||
Response<GeocodingResponse> result = client.GetGeocoding("1 Microsoft Way, Redmond, WA 98052");
|
||||
|
||||
foreach (var feature in result.Value.Features)
|
||||
{
|
||||
Console.WriteLine($"Coordinates: {string.Join(",", feature.Geometry.Coordinates)}");
|
||||
Console.WriteLine($"Address: {feature.Properties.Address.FormattedAddress}");
|
||||
Console.WriteLine($"Confidence: {feature.Properties.Confidence}");
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Batch Geocoding
|
||||
|
||||
```csharp
|
||||
using Azure.Maps.Search.Models.Queries;
|
||||
|
||||
List<GeocodingQuery> queries = new List<GeocodingQuery>
|
||||
{
|
||||
new GeocodingQuery() { Query = "400 Broad St, Seattle, WA" },
|
||||
new GeocodingQuery() { Query = "1 Microsoft Way, Redmond, WA" },
|
||||
new GeocodingQuery() { AddressLine = "Space Needle", Top = 1 },
|
||||
};
|
||||
|
||||
Response<GeocodingBatchResponse> results = client.GetGeocodingBatch(queries);
|
||||
|
||||
foreach (var batchItem in results.Value.BatchItems)
|
||||
{
|
||||
foreach (var feature in batchItem.Features)
|
||||
{
|
||||
Console.WriteLine($"Coordinates: {string.Join(",", feature.Geometry.Coordinates)}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Reverse Geocoding (Coordinates to Address)
|
||||
|
||||
```csharp
|
||||
using Azure.Core.GeoJson;
|
||||
|
||||
GeoPosition coordinates = new GeoPosition(-122.138685, 47.6305637);
|
||||
Response<GeocodingResponse> result = client.GetReverseGeocoding(coordinates);
|
||||
|
||||
foreach (var feature in result.Value.Features)
|
||||
{
|
||||
Console.WriteLine($"Address: {feature.Properties.Address.FormattedAddress}");
|
||||
Console.WriteLine($"Locality: {feature.Properties.Address.Locality}");
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Get Boundary Polygon
|
||||
|
||||
```csharp
|
||||
using Azure.Maps.Search.Models;
|
||||
|
||||
GetPolygonOptions options = new GetPolygonOptions()
|
||||
{
|
||||
Coordinates = new GeoPosition(-122.204141, 47.61256),
|
||||
ResultType = BoundaryResultTypeEnum.Locality,
|
||||
Resolution = ResolutionEnum.Small,
|
||||
};
|
||||
|
||||
Response<Boundary> result = client.GetPolygon(options);
|
||||
|
||||
Console.WriteLine($"Boundary copyright: {result.Value.Properties?.Copyright}");
|
||||
Console.WriteLine($"Polygon count: {result.Value.Geometry.Count}");
|
||||
```
|
||||
|
||||
### 5. Route Directions
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
using Azure.Core.GeoJson;
|
||||
using Azure.Maps.Routing;
|
||||
using Azure.Maps.Routing.Models;
|
||||
|
||||
var client = new MapsRoutingClient(new AzureKeyCredential(subscriptionKey));
|
||||
|
||||
List<GeoPosition> routePoints = new List<GeoPosition>()
|
||||
{
|
||||
new GeoPosition(-122.34, 47.61), // Seattle
|
||||
new GeoPosition(-122.13, 47.64) // Redmond
|
||||
};
|
||||
|
||||
RouteDirectionQuery query = new RouteDirectionQuery(routePoints);
|
||||
Response<RouteDirections> result = client.GetDirections(query);
|
||||
|
||||
foreach (var route in result.Value.Routes)
|
||||
{
|
||||
Console.WriteLine($"Distance: {route.Summary.LengthInMeters} meters");
|
||||
Console.WriteLine($"Duration: {route.Summary.TravelTimeDuration}");
|
||||
|
||||
foreach (RouteLeg leg in route.Legs)
|
||||
{
|
||||
Console.WriteLine($"Leg points: {leg.Points.Count}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Route Directions with Options
|
||||
|
||||
```csharp
|
||||
RouteDirectionOptions options = new RouteDirectionOptions()
|
||||
{
|
||||
RouteType = RouteType.Fastest,
|
||||
UseTrafficData = true,
|
||||
TravelMode = TravelMode.Bicycle,
|
||||
Language = RoutingLanguage.EnglishUsa,
|
||||
InstructionsType = RouteInstructionsType.Text,
|
||||
};
|
||||
|
||||
RouteDirectionQuery query = new RouteDirectionQuery(routePoints)
|
||||
{
|
||||
RouteDirectionOptions = options
|
||||
};
|
||||
|
||||
Response<RouteDirections> result = client.GetDirections(query);
|
||||
```
|
||||
|
||||
### 7. Route Matrix
|
||||
|
||||
```csharp
|
||||
RouteMatrixQuery routeMatrixQuery = new RouteMatrixQuery
|
||||
{
|
||||
Origins = new List<GeoPosition>()
|
||||
{
|
||||
new GeoPosition(-122.34, 47.61),
|
||||
new GeoPosition(-122.13, 47.64)
|
||||
},
|
||||
Destinations = new List<GeoPosition>()
|
||||
{
|
||||
new GeoPosition(-122.20, 47.62),
|
||||
new GeoPosition(-122.40, 47.65)
|
||||
},
|
||||
};
|
||||
|
||||
// Synchronous (up to 100 route combinations)
|
||||
Response<RouteMatrixResult> result = client.GetImmediateRouteMatrix(routeMatrixQuery);
|
||||
|
||||
foreach (var cell in result.Value.Matrix.SelectMany(row => row))
|
||||
{
|
||||
Console.WriteLine($"Distance: {cell.Response?.RouteSummary?.LengthInMeters}");
|
||||
Console.WriteLine($"Duration: {cell.Response?.RouteSummary?.TravelTimeDuration}");
|
||||
}
|
||||
|
||||
// Asynchronous (up to 700 route combinations)
|
||||
RouteMatrixOptions routeMatrixOptions = new RouteMatrixOptions(routeMatrixQuery)
|
||||
{
|
||||
TravelTimeType = TravelTimeType.All,
|
||||
};
|
||||
GetRouteMatrixOperation asyncResult = client.GetRouteMatrix(WaitUntil.Completed, routeMatrixOptions);
|
||||
```
|
||||
|
||||
### 8. Route Range (Isochrone)
|
||||
|
||||
```csharp
|
||||
RouteRangeOptions options = new RouteRangeOptions(-122.34, 47.61)
|
||||
{
|
||||
TimeBudget = new TimeSpan(0, 20, 0) // 20 minutes
|
||||
};
|
||||
|
||||
Response<RouteRangeResult> result = client.GetRouteRange(options);
|
||||
|
||||
// result.Value.ReachableRange contains the polygon
|
||||
Console.WriteLine($"Boundary points: {result.Value.ReachableRange.Boundary.Count}");
|
||||
```
|
||||
|
||||
### 9. Get Map Tiles
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
using Azure.Maps.Rendering;
|
||||
|
||||
var client = new MapsRenderingClient(new AzureKeyCredential(subscriptionKey));
|
||||
|
||||
int zoom = 10;
|
||||
int tileSize = 256;
|
||||
|
||||
// Convert coordinates to tile index
|
||||
MapTileIndex tileIndex = MapsRenderingClient.PositionToTileXY(
|
||||
new GeoPosition(13.3854, 52.517), zoom, tileSize);
|
||||
|
||||
// Fetch map tile
|
||||
GetMapTileOptions options = new GetMapTileOptions(
|
||||
MapTileSetId.MicrosoftImagery,
|
||||
new MapTileIndex(tileIndex.X, tileIndex.Y, zoom)
|
||||
);
|
||||
|
||||
Response<Stream> mapTile = client.GetMapTile(options);
|
||||
|
||||
// Save to file
|
||||
using (FileStream fileStream = File.Create("./MapTile.png"))
|
||||
{
|
||||
mapTile.Value.CopyTo(fileStream);
|
||||
}
|
||||
```
|
||||
|
||||
### 10. IP Geolocation
|
||||
|
||||
```csharp
|
||||
using System.Net;
|
||||
using Azure;
|
||||
using Azure.Maps.Geolocation;
|
||||
|
||||
var client = new MapsGeolocationClient(new AzureKeyCredential(subscriptionKey));
|
||||
|
||||
IPAddress ipAddress = IPAddress.Parse("2001:4898:80e8:b::189");
|
||||
Response<CountryRegionResult> result = client.GetCountryCode(ipAddress);
|
||||
|
||||
Console.WriteLine($"Country ISO Code: {result.Value.IsoCode}");
|
||||
```
|
||||
|
||||
### 11. Current Weather
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
using Azure.Core.GeoJson;
|
||||
using Azure.Maps.Weather;
|
||||
|
||||
var client = new MapsWeatherClient(new AzureKeyCredential(subscriptionKey));
|
||||
|
||||
var position = new GeoPosition(-122.13071, 47.64011);
|
||||
var options = new GetCurrentWeatherConditionsOptions(position);
|
||||
|
||||
Response<CurrentConditionsResult> result = client.GetCurrentWeatherConditions(options);
|
||||
|
||||
foreach (var condition in result.Value.Results)
|
||||
{
|
||||
Console.WriteLine($"Temperature: {condition.Temperature.Value} {condition.Temperature.Unit}");
|
||||
Console.WriteLine($"Weather: {condition.Phrase}");
|
||||
Console.WriteLine($"Humidity: {condition.RelativeHumidity}%");
|
||||
}
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
### Search Package
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `MapsSearchClient` | Main client for search operations |
|
||||
| `GeocodingResponse` | Geocoding result |
|
||||
| `GeocodingBatchResponse` | Batch geocoding result |
|
||||
| `GeocodingQuery` | Query for batch geocoding |
|
||||
| `ReverseGeocodingQuery` | Query for batch reverse geocoding |
|
||||
| `GetPolygonOptions` | Options for polygon retrieval |
|
||||
| `Boundary` | Boundary polygon result |
|
||||
| `BoundaryResultTypeEnum` | Boundary type (Locality, AdminDistrict, etc.) |
|
||||
| `ResolutionEnum` | Polygon resolution (Small, Medium, Large) |
|
||||
|
||||
### Routing Package
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `MapsRoutingClient` | Main client for routing operations |
|
||||
| `RouteDirectionQuery` | Query for route directions |
|
||||
| `RouteDirectionOptions` | Route calculation options |
|
||||
| `RouteDirections` | Route directions result |
|
||||
| `RouteLeg` | Segment of a route |
|
||||
| `RouteMatrixQuery` | Query for route matrix |
|
||||
| `RouteMatrixResult` | Route matrix result |
|
||||
| `RouteRangeOptions` | Options for isochrone |
|
||||
| `RouteRangeResult` | Isochrone result |
|
||||
| `RouteType` | Route type (Fastest, Shortest, Eco, Thrilling) |
|
||||
| `TravelMode` | Travel mode (Car, Truck, Bicycle, Pedestrian) |
|
||||
|
||||
### Rendering Package
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `MapsRenderingClient` | Main client for rendering |
|
||||
| `GetMapTileOptions` | Map tile options |
|
||||
| `MapTileIndex` | Tile coordinates (X, Y, Zoom) |
|
||||
| `MapTileSetId` | Tile set identifier |
|
||||
|
||||
### Common Types
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `GeoPosition` | Geographic position (longitude, latitude) |
|
||||
| `GeoBoundingBox` | Bounding box for geographic area |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Entra ID for production** — Prefer over subscription keys
|
||||
2. **Batch operations** — Use batch geocoding for multiple addresses
|
||||
3. **Cache results** — Geocoding results don't change frequently
|
||||
4. **Use appropriate tile sizes** — 256 or 512 pixels based on display
|
||||
5. **Handle rate limits** — Implement exponential backoff
|
||||
6. **Use async route matrix** — For large matrix calculations (>100)
|
||||
7. **Consider traffic data** — Set `UseTrafficData = true` for accurate ETAs
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
Response<GeocodingResponse> result = client.GetGeocoding(address);
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Status: {ex.Status}");
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
|
||||
switch (ex.Status)
|
||||
{
|
||||
case 400:
|
||||
// Invalid request parameters
|
||||
break;
|
||||
case 401:
|
||||
// Authentication failed
|
||||
break;
|
||||
case 429:
|
||||
// Rate limited - implement backoff
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.Maps.Search` | Geocoding, search | `dotnet add package Azure.Maps.Search --prerelease` |
|
||||
| `Azure.Maps.Routing` | Directions, matrix | `dotnet add package Azure.Maps.Routing --prerelease` |
|
||||
| `Azure.Maps.Rendering` | Map tiles, images | `dotnet add package Azure.Maps.Rendering --prerelease` |
|
||||
| `Azure.Maps.Geolocation` | IP geolocation | `dotnet add package Azure.Maps.Geolocation --prerelease` |
|
||||
| `Azure.Maps.Weather` | Weather data | `dotnet add package Azure.Maps.Weather --prerelease` |
|
||||
| `Azure.ResourceManager.Maps` | Account management | `dotnet add package Azure.ResourceManager.Maps --prerelease` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| Azure Maps Documentation | https://learn.microsoft.com/azure/azure-maps/ |
|
||||
| Search API Reference | https://learn.microsoft.com/dotnet/api/azure.maps.search |
|
||||
| Routing API Reference | https://learn.microsoft.com/dotnet/api/azure.maps.routing |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/maps |
|
||||
| Pricing | https://azure.microsoft.com/pricing/details/azure-maps/ |
|
||||
411
skills/official/microsoft/dotnet/integration/apicenter/SKILL.md
Normal file
411
skills/official/microsoft/dotnet/integration/apicenter/SKILL.md
Normal file
@@ -0,0 +1,411 @@
|
||||
---
|
||||
name: azure-mgmt-apicenter-dotnet
|
||||
description: |
|
||||
Azure API Center SDK for .NET. Centralized API inventory management with governance, versioning, and discovery. Use for creating API services, workspaces, APIs, versions, definitions, environments, deployments, and metadata schemas. Triggers: "API Center", "ApiCenterService", "ApiCenterWorkspace", "ApiCenterApi", "API inventory", "API governance", "API versioning", "API catalog", "API discovery".
|
||||
package: Azure.ResourceManager.ApiCenter
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.ApiCenter (.NET)
|
||||
|
||||
Centralized API inventory and governance SDK for managing APIs across your organization.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.ApiCenter
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Version**: v1.0.0 (GA)
|
||||
**API Version**: 2024-03-01
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
AZURE_RESOURCE_GROUP=<your-resource-group>
|
||||
AZURE_APICENTER_SERVICE_NAME=<your-apicenter-service>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.ApiCenter;
|
||||
|
||||
ArmClient client = new ArmClient(new DefaultAzureCredential());
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
Subscription
|
||||
└── ResourceGroup
|
||||
└── ApiCenterService # API inventory service
|
||||
├── Workspace # Logical grouping of APIs
|
||||
│ ├── Api # API definition
|
||||
│ │ └── ApiVersion # Version of the API
|
||||
│ │ └── ApiDefinition # OpenAPI/GraphQL/etc specification
|
||||
│ ├── Environment # Deployment target (dev/staging/prod)
|
||||
│ └── Deployment # API deployed to environment
|
||||
└── MetadataSchema # Custom metadata definitions
|
||||
```
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Create API Center Service
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.ApiCenter;
|
||||
using Azure.ResourceManager.ApiCenter.Models;
|
||||
|
||||
ResourceGroupResource resourceGroup = await client
|
||||
.GetDefaultSubscriptionAsync()
|
||||
.Result
|
||||
.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
ApiCenterServiceCollection services = resourceGroup.GetApiCenterServices();
|
||||
|
||||
ApiCenterServiceData data = new ApiCenterServiceData(AzureLocation.EastUS)
|
||||
{
|
||||
Identity = new ManagedServiceIdentity(ManagedServiceIdentityType.SystemAssigned)
|
||||
};
|
||||
|
||||
ArmOperation<ApiCenterServiceResource> operation = await services
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-api-center", data);
|
||||
|
||||
ApiCenterServiceResource service = operation.Value;
|
||||
```
|
||||
|
||||
### 2. Create Workspace
|
||||
|
||||
```csharp
|
||||
ApiCenterWorkspaceCollection workspaces = service.GetApiCenterWorkspaces();
|
||||
|
||||
ApiCenterWorkspaceData workspaceData = new ApiCenterWorkspaceData
|
||||
{
|
||||
Title = "Engineering APIs",
|
||||
Description = "APIs owned by the engineering team"
|
||||
};
|
||||
|
||||
ArmOperation<ApiCenterWorkspaceResource> operation = await workspaces
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "engineering", workspaceData);
|
||||
|
||||
ApiCenterWorkspaceResource workspace = operation.Value;
|
||||
```
|
||||
|
||||
### 3. Create API
|
||||
|
||||
```csharp
|
||||
ApiCenterApiCollection apis = workspace.GetApiCenterApis();
|
||||
|
||||
ApiCenterApiData apiData = new ApiCenterApiData
|
||||
{
|
||||
Title = "Orders API",
|
||||
Description = "API for managing customer orders",
|
||||
Kind = ApiKind.Rest,
|
||||
LifecycleStage = ApiLifecycleStage.Production,
|
||||
TermsOfService = new ApiTermsOfService
|
||||
{
|
||||
Uri = new Uri("https://example.com/terms")
|
||||
},
|
||||
ExternalDocumentation =
|
||||
{
|
||||
new ApiExternalDocumentation
|
||||
{
|
||||
Title = "Documentation",
|
||||
Uri = new Uri("https://docs.example.com/orders")
|
||||
}
|
||||
},
|
||||
Contacts =
|
||||
{
|
||||
new ApiContact
|
||||
{
|
||||
Name = "API Support",
|
||||
Email = "api-support@example.com"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add custom metadata
|
||||
apiData.CustomProperties = BinaryData.FromObjectAsJson(new
|
||||
{
|
||||
team = "orders-team",
|
||||
costCenter = "CC-1234"
|
||||
});
|
||||
|
||||
ArmOperation<ApiCenterApiResource> operation = await apis
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "orders-api", apiData);
|
||||
|
||||
ApiCenterApiResource api = operation.Value;
|
||||
```
|
||||
|
||||
### 4. Create API Version
|
||||
|
||||
```csharp
|
||||
ApiCenterApiVersionCollection versions = api.GetApiCenterApiVersions();
|
||||
|
||||
ApiCenterApiVersionData versionData = new ApiCenterApiVersionData
|
||||
{
|
||||
Title = "v1.0.0",
|
||||
LifecycleStage = ApiLifecycleStage.Production
|
||||
};
|
||||
|
||||
ArmOperation<ApiCenterApiVersionResource> operation = await versions
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "v1-0-0", versionData);
|
||||
|
||||
ApiCenterApiVersionResource version = operation.Value;
|
||||
```
|
||||
|
||||
### 5. Create API Definition (Upload OpenAPI Spec)
|
||||
|
||||
```csharp
|
||||
ApiCenterApiDefinitionCollection definitions = version.GetApiCenterApiDefinitions();
|
||||
|
||||
ApiCenterApiDefinitionData definitionData = new ApiCenterApiDefinitionData
|
||||
{
|
||||
Title = "OpenAPI Specification",
|
||||
Description = "Orders API OpenAPI 3.0 definition"
|
||||
};
|
||||
|
||||
ArmOperation<ApiCenterApiDefinitionResource> operation = await definitions
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "openapi", definitionData);
|
||||
|
||||
ApiCenterApiDefinitionResource definition = operation.Value;
|
||||
|
||||
// Import specification
|
||||
string openApiSpec = await File.ReadAllTextAsync("orders-api.yaml");
|
||||
|
||||
ApiSpecImportContent importContent = new ApiSpecImportContent
|
||||
{
|
||||
Format = ApiSpecImportSourceFormat.Inline,
|
||||
Value = openApiSpec,
|
||||
Specification = new ApiSpecImportSpecification
|
||||
{
|
||||
Name = "openapi",
|
||||
Version = "3.0.1"
|
||||
}
|
||||
};
|
||||
|
||||
await definition.ImportSpecificationAsync(WaitUntil.Completed, importContent);
|
||||
```
|
||||
|
||||
### 6. Export API Specification
|
||||
|
||||
```csharp
|
||||
ApiCenterApiDefinitionResource definition = await client
|
||||
.GetApiCenterApiDefinitionResource(definitionResourceId)
|
||||
.GetAsync();
|
||||
|
||||
ArmOperation<ApiSpecExportResult> operation = await definition
|
||||
.ExportSpecificationAsync(WaitUntil.Completed);
|
||||
|
||||
ApiSpecExportResult result = operation.Value;
|
||||
|
||||
// result.Format - e.g., "inline"
|
||||
// result.Value - the specification content
|
||||
```
|
||||
|
||||
### 7. Create Environment
|
||||
|
||||
```csharp
|
||||
ApiCenterEnvironmentCollection environments = workspace.GetApiCenterEnvironments();
|
||||
|
||||
ApiCenterEnvironmentData envData = new ApiCenterEnvironmentData
|
||||
{
|
||||
Title = "Production",
|
||||
Description = "Production environment",
|
||||
Kind = ApiCenterEnvironmentKind.Production,
|
||||
Server = new ApiCenterEnvironmentServer
|
||||
{
|
||||
ManagementPortalUris = { new Uri("https://portal.azure.com") }
|
||||
},
|
||||
Onboarding = new EnvironmentOnboardingModel
|
||||
{
|
||||
Instructions = "Contact platform team for access",
|
||||
DeveloperPortalUris = { new Uri("https://developer.example.com") }
|
||||
}
|
||||
};
|
||||
|
||||
ArmOperation<ApiCenterEnvironmentResource> operation = await environments
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "production", envData);
|
||||
```
|
||||
|
||||
### 8. Create Deployment
|
||||
|
||||
```csharp
|
||||
ApiCenterDeploymentCollection deployments = workspace.GetApiCenterDeployments();
|
||||
|
||||
// Get environment resource ID
|
||||
ResourceIdentifier envResourceId = ApiCenterEnvironmentResource.CreateResourceIdentifier(
|
||||
subscriptionId, resourceGroupName, serviceName, workspaceName, "production");
|
||||
|
||||
// Get API definition resource ID
|
||||
ResourceIdentifier definitionResourceId = ApiCenterApiDefinitionResource.CreateResourceIdentifier(
|
||||
subscriptionId, resourceGroupName, serviceName, workspaceName,
|
||||
"orders-api", "v1-0-0", "openapi");
|
||||
|
||||
ApiCenterDeploymentData deploymentData = new ApiCenterDeploymentData
|
||||
{
|
||||
Title = "Orders API - Production",
|
||||
Description = "Production deployment of Orders API v1.0.0",
|
||||
EnvironmentId = envResourceId,
|
||||
DefinitionId = definitionResourceId,
|
||||
State = ApiCenterDeploymentState.Active,
|
||||
Server = new ApiCenterDeploymentServer
|
||||
{
|
||||
RuntimeUris = { new Uri("https://api.example.com/orders") }
|
||||
}
|
||||
};
|
||||
|
||||
ArmOperation<ApiCenterDeploymentResource> operation = await deployments
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "orders-api-prod", deploymentData);
|
||||
```
|
||||
|
||||
### 9. Create Metadata Schema
|
||||
|
||||
```csharp
|
||||
ApiCenterMetadataSchemaCollection schemas = service.GetApiCenterMetadataSchemas();
|
||||
|
||||
string jsonSchema = """
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"team": {
|
||||
"type": "string",
|
||||
"title": "Owning Team"
|
||||
},
|
||||
"costCenter": {
|
||||
"type": "string",
|
||||
"title": "Cost Center"
|
||||
},
|
||||
"dataClassification": {
|
||||
"type": "string",
|
||||
"enum": ["public", "internal", "confidential"],
|
||||
"title": "Data Classification"
|
||||
}
|
||||
},
|
||||
"required": ["team"]
|
||||
}
|
||||
""";
|
||||
|
||||
ApiCenterMetadataSchemaData schemaData = new ApiCenterMetadataSchemaData
|
||||
{
|
||||
Schema = jsonSchema,
|
||||
AssignedTo =
|
||||
{
|
||||
new MetadataAssignment
|
||||
{
|
||||
Entity = MetadataAssignmentEntity.Api,
|
||||
Required = true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ArmOperation<ApiCenterMetadataSchemaResource> operation = await schemas
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "api-metadata", schemaData);
|
||||
```
|
||||
|
||||
### 10. List and Search APIs
|
||||
|
||||
```csharp
|
||||
// List all APIs in a workspace
|
||||
ApiCenterWorkspaceResource workspace = await client
|
||||
.GetApiCenterWorkspaceResource(workspaceResourceId)
|
||||
.GetAsync();
|
||||
|
||||
await foreach (ApiCenterApiResource api in workspace.GetApiCenterApis())
|
||||
{
|
||||
Console.WriteLine($"API: {api.Data.Title}");
|
||||
Console.WriteLine($" Kind: {api.Data.Kind}");
|
||||
Console.WriteLine($" Stage: {api.Data.LifecycleStage}");
|
||||
|
||||
// List versions
|
||||
await foreach (ApiCenterApiVersionResource version in api.GetApiCenterApiVersions())
|
||||
{
|
||||
Console.WriteLine($" Version: {version.Data.Title}");
|
||||
}
|
||||
}
|
||||
|
||||
// List environments
|
||||
await foreach (ApiCenterEnvironmentResource env in workspace.GetApiCenterEnvironments())
|
||||
{
|
||||
Console.WriteLine($"Environment: {env.Data.Title} ({env.Data.Kind})");
|
||||
}
|
||||
|
||||
// List deployments
|
||||
await foreach (ApiCenterDeploymentResource deployment in workspace.GetApiCenterDeployments())
|
||||
{
|
||||
Console.WriteLine($"Deployment: {deployment.Data.Title}");
|
||||
Console.WriteLine($" State: {deployment.Data.State}");
|
||||
}
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ApiCenterServiceResource` | API Center service instance |
|
||||
| `ApiCenterWorkspaceResource` | Logical grouping of APIs |
|
||||
| `ApiCenterApiResource` | Individual API |
|
||||
| `ApiCenterApiVersionResource` | Version of an API |
|
||||
| `ApiCenterApiDefinitionResource` | API specification (OpenAPI, etc.) |
|
||||
| `ApiCenterEnvironmentResource` | Deployment environment |
|
||||
| `ApiCenterDeploymentResource` | API deployment to environment |
|
||||
| `ApiCenterMetadataSchemaResource` | Custom metadata schema |
|
||||
| `ApiKind` | rest, graphql, grpc, soap, webhook, websocket, mcp |
|
||||
| `ApiLifecycleStage` | design, development, testing, preview, production, deprecated, retired |
|
||||
| `ApiCenterEnvironmentKind` | development, testing, staging, production |
|
||||
| `ApiCenterDeploymentState` | active, inactive |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Organize with workspaces** — Group APIs by team, domain, or product
|
||||
2. **Use metadata schemas** — Define custom properties for governance
|
||||
3. **Track lifecycle stages** — Keep API status current (design → production → deprecated)
|
||||
4. **Document environments** — Include onboarding instructions and portal URIs
|
||||
5. **Version consistently** — Use semantic versioning for API versions
|
||||
6. **Import specifications** — Upload OpenAPI/GraphQL specs for discovery
|
||||
7. **Link deployments** — Connect APIs to their runtime environments
|
||||
8. **Use managed identity** — Enable SystemAssigned identity for secure integrations
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
ArmOperation<ApiCenterApiResource> operation = await apis
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-api", apiData);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("API already exists with conflicting configuration");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 400)
|
||||
{
|
||||
Console.WriteLine($"Invalid request: {ex.Message}");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Azure error: {ex.Status} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.ResourceManager.ApiCenter` | API Center management (this SDK) | `dotnet add package Azure.ResourceManager.ApiCenter` |
|
||||
| `Azure.ResourceManager.ApiManagement` | API gateway and policies | `dotnet add package Azure.ResourceManager.ApiManagement` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.ResourceManager.ApiCenter |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.resourcemanager.apicenter |
|
||||
| Product Documentation | https://learn.microsoft.com/azure/api-center/ |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/apicenter/Azure.ResourceManager.ApiCenter |
|
||||
@@ -0,0 +1,310 @@
|
||||
---
|
||||
name: azure-mgmt-apimanagement-dotnet
|
||||
description: |
|
||||
Azure Resource Manager SDK for API Management in .NET. Use for MANAGEMENT PLANE operations: creating/managing APIM services, APIs, products, subscriptions, policies, users, groups, gateways, and backends via Azure Resource Manager. Triggers: "API Management", "APIM service", "create APIM", "manage APIs", "ApiManagementServiceResource", "API policies", "APIM products", "APIM subscriptions".
|
||||
package: Azure.ResourceManager.ApiManagement
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.ApiManagement (.NET)
|
||||
|
||||
Management plane SDK for provisioning and managing Azure API Management resources via Azure Resource Manager.
|
||||
|
||||
> **⚠️ Management vs Data Plane**
|
||||
> - **This SDK (Azure.ResourceManager.ApiManagement)**: Create services, APIs, products, subscriptions, policies, users, groups
|
||||
> - **Data Plane**: Direct API calls to your APIM gateway endpoints
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.ApiManagement
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Version**: v1.3.0
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
# For service principal auth (optional)
|
||||
AZURE_TENANT_ID=<tenant-id>
|
||||
AZURE_CLIENT_ID=<client-id>
|
||||
AZURE_CLIENT_SECRET=<client-secret>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.ApiManagement;
|
||||
|
||||
// Always use DefaultAzureCredential
|
||||
var credential = new DefaultAzureCredential();
|
||||
var armClient = new ArmClient(credential);
|
||||
|
||||
// Get subscription
|
||||
var subscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
|
||||
var subscription = armClient.GetSubscriptionResource(
|
||||
new ResourceIdentifier($"/subscriptions/{subscriptionId}"));
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
ArmClient
|
||||
└── SubscriptionResource
|
||||
└── ResourceGroupResource
|
||||
└── ApiManagementServiceResource
|
||||
├── ApiResource
|
||||
│ ├── ApiOperationResource
|
||||
│ │ └── ApiOperationPolicyResource
|
||||
│ ├── ApiPolicyResource
|
||||
│ ├── ApiSchemaResource
|
||||
│ └── ApiDiagnosticResource
|
||||
├── ApiManagementProductResource
|
||||
│ ├── ProductApiResource
|
||||
│ ├── ProductGroupResource
|
||||
│ └── ProductPolicyResource
|
||||
├── ApiManagementSubscriptionResource
|
||||
├── ApiManagementPolicyResource
|
||||
├── ApiManagementUserResource
|
||||
├── ApiManagementGroupResource
|
||||
├── ApiManagementBackendResource
|
||||
├── ApiManagementGatewayResource
|
||||
├── ApiManagementCertificateResource
|
||||
├── ApiManagementNamedValueResource
|
||||
└── ApiManagementLoggerResource
|
||||
```
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### 1. Create API Management Service
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.ApiManagement;
|
||||
using Azure.ResourceManager.ApiManagement.Models;
|
||||
|
||||
// Get resource group
|
||||
var resourceGroup = await subscription
|
||||
.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
// Define service
|
||||
var serviceData = new ApiManagementServiceData(
|
||||
location: AzureLocation.EastUS,
|
||||
sku: new ApiManagementServiceSkuProperties(
|
||||
ApiManagementServiceSkuType.Developer,
|
||||
capacity: 1),
|
||||
publisherEmail: "admin@contoso.com",
|
||||
publisherName: "Contoso");
|
||||
|
||||
// Create service (long-running operation - can take 30+ minutes)
|
||||
var serviceCollection = resourceGroup.Value.GetApiManagementServices();
|
||||
var operation = await serviceCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-apim-service",
|
||||
serviceData);
|
||||
|
||||
ApiManagementServiceResource service = operation.Value;
|
||||
```
|
||||
|
||||
### 2. Create an API
|
||||
|
||||
```csharp
|
||||
var apiData = new ApiCreateOrUpdateContent
|
||||
{
|
||||
DisplayName = "My API",
|
||||
Path = "myapi",
|
||||
Protocols = { ApiOperationInvokableProtocol.Https },
|
||||
ServiceUri = new Uri("https://backend.contoso.com/api")
|
||||
};
|
||||
|
||||
var apiCollection = service.GetApis();
|
||||
var apiOperation = await apiCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-api",
|
||||
apiData);
|
||||
|
||||
ApiResource api = apiOperation.Value;
|
||||
```
|
||||
|
||||
### 3. Create a Product
|
||||
|
||||
```csharp
|
||||
var productData = new ApiManagementProductData
|
||||
{
|
||||
DisplayName = "Starter",
|
||||
Description = "Starter tier with limited access",
|
||||
IsSubscriptionRequired = true,
|
||||
IsApprovalRequired = false,
|
||||
SubscriptionsLimit = 1,
|
||||
State = ApiManagementProductState.Published
|
||||
};
|
||||
|
||||
var productCollection = service.GetApiManagementProducts();
|
||||
var productOperation = await productCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"starter",
|
||||
productData);
|
||||
|
||||
ApiManagementProductResource product = productOperation.Value;
|
||||
|
||||
// Add API to product
|
||||
await product.GetProductApis().CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-api");
|
||||
```
|
||||
|
||||
### 4. Create a Subscription
|
||||
|
||||
```csharp
|
||||
var subscriptionData = new ApiManagementSubscriptionCreateOrUpdateContent
|
||||
{
|
||||
DisplayName = "My Subscription",
|
||||
Scope = $"/products/{product.Data.Name}",
|
||||
State = ApiManagementSubscriptionState.Active
|
||||
};
|
||||
|
||||
var subscriptionCollection = service.GetApiManagementSubscriptions();
|
||||
var subOperation = await subscriptionCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-subscription",
|
||||
subscriptionData);
|
||||
|
||||
ApiManagementSubscriptionResource subscription = subOperation.Value;
|
||||
|
||||
// Get subscription keys
|
||||
var keys = await subscription.GetSecretsAsync();
|
||||
Console.WriteLine($"Primary Key: {keys.Value.PrimaryKey}");
|
||||
```
|
||||
|
||||
### 5. Set API Policy
|
||||
|
||||
```csharp
|
||||
var policyXml = @"
|
||||
<policies>
|
||||
<inbound>
|
||||
<rate-limit calls=""100"" renewal-period=""60"" />
|
||||
<set-header name=""X-Custom-Header"" exists-action=""override"">
|
||||
<value>CustomValue</value>
|
||||
</set-header>
|
||||
<base />
|
||||
</inbound>
|
||||
<backend>
|
||||
<base />
|
||||
</backend>
|
||||
<outbound>
|
||||
<base />
|
||||
</outbound>
|
||||
<on-error>
|
||||
<base />
|
||||
</on-error>
|
||||
</policies>";
|
||||
|
||||
var policyData = new PolicyContractData
|
||||
{
|
||||
Value = policyXml,
|
||||
Format = PolicyContentFormat.Xml
|
||||
};
|
||||
|
||||
await api.GetApiPolicy().CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
policyData);
|
||||
```
|
||||
|
||||
### 6. Backup and Restore
|
||||
|
||||
```csharp
|
||||
// Backup
|
||||
var backupParams = new ApiManagementServiceBackupRestoreContent(
|
||||
storageAccount: "mystorageaccount",
|
||||
containerName: "apim-backups",
|
||||
backupName: "backup-2024-01-15")
|
||||
{
|
||||
AccessType = StorageAccountAccessType.SystemAssignedManagedIdentity
|
||||
};
|
||||
|
||||
await service.BackupAsync(WaitUntil.Completed, backupParams);
|
||||
|
||||
// Restore
|
||||
await service.RestoreAsync(WaitUntil.Completed, backupParams);
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ArmClient` | Entry point for all ARM operations |
|
||||
| `ApiManagementServiceResource` | Represents an APIM service instance |
|
||||
| `ApiManagementServiceCollection` | Collection for service CRUD |
|
||||
| `ApiResource` | Represents an API |
|
||||
| `ApiManagementProductResource` | Represents a product |
|
||||
| `ApiManagementSubscriptionResource` | Represents a subscription |
|
||||
| `ApiManagementPolicyResource` | Service-level policy |
|
||||
| `ApiPolicyResource` | API-level policy |
|
||||
| `ApiManagementUserResource` | Represents a user |
|
||||
| `ApiManagementGroupResource` | Represents a group |
|
||||
| `ApiManagementBackendResource` | Represents a backend service |
|
||||
| `ApiManagementGatewayResource` | Represents a self-hosted gateway |
|
||||
|
||||
## SKU Types
|
||||
|
||||
| SKU | Purpose | Capacity |
|
||||
|-----|---------|----------|
|
||||
| `Developer` | Development/testing (no SLA) | 1 |
|
||||
| `Basic` | Entry-level production | 1-2 |
|
||||
| `Standard` | Medium workloads | 1-4 |
|
||||
| `Premium` | High availability, multi-region | 1-12 per region |
|
||||
| `Consumption` | Serverless, pay-per-call | N/A |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `WaitUntil.Completed`** for operations that must finish before proceeding
|
||||
2. **Use `WaitUntil.Started`** for long operations like service creation (30+ min)
|
||||
3. **Always use `DefaultAzureCredential`** — never hardcode keys
|
||||
4. **Handle `RequestFailedException`** for ARM API errors
|
||||
5. **Use `CreateOrUpdateAsync`** for idempotent operations
|
||||
6. **Navigate hierarchy** via `Get*` methods (e.g., `service.GetApis()`)
|
||||
7. **Policy format** — Use XML format for policies; JSON is also supported
|
||||
8. **Service creation** — Developer SKU is fastest for testing (~15-30 min)
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
var operation = await serviceCollection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed, serviceName, serviceData);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("Service already exists");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 400)
|
||||
{
|
||||
Console.WriteLine($"Bad request: {ex.Message}");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"ARM Error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Reference Files
|
||||
|
||||
| File | When to Read |
|
||||
|------|--------------|
|
||||
| [references/service-management.md](references/service-management.md) | Service CRUD, SKUs, networking, backup/restore |
|
||||
| [references/apis-operations.md](references/apis-operations.md) | APIs, operations, schemas, versioning |
|
||||
| [references/products-subscriptions.md](references/products-subscriptions.md) | Products, subscriptions, access control |
|
||||
| [references/policies.md](references/policies.md) | Policy XML patterns, scopes, common policies |
|
||||
|
||||
## Related Resources
|
||||
|
||||
| Resource | Purpose |
|
||||
|----------|---------|
|
||||
| [API Management Documentation](https://learn.microsoft.com/en-us/azure/api-management/) | Official Azure docs |
|
||||
| [Policy Reference](https://learn.microsoft.com/en-us/azure/api-management/api-management-policies) | Complete policy reference |
|
||||
| [SDK Reference](https://learn.microsoft.com/en-us/dotnet/api/azure.resourcemanager.apimanagement) | .NET API reference |
|
||||
290
skills/official/microsoft/dotnet/m365/m365-agents/SKILL.md
Normal file
290
skills/official/microsoft/dotnet/m365/m365-agents/SKILL.md
Normal file
@@ -0,0 +1,290 @@
|
||||
---
|
||||
name: m365-agents-dotnet
|
||||
description: |
|
||||
Microsoft 365 Agents SDK for .NET. Build multichannel agents for Teams/M365/Copilot Studio with ASP.NET Core hosting, AgentApplication routing, and MSAL-based auth. Triggers: "Microsoft 365 Agents SDK", "Microsoft.Agents", "AddAgentApplicationOptions", "AgentApplication", "AddAgentAspNetAuthentication", "Copilot Studio client", "IAgentHttpAdapter".
|
||||
package: Microsoft.Agents.Hosting.AspNetCore, Microsoft.Agents.Authentication.Msal, Microsoft.Agents.CopilotStudio.Client
|
||||
---
|
||||
|
||||
# Microsoft 365 Agents SDK (.NET)
|
||||
|
||||
## Overview
|
||||
Build enterprise agents for Microsoft 365, Teams, and Copilot Studio using the Microsoft.Agents SDK with ASP.NET Core hosting, agent routing, and MSAL-based authentication.
|
||||
|
||||
## Before implementation
|
||||
- Use the microsoft-docs MCP to verify the latest APIs for AddAgent, AgentApplication, and authentication options.
|
||||
- Confirm package versions in NuGet for the Microsoft.Agents.* packages you plan to use.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Microsoft.Agents.Hosting.AspNetCore
|
||||
dotnet add package Microsoft.Agents.Authentication.Msal
|
||||
dotnet add package Microsoft.Agents.Storage
|
||||
dotnet add package Microsoft.Agents.CopilotStudio.Client
|
||||
dotnet add package Microsoft.Identity.Client.Extensions.Msal
|
||||
```
|
||||
|
||||
## Configuration (appsettings.json)
|
||||
|
||||
```json
|
||||
{
|
||||
"TokenValidation": {
|
||||
"Enabled": true,
|
||||
"Audiences": [
|
||||
"{{ClientId}}"
|
||||
],
|
||||
"TenantId": "{{TenantId}}"
|
||||
},
|
||||
"AgentApplication": {
|
||||
"StartTypingTimer": false,
|
||||
"RemoveRecipientMention": false,
|
||||
"NormalizeMentions": false
|
||||
},
|
||||
"Connections": {
|
||||
"ServiceConnection": {
|
||||
"Settings": {
|
||||
"AuthType": "ClientSecret",
|
||||
"ClientId": "{{ClientId}}",
|
||||
"ClientSecret": "{{ClientSecret}}",
|
||||
"AuthorityEndpoint": "https://login.microsoftonline.com/{{TenantId}}",
|
||||
"Scopes": [
|
||||
"https://api.botframework.com/.default"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConnectionsMap": [
|
||||
{
|
||||
"ServiceUrl": "*",
|
||||
"Connection": "ServiceConnection"
|
||||
}
|
||||
],
|
||||
"CopilotStudioClientSettings": {
|
||||
"DirectConnectUrl": "",
|
||||
"EnvironmentId": "",
|
||||
"SchemaName": "",
|
||||
"TenantId": "",
|
||||
"AppClientId": "",
|
||||
"AppClientSecret": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Core Workflow: ASP.NET Core agent host
|
||||
|
||||
```csharp
|
||||
using Microsoft.Agents.Builder;
|
||||
using Microsoft.Agents.Hosting.AspNetCore;
|
||||
using Microsoft.Agents.Storage;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddHttpClient();
|
||||
builder.AddAgentApplicationOptions();
|
||||
builder.AddAgent<MyAgent>();
|
||||
builder.Services.AddSingleton<IStorage, MemoryStorage>();
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddAgentAspNetAuthentication(builder.Configuration);
|
||||
|
||||
WebApplication app = builder.Build();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapGet("/", () => "Microsoft Agents SDK Sample");
|
||||
|
||||
var incomingRoute = app.MapPost("/api/messages",
|
||||
async (HttpRequest request, HttpResponse response, IAgentHttpAdapter adapter, IAgent agent, CancellationToken ct) =>
|
||||
{
|
||||
await adapter.ProcessAsync(request, response, agent, ct);
|
||||
});
|
||||
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
incomingRoute.RequireAuthorization();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.Urls.Add("http://localhost:3978");
|
||||
}
|
||||
|
||||
app.Run();
|
||||
```
|
||||
|
||||
## AgentApplication routing
|
||||
|
||||
```csharp
|
||||
using Microsoft.Agents.Builder;
|
||||
using Microsoft.Agents.Builder.App;
|
||||
using Microsoft.Agents.Builder.State;
|
||||
using Microsoft.Agents.Core.Models;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public sealed class MyAgent : AgentApplication
|
||||
{
|
||||
public MyAgent(AgentApplicationOptions options) : base(options)
|
||||
{
|
||||
OnConversationUpdate(ConversationUpdateEvents.MembersAdded, WelcomeAsync);
|
||||
OnActivity(ActivityTypes.Message, OnMessageAsync, rank: RouteRank.Last);
|
||||
OnTurnError(OnTurnErrorAsync);
|
||||
}
|
||||
|
||||
private static async Task WelcomeAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken ct)
|
||||
{
|
||||
foreach (ChannelAccount member in turnContext.Activity.MembersAdded)
|
||||
{
|
||||
if (member.Id != turnContext.Activity.Recipient.Id)
|
||||
{
|
||||
await turnContext.SendActivityAsync(
|
||||
MessageFactory.Text("Welcome to the agent."),
|
||||
ct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken ct)
|
||||
{
|
||||
await turnContext.SendActivityAsync(
|
||||
MessageFactory.Text($"You said: {turnContext.Activity.Text}"),
|
||||
ct);
|
||||
}
|
||||
|
||||
private static async Task OnTurnErrorAsync(
|
||||
ITurnContext turnContext,
|
||||
ITurnState turnState,
|
||||
Exception exception,
|
||||
CancellationToken ct)
|
||||
{
|
||||
await turnState.Conversation.DeleteStateAsync(turnContext, ct);
|
||||
|
||||
var endOfConversation = Activity.CreateEndOfConversationActivity();
|
||||
endOfConversation.Code = EndOfConversationCodes.Error;
|
||||
endOfConversation.Text = exception.Message;
|
||||
await turnContext.SendActivityAsync(endOfConversation, ct);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Copilot Studio direct-to-engine client
|
||||
|
||||
### DelegatingHandler for token acquisition (interactive flow)
|
||||
|
||||
```csharp
|
||||
using System.Net.Http.Headers;
|
||||
using Microsoft.Agents.CopilotStudio.Client;
|
||||
using Microsoft.Identity.Client;
|
||||
|
||||
internal sealed class AddTokenHandler : DelegatingHandler
|
||||
{
|
||||
private readonly SampleConnectionSettings _settings;
|
||||
|
||||
public AddTokenHandler(SampleConnectionSettings settings) : base(new HttpClientHandler())
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(
|
||||
HttpRequestMessage request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.Headers.Authorization is null)
|
||||
{
|
||||
string[] scopes = [CopilotClient.ScopeFromSettings(_settings)];
|
||||
|
||||
IPublicClientApplication app = PublicClientApplicationBuilder
|
||||
.Create(_settings.AppClientId)
|
||||
.WithAuthority(AadAuthorityAudience.AzureAdMyOrg)
|
||||
.WithTenantId(_settings.TenantId)
|
||||
.WithRedirectUri("http://localhost")
|
||||
.Build();
|
||||
|
||||
AuthenticationResult authResponse;
|
||||
try
|
||||
{
|
||||
var account = (await app.GetAccountsAsync()).FirstOrDefault();
|
||||
authResponse = await app.AcquireTokenSilent(scopes, account).ExecuteAsync(cancellationToken);
|
||||
}
|
||||
catch (MsalUiRequiredException)
|
||||
{
|
||||
authResponse = await app.AcquireTokenInteractive(scopes).ExecuteAsync(cancellationToken);
|
||||
}
|
||||
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResponse.AccessToken);
|
||||
}
|
||||
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Console host with CopilotClient
|
||||
|
||||
```csharp
|
||||
using Microsoft.Agents.CopilotStudio.Client;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
|
||||
|
||||
var settings = new SampleConnectionSettings(
|
||||
builder.Configuration.GetSection("CopilotStudioClientSettings"));
|
||||
|
||||
builder.Services.AddHttpClient("mcs").ConfigurePrimaryHttpMessageHandler(() =>
|
||||
{
|
||||
return new AddTokenHandler(settings);
|
||||
});
|
||||
|
||||
builder.Services
|
||||
.AddSingleton(settings)
|
||||
.AddTransient<CopilotClient>(sp =>
|
||||
{
|
||||
var logger = sp.GetRequiredService<ILoggerFactory>().CreateLogger<CopilotClient>();
|
||||
return new CopilotClient(settings, sp.GetRequiredService<IHttpClientFactory>(), logger, "mcs");
|
||||
});
|
||||
|
||||
IHost host = builder.Build();
|
||||
var client = host.Services.GetRequiredService<CopilotClient>();
|
||||
|
||||
await foreach (var activity in client.StartConversationAsync(emitStartConversationEvent: true))
|
||||
{
|
||||
Console.WriteLine(activity.Type);
|
||||
}
|
||||
|
||||
await foreach (var activity in client.AskQuestionAsync("Hello!", null))
|
||||
{
|
||||
Console.WriteLine(activity.Type);
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Use AgentApplication subclasses to centralize routing and error handling.
|
||||
2. Use MemoryStorage only for development; use persisted storage in production.
|
||||
3. Enable TokenValidation in production and require authorization on /api/messages.
|
||||
4. Keep auth secrets in configuration providers (Key Vault, managed identity, env vars).
|
||||
5. Reuse HttpClient from IHttpClientFactory and cache MSAL tokens.
|
||||
6. Prefer async handlers and pass CancellationToken to SDK calls.
|
||||
|
||||
## Reference Files
|
||||
|
||||
| File | Contents |
|
||||
| --- | --- |
|
||||
| [references/acceptance-criteria.md](references/acceptance-criteria.md) | Import paths, hosting pipeline, Copilot Studio client patterns, anti-patterns |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
| --- | --- |
|
||||
| Microsoft 365 Agents SDK | https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/ |
|
||||
| AddAgent API | https://learn.microsoft.com/en-us/dotnet/api/microsoft.agents.hosting.aspnetcore.servicecollectionextensions.addagent?view=m365-agents-sdk |
|
||||
| AgentApplication API | https://learn.microsoft.com/en-us/dotnet/api/microsoft.agents.builder.app.agentapplication?view=m365-agents-sdk |
|
||||
| Auth configuration options | https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/microsoft-authentication-library-configuration-options |
|
||||
| Copilot Studio integration | https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/integrate-with-mcs |
|
||||
| GitHub samples | https://github.com/microsoft/agents |
|
||||
488
skills/official/microsoft/dotnet/messaging/eventgrid/SKILL.md
Normal file
488
skills/official/microsoft/dotnet/messaging/eventgrid/SKILL.md
Normal file
@@ -0,0 +1,488 @@
|
||||
---
|
||||
name: azure-eventgrid-dotnet
|
||||
description: |
|
||||
Azure Event Grid SDK for .NET. Client library for publishing and consuming events with Azure Event Grid. Use for event-driven architectures, pub/sub messaging, CloudEvents, and EventGridEvents. Triggers: "Event Grid", "EventGridPublisherClient", "CloudEvent", "EventGridEvent", "publish events .NET", "event-driven", "pub/sub".
|
||||
package: Azure.Messaging.EventGrid
|
||||
---
|
||||
|
||||
# Azure.Messaging.EventGrid (.NET)
|
||||
|
||||
Client library for publishing events to Azure Event Grid topics, domains, and namespaces.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# For topics and domains (push delivery)
|
||||
dotnet add package Azure.Messaging.EventGrid
|
||||
|
||||
# For namespaces (pull delivery)
|
||||
dotnet add package Azure.Messaging.EventGrid.Namespaces
|
||||
|
||||
# For CloudNative CloudEvents interop
|
||||
dotnet add package Microsoft.Azure.Messaging.EventGrid.CloudNativeCloudEvents
|
||||
```
|
||||
|
||||
**Current Version**: 4.28.0 (stable)
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
# Topic/Domain endpoint
|
||||
EVENT_GRID_TOPIC_ENDPOINT=https://<topic-name>.<region>.eventgrid.azure.net/api/events
|
||||
EVENT_GRID_TOPIC_KEY=<access-key>
|
||||
|
||||
# Namespace endpoint (for pull delivery)
|
||||
EVENT_GRID_NAMESPACE_ENDPOINT=https://<namespace>.<region>.eventgrid.azure.net
|
||||
EVENT_GRID_TOPIC_NAME=<topic-name>
|
||||
EVENT_GRID_SUBSCRIPTION_NAME=<subscription-name>
|
||||
```
|
||||
|
||||
## Client Hierarchy
|
||||
|
||||
```
|
||||
Push Delivery (Topics/Domains)
|
||||
└── EventGridPublisherClient
|
||||
├── SendEventAsync(EventGridEvent)
|
||||
├── SendEventsAsync(IEnumerable<EventGridEvent>)
|
||||
├── SendEventAsync(CloudEvent)
|
||||
└── SendEventsAsync(IEnumerable<CloudEvent>)
|
||||
|
||||
Pull Delivery (Namespaces)
|
||||
├── EventGridSenderClient
|
||||
│ └── SendAsync(CloudEvent)
|
||||
└── EventGridReceiverClient
|
||||
├── ReceiveAsync()
|
||||
├── AcknowledgeAsync()
|
||||
├── ReleaseAsync()
|
||||
└── RejectAsync()
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### API Key Authentication
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
using Azure.Messaging.EventGrid;
|
||||
|
||||
EventGridPublisherClient client = new(
|
||||
new Uri("https://mytopic.eastus-1.eventgrid.azure.net/api/events"),
|
||||
new AzureKeyCredential("<access-key>"));
|
||||
```
|
||||
|
||||
### Microsoft Entra ID (Recommended)
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Messaging.EventGrid;
|
||||
|
||||
EventGridPublisherClient client = new(
|
||||
new Uri("https://mytopic.eastus-1.eventgrid.azure.net/api/events"),
|
||||
new DefaultAzureCredential());
|
||||
```
|
||||
|
||||
### SAS Token Authentication
|
||||
|
||||
```csharp
|
||||
string sasToken = EventGridPublisherClient.BuildSharedAccessSignature(
|
||||
new Uri(topicEndpoint),
|
||||
DateTimeOffset.UtcNow.AddHours(1),
|
||||
new AzureKeyCredential(topicKey));
|
||||
|
||||
var sasCredential = new AzureSasCredential(sasToken);
|
||||
EventGridPublisherClient client = new(
|
||||
new Uri(topicEndpoint),
|
||||
sasCredential);
|
||||
```
|
||||
|
||||
## Publishing Events
|
||||
|
||||
### EventGridEvent Schema
|
||||
|
||||
```csharp
|
||||
EventGridPublisherClient client = new(
|
||||
new Uri(topicEndpoint),
|
||||
new AzureKeyCredential(topicKey));
|
||||
|
||||
// Single event
|
||||
EventGridEvent egEvent = new(
|
||||
subject: "orders/12345",
|
||||
eventType: "Order.Created",
|
||||
dataVersion: "1.0",
|
||||
data: new { OrderId = "12345", Amount = 99.99 });
|
||||
|
||||
await client.SendEventAsync(egEvent);
|
||||
|
||||
// Batch of events
|
||||
List<EventGridEvent> events = new()
|
||||
{
|
||||
new EventGridEvent(
|
||||
subject: "orders/12345",
|
||||
eventType: "Order.Created",
|
||||
dataVersion: "1.0",
|
||||
data: new OrderData { OrderId = "12345", Amount = 99.99 }),
|
||||
new EventGridEvent(
|
||||
subject: "orders/12346",
|
||||
eventType: "Order.Created",
|
||||
dataVersion: "1.0",
|
||||
data: new OrderData { OrderId = "12346", Amount = 149.99 })
|
||||
};
|
||||
|
||||
await client.SendEventsAsync(events);
|
||||
```
|
||||
|
||||
### CloudEvent Schema
|
||||
|
||||
```csharp
|
||||
CloudEvent cloudEvent = new(
|
||||
source: "/orders/system",
|
||||
type: "Order.Created",
|
||||
data: new { OrderId = "12345", Amount = 99.99 });
|
||||
|
||||
cloudEvent.Subject = "orders/12345";
|
||||
cloudEvent.Id = Guid.NewGuid().ToString();
|
||||
cloudEvent.Time = DateTimeOffset.UtcNow;
|
||||
|
||||
await client.SendEventAsync(cloudEvent);
|
||||
|
||||
// Batch of CloudEvents
|
||||
List<CloudEvent> cloudEvents = new()
|
||||
{
|
||||
new CloudEvent("/orders", "Order.Created", new { OrderId = "1" }),
|
||||
new CloudEvent("/orders", "Order.Updated", new { OrderId = "2" })
|
||||
};
|
||||
|
||||
await client.SendEventsAsync(cloudEvents);
|
||||
```
|
||||
|
||||
### Publishing to Event Grid Domain
|
||||
|
||||
```csharp
|
||||
// Events must specify the Topic property for domain routing
|
||||
List<EventGridEvent> events = new()
|
||||
{
|
||||
new EventGridEvent(
|
||||
subject: "orders/12345",
|
||||
eventType: "Order.Created",
|
||||
dataVersion: "1.0",
|
||||
data: new { OrderId = "12345" })
|
||||
{
|
||||
Topic = "orders-topic" // Domain topic name
|
||||
},
|
||||
new EventGridEvent(
|
||||
subject: "inventory/item-1",
|
||||
eventType: "Inventory.Updated",
|
||||
dataVersion: "1.0",
|
||||
data: new { ItemId = "item-1" })
|
||||
{
|
||||
Topic = "inventory-topic"
|
||||
}
|
||||
};
|
||||
|
||||
await client.SendEventsAsync(events);
|
||||
```
|
||||
|
||||
### Custom Serialization
|
||||
|
||||
```csharp
|
||||
using System.Text.Json;
|
||||
|
||||
var serializerOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
var customSerializer = new JsonObjectSerializer(serializerOptions);
|
||||
|
||||
EventGridEvent egEvent = new(
|
||||
subject: "orders/12345",
|
||||
eventType: "Order.Created",
|
||||
dataVersion: "1.0",
|
||||
data: customSerializer.Serialize(new OrderData { OrderId = "12345" }));
|
||||
|
||||
await client.SendEventAsync(egEvent);
|
||||
```
|
||||
|
||||
## Pull Delivery (Namespaces)
|
||||
|
||||
### Send Events to Namespace Topic
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
using Azure.Messaging;
|
||||
using Azure.Messaging.EventGrid.Namespaces;
|
||||
|
||||
var senderClient = new EventGridSenderClient(
|
||||
new Uri(namespaceEndpoint),
|
||||
topicName,
|
||||
new AzureKeyCredential(topicKey));
|
||||
|
||||
// Send single event
|
||||
CloudEvent cloudEvent = new("employee_source", "Employee.Created",
|
||||
new { Name = "John", Age = 30 });
|
||||
await senderClient.SendAsync(cloudEvent);
|
||||
|
||||
// Send batch
|
||||
await senderClient.SendAsync(new[]
|
||||
{
|
||||
new CloudEvent("source", "type", new { Name = "Alice" }),
|
||||
new CloudEvent("source", "type", new { Name = "Bob" })
|
||||
});
|
||||
```
|
||||
|
||||
### Receive and Process Events
|
||||
|
||||
```csharp
|
||||
var receiverClient = new EventGridReceiverClient(
|
||||
new Uri(namespaceEndpoint),
|
||||
topicName,
|
||||
subscriptionName,
|
||||
new AzureKeyCredential(topicKey));
|
||||
|
||||
// Receive events
|
||||
ReceiveResult result = await receiverClient.ReceiveAsync(maxEvents: 10);
|
||||
|
||||
List<string> lockTokensToAck = new();
|
||||
List<string> lockTokensToRelease = new();
|
||||
|
||||
foreach (ReceiveDetails detail in result.Details)
|
||||
{
|
||||
CloudEvent cloudEvent = detail.Event;
|
||||
string lockToken = detail.BrokerProperties.LockToken;
|
||||
|
||||
try
|
||||
{
|
||||
// Process the event
|
||||
Console.WriteLine($"Event: {cloudEvent.Type}, Data: {cloudEvent.Data}");
|
||||
lockTokensToAck.Add(lockToken);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Release for retry
|
||||
lockTokensToRelease.Add(lockToken);
|
||||
}
|
||||
}
|
||||
|
||||
// Acknowledge successfully processed events
|
||||
if (lockTokensToAck.Any())
|
||||
{
|
||||
await receiverClient.AcknowledgeAsync(lockTokensToAck);
|
||||
}
|
||||
|
||||
// Release events for retry
|
||||
if (lockTokensToRelease.Any())
|
||||
{
|
||||
await receiverClient.ReleaseAsync(lockTokensToRelease);
|
||||
}
|
||||
```
|
||||
|
||||
### Reject Events (Dead Letter)
|
||||
|
||||
```csharp
|
||||
// Reject events that cannot be processed
|
||||
await receiverClient.RejectAsync(new[] { lockToken });
|
||||
```
|
||||
|
||||
## Consuming Events (Azure Functions)
|
||||
|
||||
### EventGridEvent Trigger
|
||||
|
||||
```csharp
|
||||
using Azure.Messaging.EventGrid;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
|
||||
|
||||
public static class EventGridFunction
|
||||
{
|
||||
[FunctionName("ProcessEventGridEvent")]
|
||||
public static void Run(
|
||||
[EventGridTrigger] EventGridEvent eventGridEvent,
|
||||
ILogger log)
|
||||
{
|
||||
log.LogInformation($"Event Type: {eventGridEvent.EventType}");
|
||||
log.LogInformation($"Subject: {eventGridEvent.Subject}");
|
||||
log.LogInformation($"Data: {eventGridEvent.Data}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### CloudEvent Trigger
|
||||
|
||||
```csharp
|
||||
using Azure.Messaging;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
|
||||
public class CloudEventFunction
|
||||
{
|
||||
[Function("ProcessCloudEvent")]
|
||||
public void Run(
|
||||
[EventGridTrigger] CloudEvent cloudEvent,
|
||||
FunctionContext context)
|
||||
{
|
||||
var logger = context.GetLogger("ProcessCloudEvent");
|
||||
logger.LogInformation($"Event Type: {cloudEvent.Type}");
|
||||
logger.LogInformation($"Source: {cloudEvent.Source}");
|
||||
logger.LogInformation($"Data: {cloudEvent.Data}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Parsing Events
|
||||
|
||||
### Parse EventGridEvent
|
||||
|
||||
```csharp
|
||||
// From JSON string
|
||||
string json = "..."; // Event Grid webhook payload
|
||||
EventGridEvent[] events = EventGridEvent.ParseMany(BinaryData.FromString(json));
|
||||
|
||||
foreach (EventGridEvent egEvent in events)
|
||||
{
|
||||
if (egEvent.TryGetSystemEventData(out object systemEvent))
|
||||
{
|
||||
// Handle system event
|
||||
switch (systemEvent)
|
||||
{
|
||||
case StorageBlobCreatedEventData blobCreated:
|
||||
Console.WriteLine($"Blob created: {blobCreated.Url}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle custom event
|
||||
var customData = egEvent.Data.ToObjectFromJson<MyCustomData>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Parse CloudEvent
|
||||
|
||||
```csharp
|
||||
CloudEvent[] cloudEvents = CloudEvent.ParseMany(BinaryData.FromString(json));
|
||||
|
||||
foreach (CloudEvent cloudEvent in cloudEvents)
|
||||
{
|
||||
var data = cloudEvent.Data.ToObjectFromJson<MyEventData>();
|
||||
Console.WriteLine($"Type: {cloudEvent.Type}, Data: {data}");
|
||||
}
|
||||
```
|
||||
|
||||
## System Events
|
||||
|
||||
```csharp
|
||||
// Common system event types
|
||||
using Azure.Messaging.EventGrid.SystemEvents;
|
||||
|
||||
// Storage events
|
||||
StorageBlobCreatedEventData blobCreated;
|
||||
StorageBlobDeletedEventData blobDeleted;
|
||||
|
||||
// Resource events
|
||||
ResourceWriteSuccessEventData resourceCreated;
|
||||
ResourceDeleteSuccessEventData resourceDeleted;
|
||||
|
||||
// App Service events
|
||||
WebAppUpdatedEventData webAppUpdated;
|
||||
|
||||
// Container Registry events
|
||||
ContainerRegistryImagePushedEventData imagePushed;
|
||||
|
||||
// IoT Hub events
|
||||
IotHubDeviceCreatedEventData deviceCreated;
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `EventGridPublisherClient` | Publish to topics/domains |
|
||||
| `EventGridSenderClient` | Send to namespace topics |
|
||||
| `EventGridReceiverClient` | Receive from namespace subscriptions |
|
||||
| `EventGridEvent` | Event Grid native schema |
|
||||
| `CloudEvent` | CloudEvents 1.0 schema |
|
||||
| `ReceiveResult` | Pull delivery response |
|
||||
| `ReceiveDetails` | Event with broker properties |
|
||||
| `BrokerProperties` | Lock token, delivery count |
|
||||
|
||||
## Event Schemas Comparison
|
||||
|
||||
| Feature | EventGridEvent | CloudEvent |
|
||||
|---------|----------------|------------|
|
||||
| Standard | Azure-specific | CNCF standard |
|
||||
| Required fields | subject, eventType, dataVersion, data | source, type |
|
||||
| Extensibility | Limited | Extension attributes |
|
||||
| Interoperability | Azure only | Cross-platform |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use CloudEvents** — Prefer CloudEvents for new implementations (industry standard)
|
||||
2. **Batch events** — Send multiple events in one call for efficiency
|
||||
3. **Use Entra ID** — Prefer managed identity over access keys
|
||||
4. **Idempotent handlers** — Events may be delivered more than once
|
||||
5. **Set event TTL** — Configure time-to-live for namespace events
|
||||
6. **Handle partial failures** — Acknowledge/release events individually
|
||||
7. **Use dead-letter** — Configure dead-letter for failed events
|
||||
8. **Validate schemas** — Validate event data before processing
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
await client.SendEventAsync(cloudEvent);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 401)
|
||||
{
|
||||
Console.WriteLine("Authentication failed - check credentials");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 403)
|
||||
{
|
||||
Console.WriteLine("Authorization failed - check RBAC permissions");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 413)
|
||||
{
|
||||
Console.WriteLine("Payload too large - max 1MB per event, 1MB total batch");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Event Grid error: {ex.Status} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Failover Pattern
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
var primaryClient = new EventGridPublisherClient(primaryUri, primaryKey);
|
||||
await primaryClient.SendEventsAsync(events);
|
||||
}
|
||||
catch (RequestFailedException)
|
||||
{
|
||||
// Failover to secondary region
|
||||
var secondaryClient = new EventGridPublisherClient(secondaryUri, secondaryKey);
|
||||
await secondaryClient.SendEventsAsync(events);
|
||||
}
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.Messaging.EventGrid` | Topics/Domains (this SDK) | `dotnet add package Azure.Messaging.EventGrid` |
|
||||
| `Azure.Messaging.EventGrid.Namespaces` | Pull delivery | `dotnet add package Azure.Messaging.EventGrid.Namespaces` |
|
||||
| `Azure.Identity` | Authentication | `dotnet add package Azure.Identity` |
|
||||
| `Microsoft.Azure.WebJobs.Extensions.EventGrid` | Azure Functions trigger | `dotnet add package Microsoft.Azure.WebJobs.Extensions.EventGrid` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.Messaging.EventGrid |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.messaging.eventgrid |
|
||||
| Quickstart | https://learn.microsoft.com/azure/event-grid/custom-event-quickstart |
|
||||
| Pull Delivery | https://learn.microsoft.com/azure/event-grid/pull-delivery-overview |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/eventgrid/Azure.Messaging.EventGrid |
|
||||
362
skills/official/microsoft/dotnet/messaging/eventhubs/SKILL.md
Normal file
362
skills/official/microsoft/dotnet/messaging/eventhubs/SKILL.md
Normal file
@@ -0,0 +1,362 @@
|
||||
---
|
||||
name: azure-eventhub-dotnet
|
||||
description: |
|
||||
Azure Event Hubs SDK for .NET. Use for high-throughput event streaming: sending events (EventHubProducerClient, EventHubBufferedProducerClient), receiving events (EventProcessorClient with checkpointing), partition management, and real-time data ingestion. Triggers: "Event Hubs", "event streaming", "EventHubProducerClient", "EventProcessorClient", "send events", "receive events", "checkpointing", "partition".
|
||||
package: Azure.Messaging.EventHubs
|
||||
---
|
||||
|
||||
# Azure.Messaging.EventHubs (.NET)
|
||||
|
||||
High-throughput event streaming SDK for sending and receiving events via Azure Event Hubs.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Core package (sending and simple receiving)
|
||||
dotnet add package Azure.Messaging.EventHubs
|
||||
|
||||
# Processor package (production receiving with checkpointing)
|
||||
dotnet add package Azure.Messaging.EventHubs.Processor
|
||||
|
||||
# Authentication
|
||||
dotnet add package Azure.Identity
|
||||
|
||||
# For checkpointing (required by EventProcessorClient)
|
||||
dotnet add package Azure.Storage.Blobs
|
||||
```
|
||||
|
||||
**Current Versions**: Azure.Messaging.EventHubs v5.12.2, Azure.Messaging.EventHubs.Processor v5.12.2
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
EVENTHUB_FULLY_QUALIFIED_NAMESPACE=<namespace>.servicebus.windows.net
|
||||
EVENTHUB_NAME=<event-hub-name>
|
||||
|
||||
# For checkpointing (EventProcessorClient)
|
||||
BLOB_STORAGE_CONNECTION_STRING=<storage-connection-string>
|
||||
BLOB_CONTAINER_NAME=<checkpoint-container>
|
||||
|
||||
# Alternative: Connection string auth (not recommended for production)
|
||||
EVENTHUB_CONNECTION_STRING=Endpoint=sb://<namespace>.servicebus.windows.net/;SharedAccessKeyName=...
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Messaging.EventHubs;
|
||||
using Azure.Messaging.EventHubs.Producer;
|
||||
|
||||
// Always use DefaultAzureCredential for production
|
||||
var credential = new DefaultAzureCredential();
|
||||
|
||||
var fullyQualifiedNamespace = Environment.GetEnvironmentVariable("EVENTHUB_FULLY_QUALIFIED_NAMESPACE");
|
||||
var eventHubName = Environment.GetEnvironmentVariable("EVENTHUB_NAME");
|
||||
|
||||
var producer = new EventHubProducerClient(
|
||||
fullyQualifiedNamespace,
|
||||
eventHubName,
|
||||
credential);
|
||||
```
|
||||
|
||||
**Required RBAC Roles**:
|
||||
- **Sending**: `Azure Event Hubs Data Sender`
|
||||
- **Receiving**: `Azure Event Hubs Data Receiver`
|
||||
- **Both**: `Azure Event Hubs Data Owner`
|
||||
|
||||
## Client Types
|
||||
|
||||
| Client | Purpose | When to Use |
|
||||
|--------|---------|-------------|
|
||||
| `EventHubProducerClient` | Send events immediately in batches | Real-time sending, full control over batching |
|
||||
| `EventHubBufferedProducerClient` | Automatic batching with background sending | High-volume, fire-and-forget scenarios |
|
||||
| `EventHubConsumerClient` | Simple event reading | Prototyping only, NOT for production |
|
||||
| `EventProcessorClient` | Production event processing | **Always use this for receiving in production** |
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### 1. Send Events (Batch)
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Messaging.EventHubs;
|
||||
using Azure.Messaging.EventHubs.Producer;
|
||||
|
||||
await using var producer = new EventHubProducerClient(
|
||||
fullyQualifiedNamespace,
|
||||
eventHubName,
|
||||
new DefaultAzureCredential());
|
||||
|
||||
// Create a batch (respects size limits automatically)
|
||||
using EventDataBatch batch = await producer.CreateBatchAsync();
|
||||
|
||||
// Add events to batch
|
||||
var events = new[]
|
||||
{
|
||||
new EventData(BinaryData.FromString("{\"id\": 1, \"message\": \"Hello\"}")),
|
||||
new EventData(BinaryData.FromString("{\"id\": 2, \"message\": \"World\"}"))
|
||||
};
|
||||
|
||||
foreach (var eventData in events)
|
||||
{
|
||||
if (!batch.TryAdd(eventData))
|
||||
{
|
||||
// Batch is full - send it and create a new one
|
||||
await producer.SendAsync(batch);
|
||||
batch = await producer.CreateBatchAsync();
|
||||
|
||||
if (!batch.TryAdd(eventData))
|
||||
{
|
||||
throw new Exception("Event too large for empty batch");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send remaining events
|
||||
if (batch.Count > 0)
|
||||
{
|
||||
await producer.SendAsync(batch);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Send Events (Buffered - High Volume)
|
||||
|
||||
```csharp
|
||||
using Azure.Messaging.EventHubs.Producer;
|
||||
|
||||
var options = new EventHubBufferedProducerClientOptions
|
||||
{
|
||||
MaximumWaitTime = TimeSpan.FromSeconds(1)
|
||||
};
|
||||
|
||||
await using var producer = new EventHubBufferedProducerClient(
|
||||
fullyQualifiedNamespace,
|
||||
eventHubName,
|
||||
new DefaultAzureCredential(),
|
||||
options);
|
||||
|
||||
// Handle send success/failure
|
||||
producer.SendEventBatchSucceededAsync += args =>
|
||||
{
|
||||
Console.WriteLine($"Batch sent: {args.EventBatch.Count} events");
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
producer.SendEventBatchFailedAsync += args =>
|
||||
{
|
||||
Console.WriteLine($"Batch failed: {args.Exception.Message}");
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
// Enqueue events (sent automatically in background)
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
await producer.EnqueueEventAsync(new EventData($"Event {i}"));
|
||||
}
|
||||
|
||||
// Flush remaining events before disposing
|
||||
await producer.FlushAsync();
|
||||
```
|
||||
|
||||
### 3. Receive Events (Production - EventProcessorClient)
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Messaging.EventHubs;
|
||||
using Azure.Messaging.EventHubs.Consumer;
|
||||
using Azure.Messaging.EventHubs.Processor;
|
||||
using Azure.Storage.Blobs;
|
||||
|
||||
// Blob container for checkpointing
|
||||
var blobClient = new BlobContainerClient(
|
||||
Environment.GetEnvironmentVariable("BLOB_STORAGE_CONNECTION_STRING"),
|
||||
Environment.GetEnvironmentVariable("BLOB_CONTAINER_NAME"));
|
||||
|
||||
await blobClient.CreateIfNotExistsAsync();
|
||||
|
||||
// Create processor
|
||||
var processor = new EventProcessorClient(
|
||||
blobClient,
|
||||
EventHubConsumerClient.DefaultConsumerGroup,
|
||||
fullyQualifiedNamespace,
|
||||
eventHubName,
|
||||
new DefaultAzureCredential());
|
||||
|
||||
// Handle events
|
||||
processor.ProcessEventAsync += async args =>
|
||||
{
|
||||
Console.WriteLine($"Partition: {args.Partition.PartitionId}");
|
||||
Console.WriteLine($"Data: {args.Data.EventBody}");
|
||||
|
||||
// Checkpoint after processing (or batch checkpoints)
|
||||
await args.UpdateCheckpointAsync();
|
||||
};
|
||||
|
||||
// Handle errors
|
||||
processor.ProcessErrorAsync += args =>
|
||||
{
|
||||
Console.WriteLine($"Error: {args.Exception.Message}");
|
||||
Console.WriteLine($"Partition: {args.PartitionId}");
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
// Start processing
|
||||
await processor.StartProcessingAsync();
|
||||
|
||||
// Run until cancelled
|
||||
await Task.Delay(Timeout.Infinite, cancellationToken);
|
||||
|
||||
// Stop gracefully
|
||||
await processor.StopProcessingAsync();
|
||||
```
|
||||
|
||||
### 4. Partition Operations
|
||||
|
||||
```csharp
|
||||
// Get partition IDs
|
||||
string[] partitionIds = await producer.GetPartitionIdsAsync();
|
||||
|
||||
// Send to specific partition (use sparingly)
|
||||
var options = new SendEventOptions
|
||||
{
|
||||
PartitionId = "0"
|
||||
};
|
||||
await producer.SendAsync(events, options);
|
||||
|
||||
// Use partition key (recommended for ordering)
|
||||
var batchOptions = new CreateBatchOptions
|
||||
{
|
||||
PartitionKey = "customer-123" // Events with same key go to same partition
|
||||
};
|
||||
using var batch = await producer.CreateBatchAsync(batchOptions);
|
||||
```
|
||||
|
||||
## EventPosition Options
|
||||
|
||||
Control where to start reading:
|
||||
|
||||
```csharp
|
||||
// Start from beginning
|
||||
EventPosition.Earliest
|
||||
|
||||
// Start from end (new events only)
|
||||
EventPosition.Latest
|
||||
|
||||
// Start from specific offset
|
||||
EventPosition.FromOffset(12345)
|
||||
|
||||
// Start from specific sequence number
|
||||
EventPosition.FromSequenceNumber(100)
|
||||
|
||||
// Start from specific time
|
||||
EventPosition.FromEnqueuedTime(DateTimeOffset.UtcNow.AddHours(-1))
|
||||
```
|
||||
|
||||
## ASP.NET Core Integration
|
||||
|
||||
```csharp
|
||||
// Program.cs
|
||||
using Azure.Identity;
|
||||
using Azure.Messaging.EventHubs.Producer;
|
||||
using Microsoft.Extensions.Azure;
|
||||
|
||||
builder.Services.AddAzureClients(clientBuilder =>
|
||||
{
|
||||
clientBuilder.AddEventHubProducerClient(
|
||||
builder.Configuration["EventHub:FullyQualifiedNamespace"],
|
||||
builder.Configuration["EventHub:Name"]);
|
||||
|
||||
clientBuilder.UseCredential(new DefaultAzureCredential());
|
||||
});
|
||||
|
||||
// Inject in controller/service
|
||||
public class EventService
|
||||
{
|
||||
private readonly EventHubProducerClient _producer;
|
||||
|
||||
public EventService(EventHubProducerClient producer)
|
||||
{
|
||||
_producer = producer;
|
||||
}
|
||||
|
||||
public async Task SendAsync(string message)
|
||||
{
|
||||
using var batch = await _producer.CreateBatchAsync();
|
||||
batch.TryAdd(new EventData(message));
|
||||
await _producer.SendAsync(batch);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `EventProcessorClient` for receiving** — Never use `EventHubConsumerClient` in production
|
||||
2. **Checkpoint strategically** — After N events or time interval, not every event
|
||||
3. **Use partition keys** — For ordering guarantees within a partition
|
||||
4. **Reuse clients** — Create once, use as singleton (thread-safe)
|
||||
5. **Use `await using`** — Ensures proper disposal
|
||||
6. **Handle `ProcessErrorAsync`** — Always register error handler
|
||||
7. **Batch events** — Use `CreateBatchAsync()` to respect size limits
|
||||
8. **Use buffered producer** — For high-volume scenarios with automatic batching
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure.Messaging.EventHubs;
|
||||
|
||||
try
|
||||
{
|
||||
await producer.SendAsync(batch);
|
||||
}
|
||||
catch (EventHubsException ex) when (ex.Reason == EventHubsException.FailureReason.ServiceBusy)
|
||||
{
|
||||
// Retry with backoff
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
catch (EventHubsException ex) when (ex.IsTransient)
|
||||
{
|
||||
// Transient error - safe to retry
|
||||
Console.WriteLine($"Transient error: {ex.Message}");
|
||||
}
|
||||
catch (EventHubsException ex)
|
||||
{
|
||||
// Non-transient error
|
||||
Console.WriteLine($"Error: {ex.Reason} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Checkpointing Strategies
|
||||
|
||||
| Strategy | When to Use |
|
||||
|----------|-------------|
|
||||
| Every event | Low volume, critical data |
|
||||
| Every N events | Balanced throughput/reliability |
|
||||
| Time-based | Consistent checkpoint intervals |
|
||||
| Batch completion | After processing a logical batch |
|
||||
|
||||
```csharp
|
||||
// Checkpoint every 100 events
|
||||
private int _eventCount = 0;
|
||||
|
||||
processor.ProcessEventAsync += async args =>
|
||||
{
|
||||
// Process event...
|
||||
|
||||
_eventCount++;
|
||||
if (_eventCount >= 100)
|
||||
{
|
||||
await args.UpdateCheckpointAsync();
|
||||
_eventCount = 0;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.Messaging.EventHubs` | Core sending/receiving | `dotnet add package Azure.Messaging.EventHubs` |
|
||||
| `Azure.Messaging.EventHubs.Processor` | Production processing | `dotnet add package Azure.Messaging.EventHubs.Processor` |
|
||||
| `Azure.ResourceManager.EventHubs` | Management plane (create hubs) | `dotnet add package Azure.ResourceManager.EventHubs` |
|
||||
| `Microsoft.Azure.WebJobs.Extensions.EventHubs` | Azure Functions binding | `dotnet add package Microsoft.Azure.WebJobs.Extensions.EventHubs` |
|
||||
333
skills/official/microsoft/dotnet/messaging/servicebus/SKILL.md
Normal file
333
skills/official/microsoft/dotnet/messaging/servicebus/SKILL.md
Normal file
@@ -0,0 +1,333 @@
|
||||
---
|
||||
name: azure-servicebus-dotnet
|
||||
description: |
|
||||
Azure Service Bus SDK for .NET. Enterprise messaging with queues, topics, subscriptions, and sessions. Use for reliable message delivery, pub/sub patterns, dead letter handling, and background processing. Triggers: "Service Bus", "ServiceBusClient", "ServiceBusSender", "ServiceBusReceiver", "ServiceBusProcessor", "message queue", "pub/sub .NET", "dead letter queue".
|
||||
package: Azure.Messaging.ServiceBus
|
||||
---
|
||||
|
||||
# Azure.Messaging.ServiceBus (.NET)
|
||||
|
||||
Enterprise messaging SDK for reliable message delivery with queues, topics, subscriptions, and sessions.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.Messaging.ServiceBus
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Version**: v7.20.1 (stable)
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SERVICEBUS_FULLY_QUALIFIED_NAMESPACE=<namespace>.servicebus.windows.net
|
||||
# Or connection string (less secure)
|
||||
AZURE_SERVICEBUS_CONNECTION_STRING=Endpoint=sb://...
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### Microsoft Entra ID (Recommended)
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.Messaging.ServiceBus;
|
||||
|
||||
string fullyQualifiedNamespace = "<namespace>.servicebus.windows.net";
|
||||
await using ServiceBusClient client = new(fullyQualifiedNamespace, new DefaultAzureCredential());
|
||||
```
|
||||
|
||||
### Connection String
|
||||
|
||||
```csharp
|
||||
string connectionString = "<connection_string>";
|
||||
await using ServiceBusClient client = new(connectionString);
|
||||
```
|
||||
|
||||
### ASP.NET Core Dependency Injection
|
||||
|
||||
```csharp
|
||||
services.AddAzureClients(builder =>
|
||||
{
|
||||
builder.AddServiceBusClientWithNamespace("<namespace>.servicebus.windows.net");
|
||||
builder.UseCredential(new DefaultAzureCredential());
|
||||
});
|
||||
```
|
||||
|
||||
## Client Hierarchy
|
||||
|
||||
```
|
||||
ServiceBusClient
|
||||
├── CreateSender(queueOrTopicName) → ServiceBusSender
|
||||
├── CreateReceiver(queueName) → ServiceBusReceiver
|
||||
├── CreateReceiver(topicName, subName) → ServiceBusReceiver
|
||||
├── AcceptNextSessionAsync(queueName) → ServiceBusSessionReceiver
|
||||
├── CreateProcessor(queueName) → ServiceBusProcessor
|
||||
└── CreateSessionProcessor(queueName) → ServiceBusSessionProcessor
|
||||
|
||||
ServiceBusAdministrationClient (separate client for CRUD)
|
||||
```
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Send Messages
|
||||
|
||||
```csharp
|
||||
await using ServiceBusClient client = new(fullyQualifiedNamespace, new DefaultAzureCredential());
|
||||
ServiceBusSender sender = client.CreateSender("my-queue");
|
||||
|
||||
// Single message
|
||||
ServiceBusMessage message = new("Hello world!");
|
||||
await sender.SendMessageAsync(message);
|
||||
|
||||
// Safe batching (recommended)
|
||||
using ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync();
|
||||
if (batch.TryAddMessage(new ServiceBusMessage("Message 1")))
|
||||
{
|
||||
// Message added successfully
|
||||
}
|
||||
if (batch.TryAddMessage(new ServiceBusMessage("Message 2")))
|
||||
{
|
||||
// Message added successfully
|
||||
}
|
||||
await sender.SendMessagesAsync(batch);
|
||||
```
|
||||
|
||||
### 2. Receive Messages
|
||||
|
||||
```csharp
|
||||
ServiceBusReceiver receiver = client.CreateReceiver("my-queue");
|
||||
|
||||
// Single message
|
||||
ServiceBusReceivedMessage message = await receiver.ReceiveMessageAsync();
|
||||
string body = message.Body.ToString();
|
||||
Console.WriteLine(body);
|
||||
|
||||
// Complete the message (removes from queue)
|
||||
await receiver.CompleteMessageAsync(message);
|
||||
|
||||
// Batch receive
|
||||
IReadOnlyList<ServiceBusReceivedMessage> messages = await receiver.ReceiveMessagesAsync(maxMessages: 10);
|
||||
foreach (var msg in messages)
|
||||
{
|
||||
Console.WriteLine(msg.Body.ToString());
|
||||
await receiver.CompleteMessageAsync(msg);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Message Settlement
|
||||
|
||||
```csharp
|
||||
// Complete - removes message from queue
|
||||
await receiver.CompleteMessageAsync(message);
|
||||
|
||||
// Abandon - releases lock, message can be received again
|
||||
await receiver.AbandonMessageAsync(message);
|
||||
|
||||
// Defer - prevents normal receive, use ReceiveDeferredMessageAsync
|
||||
await receiver.DeferMessageAsync(message);
|
||||
|
||||
// Dead Letter - moves to dead letter subqueue
|
||||
await receiver.DeadLetterMessageAsync(message, "InvalidFormat", "Message body was not valid JSON");
|
||||
```
|
||||
|
||||
### 4. Background Processing with Processor
|
||||
|
||||
```csharp
|
||||
ServiceBusProcessor processor = client.CreateProcessor("my-queue", new ServiceBusProcessorOptions
|
||||
{
|
||||
AutoCompleteMessages = false,
|
||||
MaxConcurrentCalls = 2
|
||||
});
|
||||
|
||||
processor.ProcessMessageAsync += async (args) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string body = args.Message.Body.ToString();
|
||||
Console.WriteLine($"Received: {body}");
|
||||
await args.CompleteMessageAsync(args.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error processing: {ex.Message}");
|
||||
await args.AbandonMessageAsync(args.Message);
|
||||
}
|
||||
};
|
||||
|
||||
processor.ProcessErrorAsync += (args) =>
|
||||
{
|
||||
Console.WriteLine($"Error source: {args.ErrorSource}");
|
||||
Console.WriteLine($"Entity: {args.EntityPath}");
|
||||
Console.WriteLine($"Exception: {args.Exception}");
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await processor.StartProcessingAsync();
|
||||
// ... application runs
|
||||
await processor.StopProcessingAsync();
|
||||
```
|
||||
|
||||
### 5. Sessions (Ordered Processing)
|
||||
|
||||
```csharp
|
||||
// Send session message
|
||||
ServiceBusMessage message = new("Hello")
|
||||
{
|
||||
SessionId = "order-123"
|
||||
};
|
||||
await sender.SendMessageAsync(message);
|
||||
|
||||
// Receive from next available session
|
||||
ServiceBusSessionReceiver receiver = await client.AcceptNextSessionAsync("my-queue");
|
||||
|
||||
// Or receive from specific session
|
||||
ServiceBusSessionReceiver receiver = await client.AcceptSessionAsync("my-queue", "order-123");
|
||||
|
||||
// Session state management
|
||||
await receiver.SetSessionStateAsync(new BinaryData("processing"));
|
||||
BinaryData state = await receiver.GetSessionStateAsync();
|
||||
|
||||
// Renew session lock
|
||||
await receiver.RenewSessionLockAsync();
|
||||
```
|
||||
|
||||
### 6. Dead Letter Queue
|
||||
|
||||
```csharp
|
||||
// Receive from dead letter queue
|
||||
ServiceBusReceiver dlqReceiver = client.CreateReceiver("my-queue", new ServiceBusReceiverOptions
|
||||
{
|
||||
SubQueue = SubQueue.DeadLetter
|
||||
});
|
||||
|
||||
ServiceBusReceivedMessage dlqMessage = await dlqReceiver.ReceiveMessageAsync();
|
||||
|
||||
// Access dead letter metadata
|
||||
string reason = dlqMessage.DeadLetterReason;
|
||||
string description = dlqMessage.DeadLetterErrorDescription;
|
||||
Console.WriteLine($"Dead letter reason: {reason} - {description}");
|
||||
```
|
||||
|
||||
### 7. Topics and Subscriptions
|
||||
|
||||
```csharp
|
||||
// Send to topic
|
||||
ServiceBusSender topicSender = client.CreateSender("my-topic");
|
||||
await topicSender.SendMessageAsync(new ServiceBusMessage("Broadcast message"));
|
||||
|
||||
// Receive from subscription
|
||||
ServiceBusReceiver subReceiver = client.CreateReceiver("my-topic", "my-subscription");
|
||||
var message = await subReceiver.ReceiveMessageAsync();
|
||||
```
|
||||
|
||||
### 8. Administration (CRUD)
|
||||
|
||||
```csharp
|
||||
var adminClient = new ServiceBusAdministrationClient(
|
||||
fullyQualifiedNamespace,
|
||||
new DefaultAzureCredential());
|
||||
|
||||
// Create queue
|
||||
var options = new CreateQueueOptions("my-queue")
|
||||
{
|
||||
MaxDeliveryCount = 10,
|
||||
LockDuration = TimeSpan.FromSeconds(30),
|
||||
RequiresSession = true,
|
||||
DeadLetteringOnMessageExpiration = true
|
||||
};
|
||||
QueueProperties queue = await adminClient.CreateQueueAsync(options);
|
||||
|
||||
// Update queue
|
||||
queue.LockDuration = TimeSpan.FromSeconds(60);
|
||||
await adminClient.UpdateQueueAsync(queue);
|
||||
|
||||
// Create topic and subscription
|
||||
await adminClient.CreateTopicAsync(new CreateTopicOptions("my-topic"));
|
||||
await adminClient.CreateSubscriptionAsync(new CreateSubscriptionOptions("my-topic", "my-subscription"));
|
||||
|
||||
// Delete
|
||||
await adminClient.DeleteQueueAsync("my-queue");
|
||||
```
|
||||
|
||||
### 9. Cross-Entity Transactions
|
||||
|
||||
```csharp
|
||||
var options = new ServiceBusClientOptions { EnableCrossEntityTransactions = true };
|
||||
await using var client = new ServiceBusClient(connectionString, options);
|
||||
|
||||
ServiceBusReceiver receiverA = client.CreateReceiver("queueA");
|
||||
ServiceBusSender senderB = client.CreateSender("queueB");
|
||||
|
||||
ServiceBusReceivedMessage receivedMessage = await receiverA.ReceiveMessageAsync();
|
||||
|
||||
using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
|
||||
{
|
||||
await receiverA.CompleteMessageAsync(receivedMessage);
|
||||
await senderB.SendMessageAsync(new ServiceBusMessage("Forwarded"));
|
||||
ts.Complete();
|
||||
}
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ServiceBusClient` | Main entry point, manages connection |
|
||||
| `ServiceBusSender` | Sends messages to queues/topics |
|
||||
| `ServiceBusReceiver` | Receives messages from queues/subscriptions |
|
||||
| `ServiceBusSessionReceiver` | Receives session messages |
|
||||
| `ServiceBusProcessor` | Background message processing |
|
||||
| `ServiceBusSessionProcessor` | Background session processing |
|
||||
| `ServiceBusAdministrationClient` | CRUD for queues/topics/subscriptions |
|
||||
| `ServiceBusMessage` | Message to send |
|
||||
| `ServiceBusReceivedMessage` | Received message with metadata |
|
||||
| `ServiceBusMessageBatch` | Batch of messages |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use singletons** — Clients, senders, receivers, and processors are thread-safe
|
||||
2. **Always dispose** — Use `await using` or call `DisposeAsync()`
|
||||
3. **Dispose order** — Close senders/receivers/processors first, then client
|
||||
4. **Use DefaultAzureCredential** — Prefer over connection strings for production
|
||||
5. **Use processors for background work** — Handles lock renewal automatically
|
||||
6. **Use safe batching** — `CreateMessageBatchAsync()` and `TryAddMessage()`
|
||||
7. **Handle transient errors** — Use `ServiceBusException.Reason`
|
||||
8. **Configure transport** — Use `AmqpWebSockets` if ports 5671/5672 are blocked
|
||||
9. **Set appropriate lock duration** — Default is 30 seconds
|
||||
10. **Use sessions for ordering** — FIFO within a session
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
await sender.SendMessageAsync(message);
|
||||
}
|
||||
catch (ServiceBusException ex) when (ex.Reason == ServiceBusFailureReason.ServiceBusy)
|
||||
{
|
||||
// Retry with backoff
|
||||
}
|
||||
catch (ServiceBusException ex)
|
||||
{
|
||||
Console.WriteLine($"Service Bus Error: {ex.Reason} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.Messaging.ServiceBus` | Service Bus (this SDK) | `dotnet add package Azure.Messaging.ServiceBus` |
|
||||
| `Azure.Messaging.EventHubs` | Event streaming | `dotnet add package Azure.Messaging.EventHubs` |
|
||||
| `Azure.Messaging.EventGrid` | Event routing | `dotnet add package Azure.Messaging.EventGrid` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.Messaging.ServiceBus |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.messaging.servicebus |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/servicebus/Azure.Messaging.ServiceBus |
|
||||
| Troubleshooting | https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/TROUBLESHOOTING.md |
|
||||
@@ -0,0 +1,486 @@
|
||||
---
|
||||
name: azure-mgmt-applicationinsights-dotnet
|
||||
description: |
|
||||
Azure Application Insights SDK for .NET. Application performance monitoring and observability resource management. Use for creating Application Insights components, web tests, workbooks, analytics items, and API keys. Triggers: "Application Insights", "ApplicationInsights", "App Insights", "APM", "application monitoring", "web tests", "availability tests", "workbooks".
|
||||
package: Azure.ResourceManager.ApplicationInsights
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.ApplicationInsights (.NET)
|
||||
|
||||
Azure Resource Manager SDK for managing Application Insights resources for application performance monitoring.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.ApplicationInsights
|
||||
dotnet add package Azure.Identity
|
||||
```
|
||||
|
||||
**Current Version**: v1.0.0 (GA)
|
||||
**API Version**: 2022-06-15
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
AZURE_RESOURCE_GROUP=<your-resource-group>
|
||||
AZURE_APPINSIGHTS_NAME=<your-appinsights-component>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.ApplicationInsights;
|
||||
|
||||
ArmClient client = new ArmClient(new DefaultAzureCredential());
|
||||
```
|
||||
|
||||
## Resource Hierarchy
|
||||
|
||||
```
|
||||
Subscription
|
||||
└── ResourceGroup
|
||||
└── ApplicationInsightsComponent # App Insights resource
|
||||
├── ApplicationInsightsComponentApiKey # API keys for programmatic access
|
||||
├── ComponentLinkedStorageAccount # Linked storage for data export
|
||||
└── (via component ID)
|
||||
├── WebTest # Availability tests
|
||||
├── Workbook # Workbooks for analysis
|
||||
├── WorkbookTemplate # Workbook templates
|
||||
└── MyWorkbook # Private workbooks
|
||||
```
|
||||
|
||||
## Core Workflows
|
||||
|
||||
### 1. Create Application Insights Component (Workspace-based)
|
||||
|
||||
```csharp
|
||||
using Azure.ResourceManager.ApplicationInsights;
|
||||
using Azure.ResourceManager.ApplicationInsights.Models;
|
||||
|
||||
ResourceGroupResource resourceGroup = await client
|
||||
.GetDefaultSubscriptionAsync()
|
||||
.Result
|
||||
.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
ApplicationInsightsComponentCollection components = resourceGroup.GetApplicationInsightsComponents();
|
||||
|
||||
// Workspace-based Application Insights (recommended)
|
||||
ApplicationInsightsComponentData data = new ApplicationInsightsComponentData(
|
||||
AzureLocation.EastUS,
|
||||
ApplicationInsightsApplicationType.Web)
|
||||
{
|
||||
Kind = "web",
|
||||
WorkspaceResourceId = new ResourceIdentifier(
|
||||
"/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.OperationalInsights/workspaces/<workspace-name>"),
|
||||
IngestionMode = IngestionMode.LogAnalytics,
|
||||
PublicNetworkAccessForIngestion = PublicNetworkAccessType.Enabled,
|
||||
PublicNetworkAccessForQuery = PublicNetworkAccessType.Enabled,
|
||||
RetentionInDays = 90,
|
||||
SamplingPercentage = 100,
|
||||
DisableIPMasking = false,
|
||||
ImmediatePurgeDataOn30Days = false,
|
||||
Tags =
|
||||
{
|
||||
{ "environment", "production" },
|
||||
{ "application", "mywebapp" }
|
||||
}
|
||||
};
|
||||
|
||||
ArmOperation<ApplicationInsightsComponentResource> operation = await components
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-appinsights", data);
|
||||
|
||||
ApplicationInsightsComponentResource component = operation.Value;
|
||||
|
||||
Console.WriteLine($"Component created: {component.Data.Name}");
|
||||
Console.WriteLine($"Instrumentation Key: {component.Data.InstrumentationKey}");
|
||||
Console.WriteLine($"Connection String: {component.Data.ConnectionString}");
|
||||
```
|
||||
|
||||
### 2. Get Connection String and Keys
|
||||
|
||||
```csharp
|
||||
ApplicationInsightsComponentResource component = await resourceGroup
|
||||
.GetApplicationInsightsComponentAsync("my-appinsights");
|
||||
|
||||
// Get connection string for SDK configuration
|
||||
string connectionString = component.Data.ConnectionString;
|
||||
string instrumentationKey = component.Data.InstrumentationKey;
|
||||
string appId = component.Data.AppId;
|
||||
|
||||
Console.WriteLine($"Connection String: {connectionString}");
|
||||
Console.WriteLine($"Instrumentation Key: {instrumentationKey}");
|
||||
Console.WriteLine($"App ID: {appId}");
|
||||
```
|
||||
|
||||
### 3. Create API Key
|
||||
|
||||
```csharp
|
||||
ApplicationInsightsComponentResource component = await resourceGroup
|
||||
.GetApplicationInsightsComponentAsync("my-appinsights");
|
||||
|
||||
ApplicationInsightsComponentApiKeyCollection apiKeys = component.GetApplicationInsightsComponentApiKeys();
|
||||
|
||||
// API key for reading telemetry
|
||||
ApplicationInsightsApiKeyContent keyContent = new ApplicationInsightsApiKeyContent
|
||||
{
|
||||
Name = "ReadTelemetryKey",
|
||||
LinkedReadProperties =
|
||||
{
|
||||
$"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/microsoft.insights/components/{component.Data.Name}/api",
|
||||
$"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/microsoft.insights/components/{component.Data.Name}/agentconfig"
|
||||
}
|
||||
};
|
||||
|
||||
ApplicationInsightsComponentApiKeyResource apiKey = await apiKeys
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, keyContent);
|
||||
|
||||
Console.WriteLine($"API Key Name: {apiKey.Data.Name}");
|
||||
Console.WriteLine($"API Key: {apiKey.Data.ApiKey}"); // Only shown once!
|
||||
```
|
||||
|
||||
### 4. Create Web Test (Availability Test)
|
||||
|
||||
```csharp
|
||||
WebTestCollection webTests = resourceGroup.GetWebTests();
|
||||
|
||||
// URL Ping Test
|
||||
WebTestData urlPingTest = new WebTestData(AzureLocation.EastUS)
|
||||
{
|
||||
Kind = WebTestKind.Ping,
|
||||
SyntheticMonitorId = "webtest-ping-myapp",
|
||||
WebTestName = "Homepage Availability",
|
||||
Description = "Checks if homepage is available",
|
||||
IsEnabled = true,
|
||||
Frequency = 300, // 5 minutes
|
||||
Timeout = 120, // 2 minutes
|
||||
WebTestKind = WebTestKind.Ping,
|
||||
IsRetryEnabled = true,
|
||||
Locations =
|
||||
{
|
||||
new WebTestGeolocation { WebTestLocationId = "us-ca-sjc-azr" }, // West US
|
||||
new WebTestGeolocation { WebTestLocationId = "us-tx-sn1-azr" }, // South Central US
|
||||
new WebTestGeolocation { WebTestLocationId = "us-il-ch1-azr" }, // North Central US
|
||||
new WebTestGeolocation { WebTestLocationId = "emea-gb-db3-azr" }, // UK South
|
||||
new WebTestGeolocation { WebTestLocationId = "apac-sg-sin-azr" } // Southeast Asia
|
||||
},
|
||||
Configuration = new WebTestConfiguration
|
||||
{
|
||||
WebTest = """
|
||||
<WebTest Name="Homepage" Enabled="True" Timeout="120"
|
||||
xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
|
||||
<Items>
|
||||
<Request Method="GET" Version="1.1" Url="https://myapp.example.com"
|
||||
ThinkTime="0" Timeout="120" ParseDependentRequests="False"
|
||||
FollowRedirects="True" RecordResult="True" Cache="False"
|
||||
ResponseTimeGoal="0" Encoding="utf-8" ExpectedHttpStatusCode="200" />
|
||||
</Items>
|
||||
</WebTest>
|
||||
"""
|
||||
},
|
||||
Tags =
|
||||
{
|
||||
{ $"hidden-link:/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/microsoft.insights/components/my-appinsights", "Resource" }
|
||||
}
|
||||
};
|
||||
|
||||
ArmOperation<WebTestResource> operation = await webTests
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "webtest-homepage", urlPingTest);
|
||||
|
||||
WebTestResource webTest = operation.Value;
|
||||
Console.WriteLine($"Web test created: {webTest.Data.Name}");
|
||||
```
|
||||
|
||||
### 5. Create Multi-Step Web Test
|
||||
|
||||
```csharp
|
||||
WebTestData multiStepTest = new WebTestData(AzureLocation.EastUS)
|
||||
{
|
||||
Kind = WebTestKind.MultiStep,
|
||||
SyntheticMonitorId = "webtest-multistep-login",
|
||||
WebTestName = "Login Flow Test",
|
||||
Description = "Tests login functionality",
|
||||
IsEnabled = true,
|
||||
Frequency = 900, // 15 minutes
|
||||
Timeout = 300, // 5 minutes
|
||||
WebTestKind = WebTestKind.MultiStep,
|
||||
IsRetryEnabled = true,
|
||||
Locations =
|
||||
{
|
||||
new WebTestGeolocation { WebTestLocationId = "us-ca-sjc-azr" }
|
||||
},
|
||||
Configuration = new WebTestConfiguration
|
||||
{
|
||||
WebTest = """
|
||||
<WebTest Name="LoginFlow" Enabled="True" Timeout="300"
|
||||
xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
|
||||
<Items>
|
||||
<Request Method="GET" Version="1.1" Url="https://myapp.example.com/login"
|
||||
ThinkTime="0" Timeout="60" />
|
||||
<Request Method="POST" Version="1.1" Url="https://myapp.example.com/api/auth"
|
||||
ThinkTime="0" Timeout="60">
|
||||
<Headers>
|
||||
<Header Name="Content-Type" Value="application/json" />
|
||||
</Headers>
|
||||
<Body>{"username":"testuser","password":"{{TestPassword}}"}</Body>
|
||||
</Request>
|
||||
</Items>
|
||||
</WebTest>
|
||||
"""
|
||||
},
|
||||
Tags =
|
||||
{
|
||||
{ $"hidden-link:/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/microsoft.insights/components/my-appinsights", "Resource" }
|
||||
}
|
||||
};
|
||||
|
||||
await webTests.CreateOrUpdateAsync(WaitUntil.Completed, "webtest-login-flow", multiStepTest);
|
||||
```
|
||||
|
||||
### 6. Create Workbook
|
||||
|
||||
```csharp
|
||||
WorkbookCollection workbooks = resourceGroup.GetWorkbooks();
|
||||
|
||||
WorkbookData workbookData = new WorkbookData(AzureLocation.EastUS)
|
||||
{
|
||||
DisplayName = "Application Performance Dashboard",
|
||||
Category = "workbook",
|
||||
Kind = WorkbookSharedTypeKind.Shared,
|
||||
SerializedData = """
|
||||
{
|
||||
"version": "Notebook/1.0",
|
||||
"items": [
|
||||
{
|
||||
"type": 1,
|
||||
"content": {
|
||||
"json": "# Application Performance\n\nThis workbook shows application performance metrics."
|
||||
},
|
||||
"name": "header"
|
||||
},
|
||||
{
|
||||
"type": 3,
|
||||
"content": {
|
||||
"version": "KqlItem/1.0",
|
||||
"query": "requests\n| summarize count() by bin(timestamp, 1h)\n| render timechart",
|
||||
"size": 0,
|
||||
"title": "Requests per Hour",
|
||||
"timeContext": {
|
||||
"durationMs": 86400000
|
||||
},
|
||||
"queryType": 0,
|
||||
"resourceType": "microsoft.insights/components"
|
||||
},
|
||||
"name": "requestsChart"
|
||||
}
|
||||
],
|
||||
"isLocked": false
|
||||
}
|
||||
""",
|
||||
SourceId = component.Id,
|
||||
Tags =
|
||||
{
|
||||
{ "environment", "production" }
|
||||
}
|
||||
};
|
||||
|
||||
// Note: Workbook ID should be a new GUID
|
||||
string workbookId = Guid.NewGuid().ToString();
|
||||
|
||||
ArmOperation<WorkbookResource> operation = await workbooks
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, workbookId, workbookData);
|
||||
|
||||
WorkbookResource workbook = operation.Value;
|
||||
Console.WriteLine($"Workbook created: {workbook.Data.DisplayName}");
|
||||
```
|
||||
|
||||
### 7. Link Storage Account
|
||||
|
||||
```csharp
|
||||
ApplicationInsightsComponentResource component = await resourceGroup
|
||||
.GetApplicationInsightsComponentAsync("my-appinsights");
|
||||
|
||||
ComponentLinkedStorageAccountCollection linkedStorage = component.GetComponentLinkedStorageAccounts();
|
||||
|
||||
ComponentLinkedStorageAccountData storageData = new ComponentLinkedStorageAccountData
|
||||
{
|
||||
LinkedStorageAccount = new ResourceIdentifier(
|
||||
"/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/<storage-account>")
|
||||
};
|
||||
|
||||
ArmOperation<ComponentLinkedStorageAccountResource> operation = await linkedStorage
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, StorageType.ServiceProfiler, storageData);
|
||||
```
|
||||
|
||||
### 8. List and Manage Components
|
||||
|
||||
```csharp
|
||||
// List all Application Insights components in resource group
|
||||
await foreach (ApplicationInsightsComponentResource component in
|
||||
resourceGroup.GetApplicationInsightsComponents())
|
||||
{
|
||||
Console.WriteLine($"Component: {component.Data.Name}");
|
||||
Console.WriteLine($" App ID: {component.Data.AppId}");
|
||||
Console.WriteLine($" Type: {component.Data.ApplicationType}");
|
||||
Console.WriteLine($" Ingestion Mode: {component.Data.IngestionMode}");
|
||||
Console.WriteLine($" Retention: {component.Data.RetentionInDays} days");
|
||||
}
|
||||
|
||||
// List web tests
|
||||
await foreach (WebTestResource webTest in resourceGroup.GetWebTests())
|
||||
{
|
||||
Console.WriteLine($"Web Test: {webTest.Data.WebTestName}");
|
||||
Console.WriteLine($" Enabled: {webTest.Data.IsEnabled}");
|
||||
Console.WriteLine($" Frequency: {webTest.Data.Frequency}s");
|
||||
}
|
||||
|
||||
// List workbooks
|
||||
await foreach (WorkbookResource workbook in resourceGroup.GetWorkbooks())
|
||||
{
|
||||
Console.WriteLine($"Workbook: {workbook.Data.DisplayName}");
|
||||
}
|
||||
```
|
||||
|
||||
### 9. Update Component
|
||||
|
||||
```csharp
|
||||
ApplicationInsightsComponentResource component = await resourceGroup
|
||||
.GetApplicationInsightsComponentAsync("my-appinsights");
|
||||
|
||||
// Update using full data (PUT operation)
|
||||
ApplicationInsightsComponentData updateData = component.Data;
|
||||
updateData.RetentionInDays = 180;
|
||||
updateData.SamplingPercentage = 50;
|
||||
updateData.Tags["updated"] = "true";
|
||||
|
||||
ArmOperation<ApplicationInsightsComponentResource> operation = await resourceGroup
|
||||
.GetApplicationInsightsComponents()
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-appinsights", updateData);
|
||||
```
|
||||
|
||||
### 10. Delete Resources
|
||||
|
||||
```csharp
|
||||
// Delete Application Insights component
|
||||
ApplicationInsightsComponentResource component = await resourceGroup
|
||||
.GetApplicationInsightsComponentAsync("my-appinsights");
|
||||
await component.DeleteAsync(WaitUntil.Completed);
|
||||
|
||||
// Delete web test
|
||||
WebTestResource webTest = await resourceGroup.GetWebTestAsync("webtest-homepage");
|
||||
await webTest.DeleteAsync(WaitUntil.Completed);
|
||||
```
|
||||
|
||||
## Key Types Reference
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ApplicationInsightsComponentResource` | App Insights component |
|
||||
| `ApplicationInsightsComponentData` | Component configuration |
|
||||
| `ApplicationInsightsComponentCollection` | Collection of components |
|
||||
| `ApplicationInsightsComponentApiKeyResource` | API key for programmatic access |
|
||||
| `WebTestResource` | Availability/web test |
|
||||
| `WebTestData` | Web test configuration |
|
||||
| `WorkbookResource` | Analysis workbook |
|
||||
| `WorkbookData` | Workbook configuration |
|
||||
| `ComponentLinkedStorageAccountResource` | Linked storage for exports |
|
||||
|
||||
## Application Types
|
||||
|
||||
| Type | Enum Value |
|
||||
|------|------------|
|
||||
| Web Application | `Web` |
|
||||
| iOS Application | `iOS` |
|
||||
| Java Application | `Java` |
|
||||
| Node.js Application | `NodeJS` |
|
||||
| .NET Application | `MRT` |
|
||||
| Other | `Other` |
|
||||
|
||||
## Web Test Locations
|
||||
|
||||
| Location ID | Region |
|
||||
|-------------|--------|
|
||||
| `us-ca-sjc-azr` | West US |
|
||||
| `us-tx-sn1-azr` | South Central US |
|
||||
| `us-il-ch1-azr` | North Central US |
|
||||
| `us-va-ash-azr` | East US |
|
||||
| `emea-gb-db3-azr` | UK South |
|
||||
| `emea-nl-ams-azr` | West Europe |
|
||||
| `emea-fr-pra-edge` | France Central |
|
||||
| `apac-sg-sin-azr` | Southeast Asia |
|
||||
| `apac-hk-hkn-azr` | East Asia |
|
||||
| `apac-jp-kaw-edge` | Japan East |
|
||||
| `latam-br-gru-edge` | Brazil South |
|
||||
| `emea-au-syd-edge` | Australia East |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use workspace-based** — Workspace-based App Insights is the current standard
|
||||
2. **Link to Log Analytics** — Store data in Log Analytics for better querying
|
||||
3. **Set appropriate retention** — Balance cost vs. data availability
|
||||
4. **Use sampling** — Reduce costs for high-volume applications
|
||||
5. **Store connection string securely** — Use Key Vault or managed identity
|
||||
6. **Enable multiple test locations** — For accurate availability monitoring
|
||||
7. **Use workbooks** — For custom dashboards and analysis
|
||||
8. **Set up alerts** — Based on availability tests and metrics
|
||||
9. **Tag resources** — For cost allocation and organization
|
||||
10. **Use private endpoints** — For secure data ingestion
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
using Azure;
|
||||
|
||||
try
|
||||
{
|
||||
ArmOperation<ApplicationInsightsComponentResource> operation = await components
|
||||
.CreateOrUpdateAsync(WaitUntil.Completed, "my-appinsights", data);
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 409)
|
||||
{
|
||||
Console.WriteLine("Component already exists");
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 400)
|
||||
{
|
||||
Console.WriteLine($"Invalid configuration: {ex.Message}");
|
||||
}
|
||||
catch (RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Azure error: {ex.Status} - {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## SDK Integration
|
||||
|
||||
Use the connection string with Application Insights SDK:
|
||||
|
||||
```csharp
|
||||
// Program.cs in ASP.NET Core
|
||||
builder.Services.AddApplicationInsightsTelemetry(options =>
|
||||
{
|
||||
options.ConnectionString = configuration["ApplicationInsights:ConnectionString"];
|
||||
});
|
||||
|
||||
// Or set via environment variable
|
||||
// APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=...;IngestionEndpoint=...
|
||||
```
|
||||
|
||||
## Related SDKs
|
||||
|
||||
| SDK | Purpose | Install |
|
||||
|-----|---------|---------|
|
||||
| `Azure.ResourceManager.ApplicationInsights` | Resource management (this SDK) | `dotnet add package Azure.ResourceManager.ApplicationInsights` |
|
||||
| `Microsoft.ApplicationInsights` | Telemetry SDK | `dotnet add package Microsoft.ApplicationInsights` |
|
||||
| `Microsoft.ApplicationInsights.AspNetCore` | ASP.NET Core integration | `dotnet add package Microsoft.ApplicationInsights.AspNetCore` |
|
||||
| `Azure.Monitor.OpenTelemetry.Exporter` | OpenTelemetry export | `dotnet add package Azure.Monitor.OpenTelemetry.Exporter` |
|
||||
|
||||
## Reference Links
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| NuGet Package | https://www.nuget.org/packages/Azure.ResourceManager.ApplicationInsights |
|
||||
| API Reference | https://learn.microsoft.com/dotnet/api/azure.resourcemanager.applicationinsights |
|
||||
| Product Documentation | https://learn.microsoft.com/azure/azure-monitor/app/app-insights-overview |
|
||||
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/applicationinsights/Azure.ResourceManager.ApplicationInsights |
|
||||
@@ -0,0 +1,230 @@
|
||||
---
|
||||
name: azure-mgmt-arizeaiobservabilityeval-dotnet
|
||||
description: |
|
||||
Azure Resource Manager SDK for Arize AI Observability and Evaluation (.NET). Use when managing Arize AI organizations
|
||||
on Azure via Azure Marketplace, creating/updating/deleting Arize resources, or integrating Arize ML observability
|
||||
into .NET applications. Triggers: "Arize AI", "ML observability", "ArizeAIObservabilityEval", "Arize organization".
|
||||
package: Azure.ResourceManager.ArizeAIObservabilityEval
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.ArizeAIObservabilityEval
|
||||
|
||||
.NET SDK for managing Arize AI Observability and Evaluation resources on Azure.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.ArizeAIObservabilityEval --version 1.0.0
|
||||
```
|
||||
|
||||
## Package Info
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Package | `Azure.ResourceManager.ArizeAIObservabilityEval` |
|
||||
| Version | `1.0.0` (GA) |
|
||||
| API Version | `2024-10-01` |
|
||||
| ARM Type | `ArizeAi.ObservabilityEval/organizations` |
|
||||
| Dependencies | `Azure.Core` >= 1.46.2, `Azure.ResourceManager` >= 1.13.1 |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
|
||||
AZURE_TENANT_ID=<your-tenant-id>
|
||||
AZURE_CLIENT_ID=<your-client-id>
|
||||
AZURE_CLIENT_SECRET=<your-client-secret>
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.ArizeAIObservabilityEval;
|
||||
|
||||
// Always use DefaultAzureCredential
|
||||
var credential = new DefaultAzureCredential();
|
||||
var armClient = new ArmClient(credential);
|
||||
```
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### Create an Arize AI Organization
|
||||
|
||||
```csharp
|
||||
using Azure.Core;
|
||||
using Azure.ResourceManager.Resources;
|
||||
using Azure.ResourceManager.ArizeAIObservabilityEval;
|
||||
using Azure.ResourceManager.ArizeAIObservabilityEval.Models;
|
||||
|
||||
// Get subscription and resource group
|
||||
var subscriptionId = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
|
||||
var subscription = await armClient.GetSubscriptionResource(
|
||||
SubscriptionResource.CreateResourceIdentifier(subscriptionId)).GetAsync();
|
||||
var resourceGroup = await subscription.Value.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
// Get the organization collection
|
||||
var collection = resourceGroup.Value.GetArizeAIObservabilityEvalOrganizations();
|
||||
|
||||
// Create organization data
|
||||
var data = new ArizeAIObservabilityEvalOrganizationData(AzureLocation.EastUS)
|
||||
{
|
||||
Properties = new ArizeAIObservabilityEvalOrganizationProperties
|
||||
{
|
||||
Marketplace = new ArizeAIObservabilityEvalMarketplaceDetails
|
||||
{
|
||||
SubscriptionId = "marketplace-subscription-id",
|
||||
OfferDetails = new ArizeAIObservabilityEvalOfferDetails
|
||||
{
|
||||
PublisherId = "arikimlabs1649082416596",
|
||||
OfferId = "arize-liftr-1",
|
||||
PlanId = "arize-liftr-1-plan",
|
||||
PlanName = "Arize AI Plan",
|
||||
TermUnit = "P1M",
|
||||
TermId = "term-id"
|
||||
}
|
||||
},
|
||||
User = new ArizeAIObservabilityEvalUserDetails
|
||||
{
|
||||
FirstName = "John",
|
||||
LastName = "Doe",
|
||||
EmailAddress = "john.doe@example.com"
|
||||
}
|
||||
},
|
||||
Tags = { ["environment"] = "production" }
|
||||
};
|
||||
|
||||
// Create (long-running operation)
|
||||
var operation = await collection.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
"my-arize-org",
|
||||
data);
|
||||
|
||||
var organization = operation.Value;
|
||||
Console.WriteLine($"Created: {organization.Data.Name}");
|
||||
```
|
||||
|
||||
### Get an Organization
|
||||
|
||||
```csharp
|
||||
// Option 1: From collection
|
||||
var org = await collection.GetAsync("my-arize-org");
|
||||
|
||||
// Option 2: Check if exists first
|
||||
var exists = await collection.ExistsAsync("my-arize-org");
|
||||
if (exists.Value)
|
||||
{
|
||||
var org = await collection.GetAsync("my-arize-org");
|
||||
}
|
||||
|
||||
// Option 3: GetIfExists (returns null if not found)
|
||||
var response = await collection.GetIfExistsAsync("my-arize-org");
|
||||
if (response.HasValue)
|
||||
{
|
||||
var org = response.Value;
|
||||
}
|
||||
```
|
||||
|
||||
### List Organizations
|
||||
|
||||
```csharp
|
||||
// List in resource group
|
||||
await foreach (var org in collection.GetAllAsync())
|
||||
{
|
||||
Console.WriteLine($"Org: {org.Data.Name}, State: {org.Data.Properties?.ProvisioningState}");
|
||||
}
|
||||
|
||||
// List in subscription
|
||||
await foreach (var org in subscription.Value.GetArizeAIObservabilityEvalOrganizationsAsync())
|
||||
{
|
||||
Console.WriteLine($"Org: {org.Data.Name}");
|
||||
}
|
||||
```
|
||||
|
||||
### Update an Organization
|
||||
|
||||
```csharp
|
||||
// Update tags
|
||||
var org = await collection.GetAsync("my-arize-org");
|
||||
var updateData = new ArizeAIObservabilityEvalOrganizationPatch
|
||||
{
|
||||
Tags = { ["environment"] = "staging", ["team"] = "ml-ops" }
|
||||
};
|
||||
var updated = await org.Value.UpdateAsync(updateData);
|
||||
```
|
||||
|
||||
### Delete an Organization
|
||||
|
||||
```csharp
|
||||
var org = await collection.GetAsync("my-arize-org");
|
||||
await org.Value.DeleteAsync(WaitUntil.Completed);
|
||||
```
|
||||
|
||||
## Key Types
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ArizeAIObservabilityEvalOrganizationResource` | Main ARM resource for Arize organizations |
|
||||
| `ArizeAIObservabilityEvalOrganizationCollection` | Collection for CRUD operations |
|
||||
| `ArizeAIObservabilityEvalOrganizationData` | Resource data model |
|
||||
| `ArizeAIObservabilityEvalOrganizationProperties` | Organization properties |
|
||||
| `ArizeAIObservabilityEvalMarketplaceDetails` | Azure Marketplace subscription info |
|
||||
| `ArizeAIObservabilityEvalOfferDetails` | Marketplace offer configuration |
|
||||
| `ArizeAIObservabilityEvalUserDetails` | User contact information |
|
||||
| `ArizeAIObservabilityEvalOrganizationPatch` | Patch model for updates |
|
||||
| `ArizeAIObservabilityEvalSingleSignOnPropertiesV2` | SSO configuration |
|
||||
|
||||
## Enums
|
||||
|
||||
| Enum | Values |
|
||||
|------|--------|
|
||||
| `ArizeAIObservabilityEvalOfferProvisioningState` | `Succeeded`, `Failed`, `Canceled`, `Provisioning`, `Updating`, `Deleting`, `Accepted` |
|
||||
| `ArizeAIObservabilityEvalMarketplaceSubscriptionStatus` | `PendingFulfillmentStart`, `Subscribed`, `Suspended`, `Unsubscribed` |
|
||||
| `ArizeAIObservabilityEvalSingleSignOnState` | `Initial`, `Enable`, `Disable` |
|
||||
| `ArizeAIObservabilityEvalSingleSignOnType` | `Saml`, `OpenId` |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use async methods** — All operations support async/await
|
||||
2. **Handle long-running operations** — Use `WaitUntil.Completed` or poll manually
|
||||
3. **Use GetIfExistsAsync** — Avoid exceptions for conditional logic
|
||||
4. **Implement retry policies** — Configure via `ArmClientOptions`
|
||||
5. **Use resource identifiers** — For direct resource access without listing
|
||||
6. **Close clients properly** — Use `using` statements or dispose explicitly
|
||||
|
||||
## Error Handling
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
var org = await collection.GetAsync("my-arize-org");
|
||||
}
|
||||
catch (Azure.RequestFailedException ex) when (ex.Status == 404)
|
||||
{
|
||||
Console.WriteLine("Organization not found");
|
||||
}
|
||||
catch (Azure.RequestFailedException ex)
|
||||
{
|
||||
Console.WriteLine($"Azure error: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
## Direct Resource Access
|
||||
|
||||
```csharp
|
||||
// Access resource directly by ID (without listing)
|
||||
var resourceId = ArizeAIObservabilityEvalOrganizationResource.CreateResourceIdentifier(
|
||||
subscriptionId,
|
||||
"my-resource-group",
|
||||
"my-arize-org");
|
||||
|
||||
var org = armClient.GetArizeAIObservabilityEvalOrganizationResource(resourceId);
|
||||
var data = await org.GetAsync();
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
- [NuGet Package](https://www.nuget.org/packages/Azure.ResourceManager.ArizeAIObservabilityEval)
|
||||
- [Azure SDK for .NET](https://github.com/Azure/azure-sdk-for-net)
|
||||
- [Arize AI](https://arize.com/)
|
||||
354
skills/official/microsoft/dotnet/partner/mongodbatlas/SKILL.md
Normal file
354
skills/official/microsoft/dotnet/partner/mongodbatlas/SKILL.md
Normal file
@@ -0,0 +1,354 @@
|
||||
---
|
||||
name: azure-mgmt-mongodbatlas-dotnet
|
||||
description: Manage MongoDB Atlas Organizations as Azure ARM resources using Azure.ResourceManager.MongoDBAtlas SDK. Use when creating, updating, listing, or deleting MongoDB Atlas organizations through Azure Marketplace integration. This SDK manages the Azure-side organization resource, not Atlas clusters/databases directly.
|
||||
package: Azure.ResourceManager.MongoDBAtlas
|
||||
---
|
||||
|
||||
# Azure.ResourceManager.MongoDBAtlas SDK
|
||||
|
||||
Manage MongoDB Atlas Organizations as Azure ARM resources with unified billing through Azure Marketplace.
|
||||
|
||||
## Package Information
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Package | `Azure.ResourceManager.MongoDBAtlas` |
|
||||
| Version | 1.0.0 (GA) |
|
||||
| API Version | 2025-06-01 |
|
||||
| Resource Type | `MongoDB.Atlas/organizations` |
|
||||
| NuGet | [Azure.ResourceManager.MongoDBAtlas](https://www.nuget.org/packages/Azure.ResourceManager.MongoDBAtlas) |
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
dotnet add package Azure.ResourceManager.MongoDBAtlas
|
||||
dotnet add package Azure.Identity
|
||||
dotnet add package Azure.ResourceManager
|
||||
```
|
||||
|
||||
## Important Scope Limitation
|
||||
|
||||
This SDK manages **MongoDB Atlas Organizations as Azure ARM resources** for marketplace integration. It does NOT directly manage:
|
||||
- Atlas clusters
|
||||
- Databases
|
||||
- Collections
|
||||
- Users/roles
|
||||
|
||||
For cluster management, use the MongoDB Atlas API directly after creating the organization.
|
||||
|
||||
## Authentication
|
||||
|
||||
```csharp
|
||||
using Azure.Identity;
|
||||
using Azure.ResourceManager;
|
||||
using Azure.ResourceManager.MongoDBAtlas;
|
||||
using Azure.ResourceManager.MongoDBAtlas.Models;
|
||||
|
||||
// Create ARM client with DefaultAzureCredential
|
||||
var credential = new DefaultAzureCredential();
|
||||
var armClient = new ArmClient(credential);
|
||||
```
|
||||
|
||||
## Core Types
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `MongoDBAtlasOrganizationResource` | ARM resource representing an Atlas organization |
|
||||
| `MongoDBAtlasOrganizationCollection` | Collection of organizations in a resource group |
|
||||
| `MongoDBAtlasOrganizationData` | Data model for organization resource |
|
||||
| `MongoDBAtlasOrganizationProperties` | Organization-specific properties |
|
||||
| `MongoDBAtlasMarketplaceDetails` | Azure Marketplace subscription details |
|
||||
| `MongoDBAtlasOfferDetails` | Marketplace offer configuration |
|
||||
| `MongoDBAtlasUserDetails` | User information for the organization |
|
||||
| `MongoDBAtlasPartnerProperties` | MongoDB-specific properties (org name, ID) |
|
||||
|
||||
## Workflows
|
||||
|
||||
### Get Organization Collection
|
||||
|
||||
```csharp
|
||||
// Get resource group
|
||||
var subscription = await armClient.GetDefaultSubscriptionAsync();
|
||||
var resourceGroup = await subscription.GetResourceGroupAsync("my-resource-group");
|
||||
|
||||
// Get organizations collection
|
||||
MongoDBAtlasOrganizationCollection organizations =
|
||||
resourceGroup.Value.GetMongoDBAtlasOrganizations();
|
||||
```
|
||||
|
||||
### Create Organization
|
||||
|
||||
```csharp
|
||||
var organizationName = "my-atlas-org";
|
||||
var location = AzureLocation.EastUS2;
|
||||
|
||||
// Build organization data
|
||||
var organizationData = new MongoDBAtlasOrganizationData(location)
|
||||
{
|
||||
Properties = new MongoDBAtlasOrganizationProperties(
|
||||
marketplace: new MongoDBAtlasMarketplaceDetails(
|
||||
subscriptionId: "your-azure-subscription-id",
|
||||
offerDetails: new MongoDBAtlasOfferDetails(
|
||||
publisherId: "mongodb",
|
||||
offerId: "mongodb_atlas_azure_native_prod",
|
||||
planId: "private_plan",
|
||||
planName: "Pay as You Go (Free) (Private)",
|
||||
termUnit: "P1M",
|
||||
termId: "gmz7xq9ge3py"
|
||||
)
|
||||
),
|
||||
user: new MongoDBAtlasUserDetails(
|
||||
emailAddress: "admin@example.com",
|
||||
upn: "admin@example.com"
|
||||
)
|
||||
{
|
||||
FirstName = "Admin",
|
||||
LastName = "User"
|
||||
}
|
||||
)
|
||||
{
|
||||
PartnerProperties = new MongoDBAtlasPartnerProperties
|
||||
{
|
||||
OrganizationName = organizationName
|
||||
}
|
||||
},
|
||||
Tags = { ["Environment"] = "Production" }
|
||||
};
|
||||
|
||||
// Create the organization (long-running operation)
|
||||
var operation = await organizations.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
organizationName,
|
||||
organizationData
|
||||
);
|
||||
|
||||
MongoDBAtlasOrganizationResource organization = operation.Value;
|
||||
Console.WriteLine($"Created: {organization.Id}");
|
||||
```
|
||||
|
||||
### Get Existing Organization
|
||||
|
||||
```csharp
|
||||
// Option 1: From collection
|
||||
MongoDBAtlasOrganizationResource org =
|
||||
await organizations.GetAsync("my-atlas-org");
|
||||
|
||||
// Option 2: From resource identifier
|
||||
var resourceId = MongoDBAtlasOrganizationResource.CreateResourceIdentifier(
|
||||
subscriptionId: "subscription-id",
|
||||
resourceGroupName: "my-resource-group",
|
||||
organizationName: "my-atlas-org"
|
||||
);
|
||||
MongoDBAtlasOrganizationResource org2 =
|
||||
armClient.GetMongoDBAtlasOrganizationResource(resourceId);
|
||||
await org2.GetAsync(); // Fetch data
|
||||
```
|
||||
|
||||
### List Organizations
|
||||
|
||||
```csharp
|
||||
// List in resource group
|
||||
await foreach (var org in organizations.GetAllAsync())
|
||||
{
|
||||
Console.WriteLine($"Org: {org.Data.Name}");
|
||||
Console.WriteLine($" Location: {org.Data.Location}");
|
||||
Console.WriteLine($" State: {org.Data.Properties?.ProvisioningState}");
|
||||
}
|
||||
|
||||
// List across subscription
|
||||
await foreach (var org in subscription.GetMongoDBAtlasOrganizationsAsync())
|
||||
{
|
||||
Console.WriteLine($"Org: {org.Data.Name} in {org.Data.Id}");
|
||||
}
|
||||
```
|
||||
|
||||
### Update Tags
|
||||
|
||||
```csharp
|
||||
// Add a single tag
|
||||
await organization.AddTagAsync("CostCenter", "12345");
|
||||
|
||||
// Replace all tags
|
||||
await organization.SetTagsAsync(new Dictionary<string, string>
|
||||
{
|
||||
["Environment"] = "Production",
|
||||
["Team"] = "Platform"
|
||||
});
|
||||
|
||||
// Remove a tag
|
||||
await organization.RemoveTagAsync("OldTag");
|
||||
```
|
||||
|
||||
### Update Organization Properties
|
||||
|
||||
```csharp
|
||||
var patch = new MongoDBAtlasOrganizationPatch
|
||||
{
|
||||
Tags = { ["UpdatedAt"] = DateTime.UtcNow.ToString("o") },
|
||||
Properties = new MongoDBAtlasOrganizationUpdateProperties
|
||||
{
|
||||
// Update user details if needed
|
||||
User = new MongoDBAtlasUserDetails(
|
||||
emailAddress: "newadmin@example.com",
|
||||
upn: "newadmin@example.com"
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
var updateOperation = await organization.UpdateAsync(
|
||||
WaitUntil.Completed,
|
||||
patch
|
||||
);
|
||||
```
|
||||
|
||||
### Delete Organization
|
||||
|
||||
```csharp
|
||||
// Delete (long-running operation)
|
||||
await organization.DeleteAsync(WaitUntil.Completed);
|
||||
```
|
||||
|
||||
## Model Properties Reference
|
||||
|
||||
### MongoDBAtlasOrganizationProperties
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `Marketplace` | `MongoDBAtlasMarketplaceDetails` | Required. Marketplace subscription details |
|
||||
| `User` | `MongoDBAtlasUserDetails` | Required. Organization admin user |
|
||||
| `PartnerProperties` | `MongoDBAtlasPartnerProperties` | MongoDB-specific properties |
|
||||
| `ProvisioningState` | `MongoDBAtlasResourceProvisioningState` | Read-only. Current provisioning state |
|
||||
|
||||
### MongoDBAtlasMarketplaceDetails
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `SubscriptionId` | `string` | Required. Azure subscription ID for billing |
|
||||
| `OfferDetails` | `MongoDBAtlasOfferDetails` | Required. Marketplace offer configuration |
|
||||
| `SubscriptionStatus` | `MarketplaceSubscriptionStatus` | Read-only. Subscription status |
|
||||
|
||||
### MongoDBAtlasOfferDetails
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `PublisherId` | `string` | Required. Publisher ID (typically "mongodb") |
|
||||
| `OfferId` | `string` | Required. Offer ID |
|
||||
| `PlanId` | `string` | Required. Plan ID |
|
||||
| `PlanName` | `string` | Required. Display name of the plan |
|
||||
| `TermUnit` | `string` | Required. Billing term unit (e.g., "P1M") |
|
||||
| `TermId` | `string` | Required. Term identifier |
|
||||
|
||||
### MongoDBAtlasUserDetails
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `EmailAddress` | `string` | Required. User email address |
|
||||
| `Upn` | `string` | Required. User principal name |
|
||||
| `FirstName` | `string` | Optional. User first name |
|
||||
| `LastName` | `string` | Optional. User last name |
|
||||
|
||||
### MongoDBAtlasPartnerProperties
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `OrganizationName` | `string` | Name of the MongoDB Atlas organization |
|
||||
| `OrganizationId` | `string` | Read-only. MongoDB Atlas organization ID |
|
||||
|
||||
## Provisioning States
|
||||
|
||||
| State | Description |
|
||||
|-------|-------------|
|
||||
| `Succeeded` | Resource provisioned successfully |
|
||||
| `Failed` | Provisioning failed |
|
||||
| `Canceled` | Provisioning was canceled |
|
||||
| `Provisioning` | Resource is being provisioned |
|
||||
| `Updating` | Resource is being updated |
|
||||
| `Deleting` | Resource is being deleted |
|
||||
| `Accepted` | Request accepted, provisioning starting |
|
||||
|
||||
## Marketplace Subscription Status
|
||||
|
||||
| Status | Description |
|
||||
|--------|-------------|
|
||||
| `PendingFulfillmentStart` | Subscription pending activation |
|
||||
| `Subscribed` | Active subscription |
|
||||
| `Suspended` | Subscription suspended |
|
||||
| `Unsubscribed` | Subscription canceled |
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Use Async Methods
|
||||
|
||||
```csharp
|
||||
// Prefer async for all operations
|
||||
var org = await organizations.GetAsync("my-org");
|
||||
await org.Value.AddTagAsync("key", "value");
|
||||
```
|
||||
|
||||
### Handle Long-Running Operations
|
||||
|
||||
```csharp
|
||||
// Wait for completion
|
||||
var operation = await organizations.CreateOrUpdateAsync(
|
||||
WaitUntil.Completed, // Blocks until done
|
||||
name,
|
||||
data
|
||||
);
|
||||
|
||||
// Or start and poll later
|
||||
var operation = await organizations.CreateOrUpdateAsync(
|
||||
WaitUntil.Started, // Returns immediately
|
||||
name,
|
||||
data
|
||||
);
|
||||
|
||||
// Poll for completion
|
||||
while (!operation.HasCompleted)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
await operation.UpdateStatusAsync();
|
||||
}
|
||||
```
|
||||
|
||||
### Check Provisioning State
|
||||
|
||||
```csharp
|
||||
var org = await organizations.GetAsync("my-org");
|
||||
if (org.Value.Data.Properties?.ProvisioningState ==
|
||||
MongoDBAtlasResourceProvisioningState.Succeeded)
|
||||
{
|
||||
Console.WriteLine("Organization is ready");
|
||||
}
|
||||
```
|
||||
|
||||
### Use Resource Identifiers
|
||||
|
||||
```csharp
|
||||
// Create identifier without API call
|
||||
var resourceId = MongoDBAtlasOrganizationResource.CreateResourceIdentifier(
|
||||
subscriptionId,
|
||||
resourceGroupName,
|
||||
organizationName
|
||||
);
|
||||
|
||||
// Get resource handle (no data yet)
|
||||
var orgResource = armClient.GetMongoDBAtlasOrganizationResource(resourceId);
|
||||
|
||||
// Fetch data when needed
|
||||
var response = await orgResource.GetAsync();
|
||||
```
|
||||
|
||||
## Common Errors
|
||||
|
||||
| Error | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| `ResourceNotFound` | Organization doesn't exist | Verify name and resource group |
|
||||
| `AuthorizationFailed` | Insufficient permissions | Check RBAC roles on resource group |
|
||||
| `InvalidParameter` | Missing required properties | Ensure all required fields are set |
|
||||
| `MarketplaceError` | Marketplace subscription issue | Verify offer details and subscription |
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [Microsoft Learn: MongoDB Atlas on Azure](https://learn.microsoft.com/en-us/azure/partner-solutions/mongodb-atlas/)
|
||||
- [API Reference](https://learn.microsoft.com/en-us/dotnet/api/azure.resourcemanager.mongodbatlas)
|
||||
- [Azure SDK for .NET](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/mongodbatlas)
|
||||
Reference in New Issue
Block a user