Share via


Microsoft Azure Resource Manager-Clientbibliothek für .NET

Microsoft Azure Resource Manager ist der Bereitstellungs- und Verwaltungsdienst für Azure. Er bietet eine Verwaltungsebene, die das Erstellen, Aktualisieren und Löschen von Ressourcen in Ihrem Azure-Konto ermöglicht.

Diese Bibliothek bietet Ressourcengruppen- und Ressourcenverwaltungsfunktionen für Microsoft Azure.

Diese Bibliothek folgt den neuen Azure SDK-Richtlinien und bietet viele Kernfunktionen:

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

Erste Schritte

Installieren des Pakets

Installieren Sie die Microsoft Azure Resources Management Core-Bibliothek für .NET mit NuGet:

dotnet add package Azure.ResourceManager

Voraussetzungen

Authentifizieren des Clients

Die Standardoption zum Erstellen eines authentifizierten Clients ist die Verwendung von DefaultAzureCredential. Da alle Verwaltungs-APIs denselben Endpunkt verwenden, muss nur eine oberste Ebene ArmClient erstellt werden, um mit Ressourcen zu interagieren.

Führen Sie den folgenden Code aus, um sich bei Azure zu authentifizieren und eine ArmClientzu erstellen:

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

Weitere Dokumentation für die Azure.Identity.DefaultAzureCredential Klasse finden Sie in diesem Dokument.

Wichtige Begriffe

Grundlegendes zur Azure-Ressourcenhierarchie

Um sowohl die Anzahl der Clients zu reduzieren, die zum Ausführen allgemeiner Aufgaben erforderlich sind, als auch die Anzahl redundanter Parameter, die jeder dieser Clients verwendet, haben wir eine Objekthierarchie im SDK eingeführt, die die Objekthierarchie in Azure imitiert. Jeder Ressourcenclient im SDK verfügt über Methoden für den Zugriff auf die Ressourcenclients der ihm untergeordneten Elemente, die bereits dem richtigen Abonnement und der richtigen Ressourcengruppe angepasst sind.

Um dieses Ziel zu erreichen, führen wir drei Standardtypen für alle Ressourcen in Azure ein:

[Ressource] Resource.cs

Diese Klasse stellt ein vollständiges Ressourcenclientobjekt dar, das eine Data-Eigenschaft enthält, die die Details als [Resource]Datentyp verfügbar macht. Es hat auch Zugriff auf alle Vorgänge für diese Ressource, ohne Bereichsparameter wie die Abonnement-ID oder den Ressourcennamen übergeben zu müssen. Diese Ressourcenklasse macht es bequem, Vorgänge direkt nach dem Ergebnis von Listenaufrufen auszuführen, da jetzt alles als vollständiger Ressourcenclient zurückgegeben wird.

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

Diese Klasse stellt das Modell dar, aus dem eine bestimmte Ressource besteht. In der Regel handelt es sich bei dieser Klasse um die Antwortdaten eines Dienstaufrufs, z. B. HTTP GET, und sie stellt Details zur zugrunde liegenden Ressource bereit. Bisher wurde diese Klasse durch eine Model-Klasse dargestellt.

[Resource]Collection.cs

Diese Klasse stellt die Vorgänge dar, die Sie für eine Sammlung von Ressourcen ausführen können, die zu einer bestimmten übergeordneten Ressource gehören. Diese Klasse stellt die meisten logischen Auflistungsvorgänge bereit.

Sammlungsverhalten Sammlungsmethode
Iterieren/Auflisten GetAll()
Index Get(Zeichenfolgenname)
Hinzufügen CreateOrUpdate(Zeichenfolgenname, [Resource]Data data)
Enthält Exists(Zeichenfolgenname)

In den meisten Fällen ist das übergeordnete Element eine ResourceGroup. Beispielsweise ist ein Subnet ein untergeordnetes Element eines VirtualNetwork und eine ResourceGroup ein untergeordnetes Element einer Subscription.

Zusammenfügen des Gesamtbilds

Stellen Sie sich vor, dass unser Unternehmen verlangt, dass alle virtuellen Computer mit dem Besitzer gekennzeichnet werden. Wir haben die Aufgabe, ein Programm zu schreiben, um das Tag allen fehlenden virtuellen Computern in einer bestimmten Ressourcengruppe hinzuzufügen.

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

Strukturierter Ressourcenbezeichner

Ressourcen-IDs enthalten nützliche Informationen zur Ressource selbst, sind jedoch einfache Zeichenfolgen, die analysiert werden müssen. Anstatt Eine eigene Analyselogik zu implementieren, können Sie ein ResourceIdentifier -Objekt verwenden, das die Analyse für Sie übernimmt: new ResourceIdentifier("myid");.

Beispiel: Analyse einer ID mithilfe eines ResourceIdentifier-Objekts

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

Verwalten vorhandener Ressourcen nach Ressourcenbezeichner

Das Ausführen von Vorgängen für bereits vorhandene Ressourcen ist ein häufiger Anwendungsfall bei Verwendung der Verwaltungsclientbibliotheken. In diesem Szenario verfügen Sie in der Regel in Form einer Zeichenfolge über den Bezeichner der Ressource, an der Sie arbeiten möchten. Obwohl sich die neue Objekthierarchie hervorragend für die Bereitstellung eignet und innerhalb des Bereichs eines bestimmten übergeordneten Elements funktioniert, ist sie in diesem spezifischen Szenario nicht die effizienteste.

