Biblioteca de clientes do Microsoft Azure Resource Manager para .NET

O Microsoft Azure Resource Manager é o serviço de implantação e gerenciamento do Azure. Ele fornece uma camada de gerenciamento que lhe permite criar, atualizar e excluir recursos em sua conta do Azure.

Essa biblioteca fornece recursos de gerenciamento de recursos e grupo de recursos para o Microsoft Azure.

Essa biblioteca segue as novas diretrizes do SDK do Azure e fornece muitos recursos principais:

- 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.

Introdução

Instalar o pacote

Instale a biblioteca principal de gerenciamento de Recursos do Microsoft Azure para .NET com o NuGet:

dotnet add package Azure.ResourceManager

Pré-requisitos

Autenticar o Cliente

A opção padrão para criar um cliente autenticado é usar DefaultAzureCredential. Como todas as APIs de gerenciamento passam pelo mesmo endpoint, para interagir com recursos, apenas um ArmClient de nível superior precisa ser criado.

Para autenticar no Azure e criar um ArmClient, faça o seguinte código:

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());

Mais documentação para a Azure.Identity.DefaultAzureCredential classe podem ser encontradas neste documento.

Principais conceitos

Noções básicas sobre a hierarquia de recursos do Azure

Para reduzir o número de clientes necessários para executar tarefas comuns e o número de parâmetros redundantes que cada um desses clientes usa, introduzimos uma hierarquia de objetos no SDK que imita a hierarquia de objetos no Azure. Cada cliente de recurso no SDK tem métodos para acessar os clientes de recursos de seus filhos que já estão no escopo da assinatura e do grupo de recursos adequados.

Para atingir essa meta, estamos introduzindo três tipos padrão para todos os recursos no Azure:

[Recurso] Resource.cs

Essa classe representa um objeto de cliente de recurso completo que contém uma propriedade Data expondo os detalhes como um tipo de dados [Resource] . Ele também tem acesso a todas as operações desse recurso sem precisar passar parâmetros de escopo, como ID de assinatura ou nome do recurso. Essa classe de recurso torna conveniente executar operações diretamente no resultado de chamadas de lista, pois tudo é retornado como um cliente de recurso completo agora.

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);
}

[Recurso]Data.cs

Essa classe representa o modelo que compõe um determinado recurso. Normalmente, essa classe são os dados de resposta de uma chamada de serviço, como HTTP GET, e fornece detalhes sobre o recurso subjacente. Anteriormente, essa classe era representada por uma classe Model .

[Recurso]Collection.cs

Essa classe representa as operações que você pode executar em uma coleção de recursos que pertencem a um recurso pai específico. Essa classe fornece a maioria das operações de coleção lógica.

Comportamento de coleção Método de coleta
Iterar/Listar GetAll()
Índice Get(nome da cadeia de caracteres)
Adicionar CreateOrUpdate(nome da cadeia de caracteres, [Recurso]Dados)
Contém Exists(nome da cadeia de caracteres)

Para a maioria das coisas, o pai será um ResourceGroup. Por exemplo, uma Sub-rede é filha de uma VirtualNetwork e um ResourceGroup é filho de uma Assinatura.

Juntando as peças

Imagine que nossa empresa exija que todas as máquinas virtuais sejam marcadas com o proprietário. Temos a tarefa de escrever um programa para adicionar a marca a qualquer máquina virtual ausente em um determinado grupo de recursos.

// 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");
   }
}

Identificador de recurso estruturado

Os IDs de recursos contêm informações úteis sobre o próprio recurso, mas são strings simples que precisam ser analisadas. Em vez de implementar sua própria lógica de análise, você pode usar um objeto ResourceIdentifier que fará a análise para você: new ResourceIdentifier("myid");.

Exemplo: analisando um ID usando um objeto ResourceIdentifier

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}");

Gerenciando recursos existentes por identificador de recurso

