你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
适用于 .NET 的 Microsoft Azure 资源管理器客户端库
Microsoft Azure 资源管理器 是 Azure 的部署和管理服务。 它提供了一个管理层,用于在 Azure 帐户中创建、更新和删除资源。
此库为 Microsoft Azure 提供资源组和资源管理功能。
此库遵循 新的 Azure SDK 准则,并提供许多核心功能:
- Support MSAL.NET, Azure.Identity is out of box for supporting MSAL.NET.
- Support [OpenTelemetry](https://opentelemetry.io/) for distributed tracing.
- HTTP pipeline with custom policies.
- Better error-handling.
- Support uniform telemetry across all languages.
入门
安装包
使用 NuGet 安装适用于 .NET 的 Microsoft Azure 资源管理核心库:
dotnet add package Azure.ResourceManager
先决条件
必须具有 Microsoft Azure 订阅。
设置一种使用 Azure 标识向 Azure 进行身份验证的方法。
一些选项包括:
- 通过 Azure CLI 登录。
- 通过 Visual Studio。
- 设置 环境变量。
有关使用 Azure 标识的详细信息和不同的身份验证方法,请参阅此文档。
对客户端进行身份验证
创建已经过身份验证的客户端的默认选项是使用 DefaultAzureCredential
。 由于所有管理 API 都通过同一终结点,因此,若要与资源交互,只需创建一个顶级 ArmClient
。
若要向 Azure 进行身份验证并创建 , ArmClient
请执行以下代码:
using System;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.Compute;
using Azure.ResourceManager.Resources;
ArmClient client = new ArmClient(new DefaultAzureCredential());
本文档中提供了 有关 Azure.Identity.DefaultAzureCredential
类的更多 文档。
关键概念
理解 Azure 资源层次结构
为了减少执行常见任务所需的客户端数,以及每个客户端采用的冗余参数数,我们在 SDK 中引入了一个对象层次结构,用于模拟 Azure 中的对象层次结构。 SDK 中的每个资源客户端都有访问其子项的资源客户端的方法,这些客户端的范围已经限定为适当的订阅和资源组。
为实现此目标,我们将为 Azure 中的所有资源引入三种标准类型:
[资源]Resource.cs
此类表示完整的资源客户端对象,该对象包含 一个 Data 属性,该属性将详细信息公开为 [Resource]数据类型 。 它还可访问该资源上的所有操作,而无需传入范围参数,如订阅 ID 或资源名称。 此资源类便于直接对列表调用的结果执行操作,因为现在所有内容都作为完整的资源客户端返回。
ArmClient client = new ArmClient(new DefaultAzureCredential());
string resourceGroupName = "myResourceGroup";
SubscriptionResource subscription = await client.GetDefaultSubscriptionAsync();
ResourceGroupCollection resourceGroups = subscription.GetResourceGroups();
ResourceGroupResource resourceGroup = await resourceGroups.GetAsync(resourceGroupName);
await foreach (VirtualMachineResource virtualMachine in resourceGroup.GetVirtualMachines())
{
//previously we would have to take the resourceGroupName and the vmName from the vm object
//and pass those into the powerOff method as well as we would need to execute that on a separate compute client
await virtualMachine.PowerOffAsync(WaitUntil.Completed);
}
[Resource]Data.cs
此类表示构成给定资源的模型。 通常,此类是来自服务调用(例如 HTTP GET)的响应数据,并提供有关基础资源的详细信息。 以前,此类由 Model 类表示。
[Resource]Collection.cs
此类表示可以对属于特定父资源的资源集合执行的操作。 此类提供大部分逻辑收集操作。
集合行为 | 集合方法 |
---|---|
Iterate/List | GetAll() |
索引 | Get(string name) |
添加 | CreateOrUpdate(string name, [Resource]Data data) |
Contains | Exists(string name) |
对于大多数情况,父级将是 ResourceGroup。 例如 Subnet 是 VirtualNetwork 的子级,ResourceGroup 是 Subscription 的子级 。
汇总
假设公司要求所有虚拟机都添加有所有者标记。 我们的任务是编写一个程序,将标记添加到给定资源组中任何缺少标记的虚拟机。
// First we construct our client
ArmClient client = new ArmClient(new DefaultAzureCredential());
// Next we get a resource group object
// ResourceGroupResource is a [Resource] object from above
SubscriptionResource subscription = await client.GetDefaultSubscriptionAsync();
ResourceGroupCollection resourceGroups = subscription.GetResourceGroups();
ResourceGroupResource resourceGroup = await resourceGroups.GetAsync("myRgName");
// Next we get the collection for the virtual machines
// vmCollection is a [Resource]Collection object from above
VirtualMachineCollection virtualMachines = resourceGroup.GetVirtualMachines();
// Next we loop over all vms in the collection
// Each vm is a [Resource] object from above
await foreach (VirtualMachineResource virtualMachine in virtualMachines)
{
// We access the [Resource]Data properties from vm.Data
if (!virtualMachine.Data.Tags.ContainsKey("owner"))
{
// We can also access all operations from vm since it is already scoped for us
await virtualMachine.AddTagAsync("owner", "tagValue");
}
}
结构化资源标识符
资源 ID 包含有关资源本身的有用信息,但它们是必须进行分析的普通字符串。 可使用 ResourceIdentifier
对象进行分析:new ResourceIdentifier("myid");
,而不是实现自己的分析逻辑。
示例:使用 ResourceIdentifier 对象分析 ID
ResourceIdentifier id = new ResourceIdentifier("/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/workshop2021-rg/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mySubnet");
Console.WriteLine($"Subscription: {id.SubscriptionId}");
Console.WriteLine($"ResourceGroupResource: {id.ResourceGroupName}");
Console.WriteLine($"Vnet: {id.Parent.Name}");
Console.WriteLine($"Subnet: {id.Name}");
按资源标识符管理现有资源
使用管理客户端库时,对现有资源执行操作是一种常见用例。 在此场景中,通常将要处理的资源的标识符作为字符串。 尽管新的对象层次结构非常适合预配并在给定父级的范围内工作,但对于此特定方案,它并不是最高效的。
下面是一个示例,你如何访问对象 AvailabilitySet
并直接使用其 ID 对其进行管理:
ResourceIdentifier id = new ResourceIdentifier("/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/workshop2021-rg/providers/Microsoft.Compute/availabilitySets/ws2021availSet");
// We construct a new client to work with
ArmClient client = new ArmClient(new DefaultAzureCredential());
// Next we get the collection of subscriptions
SubscriptionCollection subscriptions = client.GetSubscriptions();
// Next we get the specific subscription this resource belongs to
SubscriptionResource subscription = await subscriptions.GetAsync(id.SubscriptionId);
// Next we get the collection of resource groups that belong to that subscription
ResourceGroupCollection resourceGroups = subscription.GetResourceGroups();
// Next we get the specific resource group this resource belongs to
ResourceGroupResource resourceGroup = await resourceGroups.GetAsync(id.ResourceGroupName);
// Next we get the collection of availability sets that belong to that resource group
AvailabilitySetCollection availabilitySets = resourceGroup.GetAvailabilitySets();
// Finally we get the resource itself
// Note: for this last step in this example, Azure.ResourceManager.Compute is needed
AvailabilitySetResource availabilitySet = await availabilitySets.GetAsync(id.Name);
此方法需要大量代码和三次对 Azure 的 API 调用。 通过利用已在客户端本身之上提供的扩展方法,使用更少的代码且无需任何 API 调用即可完成相同的操作。 利用这些扩展方法,可以传入资源标识符并检索限定范围的资源客户端。 返回的对象是上面提到的 [Resource] ,因为它尚未联系 Azure 来检索数据,但 Data 属性将为 null。
因此,上一个示例将以与下面类似的内容结束:
ResourceIdentifier resourceId = new ResourceIdentifier("/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/workshop2021-rg/providers/Microsoft.Compute/availabilitySets/ws2021availSet");
// We construct a new client to work with
ArmClient client = new ArmClient(new DefaultAzureCredential());
// Next we get the AvailabilitySetResource resource client from the client
// The method takes in a ResourceIdentifier but we can use the implicit cast from string
AvailabilitySetResource availabilitySet = client.GetAvailabilitySetResource(resourceId);
// At this point availabilitySet.Data will be null and trying to access it will throw
// If we want to retrieve the objects data we can simply call get
availabilitySet = await availabilitySet.GetAsync();
// we now have the data representing the availabilitySet
Console.WriteLine(availabilitySet.Data.Name);
我们还提供一个选项,如果只知道构成 ResourceIdentifier
每个资源的片段,则提供静态方法从这些部分构造完整字符串。
然后,上面的示例将如下所示。
string subscriptionId = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
string resourceGroupName = "workshop2021-rg";
string availabilitySetName = "ws2021availSet";
ResourceIdentifier resourceId = AvailabilitySetResource.CreateResourceIdentifier(subscriptionId, resourceGroupName, availabilitySetName);
// We construct a new client to work with
ArmClient client = new ArmClient(new DefaultAzureCredential());
// Next we get the AvailabilitySetResource resource client from the client
// The method takes in a ResourceIdentifier but we can use the implicit cast from string
AvailabilitySetResource availabilitySet = client.GetAvailabilitySetResource(resourceId);
// At this point availabilitySet.Data will be null and trying to access it will throw
// If we want to retrieve the objects data we can simply call get
availabilitySet = await availabilitySet.GetAsync();
// we now have the data representing the availabilitySet
Console.WriteLine(availabilitySet.Data.Name);
检查 [Resource] 是否存在
如果不确定要获取的资源是否存在,或者只想检查它是否存在,则可以使用Exists()
方法,该方法可从任何 [Resource]Collection 类调用。
Exists()
如果指定的资源不存在,则 ExistsAsync()
返回 Response<bool>
bool 为 false。 这两种方法仍会授予你访问基础原始响应的权限。
在引入这些方法之前,需要捕获 RequestFailedException
并检查 404 的状态代码。
ArmClient client = new ArmClient(new DefaultAzureCredential());
SubscriptionResource subscription = await client.GetDefaultSubscriptionAsync();
ResourceGroupCollection resourceGroups = subscription.GetResourceGroups();
string resourceGroupName = "myRgName";
try
{
ResourceGroupResource resourceGroup = await resourceGroups.GetAsync(resourceGroupName);
// At this point, we are sure that myRG is a not null Resource Group, so we can use this object to perform any operations we want.
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
Console.WriteLine($"Resource Group {resourceGroupName} does not exist.");
}
现在,通过这些方便的方法,我们可以执行以下代码。
ArmClient client = new ArmClient(new DefaultAzureCredential());
SubscriptionResource subscription = await client.GetDefaultSubscriptionAsync();
ResourceGroupCollection resourceGroups = subscription.GetResourceGroups();
string resourceGroupName = "myRgName";
bool exists = await resourceGroups.ExistsAsync(resourceGroupName);
if (exists)
{
Console.WriteLine($"Resource Group {resourceGroupName} exists.");
// We can get the resource group now that we know it exists.
// This does introduce a small race condition where resource group could have been deleted between the check and the get.
ResourceGroupResource resourceGroup = await resourceGroups.GetAsync(resourceGroupName);
}
else
{
Console.WriteLine($"Resource Group {resourceGroupName} does not exist.");
}
示例
创建资源组
// First, initialize the ArmClient and get the default subscription
ArmClient client = new ArmClient(new DefaultAzureCredential());
// Now we get a ResourceGroupResource collection for that subscription
SubscriptionResource subscription = await client.GetDefaultSubscriptionAsync();
ResourceGroupCollection resourceGroups = subscription.GetResourceGroups();
// With the collection, we can create a new resource group with an specific name
string resourceGroupName = "myRgName";
AzureLocation location = AzureLocation.WestUS2;
ResourceGroupData resourceGroupData = new ResourceGroupData(location);
ArmOperation<ResourceGroupResource> operation = await resourceGroups.CreateOrUpdateAsync(WaitUntil.Completed, resourceGroupName, resourceGroupData);
ResourceGroupResource resourceGroup = operation.Value;
列出所有资源组
// First, initialize the ArmClient and get the default subscription
ArmClient client = new ArmClient(new DefaultAzureCredential());
SubscriptionResource subscription = await client.GetDefaultSubscriptionAsync();
// Now we get a ResourceGroupResource collection for that subscription
ResourceGroupCollection resourceGroups = subscription.GetResourceGroups();
// We can then iterate over this collection to get the resources in the collection
await foreach (ResourceGroupResource resourceGroup in resourceGroups)
{
Console.WriteLine(resourceGroup.Data.Name);
}
更新资源组
// Note: Resource group named 'myRgName' should exist for this example to work.
ArmClient client = new ArmClient(new DefaultAzureCredential());
SubscriptionResource subscription = await client.GetDefaultSubscriptionAsync();
ResourceGroupCollection resourceGroups = subscription.GetResourceGroups();
string resourceGroupName = "myRgName";
ResourceGroupResource resourceGroup = await resourceGroups.GetAsync(resourceGroupName);
resourceGroup = await resourceGroup.AddTagAsync("key", "value");
删除资源组
ArmClient client = new ArmClient(new DefaultAzureCredential());
SubscriptionResource subscription = await client.GetDefaultSubscriptionAsync();
ResourceGroupCollection resourceGroups = subscription.GetResourceGroups();
string resourceGroupName = "myRgName";
ResourceGroupResource resourceGroup = await resourceGroups.GetAsync(resourceGroupName);
await resourceGroup.DeleteAsync(WaitUntil.Completed);
获取 GenericResource 列表
ArmClient client = new ArmClient(new DefaultAzureCredential());
SubscriptionResource sub = client.GetDefaultSubscription();
AsyncPageable<GenericResource> networkAndVmWithTestInName = sub.GetGenericResourcesAsync(
// Set filter to only return virtual network and virtual machine resource with 'test' in the name
filter: "(resourceType eq 'Microsoft.Network/virtualNetworks' or resourceType eq 'Microsoft.Compute/virtualMachines') and substringof('test', name)",
// Include 'createdTime' and 'changeTime' properties in the returned data
expand: "createdTime,changedTime"
);
int count = 0;
await foreach (var res in networkAndVmWithTestInName)
{
Console.WriteLine($"{res.Id.Name} in resource group {res.Id.ResourceGroupName} created at {res.Data.CreatedOn} and changed at {res.Data.ChangedOn}");
count++;
}
Console.WriteLine($"{count} resources found");
创建 GenericResource
ArmClient client = new ArmClient(new DefaultAzureCredential());
var subnetName = "samplesubnet";
var addressSpaces = new Dictionary<string, object>()
{
{ "addressPrefixes", new List<string>() { "10.0.0.0/16" } }
};
var subnet = new Dictionary<string, object>()
{
{ "name", subnetName },
{ "properties", new Dictionary<string, object>()
{
{ "addressPrefix", "10.0.1.0/24" }
}
}
};
var subnets = new List<object>() { subnet };
var data = new GenericResourceData(AzureLocation.EastUS)
{
Properties = BinaryData.FromObjectAsJson(new Dictionary<string, object>()
{
{ "addressSpace", addressSpaces },
{ "subnets", subnets }
})
};
ResourceIdentifier id = new ResourceIdentifier("/subscriptions/{subscription_id}/resourceGroups/{resourcegroup_name}/providers/Microsoft.Network/virtualNetworks/{vnet_name}");
var createResult = await client.GetGenericResources().CreateOrUpdateAsync(WaitUntil.Completed, id, data);
Console.WriteLine($"Resource {createResult.Value.Id.Name} in resource group {createResult.Value.Id.ResourceGroupName} created");
更新 GenericResource
ArmClient client = new ArmClient(new DefaultAzureCredential());
var subnetName = "samplesubnet";
var addressSpaces = new Dictionary<string, object>()
{
{ "addressPrefixes", new List<string>() { "10.0.0.0/16" } }
};
var subnet = new Dictionary<string, object>()
{
{ "name", subnetName },
{ "properties", new Dictionary<string, object>()
{
{ "addressPrefix", "10.0.1.0/24" }
}
}
};
var subnets = new List<object>() { subnet };
var data = new GenericResourceData(AzureLocation.EastUS)
{
Properties = BinaryData.FromObjectAsJson(new Dictionary<string, object>()
{
{ "addressSpace", addressSpaces },
{ "subnets", subnets }
})
};
ResourceIdentifier id = new ResourceIdentifier("/subscriptions/{subscription_id}/resourceGroups/{resourcegroup_name}/providers/Microsoft.Network/virtualNetworks/{vnet_name}");
var createResult = await client.GetGenericResources().CreateOrUpdateAsync(WaitUntil.Completed, id, data);
Console.WriteLine($"Resource {createResult.Value.Id.Name} in resource group {createResult.Value.Id.ResourceGroupName} updated");
更新 GenericResourc 标记
ArmClient client = new ArmClient(new DefaultAzureCredential());
ResourceIdentifier id = new ResourceIdentifier("/subscriptions/{subscription_id}/resourceGroups/{resourcegroup_name}/providers/Microsoft.Network/virtualNetworks/{vnet_name}");
GenericResource resource = client.GetGenericResources().Get(id).Value;
GenericResourceData updateTag = new GenericResourceData(AzureLocation.EastUS);
updateTag.Tags.Add("tag1", "sample-for-genericresource");
ArmOperation<GenericResource> updateTagResult = await resource.UpdateAsync(WaitUntil.Completed, updateTag);
Console.WriteLine($"Resource {updateTagResult.Value.Id.Name} in resource group {updateTagResult.Value.Id.ResourceGroupName} updated");
获取 GenericResource
ArmClient client = new ArmClient(new DefaultAzureCredential());
ResourceIdentifier id = new ResourceIdentifier("/subscriptions/{subscription_id}/resourceGroups/{resourcegroup_name}/providers/Microsoft.Network/virtualNetworks/{vnet_name}");
Response<GenericResource> getResultFromGenericResourceCollection = await client.GetGenericResources().GetAsync(id);
Console.WriteLine($"Resource {getResultFromGenericResourceCollection.Value.Id.Name} in resource group {getResultFromGenericResourceCollection.Value.Id.ResourceGroupName} got");
GenericResource resource = getResultFromGenericResourceCollection.Value;
Response<GenericResource> getResultFromGenericResource = await resource.GetAsync();
Console.WriteLine($"Resource {getResultFromGenericResource.Value.Id.Name} in resource group {getResultFromGenericResource.Value.Id.ResourceGroupName} got");
检查 GenericResource 是否存在
ArmClient client = new ArmClient(new DefaultAzureCredential());
ResourceIdentifier id = new ResourceIdentifier("/subscriptions/{subscription_id}/resourceGroups/{resourcegroup_name}/providers/Microsoft.Network/virtualNetworks/{vnet_name}");
bool existResult = await client.GetGenericResources().ExistsAsync(id);
Console.WriteLine($"Resource exists: {existResult}");
删除 GenericResource
ArmClient client = new ArmClient(new DefaultAzureCredential());
ResourceIdentifier id = new ResourceIdentifier("/subscriptions/{subscription_id}/resourceGroups/{resourcegroup_name}/providers/Microsoft.Network/virtualNetworks/{vnet_name}");
GenericResource resource = client.GetGenericResources().Get(id).Value;
var deleteResult = await resource.DeleteAsync(WaitUntil.Completed);
Console.WriteLine($"Resource deletion response status code: {deleteResult.WaitForCompletionResponse().Status}");
有关更详细的示例,请查看我们提供的示例。
Azure 资源管理器测试
若要运行测试,请执行: dotnet test
运行代码覆盖率测试并自动生成 html 报告: dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura
覆盖率报告将以 html 格式放置在相对于 azure-proto-core-test 的路径中/coverage
,以供查看
还可以使用适当的查看器插件查看 VS 或 VsCode 报表
运行时,还会在命令行上显示一个简明的报表。
使用单个文件或测试运行测试
若要运行代码覆盖率测试,并仅使用一个测试自动生成 html 报告,请执行以下操作: dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura --filter <test-to-run>
故障排除
- 通过 GitHub 问题提出问题。
- 使用 Azure 和 .NET 标记在 Stack Overflow 上检查 以前的问题 或提出新问题。
后续步骤
更多示例代码
其他文档
如果要从旧 SDK 迁移,检查此迁移指南。
有关 Microsoft Azure SDK 的详细信息,请参阅 此网站。
贡献
有关参与此存储库的详细信息,请参阅 参与指南。
本项目欢迎贡献和建议。 大多数贡献要求你同意贡献者许可协议 (CLA),并声明你有权(并且确实有权)授予我们使用你的贡献的权利。 有关详细信息,请访问 https://cla.microsoft.com 。
提交拉取请求时,CLA 机器人会自动确定是否需要提供 CLA 并适当修饰 PR, (例如标签、注释) 。 按照机器人提供的说明操作。 只需使用 CLA 在所有存储库中执行此操作一次。
此项目采用了 Microsoft 开放源代码行为准则。 有关详细信息,请参阅行为准则常见问题解答,如有任何其他问题或评论,请联系 opencode@microsoft.com。