Hier sehen Sie ein Beispiel, wie Sie auf ein AvailabilitySet Objekt zugreifen und es direkt mit seiner ID verwalten können:

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

Dieser Ansatz erforderte viel Code und drei API-Aufrufe an Azure. Dasselbe kann mit weniger Code und ohne API-Aufrufe mithilfe von Erweiterungsmethoden erfolgen, die wir auf dem Client selbst bereitgestellt haben. Mit diesen Erweiterungsmethoden können Sie einen Ressourcenbezeichner übergeben und einen bereichsbezogenen Ressourcenclient abrufen. Bei dem zurückgegebenen Objekt handelt es sich um eine [Ressource], die oben erwähnt wurde, da es sich nicht an Azure geholt hat, um die Daten abzurufen, aber die Data-Eigenschaft ist NULL.

Das vorherige Beispiel würde daher folgendermaßen aussehen:

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

Wir bieten auch eine Option, die, wenn Sie nur die Teile kennen, aus denen die ResourceIdentifier einzelnen Ressourcen bestehen, eine statische Methode zum Erstellen der vollständigen Zeichenfolge aus diesen Teilen bereitstellt. Das obige Beispiel würde dann wie folgt aussehen.

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

Überprüfen, ob eine [Resource] vorhanden ist

Wenn Sie nicht sicher sind, ob eine Ressource vorhanden ist, die Sie abrufen möchten, oder wenn Sie nur überprüfen möchten, ob sie vorhanden ist, können Sie die -Methode verwenden Exists() , die von jeder [Resource]Collection-Klasse aufgerufen werden kann.

Exists() und ExistsAsync() gibt zurück Response<bool> , wenn die angegebene Ressource nicht vorhanden ist. Beide Methoden bieten Ihnen weiterhin Zugriff auf die zugrunde liegende Rohantwort.

Bevor diese Methoden eingeführt wurden, müssen Sie die abfangen und den RequestFailedException status Code für 404 untersuchen.

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

Mit diesen praktischen Methoden können wir nun den folgenden Code ausführen.

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

Beispiele

Erstellen einer Ressourcengruppe

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

Auflisten aller Ressourcengruppen

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

Aktualisieren einer Ressourcengruppe

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

Löschen einer Ressourcengruppe

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

Abrufen der GenericResource-Liste

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

Erstellen von 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");

Aktualisieren von 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");

Aktualisieren von GenericResourc-Tags

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

Abrufen von 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");

Überprüfen, ob GenericResource vorhanden ist

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

Löschen von 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}");

Ausführlichere Beispiele finden Sie in den verfügbaren Beispielen.

Azure Resource Manager-Tests

So führen Sie den Test aus: dotnet test

So führen Sie einen Test mit Code Coverage aus und generieren automatisch einen HTML-Bericht: dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura

Der Abdeckungsbericht wird in Ihrem Pfad relativ zu azure-proto-core-test im/coverage HTML-Format zur Anzeige platziert.

Berichte können auch vs oder VsCode mit dem richtigen Viewer-Plug-In angezeigt werden.

Ein terse-Bericht wird auch in der Befehlszeile angezeigt, wenn sie ausgeführt wird.

Ausführen eines Tests mit einer einzelnen Datei oder einem Test

So führen Sie einen Test mit Code Coverage aus und generieren automatisch einen HTML-Bericht mit nur einem einzigen Test: dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura --filter <test-to-run>

Problembehandlung

  • Melden Sie ein Problem über GitHub Issues.
  • Überprüfen Sie vorherige Fragen , oder stellen Sie neue Fragen in Stack Overflow mithilfe von Azure- und .NET-Tags.

Nächste Schritte

Weiterer Beispielcode

Weitere Dokumentation

Wenn Sie aus dem alten SDK migrieren, lesen Sie diesen Migrationsleitfaden.

Weitere Informationen zum Microsoft Azure SDK finden Sie auf dieser Website.

Mitwirken

Ausführliche Informationen zum Mitwirken zu diesem Repository finden Sie im Leitfaden zur Mitarbeit.

Beiträge und Vorschläge für dieses Projekt sind willkommen. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. Ausführliche Informationen finden Sie unter https://cla.microsoft.com.

Wenn Sie einen Pull Request übermitteln, bestimmt ein CLA-Bot automatisch, ob Sie eine CLA bereitstellen und den PR entsprechend dekorieren müssen (z. B. Bezeichnung, Kommentar). Folgen Sie den Anweisungen des Bots. Sie müssen diese Aktion nur einmal für alle Repositorys mit unserer CLA ausführen.

Für dieses Projekt gelten die Microsoft-Verhaltensregeln für Open Source (Microsoft Open Source Code of Conduct). Weitere Informationen finden Sie in den häufig gestellten Fragen zum Verhaltenskodex. Sie können sich auch an opencode@microsoft.com wenden, wenn Sie weitere Fragen oder Anmerkungen haben.