{"id":1627,"date":"2021-10-15T10:53:43","date_gmt":"2021-10-15T17:53:43","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/azure-sdk\/?p=1627"},"modified":"2023-02-14T06:33:37","modified_gmt":"2023-02-14T14:33:37","slug":"introducing-the-new-azure-sdk-resource-management-libraries-for-net","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/azure-sdk\/introducing-the-new-azure-sdk-resource-management-libraries-for-net\/","title":{"rendered":"Introducing the new Azure SDK Resource Management Libraries for .NET"},"content":{"rendered":"<p><strong>NOTE: Some code patterns covered in this blog post have evolved since the time of writing. For the latest guidance, see <a href=\"https:\/\/learn.microsoft.com\/dotnet\/azure\/sdk\/resource-management\" target=\"_blank\" rel=\"noopener\">Resource management using the Azure SDK for .NET<\/a>.<\/strong><\/p>\n<p>We&#8217;re excited to announce the preview release for .NET <a href=\"https:\/\/www.nuget.org\/packages\/Azure.ResourceManager\">Azure.ResourceManager<\/a>, which is the new base library for all management plane SDKs. Along with the base library, we&#8217;re also releasing preview versions for <a href=\"https:\/\/www.nuget.org\/packages\/Azure.ResourceManager.Compute\">Compute<\/a>, <a href=\"https:\/\/www.nuget.org\/packages\/Azure.ResourceManager.Network\">Network<\/a>, <a href=\"https:\/\/www.nuget.org\/packages\/Azure.ResourceManager.KeyVault\">Keyvault<\/a>, <a href=\"https:\/\/www.nuget.org\/packages\/Azure.ResourceManager.Resources\">Resources<\/a>, and <a href=\"https:\/\/www.nuget.org\/packages\/Azure.ResourceManager.Storage\">Storage<\/a> management plane. Each of these SDKs follows the new <a href=\"https:\/\/azure.github.io\/azure-sdk\/general_introduction.html\">Azure SDK guidelines<\/a>. This post will highlight a few new features of the libraries. We encourage you to try out the libraries and provide feedback before they go GA!<\/p>\n<h2>Key concepts<\/h2>\n<h3>Reducing Redundant Parameters<\/h3>\n<p>The previous .NET SDK generated all methods for a given resource on a flat client structure. The result was that all method calls required you to pass in the scope parameters similar to a set of static methods and classes. As an example nearly all methods on the old <code>VirtualMachinesOperations<\/code> object required you to pass in the resource group name and the virtual machine name.<\/p>\n<h4>Old<\/h4>\n<pre><code class=\"language-csharp\">string subscriptionGuid = \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\";\r\nstring resourceGroupName = \"myRg\";\r\nstring vmName = \"myVm\";\r\nComputeManagementClient computeClient = new ComputeManagementClient(subscriptionGuid, new DefaultAzureCredential());\r\nawait computeClient.VirtualMachines.StartPowerOff(resourceGroupName, vmName).WaitForCompletionAsync();\r\n\/\/do some stuff\r\nawait computeClient.VirtualMachines.StartPowerOn(resourceGroupName, vmName).WaitForCompletionAsync();<\/code><\/pre>\n<p>The new resource client classes solve this by taking in the context as a <code>ResourceIdentifier<\/code>, which eliminates the need to pass in scope parameters.<\/p>\n<h4>New<\/h4>\n<pre><code class=\"language-csharp\">ResourceIdentifier vmId = new ResourceIdentifier(\"\/subscriptions\/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\/resourceGroups\/myRg\/providers\/Microsoft.Compute\/virtualMachines\/myVm\");\r\nArmClient armClient = new ArmClient(new DefaultAzureCredential());\r\nVirtualMachine vm = armClient.GetVirtualMachine(vmId);\r\nawait vm.StartPowerOff().WaitForCompletionAsync();\r\n\/\/do some stuff\r\nawait vm.StartPowerOn().WaitForCompletionAsync();<\/code><\/pre>\n<p>Another issue was that each method you called on the <code>VirtualMachinesOperations<\/code> class is potentially operating at a different level such as <code>ListAll()<\/code> lists all VirtualMachines in the subscription used to construct the compute client. Whereas <code>List()<\/code> takes in a resource group name and might fail if you pass in a resource group that doesn&#8217;t belong to the subscription you used to construct the compute client. To illustrate what this looks like let&#8217;s take an example of shutting down all virtual machines at a subscription level and a resource group level.<\/p>\n<h4>Old<\/h4>\n<pre><code class=\"language-csharp\">string subscriptionGuid = \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\";\r\nstring resourceGroupName = \"myRg\";\r\nstring vmName = \"myVm\";\r\nComputeManagementClient computeClient = new ComputeManagementClient(subscriptionGuid, new DefaultAzureCredential());\r\n\r\n\/\/shutting down all VMs in the subscription\r\nforeach(VirtualMachine vm in computeClient.VirtualMachines.ListAll())\r\n{\r\n    \/\/we need to parse out the resource group from the id since each vm can be in a different resource group\r\n    \/\/we are doing that with a helper method GetResourceGroupFromId\r\n    string vmResourceGroupName = GetResourceGroupFromId(vm.Id);\r\n    await computeClient.VirtualMachines.StartPowerOff(vmResourceGroupName, vm.Name).WaitForCompletionAsync();\r\n}\r\n\r\n\/\/shutting down all VMs in a resource group\r\nforeach(VirtualMachine vm in computeClient.VirtualMachines.List(resourceGroupName))\r\n{\r\n    \/\/in this case we can use the constant resource group name since we used List vs ListAll\r\n    await computeClient.VirtualMachines.StartPowerOff(resourceGroupName, vm.Name).WaitForCompletionAsync();\r\n}<\/code><\/pre>\n<p>In the new design, the methods that operate at different levels are now moved to those contexts such that <code>ListAll()<\/code> becomes a method on a <code>Subscription<\/code> object called <code>GetAllVirtualMachines()<\/code>. <code>List()<\/code> becomes a method on a <code>ResourceGroup<\/code> object called <code>GetVirtualMachines().GetAll()<\/code> and no longer has the issue of accidentally passing in mismatched resource group and subscription.<\/p>\n<h4>New<\/h4>\n<pre><code class=\"language-csharp\">ResourceIdentifier vmId = new ResourceIdentifier(\"\/subscriptions\/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\/resourceGroups\/myRg\/providers\/Microsoft.Compute\/virtualMachines\/myVm\");\r\n\/\/The resource identifier class allows us to get the id of our parents all the way up the chain\r\nResourceIdentifier rgId = vmId.Parent;\r\nResourceIdentifier subscriptionId = rgId.Parent;\r\nArmClient armClient = new ArmClient(new DefaultAzureCredential());\r\n\r\n\/\/shutting down all VMs in the subscription\r\nSubscription subscription = armClient.GetSubscription(subscriptionId);\r\nforeach(VirtualMachine vm in subscription.GetAllVirtualMachines())\r\n{\r\n    \/\/The virtual machine is now a resource client so no scope parameters needed\r\n    await vm.StartPowerOff().WaitForCompletionAsync();\r\n}\r\n\r\n\/\/shutting down all VMs in a resource group\r\nResourceGroup rg = armClient.GetResourceGroup(rgId);\r\nforeach(VirtualMachine vm in rg.GetVirtualMachines().GetAll())\r\n{\r\n    \/\/The virtual machine is now a resource client so no scope parameters needed\r\n    await vm.StartPowerOff().WaitForCompletionAsync();    \r\n}<\/code><\/pre>\n<h3>Reducing Number of Clients<\/h3>\n<p>Let&#8217;s expand the example above and see what it looks like to shut down all VMs across all subscriptions. In the old design, we needed a new client <code>ResourcesManagementClient<\/code> since that is the client that will allow us to loop through all subscriptions.<\/p>\n<h4>Old<\/h4>\n<pre><code class=\"language-csharp\">string subscriptionGuid = \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\";\r\nstring resourceGroupName = \"myRg\";\r\nstring vmName = \"myVm\";\r\n\/\/the resources client requires a subscription id even though not all methods on the client will use it which can be confusing\r\nResourcesManagementClient resourcesClient = new ResourcesManagementClient(subscriptionGuid, new DefaultAzureCredential());\r\n\r\n\/\/this list call ignores the subscription id used to construct the client\r\nforeach(Subscription sub in resourcesClient.Subscriptions.List())\r\n{\r\n    \/\/need to construct a new client for each subscription returned\r\n    ComputeManagementClient computeClient = new ComputeManagementClient(sub.SubscriptionId, new DefaultAzureCredential());\r\n    foreach(VirtualMachine vm in computeClient.VirtualMachines.ListAll())\r\n    {\r\n        \/\/we need to parse out the resource group from the id since each vm can be in a different resource group\r\n        \/\/we are doing that with a helper method GetResourceGroupFromId\r\n        string vmResourceGroupName = GetResourceGroupFromId(vm.Id);\r\n        await computeClient.VirtualMachines.StartPowerOff(vmResourceGroupName, vm.Name).WaitForCompletionAsync();\r\n    }\r\n}<\/code><\/pre>\n<p>The new design allows the context to cascade down from all levels removing the need to construct multiple clients and pass context between them.<\/p>\n<h4>New<\/h4>\n<pre><code class=\"language-csharp\">ArmClient armClient = new ArmClient(new DefaultAzureCredential());\r\n\r\nforeach(Subscription sub in armClient.GetSubscriptions().GetAll())\r\n{\r\n    \/\/The subscription is now a resource client so we can immediately access the methods and no scope parameters needed\r\n    foreach(VirtualMachine vm in sub.GetAllVirtualMachines())\r\n    {\r\n        \/\/The virtual machine is now a resource client so no scope parameters needed\r\n        await vm.StartPowerOff().WaitForCompletionAsync();\r\n    }\r\n}<\/code><\/pre>\n<p>To accomplish these changes, we&#8217;re introducing three standard types for all resources in Azure:<\/p>\n<h3><strong>[Resource].cs<\/strong><\/h3>\n<p>This class represents a full resource client that contains a <strong>Data<\/strong> property exposing the details as a <strong>[Resource]Data<\/strong> type.\nIt also has access to all the operations on that resource without needing to pass in scope parameters. Having access to these methods makes it convenient to directly execute operations on the result of list calls since everything is returned as a full resource.<\/p>\n<h3><strong>[Resource]Data.cs<\/strong><\/h3>\n<p>This class represents the model that makes up a given resource. Typically, this data is what gets returned from a service call such as HTTP GET and provides details about the underlying resource. Previously, this data was represented by a <strong>Model<\/strong> class.<\/p>\n<h3><strong>[Resource]Collection.cs<\/strong><\/h3>\n<p>This class represents the operations you can do on a collection of resources belonging to a specific parent resource.\nThis object provides most of the logical collection operations.<\/p>\n<table>\n<thead>\n<tr>\n<th>Collection Behavior<\/th>\n<th>Collection Method<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Iterate\/List<\/td>\n<td>GetAll()<\/td>\n<\/tr>\n<tr>\n<td>Index<\/td>\n<td>Get(string name)<\/td>\n<\/tr>\n<tr>\n<td>Add<\/td>\n<td>CreateOrUpdate(string name, [Resource]Data data)<\/td>\n<\/tr>\n<tr>\n<td>Contains<\/td>\n<td>CheckIfExists(string name)<\/td>\n<\/tr>\n<tr>\n<td>TryGet<\/td>\n<td>GetIfExists(string name)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>For most things, the parent will be a <code>ResourceGroup<\/code>. However, each parent \/ child relationship is represented this way. For example, a <code>Subnet<\/code> is a child of a <code>VirtualNetwork<\/code> and a <code>ResourceGroup<\/code> is a child of a <code>Subscription<\/code>.<\/p>\n<h2>Putting it all together<\/h2>\n<p>Imagine that our company requires all virtual machines to be tagged with the owner. We&#8217;re tasked with writing a program to add the tag to any missing virtual machines in a given resource group.<\/p>\n<pre><code class=\"language-csharp\">\/\/ First we construct our armClient\r\nvar armClient = new ArmClient(new DefaultAzureCredential());\r\n\r\n\/\/ Next we get a resource group object\r\n\/\/ ResourceGroup is a [Resource] object from above\r\nSubscription subscription = await armClient.GetDefaultSubscriptionAsync();\r\nResourceGroup resourceGroup = await subscription.GetResourceGroups().GetAsync(\"myRgName\");\r\n\r\n\/\/ Next we get the collection for the virtual machines\r\n\/\/ vmCollection is a [Resource]Collection object from above\r\nVirtualMachineCollection vmCollection = resourceGroup.GetVirtualMachines();\r\n\r\n\/\/ Next we loop over all VMs in the collection\r\n\/\/ Each vm is a [Resource] object from above\r\nawait foreach(VirtualMachine vm in vmCollection.GetAllAsync())\r\n{\r\n    \/\/ We access the [Resource]Data properties from vm.Data\r\n    if(!vm.Data.Tags.ContainsKey(\"owner\"))\r\n    {\r\n        \/\/ We can also access all operations from vm since it is already scoped for us\r\n        await vm.AddTagAsync(\"owner\", GetOwner());\r\n    }\r\n}<\/code><\/pre>\n<h2>Structured Resource Identifier<\/h2>\n<p>Resource IDs contain useful information about the resource itself, but they&#8217;re plain strings that must be parsed. Instead of implementing your own parsing logic, you can use a <code>ResourceIdentifier<\/code> object that will do the parsing for you: <code>new ResourceIdentifer(\"myid\");<\/code>.<\/p>\n<h3>Example: Parsing an ID using a ResourceIdentifier object<\/h3>\n<pre><code class=\"language-C#\">string resourceId = \"\/subscriptions\/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\/resourceGroups\/workshop2021-rg\/providers\/Microsoft.Network\/virtualNetworks\/myVnet\/subnets\/mySubnet\";\r\nResourceIdentifier id = new ResourceIdentifier(resourceId);\r\nConsole.WriteLine($\"Subscription: {id.SubscriptionId}\");\r\nConsole.WriteLine($\"ResourceGroup: {id.ResourceGroupName}\");\r\nConsole.WriteLine($\"Vnet: {id.Parent.Name}\");\r\nConsole.WriteLine($\"Subnet: {id.Name}\");<\/code><\/pre>\n<p>However, keep in mind that some of those properties could be null. You can usually tell by the ID string itself what type of resource an ID represents, but if you&#8217;re unsure you can check if the properties are null or use the <code>Try<\/code> methods to retrieve the values as is shown below:<\/p>\n<h3>Example: ResourceIdentifier TryGet methods<\/h3>\n<pre><code class=\"language-C#\">string resourceId = \"\/subscriptions\/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\/resourceGroups\/workshop2021-rg\/providers\/Microsoft.Network\/virtualNetworks\/myVnet\/subnets\/mySubnet\";\r\n\/\/ Assume we don't know what type of resource id we have we can cast to the base type\r\nResourceIdentifier id = new ResourceIdentifier(resourceId);\r\nstring property;\r\nif (id.TryGetSubscriptionId(out property))\r\n    Console.WriteLine($\"Subscription: {property}\");\r\nif (id.TryGetResourceGroupName(out property))\r\n    Console.WriteLine($\"ResourceGroup: {property}\");\r\n\/\/ Parent is only null when we reach the top of the chain which is a Tenant\r\nConsole.WriteLine($\"Vnet: {id.Parent.Name}\");\r\n\/\/ Name will never be null\r\nConsole.WriteLine($\"Subnet: {id.Name}\");<\/code><\/pre>\n<h2>Managing Existing Resources By ID<\/h2>\n<p>Executing operations on resources that already exist is a common use case when using the management client libraries. In this scenario, you usually have the identifier of the resource you want to work on as a string. Although the new object hierarchy is great for provisioning and working within the scope of a given parent, it can lead to extra network calls if used incorrectly.<\/p>\n<p>An example of how you would access an <code>AvailabilitySet<\/code> object and manage it directly with its ID is:<\/p>\n<pre><code class=\"language-csharp\">using Azure.Identity;\r\nusing Azure.ResourceManager;\r\nusing Azure.ResourceManager.Resources;\r\nusing Azure.ResourceManager.Compute;\r\nusing System;\r\nusing System.Threading.Tasks;\r\n\r\n\/\/ Code omitted for brevity\r\n\r\nstring resourceId = \"\/subscriptions\/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\/resourceGroups\/workshop2021-rg\/providers\/Microsoft.Compute\/availabilitySets\/ws2021availSet\";\r\nResourceIdentifier id = new ResourceIdentifier(resourceId);\r\n\/\/ We construct a new armClient to work with\r\nArmClient armClient = new ArmClient(new DefaultAzureCredential());\r\n\r\n\/\/ Next we get the specific subscription this resource belongs to\r\nSubscription subscription = await armClient.GetSubscriptions().GetAsync(id.SubscriptionId);\r\n\r\n\/\/ Next we get the specific resource group this resource belongs to\r\nResourceGroup resourceGroup = await subscription.GetResourceGroups().GetAsync(id.ResourceGroupName);\r\n\r\n\/\/ Finally we get the resource itself\r\n\/\/ Note: for this last step in this example, Azure.ResourceManager.Compute is needed\r\nAvailabilitySet availabilitySet = await resourceGroup.GetAvailabilitySets().GetAsync(id.Name);\r\n\r\n\/\/ we have the data representing the availabilitySet\r\nConsole.WriteLine(availabilitySet.Data.Name);<\/code><\/pre>\n<p>This approach required many lines of code and three API calls to Azure. The same can be done with less code and without any API calls by using extension methods that we&#8217;ve provided on the client itself. These extension methods allow you to pass in a resource identifier and retrieve a scoped resource client. The object returned is a <em>[Resource]<\/em> mentioned above, and since it hasn&#8217;t reached out to Azure to retrieve the data yet the Data property will be null.<\/p>\n<p>So, the previous example would end up looking like this:<\/p>\n<pre><code class=\"language-csharp\">string resourceId = \"\/subscriptions\/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\/resourceGroups\/workshop2021-rg\/providers\/Microsoft.Compute\/availabilitySets\/ws2021availSet\";\r\nResourceIdentifier id = new ResourceIdentifier(resourceId);\r\n\/\/ We construct a new armClient to work with\r\nArmClient armClient = new ArmClient(new DefaultAzureCredential());\r\n\r\n\/\/ Next we get the AvailabilitySet resource client from the armClient\r\n\/\/ The method takes in a ResourceIdentifier or we can use the implicit cast from string\r\nAvailabilitySet availabilitySet = armClient.GetAvailabilitySet(id);\r\n\r\n\/\/ At this point availabilitySet.Data will be null and trying to access it will throw\r\n\/\/ If we want to retrieve the objects data we can call get\r\navailabilitySet = await availabilitySet.GetAsync();\r\n\r\n\/\/ we now have the data representing the availabilitySet\r\nConsole.WriteLine(availabilitySet.Data.Name);<\/code><\/pre>\n<h2>Check if a [Resource] exists<\/h2>\n<p>If you aren&#8217;t sure if a resource you want to get exists, or you just want to check if it exists, you can use <code>GetIfExists()<\/code> or <code>CheckIfExists()<\/code> methods, which can be invoked from any [Resource]Collection class.<\/p>\n<p><code>GetIfExists()<\/code> and <code>GetIfExistsAsync()<\/code> return a <code>Response&lt;T&gt;<\/code> where T is null if the specified resource doesn&#8217;t exist. <code>CheckIfExists()<\/code> and <code>CheckIfExistsAsync()<\/code> return <code>Response&lt;bool&gt;<\/code> where the bool will be false if the specified resource doesn&#8217;t exist. Both of these methods still give you access to the underlying raw response.<\/p>\n<p>Before these methods were introduced, you would need to catch the <code>RequestFailedException<\/code> and inspect the status code for 404.<\/p>\n<pre><code class=\"language-C#\">ArmClient armClient = new ArmClient(new DefaultAzureCredential());\r\nSubscription subscription = await armClient.GetDefaultSubscriptionAsync();\r\nstring rgName = \"myRgName\";\r\n\r\ntry\r\n{\r\n    ResourceGroup myRG = await subscription.GetResourceGroups().GetAsync(rgName);\r\n    \/\/ 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.\r\n}\r\ncatch (RequestFailedException ex) when (ex.Status == 404)\r\n{\r\n    Console.WriteLine($\"Resource Group {rgName} does not exist.\");\r\n}<\/code><\/pre>\n<p>Now with these convenience methods we can do the following instead.<\/p>\n<pre><code class=\"language-C#\">ArmClient armClient = new ArmClient(new DefaultAzureCredential());\r\nSubscription subscription = await armClient.GetDefaultSubscriptionAsync();\r\nstring rgName = \"myRgName\";\r\n\r\nbool exists = await subscription.GetResourceGroups().CheckIfExistsAsync(rgName);\r\n\r\nif (exists)\r\n{\r\n    Console.WriteLine($\"Resource Group {rgName} exists.\");\r\n\r\n    \/\/ We can get the resource group now that we know it exists.\r\n    \/\/ This does introduce a small race condition where resource group could have been deleted between the check and the get.\r\n    ResourceGroup myRG = await subscription.GetResourceGroups().GetAsync(rgName);\r\n}\r\nelse\r\n{\r\n    Console.WriteLine($\"Resource Group {rgName} does not exist.\");\r\n}<\/code><\/pre>\n<p>Another way to accomplish the same thing is by using <code>GetIfExists()<\/code>, which will avoid the race condition mentioned above:<\/p>\n<pre><code class=\"language-C#\">ArmClient armClient = new ArmClient(new DefaultAzureCredential());\r\nSubscription subscription = await armClient.GetDefaultSubscriptionAsync();\r\nstring rgName = \"myRgName\";\r\n\r\nResourceGroup myRG = await subscription.GetResourceGroups().GetIfExistsAsync(rgName);\r\n\r\nif (myRG == null)\r\n{\r\n    Console.WriteLine($\"Resource Group {rgName} does not exist.\");\r\n}\r\nelse\r\n{\r\n    \/\/ 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.\r\n}<\/code><\/pre>\n<h2>Conclusion<\/h2>\n<p>We hope these changes make management of your Azure resources much easier with the .NET SDK.<\/p>\n<p>Resources<\/p>\n<ul>\n<li><a href=\"https:\/\/aka.ms\/azsdk\/dotnet\/mgmt\">Quickstart<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-net\/blob\/main\/sdk\/resourcemanager\/Azure.ResourceManager\/docs\/MigrationGuide.md\">Migration Guide<\/a><\/li>\n<\/ul>\n<table>\n<thead>\n<tr>\n<th>You can check out more detailed examples for each of the SDKs below.<\/th>\n<th>Resource Provider<\/th>\n<th>Samples<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Resource Manager<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-net\/tree\/main\/sdk\/resourcemanager\/Azure.ResourceManager\/samples\">Samples<\/a><\/td>\n<\/tr>\n<tr>\n<td>Compute<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-net\/tree\/main\/sdk\/compute\/Azure.ResourceManager.Compute\/samples\">Samples<\/a><\/td>\n<\/tr>\n<tr>\n<td>Network<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-net\/tree\/main\/sdk\/network\/Azure.ResourceManager.Network\/samples\">Samples<\/a><\/td>\n<\/tr>\n<tr>\n<td>Resources<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-net\/tree\/main\/sdk\/resources\/Azure.ResourceManager.Resources\/samples\">Samples<\/a><\/td>\n<\/tr>\n<tr>\n<td>Storage<\/td>\n<td><a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-net\/tree\/main\/sdk\/storage\/Azure.ResourceManager.Storage\/samples\">Samples<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><!-- FOOTER: DO NOT EDIT OR REMOVE --><\/p>\n<p><div  class=\"d-flex justify-content-center\"><a class=\"cta_button_link btn-primary mb-24\" href=\"https:\/\/aka.ms\/azsdk\/releases\" target=\"_blank\">Azure SDK Releases<\/a><\/div><\/p>\n<h2>Azure SDK Blog Contributions<\/h2>\n<p>Thanks for reading this Azure SDK blog post. We hope you learned something new, and we welcome you to share the post. We\u2019re open to Azure SDK blog contributions from our readers. To get started, contact us at <a href=\"mailto:azsdkblog@microsoft.com\">azsdkblog@microsoft.com<\/a> with your idea, and we&#8217;ll set you up as a guest blogger.<\/p>\n<ul>\n<li>Azure SDK Website: <a href=\"https:\/\/aka.ms\/azsdk\">aka.ms\/azsdk<\/a><\/li>\n<li>Azure SDK Intro (3-minute video): <a href=\"https:\/\/aka.ms\/azsdk\/intro\">aka.ms\/azsdk\/intro<\/a><\/li>\n<li>Azure SDK Intro Deck (PowerPoint deck): <a href=\"https:\/\/aka.ms\/azsdk\/intro\/deck\">aka.ms\/azsdk\/intro\/deck<\/a><\/li>\n<li>Azure SDK Releases: <a href=\"https:\/\/aka.ms\/azsdk\/releases\">aka.ms\/azsdk\/releases<\/a><\/li>\n<li>Azure SDK Blog: <a href=\"https:\/\/aka.ms\/azsdk\/blog\">aka.ms\/azsdk\/blog<\/a><\/li>\n<li>Azure SDK Twitter: <a href=\"https:\/\/twitter.com\/AzureSDK\">twitter.com\/AzureSDK<\/a><\/li>\n<li>Azure SDK Design Guidelines: <a href=\"https:\/\/aka.ms\/azsdk\/guide\">aka.ms\/azsdk\/guide<\/a><\/li>\n<li>Azure SDKs &amp; Tools: <a href=\"https:\/\/azure.microsoft.com\/downloads\">azure.microsoft.com\/downloads<\/a><\/li>\n<li>Azure SDK Central Repository: <a href=\"https:\/\/github.com\/azure\/azure-sdk#azure-sdk\">github.com\/azure\/azure-sdk<\/a><\/li>\n<li>Azure SDK for .NET: <a href=\"https:\/\/github.com\/azure\/azure-sdk-for-net\">github.com\/azure\/azure-sdk-for-net<\/a><\/li>\n<li>Azure SDK for Java: <a href=\"https:\/\/github.com\/azure\/azure-sdk-for-java\">github.com\/azure\/azure-sdk-for-java<\/a><\/li>\n<li>Azure SDK for Python: <a href=\"https:\/\/github.com\/azure\/azure-sdk-for-python\">github.com\/azure\/azure-sdk-for-python<\/a><\/li>\n<li>Azure SDK for JavaScript\/TypeScript: <a href=\"https:\/\/github.com\/azure\/azure-sdk-for-js\">github.com\/azure\/azure-sdk-for-js<\/a><\/li>\n<li>Azure SDK for Android: <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-android\">github.com\/Azure\/azure-sdk-for-android<\/a><\/li>\n<li>Azure SDK for iOS: <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-ios\">github.com\/Azure\/azure-sdk-for-ios<\/a><\/li>\n<li>Azure SDK for Go: <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-go\">github.com\/Azure\/azure-sdk-for-go<\/a><\/li>\n<li>Azure SDK for C: <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-c\">github.com\/Azure\/azure-sdk-for-c<\/a><\/li>\n<li>Azure SDK for C++: <a href=\"https:\/\/github.com\/Azure\/azure-sdk-for-cpp\">github.com\/Azure\/azure-sdk-for-cpp<\/a><\/li>\n<\/ul>\n<p><!-- FOOTER: DO NOT EDIT OR REMOVE --><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post, we&#8217;ll share the latest announcements pertaining to Azure Resource Management libraries for .NET.<\/p>\n","protected":false},"author":69885,"featured_media":1193,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[701,834,836,826,835,732,837,705,738],"class_list":["post-1627","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-sdk","tag-net","tag-compute","tag-keyvault","tag-mgmt","tag-network","tag-release","tag-resources","tag-sdk","tag-storage"],"acf":[],"blog_post_summary":"<p>In this post, we&#8217;ll share the latest announcements pertaining to Azure Resource Management libraries for .NET.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/1627","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/users\/69885"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/comments?post=1627"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/1627\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/media\/1193"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/media?parent=1627"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/categories?post=1627"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/tags?post=1627"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}