A execução de operações em recursos que já existem é um caso de uso comum ao usar as bibliotecas do cliente de gerenciamento. Nesse cenário, você geralmente tem o identificador do recurso no qual deseja trabalhar como uma cadeia de caracteres. Embora a nova hierarquia de objetos seja ótima para provisionamento e funcione dentro do escopo de um determinado pai, ela não é a mais eficiente quando se trata desse cenário específico.

Aqui está um exemplo de como você pode acessar um AvailabilitySet objeto e gerenciá-lo diretamente com sua 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);

Essa abordagem exigia muito código e três chamadas à API para o Azure. O mesmo pode ser feito com menos código e sem chamadas de API usando métodos de extensão que fornecemos no próprio cliente. Esses métodos de extensão permitem que você passe um identificador de recurso e recupere um cliente de recurso com escopo definido. O objeto retornado é um [Recurso] mencionado acima, pois ele não entrou em contato com o Azure para recuperar os dados, mas a propriedade Data será nula.

Portanto, o exemplo anterior ficaria assim:

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);

Também fornecemos uma opção de que, se você souber apenas as partes que compõem cada ResourceIdentifier recurso, fornecerá um método estático para construir a cadeia de caracteres completa a partir dessas partes. O exemplo acima teria esta aparência.

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);

Verifique se existe um [Recurso]

Se você não tiver certeza se existe um recurso que deseja obter ou se deseja apenas marcar se ele existir, use Exists() o método , que pode ser invocado de qualquer classe [Resource]Collection.

Exists() e ExistsAsync() retornam Response<bool> onde o bool será falso se o recurso especificado não existir. Esses dois métodos ainda dão acesso à resposta bruta subjacente.

Antes de esses métodos serem introduzidos, você precisaria capturar e RequestFailedException inspecionar o código de status para 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.");
}

Agora, com esses métodos de conveniência, podemos fazer o código a seguir.

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.");
}

Exemplos

Criar um grupo de recursos

// 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;

Listar todos os grupos de recursos

// 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);
}

Atualizar um grupo de recursos

// 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");

Excluir um grupo de recursos

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);

Obter lista 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");

Criar 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");

Atualizar 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");

Atualizar marcas 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");

Obter 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");

Verificar se GenericResource existe

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}");

Excluir 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}");

Para exemplos mais detalhados, dê uma olhada nos exemplos que temos disponíveis.

Testes de Resource Manager do Azure

Para executar o teste: dotnet test

Para executar o teste com cobertura de código e gerar automaticamente um relatório html: dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura

O relatório de cobertura será colocado em seu caminho em relação ao azure-proto-core-test no/coverage formato html para exibição

Os relatórios também podem ser exibidos vs ou VsCode com o plug-in do visualizador adequado

Um relatório terse também será exibido na linha de comando durante a execução.

executar o teste com um único arquivo ou teste

Para executar o teste com cobertura de código e gerar automaticamente um relatório html com apenas um único teste: dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura --filter <test-to-run>

Solução de problemas

Próximas etapas

Mais códigos de exemplo

Outra documentação

Se você estiver migrando do SDK antigo, marcar este guia de migração.

Para obter mais informações sobre o SDK do Microsoft Azure, consulte este site.

Participante

Para obter detalhes sobre como contribuir para esse repositório, consulte o guia de contribuição.

Este projeto aceita contribuições e sugestões. A maioria das contribuições exige que você concorde com um CLA (Contrato de Licença do Colaborador) declarando que você tem o direito de nos conceder, e de fato concede, os direitos de usar sua contribuição. Para obter detalhes, visite https://cla.microsoft.com.

Quando você envia uma solicitação de pull, um CLA-bot determinará automaticamente se você precisa fornecer um CLA e decorar a PR adequadamente (por exemplo, rótulo, comentário). Basta seguir as instruções fornecidas pelo bot. Você só precisará fazer essa ação uma vez em todos os repositórios usando nosso CLA.

Este projeto adotou o Código de Conduta de Software Livre da Microsoft. Para saber mais, confira as Perguntas frequentes sobre o Código de Conduta ou contate o opencode@microsoft.com caso tenha outras dúvidas ou comentários.