使用 Azure.ResourceManager.Sql (.NET) 管理平面 SDK 编排 Azure SQL 资源
【免费下载链接】agentic-awesome-skillsAAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,600+ agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.项目地址: https://gitcode.com/GitHub_Trending/an/agentic-awesome-skills
导读
本文基于 agentic-awesome-skills 仓库中的azure-resource-manager-sql-dotnet技能文档,系统讲解在 .NET 应用中通过Azure.ResourceManager.Sql(Azure Resource Manager 管理平面 SDK)以编程方式创建、配置与治理 Azure SQL Server、数据库、弹性池、防火墙规则与故障转移组等资源。你将掌握从环境配置、DefaultAzureCredential认证、资源层级导航,到完整 CRUD 工作流、SKU 选型、错误处理与生产级最佳实践的一整套实操能力,可直接用于编写自动化部署脚本、运维工具或 CI/CD 中的 SQL 资源编排步骤。
管理平面与数据平面:先分清 SDK 的边界
在动手写代码前,必须先理解Azure.ResourceManager.Sql在整个 Azure SQL 体系中的定位。该技能文档开篇就明确强调了管理平面(Management Plane)与数据平面(Data Plane)的严格区分:
- 管理平面 SDK(
Azure.ResourceManager.Sql):负责“建”和“管”资源——创建 SQL Server、创建数据库、管理弹性池、配置防火墙规则、管理故障转移组、设置审计策略与透明数据加密(TDE)等。它操作的是一整套 ARM 资源对象。 - 数据平面 SDK(
Microsoft.Data.SqlClient):负责“用”数据——建立连接、执行查询、调用存储过程、管理事务。它直接面向*.database.windows.net上的数据库引擎。
两者的职责边界决定了它们不能互相替代:你无法用Azure.ResourceManager.Sql执行SELECT,也无法用Microsoft.Data.SqlClient创建弹性池。实际项目中通常组合使用:先用管理平面 SDK 完成资源编排,再用数据平面 SDK 执行初始化脚本或业务查询。仓库中与该技能同属 Azure 系列的 azure-sql 等技能文档,可帮助你进一步理解 Azure SQL 生态中各 SDK 的协作关系。
安装与环境变量配置
安装 NuGet 包
dotnet add package Azure.ResourceManager.Sql dotnet add package Azure.IdentityAzure.ResourceManager.Sql:本技能的核心包,提供全部 SQL 管理资源类型。Azure.Identity:提供DefaultAzureCredential等身份认证组件,是使用 ARM SDK 的必需配套。
技能文档标注的当前版本信息为Stable v1.3.0、Preview v1.4.0-beta.3。安装前可通过dotnet list package或 NuGet 官方源确认实际可用版本,并优先选择与你的 .NET 目标框架(如 net8.0/net9.0)兼容的稳定版本。
环境变量
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>AZURE_SUBSCRIPTION_ID:必填。用于构造订阅资源标识符(/subscriptions/{id}),是后续一切资源操作的作用域起点。AZURE_TENANT_ID/AZURE_CLIENT_ID/AZURE_CLIENT_SECRET:可选,仅在采用服务主体(Service Principal)认证时需要。DefaultAzureCredential会自动读取这些变量完成服务主体认证;若在本地开发环境,则优先使用 Visual Studio / Azure CLI 登录凭据,无需配置这三项。
认证:始终使用 DefaultAzureCredential
管理平面 SDK 的入口是ArmClient,而构建它的第一步是获得一个可信凭据:
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}"));几点需要理解的关键机制:
DefaultAzureCredential的链式尝试:它会按顺序尝试多种凭据来源(环境变量服务主体 → Azure CLI → Visual Studio → Managed Identity 等),从而让同一段代码在本地开发、CI/CD 与 Azure 云环境之间无缝切换,这正是技能文档强调“永远不要在生产环境硬编码密码”的原因。ResourceIdentifier:ARM 中所有资源都有全局唯一的 ARM ID,形如/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Sql/servers/{name}。SDK 通过ResourceIdentifier解析这些 ID 并在资源之间导航。- 权限模型:操作 SQL 资源需要订阅/资源组级别的 ARM 权限(如 Contributor 或自定义 RBAC 角色),这与数据平面的数据库用户权限(login/user 体系)是完全独立的两套授权。
资源层级:从 ArmClient 向下导航
ARM SDK 的核心设计是层级化集合模型——每一层资源都通过其父资源的Get{Child}s()方法(即集合)访问。技能文档给出的层级如下:
ArmClient └── SubscriptionResource └── ResourceGroupResource └── SqlServerResource ├── SqlDatabaseResource ├── ElasticPoolResource │ └── ElasticPoolDatabaseResource ├── SqlFirewallRuleResource ├── FailoverGroupResource ├── ServerBlobAuditingPolicyResource ├── EncryptionProtectorResource └── VirtualNetworkRuleResource理解这条链的意义在于:集合(*Collection)负责 CRUD,资源(*Resource)负责实例操作与再向下导航。例如要创建一个数据库,必须先拿到SqlServerResource,再通过server.GetSqlDatabases()获得SqlDatabaseCollection执行CreateOrUpdateAsync。这种“先定位父资源、再操作子资源”的模式贯穿整个 SDK,与 azure-resource-manager-cosmosdb-dotnet 等同系列管理平面技能一致,学习成本可以横向复用。
核心工作流:七个典型操作全解析
以下代码均来自技能文档,并补充了参数含义、取值范围与运行前提,可直接作为脚本骨架。
1. 创建 SQL Server
using System; 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 = Environment.GetEnvironmentVariable("SQL_ADMIN_PASSWORD") ?? throw new InvalidOperationException("SQL_ADMIN_PASSWORD is required"), 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;- 前提:
my-resource-group必须已存在;若不存在,可先用resourceGroupCollection.CreateOrUpdateAsync创建资源组。 AdministratorLoginPassword:建议从环境变量或密钥库(如 Azure Key Vault)读取,文档代码用?? throw强制要求该变量存在,避免空密码下发。Version = "12.0":当前 Azure SQL 逻辑服务器的引擎版本。MinimalTlsVersion = SqlMinimalTlsVersion.Tls1_2:强制最低 TLS 1.2,满足安全基线要求。PublicNetworkAccess:控制是否允许公网访问;生产环境若需完全私网化可设为Disabled并配合VirtualNetworkRuleResource使用。- 长时运行操作(LRO):
CreateOrUpdateAsync返回一个ArmOperation,WaitUntil.Completed表示阻塞等待操作完成;WaitUntil.Started则立即返回、由调用方手动轮询或并行执行。
2. 创建 SQL 数据库
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;Sku与Tier:决定数据库的性能层与计费模型(见下文 SKU 参考表)。S0属于按 DTU 计费的 Standard 层;若采用 vCore 模型,则使用GP_Gen5_2这类 SKU。MaxSizeBytes:数据库最大容量,单位为字节;2L * 1024 * 1024 * 1024即 2 GB。注意 Basic/Standard 层的容量上限受 SKU 约束。Collation:默认SQL_Latin1_General_CP1_CI_AS(不区分大小写);创建后不可修改,务必提前规划。RequestedBackupStorageRedundancy:备份存储冗余策略(Local/Zone/Geo),影响可用性与成本。
3. 创建弹性池
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;Capacity:池的总容量,Standard 层单位为 eDTU(弹性 DTU);此处 100 表示池共享 100 eDTU。PerDatabaseSettings:单库在池内可占用的容量区间(MinCapacity–MaxCapacity),让池内数据库弹性伸缩而无需独立预配资源。若省略,SDK 会使用默认的最小/最大设置。
4. 将数据库加入弹性池
var databaseData = new SqlDatabaseData(AzureLocation.EastUS) { ElasticPoolId = pool.Id }; await databaseCollection.CreateOrUpdateAsync( WaitUntil.Completed, "pooled-database", databaseData);将ElasticPoolId指向已有池即可把数据库移入池内,这是弹性池成本优化的核心用法——多个低负载数据库共享一份池容量,避免各自按独立 SKU 计费。同样地,将该字段置为null即可把数据库移出池。
5. 配置防火墙规则
// 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);0.0.0.0–0.0.0.0是特殊规则:表示“允许所有 Azure 服务访问”,并非真正开放全公网;通常在数据平面无法连通时作为排查第一步。- 客户端规则应遵循最小权限原则,仅放行实际出口 IP 区间(示例使用文档段
203.0.113.0/24仅作演示,请替换为真实 IP)。 - 技能文档特别提示:在尝试建立任何连接之前,先配置好防火墙规则,否则数据平面连接会因 IP 未放行而被拒绝。
6. 枚举资源
// 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}"); }GetSqlServersAsync()是订阅级别的便捷方法(跨资源组枚举),而server.GetSqlDatabases()/server.GetElasticPools()是服务器级别枚举。所有枚举方法都支持await foreach异步流式遍历,返回的资源对象通过.Data暴露属性(如Name、Location、Sku)。
7. 生成连接字符串
// 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;";- 逻辑服务器 FQDN 遵循
{server-name}.database.windows.net的确定性规则,因此可由资源名直接拼出。 Encrypt=True+TrustServerCertificate=False是面向生产的安全组合(强制 TLS 加密且不信任自签名证书),与前面MinimalTlsVersion = Tls1_2的设置形成闭环。User ID/Password为 SQL 登录凭据;若改用 Microsoft Entra ID(Azure AD)认证,可替换为Authentication=Active Directory Default等项并使用Microsoft.Data.SqlClient的对应认证模式。
关键类型参考
技能文档整理的核心类型及其职责如下,理解它们能显著降低上手成本:
| 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) |
命名规律:{资源}Resource(实例)、{资源}Collection(集合)、{资源}Data(创建/更新负载)三件套构成了该 SDK 几乎全部资源类型的统一模式,掌握此规律即可举一反三地使用FailoverGroupResource、ServerBlobAuditingPolicyResource、EncryptionProtectorResource、VirtualNetworkRuleResource等其余类型。
SKU 选型参考
数据库 SKU
| 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 |
弹性池 SKU
| 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 |
选型要点:
- DTU 模型(
Basic/Standard/Premium及*Pool):计费简单,适合中小型工作负载;S0 起步 10 DTU,Premium 最高 4000 DTU。 - vCore 模型(
GP_Gen5_2/BC_Gen5_2/HS_Gen5_2):按核数与内存计费,支持预留实例折扣,Gen5表示硬件代次、_2表示 2 个 vCore。Hyperscale(HS_)面向超大数据库与高并发读取场景。 - 弹性池以 eDTU(弹性 DTU)为单位,
PerDatabaseSettings决定池内单库容量区间。实际选型应以工作负载基准测试与成本测算为准,而非仅凭 SKU 名称。
最佳实践清单
技能文档给出的八条实践原则,对应到本文各环节如下:
WaitUntil.Completed:必须在操作真正完成后再继续执行的场景(如创建服务器后再创建数据库),用它阻塞等待。WaitUntil.Started:需要手动轮询或并行执行多个创建任务时使用,避免串行等待拖慢整体编排。- 始终使用
DefaultAzureCredential:生产环境绝不硬编码密码;本地用 IDE/CLI 凭据,云端用托管身份,CI 用服务主体。 - 处理
RequestFailedException:ARM API 的绝大多数错误都以该异常抛出,必须显式捕获(见下文)。 - 使用
CreateOrUpdateAsync实现幂等:同一资源重复调用不会报“已存在”,而是执行更新语义,天然适合重试与状态收敛。 - 通过
Get*方法导航层级:不要手工拼接子资源 ID,统一用server.GetSqlDatabases()这类方法获取集合,保证路径正确。 - 多数据库用弹性池降本:低利用率的多数据库共享池容量,是明显的成本优化手段。
- 先配防火墙再连数据平面:避免连接超时排查时误判问题根因。
错误处理:读懂 ARM 异常
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}"); }ex.Status:HTTP 状态码,是快速定位问题的第一线索:400Bad Request:请求参数非法(如 SKU 与层级不匹配、容量超出配额);404Not Found:目标资源/资源组不存在;409Conflict:资源已存在(结合CreateOrUpdateAsync的幂等语义,通常可忽略或记录后继续);403Forbidden:RBAC 权限不足,需检查服务主体/身份的角色分配。
ex.ErrorCode:ARM 返回的机器可读错误码,可用于程序化分支或写入监控告警。- 配合
WaitUntil.Started的轮询场景,还应关注轮询过程中的中间状态,并对超时做兜底处理。
相关 SDK 与延伸阅读
| 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 |
典型组合:管理平面 SDK 完成“建库建表空间”等资源编排 → 数据平面 SDK 执行初始化 DDL/DML → ORM(EF Core)承载业务数据访问。三者职责互补,构成完整的 Azure SQL .NET 技术栈。
技能文档还规划了配套的参考文件目录(references/server-management.md、references/database-operations.md、references/elastic-pools.md),分别深入覆盖服务器 CRUD 与管理员凭据/Azure AD 认证/网络、数据库 CRUD 与扩容/备份/还原/复制、弹性池管理与扩缩容等进阶主题;本技能当前版本暂未附带这些文件,需要进阶场景时可结合 Azure 官方文档与仓库中同系列技能(如 azure-sql、azure-resource-manager-cosmosdb-dotnet)交叉参考。
使用边界与注意事项
- 适用场景:本技能面向明确的 Azure SQL 资源管理平面编排任务(创建/更新/查询服务器、数据库、弹性池、防火墙、故障转移组等),当任务与上述范围匹配时使用。
- 不可替代验证:脚本产出不能替代针对具体环境的验证、测试与专家评审——尤其是网络策略、RBAC 权限与成本估算,必须结合目标订阅实际情况确认。
- 信息不足时及时澄清:当缺少必要的输入参数(如订阅 ID、资源组名、SQL 管理员密码)、权限、安全边界或成功标准时,应停止执行并向用户确认,避免在错误假设下产生破坏性变更。
本文所有代码与结论均以仓库中 azure-resource-manager-sql-dotnet 技能文档(plugins/agentic-awesome-skills-claude/skills/azure-resource-manager-sql-dotnet/SKILL.md与之内容一致)为事实基础,示例中的占位值(IP、密码、资源名)需按实际环境替换后方可运行。
【免费下载链接】agentic-awesome-skillsAAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,600+ agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.项目地址: https://gitcode.com/GitHub_Trending/an/agentic-awesome-skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考