azure-resource-manager-sql-dotnet
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.S
Install
npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-dotnet/skills/azure-resource-manager-sql-dotnet
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
git clone https://github.com/microsoft/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole microsoft/skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
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
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
AZURE_SUBSCRIPTION_ID=<your-subscription-id> # Required: Azure subscription ID
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
AZURE_TENANT_ID=<tenant-id> # For service principal auth (optional)
AZURE_CLIENT_ID=<client-id> # For service principal auth (optional)
AZURE_CLIENT_SECRET=<client-secret> # For service principal auth (optional)
Authentication
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.Sql;
// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
var credential = new DefaultAzureCredential(
DefaultAzureCredential.DefaultEnvironmentVariableName
);
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#credential-classes
// var credential = new ManagedIdentityCredential();
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
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
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
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
var databaseData = new SqlDatabaseData(AzureLocation.EastUS)
{
ElasticPoolId = pool.Id
};
await databaseCollection.CreateOrUpdateAsync(
WaitUntil.Completed,
"pooled-database",
databaseData);
5. Configure Firewall Rules
// 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
// 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
// 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
- Use
WaitUntil.Completedfor operations that must finish before proceeding - Use
WaitUntil.Startedwhen you want to poll manually or run operations in parallel - Always use
DefaultAzureCredential— never hardcode passwords in production - Handle
RequestFailedExceptionfor ARM API errors - Use
CreateOrUpdateAsyncfor idempotent operations - Navigate hierarchy via
Get*methods (e.g.,server.GetSqlDatabases()) - Use elastic pools for cost optimization when managing multiple databases
- Configure firewall rules before attempting connections
Error Handling
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 | Server CRUD, admin credentials, Azure AD auth, networking |
| references/database-operations.md | Database CRUD, scaling, backup, restore, copy |
| 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 |
Files (skills)
-
references
-
database-operations.md 6.8 KB
# Database Operations Advanced database operations for Azure SQL. ## Create Database with vCore SKU ```csharp using Azure.ResourceManager.Sql; using Azure.ResourceManager.Sql.Models; var databaseData = new SqlDatabaseData(AzureLocation.EastUS) { Sku = new SqlSku("GP_Gen5_2") { Tier = "GeneralPurpose", Family = "Gen5", Capacity = 2 // vCores }, MaxSizeBytes = 32L * 1024 * 1024 * 1024, // 32 GB ZoneRedundant = false, ReadScale = SqlDatabaseReadScale.Disabled, RequestedBackupStorageRedundancy = SqlBackupStorageRedundancy.Geo }; var operation = await databaseCollection.CreateOrUpdateAsync( WaitUntil.Completed, "my-vcore-database", databaseData); ``` ## Create Serverless Database ```csharp var databaseData = new SqlDatabaseData(AzureLocation.EastUS) { Sku = new SqlSku("GP_S_Gen5_2") { Tier = "GeneralPurpose", Family = "Gen5", Capacity = 2 // Max vCores }, AutoPauseDelay = 60, // Minutes of inactivity before auto-pause MinCapacity = 0.5, // Minimum vCores (can be fractional) MaxSizeBytes = 32L * 1024 * 1024 * 1024 }; await databaseCollection.CreateOrUpdateAsync( WaitUntil.Completed, "my-serverless-db", databaseData); ``` ## Scale Database ```csharp // Get existing database var database = await databaseCollection.GetAsync("my-database"); // Update SKU var updateData = new SqlDatabaseData(database.Value.Data.Location) { Sku = new SqlSku("S3") { Tier = "Standard" } }; await databaseCollection.CreateOrUpdateAsync( WaitUntil.Completed, "my-database", updateData); ``` ## Copy Database ```csharp var copyData = new SqlDatabaseData(AzureLocation.EastUS) { CreateMode = SqlDatabaseCreateMode.Copy, SourceDatabaseId = sourceDatabase.Id, Sku = new SqlSku("S0") { Tier = "Standard" } }; await targetDatabaseCollection.CreateOrUpdateAsync( WaitUntil.Completed, "database-copy", copyData); ``` ## Restore Database from Point-in-Time ```csharp var restoreData = new SqlDatabaseData(AzureLocation.EastUS) { CreateMode = SqlDatabaseCreateMode.PointInTimeRestore, SourceDatabaseId = sourceDatabase.Id, RestorePointInTime = DateTimeOffset.UtcNow.AddHours(-2), // 2 hours ago Sku = new SqlSku("S0") { Tier = "Standard" } }; await databaseCollection.CreateOrUpdateAsync( WaitUntil.Completed, "restored-database", restoreData); ``` ## Restore Deleted Database ```csharp // List deleted databases var deletedDatabases = server.GetRestorableDroppedDatabases(); await foreach (var deleted in deletedDatabases) { Console.WriteLine($"Deleted: {deleted.Data.DatabaseName} at {deleted.Data.DeletionOn}"); // Restore the deleted database var restoreData = new SqlDatabaseData(AzureLocation.EastUS) { CreateMode = SqlDatabaseCreateMode.Restore, RestorableDroppedDatabaseId = deleted.Id, Sku = new SqlSku("S0") { Tier = "Standard" } }; await databaseCollection.CreateOrUpdateAsync( WaitUntil.Completed, "restored-deleted-db", restoreData); break; // Restore first one } ``` ## Geo-Restore Database ```csharp // List recoverable databases (geo-replicated backups) var recoverableDatabases = server.GetRecoverableDatabases(); await foreach (var recoverable in recoverableDatabases) { var restoreData = new SqlDatabaseData(AzureLocation.WestUS) // Different region { CreateMode = SqlDatabaseCreateMode.Recovery, RecoverableDatabaseId = recoverable.Id, Sku = new SqlSku("S0") { Tier = "Standard" } }; await targetDatabaseCollection.CreateOrUpdateAsync( WaitUntil.Completed, "geo-restored-db", restoreData); break; } ``` ## Configure Long-Term Retention ```csharp var ltrPolicy = new LongTermRetentionPolicyData { WeeklyRetention = "P4W", // 4 weeks MonthlyRetention = "P12M", // 12 months YearlyRetention = "P5Y", // 5 years WeekOfYear = 1 // First week for yearly backup }; var ltrPolicyResource = database.GetLongTermRetentionPolicy(); await ltrPolicyResource.CreateOrUpdateAsync( WaitUntil.Completed, ltrPolicy); ``` ## Configure Short-Term Retention ```csharp var strPolicy = new ShortTermRetentionPolicyData { RetentionDays = 14, // 7-35 days DiffBackupIntervalInHours = 12 // 12 or 24 hours }; var strPolicyResource = database.GetShortTermRetentionPolicy(); await strPolicyResource.CreateOrUpdateAsync( WaitUntil.Completed, strPolicy); ``` ## Export Database to Bacpac ```csharp var exportRequest = new DatabaseExportDefinition { StorageKeyType = StorageKeyType.StorageAccessKey, StorageKey = "<storage-account-key>", StorageUri = new Uri("https://mystorageaccount.blob.core.windows.net/backups/mydb.bacpac"), AdministratorLogin = "sqladmin", AdministratorLoginPassword = "YourPassword123!" }; var exportOperation = await database.ExportAsync(WaitUntil.Completed, exportRequest); ``` ## Import Database from Bacpac ```csharp var importRequest = new ImportExistingDatabaseDefinition { StorageKeyType = StorageKeyType.StorageAccessKey, StorageKey = "<storage-account-key>", StorageUri = new Uri("https://mystorageaccount.blob.core.windows.net/backups/mydb.bacpac"), AdministratorLogin = "sqladmin", AdministratorLoginPassword = "YourPassword123!" }; // Import into existing database var importOperation = await database.ImportAsync(WaitUntil.Completed, importRequest); ``` ## Rename Database ```csharp var renameRequest = new ResourceMoveDefinition( new ResourceIdentifier($"{server.Id}/databases/new-database-name")); await database.RenameAsync(renameRequest); ``` ## Delete Database ```csharp var database = await databaseCollection.GetAsync("my-database"); await database.Value.DeleteAsync(WaitUntil.Completed); ``` ## Transparent Data Encryption ```csharp // TDE is enabled by default for new databases // To check or modify: var tdeResource = database.GetSqlDatabaseTransparentDataEncryption(); var tde = await tdeResource.GetAsync(); Console.WriteLine($"TDE State: {tde.Value.Data.State}"); // Disable TDE (not recommended) var tdeData = new SqlDatabaseTransparentDataEncryptionData { State = TransparentDataEncryptionState.Disabled }; await tdeResource.CreateOrUpdateAsync(WaitUntil.Completed, tdeData); ``` ## Database Threat Detection ```csharp var threatDetectionData = new SqlDatabaseSecurityAlertPolicyData { State = SecurityAlertsPolicyState.Enabled, DisabledAlerts = { }, // Empty = all alerts enabled EmailAddresses = { "security@contoso.com" }, EmailAccountAdmins = true, RetentionDays = 30, StorageEndpoint = "https://mystorageaccount.blob.core.windows.net", StorageAccountAccessKey = "<storage-key>" }; var securityPolicy = database.GetSqlDatabaseSecurityAlertPolicy(); await securityPolicy.CreateOrUpdateAsync(WaitUntil.Completed, threatDetectionData); ``` -
elastic-pools.md 5.8 KB
# Elastic Pools Elastic pool management for Azure SQL. ## Create DTU-Based Elastic Pool ```csharp using Azure.ResourceManager.Sql; using Azure.ResourceManager.Sql.Models; var poolData = new ElasticPoolData(AzureLocation.EastUS) { Sku = new SqlSku("StandardPool") { Tier = "Standard", Capacity = 100 // 100 eDTUs }, PerDatabaseSettings = new ElasticPoolPerDatabaseSettings { MinCapacity = 0, // Min eDTUs per database MaxCapacity = 100 // Max eDTUs per database } }; var poolCollection = server.GetElasticPools(); var operation = await poolCollection.CreateOrUpdateAsync( WaitUntil.Completed, "my-standard-pool", poolData); ElasticPoolResource pool = operation.Value; ``` ## Create vCore-Based Elastic Pool ```csharp var poolData = new ElasticPoolData(AzureLocation.EastUS) { Sku = new SqlSku("GP_Gen5_2") { Tier = "GeneralPurpose", Family = "Gen5", Capacity = 2 // vCores }, PerDatabaseSettings = new ElasticPoolPerDatabaseSettings { MinCapacity = 0, MaxCapacity = 2 }, ZoneRedundant = false, LicenseType = DatabaseLicenseType.LicenseIncluded // or BasePrice for Azure Hybrid Benefit }; await poolCollection.CreateOrUpdateAsync( WaitUntil.Completed, "my-vcore-pool", poolData); ``` ## Create Premium Elastic Pool with Zone Redundancy ```csharp var poolData = new ElasticPoolData(AzureLocation.EastUS) { Sku = new SqlSku("PremiumPool") { Tier = "Premium", Capacity = 125 // 125 eDTUs }, PerDatabaseSettings = new ElasticPoolPerDatabaseSettings { MinCapacity = 0, MaxCapacity = 125 }, ZoneRedundant = true }; await poolCollection.CreateOrUpdateAsync( WaitUntil.Completed, "my-premium-pool", poolData); ``` ## Add Database to Elastic Pool ```csharp // Create new database in pool var databaseData = new SqlDatabaseData(AzureLocation.EastUS) { ElasticPoolId = pool.Id }; await databaseCollection.CreateOrUpdateAsync( WaitUntil.Completed, "pooled-database", databaseData); ``` ## Move Existing Database to Elastic Pool ```csharp // Get existing database var database = await databaseCollection.GetAsync("standalone-database"); // Update to use elastic pool var updateData = new SqlDatabaseData(database.Value.Data.Location) { ElasticPoolId = pool.Id }; await databaseCollection.CreateOrUpdateAsync( WaitUntil.Completed, "standalone-database", updateData); ``` ## Remove Database from Elastic Pool ```csharp // Move database out of pool by assigning a standalone SKU var updateData = new SqlDatabaseData(database.Value.Data.Location) { Sku = new SqlSku("S0") { Tier = "Standard" }, ElasticPoolId = null }; await databaseCollection.CreateOrUpdateAsync( WaitUntil.Completed, "pooled-database", updateData); ``` ## Scale Elastic Pool ```csharp var pool = await poolCollection.GetAsync("my-elastic-pool"); var updateData = new ElasticPoolData(pool.Value.Data.Location) { Sku = new SqlSku("StandardPool") { Tier = "Standard", Capacity = 200 // Scale up to 200 eDTUs }, PerDatabaseSettings = new ElasticPoolPerDatabaseSettings { MinCapacity = 10, MaxCapacity = 100 } }; await poolCollection.CreateOrUpdateAsync( WaitUntil.Completed, "my-elastic-pool", updateData); ``` ## List Databases in Elastic Pool ```csharp var pool = await poolCollection.GetAsync("my-elastic-pool"); // Get databases in this pool var poolDatabases = pool.Value.GetElasticPoolDatabases(); await foreach (var db in poolDatabases) { Console.WriteLine($"Database: {db.Data.Name}"); Console.WriteLine($" Current SKU: {db.Data.CurrentSku?.Name}"); Console.WriteLine($" Max Size: {db.Data.MaxSizeBytes / (1024 * 1024 * 1024)} GB"); } ``` ## Monitor Elastic Pool Metrics ```csharp // Get pool resource utilization var pool = await poolCollection.GetAsync("my-elastic-pool"); // Pool metrics are available via Azure Monitor // Use Azure.ResourceManager.Monitor for detailed metrics Console.WriteLine($"Pool: {pool.Value.Data.Name}"); Console.WriteLine($"State: {pool.Value.Data.State}"); Console.WriteLine($"Max Size: {pool.Value.Data.MaxSizeBytes / (1024 * 1024 * 1024)} GB"); Console.WriteLine($"DTU Capacity: {pool.Value.Data.Sku?.Capacity}"); ``` ## Delete Elastic Pool ```csharp // First, move all databases out of the pool or delete them var pool = await poolCollection.GetAsync("my-elastic-pool"); // Check for databases var poolDatabases = pool.Value.GetElasticPoolDatabases(); await foreach (var db in poolDatabases) { // Either delete or move to standalone await db.DeleteAsync(WaitUntil.Completed); } // Now delete the pool await pool.Value.DeleteAsync(WaitUntil.Completed); ``` ## Elastic Pool SKU Reference ### DTU-Based Pools | SKU Name | Tier | eDTU Range | Max DBs | |----------|------|------------|---------| | `BasicPool` | Basic | 50-1600 | 500 | | `StandardPool` | Standard | 50-3000 | 500 | | `PremiumPool` | Premium | 125-4000 | 100 | ### vCore-Based Pools | SKU Pattern | Tier | vCore Range | |-------------|------|-------------| | `GP_Gen5_{n}` | GeneralPurpose | 2-80 | | `GP_Fsv2_{n}` | GeneralPurpose | 8-72 | | `GP_DC_{n}` | GeneralPurpose | 2-8 | | `BC_Gen5_{n}` | BusinessCritical | 2-80 | | `BC_DC_{n}` | BusinessCritical | 2-8 | ## Best Practices 1. **Right-size pools**: Start with estimated total DTU/vCore needs, monitor, and adjust 2. **Set per-database limits**: Prevent a single database from consuming all resources 3. **Use zone redundancy** for production Premium/BusinessCritical pools 4. **Monitor utilization**: Scale up when consistently above 80% utilization 5. **Consider Hyperscale**: For databases > 4TB or needing rapid scale 6. **Use Azure Hybrid Benefit**: Set `LicenseType = BasePrice` if you have SQL Server licenses -
server-management.md 5.2 KB
# Server Management Advanced server operations for Azure SQL. ## Create Server with Azure AD Authentication ```csharp using Azure.ResourceManager.Sql; using Azure.ResourceManager.Sql.Models; var serverData = new SqlServerData(AzureLocation.EastUS) { // SQL authentication (optional, can be disabled) AdministratorLogin = "sqladmin", AdministratorLoginPassword = "YourSecurePassword123!", // Azure AD authentication Administrators = new ServerExternalAdministrator { AdministratorType = SqlAdministratorType.ActiveDirectory, Login = "admin@contoso.com", Sid = Guid.Parse("<azure-ad-user-object-id>"), TenantId = Guid.Parse("<azure-ad-tenant-id>"), AzureADOnlyAuthentication = false // Set true to disable SQL auth }, Version = "12.0", MinimalTlsVersion = SqlMinimalTlsVersion.Tls1_2 }; var operation = await serverCollection.CreateOrUpdateAsync( WaitUntil.Completed, "my-sql-server", serverData); ``` ## Update Server Administrator Password ```csharp // Get existing server var server = await serverCollection.GetAsync("my-sql-server"); // Update password (requires full server data) var updateData = server.Value.Data; // Note: Password cannot be read, only set // You need to create new SqlServerData with the new password var newServerData = new SqlServerData(server.Value.Data.Location) { AdministratorLogin = server.Value.Data.AdministratorLogin, AdministratorLoginPassword = "NewSecurePassword456!" }; await serverCollection.CreateOrUpdateAsync( WaitUntil.Completed, "my-sql-server", newServerData); ``` ## Configure Public Network Access ```csharp var serverData = new SqlServerData(AzureLocation.EastUS) { AdministratorLogin = "sqladmin", AdministratorLoginPassword = "YourSecurePassword123!", // Disable public access (use private endpoints only) PublicNetworkAccess = ServerNetworkAccessFlag.Disabled, // Or enable with restrictions // PublicNetworkAccess = ServerNetworkAccessFlag.Enabled, // RestrictOutboundNetworkAccess = ServerNetworkAccessFlag.Enabled }; ``` ## Get Server by Resource ID ```csharp var resourceId = SqlServerResource.CreateResourceIdentifier( subscriptionId, "my-resource-group", "my-sql-server"); var server = armClient.GetSqlServerResource(resourceId); var serverData = await server.GetAsync(); ``` ## Delete Server ```csharp var server = await serverCollection.GetAsync("my-sql-server"); await server.Value.DeleteAsync(WaitUntil.Completed); ``` ## Check Server Name Availability ```csharp var checkRequest = new SqlNameAvailabilityContent { Name = "proposed-server-name", ResourceType = "Microsoft.Sql/servers" }; var result = await subscription.CheckSqlNameAvailabilityAsync(checkRequest); if (result.Value.IsAvailable == true) { Console.WriteLine("Name is available"); } else { Console.WriteLine($"Name unavailable: {result.Value.Reason}"); } ``` ## Virtual Network Rules ```csharp // Allow access from a specific subnet var vnetRuleData = new SqlServerVirtualNetworkRuleData { VirtualNetworkSubnetId = new ResourceIdentifier( "/subscriptions/{sub}/resourceGroups/{rg}/providers/" + "Microsoft.Network/virtualNetworks/{vnet}/subnets/{subnet}"), IgnoreMissingVnetServiceEndpoint = false }; var vnetRuleCollection = server.GetSqlServerVirtualNetworkRules(); await vnetRuleCollection.CreateOrUpdateAsync( WaitUntil.Completed, "allow-app-subnet", vnetRuleData); ``` ## Private Endpoints ```csharp // Private endpoints are created via Microsoft.Network, not SQL SDK // The SQL server just needs PublicNetworkAccess = Disabled // After creating private endpoint, approve the connection: var privateEndpointConnections = server.GetSqlServerPrivateEndpointConnections(); await foreach (var connection in privateEndpointConnections) { if (connection.Data.PrivateLinkServiceConnectionState.Status == "Pending") { // Approve the connection var approvalData = connection.Data; approvalData.PrivateLinkServiceConnectionState.Status = "Approved"; approvalData.PrivateLinkServiceConnectionState.Description = "Approved by admin"; await privateEndpointConnections.CreateOrUpdateAsync( WaitUntil.Completed, connection.Data.Name, approvalData); } } ``` ## Server Auditing ```csharp var auditingData = new SqlServerBlobAuditingPolicyData { State = BlobAuditingPolicyState.Enabled, StorageEndpoint = "https://mystorageaccount.blob.core.windows.net", StorageAccountAccessKey = "<storage-account-key>", RetentionDays = 90, IsStorageSecondaryKeyInUse = false, IsAzureMonitorTargetEnabled = true }; var auditingPolicy = server.GetSqlServerBlobAuditingPolicy(); await auditingPolicy.CreateOrUpdateAsync(WaitUntil.Completed, auditingData); ``` ## Transparent Data Encryption (Server-Level Key) ```csharp // Configure customer-managed key for TDE var protectorData = new EncryptionProtectorData { ServerKeyType = SqlServerKeyType.AzureKeyVault, ServerKeyName = "mykeyvault_mykey_abc123", AutoRotationEnabled = true }; var protector = server.GetEncryptionProtector(); await protector.CreateOrUpdateAsync(WaitUntil.Completed, protectorData); ```
-
-
SKILL.md 10.3 KB
--- 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". license: MIT metadata: author: Microsoft version: "1.0.0" 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> # Required: Azure subscription ID AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production AZURE_TENANT_ID=<tenant-id> # For service principal auth (optional) AZURE_CLIENT_ID=<client-id> # For service principal auth (optional) AZURE_CLIENT_SECRET=<client-secret> # For service principal auth (optional) ``` ## Authentication ```csharp using Azure.Identity; using Azure.ResourceManager; using Azure.ResourceManager.Sql; // Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential> var credential = new DefaultAzureCredential( DefaultAzureCredential.DefaultEnvironmentVariableName ); // Or use a specific credential directly in production: // See https://learn.microsoft.com/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#credential-classes // var credential = new ManagedIdentityCredential(); 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` |
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.