From dd487188e0de9d570f6c0ad7c5ee79504da8d4d9 Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Tue, 23 Feb 2021 14:46:24 -0800 Subject: [PATCH 01/18] initial checkin for core / tests --- eng/Packages.Data.props | 9 +- .../Azure.ResourceManager.Core.sln | 43 ++ .../Directory.Build.props | 6 + .../src/Adapters/PhArmResponse.cs | 41 ++ .../Adapters/PhTaskDeferringAsyncPageable.cs | 39 ++ .../src/Adapters/PhWrappingAsyncPageable.cs | 60 +++ .../src/Adapters/PhWrappingPageable.cs | 57 +++ .../src/Adapters/ProxyResource.cs | 62 +++ .../src/Adapters/TrackedResource.cs | 63 +++ .../src/Adapters/WrappingPage.cs | 46 ++ .../src/ApiVersionsBase.cs | 204 ++++++++ .../src/ArmBuilder.cs | 162 ++++++ .../src/ArmOperation.cs | 32 ++ .../src/ArmResponse.cs | 40 ++ .../src/ArmVoidOperation.cs | 111 +++++ .../src/Azure.ResourceManager.Core.csproj | 21 + .../src/AzureResourceManagerClient.cs | 185 +++++++ .../src/AzureResourceManagerClientOptions.cs | 156 ++++++ .../src/ContainerBase.cs | 92 ++++ .../src/ExtensionResourceContainer.cs | 100 ++++ .../src/ExtensionResourceOperationsBase.cs | 93 ++++ .../src/GenericResource.cs | 41 ++ .../src/GenericResourceData.cs | 101 ++++ .../src/GenericResourceOperations.cs | 231 +++++++++ .../src/IDeletableResource.cs | 51 ++ .../src/ITaggableResource.cs | 114 +++++ .../src/OperationsBase.cs | 73 +++ .../src/Placeholder/PhArmOperation.cs | 102 ++++ .../src/Placeholder/ResourceGroupData.cs | 67 +++ .../src/Placeholder/SubscriptionData.cs | 74 +++ .../src/Properties/AssemblyInfo.cs | 6 + .../src/ResourceContainerBase.cs | 122 +++++ .../src/ResourceGroup.cs | 41 ++ .../src/ResourceGroupContainer.cs | 108 ++++ .../src/ResourceGroupOperations.cs | 406 ++++++++++++++++ .../src/ResourceListOperations.cs | 202 ++++++++ .../src/ResourceOperationsBase.cs | 164 +++++++ .../src/Resources/GenericResourceFilter.cs | 26 + .../src/Resources/KnownKeys.cs | 43 ++ .../src/Resources/LocationContainer.cs | 61 +++ .../src/Resources/LocationData.cs | 460 ++++++++++++++++++ .../src/Resources/Plan.cs | 110 +++++ .../src/Resources/Resource.cs | 71 +++ .../src/Resources/ResourceFilterCollection.cs | 70 +++ .../src/Resources/ResourceIdentifier.cs | 291 +++++++++++ .../src/Resources/ResourceIdentity.cs | 236 +++++++++ .../src/Resources/ResourceNameFilter.cs | 71 +++ .../src/Resources/ResourceTagFilter.cs | 65 +++ .../src/Resources/ResourceType.cs | 325 +++++++++++++ .../src/Resources/ResourceTypeFilter.cs | 45 ++ .../src/Resources/Sku.cs | 110 +++++ .../src/Resources/SystemAssignedIdentity.cs | 162 ++++++ .../src/Resources/TrackedResource.cs | 30 ++ .../src/Resources/UserAssignedIdentity.cs | 146 ++++++ .../src/Subscription.cs | 41 ++ .../src/SubscriptionContainer.cs | 93 ++++ .../src/SubscriptionOperations.cs | 106 ++++ .../src/autorest.md | 12 + .../tests/ApiVersionsBaseTests.cs | 216 ++++++++ .../tests/ArmClientOptionsTests.cs | 25 + .../tests/ArmClientTests.cs | 15 + .../Azure.ResourceManager.Core.Tests.csproj | 32 ++ .../tests/IdentityTests.cs | 457 +++++++++++++++++ .../tests/LocationTests.cs | 225 +++++++++ .../tests/PlanTests.cs | 232 +++++++++ .../tests/README.MD | 16 + .../tests/Resource/TestResource.cs | 16 + .../tests/ResourceGroupOperationsTests.cs | 23 + .../tests/ResourceIdentifierTests.cs | 123 +++++ .../tests/ResourceListOperationsTest.cs | 134 +++++ .../tests/ResourceNameFilterTests.cs | 62 +++ .../tests/ResourceTagFilterTests.cs | 67 +++ .../tests/ResourceTests.cs | 64 +++ .../tests/ResourceTypeTests.cs | 233 +++++++++ .../ArmClientOptionsExtensions.cs | 10 + .../FakeResourceApiVersions.cs | 21 + .../RpImplementations/FakeRpApiVersions.cs | 12 + .../tests/SkuTests.cs | 228 +++++++++ .../tests/SubscriptionOperationsTests.cs | 52 ++ .../tests/SystemAssignedIdentityTests.cs | 339 +++++++++++++ .../tests/TaggableResourceTests.cs | 103 ++++ .../TestAssets/Identity/InvalidType.json | 13 + .../Identity/InvalidTypeIsNull.json | 13 + .../SystemAndUserAssignedInnerExtraField.json | 14 + ...SystemAndUserAssignedMiddleExtraField.json | 14 + .../SystemAndUserAssignedOuterExtraField.json | 14 + .../Identity/SystemAndUserAssignedValid.json | 13 + ...temAndUserAssignedValidMultIdentities.json | 17 + .../TestAssets/Identity/SystemAssigned.json | 8 + .../TestAssets/Identity/UserAssigned.json | 13 + .../SystemAssignedBothEmptyString.json | 8 + .../SystemAssignedBothValuesNull.json | 8 + .../SystemAssignedInvalid.json | 8 + .../SystemAssignedOneEmptyString.json | 8 + .../SystemAssignedOneOtherValueNull.json | 8 + .../SystemAssignedOneValueNull.json | 8 + .../SystemAssignedValid.json | 8 + .../SystemAssignedValidExtraField.json | 9 + .../UserAssignedBothEmptyString.json | 13 + .../UserAssignedBothValuesNull.json | 13 + .../UserAssignedExtraField.json | 14 + .../UserAssignedInvalid.json | 13 + ...UserAssignedInvalidMultipleIdentities.json | 17 + .../UserAssignedOneEmptyString.json | 13 + .../UserAssignedOneOtherValueNull.json | 13 + .../UserAssignedOneValueNull.json | 13 + .../UserAssignedValid.json | 13 + .../UserAssignedValidMultipleIdentities.json | 17 + .../tests/UserAssignedIdentityTests.cs | 315 ++++++++++++ sdk/resourcemanager/ci.yml | 31 ++ 110 files changed, 9470 insertions(+), 3 deletions(-) create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/Azure.ResourceManager.Core.sln create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/Directory.Build.props create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhArmResponse.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhTaskDeferringAsyncPageable.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhWrappingAsyncPageable.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhWrappingPageable.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/ProxyResource.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/TrackedResource.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/WrappingPage.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ApiVersionsBase.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmBuilder.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmOperation.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmResponse.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmVoidOperation.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/AzureResourceManagerClient.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/AzureResourceManagerClientOptions.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ContainerBase.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ExtensionResourceContainer.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ExtensionResourceOperationsBase.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResource.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceData.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceOperations.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/IDeletableResource.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ITaggableResource.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/OperationsBase.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/PhArmOperation.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/ResourceGroupData.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/SubscriptionData.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Properties/AssemblyInfo.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceContainerBase.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroup.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupContainer.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupOperations.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceListOperations.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceOperationsBase.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/GenericResourceFilter.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/KnownKeys.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/LocationContainer.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/LocationData.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Plan.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Resource.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceFilterCollection.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentifier.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentity.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceNameFilter.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceTagFilter.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceType.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceTypeFilter.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Sku.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SystemAssignedIdentity.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/TrackedResource.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/UserAssignedIdentity.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Subscription.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/SubscriptionContainer.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/SubscriptionOperations.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/autorest.md create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/ApiVersionsBaseTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/ArmClientOptionsTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/ArmClientTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/Azure.ResourceManager.Core.Tests.csproj create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/IdentityTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/LocationTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/PlanTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/README.MD create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/Resource/TestResource.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceGroupOperationsTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceIdentifierTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceListOperationsTest.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceNameFilterTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceTagFilterTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceTypeTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/RpImplementations/ArmClientOptionsExtensions.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/RpImplementations/FakeResourceApiVersions.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/RpImplementations/FakeRpApiVersions.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/SkuTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/SystemAssignedIdentityTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TaggableResourceTests.cs create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/InvalidType.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/InvalidTypeIsNull.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedInnerExtraField.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedMiddleExtraField.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedOuterExtraField.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedValid.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedValidMultIdentities.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAssigned.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/UserAssigned.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedBothEmptyString.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedBothValuesNull.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedInvalid.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedOneEmptyString.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedOneOtherValueNull.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedOneValueNull.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedValid.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedValidExtraField.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedBothEmptyString.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedBothValuesNull.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedExtraField.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedInvalid.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedInvalidMultipleIdentities.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedOneEmptyString.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedOneOtherValueNull.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedOneValueNull.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedValid.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedValidMultipleIdentities.json create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/UserAssignedIdentityTests.cs create mode 100644 sdk/resourcemanager/ci.yml diff --git a/eng/Packages.Data.props b/eng/Packages.Data.props index 6db7c956504be..d8abf41aec2e6 100644 --- a/eng/Packages.Data.props +++ b/eng/Packages.Data.props @@ -64,14 +64,16 @@ - + - + - + + + @@ -81,6 +83,7 @@ + diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/Azure.ResourceManager.Core.sln b/sdk/resourcemanager/Azure.ResourceManager.Core/Azure.ResourceManager.Core.sln new file mode 100644 index 0000000000000..40437f8262ad3 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/Azure.ResourceManager.Core.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31019.35 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Core", "..\..\core\Azure.Core\src\Azure.Core.csproj", "{856C6092-55EB-4C02-B7D0-9846EDD70745}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Core.TestFramework", "..\..\core\Azure.Core.TestFramework\src\Azure.Core.TestFramework.csproj", "{62AF7C88-CE3F-416E-B18E-BC6F884C89E2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.ResourceManager.Core", "src\Azure.ResourceManager.Core.csproj", "{010FE057-7BB5-4F8C-BB9A-6378144F4CA8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.ResourceManager.Core.Tests", "tests\Azure.ResourceManager.Core.Tests.csproj", "{83E7651C-7FBE-45AA-B740-31FE9A3E44C7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {856C6092-55EB-4C02-B7D0-9846EDD70745}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {856C6092-55EB-4C02-B7D0-9846EDD70745}.Debug|Any CPU.Build.0 = Debug|Any CPU + {856C6092-55EB-4C02-B7D0-9846EDD70745}.Release|Any CPU.ActiveCfg = Release|Any CPU + {856C6092-55EB-4C02-B7D0-9846EDD70745}.Release|Any CPU.Build.0 = Release|Any CPU + {62AF7C88-CE3F-416E-B18E-BC6F884C89E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62AF7C88-CE3F-416E-B18E-BC6F884C89E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62AF7C88-CE3F-416E-B18E-BC6F884C89E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62AF7C88-CE3F-416E-B18E-BC6F884C89E2}.Release|Any CPU.Build.0 = Release|Any CPU + {010FE057-7BB5-4F8C-BB9A-6378144F4CA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {010FE057-7BB5-4F8C-BB9A-6378144F4CA8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {010FE057-7BB5-4F8C-BB9A-6378144F4CA8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {010FE057-7BB5-4F8C-BB9A-6378144F4CA8}.Release|Any CPU.Build.0 = Release|Any CPU + {83E7651C-7FBE-45AA-B740-31FE9A3E44C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83E7651C-7FBE-45AA-B740-31FE9A3E44C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83E7651C-7FBE-45AA-B740-31FE9A3E44C7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83E7651C-7FBE-45AA-B740-31FE9A3E44C7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FA17550D-A020-4DD7-B3A3-0228FC290A1F} + EndGlobalSection +EndGlobal diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/Directory.Build.props b/sdk/resourcemanager/Azure.ResourceManager.Core/Directory.Build.props new file mode 100644 index 0000000000000..63bd836ad44b7 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/Directory.Build.props @@ -0,0 +1,6 @@ + + + + diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhArmResponse.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhArmResponse.cs new file mode 100644 index 0000000000000..c764cedd3366f --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhArmResponse.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Azure.ResourceManager.Core +{ + /// + /// Placeholder class, used to convert the gewneric type argument for a response from the underlyign rest API to the + /// desired type argument in the response + /// + /// The to convert the TModel into. + /// The model returned by the existing serivce calls. + public class PhArmResponse : ArmResponse + where TOperations : class + where TModel : class + { + private readonly Func _converter; + private readonly Response _wrapped; + + /// + /// Initializes a new instance of the class. + /// + /// The results to wrap. + /// The function used to convert from existing type to new type. + public PhArmResponse(Response wrapped, Func converter) + { + _wrapped = wrapped; + _converter = converter; + } + + /// + public override TOperations Value => _converter(_wrapped.Value); + + /// + public override Response GetRawResponse() + { + return _wrapped.GetRawResponse(); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhTaskDeferringAsyncPageable.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhTaskDeferringAsyncPageable.cs new file mode 100644 index 0000000000000..0740e1668a50c --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhTaskDeferringAsyncPageable.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core.Adapters +{ + /// + /// A class repreesnting an AsyncPageable that executes a given task before retrieving the first page of results + /// + /// The type of that will be returned. + public class PhTaskDeferringAsyncPageable : AsyncPageable + where TOperations : notnull + { + private readonly Func>> _task; + + /// + /// Initializes a new instance of the class. + /// + /// The function to execute returning the AsyncPageable task. + public PhTaskDeferringAsyncPageable(Func>> task) + { + _task = task; + } + + /// + public override async IAsyncEnumerable> AsPages( + string continuationToken = null, + int? pageSizeHint = null) + { + await foreach (var page in (await _task().ConfigureAwait(false)).AsPages().ConfigureAwait(false)) + { + yield return page; + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhWrappingAsyncPageable.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhWrappingAsyncPageable.cs new file mode 100644 index 0000000000000..a15098cf7eee5 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhWrappingAsyncPageable.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// Returns an AsyncPageable that transforms each page of contents after they are retrieved from the server + /// according to the profived transformation function + /// + /// The model returned by existing AsyncPageable methods. + /// The to convert TModel into. + public class PhWrappingAsyncPageable : AsyncPageable + where TOperations : class + where TModel : class + { + private readonly Func _converter; + private readonly IEnumerable> _wrapped; + + /// + /// Initializes a new instance of the class. + /// + /// The results to wrap. + /// The function used to convert from existing type to new type. + public PhWrappingAsyncPageable(AsyncPageable wrapped, Func converter) + { + _wrapped = new[] { wrapped }; + _converter = converter; + } + + /// + /// Initializes a new instance of the class. + /// + /// The results to wrap. + /// The function used to convert from existing type to new type. + public PhWrappingAsyncPageable(IEnumerable> wrapped, Func converter) + { + _wrapped = wrapped; + _converter = converter; + } + + /// + public override async IAsyncEnumerable> AsPages( + string continuationToken = null, + int? pageSizeHint = null) + { + foreach (var pageEnum in _wrapped) + { + await foreach (var page in pageEnum.AsPages().WithCancellation(CancellationToken)) + { + yield return new WrappingPage(page, _converter); + } + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhWrappingPageable.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhWrappingPageable.cs new file mode 100644 index 0000000000000..c11077cb01072 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/PhWrappingPageable.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace Azure.ResourceManager.Core +{ + /// + /// This class allows performing conversions on pages of data as they are accessed - used in the prototype to convett + /// between underlying model types and the new model types that extend Resource, + /// and also for returning Operations classes for those underlying objects. + /// + /// The type parameter of the Pageable we are wrapping. + /// The desired type parameter of the returned pageable. + public class PhWrappingPageable : Pageable + where TOperations : class + where TModel : class + { + private readonly Func _converter; + private readonly IEnumerable> _wrapped; + + /// + /// Initializes a new instance of the class. + /// + /// The results to wrap. + /// The function used to convert from existing type to new type. + public PhWrappingPageable(Pageable wrapped, Func converter) + { + _wrapped = new[] { wrapped }; + _converter = converter; + } + + /// + /// Initializes a new instance of the class. + /// + /// The results to wrap. + /// The function used to convert from existing type to new type. + public PhWrappingPageable(IEnumerable> wrapped, Func converter) + { + _wrapped = wrapped; + _converter = converter; + } + + /// + public override IEnumerable> AsPages(string continuationToken = null, int? pageSizeHint = null) + { + foreach (var pages in _wrapped) + { + foreach (var page in pages.AsPages()) + { + yield return new WrappingPage(page, _converter); + } + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/ProxyResource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/ProxyResource.cs new file mode 100644 index 0000000000000..ac7ce5335c1b4 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/ProxyResource.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing a generic proxy resource in Azure + /// + /// The type of the underlying model this class wraps + public abstract class ProxyResource : Resource + where TModel : class + { + /// + /// Initializes a new instance of the class. + /// + /// The identifier of the resource that is the target of operations. + /// The model to copy from. + protected ProxyResource(ResourceIdentifier id, TModel data) + { + Id = id; + Model = data; + } + + /// + /// Initializes a new instance of the class. + /// + /// The identifier of the resource that is the target of operations. + /// The model to copy from. + protected ProxyResource(string id, TModel data) + { + if (ReferenceEquals(id, null)) + { + Id = null; + } + else + { + Id = id; + } + + Model = data; + } + + /// + /// Gets or sets the identifier for the resource. + /// + public override ResourceIdentifier Id { get; protected set; } + + /// + /// Gets or sets the Model this resource is based off. + /// + public virtual TModel Model { get; set; } + + /// + /// Converts from a into the TModel. + /// + /// The tracked resource convert from. + public static implicit operator TModel(ProxyResource other) + { + return other.Model; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/TrackedResource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/TrackedResource.cs new file mode 100644 index 0000000000000..f6aed917167f9 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/TrackedResource.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.ResourceManager.Resources.Models; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing a generic tracked resource in Azure. + /// + /// The type of the underlying model this class wraps + public abstract class TrackedResource : TrackedResource + where TModel : class + { + /// + /// Initializes a new instance of the class. + /// + /// The identifier of the resource that is the target of operations. + /// The location of the resource. + /// The model to copy from. + protected TrackedResource(ResourceIdentifier id, LocationData location, TModel data) + { + Id = id; + Location = location; + Model = data; + } + + /// + /// Initializes a new instance of the class. + /// + /// The identifier of the resource that is the target of operations. + /// The location of the resource. + /// The model to copy from. + protected TrackedResource(string id, LocationData location, TModel data) + { + if (ReferenceEquals(id, null)) + { + Id = null; + } + else + { + Id = id; + } + + Location = location; + Model = data; + } + + /// + /// Gets or sets the Model this resource is based off. + /// + public virtual TModel Model { get; set; } + + /// + /// Converts from a into the TModel. + /// + /// The tracked resource convert from. + public static implicit operator TModel(TrackedResource other) + { + return other.Model; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/WrappingPage.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/WrappingPage.cs new file mode 100644 index 0000000000000..821f8aaff7255 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Adapters/WrappingPage.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing a wrapper over + /// + /// The type parameter we are wrapping. + /// The desired type parameter of the returned page. + internal class WrappingPage : Page + where TOperations : class + where TModel : class + { + private readonly Func _converter; + private readonly Page _wrapped; + + /// + /// Initializes a new instance of the class. + /// + /// The results to wrap. + /// The function used to convert from existing type to new type. + internal WrappingPage(Page wrapped, Func converter) + { + _wrapped = wrapped; + _converter = converter; + } + + /// + public override IReadOnlyList Values => _wrapped.Values.Select(_converter).ToImmutableList(); + + /// + public override string ContinuationToken => _wrapped.ContinuationToken; + + /// + public override Response GetRawResponse() + { + return _wrapped.GetRawResponse(); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ApiVersionsBase.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ApiVersionsBase.cs new file mode 100644 index 0000000000000..d542b5b532a00 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ApiVersionsBase.cs @@ -0,0 +1,204 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Text.RegularExpressions; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing Azure resource API versions base. + /// + public class ApiVersionsBase : IEquatable, IComparable + { + private readonly string _value; + + /// + /// Initializes a new instance of the class. + /// + /// The API version value. + protected ApiVersionsBase(string value) + { + _value = value; + } + + /// + /// + /// + /// + /// + /// + public static bool operator <(ApiVersionsBase left, ApiVersionsBase right) + { + if (left is null) + return true; + + return left.CompareTo(right) == -1; + } + + /// + /// + /// + /// + /// + /// + public static bool operator >(ApiVersionsBase left, ApiVersionsBase right) + { + if (left is null) + return false; + + return left.CompareTo(right) == 1; + } + + /// + /// Implicit operator to convert ApiVersionsBase to string. + /// + /// The ApiVersionsBase object. + /// API version value. + public static implicit operator string(ApiVersionsBase version) + { + return version._value; + } + + /// + /// Overrides == operator for comparing ApiVersionsBase object with string object. + /// + /// The ApiVersionsBase object to compare. + /// The API version value in string to compare. + /// Comparison result in boolean. Equal returns true otherwise returns false. + public static bool operator ==(ApiVersionsBase first, string second) + { + if (ReferenceEquals(null, first)) + { + return ReferenceEquals(null, second); + } + + if (ReferenceEquals(null, second)) + { + return false; + } + + return first.Equals(second); + } + + /// + /// Overrides != operator for comparing ApiVersionsBase object with string object. + /// + /// The ApiVersionsBase object to compare. + /// The API version value in string to compare. + /// Comparison result in boolean. Equal returns false otherwise returns true. + public static bool operator !=(ApiVersionsBase first, string second) + { + if (ReferenceEquals(null, first)) + { + return !ReferenceEquals(null, second); + } + + if (ReferenceEquals(null, second)) + { + return true; + } + + return !first.Equals(second); + } + + /// + /// Compares two API version values in string type. + /// + /// The API version value to compare. + /// Comparison result in integer. 1 for greater than, 0 for equals to, and -1 for less than. + public int CompareTo(string other) + { + if (other == null) + { + return 1; + } + + var regPattern = @"(\d\d\d\d-\d\d-\d\d)(.*)"; + + var otherMatch = Regex.Match(other, regPattern); + var thisMatch = Regex.Match(_value, regPattern); + + var otherDatePart = otherMatch.Groups[1].Value; + var thisDatePart = thisMatch.Groups[1].Value; + + if (otherDatePart == thisDatePart) + { + var otherPreviewPart = otherMatch.Groups[2].Value; + var thisPreviewPart = thisMatch.Groups[2].Value; + + if (otherPreviewPart == thisPreviewPart) + { + return 0; + } + + if (string.IsNullOrEmpty(otherPreviewPart)) + { + return -1; + } + + if (string.IsNullOrEmpty(thisPreviewPart)) + { + return 1; + } + + return string.Compare(thisPreviewPart, otherPreviewPart, StringComparison.InvariantCulture); + } + + return string.Compare(thisDatePart, otherDatePart, StringComparison.InvariantCulture); + } + + /// + /// Compares the API version value in ApiVersionsBase object and the one in string. + /// + /// The API version value to compare. + /// Comparison result in boolean. Equal returns true otherwise returns false. + public bool Equals(string other) + { + if (other == null) + { + return false; + } + + return other == _value; + } + + /// + /// Converts ApiVersionsBase object to string. + /// + /// The API version value. + public override string ToString() + { + return _value; + } + + /// + /// Compares the API version value in ApiVersionsBase object and the one in object. + /// + /// The object to compare. + /// Comparison result in boolean. Equal returns true otherwise returns false. + public override bool Equals(object obj) + { + if (obj is ApiVersionsBase) + { + return Equals(obj as ApiVersionsBase); + } + + if (obj is string) + { + return Equals(obj as string); + } + + return false; + } + + /// + /// Gets the hash code of the API version value. + /// + /// The hash code of the API version value. + public override int GetHashCode() + { + return _value.GetHashCode(); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmBuilder.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmBuilder.cs new file mode 100644 index 0000000000000..353a316d5b792 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmBuilder.cs @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing a builder object used to create Azure resources. + /// + /// The type of the operations class for a specific resource. + /// The type of the class containing properties for the underlying resource. + public class ArmBuilder + where TResource : Resource + where TOperations : ResourceOperationsBase + { + /// + /// Initializes a new instance of the class. + /// + /// The container object to create the resource in. + /// The resource to create. + public ArmBuilder(ResourceContainerBase container, TResource resource) + { + Resource = resource; + UnTypedContainer = container; + } + + /// + /// Gets the resource object to create. + /// + protected TResource Resource { get; private set; } + + /// + /// Gets the resource name. + /// + protected string ResourceName { get; private set; } + + /// + /// Gets the container object to create the resource in. + /// + protected ResourceContainerBase UnTypedContainer { get; private set; } + + /// + /// Creates the resource object to send to the Azure API. + /// + /// The resource to create. + public TResource Build() + { + ThrowIfNotValid(); + OnBeforeBuild(); + InternalBuild(); + OnAfterBuild(); + + return Resource; + } + + /// + /// The operation to create or update a resource. Please note some properties can be set only during creation. + /// + /// The name of the new resource to create. + /// A response with the operation for this resource. + public ArmResponse CreateOrUpdate(string name) + { + ResourceName = name; + Resource = Build(); + + return UnTypedContainer.CreateOrUpdate(name, Resource); + } + + /// + /// The operation to create or update a resource. Please note some properties can be set only during creation. + /// + /// The name of the new resource to create. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public async Task> CreateOrUpdateAsync( + string name, + CancellationToken cancellationToken = default) + { + ResourceName = name; + Resource = Build(); + + return await UnTypedContainer.CreateOrUpdateAsync(name, Resource, cancellationToken).ConfigureAwait(false); + } + + /// + /// The operation to create or update a resource. Please note some properties can be set only during creation. + /// + /// The name of the new resource to create. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An that allows polling for completion of the operation. + /// + /// Details on long running operation object. + /// + public ArmOperation StartCreateOrUpdate(string name, CancellationToken cancellationToken = default) + { + ResourceName = name; + Resource = Build(); + + return UnTypedContainer.StartCreateOrUpdate(name, Resource, cancellationToken); + } + + /// + /// The operation to create or update a resource. Please note some properties can be set only during creation. + /// + /// The name of the new resource to create. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns an that allows polling for completion of the operation. + /// + /// Details on long running operation object. + /// + public async Task> StartCreateOrUpdateAsync( + string name, + CancellationToken cancellationToken = default) + { + ResourceName = name; + Resource = Build(); + + return await UnTypedContainer.StartCreateOrUpdateAsync(name, Resource, cancellationToken).ConfigureAwait(false); + } + + /// + /// Determines whether or not the resource is valid. + /// + /// The message indicating what is wrong with the resource. + /// Whether or not the resource is valid. + protected virtual bool IsValid(out string message) + { + message = string.Empty; + + return true; + } + + /// + /// Perform any tasks necessary after the resource is built + /// + protected virtual void OnAfterBuild() + { + } + + /// + /// Perform any tasks necessary before the resource is built + /// + protected virtual void OnBeforeBuild() + { + } + + private static void InternalBuild() + { + } + + private void ThrowIfNotValid() + { + if (!IsValid(out var message)) + { + throw new InvalidOperationException(message); + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmOperation.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmOperation.cs new file mode 100644 index 0000000000000..18caabd837a3c --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmOperation.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.ResourceManager.Core +{ + /// + /// Abstract class for long-running or synchronous applications. + /// + /// The to return representing the result of the ArmOperation. + public abstract class ArmOperation : Operation + { + /// + /// Initializes a new instance of the class. + /// + /// The representing the result of the ArmOperation. + protected ArmOperation(TOperations syncValue) + { + CompletedSynchronously = syncValue != null; + SyncValue = syncValue; + } + + /// + /// Gets a value indicating whether or not the operation completed synchronously. + /// + protected bool CompletedSynchronously { get; } + + /// + /// Gets the representing the result of the ArmOperation. + /// + protected TOperations SyncValue { get; } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmResponse.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmResponse.cs new file mode 100644 index 0000000000000..d130fc8186316 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmResponse.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing a response object from azure resource manager service. + /// + public sealed class ArmResponse : ArmResponse + { + private readonly Response _response; + + /// + /// Initializes a new instance of the class. + /// + /// The azure response object to wrap. + public ArmResponse(Response response) + { + _response = response; + } + + /// + public override Response Value => _response; + + /// + public override Response GetRawResponse() + { + return _response; + } + } + + /// + /// A class representing a response object from azure resource manager service. + /// + /// The operations object return by the api call. + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Allowed when we have a generic version of the same type")] + public abstract class ArmResponse : Response + { + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmVoidOperation.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmVoidOperation.cs new file mode 100644 index 0000000000000..bbab26ea2456e --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ArmVoidOperation.cs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// Generic ARM long running operation class for operations with no returned value + /// + public sealed class ArmVoidOperation : ArmOperation + { + private readonly Operation _wrapped; + + /// + /// Initializes a new instance of the class. + /// + /// The operation that has a response which has no body. + public ArmVoidOperation(Operation other) + : base(null) + { + _wrapped = other; + } + + /// + /// Initializes a new instance of the class. + /// + /// The response which has no body. + public ArmVoidOperation(Response other) + : base(other) + { + } + + /// + public override string Id => _wrapped?.Id; + + /// + public override Response Value => SyncValue; + + /// + public override bool HasCompleted => CompletedSynchronously || _wrapped.HasCompleted; + + /// + public override bool HasValue => CompletedSynchronously || _wrapped.HasValue; + + /// + public override Response GetRawResponse() + { + return CompletedSynchronously ? SyncValue : _wrapped.GetRawResponse(); + } + + /// + public override Response UpdateStatus(CancellationToken cancellationToken = default) + { + return CompletedSynchronously ? SyncValue : _wrapped.UpdateStatus(cancellationToken); + } + + /// + public override async ValueTask UpdateStatusAsync(CancellationToken cancellationToken = default) + { + return CompletedSynchronously ? SyncValue : await _wrapped.UpdateStatusAsync(cancellationToken).ConfigureAwait(false); + } + + /// + public override async ValueTask> WaitForCompletionAsync( + CancellationToken cancellationToken = default) + { + return CompletedSynchronously + ? new WrappingResponse(SyncValue) + : await _wrapped.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false); + } + + /// + public override async ValueTask> WaitForCompletionAsync( + TimeSpan pollingInterval, + CancellationToken cancellationToken) + { + return CompletedSynchronously + ? new WrappingResponse(SyncValue) + : await _wrapped.WaitForCompletionAsync(pollingInterval, cancellationToken).ConfigureAwait(false); + } + + /// + /// A class which wraps a response with no body. + /// + internal class WrappingResponse : ArmResponse + { + private readonly Response _wrapped; + + /// + /// Initializes a new instance of the class. + /// + /// The response object to wrap. + public WrappingResponse(Response wrapped) + { + _wrapped = wrapped; + } + + /// + public override Response Value => _wrapped; + + /// + public override Response GetRawResponse() + { + return _wrapped; + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj new file mode 100644 index 0000000000000..0718cbf721cb4 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj @@ -0,0 +1,21 @@ + + + + 1.0.0-beta.1 + Azure.ResourceManager.Core + + Azure management core SDK for Azure resources. + This is a beta preview vesion. This version uses a next-generation code generator that introduces important breaking changes, but also new features (such as intuitive authentication, custom HTTP pipeline, distributed tracing and much more). + + azure;management;resource + $(NoWarn);AZC0008;AZC0001;AZC0107;CA2214;CA1036;CA1067;CA1065;SA1028 + + + + + + + + + + diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/AzureResourceManagerClient.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/AzureResourceManagerClient.cs new file mode 100644 index 0000000000000..e5db0c8bee2e7 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/AzureResourceManagerClient.cs @@ -0,0 +1,185 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Core; +using Azure.Identity; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace Azure.ResourceManager.Core +{ + /// + /// The entry point for all ARM clients. + /// + public class AzureResourceManagerClient + { + /// + /// The base URI of the service. + /// + internal const string DefaultUri = "https://management.azure.com"; + + private readonly TokenCredential _credentials; + + private readonly Uri _baseUri; + + /// + /// Initializes a new instance of the class for mocking. + /// + protected AzureResourceManagerClient() + : this(null, null, new DefaultAzureCredential(), new AzureResourceManagerClientOptions()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + public AzureResourceManagerClient(AzureResourceManagerClientOptions options = default) + : this(null, null, new DefaultAzureCredential(), options) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A credential used to authenticate to an Azure Service. + /// The client parameters to use in these operations. + public AzureResourceManagerClient(TokenCredential credential, AzureResourceManagerClientOptions options = default) + : this(null, null, credential, options) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The id of the default Azure subscription. + /// A credential used to authenticate to an Azure Service. + /// The client parameters to use in these operations. + public AzureResourceManagerClient(string defaultSubscriptionId, TokenCredential credential, AzureResourceManagerClientOptions options = default) + : this(defaultSubscriptionId, null, credential, options) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The base URI of the service. + /// A credential used to authenticate to an Azure Service. + /// The client parameters to use in these operations. + public AzureResourceManagerClient(Uri baseUri, TokenCredential credential, AzureResourceManagerClientOptions options = default) + : this(null, baseUri, credential, options) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The id of the default Azure subscription. + /// The base URI of the service. + /// A credential used to authenticate to an Azure Service. + /// The client parameters to use in these operations. + private AzureResourceManagerClient(string defaultSubscriptionId, Uri baseUri, TokenCredential credential, AzureResourceManagerClientOptions options = default) + { + _credentials = credential; + _baseUri = baseUri; + ClientOptions = options ?? new AzureResourceManagerClientOptions(); + + if (string.IsNullOrWhiteSpace(defaultSubscriptionId)) + { + DefaultSubscription = GetDefaultSubscription(); + } + else + { + DefaultSubscription = GetSubscriptionOperations(defaultSubscriptionId).Get().Value; + } + + ApiVersionOverrides = new Dictionary(); + } + + /// + /// Gets the Api version overrides. + /// + public Dictionary ApiVersionOverrides { get; private set; } + + /// + /// Gets the default Azure subscription. + /// + public Subscription DefaultSubscription { get; private set; } + + /// + /// Gets the Azure resource manager client options. + /// + internal AzureResourceManagerClientOptions ClientOptions { get; } + + /// + /// Gets the Azure subscription operations. + /// + /// The guid of the subscription. + /// Subscription operations. + public SubscriptionOperations GetSubscriptionOperations(string subscriptionGuid) => new SubscriptionOperations( + ClientOptions, + subscriptionGuid, + _credentials, + _baseUri); + + /// + /// Gets the Azure subscriptions. + /// + /// Subscription container. + public SubscriptionContainer GetSubscriptionContainer() + { + return new SubscriptionContainer(ClientOptions, _credentials, _baseUri); + } + + /// + /// Gets resource group operations. + /// + /// The id of the Azure subscription. + /// The resource group name. + /// Resource group operations. + public ResourceGroupOperations GetResourceGroupOperations(string subscriptionGuid, string resourceGroupName) + { + return GetSubscriptionOperations(subscriptionGuid).GetResourceGroupOperations(resourceGroupName); + } + + /// + /// Gets resource group operations. + /// + /// The resource identifier of the resource group. + /// Resource group operations. + public ResourceGroupOperations GetResourceGroupOperations(ResourceIdentifier resourceGroupId) + { + return GetSubscriptionOperations(resourceGroupId.Subscription).GetResourceGroupOperations(resourceGroupId.ResourceGroup); + } + + /// + /// Gets resource operations base. + /// + /// The type of the underlying model this class wraps. + /// The id of the Azure subscription. + /// The resource group name. + /// The resource type name. + /// Resource operations of the resource. + public T GetResourceOperations(string subscription, string resourceGroup, string name) + where T : OperationsBase + { + var rgOp = GetSubscriptionOperations(subscription).GetResourceGroupOperations(resourceGroup); + return Activator.CreateInstance( + typeof(T), + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, + null, + new object[] { rgOp, name }, + CultureInfo.InvariantCulture) as T; + } + + private Subscription GetDefaultSubscription() + { + var sub = GetSubscriptionContainer().List().FirstOrDefault(); + if (sub is null) + throw new Exception("No subscriptions found for the given credentials"); + return sub; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/AzureResourceManagerClientOptions.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/AzureResourceManagerClientOptions.cs new file mode 100644 index 0000000000000..2b6e0a8a2753a --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/AzureResourceManagerClientOptions.cs @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Core; +using Azure.Core.Pipeline; +using System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing Azure resource manager client options. + /// + public sealed class AzureResourceManagerClientOptions : ClientOptions + { + private static readonly object _overridesLock = new object(); + + private Dictionary _overrides = new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + public AzureResourceManagerClientOptions() + : this(LocationData.Default, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The default location to use if can't be inherited from parent. + public AzureResourceManagerClientOptions(LocationData defaultLocation) + : this(defaultLocation, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The default location to use if can't be inherited from parent. + /// The client parameters to use in these operations. + internal AzureResourceManagerClientOptions(LocationData defaultLocation, AzureResourceManagerClientOptions other = null) + { + // Will go away when moved into core since we will have directy acces the policies and transport, so just need to set those + if (!ReferenceEquals(other, null)) + Copy(other); + DefaultLocation = defaultLocation; + } + + /// + /// Gets the default location to use if can't be inherited from parent. + /// + public LocationData DefaultLocation { get; } + + /// + /// Gets each http call policies. + /// + /// A collection of http pipeline policy that may take multiple service requests to iterate over. + internal IList PerCallPolicies { get; } = new List(); + + /// + /// Gets each http retry call policies. + /// + /// A collection of http pipeline policy that may take multiple service requests to iterate over. + internal IList PerRetryPolicies { get; } = new List(); + + /// + /// Converts client options. + /// + /// The type of the underlying model this class wraps. + /// The converted client options. + public T Convert() + where T : ClientOptions, new() + { + var newOptions = new T(); + newOptions.Transport = Transport; + foreach (var pol in PerCallPolicies) + { + newOptions.AddPolicy(pol, HttpPipelinePosition.PerCall); + } + + foreach (var pol in PerRetryPolicies) + { + newOptions.AddPolicy(pol, HttpPipelinePosition.PerRetry); + } + + return newOptions; + } + + /// + /// Adds a policy for Azure resource manager client http call. + /// + /// The http call policy in the pipeline. + /// The position of the http call policy in the pipeline. + public new void AddPolicy(HttpPipelinePolicy policy, HttpPipelinePosition position) + { + // TODO policy lists are internal hence we don't have acces to them by inheriting ClientOptions in this Asembly, this is a wrapper for now to convert to the concrete + // policy options. + switch (position) + { + case HttpPipelinePosition.PerCall: + PerCallPolicies.Add(policy); + break; + case HttpPipelinePosition.PerRetry: + PerRetryPolicies.Add(policy); + break; + default: + throw new ArgumentOutOfRangeException(nameof(position), position, null); + } + + base.AddPolicy(policy, position); + } + + /// + /// Gets override object. + /// + /// The type of the underlying model this class wraps. + /// A function which returns an object. + /// The override object. + [EditorBrowsable(EditorBrowsableState.Never)] + public object GetOverrideObject(Func ctor) + { + object overrideObject; + Type type = typeof(T); + if (!_overrides.TryGetValue(type, out overrideObject)) + { + lock (_overridesLock) + { + if (!_overrides.TryGetValue(type, out overrideObject)) + { + overrideObject = ctor(); + _overrides[type] = overrideObject; + } + } + } + + return overrideObject; + } + + // Will be removed like AddPolicy when we move to azure core + private void Copy(AzureResourceManagerClientOptions other) + { + Transport = other.Transport; + foreach (var pol in other.PerCallPolicies) + { + AddPolicy(pol, HttpPipelinePosition.PerCall); + } + + foreach (var pol in other.PerRetryPolicies) + { + AddPolicy(pol, HttpPipelinePosition.PerRetry); + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ContainerBase.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ContainerBase.cs new file mode 100644 index 0000000000000..16cc90042c8a6 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ContainerBase.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Core; +using System; +using System.Globalization; + +namespace Azure.ResourceManager.Core +{ + /// + /// Base class representing collection of resources. + /// + /// The type of the class containing operations for the underlying resource. + public abstract class ContainerBase : OperationsBase + where TOperations : ResourceOperationsBase + { + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + /// A credential used to authenticate to an Azure Service. + /// The base URI of the service. + protected ContainerBase(AzureResourceManagerClientOptions options, ResourceIdentifier id, TokenCredential credential, Uri baseUri) + : base(options, id, credential, baseUri) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The resource representing the parent resource. + protected ContainerBase(ResourceOperationsBase parent) + : base(parent.ClientOptions, parent.Id, parent.Credential, parent.BaseUri) + { + Parent = parent; + } + + /// + /// Gets the parent resource of this resource + /// + protected ResourceOperationsBase Parent { get; } + + /// + /// Returns the resource from Azure if it exists + /// + /// The name of the resource you want to get. + /// The resource if it existed, null otherwise. + /// Whether or not the resource existed. + public virtual bool TryGetValue(string resourceName, out ArmResponse resource) + { + var op = GetOperation(resourceName); + + try + { + resource = op.Get(); + return true; + } + catch + { + resource = null; + return false; + } + } + + /// + /// Determines whether or not the azure resource exists in this container + /// + /// The name of the resource you want to check. + /// Whether or not the resource existed. + public virtual bool DoesExist(string resourceName) + { + ArmResponse output; + return TryGetValue(resourceName, out output); + } + + /// + /// Get an instance of the operations this container holds. + /// + /// The name of the resource to scope the operations to. + /// An instance of . + protected virtual ResourceOperationsBase GetOperation(string resourceName) + { + return Activator.CreateInstance( + typeof(TOperations).BaseType, + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, + null, + new object[] { Parent, resourceName }, + CultureInfo.InvariantCulture) as ResourceOperationsBase; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ExtensionResourceContainer.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ExtensionResourceContainer.cs new file mode 100644 index 0000000000000..a04a28023eb26 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ExtensionResourceContainer.cs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// Container for extension resources. Because there is no CreateOrUpdate, there is a difference in the input and output model + /// + /// Operations class returned. + /// Input Model. + public abstract class ExtensionResourceContainer : ExtensionResourceOperationsBase + where TOperations : ExtensionResourceOperationsBase + where TInput : class + { + /// + /// Initializes a new instance of the class. + /// Create an ResourceContainer from an operations class or client + /// + /// The operations to copy the client options from. + protected ExtensionResourceContainer(OperationsBase operations) + : base(operations) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The operations to copy the client options from. + /// The resource Id of the parent resource. + protected ExtensionResourceContainer(OperationsBase operations, ResourceIdentifier parentId) + : base(operations, parentId) + { + } + + /// + /// Validate that the given resource Id represents a valid parent for this resource + /// + /// The resource Id of the parent resource. + protected override void Validate(ResourceIdentifier identifier) + { + } + + /// + /// Create a new extension resource at the given scope. Block further execution on the current thread until creation is complete. + /// + /// The name of the created extension resource. + /// The properties of the extension resource. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An Http envelope containing the operations for the given extension. + public abstract ArmResponse Create(string name, TInput resourceDetails, CancellationToken cancellationToken = default); + + /// + /// Create a new extension resource at the given scope without blocking the current thread. + /// Returns a Task that allows control over when or if the thread is blocked. + /// + /// The name of the created extension resource. + /// The properties of the extension resource. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A Task that creates the extension resource. + public abstract Task> CreateAsync(string name, TInput resourceDetails, CancellationToken cancellationToken = default); + + /// + /// Begin Creation of a new extension resource. Block until the creation is accepted by the service. + /// The returned object allows fine-grained control over waiting for creation to complete. + /// + /// The name of the created extension resource. + /// The properties of the extension resource. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An instance of , allowing fine grained control over waiting for creation to complete. + public abstract ArmOperation StartCreate(string name, TInput resourceDetails, CancellationToken cancellationToken = default); + + /// + /// Begin Creation of a new extension resource in a background task. + /// When creation has successfully begin, the object returned from the completed task allows fine-grained control over waiting for creation to complete. + /// + /// The name of the created extension resource. + /// The properties of the extension resource. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns an that allows polling for completion of the operation. + public abstract Task> StartCreateAsync(string name, TInput resourceDetails, CancellationToken cancellationToken = default); + + /// + /// Lists the extension resources at the current scope. Blocks until the first page of results is returned. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An instance of allowing paged or non-paged enumeration of results. + public abstract Pageable ListAtScope(CancellationToken cancellationToken = default); + + /// + /// Lists the extension resources at the current scope asynchronously. The returned task completes when the first page of results is returned. + /// + /// The cancellation token clients can use to cancel any blocking HTTP requests made by this method, including + /// any Http requests that result from enumerating pages of results. + /// An instance of allowing asynchronous paged or non-paged enumeration of results. + public abstract AsyncPageable ListAtScopeAsync(CancellationToken cancellationToken = default); + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ExtensionResourceOperationsBase.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ExtensionResourceOperationsBase.cs new file mode 100644 index 0000000000000..ca83225f60b32 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ExtensionResourceOperationsBase.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// Base class for all extensions + /// + public abstract class ExtensionResourceOperationsBase : OperationsBase + { + /// + /// Initializes a new instance of the class. + /// + /// The operations to copy the client options from. + protected ExtensionResourceOperationsBase(OperationsBase genericOperations) + : base(genericOperations.ClientOptions, genericOperations.Id, genericOperations.Credential, genericOperations.BaseUri) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The operations to copy the client options from. + /// The identifier of the extension resource. + protected ExtensionResourceOperationsBase(OperationsBase genericOperations, ResourceIdentifier id) + : base(genericOperations.ClientOptions, id, genericOperations.Credential, genericOperations.BaseUri) + { + } + } + + /// + /// Separate Extension resources from non-extension resources + /// + /// The typed operations class for a specific resource. + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Resource types that differ by Type arguments")] + public abstract class ExtensionResourceOperationsBase : ExtensionResourceOperationsBase + where TOperations : ExtensionResourceOperationsBase + { + /// + /// Initializes a new instance of the class. + /// + /// The operations to copy the client options from. + protected ExtensionResourceOperationsBase(OperationsBase genericOperations) + : base(genericOperations) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The operations to copy the client options from. + /// The identifier of the extension resource. + protected ExtensionResourceOperationsBase(OperationsBase genericOperations, ResourceIdentifier id) + : base(genericOperations, id) + { + } + + /// + /// Get details and operations for this extension resource. This call will block the thread until details are returned from the service. + /// + /// An Http Response containing details and operations for the extension resource. + public abstract ArmResponse Get(); + + /// + /// Get details and operations for this extension resource. This call returns a Task that completes when the details are returned from the service. + /// + /// A token allowing cancellation of the Http call in the task. + /// A Task that retrieves the resource details. When complete, the task will yield an Http Response + /// containing details and operations for the extension resource. + public abstract Task> GetAsync(CancellationToken cancellationToken = default); + + /// + /// Get details for this resource from the service or can be overriden to provide a cached instance. + /// + /// A operation for this resource. + protected virtual TOperations GetResource() + { + return Get().Value; + } + + /// + /// Get details for this resource from the service or can be overriden to provide a cached instance. + /// + /// A that on completion returns a operation for this resource. + protected virtual async Task GetResourceAsync() + { + return (await GetAsync().ConfigureAwait(false)).Value; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResource.cs new file mode 100644 index 0000000000000..3c09153daffdb --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResource.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing a generic azure resource along with the instance operations that can be performed on it. + /// + public class GenericResource : GenericResourceOperations + { + /// + /// Initializes a new instance of the class. + /// + /// The operations object to copy the client parameters from. + /// The data model representing the generic azure resource. + internal GenericResource(ResourceOperationsBase operations, GenericResourceData resource) + : base(operations, resource.Id) + { + Data = resource; + } + + /// + /// Gets the data representing this generic azure resource. + /// + public GenericResourceData Data { get; } + + /// + protected override GenericResource GetResource() + { + return this; + } + + /// + protected override Task GetResourceAsync() + { + return Task.FromResult(this); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceData.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceData.cs new file mode 100644 index 0000000000000..560b5649eb094 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceData.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.ResourceManager.Resources.Models; +using System; +using System.Collections.Generic; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing the generic azure resource data model. + /// + public class GenericResourceData : TrackedResource + { + /// + /// Initializes a new instance of the class. + /// + /// The existing resource model to copy from. + public GenericResourceData(ResourceManager.Resources.Models.GenericResource genericResource) + : base(genericResource.Id, genericResource.Location, genericResource) + { + Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + Tags.Clear(); + foreach (var tag in genericResource.Tags) + { + Tags.Add(tag); + } + + if (Model.Sku != null) + Sku = new Sku(Model.Sku); + + if (Model.Plan != null) + Plan = new Plan(Model.Plan); + + Kind = Model.Kind; + ManagedBy = Model.ManagedBy; + } + + /// + /// Initializes a new instance of the class. + /// + /// The identifier of the resource that is the target of operations. + public GenericResourceData(ResourceIdentifier id) + : base(id, LocationData.Default, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The identifier of the resource that is the target of operations. + /// The location of the resource. + public GenericResourceData(ResourceIdentifier id, LocationData location) + : base(id, location, null) + { + } + + /// + public override ResourceIdentifier Id { get; protected set; } + + /// + /// Gets or sets who this resource is managed by. + /// + public string ManagedBy { get; set; } + + /// + /// Gets or sets the sku. + /// + public Sku Sku { get; set; } + + /// + /// Gets or sets the plan. + /// + public Plan Plan { get; set; } + + /// + /// Gets or sets the kind. + /// + public string Kind { get; set; } + + /// + /// Gets the Tags. + /// + public override IDictionary Tags { get; } + + /// + /// Converts from a into the ResourceManager.Resources.Models.GenericResource. + /// + /// The tracked resource convert from. + public static implicit operator ResourceManager.Resources.Models.GenericResource(GenericResourceData other) + { + other.Model.Tags.Clear(); + foreach (var tag in other.Tags) + { + other.Model.Tags.Add(tag); + } + + return other.Model; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceOperations.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceOperations.cs new file mode 100644 index 0000000000000..317baddd8be2a --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceOperations.cs @@ -0,0 +1,231 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Core.Pipeline; +using Azure.ResourceManager.Resources; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing the operations that can be performed over a specific ArmResource. + /// + public class GenericResourceOperations : ResourceOperationsBase, ITaggableResource, IDeletableResource + { + private readonly string _apiVersion; + + /// + /// Initializes a new instance of the class. + /// + /// The resource operations to copy the options from. + /// The identifier of the resource that is the target of operations. + internal GenericResourceOperations(ResourceOperationsBase operations, ResourceIdentifier id) + : base(operations, id) + { + _apiVersion = "BAD VALUE"; + } + + /// + protected override ResourceType ValidResourceType => ResourceGroupOperations.ResourceType; + + private ResourcesOperations Operations => new ResourcesManagementClient( + BaseUri, + Id.Subscription, + Credential, + ClientOptions.Convert()).Resources; + + /// + /// Delete the resource. + /// + /// The status of the delete operation. + public ArmResponse Delete() + { + return new ArmResponse(Operations.StartDeleteById(Id, _apiVersion).WaitForCompletionAsync().EnsureCompleted()); + } + + /// + /// Delete the resource. + /// + /// A token allowing immediate cancellation of any blocking call performed during the deletion. + /// A that on completion returns the status of the delete operation. + public async Task> DeleteAsync(CancellationToken cancellationToken = default) + { + var operation = await Operations.StartDeleteByIdAsync(Id, _apiVersion, cancellationToken).ConfigureAwait(false); + var result = await operation.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false); + return new ArmResponse(result); + } + + /// + /// Delete the resource. + /// + /// A token allowing immediate cancellation of any blocking call performed during the deletion. + /// A which allows the caller to control polling and waiting for resource deletion. + /// The operation yields the final http response to the delete request when complete. + /// + /// Details on long running operation object. + /// + public ArmOperation StartDelete(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(Operations.StartDeleteById(Id, _apiVersion, cancellationToken)); + } + + /// + /// Delete the resource. This call returns a Task that blocks until the delete operation is accepted on the service. + /// + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A that on completion returns a which + /// allows the caller to control polling and waiting for resource deletion. + /// The operation yields the final http response to the delete request when complete. + /// + /// Details on long running operation object. + /// + public async Task> StartDeleteAsync(CancellationToken cancellationToken = default) + { + var operation = await Operations.StartDeleteByIdAsync(Id, _apiVersion, cancellationToken).ConfigureAwait(false); + return new ArmVoidOperation(operation); + } + + /// + /// Add a tag to the resource + /// + /// The tag key. + /// The tag value. + /// An that allows the user to control polling and waiting for Tag completion. + public ArmOperation StartAddTag(string key, string value) + { + GenericResource resource = GetResource(); + UpdateTags(key, value, resource.Data.Tags); + return new PhArmOperation( + Operations.StartUpdateById(Id, _apiVersion, resource.Data).WaitForCompletionAsync().EnsureCompleted(), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// + /// Add a tag to the resource + /// + /// The tag key. + /// The tag value. + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A that performs the Tag operation. The Task yields an an + /// that allows the user to control polling and waiting for + /// Tag completion. + public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + GenericResource resource = GetResource(); + UpdateTags(key, value, resource.Data.Tags); + var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false); + return new PhArmOperation( + await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// + public override ArmResponse Get() + { + return new PhArmResponse( + Operations.GetById(Id, _apiVersion), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// + public override async Task> GetAsync(CancellationToken cancellationToken = default) + { + return new PhArmResponse( + await Operations.GetByIdAsync(Id, _apiVersion, cancellationToken).ConfigureAwait(false), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// + protected override void Validate(ResourceIdentifier identifier) + { + } + + /// + public ArmResponse SetTags(IDictionary tags) + { + GenericResource resource = GetResource(); + ReplaceTags(tags, resource.Data.Tags); + return new PhArmResponse( + Operations.StartUpdateById(Id, _apiVersion, resource.Data).WaitForCompletionAsync().EnsureCompleted(), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// + public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + GenericResource resource = GetResource(); + ReplaceTags(tags, resource.Data.Tags); + var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false); + return new PhArmResponse( + await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// + public ArmOperation StartSetTags(IDictionary tags) + { + GenericResource resource = GetResource(); + ReplaceTags(tags, resource.Data.Tags); + return new PhArmOperation( + Operations.StartUpdateById(Id, _apiVersion, resource.Data).WaitForCompletionAsync().EnsureCompleted(), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// + public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + GenericResource resource = GetResource(); + ReplaceTags(tags, resource.Data.Tags); + var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false); + return new PhArmOperation( + await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// + public ArmResponse RemoveTag(string key) + { + GenericResource resource = GetResource(); + DeleteTag(key, resource.Data.Tags); + return new PhArmResponse( + Operations.StartUpdateById(Id, _apiVersion, resource.Data).WaitForCompletionAsync().EnsureCompleted(), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// + public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + GenericResource resource = GetResource(); + DeleteTag(key, resource.Data.Tags); + var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false); + return new PhArmResponse( + await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// + public ArmOperation StartRemoveTag(string key) + { + GenericResource resource = GetResource(); + DeleteTag(key, resource.Data.Tags); + return new PhArmOperation( + Operations.StartUpdateById(Id, _apiVersion, resource.Data).WaitForCompletionAsync().EnsureCompleted(), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// + public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + GenericResource resource = GetResource(); + DeleteTag(key, resource.Data.Tags); + var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false); + return new PhArmOperation( + await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + v => new GenericResource(this, new GenericResourceData(v))); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/IDeletableResource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/IDeletableResource.cs new file mode 100644 index 0000000000000..19b65bfbc691f --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/IDeletableResource.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// Interface for operations to delete a resource + /// + public interface IDeletableResource + { + /// + /// Delete the resource. + /// + /// The status of the delete operation. + ArmResponse Delete(); + + /// + /// Delete the resource. + /// + /// A token allowing immediate cancellation of any blocking call performed during the deletion. + /// A that on completion returns the status of the delete operation. + Task> DeleteAsync(CancellationToken cancellationToken = default); + + /// + /// Delete the resource. + /// + /// A token allowing immediate cancellation of any blocking call performed during the deletion. + /// A which allows the caller to control polling and waiting for resource deletion. + /// The operation yields the final http response to the delete request when complete. + /// + /// Details on long running operation object. + /// + ArmOperation StartDelete(CancellationToken cancellationToken = default); + + /// + /// Delete the resource. This call returns a Task that blocks until the delete operation is accepted on the service. + /// + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A that on completion returns a which + /// allows the caller to control polling and waiting for resource deletion. + /// The operation yields the final http response to the delete request when complete. + /// + /// Details on long running operation object. + /// + Task> StartDeleteAsync(CancellationToken cancellationToken = default); + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ITaggableResource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ITaggableResource.cs new file mode 100644 index 0000000000000..abb4ba72f9a0d --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ITaggableResource.cs @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// Interface for operations that allow manipulating resource tags. + /// + /// The typed operations for a specific resource. + public interface ITaggableResource + where TOperations : ResourceOperationsBase + { + /// + /// Add a tag to the resource + /// + /// The tag key. + /// The tag value. + /// An that allows the user to control polling and waiting for Tag completion. + ArmOperation StartAddTag(string key, string value); + + /// + /// Add a tag to the resource + /// + /// The tag key. + /// The tag value. + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A that performs the Tag operation. The Task yields an an + /// that allows the user to control polling and waiting for + /// Tag completion. + Task> StartAddTagAsync( + string key, + string value, + CancellationToken cancellationToken = default); + + /// + /// Set the resource tags. + /// + /// The resource tags. + /// The status of the delete operation. + ArmResponse SetTags(IDictionary tags); + + /// + /// Set the resource tags. + /// + /// The resource tags. + /// A token allowing immediate cancellation of any blocking call performed during the deletion. + /// A that on completion returns the status of the delete operation. + Task> SetTagsAsync( + IDictionary tags, + CancellationToken cancellationToken = default); + + /// + /// Set the resource tags. + /// + /// The resource tags. + /// An that allows the user to control polling and waiting for Tag completion. + ArmOperation StartSetTags(IDictionary tags); + + /// + /// Set the resource tags. + /// + /// The resource tags. + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A that performs the Tag operation. The Task yields an an + /// that allows the user to control polling and waiting for + /// Tag completion. + Task> StartSetTagsAsync( + IDictionary tags, + CancellationToken cancellationToken = default); + + /// + /// Remove the resource tag. + /// + /// The tag key. + /// The status of the delete operation. + ArmResponse RemoveTag(string key); + + /// + /// Remove the resource tag. + /// + /// The tag key. + /// A token allowing immediate cancellation of any blocking call performed during the deletion. + /// A that on completion returns the status of the delete operation. + Task> RemoveTagAsync( + string key, + CancellationToken cancellationToken = default); + + /// + /// Remove the resource tag. + /// + /// The tag key. + /// An that allows the user to control polling and waiting for Tag completion. + ArmOperation StartRemoveTag(string key); + + /// + /// Remove the resource tag. + /// + /// The tag key. + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A that performs the Tag operation. The Task yields an an + /// that allows the user to control polling and waiting for + /// Tag completion. + Task> StartRemoveTagAsync( + string key, + CancellationToken cancellationToken = default); + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/OperationsBase.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/OperationsBase.cs new file mode 100644 index 0000000000000..526cd64b344bd --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/OperationsBase.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Azure.Core; +using Azure.ResourceManager.Resources; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing the operations that can be performed over a specific resource. + /// + public abstract class OperationsBase + { + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + /// A credential used to authenticate to an Azure Service. + /// The base URI of the service. + protected OperationsBase(AzureResourceManagerClientOptions options, ResourceIdentifier id, TokenCredential credential, Uri baseUri) + { + ClientOptions = options; + Id = id; + Credential = credential; + BaseUri = baseUri; + + Validate(id); + } + + /// + /// Gets the resource identifier. + /// + public virtual ResourceIdentifier Id { get; } + + /// + /// Gets the Azure Resource Manager client options. + /// + public virtual AzureResourceManagerClientOptions ClientOptions { get; } + + /// + /// Gets the Azure credential. + /// + public TokenCredential Credential { get; } + + /// + /// Gets the base URI of the service. + /// + public Uri BaseUri { get; } + + /// + /// Gets the valid Azure resource type for the current operations. + /// + /// A valid Azure resource type. + protected abstract ResourceType ValidResourceType { get; } + + /// + /// Gets the resource client. + /// + protected ResourcesManagementClient ResourcesClient => new ResourcesManagementClient(BaseUri, Id.Subscription, Credential); + + /// + /// Validate the resource identifier against current operations. + /// + /// The resource identifier. + protected virtual void Validate(ResourceIdentifier identifier) + { + if (identifier?.Type != ValidResourceType) + throw new InvalidOperationException($"Invalid resource type {identifier?.Type} expected {ValidResourceType}"); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/PhArmOperation.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/PhArmOperation.cs new file mode 100644 index 0000000000000..2a90185a65540 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/PhArmOperation.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// A calss representing an arm operation wrapper object. + /// + /// The to convert TModel into. + /// The model returned by existing Operation methods. + public class PhArmOperation : ArmOperation + where TOperations : class + where TModel : class + { + private readonly Func _converter; + private readonly Response _syncWrapped; + private readonly Operation _wrapped; + + /// + /// Initializes a new instance of the class. + /// + /// The results to wrap. + /// The function used to convert from existing type to new type. + public PhArmOperation(Operation wrapped, Func converter) + : base(null) + { + _wrapped = wrapped; + _converter = converter; + } + + /// + /// Initializes a new instance of the class. + /// + /// The results to wrap. + /// The function used to convert from existing type to new type. + public PhArmOperation(Response wrapped, Func converter) + : base(converter(wrapped.Value)) + { + _converter = converter; + _syncWrapped = wrapped; + } + + /// + public override string Id => _wrapped?.Id; + + /// + public override TOperations Value => CompletedSynchronously ? SyncValue : _converter(_wrapped.Value); + + /// + public override bool HasCompleted => CompletedSynchronously || _wrapped.HasCompleted; + + /// + public override bool HasValue => CompletedSynchronously || _wrapped.HasValue; + + /// + public override Response GetRawResponse() + { + return CompletedSynchronously ? _syncWrapped.GetRawResponse() : _wrapped.GetRawResponse(); + } + + /// + public override Response UpdateStatus(CancellationToken cancellationToken = default) + { + return CompletedSynchronously ? _syncWrapped.GetRawResponse() : _wrapped.UpdateStatus(cancellationToken); + } + + /// + public override ValueTask UpdateStatusAsync(CancellationToken cancellationToken = default) + { + return CompletedSynchronously + ? new ValueTask(_syncWrapped.GetRawResponse()) + : _wrapped.UpdateStatusAsync(cancellationToken); + } + + /// + public override async ValueTask> WaitForCompletionAsync( + CancellationToken cancellationToken = default) + { + return CompletedSynchronously + ? new PhArmResponse(_syncWrapped, _converter) + : new PhArmResponse( + await _wrapped.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + _converter); + } + + /// + public override async ValueTask> WaitForCompletionAsync( + TimeSpan pollingInterval, + CancellationToken cancellationToken) + { + return CompletedSynchronously + ? new PhArmResponse(_syncWrapped, _converter) + : new PhArmResponse( + await _wrapped.WaitForCompletionAsync(pollingInterval, cancellationToken).ConfigureAwait(false), + _converter); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/ResourceGroupData.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/ResourceGroupData.cs new file mode 100644 index 0000000000000..a92983a05677e --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/ResourceGroupData.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using Azure.ResourceManager.Resources.Models; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing the ResourceGroup data model. + /// + public class ResourceGroupData : TrackedResource + { + /// + /// Initializes a new instance of the class. + /// + /// The existing resource group model to copy from. + public ResourceGroupData(ResourceManager.Resources.Models.ResourceGroup rg) + : base(rg.Id, rg.Location, rg) + { + if (rg.Tags == null) + { + rg.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + } + } + + /// + public override IDictionary Tags => Model.Tags; + + /// + public override string Name => Model.Name; + + /// + /// Gets or sets the resource group properties + /// + public ResourceGroupProperties Properties + { + get => Model.Properties; + set => Model.Properties = value; + } + + /// + /// Gets or sets who this resource group is managed by + /// + public string ManagedBy + { + get => Model.ManagedBy; + set => Model.ManagedBy = value; + } + + /// + /// Converts from a into the ResourceManager.Resources.Models.ResourceGroup. + /// + /// The tracked resource convert from. + public static implicit operator ResourceManager.Resources.Models.ResourceGroup(ResourceGroupData other) + { + other.Model.Tags.Clear(); + foreach (var tag in other.Tags) + { + other.Model.Tags.Add(tag); + } + + return other.Model; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/SubscriptionData.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/SubscriptionData.cs new file mode 100644 index 0000000000000..be3b12feeb188 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/SubscriptionData.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.ResourceManager.Resources.Models; +using System.Collections.Generic; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing the subscription data model. + /// + public class SubscriptionData : Resource + { + /// + /// Initializes a new instance of the class. + /// + /// The subscription model. + public SubscriptionData(ResourceManager.Resources.Models.Subscription subscription) + { + Name = subscription.DisplayName; + SubscriptionGuid = subscription.SubscriptionId; + DisplayName = subscription.DisplayName; + State = subscription.State; + SubscriptionPolicies = subscription.SubscriptionPolicies; + AuthorizationSource = subscription.AuthorizationSource; + Id = subscription.Id; + ManagedByTenants = subscription.ManagedByTenants; + Tags = subscription.Tags; + } + + /// + /// Gets the subscription id. + /// + public override string Name { get; } + + /// + /// Gets the Id of the Subscription. + /// + public string SubscriptionGuid { get; } + + /// + /// Gets the display name of the subscription. + /// + public string DisplayName { get; } + + /// + /// Gets the state of the subscription. + /// + public SubscriptionState? State { get; } + + /// + /// Gets the policies of the subscription. + /// + public SubscriptionPolicies SubscriptionPolicies { get; } + + /// + /// Gets the authorization source of the subscription. + /// + public string AuthorizationSource { get; } + + /// + public override ResourceIdentifier Id { get; protected set; } + + /// + /// Gets an array containing the tenants managing the subscription. + /// + public IReadOnlyList ManagedByTenants { get; } + + /// + /// Gets the tags attached to the subscription. + /// + public IReadOnlyDictionary Tags { get; } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Properties/AssemblyInfo.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000000..9b296c5a8dbe4 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Azure.ResourceManager.Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d15ddcb29688295338af4b7686603fe614abd555e09efba8fb88ee09e1f7b1ccaeed2e8f823fa9eef3fdd60217fc012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593daa7b11b4")] diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceContainerBase.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceContainerBase.cs new file mode 100644 index 0000000000000..97ca5518a14bc --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceContainerBase.cs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing collection of resources and their operations over their parent. + /// + /// The type of the class containing operations for the underlying resource. + /// The type of the class containing properties for the underlying resource. + public abstract class ResourceContainerBase : ContainerBase + where TOperations : ResourceOperationsBase + where TResource : Resource + { + private static readonly object _parentLock = new object(); + private object _parentResource; + + /// + /// Initializes a new instance of the class. + /// + /// The resource representing the parent resource. + protected ResourceContainerBase(ResourceOperationsBase parent) + : base(parent) + { + } + + /// + /// Verify that the input resource Id is a valid container for this type. + /// + /// The input resource Id to check. + /// Resource identifier is not a valid type for this container. + protected override void Validate(ResourceIdentifier identifier) + { + if (identifier.Type != ValidResourceType) + throw new InvalidOperationException($"{identifier.Type} is not a valid container for {Id.Type}"); + } + + /// + /// The operation to create or update a resource. Please note some properties can be set only during creation. + /// + /// The name of the resource. + /// The desired resource configuration. + /// A response with the operation for this resource. + public abstract ArmResponse CreateOrUpdate( + string name, + TResource resourceDetails); + + /// + /// The operation to create or update a resource. Please note some properties can be set only during creation. + /// + /// The name of the resource. + /// The desired resource configuration. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public abstract Task> CreateOrUpdateAsync( + string name, + TResource resourceDetails, + CancellationToken cancellationToken = default); + + /// + /// The operation to create or update a resource. Please note some properties can be set only during creation. + /// + /// The name of the resource. + /// The desired resource configuration. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An that allows polling for completion of the operation. + /// + /// Details on long running operation object. + /// + public abstract ArmOperation StartCreateOrUpdate( + string name, + TResource resourceDetails, + CancellationToken cancellationToken = default); + + /// + /// The operation to create or update a resource. Please note some properties can be set only during creation. + /// + /// The name of the resource. + /// The desired resource configuration. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns an that allows polling for completion of the operation. + /// + /// Details on long running operation object. + /// + public abstract Task> StartCreateOrUpdateAsync( + string name, + TResource resourceDetails, + CancellationToken cancellationToken = default); + + /// + /// Gets the location of the parent object. + /// + /// The type of the parents full resource object. + /// The type of the parents operations object. + /// The associated with the parent object. + protected TParent GetParentResource() + where TParent : TParentOperations + where TParentOperations : ResourceOperationsBase + { + if (_parentResource is null) + { + lock (_parentLock) + { + if (_parentResource is null) + { + _parentResource = Parent as TParent; + if (_parentResource is null) + { + _parentResource = (Parent as TParentOperations).Get().Value; + } + } + } + } + + return _parentResource as TParent; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroup.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroup.cs new file mode 100644 index 0000000000000..805ca60f1b27f --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroup.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing a ResourceGroup along with the instance operations that can be performed on it. + /// + public class ResourceGroup : ResourceGroupOperations + { + /// + /// Initializes a new instance of the class. + /// + /// The operations to copy the client options from. + /// The ResourceGroupData to use in these operations. + internal ResourceGroup(ResourceOperationsBase operations, ResourceGroupData resource) + : base(operations, resource.Id) + { + Data = resource; + } + + /// + /// Gets the data representing this ResourceGroup. + /// + public ResourceGroupData Data { get; } + + /// + protected override ResourceGroup GetResource() + { + return this; + } + + /// + protected override Task GetResourceAsync() + { + return Task.FromResult(this); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupContainer.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupContainer.cs new file mode 100644 index 0000000000000..eb0347c081e7e --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupContainer.cs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Resources; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing collection of ResourceGroupContainer and their operations over a ResourceGroup. + /// + public class ResourceGroupContainer : ResourceContainerBase + { + /// + /// Initializes a new instance of the class. + /// + /// The parent subscription. + internal ResourceGroupContainer(SubscriptionOperations subscription) + : base(subscription) + { + } + + /// + protected override ResourceType ValidResourceType => SubscriptionOperations.ResourceType; + + private ResourceGroupsOperations Operations => new ResourcesManagementClient( + BaseUri, + Id.Subscription, + Credential, + ClientOptions.Convert()).ResourceGroups; + + /// + /// Constructs an object used to create a resource group. + /// + /// The location of the resource group. + /// The tags of the resource group. + /// Who the resource group is managed by. + /// A builder with and . + public ArmBuilder Construct(LocationData location, IDictionary tags = default, string managedBy = default) + { + var model = new ResourceManager.Resources.Models.ResourceGroup(location); + model.Tags = tags; + model.ManagedBy = managedBy; + return new ArmBuilder(this, new ResourceGroupData(model)); + } + + /// + public override ArmResponse CreateOrUpdate(string name, ResourceGroupData resourceDetails) + { + var response = Operations.CreateOrUpdate(name, resourceDetails); + return new PhArmResponse( + response, + g => new ResourceGroup(Parent, new ResourceGroupData(g))); + } + + /// + public override async Task> CreateOrUpdateAsync(string name, ResourceGroupData resourceDetails, CancellationToken cancellationToken = default) + { + var response = await Operations.CreateOrUpdateAsync(name, resourceDetails, cancellationToken).ConfigureAwait(false); + return new PhArmResponse( + response, + g => new ResourceGroup(Parent, new ResourceGroupData(g))); + } + + /// + public override ArmOperation StartCreateOrUpdate(string name, ResourceGroupData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + Operations.CreateOrUpdate(name, resourceDetails, cancellationToken), + g => new ResourceGroup(Parent, new ResourceGroupData(g))); + } + + /// + public override async Task> StartCreateOrUpdateAsync(string name, ResourceGroupData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + await Operations.CreateOrUpdateAsync(name, resourceDetails, cancellationToken).ConfigureAwait(false), + g => new ResourceGroup(Parent, new ResourceGroupData(g))); + } + + /// + /// List the resource groups for this subscription. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of resource operations that may take multiple service requests to iterate over. + public Pageable List(CancellationToken cancellationToken = default) + { + return new PhWrappingPageable( + Operations.List(null, null, cancellationToken), + s => new ResourceGroup(Parent, new ResourceGroupData(s))); + } + + /// + /// List the resource groups for this subscription. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of resource operations that may take multiple service requests to iterate over. + public AsyncPageable ListAsync(CancellationToken cancellationToken = default) + { + return new PhWrappingAsyncPageable( + Operations.ListAsync(null, null, cancellationToken), + s => new ResourceGroup(Parent, new ResourceGroupData(s))); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupOperations.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupOperations.cs new file mode 100644 index 0000000000000..1988c3db5fe19 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupOperations.cs @@ -0,0 +1,406 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Core.Pipeline; +using Azure.ResourceManager.Resources; +using Azure.ResourceManager.Resources.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing the operations that can be performed over a specific ResourceGroup. + /// + public class ResourceGroupOperations : ResourceOperationsBase, + ITaggableResource, IDeletableResource + { + /// + /// Gets the resource type definition for a ResourceType. + /// + public static readonly ResourceType ResourceType = "Microsoft.Resources/resourceGroups"; + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The name of the resource group to use. + internal ResourceGroupOperations(SubscriptionOperations options, string rgName) + : base(options, $"{options.Id}/resourceGroups/{rgName}") + { + if (rgName.Length > 90) + throw new ArgumentOutOfRangeException($"{nameof(rgName)} cannot be longer than 90 characters."); + + if (!ValidationPattern.IsMatch(rgName)) + throw new ArgumentException("The name of the resource group can include alphanumeric, underscore, parentheses, hyphen, period (except at end), and Unicode characters that match the allowed characters."); + } + + private static readonly Regex ValidationPattern = new Regex(@"^[-\w\._\(\)]+$"); + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + protected ResourceGroupOperations(ResourceOperationsBase options, ResourceIdentifier id) + : base(options, id) + { + } + + /// + protected override ResourceType ValidResourceType => ResourceType; + + private ResourceGroupsOperations Operations => new ResourcesManagementClient( + BaseUri, + Id.Subscription, + Credential, + ClientOptions.Convert()).ResourceGroups; + + /// + /// When you delete a resource group, all of its resources are also deleted. Deleting a resource group deletes all of its template deployments and currently stored operations. + /// + /// A response with the operation for this resource. + public ArmResponse Delete() + { + return new ArmResponse(Operations.StartDelete(Id.Name).WaitForCompletionAsync().EnsureCompleted()); + } + + /// + /// When you delete a resource group, all of its resources are also deleted. Deleting a resource group deletes all of its template deployments and currently stored operations. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public async Task> DeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmResponse(await Operations.StartDelete(Id.Name, cancellationToken).WaitForCompletionAsync(cancellationToken).ConfigureAwait(false)); + } + + /// + /// When you delete a resource group, all of its resources are also deleted. Deleting a resource group deletes all of its template deployments and currently stored operations. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A response with the operation for this resource. + /// + /// Details on long running operation object. + /// + public ArmOperation StartDelete(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(Operations.StartDelete(Id.Name, cancellationToken)); + } + + /// + /// When you delete a resource group, all of its resources are also deleted. Deleting a resource group deletes all of its template deployments and currently stored operations. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + /// + /// Details on long running operation object. + /// + public async Task> StartDeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(await Operations.StartDeleteAsync(Id.Name, cancellationToken).ConfigureAwait(false)); + } + + /// + public override ArmResponse Get() + { + return new PhArmResponse(Operations.Get(Id.Name), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + public override async Task> GetAsync(CancellationToken cancellationToken = default) + { + return new PhArmResponse(await Operations.GetAsync(Id.Name, cancellationToken).ConfigureAwait(false), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + /// Add a tag to a ResourceGroup. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// A response with the operation for this resource. + /// + /// Details on long running operation object. + /// + public ArmOperation StartAddTag(string name, string value) + { + var resource = GetResource(); + var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; + if (object.ReferenceEquals(patch.Tags, null)) + { + patch.Tags = new Dictionary(); + } + + patch.Tags[name] = value; + return new PhArmOperation(Operations.Update(Id.Name, patch), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + /// Add a tag to a ResourceGroup. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// /// A that on completion returns a response with the operation for this resource. + /// + /// Details on long running operation object. + /// + public async Task> StartAddTagAsync(string name, string value, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; + if (object.ReferenceEquals(patch.Tags, null)) + { + patch.Tags = new Dictionary(); + } + + patch.Tags[name] = value; + return new PhArmOperation(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + /// Create a resource with a ResourceGroupOperations. + /// + /// A string representing the name of the resource />. + /// The model representing the object to create. />. + /// A Location of where to to host the resource. />. + /// The type of the class containing the container for the specific resource. + /// The type of the operations class for a specific resource. + /// The type of the class containing properties for the underlying resource. + /// Returns a response with the operation for this resource. + public ArmResponse CreateResource(string name, TResource model, LocationData location = default) + where TResource : TrackedResource + where TOperations : ResourceOperationsBase + where TContainer : ResourceContainerBase + { + var myResource = model as TrackedResource; + + if (myResource == null) + { + myResource = new GenericResourceData(Id); + } + + if (location != null) + { + myResource = new GenericResourceData(Id, location); + } + + TContainer container = Activator.CreateInstance(typeof(TContainer), ClientOptions, myResource) as TContainer; + + return container.CreateOrUpdate(name, model); + } + + /// + /// Create a resource with a ResourceGroupOperations. + /// + /// A string representing the name of the resource />. + /// The model representing the object to create. />. + /// A Location of where to to host the resource. />. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// The type of the class containing the container for the specific resource. + /// The type of the operations class for a specific resource. + /// The type of the class containing properties for the underlying resource. + /// A that on completion returns a response with the operation for this resource. + public Task> CreateResourceAsync(string name, TResource model, LocationData location = default, CancellationToken cancellationToken = default) + where TResource : TrackedResource + where TOperations : ResourceOperationsBase + where TContainer : ResourceContainerBase + { + var myResource = model as TrackedResource; + + if (myResource == null) + { + myResource = new GenericResourceData(Id); + } + + if (location != null) + { + myResource = new GenericResourceData(Id, location); + } + + TContainer container = Activator.CreateInstance(typeof(TContainer), ClientOptions, myResource) as TContainer; + + return container.CreateOrUpdateAsync(name, model, cancellationToken); + } + + /// + public ArmResponse SetTags(IDictionary tags) + { + var resource = GetResource(); + var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; + if (object.ReferenceEquals(patch.Tags, null)) + { + patch.Tags = new Dictionary(); + } + + ReplaceTags(tags, patch.Tags); + return new PhArmResponse(Operations.Update(Id.Name, patch), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; + if (object.ReferenceEquals(patch.Tags, null)) + { + patch.Tags = new Dictionary(); + } + + ReplaceTags(tags, patch.Tags); + return new PhArmResponse(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + public ArmOperation StartSetTags(IDictionary tags) + { + var resource = GetResource(); + var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; + if (object.ReferenceEquals(patch.Tags, null)) + { + patch.Tags = new Dictionary(); + } + + ReplaceTags(tags, patch.Tags); + return new PhArmOperation(Operations.Update(Id.Name, patch), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; + if (object.ReferenceEquals(patch.Tags, null)) + { + patch.Tags = new Dictionary(); + } + + ReplaceTags(tags, patch.Tags); + return new PhArmOperation(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + public ArmResponse RemoveTag(string key) + { + var resource = GetResource(); + var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; + if (object.ReferenceEquals(patch.Tags, null)) + { + patch.Tags = new Dictionary(); + } + + DeleteTag(key, patch.Tags); + return new PhArmResponse(Operations.Update(Id.Name, patch), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; + if (object.ReferenceEquals(patch.Tags, null)) + { + patch.Tags = new Dictionary(); + } + + DeleteTag(key, patch.Tags); + return new PhArmResponse(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + public ArmOperation StartRemoveTag(string key) + { + var resource = GetResource(); + var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; + if (object.ReferenceEquals(patch.Tags, null)) + { + patch.Tags = new Dictionary(); + } + + DeleteTag(key, patch.Tags); + return new PhArmOperation(Operations.Update(Id.Name, patch), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; + if (object.ReferenceEquals(patch.Tags, null)) + { + patch.Tags = new Dictionary(); + } + + DeleteTag(key, patch.Tags); + return new PhArmOperation(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + /// Lists all available geo-locations. + /// + /// A collection of location that may take multiple service requests to iterate over. + public IEnumerable ListAvailableLocations() + { + var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); + var rgProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var rgResource = rgProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return rgResource.Locations.Select(l => (LocationData)l); + } + + /// + /// Lists all available geo-locations. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of location that may take multiple service requests to iterate over. + /// The default subscription id is null. + public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) + { + var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); + var rgProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase), cancellationToken).ConfigureAwait(false); + var rgResource = rgProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return rgResource.Locations.Select(l => (LocationData)l); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceListOperations.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceListOperations.cs new file mode 100644 index 0000000000000..469af5edd69b6 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceListOperations.cs @@ -0,0 +1,202 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Core.Resources; +using Azure.ResourceManager.Resources; +using Azure.ResourceManager.Resources.Models; +using System; +using System.Globalization; +using System.Reflection; +using System.Threading; + +namespace Azure.ResourceManager.Core +{ + /// + /// Generic list operations class. This can be extended if a specific RP has more list operations. + /// + public static class ResourceListOperations + { + /// + /// List resources under the a resource context + /// + /// The instance to use for the list. + /// Optional filters for results. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of resource operations that may take multiple service requests to iterate over. + public static Pageable ListAtContext( + ResourceGroupOperations resourceGroup, + ResourceFilterCollection resourceFilters = null, + int? top = null, + CancellationToken cancellationToken = default) + { + return ListAtContextInternal( + resourceGroup, + resourceGroup.Id.Name, + resourceFilters, + top, + cancellationToken); + } + + /// + /// List resources under the a resource context + /// + /// The instance to use for the list. + /// Optional filters for results. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of resource operations that may take multiple service requests to iterate over. + public static AsyncPageable ListAtContextAsync( + ResourceGroupOperations resourceGroup, + ResourceFilterCollection resourceFilters = null, + int? top = null, + CancellationToken cancellationToken = default) + { + return ListAtContextInternalAsync( + resourceGroup, + resourceGroup.Id.Name, + resourceFilters, + top, + cancellationToken); + } + + /// + /// List resources under a subscription + /// + /// The instance to use for the list. + /// Optional filters for results. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of resource operations that may take multiple service requests to iterate over. + public static Pageable ListAtContext( + SubscriptionOperations subscription, + ResourceFilterCollection resourceFilters = null, + int? top = null, + CancellationToken cancellationToken = default) + { + return ListAtContextInternal( + subscription, + null, + resourceFilters, + top, + cancellationToken); + } + + /// + /// List resources under the a resource context + /// + /// The instance to use for the list. + /// Optional filters for results. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of resource operations that may take multiple service requests to iterate over. + public static AsyncPageable ListAtContextAsync( + SubscriptionOperations subscription, + ResourceFilterCollection resourceFilters = null, + int? top = null, + CancellationToken cancellationToken = default) + { + return ListAtContextInternalAsync( + subscription, + null, + resourceFilters, + top, + cancellationToken); + } + + private static ResourcesManagementClient GetResourcesClient(ResourceOperationsBase resourceOperations) + { + return new ResourcesManagementClient(resourceOperations.BaseUri, resourceOperations.Id.Subscription, resourceOperations.Credential); + } + + private static AsyncPageable ListAtContextInternalAsync( + ResourceOperationsBase resourceOperations, + string scopeFilter, + ResourceFilterCollection resourceFilters = null, + int? top = null, + CancellationToken cancellationToken = default) + { + var armOperations = GetResourcesClient(resourceOperations).Resources; + AsyncPageable result; + if (scopeFilter == null) + { + result = armOperations.ListAsync(resourceFilters?.ToString(), null, top, cancellationToken); + } + else + { + result = armOperations.ListByResourceGroupAsync( + scopeFilter, + resourceFilters?.ToString(), + null, + top, + cancellationToken); + } + + return ConvertResultsAsync(result, resourceOperations); + } + + private static Pageable ListAtContextInternal( + ResourceOperationsBase resourceOperations, + string scopeFilter = null, + ResourceFilterCollection resourceFilters = null, + int? top = null, + CancellationToken cancellationToken = default) + { + var armOperations = GetResourcesClient(resourceOperations).Resources; + Pageable result; + if (scopeFilter == null) + { + result = armOperations.List(resourceFilters?.ToString(), null, top, cancellationToken); + } + else + { + result = armOperations.ListByResourceGroup( + scopeFilter, + resourceFilters?.ToString(), + null, + top, + cancellationToken); + } + + return ConvertResults(result, resourceOperations); + } + + private static Pageable ConvertResults( + Pageable result, + ResourceOperationsBase resourceOperations) + { + return new PhWrappingPageable( + result, + CreateResourceConverter(resourceOperations)); + } + + private static AsyncPageable ConvertResultsAsync( + AsyncPageable result, + ResourceOperationsBase resourceOperations) + { + return new PhWrappingAsyncPageable( + result, + CreateResourceConverter(resourceOperations)); + } + + private static Func CreateResourceConverter(ResourceOperationsBase resourceOperations) + { + return s => + { + var args = new object[] + { + resourceOperations, + Activator.CreateInstance(typeof(GenericResourceData), s as ResourceManager.Resources.Models.GenericResource) as GenericResourceData, + }; + + return Activator.CreateInstance( + typeof(GenericResource), + BindingFlags.Instance | BindingFlags.NonPublic, + null, + args, + CultureInfo.InvariantCulture) as GenericResource; + }; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceOperationsBase.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceOperationsBase.cs new file mode 100644 index 0000000000000..7cae4c30b7636 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceOperationsBase.cs @@ -0,0 +1,164 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Core; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing the operations that can be performed over a specific resource. + /// + public abstract class ResourceOperationsBase : OperationsBase + { + /// + /// Initializes a new instance of the class. + /// + /// The operations representing the resource. + protected ResourceOperationsBase(ResourceOperationsBase operations) + : base(operations.ClientOptions, operations.Id, operations.Credential, operations.BaseUri) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The operations to copy options from. + /// The resource that is the target of operations. + protected ResourceOperationsBase(ResourceOperationsBase options, ResourceIdentifier resourceId) + : base(options.ClientOptions, resourceId, options.Credential, options.BaseUri) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + /// A credential used to authenticate to an Azure Service. + /// The base URI of the service. + protected ResourceOperationsBase(AzureResourceManagerClientOptions options, ResourceIdentifier resourceId, TokenCredential credential, Uri baseUri) + : base(options, resourceId, credential, baseUri) + { + } + } + + /// + /// Base class for all operations over a resource + /// + /// The type implementing operations over the resource. + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Types differ by type argument only")] + public abstract class ResourceOperationsBase : ResourceOperationsBase + where TOperations : ResourceOperationsBase + { + /// + /// Initializes a new instance of the class. + /// + /// Generic ARMResourceOperations for this resource type. + protected ResourceOperationsBase(GenericResourceOperations genericOperations) + : base(genericOperations) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The resource representing the parent resource. + /// The identifier of the resource that is the target of operations. + protected ResourceOperationsBase(ResourceOperationsBase parentOperations, ResourceIdentifier id) + : base(parentOperations, id) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + /// A credential used to authenticate to an Azure Service. + /// The base URI of the service. + protected ResourceOperationsBase(AzureResourceManagerClientOptions options, ResourceIdentifier resourceId, TokenCredential credential, Uri baseUri) + : base(options, resourceId, credential, baseUri) + { + } + + /// + /// Gets details for this resource from the service. + /// + /// A response with the operation for this resource. + public abstract ArmResponse Get(); + + /// + /// Gets details for this resource from the service. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public abstract Task> GetAsync(CancellationToken cancellationToken = default); + + /// + /// Get details for this resource from the service or can be overriden to provide a cached instance. + /// + /// A operation for this resource. + protected virtual TOperations GetResource() + { + return Get().Value; + } + + /// + /// Get details for this resource from the service or can be overriden to provide a cached instance. + /// + /// A that on completion returns a operation for this resource. + protected virtual async Task GetResourceAsync() + { + return (await GetAsync().ConfigureAwait(false)).Value; + } + + /// + /// Gets new dictionary of tags after adding the key value pair or updating the existing key value pair + /// + /// The key to update. + /// The value to update. + /// Existing tag dictionary to update. + protected void UpdateTags(string key, string value, IDictionary existingTags) + { + if (existingTags.ContainsKey(key)) + { + existingTags[key] = value; + } + else + { + existingTags.Add(key, value); + } + } + + /// + /// Gets new dictionary of tags after remove the one key value pair + /// + /// The key to remove. + /// Existing tag dictionary to update. + protected void DeleteTag(string key, IDictionary existingTags) + { + if (existingTags.ContainsKey(key)) + { + existingTags.Remove(key); + } + } + + /// + /// Replace all the tags currently on the resource + /// + /// List of tags. + /// Existing tag dictionary to update. + protected void ReplaceTags(IDictionary tags, IDictionary existingTags) + { + existingTags.Clear(); + foreach (var tag in tags) + { + existingTags.Add(tag); + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/GenericResourceFilter.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/GenericResourceFilter.cs new file mode 100644 index 0000000000000..7868aed6f4f01 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/GenericResourceFilter.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace Azure.ResourceManager.Core.Resources +{ + /// + /// Syntactic sugar for creating ARM filters + /// + public abstract class GenericResourceFilter + { + /// + /// Gets the filter as a string. + /// + /// The string representation of the filter. + public abstract string GetFilterString(); + + /// + public override string ToString() + { + return GetFilterString(); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/KnownKeys.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/KnownKeys.cs new file mode 100644 index 0000000000000..0494380d72f49 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/KnownKeys.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Azure.ResourceManager.Core +{ + /// + /// Helper class with the known keys in a dictionary for easy access. + /// + internal static class KnownKeys + { + /// + /// Gets the key for Subscription. + /// + public static string Subscription => "subscriptions"; + + /// + /// Gets the key for Tenant. + /// + public static string Tenant => "tenants"; + + /// + /// Gets the key for Resource Group. + /// + public static string ResourceGroup => "resourcegroups"; + + /// + /// Gets the key for Location. + /// + public static string Location => "locations"; + + /// + /// Gets the key for Provider Namespace. + /// + public static string ProviderNamespace => "providers"; + + /// + /// Gets the key for Untracked Subresource. + /// + public static string UntrackedSubResource => Guid.NewGuid().ToString(); + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/LocationContainer.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/LocationContainer.cs new file mode 100644 index 0000000000000..a87b0120127c2 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/LocationContainer.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Resources; +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// Represents an Azure geography region where supported resource providers live. + /// + public class LocationContainer : OperationsBase + { + /// + /// Initializes a new instance of the class. + /// + /// The subscription that this location container belongs to. + internal LocationContainer(SubscriptionOperations subscriptionOperations) + : base(subscriptionOperations.ClientOptions, subscriptionOperations.Id, subscriptionOperations.Credential, subscriptionOperations.BaseUri) + { + } + + /// + protected override ResourceType ValidResourceType => SubscriptionOperations.ResourceType; + + /// + /// Gets the subscription client. + /// + private SubscriptionsOperations SubscriptionsClient => ResourcesClient.Subscriptions; + + /// + /// Gets the Azure subscriptions. + /// + /// Subscription container. + public SubscriptionContainer GetSubscriptionContainer() + { + return new SubscriptionContainer(ClientOptions, Credential, BaseUri); + } + + /// + /// Lists all geo-locations. + /// + /// A collection of location data that may take multiple service requests to iterate over. + public Pageable List() + { + return new PhWrappingPageable(SubscriptionsClient.ListLocations(Id.Subscription), s => s.DisplayName); + } + + /// + /// Lists all geo-locations. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of location data that may take multiple service requests to iterate over. + public AsyncPageable ListAsync(CancellationToken token = default(CancellationToken)) + { + return new PhWrappingAsyncPageable(SubscriptionsClient.ListLocationsAsync(Id.Subscription, token), s => s.DisplayName); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/LocationData.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/LocationData.cs new file mode 100644 index 0000000000000..db55f0eef8d20 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/LocationData.cs @@ -0,0 +1,460 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace Azure.ResourceManager.Core +{ + /// + /// Represents an Azure geography region where supported resource providers live. + /// + public class LocationData : IEquatable, IComparable + { + #region Public Cloud Locations + + /// + /// Public cloud location for East Asia. + /// + public static readonly LocationData EastAsia = new LocationData { Name = "eastasia", CanonicalName = "east-asia", DisplayName = "East Asia" }; + + /// + /// Public cloud location for Southeast Asia. + /// + public static readonly LocationData SoutheastAsia = new LocationData { Name = "southeastasia", CanonicalName = "southeast-asia", DisplayName = "Southeast Asia" }; + + /// + /// Public cloud location for Central US. + /// + public static readonly LocationData CentralUS = new LocationData { Name = "centralus", CanonicalName = "central-us", DisplayName = "Central US" }; + + /// + /// Public cloud location for East US. + /// + public static readonly LocationData EastUS = new LocationData { Name = "eastus", CanonicalName = "east-us", DisplayName = "East US" }; + + /// + /// Public cloud location for East US 2. + /// + public static readonly LocationData EastUS2 = new LocationData { Name = "eastus2", CanonicalName = "east-us-2", DisplayName = "East US 2" }; + + /// + /// Public cloud location for West US. + /// + public static readonly LocationData WestUS = new LocationData { Name = "westus", CanonicalName = "west-us", DisplayName = "West US" }; + + /// + /// Public cloud location for North Central US. + /// + public static readonly LocationData NorthCentralUS = new LocationData { Name = "northcentralus", CanonicalName = "north-central-us", DisplayName = "North Central US" }; + + /// + /// Public cloud location for South Central US. + /// + public static readonly LocationData SouthCentralUS = new LocationData { Name = "southcentralus", CanonicalName = "south-central-us", DisplayName = "South Central US" }; + + /// + /// Public cloud location for North Europe. + /// + public static readonly LocationData NorthEurope = new LocationData { Name = "northeurope", CanonicalName = "north-europe", DisplayName = "North Europe" }; + + /// + /// Public cloud location for West Europe. + /// + public static readonly LocationData WestEurope = new LocationData { Name = "westeurope", CanonicalName = "west-europe", DisplayName = "West Europe" }; + + /// + /// Public cloud location for Japan West. + /// + public static readonly LocationData JapanWest = new LocationData { Name = "japanwest", CanonicalName = "japan-west", DisplayName = "Japan West" }; + + /// + /// Public cloud location for Japan East. + /// + public static readonly LocationData JapanEast = new LocationData { Name = "japaneast", CanonicalName = "japan-east", DisplayName = "Japan East" }; + + /// + /// Public cloud location for Brazil South. + /// + public static readonly LocationData BrazilSouth = new LocationData { Name = "brazilsouth", CanonicalName = "brazil-south", DisplayName = "Brazil South" }; + + /// + /// Public cloud location for Australia East. + /// + public static readonly LocationData AustraliaEast = new LocationData { Name = "australiaeast", CanonicalName = "australia-east", DisplayName = "Australia East" }; + + /// + /// Public cloud location for Australia Southeast. + /// + public static readonly LocationData AustraliaSoutheast = new LocationData { Name = "australiasoutheast", CanonicalName = "australia-southeast", DisplayName = "Australia Southeast" }; + + /// + /// Public cloud location for South India. + /// + public static readonly LocationData SouthIndia = new LocationData { Name = "southindia", CanonicalName = "south-india", DisplayName = "South India" }; + + /// + /// Public cloud location for Central India. + /// + public static readonly LocationData CentralIndia = new LocationData { Name = "centralindia", CanonicalName = "central-india", DisplayName = "Central India" }; + + /// + /// Public cloud location for West India. + /// + public static readonly LocationData WestIndia = new LocationData { Name = "westindia", CanonicalName = "west-india", DisplayName = "West India" }; + + /// + /// Public cloud location for Canada Central. + /// + public static readonly LocationData CanadaCentral = new LocationData { Name = "canadacentral", CanonicalName = "canada-central", DisplayName = "Canada Central" }; + + /// + /// Public cloud location for Canada East. + /// + public static readonly LocationData CanadaEast = new LocationData { Name = "canadaeast", CanonicalName = "canada-east", DisplayName = "Canada East" }; + + /// + /// Public cloud location for UK South. + /// + public static readonly LocationData UKSouth = new LocationData { Name = "uksouth", CanonicalName = "uk-south", DisplayName = "UK South" }; + + /// + /// Public cloud location for UK West. + /// + public static readonly LocationData UKWest = new LocationData { Name = "ukwest", CanonicalName = "uk-west", DisplayName = "UK West" }; + + /// + /// Public cloud location for West Central US. + /// + public static readonly LocationData WestCentralUS = new LocationData { Name = "westcentralus", CanonicalName = "west-central-us", DisplayName = "West Central US" }; + + /// + /// Public cloud location for West US 2. + /// + public static readonly LocationData WestUS2 = new LocationData { Name = "westus2", CanonicalName = "west-us-2", DisplayName = "West US 2" }; + + /// + /// Public cloud location for Korea Central. + /// + public static readonly LocationData KoreaCentral = new LocationData { Name = "koreacentral", CanonicalName = "korea-central", DisplayName = "Korea Central" }; + + /// + /// Public cloud location for Korea South. + /// + public static readonly LocationData KoreaSouth = new LocationData { Name = "koreasouth", CanonicalName = "korea-south", DisplayName = "Korea South" }; + + /// + /// Public cloud location for France Central. + /// + public static readonly LocationData FranceCentral = new LocationData { Name = "francecentral", CanonicalName = "france-central", DisplayName = "France Central" }; + + /// + /// Public cloud location for France South. + /// + public static readonly LocationData FranceSouth = new LocationData { Name = "francesouth", CanonicalName = "france-south", DisplayName = "France South" }; + + /// + /// Public cloud location for Australia Central. + /// + public static readonly LocationData AustraliaCentral = new LocationData { Name = "australiacentral", CanonicalName = "australia-central", DisplayName = "Australia Central" }; + + /// + /// Public cloud location for Australia Central 2. + /// + public static readonly LocationData AustraliaCentral2 = new LocationData { Name = "australiacentral2", CanonicalName = "australia-central-2", DisplayName = "Australia Central 2" }; + + /// + /// Public cloud location for UAE Central. + /// + public static readonly LocationData UAECentral = new LocationData { Name = "uaecentral", CanonicalName = "uae-central", DisplayName = "UAE Central" }; + + /// + /// Public cloud location for UAE North. + /// + public static readonly LocationData UAENorth = new LocationData { Name = "uaenorth", CanonicalName = "uae-north", DisplayName = "UAE North" }; + + /// + /// Public cloud location for South Africa North. + /// + public static readonly LocationData SouthAfricaNorth = new LocationData { Name = "southafricanorth", CanonicalName = "south-africa-north", DisplayName = "South Africa North" }; + + /// + /// Public cloud location for South Africa West. + /// + public static readonly LocationData SouthAfricaWest = new LocationData { Name = "southafricawest", CanonicalName = "south-africa-west", DisplayName = "South Africa West" }; + + /// + /// Public cloud location for Switzerland North. + /// + public static readonly LocationData SwitzerlandNorth = new LocationData { Name = "switzerlandnorth", CanonicalName = "switzerland-north", DisplayName = "Switzerland North" }; + + /// + /// Public cloud location for Switzerland West. + /// + public static readonly LocationData SwitzerlandWest = new LocationData { Name = "switzerlandwest", CanonicalName = "switzerland-west", DisplayName = "Switzerland West" }; + + /// + /// Public cloud location for Germany North. + /// + public static readonly LocationData GermanyNorth = new LocationData { Name = "germanynorth", CanonicalName = "germany-north", DisplayName = "Germany North" }; + + /// + /// Public cloud location for Germany West Central. + /// + public static readonly LocationData GermanyWestCentral = new LocationData { Name = "germanywestcentral", CanonicalName = "germany-west-central", DisplayName = "Germany West Central" }; + + /// + /// Public cloud location for Norway West. + /// + public static readonly LocationData NorwayWest = new LocationData { Name = "norwaywest", CanonicalName = "norway-west", DisplayName = "Norway West" }; + + /// + /// Public cloud location for Brazil Southeast. + /// + public static readonly LocationData BrazilSoutheast = new LocationData { Name = "brazilsoutheast", CanonicalName = "brazil-southeast", DisplayName = "Brazil Southeast" }; + + #endregion + private static readonly Dictionary PublicCloudLocations = new Dictionary() + { + { "EASTASIA", EastAsia }, + { "SOUTHEASTASIA", SoutheastAsia }, + { "CENTRALUS", CentralUS }, + { "EASTUS", EastUS }, + { "EASTUS2", EastUS2 }, + { "WESTUS", WestUS }, + { "NORTHCENTRALUS", NorthCentralUS }, + { "SOUTHCENTRALUS", SouthCentralUS }, + { "NORTHEUROPE", NorthEurope }, + { "WESTEUROPE", WestEurope }, + { "JAPANWEST", JapanWest }, + { "JAPANEAST", JapanEast }, + { "BRAZILSOUTH", BrazilSouth }, + { "AUSTRALIAEAST", AustraliaEast }, + { "AUSTRALIASOUTHEAST", AustraliaSoutheast }, + { "SOUTHINDIA", SouthIndia }, + { "CENTRALINDIA", CentralIndia }, + { "WESTINDIA", WestIndia }, + { "CANADACENTRAL", CanadaCentral }, + { "CANADAEAST", CanadaEast }, + { "UKSOUTH", UKSouth }, + { "UKWEST", UKWest }, + { "WESTCENTRALUS", WestCentralUS }, + { "WESTUS2", WestUS2 }, + { "KOREACENTRAL", KoreaCentral }, + { "KOREASOUTH", KoreaSouth }, + { "FRANCECENTRAL", FranceCentral }, + { "FRANCESOUTH", FranceSouth }, + { "AUSTRALIACENTRAL", AustraliaCentral }, + { "AUSTRALIACENTRAL2", AustraliaCentral2 }, + { "UAECENTRAL", UAECentral }, + { "UAENORTH", UAENorth }, + { "SOUTHAFRICANORTH", SouthAfricaNorth }, + { "SOUTHAFRICAWEST", SouthAfricaWest }, + { "SWITZERLANDNORTH", SwitzerlandNorth }, + { "SWITZERLANDWEST", SwitzerlandWest }, + { "GERMANYNORTH", GermanyNorth }, + { "GERMANYWESTCENTRAL", GermanyWestCentral }, + { "NORWAYWEST", NorwayWest }, + { "BRAZILSOUTHEAST", BrazilSoutheast }, + }; + + private const string CanonicalPattern = "^[a-z]+(-[a-z]+)+(-[1-9])?$"; + private const string DisplayPattern = "^[A-Z]+[a-z]*( [A-Z]+[a-z]*)+( [1-9])?$"; + private const string RegexDash = @"-"; + private const string RegexWhitespace = @" "; + + private LocationData() + { + } + + private LocationData(string location) + { + switch (DetectNameType(location)) + { + case NameType.Name: + Name = location; + CanonicalName = location; + DisplayName = location; + break; + case NameType.CanonicalName: + Name = GetDefaultNameFromCanonicalName(location); + CanonicalName = location; + DisplayName = GetDisplayNameFromCanonicalName(location); + break; + case NameType.DisplayName: + Name = GetDefaultNameFromDisplayName(location); + CanonicalName = GetCanonicalNameFromDisplayName(location); + DisplayName = location; + break; + } + } + + private enum NameType + { + Name, + CanonicalName, + DisplayName, + } + + /// + /// Gets default Location object: West US. + /// + public static ref readonly LocationData Default => ref WestUS; + + /// + /// Gets a location name consisting of only lowercase characters without white spaces or any separation character between words, e.g. "westus". + /// + public string Name { get; private set; } + + /// + /// Gets a location canonical name consisting of only lowercase chararters with a '-' between words, e.g. "west-us". + /// + public string CanonicalName { get; private set; } + + /// + /// Gets a location display name consisting of titlecase words or alphanumeric characters separated by whitespaces, e.g. "West US" + /// + public string DisplayName { get; private set; } + + /// + /// Creates a new location implicitly from a string. + /// + /// String to be assigned in the Name, CanonicalName or DisplayName form. + public static implicit operator LocationData(string other) + { + if (other == null) + return null; + + var normalizedName = NormalizationUtility(other); + LocationData value; + if (PublicCloudLocations.TryGetValue(normalizedName, out value)) + { + return value; + } + + return new LocationData(other); + } + + /// + /// Creates a string implicitly from a Location object. + /// + /// Location object to be assigned. + public static implicit operator string(LocationData other) + { + if (other == null) + { + return null; + } + + return other.ToString(); + } + + /// + /// Detects if a location object is equal to another location instance or a string representing the location name. + /// + /// Location object or name as a string. + /// True or false. + public bool Equals(LocationData other) + { + if (other == null) + return false; + + return Name == other.Name && CanonicalName == other.CanonicalName && DisplayName == other.DisplayName; + } + + /// + /// Gets the display name of a location object. + /// + /// Display name. + public override string ToString() + { + return DisplayName; + } + + /// + /// Compares this Location name to another Location to expose if it is greater, less or equal than this one. + /// + /// Location object or name as a string. + /// -1 for less than, 0 for equals, 1 for greater than. + public int CompareTo(LocationData other) + { + if (ReferenceEquals(other, null)) + { + return 1; + } + + return string.Compare(Name, other.Name, StringComparison.InvariantCulture); + } + + private static string NormalizationUtility(string value) + { + if (string.IsNullOrEmpty(value)) + { + return value; + } + + var sb = new StringBuilder(value.Length); + for (var index = 0; index < value.Length; ++index) + { + var c = value[index]; + if (char.IsLetterOrDigit(c)) + { + sb.Append(c); + } + } + + return sb.ToString().ToUpperInvariant(); + } + + private static NameType DetectNameType(string location) + { + if (Regex.IsMatch(location, CanonicalPattern)) + { + return NameType.CanonicalName; + } + else if (Regex.IsMatch(location, DisplayPattern)) + { + return NameType.DisplayName; + } + else + { + return NameType.Name; + } + } + + private static string GetCanonicalNameFromDisplayName(string name) + { + return name.Replace(RegexWhitespace, RegexDash).ToLower(CultureInfo.InvariantCulture); + } + + private static string GetDisplayNameFromCanonicalName(string name) + { + char[] chName = name.ToCharArray(); + chName[0] = char.ToUpper(chName[0], CultureInfo.InvariantCulture); + + for (int i = 0; i < chName.Length - 1; i++) + { + if (chName[i] == '-') + { + chName[i] = ' '; + chName[i + 1] = char.ToUpper(chName[i + 1], CultureInfo.InvariantCulture); + } + } + + return new string(chName); + } + + private static string GetDefaultNameFromCanonicalName(string name) + { + return name.Replace(RegexDash, string.Empty); + } + + private static string GetDefaultNameFromDisplayName(string name) + { + return name.Replace(RegexWhitespace, string.Empty).ToLower(CultureInfo.InvariantCulture); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Plan.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Plan.cs new file mode 100644 index 0000000000000..6f2186f245b63 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Plan.cs @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Azure.ResourceManager.Core +{ + /// + /// Representation of a publisher plan for marketplace RPs. + /// + public sealed class Plan : IEquatable, IComparable + { + /// + /// Initializes a new instance of the class. + /// + /// Plan's Name. + /// Plan's Publisher. + /// Plan's Product. + /// Plan's Promotion Code. + /// Plan's Version. + internal Plan(string name, string publisher, string product, string promotionCode, string version) + { + Name = name; + Publisher = publisher; + Product = product; + PromotionCode = promotionCode; + Version = version; + } + + /// + /// Initializes a new instance of the class. + /// + /// The plan to copy from. + internal Plan(ResourceManager.Resources.Models.Plan plan) + : this(plan.Name, plan.Publisher, plan.Product, plan.PromotionCode, plan.Version) + { + } + + /// + /// Gets the plan's Name. + /// + public string Name { get; private set; } + + /// + /// Gets the plan's Publisher. + /// + public string Publisher { get; private set; } + + /// + /// Gets the plan's product. + /// + public string Product { get; private set; } + + /// + /// Gets the plan's Promotion Code. + /// + public string PromotionCode { get; private set; } + + /// + /// Gets the plan's version. + /// + public string Version { get; private set; } + + /// + /// Compares this with another instance. + /// + /// object to compare. + /// -1 for less than, 0 for equals, 1 for greater than. + public int CompareTo(Plan other) + { + if (other == null) + return 1; + + if (ReferenceEquals(this, other)) + return 0; + + int compareResult = 0; + if ((compareResult = string.Compare(Name, other.Name, StringComparison.InvariantCultureIgnoreCase)) == 0 && + (compareResult = string.Compare(Product, other.Product, StringComparison.InvariantCultureIgnoreCase)) == 0 && + (compareResult = string.Compare(PromotionCode, other.PromotionCode, StringComparison.InvariantCultureIgnoreCase)) == 0 && + (compareResult = string.Compare(Publisher, other.Publisher, StringComparison.InvariantCultureIgnoreCase)) == 0 && + (compareResult = string.Compare(Version, other.Version, StringComparison.InvariantCultureIgnoreCase)) == 0) + { + return 0; + } + + return compareResult; + } + + /// + /// Compares this instance with another object and determines if they are equals. + /// + /// object to compare. + /// True if they are equals, otherwise false. + public bool Equals(Plan other) + { + if (other == null) + return false; + + if (ReferenceEquals(this, other)) + return true; + + return string.Equals(Name, other.Name, StringComparison.InvariantCultureIgnoreCase) && + string.Equals(Product, other.Product, StringComparison.InvariantCultureIgnoreCase) && + string.Equals(PromotionCode, other.PromotionCode, StringComparison.InvariantCultureIgnoreCase) && + string.Equals(Publisher, other.Publisher, StringComparison.InvariantCultureIgnoreCase) && + string.Equals(Version, other.Version, StringComparison.InvariantCultureIgnoreCase); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Resource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Resource.cs new file mode 100644 index 0000000000000..86a35ac05cf77 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Resource.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing the base resource used by all azure resources. + /// + public abstract class Resource : IEquatable, IEquatable, IComparable, + IComparable + { + /// + /// Gets or sets the resource identifier. + /// + public abstract ResourceIdentifier Id { get; protected set; } + + /// + /// Gets the name. + /// + public virtual string Name => Id?.Name; + + /// + /// Gets the resource type. + /// + public virtual ResourceType Type => Id?.Type; + + /// + public virtual int CompareTo(Resource other) + { + if (other == null) + return 1; + + if (ReferenceEquals(this, other)) + return 0; + + int compareResult = 0; + if ((compareResult = string.Compare(Id, other.Id, StringComparison.InvariantCultureIgnoreCase)) == 0 && + (compareResult = string.Compare(Name, other.Name, StringComparison.InvariantCultureIgnoreCase)) == 0 && + (compareResult = Type.CompareTo(other.Type)) == 0) + return 0; + + return compareResult; + } + + /// + public virtual int CompareTo(string other) + { + return string.Compare(Id?.Id, other, StringComparison.InvariantCultureIgnoreCase); + } + + /// + public virtual bool Equals(Resource other) + { + if (Id == null) + return false; + + return Id.Equals(other?.Id); + } + + /// + public virtual bool Equals(string other) + { + if (Id == null) + return false; + + return Id.Equals(other); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceFilterCollection.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceFilterCollection.cs new file mode 100644 index 0000000000000..3580836b2a567 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceFilterCollection.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; + +namespace Azure.ResourceManager.Core.Resources +{ + /// + /// A class representing a collection of arm filters. + /// + public sealed class ResourceFilterCollection + { + /// + /// Initializes a new instance of the class. + /// + public ResourceFilterCollection() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The resource type to filter by. + public ResourceFilterCollection(ResourceType type) + { + ResourceTypeFilter = new ResourceTypeFilter(type); + } + + /// + /// Gets or sets the substring filter to use in the collection. + /// + public ResourceNameFilter SubstringFilter { get; set; } + + /// + /// Gets the resource type filter to use in the collection. + /// + public ResourceTypeFilter ResourceTypeFilter { get; } + + /// + /// Gets or sets the tag filter to use in the collection. + /// + public ResourceTagFilter TagFilter { get; set; } + + /// + public override string ToString() + { + var builder = new List(); + + var substring = ResourceTypeFilter?.GetFilterString(); + if (!string.IsNullOrWhiteSpace(substring)) + { + builder.Add(substring); + } + + substring = SubstringFilter?.GetFilterString(); + if (!string.IsNullOrWhiteSpace(substring)) + { + builder.Add(substring); + } + + substring = TagFilter?.GetFilterString(); + if (!string.IsNullOrWhiteSpace(substring)) + { + builder.Add(substring); + } + + return $"{string.Join(" and ", builder)}"; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentifier.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentifier.cs new file mode 100644 index 0000000000000..692e861638f79 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentifier.cs @@ -0,0 +1,291 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Azure.ResourceManager.Core +{ + /// + /// Canonical Representation of a Resource Identity + /// + public sealed class ResourceIdentifier : + IEquatable, + IEquatable, + IComparable, + IComparable + { + private readonly IDictionary _partsDictionary = + new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Initializes a new instance of the class. + /// + /// The identifier of the resource that is the target of operations. + public ResourceIdentifier(string id) + { + Id = id; + Parse(id); + } + + /// + /// Gets the Resource ID. + /// + public string Id { get; private set; } + + /// + /// Gets the Resource Name. + /// + public string Name { get; private set; } + + /// + /// Gets the Resource Type. + /// + public ResourceType Type { get; private set; } + + /// + /// Gets the Subscription. + /// + public string Subscription => _partsDictionary.ContainsKey(KnownKeys.Subscription) + ? _partsDictionary[KnownKeys.Subscription] + : null; + + /// + /// Gets the Resource Group. + /// + public string ResourceGroup => _partsDictionary.ContainsKey(KnownKeys.ResourceGroup) + ? _partsDictionary[KnownKeys.ResourceGroup] + : null; + + /// + /// Gets the Parent. + /// Currently this will contain the identifier for either the parent resource, the resource group, the location, the subscription, or the tenant that is the logical parent of this resource. + /// + public ResourceIdentifier Parent { get; private set; } + + /// + /// Initializes a new instance of the class from a string. + /// + /// String to be implicit converted into a object. + public static implicit operator ResourceIdentifier(string other) + { + return new ResourceIdentifier(other); // will null check in PR #119 + } + + /// + /// Creates a new string from a object. + /// + /// object to be implicit converted into an string. + public static implicit operator string(ResourceIdentifier other) + { + return other.Id; + } + + /// + /// Allow static, safe comparisons of resource identifier strings or objects. + /// + /// A resource id. + /// Another resource id. + /// True if the resource ids are equivalent, otherwise False. + public static bool Equals(ResourceIdentifier x, ResourceIdentifier y) + { + if (null == x && null == y) + return true; + + if (null == x || null == y) + return false; + + return x.Equals(y); + } + + /// + /// Allow static null-safe comparisons between resource identifier strings or objects. + /// + /// A resource id. + /// Another resource id. + /// -1 if x < y, 0 if x == y, 1 if x > y. + public static int CompareTo(ResourceIdentifier x, ResourceIdentifier y) + { + if (null == x && null == y) + return 0; + + if (null == x) + return -1; + + if (null == y) + return 1; + + return x.CompareTo(y); + } + + /// + public override int GetHashCode() + { + return Id.GetHashCode(); + } + + /// + public override string ToString() + { + return Id; + } + + /// + /// Compares this instance ID with another instance's ID. + /// + /// object to compare. + /// -1 for less than, 0 for equals, 1 for greater than. + public int CompareTo(ResourceIdentifier other) + { + return string.Compare( + Id?.ToLowerInvariant(), + other?.Id?.ToLowerInvariant(), + StringComparison.InvariantCultureIgnoreCase); + } + + /// + /// Compares this instance ID with another plain text ID. + /// + /// The ID to compare. + /// -1 for less than, 0 for equals, 1 for greater than. + public int CompareTo(string other) + { + return string.Compare( + Id?.ToLowerInvariant(), + other?.ToLowerInvariant(), + StringComparison.InvariantCultureIgnoreCase); + } + + /// + /// Compares this instance ID with another instance's ID and determines if they are equals. + /// + /// object to compare. + /// True if they are equals, otherwise false. + public bool Equals(ResourceIdentifier other) + { + return string.Equals( + Id?.ToLowerInvariant(), + other?.Id?.ToLowerInvariant(), + StringComparison.InvariantCultureIgnoreCase); + } + + /// + /// Compares this instance ID with another plain text ID. and determines if they are equals. + /// + /// The ID to compare. + /// True if they are equals, otherwise false. + public bool Equals(string other) + { + return string.Equals( + Id?.ToLowerInvariant(), + other?.ToLowerInvariant(), + StringComparison.InvariantCultureIgnoreCase); + } + + /// + /// Populate Resource Identity fields from input string. + /// + /// A properly formed resource identity. + private void Parse(string id) + { + // Throw for null, empty, and string without the correct form + if (string.IsNullOrWhiteSpace(id) || !id.Contains('/')) + throw new ArgumentOutOfRangeException($"'{id}' is not a valid resource"); + + // Resource ID paths consist mainly of name/value pairs. Split the uri so we have an array of name/value pairs + var parts = id.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + + // There must be at least one name/value pair for the resource id to be valid + if (parts.Count < 2) + throw new ArgumentOutOfRangeException($"'{id}' is not a valid resource"); + + // This is asserting that resources must start with '/subscriptions', /tenants, or /locations. + // TODO: we will need to update this code to accomodate tenant based resources (which start with /providers) + if (!(KnownKeys.Subscription.Equals(parts[0], StringComparison.InvariantCultureIgnoreCase) || + KnownKeys.Tenant.Equals(parts[0], StringComparison.InvariantCultureIgnoreCase) || + KnownKeys.Location.Equals(parts[0], StringComparison.InvariantCultureIgnoreCase))) + { + throw new ArgumentOutOfRangeException($"'{id}' is not a valid resource"); + } + + Type = new ResourceType(id); + + // In the case that this resource is a singleton proxy resource, the number of parts will be odd, + // where the last part is the type name of the singleton + if (parts.Count % 2 != 0) + { + _partsDictionary.Add(KnownKeys.UntrackedSubResource, parts.Last()); + parts.RemoveAt(parts.Count - 1); + } + + // This spplits into resource that come from a provider (which have the providers keyword) and + // resources that are built in to ARM (e.g. /subscriptions/{sub}, /subscriptions/{sub}/resourceGroups/{rg}) + // TODO: This code will need to be updates for extension resources, which have two providers + if (id.ToLowerInvariant().Contains("providers")) + { + ParseProviderResource(parts); + } + else + { + ParseGenericResource(parts); + } + } + + /// + /// Helper method to parse a built in resource. + /// + /// Resource ID paths consisting of name/value pairs. + private void ParseGenericResource(IList parts) + { + Debug.Assert(parts != null, "Parts parameter is null."); + Debug.Assert(parts.Count > 1, "Parts should be a list containing more than 1 elements."); + + // The resource consists of well-known name-value pairs. Make a resource dictionary + // using the names as keys, and the values as values + for (var i = 0; i < parts.Count - 1; i += 2) + { + _partsDictionary.Add(parts[i], parts[i + 1]); + } + + // resource name is always the last part + Name = parts.Last(); + parts.RemoveAt(parts.Count - 1); + parts.RemoveAt(parts.Count - 1); + + // remove the last key/value pair to arrive at the parent (Count will be zero for /subscriptions/{foo}) + Parent = parts.Count > 1 ? new ResourceIdentifier($"/{string.Join("/", parts)}") : null; + } + + /// + /// Helper method to parse a resource that comes from a provider. + /// + /// Resource ID paths consisting of name/value pairs. + private void ParseProviderResource(IList parts) + { + // The resource consists of name/value pairs, make a dictionary out of it + for (var i = 0; i < parts.Count - 1; i += 2) + { + _partsDictionary[parts[i]] = parts[i + 1]; + } + + Name = parts.Last(); + parts.RemoveAt(parts.Count - 1); + + // remove the type name (there will be no typename if this is a singleton sub resource) + if (parts.Count % 2 == 1) + parts.RemoveAt(parts.Count - 1); + + // If this is a top-level resource, remove the providers/Namespace pair, otherwise continue + if (parts.Count > 2 && string.Equals(parts[parts.Count - 2], KnownKeys.ProviderNamespace)) + { + parts.RemoveAt(parts.Count - 1); + parts.RemoveAt(parts.Count - 1); + } + + // If this is not a top-level resource, it will have a parent + Parent = parts.Count > 1 ? new ResourceIdentifier($"/{string.Join("/", parts)}") : null; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentity.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentity.cs new file mode 100644 index 0000000000000..ecba2a9905c86 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentity.cs @@ -0,0 +1,236 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Core; +using System; +using System.Collections.Generic; +using System.Text.Json; + +namespace Azure.ResourceManager.Core +{ + /// + /// Represents a managed identity + /// + public class ResourceIdentity : IEquatable + { + private const string SystemAssigned = "SystemAssigned"; + private const string UserAssigned = "UserAssigned"; + private const string SystemAndUserAssigned = "SystemAssigned, UserAssigned"; + + /// + /// Initializes a new instance of the class. + /// + public ResourceIdentity() + : this(null, false) + { + } // not system or user + + /// + /// Initializes a new instance of the class. + /// + /// Dictionary with a key and a object value. + /// Flag for using or not. + public ResourceIdentity(Dictionary user, bool useSystemAssigned) + { + // check for combination of user and system on the impact to type value + SystemAssignedIdentity = useSystemAssigned ? new SystemAssignedIdentity() : null; + UserAssignedIdentities = new Dictionary(); + if (user != null) + { + foreach (KeyValuePair id in user) + { + UserAssignedIdentities.Add(id.Key, id.Value); + } + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The to use. + /// Dictionary with a key and a object value. + public ResourceIdentity(SystemAssignedIdentity systemAssigned, IDictionary user) + { + // TODO: remove this constructor later + SystemAssignedIdentity = systemAssigned; + if (user == null) + { + UserAssignedIdentities = new Dictionary(); + } + else + { + UserAssignedIdentities = user; + } + } + + /// + /// Gets the SystemAssignedIdentity. + /// + public SystemAssignedIdentity SystemAssignedIdentity { get; private set; } + + /// + /// Gets a dictionary of the User Assigned Identities. + /// + public IDictionary UserAssignedIdentities { get; private set; } + + /// + /// Converts a into an object. + /// + /// A containing an . + /// New Identity object with JSON values. + internal static ResourceIdentity Deserialize(JsonElement element) + { + if (element.ValueKind == JsonValueKind.Undefined) + { + throw new ArgumentException("JsonElement cannot be undefined ", nameof(element)); + } + + Optional systemAssignedIdentity = default; + IDictionary userAssignedIdentities = new Dictionary(); + string type = string.Empty; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("userAssignedIdentities")) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + userAssignedIdentities = null; + continue; + } + + string resourceId = string.Empty; + foreach (var keyValuePair in property.Value.EnumerateObject()) + { + resourceId = keyValuePair.Name; + var userAssignedIdentity = UserAssignedIdentity.Deserialize(keyValuePair.Value); + userAssignedIdentities.Add(resourceId, userAssignedIdentity); + } + + continue; + } + + if (property.NameEquals("type")) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + throw new InvalidOperationException("The type property had a JsonValueKind equal to Null"); + } + + type = property.Value.GetString(); + } + + if (type.Equals(SystemAssigned)) + { + systemAssignedIdentity = SystemAssignedIdentity.Deserialize(element); + continue; + } + + if (type.Equals(SystemAndUserAssigned)) + { + systemAssignedIdentity = SystemAssignedIdentity.Deserialize(element); + continue; + } + } + + return new ResourceIdentity(systemAssignedIdentity, userAssignedIdentities); + } + + /// + /// Converts an object into a . + /// + /// Utf8JsonWriter object to which the output is going to be written. + /// Identity object to be converted. + internal static void Serialize(Utf8JsonWriter writer, ResourceIdentity identity) + { + if (writer == null) + throw new ArgumentNullException(nameof(writer)); + + if (identity == null) + throw new ArgumentNullException(nameof(identity)); + + writer.WriteStartObject(); + writer.WritePropertyName("identity"); + + if (identity.SystemAssignedIdentity == null && identity.UserAssignedIdentities.Count == 0) + { + writer.WriteStringValue("null"); + writer.WriteEndObject(); + writer.Flush(); + return; + } + + writer.WriteStartObject(); + if (identity.SystemAssignedIdentity != null && identity.UserAssignedIdentities.Count != 0) + { + SystemAssignedIdentity.Serialize(writer, identity.SystemAssignedIdentity); + writer.WritePropertyName("kind"); + writer.WriteStringValue(SystemAndUserAssigned); + writer.WritePropertyName("userAssignedIdentities"); + writer.WriteStartObject(); + foreach (var keyValuePair in identity.UserAssignedIdentities) + { + writer.WritePropertyName(keyValuePair.Key); + UserAssignedIdentity.Serialize(writer, keyValuePair.Value); + } + + writer.WriteEndObject(); + } + else if (identity.SystemAssignedIdentity != null) + { + SystemAssignedIdentity.Serialize(writer, identity.SystemAssignedIdentity); + writer.WritePropertyName("kind"); + writer.WriteStringValue(SystemAssigned); + } + else if (identity.UserAssignedIdentities.Count != 0) + { + writer.WritePropertyName("kind"); + writer.WriteStringValue(UserAssigned); + writer.WritePropertyName("userAssignedIdentities"); + writer.WriteStartObject(); + foreach (var keyValuePair in identity.UserAssignedIdentities) + { + writer.WritePropertyName(keyValuePair.Key); + UserAssignedIdentity.Serialize(writer, keyValuePair.Value); + } + + writer.WriteEndObject(); + } + + writer.WriteEndObject(); + writer.WriteEndObject(); + writer.Flush(); + } + + /// + /// Detects if this Identity is equals to another Identity instance. + /// + /// Identity object to compare. + /// True if they are equal, otherwise False. + public bool Equals(ResourceIdentity other) + { + if (other == null) + return false; + + if (UserAssignedIdentities.Count == other.UserAssignedIdentities.Count) + { + foreach (var identity in UserAssignedIdentities) + { + UserAssignedIdentity value; + if (other.UserAssignedIdentities.TryGetValue(identity.Key, out value)) + { + if (!UserAssignedIdentity.Equals(identity.Value, value)) + { + return false; + } + } + else + { + return false; + } + } + } + + return SystemAssignedIdentity.Equals(SystemAssignedIdentity, other.SystemAssignedIdentity); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceNameFilter.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceNameFilter.cs new file mode 100644 index 0000000000000..6f8fe88f4584b --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceNameFilter.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace Azure.ResourceManager.Core.Resources +{ + /// + /// A class representing a substring filter used in Azure API calls. + /// + public class ResourceNameFilter : GenericResourceFilter, IEquatable, IEquatable + { + /// + /// Gets or sets the name. + /// + public string Name { get; set; } + + /// + /// Gets or sets the resource group. + /// + public string ResourceGroup { get; set; } + + /// + /// Converts a string into an . + /// + /// The string that can be match in any part of the resource name. + public static implicit operator ResourceNameFilter(string nameString) + { + if (nameString is null) + return null; + + return new ResourceNameFilter { Name = nameString }; + } + + /// + public bool Equals(string other) + { + if (other is null) + return false; + + return string.Equals(other, Name); + } + + /// + public bool Equals(ResourceNameFilter other) + { + if (other is null) + return false; + + return string.Equals(other.Name, Name); + } + + /// + public override string GetFilterString() + { + var builder = new List(); + if (!string.IsNullOrWhiteSpace(Name)) + { + builder.Add($"substringof('{Name}', name)"); + } + + if (!string.IsNullOrWhiteSpace(ResourceGroup)) + { + builder.Add($"substringof('{ResourceGroup}', name)"); + } + + return string.Join(" and ", builder); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceTagFilter.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceTagFilter.cs new file mode 100644 index 0000000000000..c49c0433fec0b --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceTagFilter.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Azure.ResourceManager.Core.Resources +{ + /// + /// A class representing a tag filter used in Azure API calls. + /// + public class ResourceTagFilter : GenericResourceFilter, IEquatable + { + private readonly Tuple _tag; + + /// + /// Initializes a new instance of the class. + /// + /// The tag to filter by. + public ResourceTagFilter(Tuple tag) + { + if (tag?.Item1 is null || tag?.Item2 is null) + throw new ArgumentNullException(nameof(tag), "The tag, its key, and its value must not be null"); + + _tag = tag; + Key = _tag.Item1; + Value = _tag.Item2; + } + + /// + /// Initializes a new instance of the class. + /// + /// The key of the tag to filter by. + /// The value of the tag to filter by. + public ResourceTagFilter(string tagKey, string tagValue) + : this(new Tuple(tagKey, tagValue)) + { + } + + /// + /// Gets the key to filter by. + /// + public string Key { get; } + + /// + /// Gets the value to filter by. + /// + public string Value { get; } + + /// + public bool Equals(ResourceTagFilter other) + { + if (other is null) + return false; + + return string.Equals(other.Key, Key) && + string.Equals(other.Value, Value); + } + + /// + public override string GetFilterString() + { + return $"tagName eq '{_tag.Item1}' and tagValue eq '{_tag.Item2}'"; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceType.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceType.cs new file mode 100644 index 0000000000000..b4dc44088dfee --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceType.cs @@ -0,0 +1,325 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Azure.ResourceManager.Core +{ + /// + /// Structure respresenting a resource type + /// + public sealed class ResourceType : IEquatable, IEquatable, IComparable, + IComparable + { + /// + /// The "none" resource type + /// + public static readonly ResourceType None = new ResourceType { Namespace = string.Empty, Type = string.Empty }; + + /// + /// Initializes a new instance of the class. + /// + /// Option to provide the Resource Type directly, or a Resource ID from which the type is going to be obtained. + public ResourceType(string resourceIdOrType) + { + if (string.IsNullOrWhiteSpace(resourceIdOrType)) + throw new ArgumentException($"{nameof(resourceIdOrType)} cannot be null or whitespace", nameof(resourceIdOrType)); + + Parse(resourceIdOrType); + } + + private ResourceType() + { + } + + /// + /// Gets the resource type Namespace. + /// + public string Namespace { get; private set; } + + /// + /// Gets the resource Type. + /// + public string Type { get; private set; } + + /// + /// Gets the resource type Parent. + /// + public ResourceType Parent + { + get + { + var parts = Type.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + + if (parts.Length < 2) + return None; + + var list = new List(parts); + list.RemoveAt(list.Count - 1); + + return new ResourceType($"{Namespace}/{string.Join("/", list.ToArray())}"); + } + } + + /// + /// Implicit operator for initializing a instance from a string. + /// + /// String to be conferted into a object. + public static implicit operator ResourceType(string other) + { + if (other is null) + return null; + + return new ResourceType(other); + } + + /// + /// Compares a object with a . + /// + /// object. + /// String. + /// True if they are equal, otherwise False. + public static bool operator ==(ResourceType source, string target) + { + if (source is null) + return target is null; + + return source.Equals(target); + } + + /// + /// Compares a with a object. + /// + /// String representation of a ResourceType. + /// object. + /// True if they are equal, otherwise False. + public static bool operator ==(string source, ResourceType target) + { + if (target is null) + return source is null; + + return target.Equals(source); + } + + /// + /// Compares two objects. + /// + /// First object. + /// Second object. + /// True if they are equal, otherwise False. + public static bool operator ==(ResourceType source, ResourceType target) + { + if (source is null) + return target is null; + + return source.Equals(target); + } + + /// + /// Compares a object with a . + /// + /// object. + /// String representation of a ResourceType. + /// False if they are equal, otherwise True. + public static bool operator !=(ResourceType source, string target) + { + if (source is null) + return !(target is null); + + return !source.Equals(target); + } + + /// + /// Compares a with a object. + /// + /// String. + /// object. + /// False if they are equal, otherwise True. + public static bool operator !=(string source, ResourceType target) + { + if (target is null) + return !(source is null); + + return !target.Equals(source); + } + + /// + /// Compares two objects. + /// + /// First object. + /// Second object. + /// False if they are equal, otherwise True. + public static bool operator !=(ResourceType source, ResourceType target) + { + if (source is null) + return !(target is null); + + return !source.Equals(target); + } + + /// + /// Compares this with another instance. + /// + /// object to compare. + /// -1 for less than, 0 for equals, 1 for greater than. + public int CompareTo(ResourceType other) + { + if (other is null) + return 1; + + if (ReferenceEquals(this, other)) + return 0; + + int compareResult = 0; + if ((compareResult = string.Compare(Namespace, other.Namespace, StringComparison.InvariantCultureIgnoreCase)) == 0 && + (compareResult = string.Compare(Type, other.Type, StringComparison.InvariantCultureIgnoreCase)) == 0 && + (other.Parent != null)) + { + return Parent.CompareTo(other.Parent); + } + + return compareResult; + } + + /// + /// Compares this with a resource type representation as a string. + /// + /// String to compare. + /// -1 for less than, 0 for equals, 1 for greater than. + public int CompareTo(string other) + { + if (other is null) + return 1; + + return CompareTo(new ResourceType(other)); + } + + /// + /// Compares this instance with another object and determines if they are equals. + /// + /// object to compare. + /// True if they are equals, otherwise false. + public bool Equals(ResourceType other) + { + if (other is null) + return false; + + return string.Equals(ToString(), other.ToString(), StringComparison.InvariantCultureIgnoreCase); + } + + /// + /// Compares this instance with a string and determines if they are equals. + /// + /// String to compare. + /// True if they are equals, otherwise false. + public bool Equals(string other) + { + if (other is null) + return false; + + return string.Equals(ToString(), other, StringComparison.InvariantCultureIgnoreCase); + } + + /// + public override string ToString() + { + return $"{Namespace}/{Type}"; + } + + /// + public override bool Equals(object obj) + { + if (obj is null) + return false; + + var resourceObj = obj as ResourceType; + + if (resourceObj != null) + return Equals(resourceObj); + + var stringObj = obj as string; + + if (stringObj != null) + return Equals(stringObj); + + return base.Equals(obj); + } + + /// + public override int GetHashCode() + { + return ToString().GetHashCode(); + } + + /// + /// Helper method to determine if the given string is a Resource ID or a Type, + /// and then assign the proper values to the class properties. + /// + /// String to be parsed. + private void Parse(string resourceIdOrType) + { + // Note that this code will either parse a resource id to find the type, or a resource type + resourceIdOrType = resourceIdOrType.Trim('/'); + + // split the path into segments + var parts = resourceIdOrType.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + + // There must be at least a namespace and type name + if (parts.Count < 1) + throw new ArgumentOutOfRangeException(nameof(resourceIdOrType)); + + // if the type is just subscriptions, it is a built-in type in the Microsoft.Resources namespace + if (parts.Count == 1) + { + // Simple resource type + Type = parts[0]; + Namespace = "Microsoft.Resources"; + } + + // Handle resource identifiers from RPs (they have the /providers path segment) + if (parts.Contains(KnownKeys.ProviderNamespace)) + { + // it is a resource id from a provider + var index = parts.LastIndexOf(KnownKeys.ProviderNamespace); + for (var i = index; i >= 0; --i) + { + parts.RemoveAt(i); + } + + if (parts.Count < 3) + throw new ArgumentOutOfRangeException(nameof(resourceIdOrType), "Invalid resource id."); + + var type = new List(); + for (var i = 1; i < parts.Count; i += 2) + { + type.Add(parts[i]); + } + + Namespace = parts[0]; + Type = string.Join("/", type); + } + + // Handle resource types (Micsrsoft.Compute/virtualMachines, Microsoft.Network/virtualNetworks/subnets) + else if (parts[0].Contains('.')) + { + // it is a full type name + Namespace = parts[0]; + Type = string.Join("/", parts.Skip(Math.Max(0, 1)).Take(parts.Count - 1)); + } + + // Handle built-in resource ids (e.g. /subscriptions/{sub}, /subscriptions/{sub}/resourceGroups/{rg}) + else if (parts.Count % 2 == 0) + { + // primitive resource manager resource id + Namespace = "Microsoft.Resources"; + Type = parts[parts.Count - 2]; + } + else + { + throw new ArgumentOutOfRangeException(nameof(resourceIdOrType)); + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceTypeFilter.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceTypeFilter.cs new file mode 100644 index 0000000000000..099584c5d3991 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceTypeFilter.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Azure.ResourceManager.Core.Resources +{ + /// + /// A class representing a resource type filter used in Azure API calls. + /// + public class ResourceTypeFilter : GenericResourceFilter, IEquatable, IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// The resource type to filter by. + public ResourceTypeFilter(ResourceType resourceType) + { + ResourceType = resourceType; + } + + /// + /// Gets the resource type to filter by. + /// + public ResourceType ResourceType { get; } + + /// + public bool Equals(string other) + { + throw new NotImplementedException(); + } + + /// + public bool Equals(ResourceTypeFilter other) + { + throw new NotImplementedException(); + } + + /// + public override string GetFilterString() + { + return $"resourceType EQ '{ResourceType}'"; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Sku.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Sku.cs new file mode 100644 index 0000000000000..25008bfb6f51c --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Sku.cs @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Azure.ResourceManager.Resources.Models; + +namespace Azure.ResourceManager.Core +{ + /// + /// Representaion of ARM SKU + /// + public sealed class Sku : IEquatable, IComparable + { + /// + /// Initializes a new instance of the class. + /// + /// SKU's name. + /// SKU's tier. + /// SKU's family. + /// SKU's size. + /// SKU's capacity. + internal Sku(string name, string tier, string family, string size, long? capacity = null) + { + Name = name; + Tier = tier; + Family = family; + Size = size; + Capacity = capacity; + } + + /// + /// Initializes a new instance of the class. + /// + /// The sku to copy from. + internal Sku(ResourceManager.Resources.Models.Sku sku) + : this(sku.Name, sku.Tier, sku.Family, sku.Size, sku.Capacity) + { + } + + /// + /// Gets the Name. + /// + public string Name { get; private set; } + + /// + /// Gets the Tier. + /// + public string Tier { get; private set; } + + /// + /// Gets the Family. + /// + public string Family { get; private set; } + + /// + /// Gets the Size. + /// + public string Size { get; private set; } + + /// + /// Gets the Capacity. + /// + public long? Capacity { get; private set; } + + /// + /// Compares this with another instance. + /// + /// object to compare. + /// -1 for less than, 0 for equals, 1 for greater than. + public int CompareTo(Sku other) + { + if (other == null) + return 1; + + if (object.ReferenceEquals(this, other)) + return 0; + + int compareResult = 0; + if ((compareResult = string.Compare(Name, other.Name, StringComparison.InvariantCultureIgnoreCase)) == 0 && + (compareResult = string.Compare(Family, other.Family, StringComparison.InvariantCultureIgnoreCase)) == 0 && + (compareResult = string.Compare(Size, other.Size, StringComparison.InvariantCultureIgnoreCase)) == 0 && + (compareResult = string.Compare(Tier, other.Tier, StringComparison.InvariantCultureIgnoreCase)) == 0) + { + return Nullable.Compare(Capacity, other.Capacity); + } + + return compareResult; + } + + /// + /// Compares this instance with another object and determines if they are equals. + /// + /// object to compare. + /// True if they are equals, otherwise false. + public bool Equals(Sku other) + { + if (other == null) + return false; + + if (object.ReferenceEquals(this, other)) + return true; + + return string.Equals(Name, other.Name, StringComparison.InvariantCultureIgnoreCase) && + string.Equals(Family, other.Family, StringComparison.InvariantCultureIgnoreCase) && + string.Equals(Size, other.Size, StringComparison.InvariantCultureIgnoreCase) && + string.Equals(Tier, other.Tier, StringComparison.InvariantCultureIgnoreCase) && + long.Equals(Capacity, other.Capacity); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SystemAssignedIdentity.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SystemAssignedIdentity.cs new file mode 100644 index 0000000000000..e2f7dcbc73219 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SystemAssignedIdentity.cs @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.ResourceManager.Core +{ + using System; + using System.Text.Json; + using Azure.Core; + + /// + /// A class representing an Identity assigned by the system. + /// + public sealed class SystemAssignedIdentity + { + /// + /// Initializes a new instance of the class with Null properties. + /// + public SystemAssignedIdentity() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Application TenantId . + /// PrincipalId. + public SystemAssignedIdentity(Guid tenantId, Guid principalId) + { + TenantId = tenantId; + PrincipalId = principalId; + } + + /// + /// Gets the Tenant ID. + /// + public Guid? TenantId { get; private set; } + + /// + /// Gets the Principal ID. + /// + public Guid? PrincipalId { get; private set; } + + /// + /// Converts a into an object. + /// + /// A containing an identity. + /// New object with JSON values. + internal static SystemAssignedIdentity Deserialize(JsonElement element) + { + if (element.ValueKind == JsonValueKind.Undefined) + { + throw new ArgumentException("JsonElement cannot be undefined ", nameof(element)); + } + + Guid principalId = default; + Guid tenantId = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("principalId")) + { + if (property.Value.ValueKind != JsonValueKind.Null) + principalId = Guid.Parse(property.Value.GetString()); + } + + if (property.NameEquals("tenantId")) + { + if (property.Value.ValueKind != JsonValueKind.Null) + tenantId = Guid.Parse(property.Value.GetString()); + } + } + + if (principalId == default(Guid) && tenantId == default(Guid)) + return null; + + if (principalId == default(Guid) || tenantId == default(Guid)) + throw new InvalidOperationException("Either TenantId or PrincipalId were null"); + + return new SystemAssignedIdentity(tenantId, principalId); + } + + /// + /// Converts an object into a . + /// + /// Utf8JsonWriter object to which the output is going to be written. + /// object to be converted. + internal static void Serialize(Utf8JsonWriter writer, SystemAssignedIdentity systemAssignedIdentity) + { + if (systemAssignedIdentity == null) + throw new ArgumentNullException(nameof(systemAssignedIdentity)); + + if (writer == null) + throw new ArgumentNullException(nameof(writer)); + + writer.WritePropertyName("principalId"); + if (!Optional.IsDefined(systemAssignedIdentity.PrincipalId)) + { + writer.WriteStringValue("null"); + } + else + { + writer.WriteStringValue(systemAssignedIdentity.PrincipalId.ToString()); + } + + writer.WritePropertyName("tenantId"); + if (!Optional.IsDefined(systemAssignedIdentity.TenantId)) + { + writer.WriteStringValue("null"); + } + else + { + writer.WriteStringValue(systemAssignedIdentity.TenantId.ToString()); + } + + writer.Flush(); + } + + /// + /// Compares two objects to determine if they are equal. + /// + /// First object to compare. + /// Second object to compare. + /// True if they are equal, otherwise False. + public static bool Equals(SystemAssignedIdentity original, SystemAssignedIdentity other) + { + if (original == null) + return other == null; + + return original.Equals(other); + } + + /// + /// Compares this with another instance. + /// + /// object to compare. + /// -1 for less than, 0 for equals, 1 for greater than. + public int CompareTo(SystemAssignedIdentity other) + { + if (other == null) + return 1; + + int compareResult = 0; + if ((compareResult = TenantId.GetValueOrDefault().CompareTo(other.TenantId.GetValueOrDefault())) == 0 && + (compareResult = PrincipalId.GetValueOrDefault().CompareTo(other.PrincipalId.GetValueOrDefault())) == 0) + return 0; + + return compareResult; + } + + /// + /// Compares this instance with another object and determines if they are equals. + /// + /// object to compare. + /// True if they are equal, otherwise false. + public bool Equals(SystemAssignedIdentity other) + { + if (other == null) + return false; + + return TenantId.Equals(other.TenantId) && PrincipalId.Equals(other.PrincipalId); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/TrackedResource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/TrackedResource.cs new file mode 100644 index 0000000000000..29a7d114752b2 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/TrackedResource.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace Azure.ResourceManager.Core +{ + /// + /// Generic representation of a tracked resource. All tracked resources should extend this class + /// + public abstract class TrackedResource : Resource + { + /// + /// Gets the tags. + /// + public virtual IDictionary Tags => + new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + /// + /// Gets or sets the location the resource is in. + /// + public virtual LocationData Location { get; protected set; } + + /// + /// Gets or sets the identifier for the resource. + /// + public override ResourceIdentifier Id { get; protected set; } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/UserAssignedIdentity.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/UserAssignedIdentity.cs new file mode 100644 index 0000000000000..3450deff2ff75 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/UserAssignedIdentity.cs @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.ResourceManager.Core +{ + using System; + using System.Text.Json; + using Azure.Core; + + /// + /// A class representing an Identity assigned by the user. + /// + public sealed class UserAssignedIdentity + { + /// + /// Initializes a new instance of the class. + /// + /// ClientId . + /// PrincipalId. + public UserAssignedIdentity(Guid clientId, Guid principalId) + { + ClientId = clientId; + PrincipalId = principalId; + } + + /// + /// Gets or sets the Client ID. + /// + public Guid ClientId { get; set; } + + /// + /// Gets or sets the Principal ID. + /// + public Guid PrincipalId { get; set; } + + /// + /// Converts a into an object. + /// + /// A containing an identity. + /// New object with JSON values. + internal static UserAssignedIdentity Deserialize(JsonElement element) + { + if (element.ValueKind == JsonValueKind.Undefined) + { + throw new ArgumentException("JsonElement is undefined " + nameof(element)); + } + + Guid principalId = default; + Guid clientId = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("principalId")) + { + if (property.Value.ValueKind != JsonValueKind.Null) + principalId = Guid.Parse(property.Value.GetString()); + } + + if (property.NameEquals("clientId")) + { + if (property.Value.ValueKind != JsonValueKind.Null) + clientId = Guid.Parse(property.Value.GetString()); + } + } + + if (principalId == default(Guid) && clientId == default(Guid)) + return null; + + if (principalId == default(Guid) || clientId == default(Guid)) + throw new InvalidOperationException("Either ClientId or PrincipalId were null"); + + return new UserAssignedIdentity(clientId, principalId); + } + + /// + /// Converts an object into a . + /// + /// Utf8JsonWriter object to which the output is going to be written. + /// object to be converted. + internal static void Serialize(Utf8JsonWriter writer, UserAssignedIdentity userAssignedIdentity) + { + if (userAssignedIdentity == null) + throw new ArgumentNullException(nameof(userAssignedIdentity)); + + if (writer == null) + throw new ArgumentNullException(nameof(writer)); + + writer.WriteStartObject(); + + writer.WritePropertyName("clientId"); + writer.WriteStringValue(userAssignedIdentity.ClientId.ToString()); + + writer.WritePropertyName("principalId"); + writer.WriteStringValue(userAssignedIdentity.PrincipalId.ToString()); + + writer.WriteEndObject(); + writer.Flush(); + } + + /// + /// Compares two objects to determine if they are equal. + /// + /// First object to compare. + /// Second object to compare. + /// True if they are equal, otherwise False. + public static bool Equals(UserAssignedIdentity original, UserAssignedIdentity other) + { + if (original == null) + return other == null; + + return original.Equals(other); + } + + /// + /// Compares this with another instance. + /// + /// object to compare. + /// -1 for less than, 0 for equals, 1 for greater than. + public int CompareTo(UserAssignedIdentity other) + { + if (other == null) + return 1; + + int compareResult = 0; + if ((compareResult = ClientId.CompareTo(other.ClientId)) == 0 && + (compareResult = PrincipalId.CompareTo(other.PrincipalId)) == 0) + { + return 0; + } + + return compareResult; + } + + /// + /// Compares this instance with another object and determines if they are equals. + /// + /// object to compare. + /// True if they are equal, otherwise false. + public bool Equals(UserAssignedIdentity other) + { + if (other == null) + return false; + + return ClientId.Equals(other.ClientId) && PrincipalId.Equals(other.PrincipalId); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Subscription.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Subscription.cs new file mode 100644 index 0000000000000..c980886409c99 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Subscription.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing a Subscription along with the instance operations that can be performed on it. + /// + public class Subscription : SubscriptionOperations + { + /// + /// Initializes a new instance of the class. + /// + /// The subscription operations to copy the client options from. + /// The resource data model. + internal Subscription(SubscriptionOperations subscription, SubscriptionData subscriptionData) + : base(subscription, subscriptionData.Id) + { + Data = subscriptionData; + } + + /// + /// Gets the subscription data model. + /// + public SubscriptionData Data { get; } + + /// + protected override Subscription GetResource() + { + return this; + } + + /// + protected override Task GetResourceAsync() + { + return Task.FromResult(this); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/SubscriptionContainer.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/SubscriptionContainer.cs new file mode 100644 index 0000000000000..c4bb2ca123434 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/SubscriptionContainer.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Core; +using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Resources; +using System; +using System.Threading; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing collection of Subscription and their operations + /// + public class SubscriptionContainer : ContainerBase + { + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// A credential used to authenticate to an Azure Service. + /// The base URI of the service. + internal SubscriptionContainer(AzureResourceManagerClientOptions options, TokenCredential credential, Uri baseUri) + : base(options, null, credential, baseUri) + { + } + + /// + /// Gets the valid resource type associated with the container. + /// + protected override ResourceType ValidResourceType => SubscriptionOperations.ResourceType; + + /// + /// Gets the operations that can be performed on the container. + /// + private SubscriptionsOperations Operations => new ResourcesManagementClient( + BaseUri, + Guid.NewGuid().ToString(), + Credential, + ClientOptions.Convert()).Subscriptions; + + /// + /// Lists all subscriptions in the current container. + /// + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A collection of resource operations that may take multiple service requests to iterate over. + public Pageable List(CancellationToken cancellationToken = default) + { + return new PhWrappingPageable( + Operations.List(cancellationToken), + Converter()); + } + + /// + /// Lists all subscriptions in the current container. + /// + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// An async collection of resource operations that may take multiple service requests to iterate over. + public AsyncPageable ListAsync(CancellationToken cancellationToken = default) + { + return new PhWrappingAsyncPageable( + Operations.ListAsync(cancellationToken), + Converter()); + } + + /// + /// Validate the resource identifier is supported in the current container. + /// + /// The identifier of the resource. + protected override void Validate(ResourceIdentifier identifier) + { + if (identifier != null) + throw new ArgumentException("Invalid parent for subscription container"); + } + + /// + /// Get an instance of the operations this container holds. + /// + /// The guid of the subscription to be found. + /// An instance of . + protected override ResourceOperationsBase GetOperation(string subscriptionGuid) + { + return new SubscriptionOperations(ClientOptions, subscriptionGuid, Credential, BaseUri); + } + + private Func Converter() + { + return s => new Subscription(new SubscriptionOperations(ClientOptions, s.SubscriptionId, Credential, BaseUri), new SubscriptionData(s)); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/SubscriptionOperations.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/SubscriptionOperations.cs new file mode 100644 index 0000000000000..4975f46565b81 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/SubscriptionOperations.cs @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core; +using Azure.ResourceManager.Resources; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class representing the operations that can be performed over a specific subscription. + /// + public class SubscriptionOperations : ResourceOperationsBase + { + /// + /// The resource type for subscription + /// + public static readonly ResourceType ResourceType = "Microsoft.Resources/subscriptions"; + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The Id of the subscription. + /// A credential used to authenticate to an Azure Service. + /// The base URI of the service. + internal SubscriptionOperations(AzureResourceManagerClientOptions options, string subscriptionId, TokenCredential credential, Uri baseUri) + : base(options, $"/subscriptions/{subscriptionId}", credential, baseUri) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The subscription operations to copy client options from. + /// The identifier of the subscription. + protected SubscriptionOperations(SubscriptionOperations subscription, ResourceIdentifier id) + : base(subscription.ClientOptions, id, subscription.Credential, subscription.BaseUri) + { + } + + /// + /// Gets the valid resource type for this operation class + /// + protected override ResourceType ValidResourceType => ResourceType; + + private SubscriptionsOperations SubscriptionsClient => new ResourcesManagementClient( + BaseUri, + Guid.NewGuid().ToString(), + Credential, + ClientOptions.Convert()).Subscriptions; + + /// + /// Gets the resource group operations for a given resource group. + /// + /// The name of the resource group. + /// The resource group operations. + /// resourceGroupName must be at least one character long and cannot be longer than 90 characters. + /// The name of the resource group can include alphanumeric, underscore, parentheses, hyphen, period (except at end), and Unicode characters that match the allowed characters. + public ResourceGroupOperations GetResourceGroupOperations(string resourceGroupName) + { + return new ResourceGroupOperations(this, resourceGroupName); + } + + /// + /// Gets the resource group container under this subscription + /// + /// The resource group container. + public ResourceGroupContainer GetResourceGroupContainer() + { + return new ResourceGroupContainer(this); + } + + /// + /// Gets the location group container under this subscription + /// + /// The resource group container. + public LocationContainer GetLocationContainer() + { + return new LocationContainer(this); + } + + /// + public override ArmResponse Get() + { + return new PhArmResponse( + SubscriptionsClient.Get(Id.Name), + Converter()); + } + + /// + public override async Task> GetAsync(CancellationToken cancellationToken = default) + { + return new PhArmResponse( + await SubscriptionsClient.GetAsync(Id.Name, cancellationToken).ConfigureAwait(false), + Converter()); + } + + private Func Converter() + { + return s => new Subscription(this, new SubscriptionData(s)); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/autorest.md b/sdk/resourcemanager/Azure.ResourceManager.Core/src/autorest.md new file mode 100644 index 0000000000000..3beda43f741b4 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/autorest.md @@ -0,0 +1,12 @@ +# Generated code configuration + +Run `dotnet build /t:GenerateCode` to generate code. + +``` yaml +azure-arm: true +title: ResourcesManagementCore +library-name: ResourcesManagementCore +modelerfour: + lenient-model-deduplication: true +``` + diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ApiVersionsBaseTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ApiVersionsBaseTests.cs new file mode 100644 index 0000000000000..827ca1a28e02f --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ApiVersionsBaseTests.cs @@ -0,0 +1,216 @@ +using NUnit.Framework; +using System; + +namespace Azure.ResourceManager.Core.Tests +{ + public class ApiVersionsBaseTests + { + [TestCase] + public void VersionToString() + { + AzureResourceManagerClientOptions options = new AzureResourceManagerClientOptions(); + Assert.AreEqual("2020-06-01", options.FakeRpApiVersions().FakeResourceVersion); + } + + [TestCase] + public void EqualsOperator() + { + AzureResourceManagerClientOptions options1 = new AzureResourceManagerClientOptions(); + AzureResourceManagerClientOptions options2 = new AzureResourceManagerClientOptions(); + + Assert.IsTrue(options1.FakeRpApiVersions().FakeResourceVersion == options2.FakeRpApiVersions().FakeResourceVersion); + } + + [TestCase] + public void EqualsOperatorString() + { + AzureResourceManagerClientOptions options = new AzureResourceManagerClientOptions(); + + Assert.IsTrue(FakeResourceApiVersions.Default == options.FakeRpApiVersions().FakeResourceVersion); + } + + [TestCase] + public void EqualsOperatorStringFirstNull() + { + AzureResourceManagerClientOptions options = new AzureResourceManagerClientOptions(); + + Assert.IsFalse(null == options.FakeRpApiVersions().FakeResourceVersion); + } + + [TestCase] + public void EqualsOperatorStringSecondNull() + { + AzureResourceManagerClientOptions options = new AzureResourceManagerClientOptions(); + + Assert.IsFalse(options.FakeRpApiVersions().FakeResourceVersion == null); + } + + [TestCase] + public void EqualsOperatorStringBothNull() + { + FakeResourceApiVersions v1 = null; + + Assert.IsTrue(v1 == null); + } + + [TestCase] + public void NotEqualsOperator() + { + AzureResourceManagerClientOptions options1 = new AzureResourceManagerClientOptions(); + AzureResourceManagerClientOptions options2 = new AzureResourceManagerClientOptions(); + + Assert.IsFalse(options1.FakeRpApiVersions().FakeResourceVersion != options2.FakeRpApiVersions().FakeResourceVersion); + } + + [TestCase] + public void NotEqualsOperatorStringFirstNull() + { + AzureResourceManagerClientOptions options = new AzureResourceManagerClientOptions(); + + Assert.IsTrue(null != options.FakeRpApiVersions().FakeResourceVersion); + } + + [TestCase] + public void NotEqualsOperatorStringSecondNull() + { + AzureResourceManagerClientOptions options = new AzureResourceManagerClientOptions(); + + Assert.IsTrue(options.FakeRpApiVersions().FakeResourceVersion != null); + } + + [TestCase] + public void NotEqualsOperatorStringBothNull() + { + FakeResourceApiVersions v1 = null; + + Assert.IsFalse(v1 != null); + } + + [TestCase] + public void EqualsMethod() + { + AzureResourceManagerClientOptions options1 = new AzureResourceManagerClientOptions(); + AzureResourceManagerClientOptions options2 = new AzureResourceManagerClientOptions(); + + Assert.IsTrue(options1.FakeRpApiVersions().FakeResourceVersion.Equals(options2.FakeRpApiVersions().FakeResourceVersion)); + } + + [TestCase] + public void EqualsMethodVersionNull() + { + AzureResourceManagerClientOptions options = new AzureResourceManagerClientOptions(); + FakeResourceApiVersions version = null; + Assert.IsFalse(options.FakeRpApiVersions().FakeResourceVersion.Equals(version)); + } + + [TestCase] + public void EqualsMethodStringNull() + { + AzureResourceManagerClientOptions options = new AzureResourceManagerClientOptions(); + string version = null; + Assert.IsFalse(options.FakeRpApiVersions().FakeResourceVersion.Equals(version)); + } + + [TestCase] + public void EqualsMethodAsObject() + { + AzureResourceManagerClientOptions options1 = new AzureResourceManagerClientOptions(); + AzureResourceManagerClientOptions options2 = new AzureResourceManagerClientOptions(); + + object obj = options2.FakeRpApiVersions().FakeResourceVersion; + Assert.IsTrue(options1.FakeRpApiVersions().FakeResourceVersion.Equals(obj)); + } + + [TestCase] + public void EqualsMethodAsObjectThatIsString() + { + AzureResourceManagerClientOptions options1 = new AzureResourceManagerClientOptions(); + AzureResourceManagerClientOptions options2 = new AzureResourceManagerClientOptions(); + + object obj = options2.FakeRpApiVersions().FakeResourceVersion.ToString(); + Assert.IsTrue(options1.FakeRpApiVersions().FakeResourceVersion.Equals(obj)); + } + + [TestCase] + public void EqualsMethodAsObjectThatIsInt() + { + AzureResourceManagerClientOptions options = new AzureResourceManagerClientOptions(); + + object obj = 1; + Assert.IsFalse(options.FakeRpApiVersions().FakeResourceVersion.Equals(obj)); + } + + [TestCase] + public void ImplicitToString() + { + AzureResourceManagerClientOptions options = new AzureResourceManagerClientOptions(); + options.FakeRpApiVersions().FakeResourceVersion = FakeResourceApiVersions.V2019_12_01; + string version = options.FakeRpApiVersions().FakeResourceVersion; + Assert.IsTrue(version == "2019-12-01"); + } + + [TestCase(-1, "2019-12-01", "2020-06-01")] + [TestCase(0, "2019-12-01", "2019-12-01")] + [TestCase(1, "2020-06-01", "2019-12-01")] + [TestCase(1, "2020-06-01", null)] + public void CompareToMethodString(int expected, string version1, string version2) + { + FakeResourceApiVersions v1 = version1 == "2019-12-01" ? FakeResourceApiVersions.V2019_12_01 : FakeResourceApiVersions.V2020_06_01; + Assert.AreEqual(expected, v1.CompareTo(version2)); + } + + private FakeResourceApiVersions ConvertFromString(string version) + { + switch(version) + { + case "2019-12-01": + return FakeResourceApiVersions.V2019_12_01; + case "2020-06-01": + return FakeResourceApiVersions.V2020_06_01; + case "2019-12-01-preview": + return FakeResourceApiVersions.V2019_12_01_preview; + case "2019-12-01-preview-1": + return FakeResourceApiVersions.V2019_12_01_preview_1; + case "2019-12-01-foobar": + return FakeResourceApiVersions.V2019_12_01_foobar; + default: + throw new ArgumentException($"Version ({version}) was not valid"); + } + } + + [TestCase(-1, "2019-12-01", "2020-06-01")] + [TestCase(-1, "2019-12-01-preview", "2020-06-01")] + [TestCase(1, "2020-06-01", "2019-12-01-preview")] + [TestCase(0, "2019-12-01", "2019-12-01")] + [TestCase(-1, "2019-12-01-foobar", "2019-12-01-preview")] + [TestCase(1, "2019-12-01-preview", "2019-12-01-foobar")] + [TestCase(1, "2019-12-01-preview-1", "2019-12-01-preview")] + [TestCase(-1, "2019-12-01-preview", "2019-12-01-preview-1")] + [TestCase(0, "2019-12-01-preview", "2019-12-01-preview")] + [TestCase(-1, "2019-12-01-preview", "2019-12-01")] + [TestCase(1, "2019-12-01", "2019-12-01-preview")] + [TestCase(1, "2020-06-01", "2019-12-01")] + [TestCase(1, "2020-06-01", null)] + public void CompareToMethodVersionObject(int expected, string version1, string version2) + { + FakeResourceApiVersions v1 = ConvertFromString(version1); + FakeResourceApiVersions v2 = null; + if (version2 != null) + v2 = ConvertFromString(version2); + Assert.AreEqual(expected, v1.CompareTo(v2)); + } + + [TestCase] + public void ToStringTest() + { + Assert.AreEqual("2020-06-01", FakeResourceApiVersions.Default.ToString()); + } + + [TestCase] + public void GetHashCodeTest() + { + FakeResourceApiVersions version = FakeResourceApiVersions.Default; + Assert.AreEqual(version.ToString().GetHashCode(), version.GetHashCode()); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ArmClientOptionsTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ArmClientOptionsTests.cs new file mode 100644 index 0000000000000..7ce6a32f5a405 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ArmClientOptionsTests.cs @@ -0,0 +1,25 @@ +using NUnit.Framework; + +namespace Azure.ResourceManager.Core.Tests +{ + public class AzureResourceManagerClientOptionsTests + { + [TestCase] + public void VersionIsDefault() + { + AzureResourceManagerClientOptions options = new AzureResourceManagerClientOptions(); + Assert.AreEqual(FakeResourceApiVersions.Default, options.FakeRpApiVersions().FakeResourceVersion); + } + + [TestCase] + public void MultiClientSeparateVersions() + { + AzureResourceManagerClientOptions options1 = new AzureResourceManagerClientOptions(); + AzureResourceManagerClientOptions options2 = new AzureResourceManagerClientOptions(); + + options2.FakeRpApiVersions().FakeResourceVersion = FakeResourceApiVersions.V2019_12_01; + Assert.AreEqual(FakeResourceApiVersions.Default, options1.FakeRpApiVersions().FakeResourceVersion); + Assert.AreEqual(FakeResourceApiVersions.V2019_12_01, options2.FakeRpApiVersions().FakeResourceVersion); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ArmClientTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ArmClientTests.cs new file mode 100644 index 0000000000000..04b52e63ba373 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ArmClientTests.cs @@ -0,0 +1,15 @@ +using NUnit.Framework; + +namespace Azure.ResourceManager.Core.Tests +{ + public class ArmClientTests + { + [TestCase] + public void CreateResourceFromId() + { + //TODO: 4622 needs complete with a Mocked example to fill in this test + //public ArmResponse CreateResource(string subscription, string resourceGroup, string name, TResource model, azure_proto_core.Location location = default) + Assert.Ignore(); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Azure.ResourceManager.Core.Tests.csproj b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Azure.ResourceManager.Core.Tests.csproj new file mode 100644 index 0000000000000..b45d1b35adf6c --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Azure.ResourceManager.Core.Tests.csproj @@ -0,0 +1,32 @@ + + + + $(DefineConstants);RESOURCES_RP + ; + + + SA1649;SA1633;SA1000;SA1028;SA1400;SA1508 + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/IdentityTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/IdentityTests.cs new file mode 100644 index 0000000000000..051064fb1384d --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/IdentityTests.cs @@ -0,0 +1,457 @@ +using Azure.Core.TestFramework; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; + +namespace Azure.ResourceManager.Core.Tests +{ + public class IdentityTests + { + [TestCase] + public void CheckNoParamConstructor() + { + ResourceIdentity identity = new ResourceIdentity(); + Assert.IsNotNull(identity); + Assert.IsTrue(identity.UserAssignedIdentities.Count == 0); + Assert.IsNull(identity.SystemAssignedIdentity); + } + + [TestCase ("/subscriptions/6b085460-5f00-477e-ba44-1035046e9101/resourceGroups/tester/providers/Microsoft.Web/sites/autotest", false)] + [TestCase ("", true)] + [TestCase (" ", true)] + [TestCase (null, true)] + public void CheckUserTrueConstructor(string resourceID, bool invalidParameter) + { + var dict1 = new Dictionary(); + + if (invalidParameter) + { + Assert.Throws(() => { dict1[resourceID] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); }); + } + else + { + dict1[resourceID] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); + ResourceIdentity identity = new ResourceIdentity(dict1, true); + Assert.IsNotNull(identity); + Assert.IsNotNull(identity.UserAssignedIdentities); + Assert.IsTrue(identity.UserAssignedIdentities.Count == 1); + Assert.IsNotNull(identity.SystemAssignedIdentity); + Assert.IsNull(identity.SystemAssignedIdentity.TenantId); + Assert.IsNull(identity.SystemAssignedIdentity.PrincipalId); + } + } + + [TestCase ("/subscriptions/6b085460-5f00-477e-ba44-1035046e9101/resourceGroups/tester/providers/Microsoft.Web/sites/autotest", false)] + [TestCase("", true)] + [TestCase(" ", true)] + [TestCase(null, true)] + public void CheckUserFalseConstructor(string resourceID, bool invalidParameter) + { + var dict1 = new Dictionary(); + + if(invalidParameter) + { + Assert.Throws(() => { dict1[resourceID] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); }); + } + else + { + dict1[resourceID] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); + var system = new SystemAssignedIdentity(Guid.Empty, Guid.Empty); + ResourceIdentity identity = new ResourceIdentity(system, dict1); + Assert.IsNotNull(identity); + Assert.IsNotNull(identity.UserAssignedIdentities); + Assert.IsTrue(identity.UserAssignedIdentities.Count == 1); + Assert.IsNotNull(identity.SystemAssignedIdentity); + Assert.IsTrue(identity.SystemAssignedIdentity.TenantId.Equals(Guid.Empty)); + Assert.IsTrue(identity.SystemAssignedIdentity.PrincipalId.Equals(Guid.Empty)); + } + } + + [TestCase] + public void EqualsNullOtherTestFalse() + { + ResourceIdentity identity = new ResourceIdentity(); + ResourceIdentity other = null; + Assert.IsFalse(identity.Equals(other)); + } + + [TestCase] + public void EqualsNullOtherTest() + { + ResourceIdentity identity = new ResourceIdentity(); + ResourceIdentity other = new ResourceIdentity(); + Assert.IsTrue(identity.Equals(other)); + } + + [TestCase] + public void EqualsReferenceTestTrue() + { + var dict1 = new Dictionary(); + dict1["/subscriptions/6b085460-5vbg-477e-ba44-1035046e9101/resourceGroups/tester/providers/Microsoft.Web/sites/autotest"] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); + var system = new SystemAssignedIdentity(Guid.Empty, Guid.Empty); + ResourceIdentity identity = new ResourceIdentity(system, dict1); + ResourceIdentity identity1 = identity; + Assert.IsTrue(identity.Equals(identity1)); + } + + [TestCase] + public void EqualsTestTrue() + { + var dict1 = new Dictionary(); + dict1["/subscriptions/6b085460-5f21-477e-ba44-1035046e9000/resourceGroups/tester/providers/Microsoft.Web/sites/autotest"] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); + var system = new SystemAssignedIdentity(Guid.Empty, Guid.Empty); + ResourceIdentity identity = new ResourceIdentity(system, dict1); + var dict2 = new Dictionary(); + dict2["/subscriptions/6b085460-5f21-477e-ba44-1035046e9000/resourceGroups/tester/providers/Microsoft.Web/sites/autotest"] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); + var system2 = new SystemAssignedIdentity(Guid.Empty, Guid.Empty); + ResourceIdentity identity1 = new ResourceIdentity(system2, dict2); + Assert.IsTrue(identity.Equals(identity1)); + } + + [TestCase] + public void EqualsTestFalse() + { + var dict1 = new Dictionary(); + dict1["/subscriptions/6b085460-5f21-477e-ba44-1035nbhs9101/resourceGroups/tester/providers/Microsoft.Web/sites/autotest"] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); + var system = new SystemAssignedIdentity(Guid.Empty, Guid.Empty); + ResourceIdentity identity = new ResourceIdentity(system, dict1); + var dict2 = new Dictionary(); + dict2["/subscriptions/6b08dfsg-5f21-475e-ba44-1035046e9101/resourceGroups/tester/providers/Microsoft.Web/sites/autotest"] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); + var system2 = new SystemAssignedIdentity(Guid.Empty, Guid.Empty); + ResourceIdentity identity1 = new ResourceIdentity(system2, dict2); + Assert.IsFalse(identity.Equals(identity1)); + } + + [TestCase] + public void EqualsTestFalseSameKey() + { + var dict1 = new Dictionary(); + dict1["/subscriptions/6b085460-5f21-477e-ba44-10ancd6e9101/resourceGroups/tester/providers/Microsoft.Web/sites/autotest"] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); + var system = new SystemAssignedIdentity(Guid.Empty, Guid.Empty); + ResourceIdentity identity = new ResourceIdentity(system, dict1); + var dict2 = new Dictionary(); + dict2["/subscriptions/6b085460-5f21-477e-ba44-10ancd6e9101/resourceGroups/tester/providers/Microsoft.Web/sites/autotest"] = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), Guid.Empty); + var system2 = new SystemAssignedIdentity(Guid.Empty, Guid.Empty); + ResourceIdentity identity1 = new ResourceIdentity(system2, dict2); + Assert.IsFalse(identity.Equals(identity1)); + } + + [TestCase] + public void TestDeserializerInvalidDefaultJson() + { + JsonElement invalid = default(JsonElement); + Assert.Throws(delegate { ResourceIdentity.Deserialize(invalid); }); + } + + public JsonProperty DeserializerHelper(string filename) + { + string json = GetFileText(filename); + JsonDocument document = JsonDocument.Parse(json); + JsonElement rootElement = document.RootElement; + return rootElement.EnumerateObject().First(); + } + + private static string GetFileText(string filename) + { + return File.ReadAllText(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestAssets", "Identity", filename)); + } + + [TestCase] + public void TestDeserializerInvalidNullType() + { + var identityJsonProperty = DeserializerHelper("InvalidTypeIsNull.json"); + Assert.Throws(delegate { ResourceIdentity.Deserialize(identityJsonProperty.Value); }); + } + + [TestCase] + public void TestDeserializerValidSystemAndUserAssigned() + { + var identityJsonProperty = DeserializerHelper("SystemAndUserAssignedValid.json"); + ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value); + Assert.IsTrue("22fdaec1-8b9f-49dc-bd72-ddaf8f215577".Equals(back.SystemAssignedIdentity.PrincipalId.ToString())); + Assert.IsTrue("72f988af-86f1-41af-91ab-2d7cd011db47".Equals(back.SystemAssignedIdentity.TenantId.ToString())); + var user = back.UserAssignedIdentities; + Assert.AreEqual("/subscriptions/db1ab6f0-4769-4aa7-930e-01e2ef9c123c/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity", user.Keys.First().ToString()); + Assert.AreEqual("9a9eaa6a-b49c-4c63-afb5-3b72e3e65422", user.Values.First().ClientId.ToString()); + Assert.AreEqual("77563a98-c9d9-407b-a7af-592d21fa2153", user.Values.First().PrincipalId.ToString()); + } + + [TestCase] + public void TestDeserializerInvalidType() + { + var identityJsonProperty = DeserializerHelper("InvalidType.json"); + ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value); + var user = back.UserAssignedIdentities; + Assert.AreEqual("/subscriptions/db1ab6f0-4769-tgds-930e-01e2ef9c123c/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity", user.Keys.First().ToString()); + Assert.AreEqual("9a2eaa6a-b49c-4a63-afb5-3b72e3e65422", user.Values.First().ClientId.ToString()); + Assert.AreEqual("77563a98-c9d9-4f7b-a7af-592d21fa2153", user.Values.First().PrincipalId.ToString()); + } + + [TestCase] + public void TestDeserializerValidInnerExtraField() + { + var identityJsonProperty = DeserializerHelper("SystemAndUserAssignedInnerExtraField.json"); + ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value); + Assert.IsTrue("22fddec1-8b9f-49dc-bd72-ddaf8f215577".Equals(back.SystemAssignedIdentity.PrincipalId.ToString())); + Assert.IsTrue("72f988bf-86f1-41af-91ab-2d7cd011db47".Equals(back.SystemAssignedIdentity.TenantId.ToString())); + var user = back.UserAssignedIdentities; + Assert.AreEqual("/subscriptions/db1ab6f0-4769-4b27-9dde-01e2ef9c123c/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity", user.Keys.First().ToString()); + Assert.AreEqual("9a9eaa6a-b49c-4c63-afb5-3b72e3e65422", user.Values.First().ClientId.ToString()); + Assert.AreEqual("77563a98-c9d9-407b-a7af-592d21fa2153", user.Values.First().PrincipalId.ToString()); + } + + [TestCase] + public void TestDeserializerValidMiddleExtraField() + { + var identityJsonProperty = DeserializerHelper("SystemAndUserAssignedMiddleExtraField.json"); + ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value); + Assert.IsTrue("22fddec1-8b9f-49dc-bd72-ddaf8f215577".Equals(back.SystemAssignedIdentity.PrincipalId.ToString())); + Assert.IsTrue("72f988bf-86f1-41af-91ab-2d7cd011db47".Equals(back.SystemAssignedIdentity.TenantId.ToString())); + var user = back.UserAssignedIdentities; + Assert.AreEqual("/subscriptions/db1ab6f0-4769-4b27-9dde-01e2ef9c123c/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity", user.Keys.First().ToString()); + Assert.AreEqual("9a9eaa6a-b49c-4c63-afb5-3b72e3e65422", user.Values.First().ClientId.ToString()); + Assert.AreEqual("77563a98-c9d9-407b-a7af-592d21fa2153", user.Values.First().PrincipalId.ToString()); + } + + [TestCase] + public void TestDeserializerValidOuterExtraField() + { + var json = GetFileText("SystemAndUserAssignedOuterExtraField.json"); + JsonDocument document = JsonDocument.Parse(json); + JsonElement rootElement = document.RootElement; + var identityJsonProperty = rootElement.EnumerateObject().ElementAt(1); + ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value); + Assert.IsTrue("22fddec1-8b9f-49dc-bd72-ddaf8f215577".Equals(back.SystemAssignedIdentity.PrincipalId.ToString())); + Assert.IsTrue("72f988bf-86f1-41af-91ab-2d7cd011db47".Equals(back.SystemAssignedIdentity.TenantId.ToString())); + var user = back.UserAssignedIdentities; + Assert.AreEqual("/subscriptions/db1ab6f0-3466-4b27-930e-01e2ef9c123c/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity", user.Keys.First().ToString()); + Assert.AreEqual("9a2eaa6b-b49c-4c63-afb5-3b72e3e65422", user.Values.First().ClientId.ToString()); + Assert.AreEqual("7756fa98-c9d9-477b-a7af-592d21fa2153", user.Values.First().PrincipalId.ToString()); + } + + [TestCase] + public void TestDeserializerValidSystemAndMultUser() + { + var identityJsonProperty = DeserializerHelper("SystemAndUserAssignedValidMultIdentities.json"); + ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value); + Assert.IsTrue("22fddec1-8b9f-49dc-bd72-ddaf8f215570".Equals(back.SystemAssignedIdentity.PrincipalId.ToString())); + Assert.IsTrue("72f988bf-86f1-41af-91ab-2d7cd011db40".Equals(back.SystemAssignedIdentity.TenantId.ToString())); + var user = back.UserAssignedIdentities; + Assert.AreEqual("/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123z/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity0", user.Keys.First().ToString()); + Assert.AreEqual("9a2eaa6a-b49c-4c63-afb5-3b72e3e65422", user.Values.First().ClientId.ToString()); + Assert.AreEqual("77563a98-c9d9-477b-a7af-592d21fa2153", user.Values.First().PrincipalId.ToString()); + Assert.AreEqual("/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9cfrgh/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity1", user.Keys.ElementAt(1).ToString()); + Assert.AreEqual("9a2eaa6a-b49c-4c63-afb5-3b72e3c65420", user.Values.ElementAt(1).ClientId.ToString()); + Assert.AreEqual("77563a98-c9d9-477b-a7af-592d2bfa2150", user.Values.ElementAt(1).PrincipalId.ToString()); + } + + [TestCase] + public void TestDeserializerValidSystemAssigned() + { + var identityJsonProperty = DeserializerHelper("SystemAssigned.json"); + ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value); + Assert.IsTrue("22fddec1-8b9f-49dc-bd72-ddaf8f215577".Equals(back.SystemAssignedIdentity.PrincipalId.ToString())); + Assert.IsTrue("72f988bf-86f1-41af-91ab-2d7cd011db47".Equals(back.SystemAssignedIdentity.TenantId.ToString())); + Assert.IsTrue(back.UserAssignedIdentities.Count == 0); + } + + [TestCase] + public void TestDeserializerValidUserAssigned() + { + var identityJsonProperty = DeserializerHelper("UserAssigned.json"); + ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value); + Assert.IsNull(back.SystemAssignedIdentity); + var user = back.UserAssignedIdentities; + Assert.AreEqual("/subscriptions/db1ab6f0-4769-4b2e-930e-01e2ef9c123c/resourceGroups/tester-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity", user.Keys.First().ToString()); + Assert.AreEqual("9a2eaa6a-b49c-4c63-afb5-3b72e3e65422", user.Values.First().ClientId.ToString()); + Assert.AreEqual("77563a98-c9d9-477b-a7af-592d21fa2153", user.Values.First().PrincipalId.ToString()); + } + + [TestCase] + public void TestSerializerValidSystemAndUser() + { + SystemAssignedIdentity systemAssignedIdentity = new SystemAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + UserAssignedIdentity userAssignedIdentity = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + var dict1 = new Dictionary(); + dict1["/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport"] = userAssignedIdentity; + ResourceIdentity identity = new ResourceIdentity(systemAssignedIdentity, dict1); + string system = "\"principalId\":\"de29bab1-49e1-4705-819b-4dfddceaaa98\",\"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\""; + string user = "{\"clientId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\",\"principalId\":\"de29bab1-49e1-4705-819b-4dfddceaaa98\"}"; + string expected = "{\"identity\":{" + + system + "," + + "\"kind\":\"SystemAssigned, UserAssigned\"," + + "\"userAssignedIdentities\":" + + "{" + "\"/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport\":" + + user + "}}}"; + string actual = ""; + using (Stream stream = new MemoryStream()) + { + using (StreamReader streamReader = new StreamReader(stream)) + { + var writer = new Utf8JsonWriter(stream); + ResourceIdentity.Serialize(writer, identity); + stream.Seek(0, SeekOrigin.Begin); + actual = streamReader.ReadToEnd(); + } + } + Assert.AreEqual(expected, actual); + } + + [TestCase] + public void TestSerializerValidSystemAndMultUser() + { + SystemAssignedIdentity systemAssignedIdentity = new SystemAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + UserAssignedIdentity userAssignedIdentity1 = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + UserAssignedIdentity userAssignedIdentity2 = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011cb47"), new Guid("de29bab1-49e1-4705-819b-4dfddcebaa98")); + var dict1 = new Dictionary(); + dict1["/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport1"] = userAssignedIdentity1; + dict1["/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport2"] = userAssignedIdentity2; + ResourceIdentity identity = new ResourceIdentity(systemAssignedIdentity, dict1); + string system = "\"principalId\":\"de29bab1-49e1-4705-819b-4dfddceaaa98\",\"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\""; + string user = "{\"clientId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\",\"principalId\":\"de29bab1-49e1-4705-819b-4dfddceaaa98\"}"; + string user2 = "{\"clientId\":\"72f988bf-86f1-41af-91ab-2d7cd011cb47\",\"principalId\":\"de29bab1-49e1-4705-819b-4dfddcebaa98\"}"; + string expected = "{\"identity\":{" + + system + "," + + "\"kind\":\"SystemAssigned, UserAssigned\"," + + "\"userAssignedIdentities\":" + + "{" + "\"/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport1\":" + + user + "," + + "\"/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport2\":" + + user2 + "}}}"; + string actual = ""; + using (Stream stream = new MemoryStream()) + { + using (StreamReader streamReader = new StreamReader(stream)) + { + var writer = new Utf8JsonWriter(stream); + ResourceIdentity.Serialize(writer, identity); + stream.Seek(0, SeekOrigin.Begin); + actual = streamReader.ReadToEnd(); + } + } + Assert.AreEqual(expected, actual); + } + + [TestCase] + public void TestSerializerValidSystemOnly() + { + SystemAssignedIdentity systemAssignedIdentity = new SystemAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + ResourceIdentity identity = new ResourceIdentity(systemAssignedIdentity, null); + string system = "\"principalId\":\"de29bab1-49e1-4705-819b-4dfddceaaa98\",\"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\""; + string expected = "{\"identity\":{" + + system + "," + + "\"kind\":\"SystemAssigned\"}}"; + string actual = ""; + using (Stream stream = new MemoryStream()) + { + using (StreamReader streamReader = new StreamReader(stream)) + { + var writer = new Utf8JsonWriter(stream); + ResourceIdentity.Serialize(writer, identity); + stream.Seek(0, SeekOrigin.Begin); + actual = streamReader.ReadToEnd(); + } + } + Assert.AreEqual(expected, actual); + } + + [TestCase] + public void TestSerializerValidUserEmptySystem() + { + UserAssignedIdentity userAssignedIdentity = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + var dict1 = new Dictionary(); + dict1["/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport"] = userAssignedIdentity; + ResourceIdentity identity = new ResourceIdentity(dict1, true); + string system = "\"principalId\":\"null\",\"tenantId\":\"null\""; + string user = "{\"clientId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\",\"principalId\":\"de29bab1-49e1-4705-819b-4dfddceaaa98\"}"; + string expected = "{\"identity\":{" + + system + "," + + "\"kind\":\"SystemAssigned, UserAssigned\"," + + "\"userAssignedIdentities\":" + + "{" + "\"/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport\":" + + user + "}}}"; + string actual = ""; + using (Stream stream = new MemoryStream()) + { + using (StreamReader streamReader = new StreamReader(stream)) + { + var writer = new Utf8JsonWriter(stream); + ResourceIdentity.Serialize(writer, identity); + stream.Seek(0, SeekOrigin.Begin); + actual = streamReader.ReadToEnd(); + } + } + Assert.AreEqual(expected, actual); + } + + [TestCase] + public void TestSerializerValidUserNullSystem() + { + UserAssignedIdentity userAssignedIdentity = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + var dict1 = new Dictionary(); + dict1["/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport"] = userAssignedIdentity; + ResourceIdentity identity = new ResourceIdentity(dict1, false); + string user = "{\"clientId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\",\"principalId\":\"de29bab1-49e1-4705-819b-4dfddceaaa98\"}"; + string expected = "{\"identity\":{" + + "\"kind\":\"UserAssigned\"," + + "\"userAssignedIdentities\":" + + "{" + "\"/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport\":" + + user + "}}}"; + string actual = ""; + using (Stream stream = new MemoryStream()) + { + using (StreamReader streamReader = new StreamReader(stream)) + { + var writer = new Utf8JsonWriter(stream); + ResourceIdentity.Serialize(writer, identity); + stream.Seek(0, SeekOrigin.Begin); + actual = streamReader.ReadToEnd(); + } + } + Assert.AreEqual(expected, actual); + } + + [TestCase] + public void TestSerializerValidIdentityNull() + { + ResourceIdentity identity = new ResourceIdentity(); + string expected = "{\"identity\":\"null\"}"; + string actual = ""; + using (Stream stream = new MemoryStream()) + { + using (StreamReader streamReader = new StreamReader(stream)) + { + var writer = new Utf8JsonWriter(stream); + ResourceIdentity.Serialize(writer, identity); + stream.Seek(0, SeekOrigin.Begin); + actual = streamReader.ReadToEnd(); + } + } + Assert.AreEqual(expected, actual); + } + + [TestCase] + public void TestSerializerInvalidNullWriter() + { + ResourceIdentity identity = new ResourceIdentity(); + using (Stream stream = new MemoryStream()) + { + Assert.Throws(delegate { ResourceIdentity.Serialize(null, identity); }); + } + } + + [TestCase] + public void TestSerializerInvalidNullIdentity() + { + ResourceIdentity identity = null; + using (Stream stream = new MemoryStream()) + { + var writer = new Utf8JsonWriter(stream); + Assert.Throws(delegate { ResourceIdentity.Serialize(writer, identity); }); + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/LocationTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/LocationTests.cs new file mode 100644 index 0000000000000..84d6f686573fc --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/LocationTests.cs @@ -0,0 +1,225 @@ +using NUnit.Framework; +using System; + +namespace Azure.ResourceManager.Core.Tests +{ + public class LocationTests + { + [TestCase("westus", "westus", "west-us", "West US")] + [TestCase("west-us", "westus", "west-us", "West US")] + [TestCase("West US", "westus", "west-us", "West US")] + [TestCase("privatecloud", "privatecloud", "privatecloud", "privatecloud")] + [TestCase("private-cloud", "privatecloud", "private-cloud", "Private Cloud")] + [TestCase("Private Cloud", "privatecloud", "private-cloud", "Private Cloud")] + [TestCase("@$!()*&", "@$!()*&", "@$!()*&", "@$!()*&")] + [TestCase("W3$t U$ 2", "W3$t U$ 2", "W3$t U$ 2", "W3$t U$ 2")] + [TestCase("", "", "", "")] + [TestCase(" ", " ", " ", " ")] + [TestCase(null, null, null, null)] + public void CanCreateLocation(string name, string expectedName, string expectedCanonincalName, string expectedDisplayName) + { + LocationData location = name; + if (name == null) + { + Assert.IsNull(location); + } + else + { + Assert.AreEqual(expectedName, location.Name); + Assert.AreEqual(expectedCanonincalName, location.CanonicalName); + Assert.AreEqual(expectedDisplayName, location.DisplayName); + } + } + + [TestCase("USNorth")] + [TestCase("Us West 12")] + [TestCase("Us West 1a")] + [TestCase(" Us West 1")] + [TestCase("Us West 1 ")] + [TestCase("*Us West")] + [TestCase("Us *West")] + [TestCase("Us West *")] + [TestCase("")] + public void NameTypeIsName(string location) + { + LocationData loc = location; + Assert.IsTrue(loc.Name == loc.DisplayName && loc.Name == loc.CanonicalName); + } + + [TestCase("us-west")] + [TestCase("us-west-west")] + [TestCase("us-west-2")] + [TestCase("us-west-west-2")] + [TestCase("a-b-c-5")] + public void NameTypeIsCanonical(string location) + { + LocationData loc = location; + Assert.IsTrue(loc.CanonicalName == location && loc.Name != location && loc.DisplayName != location); + } + + [TestCase("Us West")] + [TestCase("US West")] + [TestCase("USa West")] + [TestCase("West US")] + [TestCase("West USa")] + [TestCase("Us West West")] + [TestCase("Us West 2")] + [TestCase("Us West West 2")] + [TestCase("A B C 5")] + public void NameTypeIsDisplayName(string location) + { + LocationData loc = location; + Assert.IsTrue(loc.DisplayName == location && loc.Name != location && loc.CanonicalName != location); + } + + [TestCase(true, "West Us", "West Us")] + [TestCase(true, "West Us", "WestUs")] + [TestCase(true, "!#()@(#@", "!#()@(#@")] + [TestCase(true, "W3$t U$", "W3$t U$")] + [TestCase(true, "1234567890", "1234567890")] + [TestCase(false, "West Us", "WestUs2")] + [TestCase(false, "West US", "")] + [TestCase(false, "West US", "!#()@(#@")] + [TestCase(false, "West US", "W3$t U$")] + [TestCase(false, "West US", null)] + public void EqualsToObject(bool expected, string left, string right) + { + LocationData loc1 = left; + LocationData loc2 = right; + Assert.AreEqual(expected, loc1.Equals(loc2)); + + if (expected) + { + Assert.AreEqual(0, loc1.CompareTo(loc2)); + } + else + { + Assert.AreNotEqual(0, loc1.CompareTo(loc2)); + } + } + + [TestCase(true, "West Us", "West Us")] + [TestCase(true, "West Us", "WestUs")] + [TestCase(true, "!#()@(#@", "!#()@(#@")] + [TestCase(true, "W3$t U$", "W3$t U$")] + [TestCase(true, "1234567890", "1234567890")] + [TestCase(false, "West Us", "WestUs2")] + [TestCase(false, "West Us", "")] + [TestCase(false, "West Us", "!#()@(#@")] + [TestCase(false, "West Us", "W3$t U$")] + [TestCase(false, "West Us", null)] + public void EqualsToString(bool expected, string left, string right) + { + LocationData location = left; + Assert.AreEqual(expected, location.Equals(right)); + + if (expected) + { + Assert.AreEqual(0, location.CompareTo(right)); + } + else + { + Assert.AreNotEqual(0, location.CompareTo(right)); + } + } + + [TestCase("", "")] + [TestCase("West US", "West US")] + [TestCase("west-us", "West US")] + [TestCase("westus2", "West US 2")] + [TestCase("private-cloud", "Private Cloud")] + public void CanParseToString(string name, string expected) + { + LocationData location1 = name; + Assert.AreEqual(expected, location1.ToString()); + } + + [TestCase("West US", "West US", 0)] + [TestCase("West US", "west-us", 0)] + [TestCase("West US", "westus", 0)] + [TestCase("Central Europe", "Central Europe", 0)] + [TestCase("Central Europe", "central-europe", 0)] + [TestCase("Central Europe", "centraleurope", 0)] + [TestCase("South US", "East US", 1)] + [TestCase("South US", "east-us", 1)] + [TestCase("South US", "West US", -1)] + [TestCase("South US", "west-us", -1)] + [TestCase("South US", null, 1)] + public void CompareToObject(string left, string right, int expected) + { + LocationData location1 = left; + LocationData location2 = right; + Assert.AreEqual(expected, location1.CompareTo(location2)); + if (right != null) + { + Assert.AreEqual(expected * -1, location2.CompareTo(location1)); + } + } + + [TestCase("West US", "West US", 0)] + [TestCase("West US", "west-us", 0)] + [TestCase("West US", "westus", 0)] + [TestCase("Central Europe", "Central Europe", 0)] + [TestCase("Central Europe", "central-europe", 0)] + [TestCase("Central Europe", "centraleurope", 0)] + [TestCase("South US", "East US", 1)] + [TestCase("South US", "east-us", 1)] + [TestCase("South US", "West US", -1)] + [TestCase("South US", "west-us", -1)] + [TestCase("South US", null, 1)] + public void CompareToString(string left, string right, int expected) + { + LocationData location1 = left; + Assert.AreEqual(expected, location1.CompareTo(right)); + if (right != null) + { + location1 = right; + Assert.AreEqual(expected * -1, location1.CompareTo(left)); + } + } + + [TestCase("West US", "West US")] + [TestCase("west-us", "West US")] + [TestCase("westus", "West US")] + [TestCase("Private Cloud", "Private Cloud")] + [TestCase("private-cloud", "Private Cloud")] + [TestCase("privatecloud", "privatecloud")] + [TestCase("1$S#@$%^", "1$S#@$%^")] + [TestCase("", "")] + [TestCase(" ", " ")] + [TestCase(null, null)] + public void CanCastLocationToString(string name, string expected) + { + LocationData location = name; + string strLocation = location; + Assert.AreEqual(expected, strLocation); + } + + [TestCase ("West US", "West US")] + [TestCase ("west-us", "West US")] + [TestCase ("westus", "West US")] + [TestCase ("Private Cloud", "Private Cloud")] + [TestCase ("private-cloud", "Private Cloud")] + [TestCase ("privatecloud", "privatecloud")] + [TestCase ("1$S#@$%^", "1$S#@$%^")] + [TestCase ("","")] + [TestCase (" ", " ")] + [TestCase (null,null)] + public void CanCastStringToLocation(string name, string expected) + { + LocationData location1 = name; + if (name == null) + Assert.Throws(()=> { string x = location1.DisplayName; }); + else + Assert.AreEqual(expected, location1.DisplayName); + } + + [Test] + public void CanAccessDefaultLocation() + { + Assert.IsNotNull(LocationData.Default); + LocationData location = LocationData.Default; + Assert.IsTrue(location.Equals(LocationData.Default)); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/PlanTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/PlanTests.cs new file mode 100644 index 0000000000000..0eea99ed7984b --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/PlanTests.cs @@ -0,0 +1,232 @@ +using NUnit.Framework; + +namespace Azure.ResourceManager.Core.Tests +{ + class PlanTests + { + [TestCase(0, "name", "name")] + [TestCase(0, "Name", "name")] + [TestCase(0, null, null)] + [TestCase(1, "name", null)] + [TestCase(-1, null, "name")] + [TestCase(0, "${?/>._`", "${?/>._`")] + [TestCase(1, "${?/>._`", "")] + public void CompareToName(int expected, string name1, string name2) + { + Plan plan1 = new Plan(name1, null, null, null, null); + Plan plan2 = new Plan(name2, null, null, null, null); + Assert.AreEqual(expected, plan1.CompareTo(plan2)); + } + + [TestCase(0, "product", "product")] + [TestCase(0, "Product", "product")] + [TestCase(0, null, null)] + [TestCase(1, "product", null)] + [TestCase(-1, null, "product")] + [TestCase(0, "${?/>._`", "${?/>._`")] + [TestCase(1, "${?/>._`", "")] + public void CompareToProduct(int expected, string product1, string product2) + { + Plan plan1 = new Plan(null, null, product1, null, null); + Plan plan2 = new Plan(null, null, product2, null, null); + Assert.AreEqual(expected, plan1.CompareTo(plan2)); + } + + [TestCase(0, "promotionCode", "promotionCode")] + [TestCase(0, "PromotionCode", "promotionCode")] + [TestCase(0, null, null)] + [TestCase(1, "promotionCode", null)] + [TestCase(-1, null, "promotionCode")] + [TestCase(0, "${?/>._`", "${?/>._`")] + [TestCase(1, "${?/>._`", "")] + public void CompareToPromotionCode(int expected, string promotionCode1, string promotionCode2) + { + Plan plan1 = new Plan(null, null, null, promotionCode1, null); + Plan plan2 = new Plan(null, null, null, promotionCode2, null); + Assert.AreEqual(expected, plan1.CompareTo(plan2)); + } + + [TestCase(0, "publisher", "publisher")] + [TestCase(0, "Publisher", "publisher")] + [TestCase(0, null, null)] + [TestCase(1, "publisher", null)] + [TestCase(-1, null, "publisher")] + [TestCase(0, "${?/>._`", "${?/>._`")] + [TestCase(1, "${?/>._`", "")] + public void CompareToPublisher(int expected, string publisher1, string publisher2) + { + Plan plan1 = new Plan(null, publisher1, null, null, null); + Plan plan2 = new Plan(null, publisher2, null, null, null); + Assert.AreEqual(expected, plan1.CompareTo(plan2)); + } + + [TestCase(0, "version", "version")] + [TestCase(0, "Version", "version")] + [TestCase(0, null, null)] + [TestCase(1, "version", null)] + [TestCase(-1, null, "version")] + [TestCase(0, "${?/>._`", "${?/>._`")] + [TestCase(1, "${?/>._`", "")] + public void CompareToVersion(int expected, string version1, string version2) + { + Plan plan1 = new Plan(null, null, null, null, version1); + Plan plan2 = new Plan(null, null, null, null, version2); + Assert.AreEqual(expected, plan1.CompareTo(plan2)); + } + + [Test] + public void CompareToNullPlan() + { + Plan plan1 = new Plan(null, null, null, null, null); + Plan plan2 = null; + Assert.AreEqual(1, plan1.CompareTo(plan2)); + } + + [Test] + public void CompareToSamePlans() + { + Plan plan1 = new Plan(null, null, null, null, null); + Plan plan2 = plan1; + Assert.AreEqual(0, plan1.CompareTo(plan2)); + } + + [TestCase(1, "Nameb", "namea", "versiona", "Versionb")] + [TestCase(1, "Nameb", "namea", "versiona", "versiona")] + [TestCase(-1, "namea", "Nameb", "Versionb", "versiona")] + public void CompareToMore(int expected, string name1, string name2, string version1, string version2) + { + Plan plan1 = new Plan(name1, null, null, null, version1); + Plan plan2 = new Plan(name2, null, null, null, version2); + Assert.AreEqual(expected, plan1.CompareTo(plan2)); + } + + [TestCase(true, "name", "name")] + [TestCase(true, "Name", "name")] + [TestCase(true, null, null)] + [TestCase(false, "name", null)] + [TestCase(false, null, "name")] + [TestCase(true, "${?/>._`", "${?/>._`")] + [TestCase(false, "${?/>._`", "")] + public void EqualsToName(bool expected, string name1, string name2) + { + Plan plan1 = new Plan(name1, null, null, null, null); + Plan plan2 = new Plan(name2, null, null, null, null); + if (expected) + { + Assert.IsTrue(plan1.Equals(plan2)); + } + else + { + Assert.IsFalse(plan1.Equals(plan2)); + } + } + + [TestCase(true, "product", "product")] + [TestCase(true, "Product", "product")] + [TestCase(true, null, null)] + [TestCase(false, "product", null)] + [TestCase(false, null, "product")] + [TestCase(true, "${?/>._`", "${?/>._`")] + [TestCase(false, "${?/>._`", "")] + public void EqualsToProduct(bool expected, string product1, string product2) + { + Plan plan1 = new Plan(null, null, product1, null, null); + Plan plan2 = new Plan(null, null, product2, null, null); + if (expected) + { + Assert.IsTrue(plan1.Equals(plan2)); + } + else + { + Assert.IsFalse(plan1.Equals(plan2)); + } + } + + [TestCase(true, "promotionCode", "promotionCode")] + [TestCase(true, "PromotionCode", "promotionCode")] + [TestCase(true, null, null)] + [TestCase(false, "promotionCode", null)] + [TestCase(false, null, "promotionCode")] + [TestCase(true, "${?/>._`", "${?/>._`")] + [TestCase(false, "${?/>._`", "")] + public void EqualsToPromotionCode(bool expected, string promotionCode1, string promotionCode2) + { + Plan plan1 = new Plan(null, null, null, promotionCode1, null); + Plan plan2 = new Plan(null, null, null, promotionCode2, null); + if (expected) + { + Assert.IsTrue(plan1.Equals(plan2)); + } + else + { + Assert.IsFalse(plan1.Equals(plan2)); + } + } + + [TestCase(true, "publisher", "publisher")] + [TestCase(true, "Publisher", "publisher")] + [TestCase(true, null, null)] + [TestCase(false, "publisher", null)] + [TestCase(false, null, "publisher")] + [TestCase(true, "${?/>._`", "${?/>._`")] + [TestCase(false, "${?/>._`", "")] + public void EqualsToPublisher(bool expected, string publisher1, string publisher2) + { + Plan plan1 = new Plan(null, publisher1, null, null, null); + Plan plan2 = new Plan(null, publisher2, null, null, null); + if (expected) + { + Assert.IsTrue(plan1.Equals(plan2)); + } + else + { + Assert.IsFalse(plan1.Equals(plan2)); + } + } + + [TestCase(true, "version", "version")] + [TestCase(true, "Version", "version")] + [TestCase(true, null, null)] + [TestCase(false, "version", null)] + [TestCase(false, null, "version")] + [TestCase(true, "${?/>._`", "${?/>._`")] + [TestCase(false, "${?/>._`", "")] + public void EqualsToVersion(bool expected, string version1, string version2) + { + Plan plan1 = new Plan(null, null, null, null, version1); + Plan plan2 = new Plan(null, null, null, null, version2); + if (expected) + { + Assert.IsTrue(plan1.Equals(plan2)); + } + else + { + Assert.IsFalse(plan1.Equals(plan2)); + } + } + + [Test] + public void EqualsToNullPlan() + { + Plan plan1 = new Plan(null, null, null, null, null); + Plan plan2 = null; + Assert.IsFalse(plan1.Equals(plan2)); + } + + [Test] + public void EqualsToObject() + { + Plan plan1 = new Plan(null, null, null, null, null); + object plan2 = "random"; + Assert.IsFalse(plan1.Equals(plan2)); + } + + [Test] + public void EqualsToSamePlans() + { + Plan plan1 = new Plan(null, null, null, null, null); + Plan plan2 = plan1; + Assert.IsTrue(plan1.Equals(plan2)); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/README.MD b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/README.MD new file mode 100644 index 0000000000000..36d54fd1c2eed --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/README.MD @@ -0,0 +1,16 @@ +# azure-proto-core-test + +To run test: ```dotnet test``` + +To run test with code coverage and auto generate an html report: ```dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura``` + +Coverage report will be placed in your path relative to azure-proto-core-test in```/coverage``` in html format for viewing + +Reports can also be viewed VS or VsCode with the proper viewer plugin + +A terse report will also be displayed on the command line when running. + + +## run test with single file or test + +To run test with code coverage and auto generate an html report with just a single test: ```dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura --filter ``` \ No newline at end of file diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Resource/TestResource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Resource/TestResource.cs new file mode 100644 index 0000000000000..8bb221bae37e7 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Resource/TestResource.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Azure.ResourceManager.Core.Tests +{ + public class TestResource : Resource + { + public TestResource(string id) + { + Id = id; + } + + public override ResourceIdentifier Id { get; protected set; } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceGroupOperationsTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceGroupOperationsTests.cs new file mode 100644 index 0000000000000..14b573e26a5f8 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceGroupOperationsTests.cs @@ -0,0 +1,23 @@ +using NUnit.Framework; + +namespace Azure.ResourceManager.Core.Tests +{ + public class ResourceGroupOperationsTests + { + [TestCase] + public void CreateResourceFromModel() + { + //TODO: 4622 needs complete with a Mocked example to fill in this test + //public ArmResponse CreateResource(string name, TResource model, azure_proto_core.Location location = default) + Assert.Ignore(); + } + + [TestCase] + public void CreateResourceFromModelAsync() + { + //TODO: 4622 needs complete with a Mocked example to fill in this test + //public ArmResponse CreateResource(string name, TResource model, azure_proto_core.Location location = default) + Assert.Ignore(); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceIdentifierTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceIdentifierTests.cs new file mode 100644 index 0000000000000..39e95ccf9893c --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceIdentifierTests.cs @@ -0,0 +1,123 @@ +using NUnit.Framework; +using System; + +namespace Azure.ResourceManager.Core.Tests +{ + public class ResourceIdentifierTests + { + const string TrackedResourceId = + "/subscriptions/0c2f6471-1bf0-4dda-aec3-cb9272f09575/resourceGroups/myRg/providers/Microsoft.Compute/virtualMachines/myVm"; + const string ChildResourceId = + "/subscriptions/0c2f6471-1bf0-4dda-aec3-cb9272f09575/resourceGroups/myRg/providers/Microsoft.Network/vortualNetworks/myNet/subnets/mySubnet"; + const string ResourceGroupResourceId = "/subscriptions/0c2f6471-1bf0-4dda-aec3-cb9272f09575/resourceGroups/myRg"; + const string LocationResourceId = "/subscriptions/0c2f6471-1bf0-4dda-aec3-cb9272f09575/locations/MyLocation"; + const string SubscriptionResourceId = "/subscriptions/0c2f6471-1bf0-4dda-aec3-cb9272f09575"; + + [SetUp] + public void Setup() + { + } + + [TestCase("0c2f6471-1bf0-4dda-aec3-cb9272f09575", "myRg", "Microsoft.Compute", "virtualMachines", "myVM")] + [TestCase("0c2f6471-1bf0-4dda-aec3-cb9272f09575", "!@#$%^&*()-_+=;:'\",<.>/?", "Microsoft.Network", "virtualNetworks", "MvVM_vnet")] + [TestCase("0c2f6471-1bf0-4dda-aec3-cb9272f09575", "myRg", "Microsoft.Network", "publicIpAddresses", "!@#$%^&*()-_+=;:'\",<.>/?")] + public void CanParseRPIds(string subscription, string resourceGroup, string provider, string type, string name) + { + var resourceId = $"/subscriptions/{subscription}/resourceGroups/{Uri.EscapeDataString(resourceGroup)}/providers/{provider}/{type}/{Uri.EscapeDataString(name)}"; + ResourceIdentifier subject = resourceId; + Assert.AreEqual(subject.ToString(), resourceId); + Assert.AreEqual(subject.Subscription, subscription); + Assert.AreEqual(Uri.UnescapeDataString(subject.ResourceGroup), resourceGroup); + Assert.AreEqual(subject.Type.Namespace, provider); + Assert.AreEqual(subject.Type.Type, type); + Assert.AreEqual(Uri.UnescapeDataString(subject.Name), name); + } + + [TestCase(TrackedResourceId, "Microsoft.Authorization", "roleAssignments", "myRa")] + [TestCase(ChildResourceId, "Microsoft.Authorization", "roleAssignments", "myRa")] + [TestCase(ResourceGroupResourceId, "Microsoft.Authorization", "roleAssignments", "myRa")] + [TestCase(LocationResourceId, "Microsoft.Authorization", "roleAssignments", "myRa")] + [TestCase(SubscriptionResourceId, "Microsoft.Authorization", "roleAssignments", "myRa")] + public void CanParseExtensionResourceIds(string baseId, string extensionNamespace, string extensionType, string extensionName) + { + ResourceIdentifier targetResourceId = baseId; + ResourceIdentifier subject = $"{baseId}/providers/{extensionNamespace}/{extensionType}/{extensionName}"; + ResourceType expectedType = $"{extensionNamespace}/{extensionType}"; + Assert.AreNotEqual(targetResourceId.Type, subject.Type); + Assert.AreEqual(expectedType, subject.Type); + Assert.NotNull(subject.Parent); + Assert.AreEqual(targetResourceId, subject.Parent); + } + + [Test] + public void CanParseSubscriptions() + { + ResourceIdentifier subject = "/subscriptions/0c2f6471-1bf0-4dda-aec3-cb9272f09575"; + Assert.AreEqual(subject.ToString(), "/subscriptions/0c2f6471-1bf0-4dda-aec3-cb9272f09575"); + Assert.AreEqual(subject.Subscription, "0c2f6471-1bf0-4dda-aec3-cb9272f09575"); + Assert.AreEqual(subject.Type.Namespace, "Microsoft.Resources"); + Assert.AreEqual(subject.Type.Type, "subscriptions"); + } + + [Test] + public void CanParseResourceGroups() + { + ResourceIdentifier subject = "/subscriptions/0c2f6471-1bf0-4dda-aec3-cb9272f09575/resourceGroups/myRg"; + Assert.AreEqual(subject.ToString(), "/subscriptions/0c2f6471-1bf0-4dda-aec3-cb9272f09575/resourceGroups/myRg"); + Assert.AreEqual(subject.Subscription, "0c2f6471-1bf0-4dda-aec3-cb9272f09575"); + Assert.AreEqual(subject.ResourceGroup, "myRg"); + Assert.AreEqual(subject.Type.Namespace, "Microsoft.Resources"); + Assert.AreEqual(subject.Type.Type, "resourceGroups"); + } + + [TestCase("MyVnet", "MySubnet")] + [TestCase("!@#$%^&*()-_+=;:'\",<.>/?", "MySubnet")] + [TestCase("MyVnet", "!@#$%^&*()-_+=;:'\",<.>/?")] + public void CanParseChildResources(string parentName, string name) + { + var resourceId = $"/subscriptions/0c2f6471-1bf0-4dda-aec3-cb9272f09575/resourceGroups/myRg/providers/Microsoft.Network/virtualNetworks/{Uri.EscapeDataString(parentName)}/subnets/{Uri.EscapeDataString(name)}"; + ResourceIdentifier subject = resourceId; + Assert.AreEqual(subject.ToString(), resourceId); + Assert.AreEqual(subject.Subscription, "0c2f6471-1bf0-4dda-aec3-cb9272f09575"); + Assert.AreEqual(Uri.UnescapeDataString(subject.ResourceGroup), "myRg"); + Assert.AreEqual(subject.Type.Namespace, "Microsoft.Network"); + Assert.AreEqual(subject.Type.Parent.Type, "virtualNetworks"); + Assert.AreEqual(subject.Type.Type, "virtualNetworks/subnets"); + Assert.AreEqual(Uri.UnescapeDataString(subject.Name), name); + + // check parent type parsing + var parentResource = $"/subscriptions/0c2f6471-1bf0-4dda-aec3-cb9272f09575/resourceGroups/myRg/providers/Microsoft.Network/virtualNetworks/{Uri.EscapeDataString(parentName)}"; + Assert.AreEqual(subject.Parent, parentResource); + Assert.AreEqual(subject.Parent.ToString(), parentResource); + Assert.AreEqual(subject.Parent.Subscription, "0c2f6471-1bf0-4dda-aec3-cb9272f09575"); + Assert.AreEqual(Uri.UnescapeDataString(subject.Parent.ResourceGroup), "myRg"); + Assert.AreEqual(subject.Parent.Type.Namespace, "Microsoft.Network"); + Assert.AreEqual(subject.Parent.Type.Type, "virtualNetworks"); + Assert.AreEqual(Uri.UnescapeDataString(subject.Parent.Name), parentName); + } + + [TestCase("UnformattedString", Description ="Too Few Elements")] + [TestCase("/subs/sub1/rgs/rg1/", Description = "No known parts")] + [TestCase("/subscriptions/sub1/resourceGroups", Description = "Too few parts")] + public void ThrowsOnInvalidUri(string resourceId) + { + Assert.Throws(new TestDelegate(() => ConvertToResourceId(resourceId))); + } + + public ResourceIdentifier ConvertToResourceId(string resourceId) + { + ResourceIdentifier subject = resourceId; + return subject; + } + + [TestCase(true, "/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport", "/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport")] + [TestCase(false, "/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport2", "/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport")] + [TestCase(false, "/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test/providers/Microsoft.Web/sites/autoreport", "/subscriptions/6b085460-5f21-477e-ba44-1035046e9101/resourceGroups/nbhatia_test")] + public void CheckHashCode(bool expected, string resourceId1, string resourceId2) + { + ResourceIdentifier resourceIdentifier1 = new ResourceIdentifier(resourceId1); + ResourceIdentifier resourceIdentifier2 = new ResourceIdentifier(resourceId2); + Assert.AreEqual(expected, resourceIdentifier1.GetHashCode() == resourceIdentifier2.GetHashCode()); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceListOperationsTest.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceListOperationsTest.cs new file mode 100644 index 0000000000000..85bbc3ce8bc2c --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceListOperationsTest.cs @@ -0,0 +1,134 @@ +using Azure.Identity; +using Azure.ResourceManager.Resources.Models; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Azure.ResourceManager.Core.Tests +{ + public class ResourceListOperationsTest + { + [TestCase] + public void TestArmResponseArmResource() + { + var expected = GetGenericResource(); + var asArmOp = (GenericResource)TestListActivator(expected); + + Assert.IsNotNull(asArmOp.Data.Sku); + Assert.AreEqual(expected.Sku.Capacity, asArmOp.Data.Sku.Capacity); + Assert.AreEqual(expected.Sku.Family, asArmOp.Data.Sku.Family); + Assert.AreEqual(expected.Sku.Name, asArmOp.Data.Sku.Name); + Assert.AreEqual(expected.Sku.Size, asArmOp.Data.Sku.Size); + Assert.AreEqual(expected.Sku.Tier, asArmOp.Data.Sku.Tier); + + Assert.IsNotNull(asArmOp.Data.Plan); + Assert.AreEqual(expected.Plan.Name, asArmOp.Data.Plan.Name); + Assert.AreEqual(expected.Plan.Product, asArmOp.Data.Plan.Product); + Assert.AreEqual(expected.Plan.PromotionCode, asArmOp.Data.Plan.PromotionCode); + Assert.AreEqual(expected.Plan.Publisher, asArmOp.Data.Plan.Publisher); + Assert.AreEqual(expected.Plan.Version, asArmOp.Data.Plan.Version); + + Assert.IsTrue(expected.Location == asArmOp.Data.Location); + Assert.AreEqual(expected.Kind, asArmOp.Data.Kind); + Assert.AreEqual(expected.ManagedBy, asArmOp.Data.ManagedBy); + } + + private static ResourceManager.Resources.Models.Plan GetPlan() + { + var plan = new ResourceManager.Resources.Models.Plan(); + plan.Name = "name"; + plan.Product = "product"; + plan.Publisher = "publisher"; + plan.PromotionCode = "promo"; + plan.Version = "version"; + return plan; + } + + private static ResourceManager.Resources.Models.Sku GetSku() + { + var sku = new ResourceManager.Resources.Models.Sku(); + sku.Capacity = 10; + sku.Family = "family"; + sku.Name = "name"; + sku.Size = "size"; + sku.Tier = "tier"; + return sku; + } + + [TestCase] + public void TestArmResourceActivator() + { + var expected = GetGenericResource(); + var actual = Activator.CreateInstance(typeof(GenericResourceData), expected as ResourceManager.Resources.Models.GenericResource) as GenericResourceData; + + Assert.IsNotNull(actual.Sku); + Assert.AreEqual(expected.Sku.Capacity, actual.Sku.Capacity); + Assert.AreEqual(expected.Sku.Family, actual.Sku.Family); + Assert.AreEqual(expected.Sku.Name, actual.Sku.Name); + Assert.AreEqual(expected.Sku.Size, actual.Sku.Size); + Assert.AreEqual(expected.Sku.Tier, actual.Sku.Tier); + + Assert.IsNotNull(actual.Plan); + Assert.AreEqual(expected.Plan.Name, actual.Plan.Name); + Assert.AreEqual(expected.Plan.Product, actual.Plan.Product); + Assert.AreEqual(expected.Plan.PromotionCode, actual.Plan.PromotionCode); + Assert.AreEqual(expected.Plan.Publisher, actual.Plan.Publisher); + Assert.AreEqual(expected.Plan.Version, actual.Plan.Version); + + Assert.IsTrue(expected.Location == actual.Location); + Assert.AreEqual(expected.Kind, actual.Kind); + Assert.AreEqual(expected.ManagedBy, actual.ManagedBy); + + } + + private static object TestListActivator(GenericResourceExpanded genericResource) + { + var createResourceConverterMethod = typeof(ResourceListOperations).GetMethod("CreateResourceConverter", BindingFlags.Static | BindingFlags.NonPublic); + ResourceGroupOperations rgOp = GetResourceGroupOperations(); + var activatorFunction = (Func)createResourceConverterMethod.Invoke(null, new object[] { rgOp }); + return activatorFunction.DynamicInvoke(new object[] { genericResource }); + } + + private static ResourceGroupOperations GetResourceGroupOperations() + { + var rgOp = new ResourceGroupOperations( + new SubscriptionOperations( + new AzureResourceManagerClientOptions(), + Guid.Empty.ToString(), + new DefaultAzureCredential(), //should make a fake credential creation + new Uri("http://foo.com")), + "rgName"); + return rgOp; + } + + private static GenericResourceExpanded GetGenericResource() + { + return GetGenereicResource( + new Dictionary { { "tag1", "value1" } }, + GetSku(), + GetPlan(), + "UserAssigned", + "test", + "Japan East"); + } + + private static GenericResourceExpanded GetGenereicResource( + Dictionary tags, + ResourceManager.Resources.Models.Sku sku, + ResourceManager.Resources.Models.Plan plan, + string kind, + string managedBy, + string location) + { + var resource = new GenericResourceExpanded(); + resource.Location = location; + resource.Tags = tags ?? new Dictionary(); + resource.Sku = sku; + resource.Plan = plan; + resource.Kind = kind; + resource.ManagedBy = managedBy; + return resource; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceNameFilterTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceNameFilterTests.cs new file mode 100644 index 0000000000000..5e029cef396d2 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceNameFilterTests.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.ResourceManager.Core.Resources; +using NUnit.Framework; + +namespace Azure.ResourceManager.Core.Tests +{ + public class ResourceNameFilterTests + { + [TestCase("filter")] + [TestCase("")] + [TestCase(")(@#$)&!)(@")] + public void ImplicitCast(string filter) + { + ResourceNameFilter nameFilter = filter; + Assert.AreEqual(nameFilter.Name, filter); + } + + [TestCase] + public void ImplicitCastNull() + { + string filter = null; + ResourceNameFilter nameFilter = filter; + Assert.IsNull(nameFilter); + } + + [TestCase(true, "foo", "foo")] + [TestCase(false, "foo", "")] + [TestCase(false, "foo", null)] + [TestCase(true, ")(@#$)&!)(@", ")(@#$)&!)(@")] + [TestCase(true, "", "")] + [TestCase(false, "foo", "bar")] + public void EqualsWithString(bool expected, string left, string right) + { + ResourceNameFilter leftFilter = left; + Assert.AreEqual(expected, leftFilter.Equals(right)); + } + + [TestCase(true, "foo", "foo")] + [TestCase(false, "foo", "")] + [TestCase(false, "foo", null)] + [TestCase(true, ")(@#$)&!)(@", ")(@#$)&!)(@")] + [TestCase(true, "", "")] + [TestCase(false, "foo", "bar")] + public void EqualsWithResourceNameFilter(bool expected, string left, string right) + { + ResourceNameFilter leftFilter = left; + ResourceNameFilter rightFilter = right; + Assert.AreEqual(expected, leftFilter.Equals(rightFilter)); + } + + [TestCase("substringof('filter', name)", "filter")] + [TestCase("", "")] + [TestCase("substringof(')(@#$)&!)(@', name)", ")(@#$)&!)(@")] + public void GetFilterString(string expected, string filter) + { + ResourceNameFilter nameFilter = filter; + Assert.AreEqual(expected, nameFilter.GetFilterString()); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceTagFilterTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceTagFilterTests.cs new file mode 100644 index 0000000000000..a3be301ead6a1 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceTagFilterTests.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.ResourceManager.Core.Resources; +using NUnit.Framework; +using System; + +namespace Azure.ResourceManager.Core.Tests +{ + public class ResourceTagFilterTests + { + [TestCase] + public void ConstructNullTuple() + { + Assert.Throws(() => new ResourceTagFilter(null)); + } + + [TestCase] + public void ConstructNullTupleKey() + { + string key = null; + Assert.Throws(() => new ResourceTagFilter(Tuple.Create(key, ""))); + } + + [TestCase] + public void ConstructNullTupleValue() + { + string value = null; + Assert.Throws(() => new ResourceTagFilter(Tuple.Create("", value))); + } + + [TestCase] + public void ConstructNullKey() + { + Assert.Throws(() => new ResourceTagFilter(null, "")); + } + + [TestCase] + public void ConstructNullValue() + { + Assert.Throws(() => new ResourceTagFilter("", null)); + } + + [TestCase(true, "key", "value", "key", "value")] + [TestCase(false, "key", "value", "key1", "value")] + [TestCase(false, "key", "value", "key", "value1")] + [TestCase(false, "key1", "value", "key", "value")] + [TestCase(false, "key", "value1", "key", "value")] + [TestCase(false, "key", "value", "key1", "value1")] + [TestCase(false, "key", "value", "", "value")] + [TestCase(false, "key", "value", "key", "")] + public void Equals(bool expected, string leftKey, string leftValue, string rightKey, string rightValue) + { + ResourceTagFilter leftFilter = new ResourceTagFilter(leftKey, leftValue); + ResourceTagFilter rightFilter = new ResourceTagFilter(rightKey, rightValue); + Assert.AreEqual(expected, leftFilter.Equals(rightFilter)); + } + + [TestCase("tagName eq 'key' and tagValue eq 'value'", "key", "value")] + [TestCase("tagName eq ')(@#$)&!)(@' and tagValue eq ')#$)(USkjao'", ")(@#$)&!)(@", ")#$)(USkjao")] + public void GetFilterString(string expected, string key, string value) + { + ResourceTagFilter tagFilter = new ResourceTagFilter(key, value); + Assert.AreEqual(expected, tagFilter.GetFilterString()); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceTests.cs new file mode 100644 index 0000000000000..7daa8db050283 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceTests.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using NUnit.Framework; + +namespace Azure.ResourceManager.Core.Tests +{ + public class ResourceTests + { + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1")] + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.classicStorage/storageAccounts/account1")] + [TestCase(-1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.DiffSpace/storageAccounts/account2")] + [TestCase(1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.DiffSpace/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account2")] + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.${?>._`/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.${?>._`/storageAccounts/account1")] + [TestCase(-1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/${?>._`", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account2")] + public void CompareToObject(int expected, string id1, string id2) + { + TestResource resource1 = new TestResource(id1); + TestResource resource2 = new TestResource(id2); + Assert.AreEqual(expected, resource1.CompareTo(resource2)); + } + + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1")] + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.classicStorage/storageAccounts/account1")] + [TestCase(-1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.DiffSpace/storageAccounts/account2")] + [TestCase(1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.DiffSpace/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account2")] + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.${?>._`/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.${?>._`/storageAccounts/account1")] + [TestCase(-1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/${?>._`", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account2")] + public void CompareToString(int expected, string id1, string id2) + { + TestResource resource1 = new TestResource(id1); + Assert.AreEqual(expected, resource1.CompareTo(id2)); + } + + [Test] + public void CompareToNull() + { + TestResource resource1 = new TestResource("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1"); + TestResource resource2 = null; + Assert.AreEqual(1, resource1.CompareTo(resource2)); + Assert.AreEqual(1, resource1.CompareTo((string)null)); + } + + [Test] + public void CompareToSame() + { + TestResource resource1 = new TestResource("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1"); + TestResource resource2 = resource1; + Assert.AreEqual(0, resource1.CompareTo(resource2)); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceTypeTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceTypeTests.cs new file mode 100644 index 0000000000000..24663714320c5 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceTypeTests.cs @@ -0,0 +1,233 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using NUnit.Framework; +using System; + +namespace Azure.ResourceManager.Core.Tests +{ + public class ResourceTypeTests + { + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account2")] + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.classicStorage/storageAccounts/account2")] + [TestCase(-1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.DiffSpace/storageAccounts/account2")] + [TestCase(1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.DiffSpace/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account2")] + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.${?>._`/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.${?>._`/storageAccounts/account2")] + [TestCase(-1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.${?>._`/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account2")] + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "Microsoft.ClassicStorage/storageAccounts")] + [TestCase(0, "Microsoft.classicStorage/storageAccounts", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1")] + [TestCase(-1, "Microsoft.ClassicStorage/storageAccounts", "Microsoft.DiffSpace/storageAccounts")] + [TestCase(1, "Microsoft.ClassicStorage/storageAccounts", "Microsoft.${?>._`/storageAccounts")] + public void CompateToNamespace(int expected, string resource1, string resource2) + { + ResourceType resourceType1 = new ResourceType(resource1); + ResourceType resourceType2 = new ResourceType(resource2); + Assert.AreEqual(expected, resourceType1.CompareTo(resourceType2)); + } + + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/StorageAccounts/account2")] + [TestCase(1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/diffaccount/account2")] + [TestCase(-1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/diffaccount/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account2")] + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/${?>._`/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/${?>._`/account2")] + [TestCase(-1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/${?>._`/account1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account2")] + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1", + "Microsoft.ClassicStorage/StorageAccounts")] + [TestCase(0, "Microsoft.classicStorage/storageAccounts", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/StorageAccounts/account2")] + [TestCase(-1, "Microsoft.ClassicStorage/diffaccount", "Microsoft.ClassicStorage/storageAccounts")] + [TestCase(1, "Microsoft.ClassicStorage/storageAccounts", "Microsoft.ClassicStorage/${?>._`")] + public void CompateToType(int expected, string resource1, string resource2) + { + ResourceType resourceType1 = new ResourceType(resource1); + ResourceType resourceType2 = new ResourceType(resource2); + Assert.AreEqual(expected, resourceType1.CompareTo(resourceType2)); + } + + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/virtualNetworks/Testvnet/subnets/default1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/virtualNetworks/Testvnet/subnets/default2")] + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/virtualNetworks/Testvnet/subnets/default1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/virtualnetworks/Testvnet/subnets/default2")] + [TestCase(1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/virtualNetworks/Testvnet/subnets/default1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/parentTest/Testvnet/subnets/default2")] + [TestCase(-1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/parentTest/Testvnet/subnets/default1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/virtualNetworks/Testvnet/subnets/default2")] + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/${?>._`/Testvnet/${?>._`/default1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/${?>._`/Testvnet/${?>._`/default2")] + [TestCase(-1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/${?>._`/Testvnet/subnets/default1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/virtualNetworks/Testvnet/${?>._`/default2")] + [TestCase(0, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/virtualNetworks/Testvnet/subnets/default1", + "Microsoft.Network/virtualNetworks/subnets")] + [TestCase(0, "Microsoft.Network/virtualNetworks/subnets", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network/virtualNetworks/Testvnet/subnets/default2")] + [TestCase(-1, "Microsoft.Network/parentTest/subnets", "Microsoft.Network/virtualNetworks/subnets")] + [TestCase(1, "Microsoft.Network/virtualNetworks/subnets", "Microsoft.Network/${?>._`/subnets")] + public void CompateToParent(int expected, string resource1, string resource2) + { + ResourceType resourceType1 = new ResourceType(resource1); + ResourceType resourceType2 = new ResourceType(resource2); + Assert.AreEqual(expected, resourceType1.CompareTo(resourceType2)); + } + + [Test] + public void CompateToSameResourceTypes() + { + ResourceType resourceType1 = new ResourceType("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1"); + ResourceType resourceType2 = resourceType1; + Assert.AreEqual(0, resourceType1.CompareTo(resourceType2)); + } + + [Test] + public void CompateToNull() + { + ResourceType resourceType1 = new ResourceType("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.ClassicStorage/storageAccounts/account1"); + ResourceType resourceType2 = null; + Assert.AreEqual(1, resourceType1.CompareTo(resourceType2)); + } + + [TestCase(-1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network1/virtualNetworks2/Testvnet/subnets/default1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.network2/virtualNetworks1/Testvnet/Subnets/default2")] + [TestCase(1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.network2/VirtualNetworks2/Testvnet/subnets2/default1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network1/virtualNetworks1/Testvnet/Subnets1/default2")] + [TestCase(1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.network2/VirtualNetworks2/Testvnet/Subnets2/default1", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.network1/virtualNetworks1/Testvnet/Subnets1/default2")] + [TestCase(1, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.network2/VirtualNetworks2/Testvnet/Subnets2/default1", + "Microsoft.network1/parentTest/subnets1")] + [TestCase(-1, "Microsoft.Network1/VirtualNetworks1/subnets2", + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/testRg/providers/Microsoft.Network1/VirtualNetworks2/Testvnet/subnets1/default2")] + [TestCase(1, "Microsoft.network1/virtualvetworks1/Subnets2", "Microsoft.Network1/VirtualNetworks2/subnets1")] + public void CompateToMore(int expected, string resource1, string resource2) + { + ResourceType resourceType1 = new ResourceType(resource1); + ResourceType resourceType2 = new ResourceType(resource2); + Assert.AreEqual(expected, resourceType1.CompareTo(resourceType2)); + } + + [TestCase("")] + [TestCase("\n")] + [TestCase("\t")] + [TestCase(" ")] + [TestCase("\r")] + [TestCase(null)] + public void InvalidConstructorParam(string input) + { + Assert.Throws(() => new ResourceType(input)); + } + + [TestCase] + public void NullImplicitFromString() + { + string from = null; + ResourceType to = from; + + Assert.IsNull(to); + } + + [TestCase(false, null, "Microsoft.Network1/VirtualNetworks2/subnets1")] + [TestCase(false, "Microsoft.Network1/VirtualNetworks2/subnets1", null)] + [TestCase(true, null, null)] + [TestCase(true, "Microsoft.Network1/VirtualNetworks2/subnets1", "Microsoft.Network1/VirtualNetworks2/subnets1")] + public void EqualsOpResourceTypeToString(bool expected, string left, string right) + { + ResourceType leftResource = left; + Assert.AreEqual(expected, leftResource == right); + } + + [TestCase(false, null, "Microsoft.Network1/VirtualNetworks2/subnets1")] + [TestCase(false, "Microsoft.Network1/VirtualNetworks2/subnets1", null)] + [TestCase(true, null, null)] + [TestCase(true, "Microsoft.Network1/VirtualNetworks2/subnets1", "Microsoft.Network1/VirtualNetworks2/subnets1")] + public void EqualsOpStringToResourceType(bool expected, string left, string right) + { + ResourceType rightResource = right; + Assert.AreEqual(expected, left == rightResource); + } + + [TestCase(false, null, "Microsoft.Network1/VirtualNetworks2/subnets1")] + [TestCase(false, "Microsoft.Network1/VirtualNetworks2/subnets1", null)] + [TestCase(true, null, null)] + [TestCase(true, "Microsoft.Network1/VirtualNetworks2/subnets1", "Microsoft.Network1/VirtualNetworks2/subnets1")] + public void EqualsOpResourceTypeToResourceType(bool expected, string left, string right) + { + ResourceType leftResource = left; + ResourceType rightResource = right; + Assert.AreEqual(expected, leftResource == rightResource); + } + + [TestCase(true, null, "Microsoft.Network1/VirtualNetworks2/subnets1")] + [TestCase(true, "Microsoft.Network1/VirtualNetworks2/subnets1", null)] + [TestCase(false, null, null)] + [TestCase(false, "Microsoft.Network1/VirtualNetworks2/subnets1", "Microsoft.Network1/VirtualNetworks2/subnets1")] + public void NotEqualsOpResourceTypeToString(bool expected, string left, string right) + { + ResourceType leftResource = left; + Assert.AreEqual(expected, leftResource != right); + } + + [TestCase(true, null, "Microsoft.Network1/VirtualNetworks2/subnets1")] + [TestCase(true, "Microsoft.Network1/VirtualNetworks2/subnets1", null)] + [TestCase(false, null, null)] + [TestCase(false, "Microsoft.Network1/VirtualNetworks2/subnets1", "Microsoft.Network1/VirtualNetworks2/subnets1")] + public void NotEqualsOpStringToResourceType(bool expected, string left, string right) + { + ResourceType rightResource = right; + Assert.AreEqual(expected, left != rightResource); + } + + [TestCase(true, null, "Microsoft.Network1/VirtualNetworks2/subnets1")] + [TestCase(true, "Microsoft.Network1/VirtualNetworks2/subnets1", null)] + [TestCase(false, null, null)] + [TestCase(false, "Microsoft.Network1/VirtualNetworks2/subnets1", "Microsoft.Network1/VirtualNetworks2/subnets1")] + public void NotEqualsOpResourceTypeToResourceType(bool expected, string left, string right) + { + ResourceType leftResource = left; + ResourceType rightResource = right; + Assert.AreEqual(expected, leftResource != rightResource); + } + + [TestCase] + public void CompareToNulString() + { + string other = null; + ResourceType rt = "Microsoft.Network1/VirtualNetworks2/subnets1"; + Assert.AreEqual(1, rt.CompareTo(other)); + } + + [TestCase] + public void ParseArgumentException() + { + Assert.Throws(() => { ResourceType rt = "/"; }); + } + + [TestCase(false, "Microsoft.Network1/VirtualNetworks2/subnets1", null)] + [TestCase(true, "Microsoft.Network1/VirtualNetworks2/subnets1", "Microsoft.Network1/VirtualNetworks2/subnets1")] + public void EqualsWithObjectResourceType(bool expected, string left, string right) + { + ResourceType rt = left; + ResourceType rightRt = right; + object rightObject = rightRt; + Assert.AreEqual(expected, rt.Equals(rightObject)); + } + + [TestCase(false, "Microsoft.Network1/VirtualNetworks2/subnets1", null)] + [TestCase(true, "Microsoft.Network1/VirtualNetworks2/subnets1", "Microsoft.Network1/VirtualNetworks2/subnets1")] + public void EqualsWithObjectString(bool expected, string left, string right) + { + ResourceType rt = left; + object rightObject = right; + Assert.AreEqual(expected, rt.Equals(rightObject)); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/RpImplementations/ArmClientOptionsExtensions.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/RpImplementations/ArmClientOptionsExtensions.cs new file mode 100644 index 0000000000000..fbced51f6a655 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/RpImplementations/ArmClientOptionsExtensions.cs @@ -0,0 +1,10 @@ +namespace Azure.ResourceManager.Core.Tests +{ + public static class AzureResourceManagerClientOptionsExtensions + { + public static FakeRpApiVersions FakeRpApiVersions(this AzureResourceManagerClientOptions AzureResourceManagerClientOptions) + { + return AzureResourceManagerClientOptions.GetOverrideObject(() => new FakeRpApiVersions()) as FakeRpApiVersions; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/RpImplementations/FakeResourceApiVersions.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/RpImplementations/FakeResourceApiVersions.cs new file mode 100644 index 0000000000000..376a2d84b9a8b --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/RpImplementations/FakeResourceApiVersions.cs @@ -0,0 +1,21 @@ +namespace Azure.ResourceManager.Core.Tests +{ + public class FakeResourceApiVersions : ApiVersionsBase + { + public static readonly FakeResourceApiVersions V2020_06_01 = new FakeResourceApiVersions("2020-06-01"); + public static readonly FakeResourceApiVersions V2019_12_01 = new FakeResourceApiVersions("2019-12-01"); + public static readonly FakeResourceApiVersions V2019_12_01_preview = new FakeResourceApiVersions("2019-12-01-preview"); + public static readonly FakeResourceApiVersions V2019_12_01_preview_1 = new FakeResourceApiVersions("2019-12-01-preview-1"); + public static readonly FakeResourceApiVersions V2019_12_01_foobar = new FakeResourceApiVersions("2019-12-01-foobar"); + public static readonly FakeResourceApiVersions Default = V2020_06_01; + + private FakeResourceApiVersions(string value) : base(value) { } + + public static implicit operator string(FakeResourceApiVersions version) + { + if (version == null) + return null; + return version.ToString(); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/RpImplementations/FakeRpApiVersions.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/RpImplementations/FakeRpApiVersions.cs new file mode 100644 index 0000000000000..1e3e6dc21bb34 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/RpImplementations/FakeRpApiVersions.cs @@ -0,0 +1,12 @@ +namespace Azure.ResourceManager.Core.Tests +{ + public class FakeRpApiVersions + { + internal FakeRpApiVersions() + { + FakeResourceVersion = FakeResourceApiVersions.Default; + } + + public FakeResourceApiVersions FakeResourceVersion { get; set; } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SkuTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SkuTests.cs new file mode 100644 index 0000000000000..e12ef2fcd5e2e --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SkuTests.cs @@ -0,0 +1,228 @@ +using NUnit.Framework; + +namespace Azure.ResourceManager.Core.Tests +{ + class SkuTests + { + [TestCase(0, "name", "name")] + [TestCase(0, "Name", "name")] + [TestCase(0, null, null)] + [TestCase(1, "name", null)] + [TestCase(-1, null, "name")] + [TestCase(0, "${?/>._`", "${?/>._`")] + [TestCase(1, "${?/>._`", "")] + public void CompareToName(int expected, string name1, string name2) + { + Sku sku1 = new Sku(name1, null, null, null); + Sku sku2 = new Sku(name2, null, null, null); + Assert.AreEqual(expected, sku1.CompareTo(sku2)); + } + + [TestCase(0, "family", "family")] + [TestCase(0, "Family", "family")] + [TestCase(0, null, null)] + [TestCase(1, "family", null)] + [TestCase(-1, null, "family")] + [TestCase(0, "${?/>._`", "${?/>._`")] + [TestCase(1, "${?/>._`", "")] + public void CompareToFamily(int expected, string family1, string family2) + { + Sku sku1 = new Sku(null, null, family1, null); + Sku sku2 = new Sku(null, null, family2, null); + Assert.AreEqual(expected, sku1.CompareTo(sku2)); + } + + [TestCase(0, "size", "size")] + [TestCase(0, "Size", "size")] + [TestCase(0, null, null)] + [TestCase(1, "size", null)] + [TestCase(-1, null, "size")] + [TestCase(0, "${?/>._`", "${?/>._`")] + [TestCase(1, "${?/>._`", "")] + public void CompareToSize(int expected, string size1, string size2) + { + Sku sku1 = new Sku(null, null, null, size1); + Sku sku2 = new Sku(null, null, null, size2); + Assert.AreEqual(expected, sku1.CompareTo(sku2)); + } + + [TestCase(0, "tier", "tier")] + [TestCase(0, "Tier", "tier")] + [TestCase(0, null, null)] + [TestCase(1, "tier", null)] + [TestCase(-1, null, "tier")] + [TestCase(0, "${?/>._`", "${?/>._`")] + [TestCase(1, "${?/>._`", "")] + public void CompareToTier(int expected, string tier1, string tier2) + { + Sku sku1 = new Sku(null, tier1, null, null); + Sku sku2 = new Sku(null, tier2, null, null); + Assert.AreEqual(expected, sku1.CompareTo(sku2)); + } + + [TestCase(0, 1, 1)] + [TestCase(1, 1, -1)] + [TestCase(0, null, null)] + [TestCase(1, -1, null)] + [TestCase(-1, null, 1)] + public void CompareToCapacity(int expected, long? capacity1, long? capacity2) + { + Sku sku1 = capacity1 == null ? new Sku(null, null, null, null) : new Sku(null, null, null, null, capacity1); + Sku sku2 = capacity2 == null ? new Sku(null, null, null, null) : new Sku(null, null, null, null, capacity2); + Assert.AreEqual(expected, sku1.CompareTo(sku2)); + } + + [Test] + public void CompareToNullSku() + { + Sku sku1 = new Sku(null, null, null, null); + Sku sku2 = null; + Assert.AreEqual(1, sku1.CompareTo(sku2)); + } + + [Test] + public void CompareToSameSkus() + { + Sku sku1 = new Sku(null, null, null, null); + Sku sku2 = sku1; + Assert.AreEqual(0, sku1.CompareTo(sku2)); + } + + [TestCase(1, "Nameb", "namea", "familya", "Familyb")] + [TestCase(1, "Nameb", "namea", "familya", "familya")] + [TestCase(-1, "namea", "Nameb", "Familyb", "familya")] + public void CompareToMore(int expected, string name1, string name2, string family1, string family2) + { + Sku sku1 = new Sku(name1, null, family1, null); + Sku sku2 = new Sku(name2, null, family2, null); + Assert.AreEqual(expected, sku1.CompareTo(sku2)); + } + + [TestCase(true, "name", "name")] + [TestCase(true, "Name", "name")] + [TestCase(true, null, null)] + [TestCase(false, "name", null)] + [TestCase(false, null, "name")] + [TestCase(true, "${?/>._`", "${?/>._`")] + [TestCase(false, "${?/>._`", "")] + public void EqualsToName(bool expected, string name1, string name2) + { + Sku sku1 = new Sku(name1, null, null, null); + Sku sku2 = new Sku(name2, null, null, null); + if (expected) + { + Assert.IsTrue(sku1.Equals(sku2)); + } + else + { + Assert.IsFalse(sku1.Equals(sku2)); + } + } + + [TestCase(true, "family", "family")] + [TestCase(true, "Family", "family")] + [TestCase(true, null, null)] + [TestCase(false, "family", null)] + [TestCase(false, null, "family")] + [TestCase(true, "${?/>._`", "${?/>._`")] + [TestCase(false, "${?/>._`", "")] + public void EqualsToFamily(bool expected, string family1, string family2) + { + Sku sku1 = new Sku(null, null, family1, null); + Sku sku2 = new Sku(null, null, family2, null); + if (expected) + { + Assert.IsTrue(sku1.Equals(sku2)); + } + else + { + Assert.IsFalse(sku1.Equals(sku2)); + } + } + + [TestCase(true, "size", "size")] + [TestCase(true, "Size", "size")] + [TestCase(true, null, null)] + [TestCase(false, "size", null)] + [TestCase(false, null, "size")] + [TestCase(true, "${?/>._`", "${?/>._`")] + [TestCase(false, "${?/>._`", "")] + public void EqualsToSize(bool expected, string size1, string size2) + { + Sku sku1 = new Sku(null, null, null, size1); + Sku sku2 = new Sku(null, null, null, size2); + if (expected) + { + Assert.IsTrue(sku1.Equals(sku2)); + } + else + { + Assert.IsFalse(sku1.Equals(sku2)); + } + } + + [TestCase(true, "tier", "tier")] + [TestCase(true, "Tier", "tier")] + [TestCase(true, null, null)] + [TestCase(false, "tier", null)] + [TestCase(false, null, "tier")] + [TestCase(true, "${?/>._`", "${?/>._`")] + [TestCase(false, "${?/>._`", "")] + public void EqualsToTier(bool expected, string tier1, string tier2) + { + Sku sku1 = new Sku(null, tier1, null, null); + Sku sku2 = new Sku(null, tier2, null, null); + if (expected) + { + Assert.IsTrue(sku1.Equals(sku2)); + } + else + { + Assert.IsFalse(sku1.Equals(sku2)); + } + } + + [TestCase(true, 1, 1)] + [TestCase(false, 1, 0)] + [TestCase(true, null, null)] + [TestCase(false, 1, null)] + [TestCase(false, null, 1)] + public void EqualsToCapacity(bool expected, long? capacity1, long? capacity2) + { + Sku sku1 = capacity1 == null ? new Sku(null, null, null, null) : new Sku(null, null, null, null, capacity1); + Sku sku2 = capacity2 == null ? new Sku(null, null, null, null) : new Sku(null, null, null, null, capacity2); + if (expected) + { + Assert.IsTrue(sku1.Equals(sku2)); + } + else + { + Assert.IsFalse(sku1.Equals(sku2)); + } + } + + [Test] + public void EqualsToNullSku() + { + Sku sku1 = new Sku(null, null, null, null); + Sku sku2 = null; + Assert.IsFalse(sku1.Equals(sku2)); + } + + [Test] + public void EqualsToObject() + { + Sku sku1 = new Sku(null, null, null, null); + object sku2 = "random"; + Assert.IsFalse(sku1.Equals(sku2)); + } + + [Test] + public void EqualsToSameSkus() + { + Sku sku1 = new Sku(null, null, null, null); + Sku sku2 = sku1; + Assert.IsTrue(sku1.Equals(sku2)); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs new file mode 100644 index 0000000000000..452b0192edf15 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using NUnit.Framework; +using System; + +namespace Azure.ResourceManager.Core.Tests +{ + public class SubscriptionOperationsTests + { + [TestCase(null)] + [TestCase("")] + public void TestGetResourceGroupOpsArgNullException(string resourceGroupName) + { + var client = new AzureResourceManagerClient(); + var subOps = client.DefaultSubscription; + Assert.Throws(delegate { subOps.GetResourceGroupOperations(resourceGroupName); }); + } + + [TestCase("te%st")] + [TestCase("test ")] + [TestCase("te$st")] + [TestCase("te#st")] + [TestCase("te#st")] + public void TestGetResourceGroupOpsArgException(string resourceGroupName) + { + var client = new AzureResourceManagerClient(); + var subOps = client.DefaultSubscription; + Assert.Throws(delegate { subOps.GetResourceGroupOperations(resourceGroupName); }); + } + + [TestCase("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + public void TestGetResourceGroupOpsOutOfRangeArgException(string resourceGroupName) + { + var client = new AzureResourceManagerClient(); + var subOps = client.DefaultSubscription; + Assert.Throws(delegate { subOps.GetResourceGroupOperations(resourceGroupName); }); + } + + [TestCase("te.st")] + [TestCase("te")] + [TestCase("t")] + [TestCase("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [TestCase("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + public void TestGetResourceGroupOpsValid(string resourceGroupName) + { + var client = new AzureResourceManagerClient(); + var subOps = client.DefaultSubscription; + Assert.DoesNotThrow(delegate { subOps.GetResourceGroupOperations(resourceGroupName); }); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SystemAssignedIdentityTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SystemAssignedIdentityTests.cs new file mode 100644 index 0000000000000..90b7a7c8b9dca --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SystemAssignedIdentityTests.cs @@ -0,0 +1,339 @@ +using Azure.Core.TestFramework; +using NUnit.Framework; +using System; +using System.IO; +using System.Linq; +using System.Text.Json; + +namespace Azure.ResourceManager.Core.Tests +{ + public class SystemAssignedIdentityTests + { + [TestCase(0, null, null, null, null)] + [TestCase(0, "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + + [TestCase(1, "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98", null, null)] + [TestCase(1, "72f988bf-86f1-41af-91ab-2d7cd011db48", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + [TestCase(1, "72f988bf-86f1-41af-91ab-2d7cd011db48", "de29bab1-49e1-4705-819b-4dfddceaaa97", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + [TestCase(1, "72f988bf-86f1-41af-91ab-2d7cd011db48", "de29bab1-49e1-4705-819b-4dfddceaaa99", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + [TestCase(1, "72f988bf-86f1-41af-91ab-2d7cd011eb47", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + + [TestCase(-1, null, null, "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + [TestCase(-1, "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db48", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + [TestCase(-1, "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db48", "de29bab1-49e1-4705-819b-4dfddceaaa99")] + [TestCase(-1, "72f988bf-86f1-41af-91ab-2d7cd011db46", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db48", "de29bab1-49e1-4705-819b-4dfddceaaa97")] + [TestCase(-1, "72f988bf-86f1-41af-91ab-2d7cd011db46", "de29bab1-49e1-4705-819b-4dfdbceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db46", "de29bab1-49e1-4705-819b-4dfddceaaa99")] + public void CompareTo(int answer, string tenantId1, string principalId1, string tenantId2, string principalId2) + { + SystemAssignedIdentity identity1; + SystemAssignedIdentity identity2; + if (tenantId1 == null) + { + identity1 = new SystemAssignedIdentity(); + } + else + { + identity1 = new SystemAssignedIdentity(new Guid(tenantId1), new Guid(principalId1)); + } + + if (tenantId2 == null) + { + identity2 = new SystemAssignedIdentity(); + } + else + { + identity2 = new SystemAssignedIdentity(new Guid(tenantId2), new Guid(principalId2)); + } + + Assert.AreEqual(answer, identity1.CompareTo(identity2)); + Assert.AreEqual(answer * -1, identity2.CompareTo(identity1)); + } + + [TestCase(null, null, null, null)] + [TestCase("72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + public void EqualsMethodTrue(string tenantId1, string principalId1, string tenantId2, string principalId2) + { + SystemAssignedIdentity identity1; + SystemAssignedIdentity identity2; + if (tenantId1 == null) + { + identity1 = new SystemAssignedIdentity(); + } + else + { + identity1 = new SystemAssignedIdentity(new Guid(tenantId1), new Guid(principalId1)); + } + + if (tenantId2 == null) + { + identity2 = new SystemAssignedIdentity(); + } + else + { + identity2 = new SystemAssignedIdentity(new Guid(tenantId2), new Guid(principalId2)); + } + + Assert.IsTrue(identity1.Equals(identity2)); + } + + [TestCase(null, null, "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + [TestCase("72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98", null, null)] + [TestCase("72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db44", "de29bab1-49e1-4705-819b-4dfddceaaa94")] + [TestCase("72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa93", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa91")] + [TestCase("72f988bf-86f1-41af-91ab-2d7cd011db49", "de29bab1-49e1-4705-819b-4dfddceaaa91", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa91")] + public void EqualsMethodFalse(string tenantId1, string principalId1, string tenantId2, string principalId2) + { + SystemAssignedIdentity identity1; + SystemAssignedIdentity identity2; + if (tenantId1 == null) + { + identity1 = new SystemAssignedIdentity(); + } + else + { + identity1 = new SystemAssignedIdentity(new Guid(tenantId1), new Guid(principalId1)); + } + + if (tenantId2 == null) + { + identity2 = new SystemAssignedIdentity(); + } + else + { + identity2 = new SystemAssignedIdentity(new Guid(tenantId2), new Guid(principalId2)); + } + + Assert.IsFalse(identity1.Equals(identity2)); + } + + [TestCase] + public void EqualsMethodBothIdentitiesEmpty() + { + SystemAssignedIdentity identity1 = new SystemAssignedIdentity(); + SystemAssignedIdentity identity2 = new SystemAssignedIdentity(); + Assert.IsTrue(identity1.Equals(identity2)); + } + + [TestCase] + public void EqualsMethodOneIdentityNull() + { + SystemAssignedIdentity identity1 = new SystemAssignedIdentity(); + SystemAssignedIdentity identity2 = null; + Assert.IsFalse(identity1.Equals(identity2)); + } + + [TestCase] + public void CompareToMethodBothIdentitiesEmpty() + { + SystemAssignedIdentity identity1 = new SystemAssignedIdentity(); + SystemAssignedIdentity identity2 = new SystemAssignedIdentity(); + Assert.AreEqual(0, identity1.CompareTo(identity2)); + } + + [TestCase] + public void CompareToMethodOneIdentityNull() + { + SystemAssignedIdentity identity1 = new SystemAssignedIdentity(); + SystemAssignedIdentity identity2 = null; + Assert.AreEqual(1, identity1.CompareTo(identity2)); + } + + public JsonProperty DeserializerHelper(string filename) + { + string json = GetFileText(filename); + JsonDocument document = JsonDocument.Parse(json); + JsonElement rootElement = document.RootElement; + return rootElement.EnumerateObject().First(); + } + + private static string GetFileText(string filename) + { + return File.ReadAllText(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestAssets", "SystemAssignedIdentity", filename)); + } + + [TestCase] + public void TestDeserializerDefaultJson() + { + JsonElement invalid = default(JsonElement); + Assert.Throws(delegate { SystemAssignedIdentity.Deserialize(invalid); }); + } + + [TestCase] + public void TestDeserializerValid() + { + var identityJsonProperty = DeserializerHelper("SystemAssignedValid.json"); + SystemAssignedIdentity back = SystemAssignedIdentity.Deserialize(identityJsonProperty.Value); + Assert.IsTrue("de29bab1-49e1-4705-819b-4dfddceaaa98".Equals(back.PrincipalId.ToString())); + Assert.IsTrue("72f988bf-86f1-41af-91ab-2d7cd011db47".Equals(back.TenantId.ToString())); + } + + [TestCase] + public void TestDeserializerValidExtraField() + { + var json = GetFileText("SystemAssignedValidExtraField.json"); + JsonDocument document = JsonDocument.Parse(json); + JsonElement rootElement = document.RootElement; + var identityJsonProperty = rootElement.EnumerateObject().ElementAt(1); + SystemAssignedIdentity back = SystemAssignedIdentity.Deserialize(identityJsonProperty.Value); + Assert.IsTrue("de29bab1-49e1-4705-819b-4dfddceaaa98".Equals(back.PrincipalId.ToString())); + Assert.IsTrue("72f988bf-86f1-41af-91ab-2d7cd011db47".Equals(back.TenantId.ToString())); + } + + [TestCase] + public void TestDeserializerBothValuesNull() + { + var identityJsonProperty = DeserializerHelper("SystemAssignedBothValuesNull.json"); + var back = SystemAssignedIdentity.Deserialize(identityJsonProperty.Value); + Assert.IsNull(back); + } + + [TestCase] + public void TestDeserializerBothEmptyString() + { + var identityJsonProperty = DeserializerHelper("SystemAssignedBothEmptyString.json"); + Assert.Throws(delegate { SystemAssignedIdentity.Deserialize(identityJsonProperty.Value); }); + } + + [TestCase] + public void TestDeserializerOneEmptyString() + { + var identityJsonProperty = DeserializerHelper("SystemAssignedOneEmptyString.json"); + Assert.Throws(delegate { SystemAssignedIdentity.Deserialize(identityJsonProperty.Value); }); + } + + [TestCase] + public void TestDeserializerTenantIdValueNull() + { + var identityJsonProperty = DeserializerHelper("SystemAssignedOneValueNull.json"); + Assert.Throws(delegate { SystemAssignedIdentity.Deserialize(identityJsonProperty.Value); }); + } + + [TestCase] + public void TestDeserializerPrincipalIdValueNull() + { + var identityJsonProperty = DeserializerHelper("SystemAssignedOneOtherValueNull.json"); + Assert.Throws(delegate { SystemAssignedIdentity.Deserialize(identityJsonProperty.Value); }); + } + + [TestCase] + public void TestDeserializerTenantIdInvalid() + { + var identityJsonProperty = DeserializerHelper("SystemAssignedInvalid.json"); + Assert.Throws(delegate { SystemAssignedIdentity.Deserialize(identityJsonProperty.Value); }); + } + + [TestCase] + public void TestSerializerValidIdentity() + { + SystemAssignedIdentity systemAssignedIdentity = new SystemAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + string actual = ""; + using (Stream stream = new MemoryStream()) + { + using (StreamReader streamReader = new StreamReader(stream)) + { + var writer = new Utf8JsonWriter(stream); + writer.WriteStartObject(); + SystemAssignedIdentity.Serialize(writer, systemAssignedIdentity); + writer.WriteEndObject(); + writer.Flush(); + stream.Seek(0, SeekOrigin.Begin); + actual = streamReader.ReadToEnd(); + } + } + string expected = "{\"principalId\":\"de29bab1-49e1-4705-819b-4dfddceaaa98\",\"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\"}"; + Assert.AreEqual(expected, actual); + } + + [TestCase] + public void TestSerializerEmptyIdentity() + { + SystemAssignedIdentity systemAssignedIdentity = new SystemAssignedIdentity(); + string actual = ""; + using (Stream stream = new MemoryStream()) + { + using (StreamReader streamReader = new StreamReader(stream)) + { + var writer = new Utf8JsonWriter(stream); + writer.WriteStartObject(); + SystemAssignedIdentity.Serialize(writer, systemAssignedIdentity); + writer.WriteEndObject(); + writer.Flush(); + stream.Seek(0, SeekOrigin.Begin); + actual = streamReader.ReadToEnd(); + } + } + string expected = "{\"principalId\":\"null\",\"tenantId\":\"null\"}"; + Assert.AreEqual(expected, actual); + } + + [TestCase] + public void TestSerializerNullIdentity() + { + SystemAssignedIdentity systemAssignedIdentity = null; + using (Stream stream = new MemoryStream()) + { + var writer = new Utf8JsonWriter(stream); + writer.WriteStartObject(); + Assert.Throws(delegate { SystemAssignedIdentity.Serialize(writer, systemAssignedIdentity); }); + } + } + + [TestCase] + public void TestSerializerNullWriter() + { + SystemAssignedIdentity systemAssignedIdentity = new SystemAssignedIdentity(); + using (Stream stream = new MemoryStream()) + { + Assert.Throws(delegate { SystemAssignedIdentity.Serialize(null, systemAssignedIdentity); }); + } + } + + [TestCase] + public void TestEqualsBothIdentitiesNull() + { + SystemAssignedIdentity identity1 = null; + SystemAssignedIdentity identity2 = null; + Assert.IsTrue(SystemAssignedIdentity.Equals(identity1, identity2)); + } + + [TestCase] + public void TestEqualsOneIdentityNull() + { + SystemAssignedIdentity identity1 = null; + SystemAssignedIdentity identity2 = new SystemAssignedIdentity(); + Assert.IsFalse(SystemAssignedIdentity.Equals(identity1, identity2)); + } + + [TestCase] + public void TestEqualsOtherIdentityNull() + { + SystemAssignedIdentity identity1 = new SystemAssignedIdentity(); + SystemAssignedIdentity identity2 = null; + Assert.IsFalse(SystemAssignedIdentity.Equals(identity1, identity2)); + } + + [TestCase] + public void TestEqualsReference() + { + SystemAssignedIdentity identity1 = new SystemAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + SystemAssignedIdentity identity2 = identity1; + Assert.IsTrue(SystemAssignedIdentity.Equals(identity1, identity2)); + } + + [TestCase] + public void TestEqualsTrue() + { + SystemAssignedIdentity identity1 = new SystemAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + SystemAssignedIdentity identity2 = new SystemAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + Assert.IsTrue(SystemAssignedIdentity.Equals(identity1, identity2)); + } + + [TestCase] + public void TestEqualsFalse() + { + SystemAssignedIdentity identity1 = new SystemAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + SystemAssignedIdentity identity2 = new SystemAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db42"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + Assert.IsFalse(SystemAssignedIdentity.Equals(identity1, identity2)); + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TaggableResourceTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TaggableResourceTests.cs new file mode 100644 index 0000000000000..e98ab9a66e415 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TaggableResourceTests.cs @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Identity; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Azure.ResourceManager.Core.Tests +{ + [TestFixture] + public class TaggableResourceTests + { + private static readonly IDictionary UpdateTags = new Dictionary { { "UpdateKey1", "UpdateValue1" }, { "UpdateKey2", "UpdateValue2" } }; + private static readonly IDictionary OriTags = new Dictionary { { "key1", "value1" }, { "key2", "value2" } }; + + private ResourceGroup _rg; + + [SetUp] + public async Task GlobalSetUp() + { + var armClient = new AzureResourceManagerClient(); + _rg = armClient.DefaultSubscription.GetResourceGroupContainer().Construct(LocationData.WestUS2).CreateOrUpdate($"{Environment.UserName}-rg-{Environment.TickCount}").Value; + + await _rg.StartAddTag("key1", "value1").WaitForCompletionAsync(); + await _rg.StartAddTag("key2", "value2").WaitForCompletionAsync(); + } + + [TearDown] + public void GlobalTearDown() + { + _rg.StartDelete(); + } + + [Test] + public void TestSetTagsActivator() + { + var result = _rg.SetTags(UpdateTags); + Assert.AreEqual(result.Value.Data.Tags, UpdateTags); + } + + [Test] + public async Task TestSetTagsAsyncActivator() + { + var result = await _rg.SetTagsAsync(UpdateTags); + Assert.AreEqual(result.Value.Data.Tags, UpdateTags); + } + + [Test] + public void TestStartSetTagsActivator() + { + var result = _rg.StartSetTags(UpdateTags).WaitForCompletionAsync().Result; + Assert.AreEqual(result.Value.Data.Tags, UpdateTags); + } + + [Test] + public async Task TestStartSetTagsAsyncActivator() + { + var result = await _rg.StartSetTagsAsync(UpdateTags); + Assert.AreEqual(result.Value.Data.Tags, UpdateTags); + } + + [TestCaseSource(nameof(TagSource))] + public void TestRemoveTagActivator(string key, IDictionary tags) + { + var result = _rg.RemoveTag(key); + Assert.AreEqual(result.Value.Data.Tags, tags); + } + + [TestCaseSource(nameof(TagSource))] + public async Task TestRemoveTagAsyncActivator(string key, IDictionary tags) + { + var result = await _rg.RemoveTagAsync(key); + Assert.AreEqual(result.Value.Data.Tags, tags); + } + + [TestCaseSource(nameof(TagSource))] + public void TestStartRemoveTagActivator(string key, IDictionary tags) + { + var result = _rg.StartRemoveTag(key).WaitForCompletionAsync().Result; + Assert.AreEqual(result.Value.Data.Tags, tags); + } + + [TestCaseSource(nameof(TagSource))] + public async Task TestStartRemoveTagAsyncActivator(string key, IDictionary tags) + { + var result = await _rg.StartRemoveTagAsync(key); + Assert.AreEqual(result.Value.Data.Tags, tags); + } + + static IEnumerable TagSource() + { + IDictionary OriKey1 = new Dictionary { { "key1", "value1" } }; + IDictionary OriKey2 = new Dictionary { { "key2", "value2" } }; + + return new[] { new object[] {"key1", OriKey2 }, + new object[] {"key2", OriKey1 }, + new object[] {"NullKey", OriTags } + }; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/InvalidType.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/InvalidType.json new file mode 100644 index 0000000000000..88dc49b9af0e7 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/InvalidType.json @@ -0,0 +1,13 @@ +{ + "identity": { + "principalId": "22fddec1-8b9f-49dc-bd72-ddaf8f215577", + "tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47", + "type": "Test", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-tgds-930e-01e2ef9c123c/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": "9a2eaa6a-b49c-4a63-afb5-3b72e3e65422", + "principalId": "77563a98-c9d9-4f7b-a7af-592d21fa2153" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/InvalidTypeIsNull.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/InvalidTypeIsNull.json new file mode 100644 index 0000000000000..2fd197d51a488 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/InvalidTypeIsNull.json @@ -0,0 +1,13 @@ +{ + "identity": { + "principalId": "22fddec1-8a9f-49dc-bd72-ddaf8f215577", + "tenantId": "72f988bf-86f1-4aaf-91ab-2d7cd011db47", + "type": null, + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c1b3c/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": "9a2eaa6a-b4ac-4c63-afb5-3b72e3e65422", + "principalId": "77563a98-c9a9-477b-a7af-592d21fa2153" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedInnerExtraField.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedInnerExtraField.json new file mode 100644 index 0000000000000..ccdfa20449ddc --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedInnerExtraField.json @@ -0,0 +1,14 @@ +{ + "identity": { + "extraId" : "test", + "principalId": "22fddec1-8b9f-49dc-bd72-ddaf8f215577", + "tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47", + "type": "SystemAssigned, UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-9dde-01e2ef9c123c/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": "9a9eaa6a-b49c-4c63-afb5-3b72e3e65422", + "principalId": "77563a98-c9d9-407b-a7af-592d21fa2153" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedMiddleExtraField.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedMiddleExtraField.json new file mode 100644 index 0000000000000..25bd5585dfdf8 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedMiddleExtraField.json @@ -0,0 +1,14 @@ +{ + "identity": { + "principalId": "22fddec1-8b9f-49dc-bd72-ddaf8f215577", + "extraId": "test", + "tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47", + "type": "SystemAssigned, UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-9dde-01e2ef9c123c/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": "9a9eaa6a-b49c-4c63-afb5-3b72e3e65422", + "principalId": "77563a98-c9d9-407b-a7af-592d21fa2153" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedOuterExtraField.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedOuterExtraField.json new file mode 100644 index 0000000000000..9a61e21026640 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedOuterExtraField.json @@ -0,0 +1,14 @@ +{ + "principalId": "test", + "identity": { + "principalId": "22fddec1-8b9f-49dc-bd72-ddaf8f215577", + "tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47", + "type": "SystemAssigned, UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-3466-4b27-930e-01e2ef9c123c/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": "9a2eaa6b-b49c-4c63-afb5-3b72e3e65422", + "principalId": "7756fa98-c9d9-477b-a7af-592d21fa2153" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedValid.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedValid.json new file mode 100644 index 0000000000000..c9207ff65a275 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedValid.json @@ -0,0 +1,13 @@ +{ + "identity": { + "principalId": "22fdaec1-8b9f-49dc-bd72-ddaf8f215577", + "tenantId": "72f988af-86f1-41af-91ab-2d7cd011db47", + "type": "SystemAssigned, UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4aa7-930e-01e2ef9c123c/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": "9a9eaa6a-b49c-4c63-afb5-3b72e3e65422", + "principalId": "77563a98-c9d9-407b-a7af-592d21fa2153" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedValidMultIdentities.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedValidMultIdentities.json new file mode 100644 index 0000000000000..660ab014dee52 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAndUserAssignedValidMultIdentities.json @@ -0,0 +1,17 @@ +{ + "identity": { + "principalId": "22fddec1-8b9f-49dc-bd72-ddaf8f215570", + "tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db40", + "type": "SystemAssigned, UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123z/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity0": { + "clientId": "9a2eaa6a-b49c-4c63-afb5-3b72e3e65422", + "principalId": "77563a98-c9d9-477b-a7af-592d21fa2153" + }, + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9cfrgh/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity1": { + "clientId": "9a2eaa6a-b49c-4c63-afb5-3b72e3c65420", + "principalId": "77563a98-c9d9-477b-a7af-592d2bfa2150" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAssigned.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAssigned.json new file mode 100644 index 0000000000000..fed8ac668cf44 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/SystemAssigned.json @@ -0,0 +1,8 @@ +{ + "identity": { + "principalId": "22fddec1-8b9f-49dc-bd72-ddaf8f215577", + "tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47", + "type": "SystemAssigned", + "userAssignedIdentities": null + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/UserAssigned.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/UserAssigned.json new file mode 100644 index 0000000000000..52a51e50116db --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/Identity/UserAssigned.json @@ -0,0 +1,13 @@ +{ + "identity": { + "principalId": null, + "tenantId": null, + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b2e-930e-01e2ef9c123c/resourceGroups/tester-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": "9a2eaa6a-b49c-4c63-afb5-3b72e3e65422", + "principalId": "77563a98-c9d9-477b-a7af-592d21fa2153" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedBothEmptyString.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedBothEmptyString.json new file mode 100644 index 0000000000000..dfc0a251aeb09 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedBothEmptyString.json @@ -0,0 +1,8 @@ +{ + "identity": { + "principalId": "", + "tenantId": "", + "type": "SystemAssigned", + "userAssignedIdentities": null + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedBothValuesNull.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedBothValuesNull.json new file mode 100644 index 0000000000000..56b0ab1a0c603 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedBothValuesNull.json @@ -0,0 +1,8 @@ +{ + "identity": { + "principalId": null, + "tenantId": null, + "type": "SystemAssigned", + "userAssignedIdentities": null + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedInvalid.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedInvalid.json new file mode 100644 index 0000000000000..bb1568a93322e --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedInvalid.json @@ -0,0 +1,8 @@ +{ + "identity": { + "principalId": "de29bab1-49e1-4705-819b-4dfddceaaa98", + "tenantId": 7, + "type": "SystemAssigned", + "userAssignedIdentities": null + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedOneEmptyString.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedOneEmptyString.json new file mode 100644 index 0000000000000..b348a6fc02d35 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedOneEmptyString.json @@ -0,0 +1,8 @@ +{ + "identity": { + "principalId": "", + "tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47", + "type": "SystemAssigned", + "userAssignedIdentities": null + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedOneOtherValueNull.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedOneOtherValueNull.json new file mode 100644 index 0000000000000..9b5883ae8b68d --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedOneOtherValueNull.json @@ -0,0 +1,8 @@ +{ + "identity": { + "principalId": null, + "tenantId": "de29bab1-49e1-4705-819b-4dfddceaaa98", + "type": "SystemAssigned", + "userAssignedIdentities": null + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedOneValueNull.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedOneValueNull.json new file mode 100644 index 0000000000000..a8edb93db9e50 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedOneValueNull.json @@ -0,0 +1,8 @@ +{ + "identity": { + "principalId": "de29bab1-49e1-4705-819b-4dfddceaaa98", + "tenantId": null, + "type": "SystemAssigned", + "userAssignedIdentities": null + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedValid.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedValid.json new file mode 100644 index 0000000000000..3262885a1d5d1 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedValid.json @@ -0,0 +1,8 @@ +{ + "identity": { + "principalId": "de29bab1-49e1-4705-819b-4dfddceaaa98", + "tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47", + "type": "SystemAssigned", + "userAssignedIdentities": null + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedValidExtraField.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedValidExtraField.json new file mode 100644 index 0000000000000..752dd9b5788aa --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/SystemAssignedIdentity/SystemAssignedValidExtraField.json @@ -0,0 +1,9 @@ +{ + "principalId": "test", + "identity": { + "principalId": "de29bab1-49e1-4705-819b-4dfddceaaa98", + "tenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47", + "type": "SystemAssigned", + "userAssignedIdentities": null + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedBothEmptyString.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedBothEmptyString.json new file mode 100644 index 0000000000000..4129138735a05 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedBothEmptyString.json @@ -0,0 +1,13 @@ +{ + "identity": { + "principalId": null, + "tenantId": null, + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123c/resourceGroups/nbhatia-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": "", + "principalId": "" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedBothValuesNull.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedBothValuesNull.json new file mode 100644 index 0000000000000..d746769930593 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedBothValuesNull.json @@ -0,0 +1,13 @@ +{ + "identity": { + "principalId": null, + "tenantId": null, + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123c/resourceGroups/nbhatia-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": null, + "principalId": null + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedExtraField.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedExtraField.json new file mode 100644 index 0000000000000..a197f59551fcf --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedExtraField.json @@ -0,0 +1,14 @@ +{ + "identity": { + "principalId": null, + "tenantId": null, + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123c/resourceGroups/nbhatia-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "tenantId": "3beb288c-c3f9-4300-896f-02fbf175b7he", + "clientId": "3beb288c-c3f9-4300-896f-02fbf175b6be", + "principalId": "d0416856-d6cf-466d-8d64-ddc8d7782096" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedInvalid.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedInvalid.json new file mode 100644 index 0000000000000..a3c6a3509f798 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedInvalid.json @@ -0,0 +1,13 @@ +{ + "identity": { + "principalId": null, + "tenantId": null, + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123c/resourceGroups/nbhatia-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": 6, + "principalId": "3beb288c-c3f9-4300-896f-02fbf175b6be" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedInvalidMultipleIdentities.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedInvalidMultipleIdentities.json new file mode 100644 index 0000000000000..001a5e7a6d1fe --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedInvalidMultipleIdentities.json @@ -0,0 +1,17 @@ +{ + "identity": { + "principalId": null, + "tenantId": null, + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123c/resourceGroups/nbhatia-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": 7, + "principalId": "d0416856-d6cf-466d-8d64-ddc8d7782096" + }, + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123c/resourceGroups/nbhatia-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity2": { + "clientId": "3beb288c-c3f9-4300-896f-02fbf175b6be", + "principalId": 23 + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedOneEmptyString.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedOneEmptyString.json new file mode 100644 index 0000000000000..1604f0a36c4e8 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedOneEmptyString.json @@ -0,0 +1,13 @@ +{ + "identity": { + "principalId": null, + "tenantId": null, + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123c/resourceGroups/nbhatia-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": "3beb288c-c3f9-4300-896f-02fbf175b6be", + "principalId": "" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedOneOtherValueNull.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedOneOtherValueNull.json new file mode 100644 index 0000000000000..371903d0cd5a7 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedOneOtherValueNull.json @@ -0,0 +1,13 @@ +{ + "identity": { + "principalId": null, + "tenantId": null, + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123c/resourceGroups/nbhatia-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": "3beb288c-c3f9-4300-896f-02fbf175b6be", + "principalId": null + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedOneValueNull.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedOneValueNull.json new file mode 100644 index 0000000000000..1764f6db7b300 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedOneValueNull.json @@ -0,0 +1,13 @@ +{ + "identity": { + "principalId": null, + "tenantId": null, + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123c/resourceGroups/nbhatia-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": null, + "principalId": "3beb288c-c3f9-4300-896f-02fbf175b6be" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedValid.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedValid.json new file mode 100644 index 0000000000000..be76c83688258 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedValid.json @@ -0,0 +1,13 @@ +{ + "identity": { + "principalId": null, + "tenantId": null, + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123c/resourceGroups/nbhatia-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": "3beb288c-c3f9-4300-896f-02fbf175b6be", + "principalId": "d0416856-d6cf-466d-8d64-ddc8d7782096" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedValidMultipleIdentities.json b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedValidMultipleIdentities.json new file mode 100644 index 0000000000000..1c532e9c3ee89 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TestAssets/UserAssignedIdentity/UserAssignedValidMultipleIdentities.json @@ -0,0 +1,17 @@ +{ + "identity": { + "principalId": null, + "tenantId": null, + "type": "UserAssigned", + "userAssignedIdentities": { + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123c/resourceGroups/nbhatia-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity": { + "clientId": "3beb288c-c3f9-4300-896f-02fbf175b6be", + "principalId": "d0416856-d6cf-466d-8d64-ddc8d7782096" + }, + "/subscriptions/db1ab6f0-4769-4b27-930e-01e2ef9c123c/resourceGroups/nbhatia-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity2": { + "clientId": "fbb39377-ff46-4a82-8c47-42d573180482", + "principalId": "6d63ce43-c3ac-4b03-933d-4bc31eae50b2" + } + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/UserAssignedIdentityTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/UserAssignedIdentityTests.cs new file mode 100644 index 0000000000000..c1ac70ce053d1 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/UserAssignedIdentityTests.cs @@ -0,0 +1,315 @@ +using NUnit.Framework; +using System; +using System.IO; +using System.Linq; +using System.Text.Json; + +namespace Azure.ResourceManager.Core.Tests +{ + public class UserAssignedIdentityTests + { + [TestCase(0, "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + + [TestCase(1, "72f988bf-86f1-41af-91ab-2d7cd011db48", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + [TestCase(1, "72f988bf-86f1-41af-91ab-2d7cd011db48", "de29bab1-49e1-4705-819b-4dfddceaaa97", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + [TestCase(1, "72f988bf-86f1-41af-91ab-2d7cd011db48", "de29bab1-49e1-4705-819b-4dfddceaaa99", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + [TestCase(1, "72f988bf-86f1-41af-91ab-2d7cd011eb47", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + + [TestCase(-1, "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db48", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + [TestCase(-1, "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db48", "de29bab1-49e1-4705-819b-4dfddceaaa99")] + [TestCase(-1, "72f988bf-86f1-41af-91ab-2d7cd011db46", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db48", "de29bab1-49e1-4705-819b-4dfddceaaa97")] + [TestCase(-1, "72f988bf-86f1-41af-91ab-2d7cd011db46", "de29bab1-49e1-4705-819b-4dfdbceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db46", "de29bab1-49e1-4705-819b-4dfddceaaa99")] + public void CompareTo(int answer, string clientId1, string principalId1, string clientId2, string principalId2) + { + UserAssignedIdentity identity1 = new UserAssignedIdentity(new Guid(clientId1), new Guid(principalId1)); + UserAssignedIdentity identity2 = new UserAssignedIdentity(new Guid(clientId2), new Guid(principalId2)); + + Assert.AreEqual(answer, identity1.CompareTo(identity2)); + Assert.AreEqual(answer * -1, identity2.CompareTo(identity1)); + } + + [TestCase("72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98")] + public void EqualsMethodTrue(string clientId1, string principalId1, string clientId2, string principalId2) + { + UserAssignedIdentity identity1 = new UserAssignedIdentity(new Guid(clientId1), new Guid(principalId1)); + UserAssignedIdentity identity2 = new UserAssignedIdentity(new Guid(clientId2), new Guid(principalId2)); + Assert.IsTrue(identity1.Equals(identity2)); + } + + [TestCase("72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa98", "72f988bf-86f1-41af-91ab-2d7cd011db44", "de29bab1-49e1-4705-819b-4dfddceaaa94")] + [TestCase("72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa93", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa91")] + [TestCase("72f988bf-86f1-41af-91ab-2d7cd011db49", "de29bab1-49e1-4705-819b-4dfddceaaa91", "72f988bf-86f1-41af-91ab-2d7cd011db47", "de29bab1-49e1-4705-819b-4dfddceaaa91")] + public void EqualsMethodFalse(string clientId1, string principalId1, string clientId2, string principalId2) + { + UserAssignedIdentity identity1 = new UserAssignedIdentity(new Guid(clientId1), new Guid(principalId1)); + UserAssignedIdentity identity2 = new UserAssignedIdentity(new Guid(clientId2), new Guid(principalId2)); + Assert.IsFalse(identity1.Equals(identity2)); + } + + [TestCase] + public void EqualsMethodOneIdentityNull() + { + UserAssignedIdentity identity1 = new UserAssignedIdentity(Guid.Empty, Guid.Empty); + UserAssignedIdentity identity2 = null; + Assert.IsFalse(identity1.Equals(identity2)); + } + + [TestCase] + public void CompareToMethodOneIdentityNull() + { + UserAssignedIdentity identity1 = new UserAssignedIdentity(Guid.Empty, Guid.Empty); + UserAssignedIdentity identity2 = null; + Assert.AreEqual(1, identity1.CompareTo(identity2)); + } + + public JsonElement DeserializerHelper(string filename) + { + var json = GetFileText(filename); + JsonDocument document = JsonDocument.Parse(json); + JsonElement rootElement = document.RootElement; + var properties = rootElement.EnumerateObject().First().Value; + foreach (var property in properties.EnumerateObject()) + { + if (property.NameEquals("userAssignedIdentities")) + { + foreach (var keyValuePair in property.Value.EnumerateObject()) + { + return keyValuePair.Value; + } + } + } + return default(JsonElement); + } + + private static string GetFileText(string filename) + { + return File.ReadAllText(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestAssets", "UserAssignedIdentity", filename)); + } + + [TestCase] + public void TestDeserializerDefaultJson() + { + JsonElement invalid = default(JsonElement); + Assert.Throws(delegate { UserAssignedIdentity.Deserialize(invalid); }); + } + + [TestCase] + public void TestDeserializerValid() + { + var identityJsonProperty = DeserializerHelper("UserAssignedValid.json"); + UserAssignedIdentity back = UserAssignedIdentity.Deserialize(identityJsonProperty); + Assert.IsTrue("3beb288c-c3f9-4300-896f-02fbf175b6be".Equals(back.ClientId.ToString())); + Assert.IsTrue("d0416856-d6cf-466d-8d64-ddc8d7782096".Equals(back.PrincipalId.ToString())); + } + + [TestCase] + public void TestDeserializerValidExtraField() + { + var identityJsonProperty = DeserializerHelper("UserAssignedExtraField.json"); + UserAssignedIdentity back = UserAssignedIdentity.Deserialize(identityJsonProperty); + Assert.IsTrue("3beb288c-c3f9-4300-896f-02fbf175b6be".Equals(back.ClientId.ToString())); + Assert.IsTrue("d0416856-d6cf-466d-8d64-ddc8d7782096".Equals(back.PrincipalId.ToString())); + } + + [TestCase] + public void TestDeserializerBothValuesNull() + { + var identityJsonProperty = DeserializerHelper("UserAssignedBothValuesNull.json"); + var back = UserAssignedIdentity.Deserialize(identityJsonProperty); + Assert.IsNull(back); + } + + [TestCase] + public void TestDeserializerBothEmptyString() + { + var identityJsonProperty = DeserializerHelper("UserAssignedBothEmptyString.json"); + Assert.Throws(delegate { UserAssignedIdentity.Deserialize(identityJsonProperty); }); + } + + [TestCase] + public void TestDeserializerOneEmptyString() + { + var identityJsonProperty = DeserializerHelper("UserAssignedOneEmptyString.json"); + Assert.Throws(delegate { UserAssignedIdentity.Deserialize(identityJsonProperty); }); + } + + [TestCase] + public void TestDeserializerClientIdValueNull() + { + var identityJsonProperty = DeserializerHelper("UserAssignedOneValueNull.json"); + Assert.Throws(delegate { UserAssignedIdentity.Deserialize(identityJsonProperty); }); + } + + [TestCase] + public void TestDeserializerPrincipalIdValueNull() + { + var identityJsonProperty = DeserializerHelper("UserAssignedOneOtherValueNull.json"); + Assert.Throws(delegate { UserAssignedIdentity.Deserialize(identityJsonProperty); }); + } + + [TestCase] + public void TestDeserializerClientIdInvalid() + { + var identityJsonProperty = DeserializerHelper("UserAssignedInvalid.json"); + Assert.Throws(delegate { UserAssignedIdentity.Deserialize(identityJsonProperty); }); + } + + [TestCase] + public void TestDeserializerInvalidMultipleIdentities() + { + var json = GetFileText("UserAssignedInvalidMultipleIdentities.json"); + JsonDocument document = JsonDocument.Parse(json); + var properties = document.RootElement.EnumerateObject().First().Value; + foreach (var property in properties.EnumerateObject()) + { + if (property.NameEquals("userAssignedIdentities")) + { + foreach (var keyValuePair in property.Value.EnumerateObject()) + { + Assert.Throws(delegate { UserAssignedIdentity.Deserialize(keyValuePair.Value); }); + } + } + } + } + + [TestCase] + public void TestDeserializerValidMultipleIdentities() + { + var json = GetFileText("UserAssignedValidMultipleIdentities.json"); + JsonDocument document = JsonDocument.Parse(json); + UserAssignedIdentity[] identities = new UserAssignedIdentity[2]; + var properties = document.RootElement.EnumerateObject().First().Value; + int count = 0; + foreach (var property in properties.EnumerateObject()) + { + if (property.NameEquals("userAssignedIdentities")) + { + foreach (var keyValuePair in property.Value.EnumerateObject()) + { + identities[count] = UserAssignedIdentity.Deserialize(keyValuePair.Value); + count++; + } + } + } + Assert.IsTrue("3beb288c-c3f9-4300-896f-02fbf175b6be".Equals(identities[0].ClientId.ToString())); + Assert.IsTrue("d0416856-d6cf-466d-8d64-ddc8d7782096".Equals(identities[0].PrincipalId.ToString())); + Assert.IsTrue("fbb39377-ff46-4a82-8c47-42d573180482".Equals(identities[1].ClientId.ToString())); + Assert.IsTrue("6d63ce43-c3ac-4b03-933d-4bc31eae50b2".Equals(identities[1].PrincipalId.ToString())); + } + + [TestCase] + public void TestSerializerValidIdentity() + { + UserAssignedIdentity userAssignedIdentity = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + string actual = ""; + using (Stream stream = new MemoryStream()) + { + using (StreamReader streamReader = new StreamReader(stream)) + { + var writer = new Utf8JsonWriter(stream); + UserAssignedIdentity.Serialize(writer, userAssignedIdentity); + stream.Seek(0, SeekOrigin.Begin); + actual = streamReader.ReadToEnd(); + } + } + string expected = "{\"clientId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\",\"principalId\":\"de29bab1-49e1-4705-819b-4dfddceaaa98\"}"; + Assert.AreEqual(expected, actual); + } + + [TestCase] + public void TestSerializerNullIdentity() + { + UserAssignedIdentity userAssignedIdentity = null; + using (Stream stream = new MemoryStream()) + { + var writer = new Utf8JsonWriter(stream); + Assert.Throws(delegate { UserAssignedIdentity.Serialize(writer, userAssignedIdentity); }); + } + } + + [TestCase] + public void TestSerializerNullWriter() + { + UserAssignedIdentity userAssignedIdentity = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + using (Stream stream = new MemoryStream()) + { + Assert.Throws(delegate { UserAssignedIdentity.Serialize(null, userAssignedIdentity); }); + } + } + + [TestCase] + public void TestSerializerArray() + { + UserAssignedIdentity userAssignedIdentity1 = new UserAssignedIdentity(new Guid("3beb288c-c3f9-4300-896f-02fbf175b6be"), new Guid("d0416856-d6cf-466d-8d64-ddc8d7782096")); + UserAssignedIdentity userAssignedIdentity2 = new UserAssignedIdentity(new Guid("fbb39377-ff46-4a82-8c47-42d573180482"), new Guid("6d63ce43-c3ac-4b03-933d-4bc31eae50b2")); + string actual = ""; + UserAssignedIdentity[] identities = { userAssignedIdentity1, userAssignedIdentity2 }; + using (Stream stream = new MemoryStream()) + { + using (StreamReader streamReader = new StreamReader(stream)) + { + foreach (var identity in identities) + { + var writer = new Utf8JsonWriter(stream); + UserAssignedIdentity.Serialize(writer, identity); + stream.Seek(0, SeekOrigin.Begin); + actual = streamReader.ReadToEnd(); + } + } + } + string expected = "{\"clientId\":\"3beb288c-c3f9-4300-896f-02fbf175b6be\"," + + "\"principalId\":\"d0416856-d6cf-466d-8d64-ddc8d7782096\"}" + + "{\"clientId\":\"fbb39377-ff46-4a82-8c47-42d573180482\"," + + "\"principalId\":\"6d63ce43-c3ac-4b03-933d-4bc31eae50b2\"}"; + Assert.AreEqual(expected, actual); + } + + [TestCase] + public void TestEqualsBothIdentitiesNull() + { + UserAssignedIdentity identity1 = null; + UserAssignedIdentity identity2 = null; + Assert.IsTrue(UserAssignedIdentity.Equals(identity1, identity2)); + } + + [TestCase] + public void TestEqualsOneIdentityNull() + { + UserAssignedIdentity identity1 = null; + UserAssignedIdentity identity2 = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + Assert.IsFalse(UserAssignedIdentity.Equals(identity1, identity2)); + } + + [TestCase] + public void TestEqualsOtherIdentityNull() + { + UserAssignedIdentity identity1 = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + UserAssignedIdentity identity2 = null; + Assert.IsFalse(UserAssignedIdentity.Equals(identity1, identity2)); + } + + [TestCase] + public void TestEqualsReference() + { + UserAssignedIdentity identity1 = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + UserAssignedIdentity identity2 = identity1; + Assert.IsTrue(UserAssignedIdentity.Equals(identity1, identity2)); + } + + [TestCase] + public void TestEqualsTrue() + { + UserAssignedIdentity identity1 = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + UserAssignedIdentity identity2 = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + Assert.IsTrue(UserAssignedIdentity.Equals(identity1, identity2)); + } + + [TestCase] + public void TestEqualsFalse() + { + UserAssignedIdentity identity1 = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db47"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + UserAssignedIdentity identity2 = new UserAssignedIdentity(new Guid("72f988bf-86f1-41af-91ab-2d7cd011db42"), new Guid("de29bab1-49e1-4705-819b-4dfddceaaa98")); + Assert.IsFalse(UserAssignedIdentity.Equals(identity1, identity2)); + } + } +} diff --git a/sdk/resourcemanager/ci.yml b/sdk/resourcemanager/ci.yml new file mode 100644 index 0000000000000..68b222242f134 --- /dev/null +++ b/sdk/resourcemanager/ci.yml @@ -0,0 +1,31 @@ +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. + +trigger: + branches: + include: + - master + - hotfix/* + - release/* + paths: + include: + - sdk/resourcemanager/ + +pr: + branches: + include: + - master + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/resourcemanager/ + +extends: + template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + ServiceDirectory: resourcemanager + ArtifactName: packages + Artifacts: + - name: Azure.ResourceManager.Core + safeName: AzureResourceManagerCore From 346dc34ea434a4329380e3c23fa280ec7879db10 Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Tue, 23 Feb 2021 15:01:47 -0800 Subject: [PATCH 02/18] Add readme.md file --- .../Azure.ResourceManager.Core/README.md | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/README.md diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/README.md b/sdk/resourcemanager/Azure.ResourceManager.Core/README.md new file mode 100644 index 0000000000000..2dad581ecc794 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/README.md @@ -0,0 +1,77 @@ +# Azure Resources Management core library for .NET + +This package follows the [new Azure SDK guidelines](https://azure.github.io/azure-sdk/general_introduction.html) which provide a number of core capabilities that are shared amongst all Azure SDKs, including the intuitive Azure Identity library, an HTTP Pipeline with custom policies, error-handling, distributed tracing, and much more. + +## Getting started + +### Install the package + +Install the Azure Resources management core library for .NET with [NuGet](https://www.nuget.org/): + +```PowerShell +Install-Package Azure.ResourceManager.Core -Version 1.0.0-beta.1 +``` + +### Prerequisites + +* You must have an [Azure subscription](https://azure.microsoft.com/free/) + +### Authenticate the Client + +To create an authenticated client and start interacting with Azure resources, please see the [quickstart guide here](https://github.com/Azure/azure-sdk-for-net/blob/master/doc/mgmt_preview_quickstart.md) + +## Key concepts + +Key concepts of the Azure .NET SDK can be found [here](https://azure.github.io/azure-sdk/dotnet_introduction.html) + +## Documentation + +Documentation is available to help you learn how to use this package + +- [Quickstart](https://github.com/Azure/azure-sdk-for-net/blob/master/doc/mgmt_preview_quickstart.md) +- [API References](https://docs.microsoft.com/dotnet/api/?view=azure-dotnet) +- [Authentication](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/identity/Azure.Identity/README.md) + +## Examples + +Code samples for using the management library for .NET can be found in the following locations +- [.NET Management Library Code Samples](https://docs.microsoft.com/samples/browse/?branch=master&languages=csharp&term=managing%20using%20Azure%20.NET%20SDK) + +## Troubleshooting + +- File an issue via [Github + Issues](https://github.com/Azure/azure-sdk-for-net/issues) +- Check [previous + questions](https://stackoverflow.com/questions/tagged/azure+.net) + or ask new ones on Stack Overflow using azure and .net tags. + + +## Next steps + +For more information on Azure SDK, please refer to [this website](https://azure.github.io/azure-sdk/) + +## Contributing + +For details on contributing to this repository, see the contributing +guide. + +This project welcomes contributions and suggestions. 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. For details, visit . + +When you submit a pull request, a CLA-bot will automatically determine +whether you need to provide a CLA and decorate the PR appropriately +(e.g., label, comment). Simply follow the instructions provided by the +bot. You will only need to do this once across all repositories using +our CLA. + +This project has adopted the Microsoft Open Source Code of Conduct. For +more information see the Code of Conduct FAQ or contact + with any additional questions or comments. + + +[style-guide-msft]: https://docs.microsoft.com/style-guide/capitalization +[style-guide-cloud]: https://aka.ms/azsdk/cloud-style-guide + +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-net%2Fsdk%2Ftemplate%2FAzure.Template%2FREADME.png) From 06983029e7538abe7a7a4b43beeb4bf59dac6a56 Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Tue, 23 Feb 2021 15:11:09 -0800 Subject: [PATCH 03/18] Fix header in readme --- sdk/resourcemanager/Azure.ResourceManager.Core/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/README.md b/sdk/resourcemanager/Azure.ResourceManager.Core/README.md index 2dad581ecc794..d3ec87665ccdf 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/README.md +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/README.md @@ -1,4 +1,4 @@ -# Azure Resources Management core library for .NET +# Azure ResourceManager Core client library for .NET This package follows the [new Azure SDK guidelines](https://azure.github.io/azure-sdk/general_introduction.html) which provide a number of core capabilities that are shared amongst all Azure SDKs, including the intuitive Azure Identity library, an HTTP Pipeline with custom policies, error-handling, distributed tracing, and much more. From 706f22ec0f8981d042eee332b5cc02e74be8fabd Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Tue, 23 Feb 2021 16:27:18 -0800 Subject: [PATCH 04/18] remove autorest until we convert off hand written types --- .../src/Azure.ResourceManager.Core.csproj | 1 + .../Azure.ResourceManager.Core/src/autorest.md | 12 ------------ 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/autorest.md diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj index 0718cbf721cb4..364c9d64f576c 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj @@ -9,6 +9,7 @@ azure;management;resource $(NoWarn);AZC0008;AZC0001;AZC0107;CA2214;CA1036;CA1067;CA1065;SA1028 + true diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/autorest.md b/sdk/resourcemanager/Azure.ResourceManager.Core/src/autorest.md deleted file mode 100644 index 3beda43f741b4..0000000000000 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/autorest.md +++ /dev/null @@ -1,12 +0,0 @@ -# Generated code configuration - -Run `dotnet build /t:GenerateCode` to generate code. - -``` yaml -azure-arm: true -title: ResourcesManagementCore -library-name: ResourcesManagementCore -modelerfour: - lenient-model-deduplication: true -``` - From 178af7ae358d87c6eed72d050b250143037dc62e Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Tue, 23 Feb 2021 17:45:25 -0800 Subject: [PATCH 05/18] update azure.core reference remove sets to tags ignore tests which require credentials for now --- eng/Packages.Data.props | 2 +- .../src/Placeholder/ResourceGroupData.cs | 4 -- .../src/ResourceGroupContainer.cs | 8 ++- .../src/ResourceGroupOperations.cs | 70 +++---------------- .../tests/ResourceListOperationsTest.cs | 8 ++- .../tests/SubscriptionOperationsTests.cs | 4 ++ .../tests/TaggableResourceTests.cs | 1 + 7 files changed, 30 insertions(+), 67 deletions(-) diff --git a/eng/Packages.Data.props b/eng/Packages.Data.props index d8abf41aec2e6..c439dc541a144 100644 --- a/eng/Packages.Data.props +++ b/eng/Packages.Data.props @@ -83,7 +83,7 @@ - + diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/ResourceGroupData.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/ResourceGroupData.cs index a92983a05677e..175310efcb454 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/ResourceGroupData.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Placeholder/ResourceGroupData.cs @@ -19,10 +19,6 @@ public class ResourceGroupData : TrackedResource(StringComparer.InvariantCultureIgnoreCase); - } } /// diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupContainer.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupContainer.cs index eb0347c081e7e..cce890cff5766 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupContainer.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupContainer.cs @@ -42,7 +42,13 @@ internal ResourceGroupContainer(SubscriptionOperations subscription) public ArmBuilder Construct(LocationData location, IDictionary tags = default, string managedBy = default) { var model = new ResourceManager.Resources.Models.ResourceGroup(location); - model.Tags = tags; + if (!(tags is null)) + { + foreach (var tag in tags) + { + model.Tags.Add(tag); + } + } model.ManagedBy = managedBy; return new ArmBuilder(this, new ResourceGroupData(model)); } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupOperations.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupOperations.cs index 1988c3db5fe19..0f8b59872b20d 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupOperations.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupOperations.cs @@ -136,12 +136,7 @@ public override async Task> GetAsync(CancellationToke public ArmOperation StartAddTag(string name, string value) { var resource = GetResource(); - var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; - if (object.ReferenceEquals(patch.Tags, null)) - { - patch.Tags = new Dictionary(); - } - + var patch = new ResourceGroupPatchable(); patch.Tags[name] = value; return new PhArmOperation(Operations.Update(Id.Name, patch), g => { @@ -163,12 +158,7 @@ public ArmOperation StartAddTag(string name, string value) public async Task> StartAddTagAsync(string name, string value, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; - if (object.ReferenceEquals(patch.Tags, null)) - { - patch.Tags = new Dictionary(); - } - + var patch = new ResourceGroupPatchable(); patch.Tags[name] = value; return new PhArmOperation(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => { @@ -245,12 +235,7 @@ public Task> CreateResourceAsync SetTags(IDictionary tags) { var resource = GetResource(); - var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; - if (object.ReferenceEquals(patch.Tags, null)) - { - patch.Tags = new Dictionary(); - } - + var patch = new ResourceGroupPatchable(); ReplaceTags(tags, patch.Tags); return new PhArmResponse(Operations.Update(Id.Name, patch), g => { @@ -262,12 +247,7 @@ public ArmResponse SetTags(IDictionary tags) public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; - if (object.ReferenceEquals(patch.Tags, null)) - { - patch.Tags = new Dictionary(); - } - + var patch = new ResourceGroupPatchable(); ReplaceTags(tags, patch.Tags); return new PhArmResponse(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => { @@ -279,12 +259,7 @@ public async Task> SetTagsAsync(IDictionary StartSetTags(IDictionary tags) { var resource = GetResource(); - var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; - if (object.ReferenceEquals(patch.Tags, null)) - { - patch.Tags = new Dictionary(); - } - + var patch = new ResourceGroupPatchable(); ReplaceTags(tags, patch.Tags); return new PhArmOperation(Operations.Update(Id.Name, patch), g => { @@ -296,12 +271,7 @@ public ArmOperation StartSetTags(IDictionary tags public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; - if (object.ReferenceEquals(patch.Tags, null)) - { - patch.Tags = new Dictionary(); - } - + var patch = new ResourceGroupPatchable(); ReplaceTags(tags, patch.Tags); return new PhArmOperation(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => { @@ -313,12 +283,7 @@ public async Task> StartSetTagsAsync(IDictionary RemoveTag(string key) { var resource = GetResource(); - var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; - if (object.ReferenceEquals(patch.Tags, null)) - { - patch.Tags = new Dictionary(); - } - + var patch = new ResourceGroupPatchable(); DeleteTag(key, patch.Tags); return new PhArmResponse(Operations.Update(Id.Name, patch), g => { @@ -330,12 +295,7 @@ public ArmResponse RemoveTag(string key) public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; - if (object.ReferenceEquals(patch.Tags, null)) - { - patch.Tags = new Dictionary(); - } - + var patch = new ResourceGroupPatchable(); DeleteTag(key, patch.Tags); return new PhArmResponse(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => { @@ -347,12 +307,7 @@ public async Task> RemoveTagAsync(string key, Cancell public ArmOperation StartRemoveTag(string key) { var resource = GetResource(); - var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; - if (object.ReferenceEquals(patch.Tags, null)) - { - patch.Tags = new Dictionary(); - } - + var patch = new ResourceGroupPatchable(); DeleteTag(key, patch.Tags); return new PhArmOperation(Operations.Update(Id.Name, patch), g => { @@ -364,12 +319,7 @@ public ArmOperation StartRemoveTag(string key) public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patch = new ResourceGroupPatchable() { Tags = resource.Data.Tags }; - if (object.ReferenceEquals(patch.Tags, null)) - { - patch.Tags = new Dictionary(); - } - + var patch = new ResourceGroupPatchable(); DeleteTag(key, patch.Tags); return new PhArmOperation(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => { diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceListOperationsTest.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceListOperationsTest.cs index 85bbc3ce8bc2c..6e58d141ddea1 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceListOperationsTest.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceListOperationsTest.cs @@ -123,7 +123,13 @@ private static GenericResourceExpanded GetGenereicResource( { var resource = new GenericResourceExpanded(); resource.Location = location; - resource.Tags = tags ?? new Dictionary(); + if (!(tags is null)) + { + foreach (var tag in tags) + { + resource.Tags.Add(tag); + } + } resource.Sku = sku; resource.Plan = plan; resource.Kind = kind; diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs index 452b0192edf15..9489d4925482f 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs @@ -10,6 +10,7 @@ public class SubscriptionOperationsTests { [TestCase(null)] [TestCase("")] + [Ignore("Will remove after ADO 5122")] public void TestGetResourceGroupOpsArgNullException(string resourceGroupName) { var client = new AzureResourceManagerClient(); @@ -22,6 +23,7 @@ public void TestGetResourceGroupOpsArgNullException(string resourceGroupName) [TestCase("te$st")] [TestCase("te#st")] [TestCase("te#st")] + [Ignore("Will remove after ADO 5122")] public void TestGetResourceGroupOpsArgException(string resourceGroupName) { var client = new AzureResourceManagerClient(); @@ -30,6 +32,7 @@ public void TestGetResourceGroupOpsArgException(string resourceGroupName) } [TestCase("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [Ignore("Will remove after ADO 5122")] public void TestGetResourceGroupOpsOutOfRangeArgException(string resourceGroupName) { var client = new AzureResourceManagerClient(); @@ -42,6 +45,7 @@ public void TestGetResourceGroupOpsOutOfRangeArgException(string resourceGroupNa [TestCase("t")] [TestCase("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] [TestCase("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [Ignore("Will remove after ADO 5122")] public void TestGetResourceGroupOpsValid(string resourceGroupName) { var client = new AzureResourceManagerClient(); diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TaggableResourceTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TaggableResourceTests.cs index e98ab9a66e415..94023cf197e68 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TaggableResourceTests.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TaggableResourceTests.cs @@ -10,6 +10,7 @@ namespace Azure.ResourceManager.Core.Tests { [TestFixture] + [Ignore("Will remove after ADO 5122")] public class TaggableResourceTests { private static readonly IDictionary UpdateTags = new Dictionary { { "UpdateKey1", "UpdateValue1" }, { "UpdateKey2", "UpdateValue2" } }; From 903221e19801a5aac6099b8f218ead2ebcaa908e Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Wed, 24 Feb 2021 14:29:07 -0800 Subject: [PATCH 06/18] Add the proto client which will be used until the autogen work is complete --- .../Proto.Client/Proto-Client.sln | 49 + .../Extensions/RoleAssignmentExtensions.cs | 92 + .../RoleAssignmentCreateParameters.cs | 55 + .../Placeholder/RoleAssignmentData.cs | 72 + .../authorization/Proto.Authorization.csproj | 30 + .../authorization/RoleAssignment.cs | 39 + .../authorization/RoleAssignmentContainer.cs | 127 ++ .../authorization/RoleAssignmentOperations.cs | 116 ++ ...re.ResourceManager.Authorization.deps.json | 347 ++++ .../Azure.ResourceManager.Authorization.dll | Bin 0 -> 254976 bytes .../Azure.ResourceManager.Authorization.xml | 1804 +++++++++++++++++ .../Proto.Client/compute/AvailabilitySet.cs | 39 + .../compute/AvailabilitySetContainer.cs | 143 ++ .../compute/AvailabilitySetOperations.cs | 339 ++++ .../compute/ComputeRestApiVersions.cs | 24 + .../Convenience/VirtualMachineModelBuilder.cs | 57 + .../VirtualMachineModelBuilderBase.cs | 50 + .../compute/Extensions/ArmClientExtensions.cs | 46 + ...eResourceManagerClientOptionsExtensions.cs | 20 + .../Extensions/ResourceGroupExtensions.cs | 56 + .../Extensions/SubscriptionExtensions.cs | 117 ++ .../Placeholder/AvailabilitySetData.cs | 74 + .../compute/Placeholder/VirtualMachineData.cs | 180 ++ .../Proto.Client/compute/Proto.Compute.csproj | 19 + .../AvailabilitySetsApiVersions.cs | 38 + .../VirtualMachinesApiVersions.cs | 38 + .../Proto.Client/compute/VirtualMachine.cs | 39 + .../compute/VirtualMachineContainer.cs | 225 ++ .../compute/VirtualMachineOperations.cs | 449 ++++ .../network/Extensions/ArmClientExtensions.cs | 108 + .../Extensions/ResourceGroupExtensions.cs | 103 + .../Extensions/SubscriptionExtensions.cs | 161 ++ .../Proto.Client/network/NetworkInterface.cs | 34 + .../network/NetworkInterfaceContainer.cs | 193 ++ .../network/NetworkInterfaceOperations.cs | 301 +++ .../network/NetworkSecurityGroup.cs | 44 + .../network/NetworkSecurityGroupContainer.cs | 225 ++ .../network/NetworkSecurityGroupOperations.cs | 300 +++ .../Placeholder/NetworkInterfaceData.cs | 130 ++ .../Placeholder/NetworkSecurityGroupData.cs | 65 + .../Placeholder/PublicIPAddressData.cs | 121 ++ .../network/Placeholder/SubnetData.cs | 129 ++ .../network/Placeholder/VirtualNetworkData.cs | 109 + .../Proto.Client/network/Proto.Network.csproj | 20 + .../Proto.Client/network/PublicIpAddress.cs | 39 + .../network/PublicIpAddressContainer.cs | 178 ++ .../network/PublicIpAddressOperations.cs | 295 +++ .../Proto.Client/network/Subnet.cs | 37 + .../Proto.Client/network/SubnetBuilder.cs | 27 + .../Proto.Client/network/SubnetContainer.cs | 118 ++ .../Proto.Client/network/SubnetOperations.cs | 110 + .../Proto.Client/network/VirtualNetwork.cs | 39 + .../network/VirtualNetworkContainer.cs | 177 ++ .../network/VirtualNetworkOperations.cs | 325 +++ .../Proto.Client/src/Common/ScenarioHelper.cs | 10 + .../Proto.Client/src/Program.cs | 35 + .../Proto.Client/src/Proto.Client.csproj | 19 + .../Proto.Client/src/Scenario.cs | 21 + .../Proto.Client/src/ScenarioContext.cs | 30 + .../Proto.Client/src/ScenarioFactory.cs | 55 + .../src/Scenarios/AddTagToGeneric.cs | 30 + .../Proto.Client/src/Scenarios/All.cs | 45 + .../src/Scenarios/CheckResourceExists.cs | 55 + .../src/Scenarios/ClientOverrides.cs | 62 + .../src/Scenarios/CreateMultipleVms.cs | 70 + .../Scenarios/CreateSingleVMCheckLocation.cs | 90 + .../src/Scenarios/CreateSingleVmExample.cs | 54 + .../Scenarios/CreateSingleVmExampleAsync.cs | 58 + .../src/Scenarios/DefaultSubscription.cs | 30 + .../src/Scenarios/DeleteGeneric.cs | 37 + .../src/Scenarios/GenericEntityLoop.cs | 23 + .../src/Scenarios/GetFromOperations.cs | 29 + .../src/Scenarios/GetSubscription.cs | 20 + .../src/Scenarios/GetVMTaskExamples.cs | 54 + .../src/Scenarios/ListByNameExpanded.cs | 133 ++ .../src/Scenarios/NullDataValues.cs | 23 + .../src/Scenarios/RoleAssignment.cs | 68 + .../Proto.Client/src/Scenarios/SetTagsOnVm.cs | 56 + .../src/Scenarios/ShutdownVmsByLINQ.cs | 49 + .../src/Scenarios/ShutdownVmsByName.cs | 28 + .../ShutdownVmsByNameAcrossResourceGroups.cs | 38 + .../ShutdownVmsByNameAcrossSubscriptions.cs | 57 + .../src/Scenarios/ShutdownVmsByTag.cs | 44 + .../Scenarios/StartCreateSingleVmExample.cs | 58 + .../StartCreateSingleVmExampleAsync.cs | 57 + .../Proto.Client/src/Scenarios/StartFromVm.cs | 26 + .../Proto.Client/src/Scenarios/StartStopVm.cs | 25 + .../src/Scenarios/SubscriptionExists.cs | 21 + .../src/Scenarios/UseParentLocation.cs | 60 + .../src/Scenarios/VmModelBuilder.cs | 59 + 90 files changed, 9701 insertions(+) create mode 100644 sdk/resourcemanager/Proto.Client/Proto-Client.sln create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Extensions/RoleAssignmentExtensions.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Placeholder/RoleAssignmentCreateParameters.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Placeholder/RoleAssignmentData.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Proto.Authorization.csproj create mode 100644 sdk/resourcemanager/Proto.Client/authorization/RoleAssignment.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/RoleAssignmentContainer.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/RoleAssignmentOperations.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.deps.json create mode 100644 sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.dll create mode 100644 sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.xml create mode 100644 sdk/resourcemanager/Proto.Client/compute/AvailabilitySet.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/AvailabilitySetContainer.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/AvailabilitySetOperations.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/ComputeRestApiVersions.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/Convenience/VirtualMachineModelBuilder.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/Convenience/VirtualMachineModelBuilderBase.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/Extensions/ArmClientExtensions.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/Extensions/AzureResourceManagerClientOptionsExtensions.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/Extensions/ResourceGroupExtensions.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/Extensions/SubscriptionExtensions.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/Placeholder/AvailabilitySetData.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/Placeholder/VirtualMachineData.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/Proto.Compute.csproj create mode 100644 sdk/resourcemanager/Proto.Client/compute/VersionOverrides/AvailabilitySetsApiVersions.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/VersionOverrides/VirtualMachinesApiVersions.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/VirtualMachine.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/VirtualMachineContainer.cs create mode 100644 sdk/resourcemanager/Proto.Client/compute/VirtualMachineOperations.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/Extensions/ArmClientExtensions.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/Extensions/ResourceGroupExtensions.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/Extensions/SubscriptionExtensions.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/NetworkInterface.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/NetworkInterfaceContainer.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/NetworkInterfaceOperations.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroup.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupContainer.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupOperations.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkInterfaceData.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkSecurityGroupData.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/Placeholder/PublicIPAddressData.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/Placeholder/SubnetData.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/Placeholder/VirtualNetworkData.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/Proto.Network.csproj create mode 100644 sdk/resourcemanager/Proto.Client/network/PublicIpAddress.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/PublicIpAddressContainer.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/PublicIpAddressOperations.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/Subnet.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/SubnetBuilder.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/SubnetContainer.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/SubnetOperations.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/VirtualNetwork.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/VirtualNetworkContainer.cs create mode 100644 sdk/resourcemanager/Proto.Client/network/VirtualNetworkOperations.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Common/ScenarioHelper.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Program.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Proto.Client.csproj create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenario.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/ScenarioContext.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/ScenarioFactory.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/AddTagToGeneric.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/All.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/CheckResourceExists.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/ClientOverrides.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/CreateMultipleVms.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/CreateSingleVMCheckLocation.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/CreateSingleVmExample.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/CreateSingleVmExampleAsync.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/DefaultSubscription.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/DeleteGeneric.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/GenericEntityLoop.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/GetFromOperations.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/GetSubscription.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/GetVMTaskExamples.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/ListByNameExpanded.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/NullDataValues.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/RoleAssignment.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/SetTagsOnVm.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByLINQ.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByName.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByNameAcrossResourceGroups.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByNameAcrossSubscriptions.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByTag.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/StartCreateSingleVmExample.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/StartCreateSingleVmExampleAsync.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/StartFromVm.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/StartStopVm.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/SubscriptionExists.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/UseParentLocation.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/VmModelBuilder.cs diff --git a/sdk/resourcemanager/Proto.Client/Proto-Client.sln b/sdk/resourcemanager/Proto.Client/Proto-Client.sln new file mode 100644 index 0000000000000..fab7da5acb646 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/Proto-Client.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31019.35 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.ResourceManager.Core", "..\Azure.ResourceManager.Core\src\Azure.ResourceManager.Core.csproj", "{AEBBB0DB-BA2F-42A2-85DD-0019EE64492B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Proto.Compute", "compute\Proto.Compute.csproj", "{8948E020-7C45-4817-9142-D80368F3FB11}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Proto.Network", "network\Proto.Network.csproj", "{AD152BEB-7AE0-42A1-873E-E2AB4519ADA7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Proto.Client", "src\Proto.Client.csproj", "{280DD3C4-7EC6-471A-BB77-1E7F2F7D6666}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Proto.Authorization", "authorization\Proto.Authorization.csproj", "{540E8EF4-40B6-4F23-8744-2E6705186E7B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AEBBB0DB-BA2F-42A2-85DD-0019EE64492B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEBBB0DB-BA2F-42A2-85DD-0019EE64492B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEBBB0DB-BA2F-42A2-85DD-0019EE64492B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEBBB0DB-BA2F-42A2-85DD-0019EE64492B}.Release|Any CPU.Build.0 = Release|Any CPU + {8948E020-7C45-4817-9142-D80368F3FB11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8948E020-7C45-4817-9142-D80368F3FB11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8948E020-7C45-4817-9142-D80368F3FB11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8948E020-7C45-4817-9142-D80368F3FB11}.Release|Any CPU.Build.0 = Release|Any CPU + {AD152BEB-7AE0-42A1-873E-E2AB4519ADA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD152BEB-7AE0-42A1-873E-E2AB4519ADA7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD152BEB-7AE0-42A1-873E-E2AB4519ADA7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD152BEB-7AE0-42A1-873E-E2AB4519ADA7}.Release|Any CPU.Build.0 = Release|Any CPU + {280DD3C4-7EC6-471A-BB77-1E7F2F7D6666}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {280DD3C4-7EC6-471A-BB77-1E7F2F7D6666}.Debug|Any CPU.Build.0 = Debug|Any CPU + {280DD3C4-7EC6-471A-BB77-1E7F2F7D6666}.Release|Any CPU.ActiveCfg = Release|Any CPU + {280DD3C4-7EC6-471A-BB77-1E7F2F7D6666}.Release|Any CPU.Build.0 = Release|Any CPU + {540E8EF4-40B6-4F23-8744-2E6705186E7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {540E8EF4-40B6-4F23-8744-2E6705186E7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {540E8EF4-40B6-4F23-8744-2E6705186E7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {540E8EF4-40B6-4F23-8744-2E6705186E7B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6C3A85E2-78AD-4C82-8943-A495B3B42533} + EndGlobalSection +EndGlobal diff --git a/sdk/resourcemanager/Proto.Client/authorization/Extensions/RoleAssignmentExtensions.cs b/sdk/resourcemanager/Proto.Client/authorization/Extensions/RoleAssignmentExtensions.cs new file mode 100644 index 0000000000000..b8009caf3628b --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Extensions/RoleAssignmentExtensions.cs @@ -0,0 +1,92 @@ +using Azure.ResourceManager.Core; + +namespace Proto.Authorization +{ + /// + /// Extensions for RoleAssignment Containers and Operations + /// + public static class RoleAssignmentExtensions + { + /// + /// Get RoleAssignment Container for the given resource. Note that this is only valid for unconstrained role assignments, so + /// it is a generation-time decision if we include this. + /// + /// The resource that is the target of the roel assignemnt + /// A that allows creating and listing RoleAssignments + public static RoleAssignmentContainer GetRoleAssignmentContainer(this ResourceOperationsBase resource) + { + return new RoleAssignmentContainer(resource); + } + + /// + /// Get RoleAssignment Container for the given resource. Note that this is only valid for unconstrained role assignments, so + /// it is a generation-time decision if we include this. + /// + /// The subscription that is the target of the role assignemnt + /// A that allows creating and listing RoleAssignments + public static RoleAssignmentContainer GetRoleAssignmentContainer(this SubscriptionOperations resource) + { + return new RoleAssignmentContainer(resource); + } + + /// + /// Get RoleAssignment Container for the given resource and scope. Note that this is only valid for unconstrained role assignments, so + /// it is a generation-time decision if we include this. + /// + /// The subscription containign the role assignment + /// The target of the role assignment + /// A that allows creating and listing RoleAssignments + public static RoleAssignmentContainer GetRoleAssigmentContainerAtScope(this SubscriptionOperations subscription, ResourceIdentifier scope) + { + return new RoleAssignmentContainer(subscription, scope); + } + + /// + /// Get RoleAssignment Container for the given resource and scope. Note that this is only valid for unconstrained role assignments, so + /// it is a generation-time decision if we include this. + /// + /// The subscription containign the role assignment + /// The target of the role assignment + /// A that allows creating and listing RoleAssignments + public static RoleAssignmentContainer GetRoleAssigmentContainerAtScope(this SubscriptionOperations subscription, Resource scope) + { + return new RoleAssignmentContainer(subscription, scope.Id); + } + + /// + /// Get RoleAssignment Operations for the given resource and scope. Note that this is only valid for unconstrained role assignments, so + /// it is a generation-time decision if we include this. + /// + /// The resource containing the role assignment + /// The name of the role assignment + /// A that allows getting and deleting RoleAssignments + public static RoleAssignmentOperations GetRoleAssignmentOperations(this ResourceOperationsBase resource, string name) + { + return new RoleAssignmentOperations(resource, $"{resource.Id}/providers/Microsoft.Authorization/roleAssignments/{name}"); + } + + /// + /// Get RoleAssignment Operations for the given resource and scope. Note that this is only valid for unconstrained role assignments, so + /// it is a generation-time decision if we include this. + /// + /// The subscription containing the role assignment + /// The name of the role assignment + /// A that allows getting and deleting RoleAssignments + public static RoleAssignmentOperations GetRoleAssignmentOperations(this SubscriptionOperations resource, string name) + { + return new RoleAssignmentOperations(resource, $"{resource.Id}/providers/Microsoft.Authorization/roleAssignments/{name}"); + } + + /// + /// Get RoleAssignment Operations for the given resource and scope. Note that this is only valid for unconstrained role assignments, so + /// it is a generation-time decision if we include this. + /// + /// The subscription containing the role assignment + /// The id of the role assignment + /// A that allows getting and deleting RoleAssignments + public static RoleAssignmentOperations GetRoleAssignmentOperationsAtScope(this SubscriptionOperations resource, ResourceIdentifier resourceId) + { + return new RoleAssignmentOperations(resource, resourceId); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Placeholder/RoleAssignmentCreateParameters.cs b/sdk/resourcemanager/Proto.Client/authorization/Placeholder/RoleAssignmentCreateParameters.cs new file mode 100644 index 0000000000000..d702b00b93e2e --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Placeholder/RoleAssignmentCreateParameters.cs @@ -0,0 +1,55 @@ +using Azure.ResourceManager.Authorization.Models; +using Azure.ResourceManager.Core; + +namespace Proto.Authorization +{ + /// + /// Creation properties for RoleAssignments + /// + public class RoleAssignmentCreateParameters + { + /// + /// Initializes a new instance of the class. + /// + /// The granted permissiosn for this assignment + /// The principal id for this assignment + public RoleAssignmentCreateParameters(ResourceIdentifier roleDefinitionId, string principalId) + { + RoleDefinitionId = roleDefinitionId; + PrincipalId = principalId; + PrincipalType = Azure.ResourceManager.Authorization.Models.PrincipalType.ServicePrincipal; + } + + /// + /// Gets the identifier of the role definition used in the assignment + /// + public ResourceIdentifier RoleDefinitionId { get; } + + /// + /// Gets the Object ID of the principal used in the assignment + /// + public string PrincipalId { get; } + + /// + /// Gets or sets the type of the principal used in the assignment + /// + public PrincipalType? PrincipalType { get; set; } + + /// + /// Gets or sets the data indicating whether the principal can delegate privileges + /// + public bool? CanDelegate { get; set; } + + /// + /// Return the underlying serialization model + /// + /// The serialization model for the role assignemnt + public Azure.ResourceManager.Authorization.Models.RoleAssignmentCreateParameters ToModel() + { + var model = new Azure.ResourceManager.Authorization.Models.RoleAssignmentCreateParameters(RoleDefinitionId, PrincipalId); + model.PrincipalType = PrincipalType; + model.CanDelegate = CanDelegate; + return model; + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Placeholder/RoleAssignmentData.cs b/sdk/resourcemanager/Proto.Client/authorization/Placeholder/RoleAssignmentData.cs new file mode 100644 index 0000000000000..d5741c69fc315 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Placeholder/RoleAssignmentData.cs @@ -0,0 +1,72 @@ +using Azure.ResourceManager.Authorization.Models; +using Azure.ResourceManager.Core; + +namespace Proto.Authorization +{ + /// + /// Placholder class containing Role assignment POCO properties. + /// + public class RoleAssignmentData : Resource + { + private Azure.ResourceManager.Authorization.Models.RoleAssignment _model; + + /// + /// Initializes a new instance of the class. + /// + /// The Track2 management plane assignment. + public RoleAssignmentData(Azure.ResourceManager.Authorization.Models.RoleAssignment assign) + { + _model = assign; + Id = assign.Id; + Scope = assign.Scope; + RoleDefinitionId = assign.RoleDefinitionId; + PrincipalId = assign.PrincipalId; + PrincipalType = assign.PrincipalType; + CanDelegate = assign.CanDelegate; + } + + /// + /// Gets the resource type of thsi resource. + /// + public static Azure.ResourceManager.Core.ResourceType ResourceType => "Microsoft.Authorization/roleAssignments"; + + /// + /// Gets the target of this role assignment. + /// + public string Scope { get; } + + /// + /// Gets the role definition id for this role assignment - determines the permissions allowed by this assignment. + /// + public ResourceIdentifier RoleDefinitionId { get; } + + /// + /// Gets the ActiveDirectory principal that is assigned privileges to the target by this assignemnt. + /// + public string PrincipalId { get; } + + /// + /// Gets the type of the principal associated with this assignment. + /// + public PrincipalType? PrincipalType { get; } + + /// + /// Gets the value determining whether the principal can delegate its permissions. + /// + public bool? CanDelegate { get; } + + /// + /// Gets or sets the identifier of the RoleAssignment. + /// + public override ResourceIdentifier Id { get; protected set; } + + /// + /// Gets the Track2 Management model associated with the data object. + /// + /// The Track2 Role Assignment, for serialization. + public Azure.ResourceManager.Authorization.Models.RoleAssignment ToModel() + { + return _model; + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Proto.Authorization.csproj b/sdk/resourcemanager/Proto.Client/authorization/Proto.Authorization.csproj new file mode 100644 index 0000000000000..0f2bd6bccefe7 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Proto.Authorization.csproj @@ -0,0 +1,30 @@ + + + + netstandard2.0 + Proto.Authorization + latest + ..\stylecop.ruleset + obj\docs.xml + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + ..\lib\Azure.ResourceManager.Authorization.dll + + + + + diff --git a/sdk/resourcemanager/Proto.Client/authorization/RoleAssignment.cs b/sdk/resourcemanager/Proto.Client/authorization/RoleAssignment.cs new file mode 100644 index 0000000000000..a18da72944083 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/RoleAssignment.cs @@ -0,0 +1,39 @@ +using System.Threading.Tasks; +using Azure.ResourceManager.Core; + +namespace Proto.Authorization +{ + /// + /// A Role Assignment for Role-based access control in ARM + /// + public class RoleAssignment : RoleAssignmentOperations + { + /// + /// Initializes a new instance of the class. + /// + /// The operations class to copy the http settings from. + /// The properties of the resource. + internal RoleAssignment(OperationsBase operations, RoleAssignmentData data) + : base(operations, data?.Id) + { + Data = data; + } + + /// + /// Gets the properties of the RoleAssignment. + /// + public RoleAssignmentData Data { get; } + + /// + protected override RoleAssignment GetResource() + { + return this; + } + + /// + protected override Task GetResourceAsync() + { + return Task.FromResult(this); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/RoleAssignmentContainer.cs b/sdk/resourcemanager/Proto.Client/authorization/RoleAssignmentContainer.cs new file mode 100644 index 0000000000000..c0a17bef35862 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/RoleAssignmentContainer.cs @@ -0,0 +1,127 @@ +using Azure.ResourceManager.Authorization; +using Azure.ResourceManager.Core; +using System.Threading; +using System.Threading.Tasks; + +namespace Proto.Authorization +{ + /// + /// Container for role assignments - note that in this case, the container is either a TrackedResource or a resource Id + /// + public class RoleAssignmentContainer : ExtensionResourceContainer + { + /// + /// Initializes a new instance of the class. + /// + /// A generic operations class representing the parent of the role Assignment. + internal RoleAssignmentContainer(OperationsBase operations) + : base(operations) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client options with http client details for these operations. + /// The resource id of the target resource, resource group, or subscription for this role assignment. + internal RoleAssignmentContainer(OperationsBase operations, ResourceIdentifier scope) + : base(operations, scope) + { + } + + /// + protected override ResourceType ValidResourceType => RoleAssignmentOperations.ResourceType; + + /// + /// Gets the resource type of the resource being created. + /// + private RoleAssignmentsOperations Operations => new AuthorizationManagementClient(Id.Subscription, BaseUri, Credential).RoleAssignments; + + /// + /// Create a role assignment. This method blocks until the RoleAssignment is created on the service. + /// + /// The name of the role assignment. + /// The properties of the role assignment. + /// A token that allows cancellation of any blockign API calls made during this method. + /// The created role assignment. + public override ArmResponse Create(string name, RoleAssignmentCreateParameters resourceDetails, CancellationToken cancellationToken = default) + { + var response = Operations.Create(Id, name, resourceDetails.ToModel(), cancellationToken); + return new PhArmResponse( + response, + a => new RoleAssignment(this, new RoleAssignmentData(a))); + } + + /// + /// Create a role assignment. This method returns a task performs the creation. The task may make multiple blocking calls. + /// When complete the task yields the created RoleAssignment. + /// + /// The name of the role assignment. + /// The properties of the role assignment. + /// A token that allows cancellation of any blockign API calls made during this method. + /// A Task that yields the created role assignment when complete. + public async override Task> CreateAsync(string name, RoleAssignmentCreateParameters resourceDetails, CancellationToken cancellationToken = default) + { + var response = await Operations.CreateAsync(Id, name, resourceDetails.ToModel(), cancellationToken).ConfigureAwait(false); + return new PhArmResponse( + response, + a => new RoleAssignment(this, new RoleAssignmentData(a))); + } + + /// + /// Create a role assignment. This method blocks until the RoleAssignment creation is accepted on the service. It returns an + /// allowing the caller to control polling and waiting for the creation to complete. + /// + /// The name of the role assignment. + /// The properties of the role assignment. + /// A token that allows cancellation of any blocking API calls made during this method. + /// An ArmOperation that yields the created role assignment and gives the user control over polling. + public override ArmOperation StartCreate(string name, RoleAssignmentCreateParameters resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + Operations.Create(Id, name, resourceDetails.ToModel(), cancellationToken), + a => new RoleAssignment(this, new RoleAssignmentData(a))); + } + + /// + /// Create a role assignment. This method blocks until the RoleAssignment creation is accepted on the service. It returns an + /// allowing the caller to control polling and waiting for the creation to complete. + /// + /// The name of the role assignment. + /// The properties of the role assignment. + /// A token that allows cancellation of any blocking API calls made during this method. + /// A that yields the created role assignment and gives the user control over polling. + public async override Task> StartCreateAsync(string name, RoleAssignmentCreateParameters resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + await Operations.CreateAsync(Id, name, resourceDetails.ToModel(), cancellationToken).ConfigureAwait(false), + a => new RoleAssignment(this, new RoleAssignmentData(a))); + } + + /// + /// List all role assignments at this scope. This call blocks until the first page or results is returned from the service. + /// + /// A token that allows cancellation of any blocking API calls made during this method. + /// A that allows paged enumeration of the role assignments at this scope. + public override Azure.Pageable ListAtScope(CancellationToken cancellationToken = default) + { + throw new System.NotImplementedException(); + } + + /// + /// List all role assignments at this scope. + /// + /// A token that allows cancellation of any blocking API calls made during this method. + /// A that allows asynchronous paged enumeration of the role assignments at this scope. + public override Azure.AsyncPageable ListAtScopeAsync(CancellationToken cancellationToken = default) + { + throw new System.NotImplementedException(); + } + + /// + protected override void Validate(ResourceIdentifier identifier) + { + return; + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/RoleAssignmentOperations.cs b/sdk/resourcemanager/Proto.Client/authorization/RoleAssignmentOperations.cs new file mode 100644 index 0000000000000..bf94bb0852a2e --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/RoleAssignmentOperations.cs @@ -0,0 +1,116 @@ +using Azure; +using Azure.ResourceManager.Authorization; +using Azure.ResourceManager.Core; +using System.Threading; +using System.Threading.Tasks; + +namespace Proto.Authorization +{ + /// + /// Operations over Role Assignments for Role-based access control to ARM resources + /// + public class RoleAssignmentOperations : ExtensionResourceOperationsBase, IDeletableResource + { + /// + /// Gets the resource type for Role Assignments + /// + public static readonly ResourceType ResourceType = "Microsoft.Authorization/roleAssignments"; + + /// + /// Initializes a new instance of the class. + /// Allows creating operations specific to a role assignment from generic ARM operations for the same resource + /// + /// A generic operations class corresponding to a Role Assignment. + internal RoleAssignmentOperations(GenericResourceOperations genericOperations) + : base(genericOperations) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The http settings to use with these operations. + /// The resource identifier for the RoleAssignment to operate on. + internal RoleAssignmentOperations(OperationsBase operation, ResourceIdentifier id) + : base(operation, id) + { + } + + /// + protected override ResourceType ValidResourceType => ResourceType; + + private RoleAssignmentsOperations Operations => new AuthorizationManagementClient( + Id.Subscription, + BaseUri, + Credential).RoleAssignments; + + /// + /// Delete a role assignment. This operation may involve multiple blocking calls to the service. + /// The operation returns when deletion is complete on the service. + /// + /// The http response returned from the server. + public ArmResponse Delete() + { + return new ArmResponse(Operations.DeleteById(Id).GetRawResponse()); + } + + /// + /// Delete a role assignment. This operation creates a Task to perform and monitor deletion of the role assignment. + /// The task may perform multiple blocking calls, the provided can be + /// used to cancel any of these calls. + /// + /// A token allowing the user to cancel the REST API call. + /// A Task that will yield the http response from the server to the delete request once the Task is completed. + public async Task> DeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmResponse((await Operations.DeleteByIdAsync(Id)).GetRawResponse()); + } + + /// + /// Delete a Role Assignment. This call blocks until the initial response is returned from the service. + /// + /// A token allowing the user to cancel the REST API call. + /// An that allows the user to control how to wait and poll + /// for the delete operation to complete. + public ArmOperation StartDelete(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(Operations.DeleteById(Id, cancellationToken).GetRawResponse()); + } + + /// + /// Delete a Role Assignment. This call returns a Task that blocks until the initial response is returned from the service. + /// The task yields an that allows the user to control how to wait and poll for the + /// delete operation on the service to complete + /// + /// A token allowing the user to cancel the REST API call. + /// A . The task yields an Operation that allows the caller to control how to + /// wait and poll for operation completion on the service. + public async Task> StartDeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation((await Operations.DeleteByIdAsync(Id, cancellationToken)).GetRawResponse()); + } + + /// + /// Gets the RoleAssignment. This call will block until a response is returned from the servcie. + /// + /// A , the http response from the service. + public override ArmResponse Get() + { + return new PhArmResponse( + Operations.GetById(Id), a => new RoleAssignment(this, new RoleAssignmentData(a))); + } + + /// + /// Get the role assignment. This call returns a . When complete, the Task yields the + /// + /// A token allowing the user to cancel the REST API call. + /// A that performs the Get operation. The Task yields a + /// when complete. + public async override Task> GetAsync(CancellationToken cancellationToken = default) + { + return new PhArmResponse( + await Operations.GetByIdAsync(Id, cancellationToken), + a => new RoleAssignment(this, new RoleAssignmentData(a))); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.deps.json b/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.deps.json new file mode 100644 index 0000000000000..9156794a2e23f --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.deps.json @@ -0,0 +1,347 @@ +{ + "runtimeTarget": { + "name": ".NETStandard,Version=v2.0/", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETStandard,Version=v2.0": {}, + ".NETStandard,Version=v2.0/": { + "Azure.ResourceManager.Authorization/1.0.0-alpha.20201115.1": { + "dependencies": { + "AutoRest.CSharp.V3": "1.0.0-alpha.20201013.3", + "Azure.ClientSdk.Analyzers": "0.1.1-dev.20200929.2", + "Azure.Core": "1.3.0", + "Microsoft.CodeAnalysis.FxCopAnalyzers": "2.6.2", + "Microsoft.DotNet.GenAPI": "5.0.0-beta.19552.1", + "Microsoft.SourceLink.GitHub": "1.0.0", + "NETStandard.Library": "2.0.3", + "StyleCop.Analyzers": "1.1.118", + "System.Text.Json": "4.6.0" + }, + "runtime": { + "Azure.ResourceManager.Authorization.dll": {} + } + }, + "AutoRest.CSharp.V3/1.0.0-alpha.20201013.3": {}, + "Azure.ClientSdk.Analyzers/0.1.1-dev.20200929.2": {}, + "Azure.Core/1.3.0": { + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.0.0", + "System.Buffers": "4.5.0", + "System.Diagnostics.DiagnosticSource": "4.6.0", + "System.Memory": "4.5.3", + "System.Numerics.Vectors": "4.5.0", + "System.Threading.Tasks.Extensions": "4.5.2" + }, + "runtime": { + "lib/netstandard2.0/Azure.Core.dll": { + "assemblyVersion": "1.3.0.0", + "fileVersion": "1.300.20.35202" + } + } + }, + "Microsoft.Bcl.AsyncInterfaces/1.0.0": { + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.2" + }, + "runtime": { + "lib/netstandard2.0/Microsoft.Bcl.AsyncInterfaces.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "4.700.19.46214" + } + } + }, + "Microsoft.Build.Tasks.Git/1.0.0": {}, + "Microsoft.CodeAnalysis.FxCopAnalyzers/2.6.2": { + "dependencies": { + "Microsoft.CodeQuality.Analyzers": "2.6.2", + "Microsoft.NetCore.Analyzers": "2.6.2", + "Microsoft.NetFramework.Analyzers": "2.6.2", + "Text.Analyzers": "2.6.2" + } + }, + "Microsoft.CodeQuality.Analyzers/2.6.2": {}, + "Microsoft.DotNet.GenAPI/5.0.0-beta.19552.1": {}, + "Microsoft.NetCore.Analyzers/2.6.2": {}, + "Microsoft.NETCore.Platforms/1.1.0": {}, + "Microsoft.NetFramework.Analyzers/2.6.2": {}, + "Microsoft.SourceLink.Common/1.0.0": {}, + "Microsoft.SourceLink.GitHub/1.0.0": { + "dependencies": { + "Microsoft.Build.Tasks.Git": "1.0.0", + "Microsoft.SourceLink.Common": "1.0.0" + } + }, + "NETStandard.Library/2.0.3": { + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "StyleCop.Analyzers/1.1.118": {}, + "System.Buffers/4.5.0": { + "runtime": { + "lib/netstandard2.0/System.Buffers.dll": { + "assemblyVersion": "4.0.3.0", + "fileVersion": "4.6.26515.6" + } + } + }, + "System.Diagnostics.DiagnosticSource/4.6.0": { + "dependencies": { + "System.Memory": "4.5.3" + }, + "runtime": { + "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll": { + "assemblyVersion": "4.0.4.0", + "fileVersion": "4.700.19.46214" + } + } + }, + "System.Memory/4.5.3": { + "dependencies": { + "System.Buffers": "4.5.0", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.6.0" + }, + "runtime": { + "lib/netstandard2.0/System.Memory.dll": { + "assemblyVersion": "4.0.1.1", + "fileVersion": "4.6.27617.2" + } + } + }, + "System.Numerics.Vectors/4.5.0": { + "runtime": { + "lib/netstandard2.0/System.Numerics.Vectors.dll": { + "assemblyVersion": "4.1.4.0", + "fileVersion": "4.6.26515.6" + } + } + }, + "System.Runtime.CompilerServices.Unsafe/4.6.0": { + "runtime": { + "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": { + "assemblyVersion": "4.0.5.0", + "fileVersion": "4.700.19.46214" + } + } + }, + "System.Text.Encodings.Web/4.6.0": { + "dependencies": { + "System.Memory": "4.5.3" + }, + "runtime": { + "lib/netstandard2.0/System.Text.Encodings.Web.dll": { + "assemblyVersion": "4.0.4.0", + "fileVersion": "4.700.19.46214" + } + } + }, + "System.Text.Json/4.6.0": { + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.0.0", + "System.Buffers": "4.5.0", + "System.Memory": "4.5.3", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.6.0", + "System.Text.Encodings.Web": "4.6.0", + "System.Threading.Tasks.Extensions": "4.5.2" + }, + "runtime": { + "lib/netstandard2.0/System.Text.Json.dll": { + "assemblyVersion": "4.0.0.0", + "fileVersion": "4.700.19.46214" + } + } + }, + "System.Threading.Tasks.Extensions/4.5.2": { + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.6.0" + }, + "runtime": { + "lib/netstandard2.0/System.Threading.Tasks.Extensions.dll": { + "assemblyVersion": "4.2.0.0", + "fileVersion": "4.6.27129.4" + } + } + }, + "Text.Analyzers/2.6.2": {} + } + }, + "libraries": { + "Azure.ResourceManager.Authorization/1.0.0-alpha.20201115.1": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "AutoRest.CSharp.V3/1.0.0-alpha.20201013.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-cDkrad9n4HGxYslwAHeaKzZoMCvjY1r3VNU+ow+1njE9P5WPzzQ632NLaXw3OKhyRyAMYZYLlxlWsRQdvKyk2g==", + "path": "autorest.csharp.v3/1.0.0-alpha.20201013.3", + "hashPath": "autorest.csharp.v3.1.0.0-alpha.20201013.3.nupkg.sha512" + }, + "Azure.ClientSdk.Analyzers/0.1.1-dev.20200929.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-lfQaBxMYPEIVud0H8ieaJDpwQR/NhUNC9QUW7rJ0NoifkTE2pbay4I0Ujfk50B7FTynalk1NGycCCOuKbAJzqA==", + "path": "azure.clientsdk.analyzers/0.1.1-dev.20200929.2", + "hashPath": "azure.clientsdk.analyzers.0.1.1-dev.20200929.2.nupkg.sha512" + }, + "Azure.Core/1.3.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-OJLa0kwBYI3wH0g7xhkLoShUwmOCx5LkEVB2lWXqqTtQAklIKLs2znuVyFpqDICE9vJhvA5aVQHhbSz9+Hv+LQ==", + "path": "azure.core/1.3.0", + "hashPath": "azure.core.1.3.0.nupkg.sha512" + }, + "Microsoft.Bcl.AsyncInterfaces/1.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-K63Y4hORbBcKLWH5wnKgzyn7TOfYzevIEwIedQHBIkmkEBA9SCqgvom+XTuE+fAFGvINGkhFItaZ2dvMGdT5iw==", + "path": "microsoft.bcl.asyncinterfaces/1.0.0", + "hashPath": "microsoft.bcl.asyncinterfaces.1.0.0.nupkg.sha512" + }, + "Microsoft.Build.Tasks.Git/1.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-z2fpmmt+1Jfl+ZnBki9nSP08S1/tbEOxFdsK1rSR+LBehIJz1Xv9/6qOOoGNqlwnAGGVGis1Oj6S8Kt9COEYlQ==", + "path": "microsoft.build.tasks.git/1.0.0", + "hashPath": "microsoft.build.tasks.git.1.0.0.nupkg.sha512" + }, + "Microsoft.CodeAnalysis.FxCopAnalyzers/2.6.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-fub9nWKI08wCmC9hnARCCYDLEd+aAs/aqhAQ27BmaP2fq3Jh8WSgNFLNInic+9lXQmibU6r7SSKbMroh3aM3ig==", + "path": "microsoft.codeanalysis.fxcopanalyzers/2.6.2", + "hashPath": "microsoft.codeanalysis.fxcopanalyzers.2.6.2.nupkg.sha512" + }, + "Microsoft.CodeQuality.Analyzers/2.6.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-HEH7nPissPcvMhx0MOXXaARkmzkEP7hDFBz3o06ZDj3rpE+F0e5hVoGJDr+qPsbJaeDoAlbArzTYfGsbZPSlPA==", + "path": "microsoft.codequality.analyzers/2.6.2", + "hashPath": "microsoft.codequality.analyzers.2.6.2.nupkg.sha512" + }, + "Microsoft.DotNet.GenAPI/5.0.0-beta.19552.1": { + "type": "package", + "serviceable": true, + "sha512": "sha512-lfpxOPSnPK/2fmWQbDfSX6CbYl5w3t/uXO31VWRIOeIvaJC9eopXQQ8styZYgYERl6ETePNv4qMk5r+B3QftGw==", + "path": "microsoft.dotnet.genapi/5.0.0-beta.19552.1", + "hashPath": "microsoft.dotnet.genapi.5.0.0-beta.19552.1.nupkg.sha512" + }, + "Microsoft.NetCore.Analyzers/2.6.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-HUfJMvzy4xJ6b4X99NGer2Pxn5On/WU/JscTzxKODg9srhJdtlWNhdorbejl2igrBi7RX6ufBhfvp8aBAEMy1A==", + "path": "microsoft.netcore.analyzers/2.6.2", + "hashPath": "microsoft.netcore.analyzers.2.6.2.nupkg.sha512" + }, + "Microsoft.NETCore.Platforms/1.1.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==", + "path": "microsoft.netcore.platforms/1.1.0", + "hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512" + }, + "Microsoft.NetFramework.Analyzers/2.6.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-HdNe8zptYp3bl7VDPTXDzeKMqkQva8m9mUI0C1HFN2iiMsMGnV3gqOeDQenH6NCTgSwYRnokHFsvcOeEODMd0g==", + "path": "microsoft.netframework.analyzers/2.6.2", + "hashPath": "microsoft.netframework.analyzers.2.6.2.nupkg.sha512" + }, + "Microsoft.SourceLink.Common/1.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-G8DuQY8/DK5NN+3jm5wcMcd9QYD90UV7MiLmdljSJixi3U/vNaeBKmmXUqI4DJCOeWizIUEh4ALhSt58mR+5eg==", + "path": "microsoft.sourcelink.common/1.0.0", + "hashPath": "microsoft.sourcelink.common.1.0.0.nupkg.sha512" + }, + "Microsoft.SourceLink.GitHub/1.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-aZyGyGg2nFSxix+xMkPmlmZSsnGQ3w+mIG23LTxJZHN+GPwTQ5FpPgDo7RMOq+Kcf5D4hFWfXkGhoGstawX13Q==", + "path": "microsoft.sourcelink.github/1.0.0", + "hashPath": "microsoft.sourcelink.github.1.0.0.nupkg.sha512" + }, + "NETStandard.Library/2.0.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "path": "netstandard.library/2.0.3", + "hashPath": "netstandard.library.2.0.3.nupkg.sha512" + }, + "StyleCop.Analyzers/1.1.118": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Onx6ovGSqXSK07n/0eM3ZusiNdB6cIlJdabQhWGgJp3Vooy9AaLS/tigeybOJAobqbtggTamoWndz72JscZBvw==", + "path": "stylecop.analyzers/1.1.118", + "hashPath": "stylecop.analyzers.1.1.118.nupkg.sha512" + }, + "System.Buffers/4.5.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A==", + "path": "system.buffers/4.5.0", + "hashPath": "system.buffers.4.5.0.nupkg.sha512" + }, + "System.Diagnostics.DiagnosticSource/4.6.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-mbBgoR0rRfl2uimsZ2avZY8g7Xnh1Mza0rJZLPcxqiMWlkGukjmRkuMJ/er+AhQuiRIh80CR/Hpeztr80seV5g==", + "path": "system.diagnostics.diagnosticsource/4.6.0", + "hashPath": "system.diagnostics.diagnosticsource.4.6.0.nupkg.sha512" + }, + "System.Memory/4.5.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", + "path": "system.memory/4.5.3", + "hashPath": "system.memory.4.5.3.nupkg.sha512" + }, + "System.Numerics.Vectors/4.5.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==", + "path": "system.numerics.vectors/4.5.0", + "hashPath": "system.numerics.vectors.4.5.0.nupkg.sha512" + }, + "System.Runtime.CompilerServices.Unsafe/4.6.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-HxozeSlipUK7dAroTYwIcGwKDeOVpQnJlpVaOkBz7CM4TsE5b/tKlQBZecTjh6FzcSbxndYaxxpsBMz+wMJeyw==", + "path": "system.runtime.compilerservices.unsafe/4.6.0", + "hashPath": "system.runtime.compilerservices.unsafe.4.6.0.nupkg.sha512" + }, + "System.Text.Encodings.Web/4.6.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-BXgFO8Yi7ao7hVA/nklD0Hre1Bbce048ZqryGZVFifGNPuh+2jqF1i/jLJLMfFGZIzUOw+nCIeH24SQhghDSPw==", + "path": "system.text.encodings.web/4.6.0", + "hashPath": "system.text.encodings.web.4.6.0.nupkg.sha512" + }, + "System.Text.Json/4.6.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-4F8Xe+JIkVoDJ8hDAZ7HqLkjctN/6WItJIzQaifBwClC7wmoLSda/Sv2i6i1kycqDb3hWF4JCVbpAweyOKHEUA==", + "path": "system.text.json/4.6.0", + "hashPath": "system.text.json.4.6.0.nupkg.sha512" + }, + "System.Threading.Tasks.Extensions/4.5.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-BG/TNxDFv0svAzx8OiMXDlsHfGw623BZ8tCXw4YLhDFDvDhNUEV58jKYMGRnkbJNm7c3JNNJDiN7JBMzxRBR2w==", + "path": "system.threading.tasks.extensions/4.5.2", + "hashPath": "system.threading.tasks.extensions.4.5.2.nupkg.sha512" + }, + "Text.Analyzers/2.6.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-UcSpZtr7TXPWj59F9WGZd7Edcm+ppBW4OeA/VuqX0RUJ6t/qgCqPfbRAJ/WbT5hcMHaI2pOn7kMbG5gOayravQ==", + "path": "text.analyzers/2.6.2", + "hashPath": "text.analyzers.2.6.2.nupkg.sha512" + } + } +} \ No newline at end of file diff --git a/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.dll b/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.dll new file mode 100644 index 0000000000000000000000000000000000000000..0d552971f9190386d80959f546d607206e1923cc GIT binary patch literal 254976 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 YT7j@YbYQwJ=dzQTwd=0A{- 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 A*#H0l literal 0 HcmV?d00001 diff --git a/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.xml b/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.xml new file mode 100644 index 0000000000000..5bc60e4e6cada --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.xml @@ -0,0 +1,1804 @@ + + + + Azure.ResourceManager.Authorization + + + + Authorization service management client. + + + Initializes a new instance of AuthorizationManagementClient for mocking. + + + Initializes a new instance of AuthorizationManagementClient. + The ID of the target subscription. + The OAuth token for making client requests. + The options for configuring the client. + + + Initializes a new instance of AuthorizationManagementClient. + The ID of the target subscription. + server parameter. + The OAuth token for making client requests. + The options for configuring the client. + is null. + + + Returns an instance of ClassicAdministratorsOperations. + + + Returns an instance of GlobalAdministratorOperations. + + + Returns an instance of ProviderOperationsMetadataOperations. + + + Returns an instance of RoleAssignmentsOperations. + + + Returns an instance of PermissionsOperations. + + + Returns an instance of RoleDefinitionsOperations. + + + Returns an instance of DenyAssignmentsOperations. + + + Client options for Authorization. + + + The ClassicAdministrators service client. + + + Initializes a new instance of ClassicAdministratorsOperations for mocking. + + + Initializes a new instance of ClassicAdministratorsOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + The ID of the target subscription. + server parameter. + + + Gets service administrator, account administrator, and co-administrators for the subscription. + The cancellation token to use. + + + Gets service administrator, account administrator, and co-administrators for the subscription. + The cancellation token to use. + + + Initializes a new instance of ClassicAdministratorsRestOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + The ID of the target subscription. + server parameter. + is null. + + + Gets service administrator, account administrator, and co-administrators for the subscription. + The cancellation token to use. + + + Gets service administrator, account administrator, and co-administrators for the subscription. + The cancellation token to use. + + + Gets service administrator, account administrator, and co-administrators for the subscription. + The URL to the next page of results. + The cancellation token to use. + is null. + + + Gets service administrator, account administrator, and co-administrators for the subscription. + The URL to the next page of results. + The cancellation token to use. + is null. + + + The DenyAssignments service client. + + + Initializes a new instance of DenyAssignmentsOperations for mocking. + + + Initializes a new instance of DenyAssignmentsOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + The ID of the target subscription. + server parameter. + + + Get the specified deny assignment. + The scope of the deny assignment. + The ID of the deny assignment to get. + The cancellation token to use. + + + Get the specified deny assignment. + The scope of the deny assignment. + The ID of the deny assignment to get. + The cancellation token to use. + + + Gets a deny assignment by ID. + The fully qualified deny assignment ID. For example, use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for subscription level deny assignments, or /providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for tenant level deny assignments. + The cancellation token to use. + + + Gets a deny assignment by ID. + The fully qualified deny assignment ID. For example, use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for subscription level deny assignments, or /providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for tenant level deny assignments. + The cancellation token to use. + + + Gets deny assignments for a resource. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get deny assignments for. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + , , , , or is null. + + + Gets deny assignments for a resource. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get deny assignments for. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + , , , , or is null. + + + Gets deny assignments for a resource group. + The name of the resource group. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + is null. + + + Gets deny assignments for a resource group. + The name of the resource group. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + is null. + + + Gets all deny assignments for the subscription. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + + + Gets all deny assignments for the subscription. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + + + Gets deny assignments for a scope. + The scope of the deny assignments. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + is null. + + + Gets deny assignments for a scope. + The scope of the deny assignments. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + is null. + + + Initializes a new instance of DenyAssignmentsRestOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + The ID of the target subscription. + server parameter. + is null. + + + Gets deny assignments for a resource. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get deny assignments for. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + , , , , or is null. + + + Gets deny assignments for a resource. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get deny assignments for. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + , , , , or is null. + + + Gets deny assignments for a resource group. + The name of the resource group. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + is null. + + + Gets deny assignments for a resource group. + The name of the resource group. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + is null. + + + Gets all deny assignments for the subscription. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + + + Gets all deny assignments for the subscription. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + + + Get the specified deny assignment. + The scope of the deny assignment. + The ID of the deny assignment to get. + The cancellation token to use. + or is null. + + + Get the specified deny assignment. + The scope of the deny assignment. + The ID of the deny assignment to get. + The cancellation token to use. + or is null. + + + Gets a deny assignment by ID. + The fully qualified deny assignment ID. For example, use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for subscription level deny assignments, or /providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for tenant level deny assignments. + The cancellation token to use. + is null. + + + Gets a deny assignment by ID. + The fully qualified deny assignment ID. For example, use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for subscription level deny assignments, or /providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for tenant level deny assignments. + The cancellation token to use. + is null. + + + Gets deny assignments for a scope. + The scope of the deny assignments. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + is null. + + + Gets deny assignments for a scope. + The scope of the deny assignments. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + is null. + + + Gets deny assignments for a resource. + The URL to the next page of results. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get deny assignments for. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + , , , , , or is null. + + + Gets deny assignments for a resource. + The URL to the next page of results. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get deny assignments for. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + , , , , , or is null. + + + Gets deny assignments for a resource group. + The URL to the next page of results. + The name of the resource group. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + or is null. + + + Gets deny assignments for a resource group. + The URL to the next page of results. + The name of the resource group. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + or is null. + + + Gets all deny assignments for the subscription. + The URL to the next page of results. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + is null. + + + Gets all deny assignments for the subscription. + The URL to the next page of results. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + is null. + + + Gets deny assignments for a scope. + The URL to the next page of results. + The scope of the deny assignments. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + or is null. + + + Gets deny assignments for a scope. + The URL to the next page of results. + The scope of the deny assignments. + The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. + The cancellation token to use. + or is null. + + + The GlobalAdministrator service client. + + + Initializes a new instance of GlobalAdministratorOperations for mocking. + + + Initializes a new instance of GlobalAdministratorOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + server parameter. + + + Elevates access for a Global Administrator. + The cancellation token to use. + + + Elevates access for a Global Administrator. + The cancellation token to use. + + + Initializes a new instance of GlobalAdministratorRestOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + server parameter. + + + Elevates access for a Global Administrator. + The cancellation token to use. + + + Elevates access for a Global Administrator. + The cancellation token to use. + + + Classic Administrators. + + + Initializes a new instance of ClassicAdministrator. + + + Initializes a new instance of ClassicAdministrator. + The ID of the administrator. + The name of the administrator. + The type of the administrator. + The email address of the administrator. + The role of the administrator. + + + The ID of the administrator. + + + The name of the administrator. + + + The type of the administrator. + + + The email address of the administrator. + + + The role of the administrator. + + + ClassicAdministrator list result information. + + + Initializes a new instance of ClassicAdministratorListResult. + + + Initializes a new instance of ClassicAdministratorListResult. + An array of administrators. + The URL to use for getting the next set of results. + + + An array of administrators. + + + The URL to use for getting the next set of results. + + + Deny Assignment. + + + Initializes a new instance of DenyAssignment. + + + Initializes a new instance of DenyAssignment. + The deny assignment ID. + The deny assignment name. + The deny assignment type. + The display name of the deny assignment. + The description of the deny assignment. + An array of permissions that are denied by the deny assignment. + The deny assignment scope. + Determines if the deny assignment applies to child scopes. Default value is false. + Array of principals to which the deny assignment applies. + Array of principals to which the deny assignment does not apply. + Specifies whether this deny assignment was created by Azure and cannot be edited or deleted. + + + The deny assignment ID. + + + The deny assignment name. + + + The deny assignment type. + + + The display name of the deny assignment. + + + The description of the deny assignment. + + + An array of permissions that are denied by the deny assignment. + + + The deny assignment scope. + + + Determines if the deny assignment applies to child scopes. Default value is false. + + + Array of principals to which the deny assignment applies. + + + Array of principals to which the deny assignment does not apply. + + + Specifies whether this deny assignment was created by Azure and cannot be edited or deleted. + + + Deny Assignments filter. + + + Initializes a new instance of DenyAssignmentFilter. + + + Return deny assignment with specified name. + + + Return all deny assignments where the specified principal is listed in the principals list of deny assignments. + + + Return all deny assignments where the specified principal is listed either in the principals list or exclude principals list of deny assignments. + + + Deny assignment list operation result. + + + Initializes a new instance of DenyAssignmentListResult. + + + Initializes a new instance of DenyAssignmentListResult. + Deny assignment list. + The URL to use for getting the next set of results. + + + Deny assignment list. + + + The URL to use for getting the next set of results. + + + Deny assignment permissions. + + + Initializes a new instance of DenyAssignmentPermission. + + + Initializes a new instance of DenyAssignmentPermission. + Actions to which the deny assignment does not grant access. + Actions to exclude from that the deny assignment does not grant access. + Data actions to which the deny assignment does not grant access. + Data actions to exclude from that the deny assignment does not grant access. + + + Actions to which the deny assignment does not grant access. + + + Actions to exclude from that the deny assignment does not grant access. + + + Data actions to which the deny assignment does not grant access. + + + Data actions to exclude from that the deny assignment does not grant access. + + + Role definition permissions. + + + Initializes a new instance of Permission. + + + Initializes a new instance of Permission. + Allowed actions. + Denied actions. + Allowed Data actions. + Denied Data actions. + + + Allowed actions. + + + Denied actions. + + + Allowed Data actions. + + + Denied Data actions. + + + Permissions information. + + + Initializes a new instance of PermissionGetResult. + + + Initializes a new instance of PermissionGetResult. + An array of permissions. + The URL to use for getting the next set of results. + + + An array of permissions. + + + The URL to use for getting the next set of results. + + + Deny assignment principal. + + + Initializes a new instance of Principal. + + + Initializes a new instance of Principal. + Object ID of the Azure AD principal (user, group, or service principal) to which the deny assignment applies. An empty guid '00000000-0000-0000-0000-000000000000' as principal id and principal type as 'Everyone' represents all users, groups and service principals. + Type of object represented by principal id (user, group, or service principal). An empty guid '00000000-0000-0000-0000-000000000000' as principal id and principal type as 'Everyone' represents all users, groups and service principals. + + + Object ID of the Azure AD principal (user, group, or service principal) to which the deny assignment applies. An empty guid '00000000-0000-0000-0000-000000000000' as principal id and principal type as 'Everyone' represents all users, groups and service principals. + + + Type of object represented by principal id (user, group, or service principal). An empty guid '00000000-0000-0000-0000-000000000000' as principal id and principal type as 'Everyone' represents all users, groups and service principals. + + + The principal type of the assigned principal ID. + + + Determines if two values are the same. + is null. + + + User. + + + Group. + + + ServicePrincipal. + + + Unknown. + + + DirectoryRoleTemplate. + + + ForeignGroup. + + + Application. + + + MSI. + + + DirectoryObjectOrGroup. + + + Everyone. + + + Determines if two values are the same. + + + Determines if two values are not the same. + + + Converts a string to a . + + + + + + + + + + + + + + + Operation. + + + Initializes a new instance of ProviderOperation. + + + Initializes a new instance of ProviderOperation. + The operation name. + The operation display name. + The operation description. + The operation origin. + The operation properties. + The dataAction flag to specify the operation type. + + + The operation name. + + + The operation display name. + + + The operation description. + + + The operation origin. + + + The operation properties. + + + The dataAction flag to specify the operation type. + + + Provider Operations metadata. + + + Initializes a new instance of ProviderOperationsMetadata. + + + Initializes a new instance of ProviderOperationsMetadata. + The provider id. + The provider name. + The provider type. + The provider display name. + The provider resource types. + The provider operations. + + + The provider id. + + + The provider name. + + + The provider type. + + + The provider display name. + + + The provider resource types. + + + The provider operations. + + + Provider operations metadata list. + + + Initializes a new instance of ProviderOperationsMetadataListResult. + + + Initializes a new instance of ProviderOperationsMetadataListResult. + The list of providers. + The URL to use for getting the next set of results. + + + The list of providers. + + + The URL to use for getting the next set of results. + + + Resource Type. + + + Initializes a new instance of ResourceType. + + + Initializes a new instance of ResourceType. + The resource type name. + The resource type display name. + The resource type operations. + + + The resource type name. + + + The resource type display name. + + + The resource type operations. + + + Role Assignments. + + + Initializes a new instance of RoleAssignment. + + + Initializes a new instance of RoleAssignment. + The role assignment ID. + The role assignment name. + The role assignment type. + The role assignment scope. + The role definition ID. + The principal ID. + The principal type of the assigned principal ID. + The Delegation flag for the role assignment. + + + The role assignment ID. + + + The role assignment name. + + + The role assignment type. + + + The role assignment scope. + + + The role definition ID. + + + The principal ID. + + + The principal type of the assigned principal ID. + + + The Delegation flag for the role assignment. + + + Role assignment create parameters. + + + Initializes a new instance of RoleAssignmentCreateParameters. + The role definition ID used in the role assignment. + The principal ID assigned to the role. This maps to the ID inside the Active Directory. It can point to a user, service principal, or security group. + or is null. + + + The role definition ID used in the role assignment. + + + The principal ID assigned to the role. This maps to the ID inside the Active Directory. It can point to a user, service principal, or security group. + + + The principal type of the assigned principal ID. + + + The delegation flag used for creating a role assignment. + + + Role Assignments filter. + + + Initializes a new instance of RoleAssignmentFilter. + + + Returns role assignment of the specific principal. + + + The Delegation flag for the role assignment. + + + Role assignment list operation result. + + + Initializes a new instance of RoleAssignmentListResult. + + + Initializes a new instance of RoleAssignmentListResult. + Role assignment list. + The URL to use for getting the next set of results. + + + Role assignment list. + + + The URL to use for getting the next set of results. + + + Role definition. + + + Initializes a new instance of RoleDefinition. + + + Initializes a new instance of RoleDefinition. + The role definition ID. + The role definition name. + The role definition type. + The role name. + The role definition description. + The role type. + Role definition permissions. + Role definition assignable scopes. + + + The role definition ID. + + + The role definition name. + + + The role definition type. + + + The role name. + + + The role definition description. + + + The role type. + + + Role definition permissions. + + + Role definition assignable scopes. + + + Role Definitions filter. + + + Initializes a new instance of RoleDefinitionFilter. + + + Returns role definition with the specific name. + + + Returns role definition with the specific type. + + + Role definition list operation result. + + + Initializes a new instance of RoleDefinitionListResult. + + + Initializes a new instance of RoleDefinitionListResult. + Role definition list. + The URL to use for getting the next set of results. + + + Role definition list. + + + The URL to use for getting the next set of results. + + + The Permissions service client. + + + Initializes a new instance of PermissionsOperations for mocking. + + + Initializes a new instance of PermissionsOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + The ID of the target subscription. + server parameter. + + + Gets all permissions the caller has for a resource group. + The name of the resource group. + The cancellation token to use. + is null. + + + Gets all permissions the caller has for a resource group. + The name of the resource group. + The cancellation token to use. + is null. + + + Gets all permissions the caller has for a resource. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get the permissions for. + The cancellation token to use. + , , , , or is null. + + + Gets all permissions the caller has for a resource. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get the permissions for. + The cancellation token to use. + , , , , or is null. + + + Initializes a new instance of PermissionsRestOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + The ID of the target subscription. + server parameter. + is null. + + + Gets all permissions the caller has for a resource group. + The name of the resource group. + The cancellation token to use. + is null. + + + Gets all permissions the caller has for a resource group. + The name of the resource group. + The cancellation token to use. + is null. + + + Gets all permissions the caller has for a resource. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get the permissions for. + The cancellation token to use. + , , , , or is null. + + + Gets all permissions the caller has for a resource. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get the permissions for. + The cancellation token to use. + , , , , or is null. + + + Gets all permissions the caller has for a resource group. + The URL to the next page of results. + The name of the resource group. + The cancellation token to use. + or is null. + + + Gets all permissions the caller has for a resource group. + The URL to the next page of results. + The name of the resource group. + The cancellation token to use. + or is null. + + + Gets all permissions the caller has for a resource. + The URL to the next page of results. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get the permissions for. + The cancellation token to use. + , , , , , or is null. + + + Gets all permissions the caller has for a resource. + The URL to the next page of results. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get the permissions for. + The cancellation token to use. + , , , , , or is null. + + + The ProviderOperationsMetadata service client. + + + Initializes a new instance of ProviderOperationsMetadataOperations for mocking. + + + Initializes a new instance of ProviderOperationsMetadataOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + server parameter. + + + Gets provider operations metadata for the specified resource provider. + The namespace of the resource provider. + Specifies whether to expand the values. + The cancellation token to use. + + + Gets provider operations metadata for the specified resource provider. + The namespace of the resource provider. + Specifies whether to expand the values. + The cancellation token to use. + + + Gets provider operations metadata for all resource providers. + Specifies whether to expand the values. + The cancellation token to use. + + + Gets provider operations metadata for all resource providers. + Specifies whether to expand the values. + The cancellation token to use. + + + Initializes a new instance of ProviderOperationsMetadataRestOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + server parameter. + + + Gets provider operations metadata for the specified resource provider. + The namespace of the resource provider. + Specifies whether to expand the values. + The cancellation token to use. + is null. + + + Gets provider operations metadata for the specified resource provider. + The namespace of the resource provider. + Specifies whether to expand the values. + The cancellation token to use. + is null. + + + Gets provider operations metadata for all resource providers. + Specifies whether to expand the values. + The cancellation token to use. + + + Gets provider operations metadata for all resource providers. + Specifies whether to expand the values. + The cancellation token to use. + + + Gets provider operations metadata for all resource providers. + The URL to the next page of results. + Specifies whether to expand the values. + The cancellation token to use. + is null. + + + Gets provider operations metadata for all resource providers. + The URL to the next page of results. + Specifies whether to expand the values. + The cancellation token to use. + is null. + + + The RoleAssignments service client. + + + Initializes a new instance of RoleAssignmentsOperations for mocking. + + + Initializes a new instance of RoleAssignmentsOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + The ID of the target subscription. + server parameter. + + + Deletes a role assignment. + The scope of the role assignment to delete. + The name of the role assignment to delete. + The cancellation token to use. + + + Deletes a role assignment. + The scope of the role assignment to delete. + The name of the role assignment to delete. + The cancellation token to use. + + + Creates a role assignment. + The scope of the role assignment to create. The scope can be any REST resource instance. For example, use '/subscriptions/{subscription-id}/' for a subscription, '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}' for a resource group, and '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/{resource-provider}/{resource-type}/{resource-name}' for a resource. + The name of the role assignment to create. It can be any valid GUID. + Parameters for the role assignment. + The cancellation token to use. + + + Creates a role assignment. + The scope of the role assignment to create. The scope can be any REST resource instance. For example, use '/subscriptions/{subscription-id}/' for a subscription, '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}' for a resource group, and '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/{resource-provider}/{resource-type}/{resource-name}' for a resource. + The name of the role assignment to create. It can be any valid GUID. + Parameters for the role assignment. + The cancellation token to use. + + + Get the specified role assignment. + The scope of the role assignment. + The name of the role assignment to get. + The cancellation token to use. + + + Get the specified role assignment. + The scope of the role assignment. + The name of the role assignment to get. + The cancellation token to use. + + + Deletes a role assignment. + The ID of the role assignment to delete. + The cancellation token to use. + + + Deletes a role assignment. + The ID of the role assignment to delete. + The cancellation token to use. + + + Creates a role assignment by ID. + The ID of the role assignment to create. + Parameters for the role assignment. + The cancellation token to use. + + + Creates a role assignment by ID. + The ID of the role assignment to create. + Parameters for the role assignment. + The cancellation token to use. + + + Gets a role assignment by ID. + The ID of the role assignment to get. + The cancellation token to use. + + + Gets a role assignment by ID. + The ID of the role assignment to get. + The cancellation token to use. + + + Gets role assignments for a resource. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get role assignments for. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + , , , , or is null. + + + Gets role assignments for a resource. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get role assignments for. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + , , , , or is null. + + + Gets role assignments for a resource group. + The name of the resource group. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + is null. + + + Gets role assignments for a resource group. + The name of the resource group. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + is null. + + + Gets all role assignments for the subscription. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + + + Gets all role assignments for the subscription. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + + + Gets role assignments for a scope. + The scope of the role assignments. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + is null. + + + Gets role assignments for a scope. + The scope of the role assignments. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + is null. + + + Initializes a new instance of RoleAssignmentsRestOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + The ID of the target subscription. + server parameter. + is null. + + + Gets role assignments for a resource. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get role assignments for. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + , , , , or is null. + + + Gets role assignments for a resource. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get role assignments for. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + , , , , or is null. + + + Gets role assignments for a resource group. + The name of the resource group. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + is null. + + + Gets role assignments for a resource group. + The name of the resource group. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + is null. + + + Deletes a role assignment. + The scope of the role assignment to delete. + The name of the role assignment to delete. + The cancellation token to use. + or is null. + + + Deletes a role assignment. + The scope of the role assignment to delete. + The name of the role assignment to delete. + The cancellation token to use. + or is null. + + + Creates a role assignment. + The scope of the role assignment to create. The scope can be any REST resource instance. For example, use '/subscriptions/{subscription-id}/' for a subscription, '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}' for a resource group, and '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/{resource-provider}/{resource-type}/{resource-name}' for a resource. + The name of the role assignment to create. It can be any valid GUID. + Parameters for the role assignment. + The cancellation token to use. + , , or is null. + + + Creates a role assignment. + The scope of the role assignment to create. The scope can be any REST resource instance. For example, use '/subscriptions/{subscription-id}/' for a subscription, '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}' for a resource group, and '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/{resource-provider}/{resource-type}/{resource-name}' for a resource. + The name of the role assignment to create. It can be any valid GUID. + Parameters for the role assignment. + The cancellation token to use. + , , or is null. + + + Get the specified role assignment. + The scope of the role assignment. + The name of the role assignment to get. + The cancellation token to use. + or is null. + + + Get the specified role assignment. + The scope of the role assignment. + The name of the role assignment to get. + The cancellation token to use. + or is null. + + + Deletes a role assignment. + The ID of the role assignment to delete. + The cancellation token to use. + is null. + + + Deletes a role assignment. + The ID of the role assignment to delete. + The cancellation token to use. + is null. + + + Creates a role assignment by ID. + The ID of the role assignment to create. + Parameters for the role assignment. + The cancellation token to use. + or is null. + + + Creates a role assignment by ID. + The ID of the role assignment to create. + Parameters for the role assignment. + The cancellation token to use. + or is null. + + + Gets a role assignment by ID. + The ID of the role assignment to get. + The cancellation token to use. + is null. + + + Gets a role assignment by ID. + The ID of the role assignment to get. + The cancellation token to use. + is null. + + + Gets all role assignments for the subscription. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + + + Gets all role assignments for the subscription. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + + + Gets role assignments for a scope. + The scope of the role assignments. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + is null. + + + Gets role assignments for a scope. + The scope of the role assignments. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + is null. + + + Gets role assignments for a resource. + The URL to the next page of results. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get role assignments for. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + , , , , , or is null. + + + Gets role assignments for a resource. + The URL to the next page of results. + The name of the resource group. + The namespace of the resource provider. + The parent resource identity. + The resource type of the resource. + The name of the resource to get role assignments for. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + , , , , , or is null. + + + Gets role assignments for a resource group. + The URL to the next page of results. + The name of the resource group. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + or is null. + + + Gets role assignments for a resource group. + The URL to the next page of results. + The name of the resource group. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + or is null. + + + Gets all role assignments for the subscription. + The URL to the next page of results. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + is null. + + + Gets all role assignments for the subscription. + The URL to the next page of results. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + is null. + + + Gets role assignments for a scope. + The URL to the next page of results. + The scope of the role assignments. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + or is null. + + + Gets role assignments for a scope. + The URL to the next page of results. + The scope of the role assignments. + The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + The cancellation token to use. + or is null. + + + The RoleDefinitions service client. + + + Initializes a new instance of RoleDefinitionsOperations for mocking. + + + Initializes a new instance of RoleDefinitionsOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + server parameter. + + + Deletes a role definition. + The scope of the role definition. + The ID of the role definition to delete. + The cancellation token to use. + + + Deletes a role definition. + The scope of the role definition. + The ID of the role definition to delete. + The cancellation token to use. + + + Get role definition by name (GUID). + The scope of the role definition. + The ID of the role definition. + The cancellation token to use. + + + Get role definition by name (GUID). + The scope of the role definition. + The ID of the role definition. + The cancellation token to use. + + + Creates or updates a role definition. + The scope of the role definition. + The ID of the role definition. + The values for the role definition. + The cancellation token to use. + + + Creates or updates a role definition. + The scope of the role definition. + The ID of the role definition. + The values for the role definition. + The cancellation token to use. + + + Gets a role definition by ID. + The fully qualified role definition ID. Use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for subscription level role definitions, or /providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for tenant level role definitions. + The cancellation token to use. + + + Gets a role definition by ID. + The fully qualified role definition ID. Use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for subscription level role definitions, or /providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for tenant level role definitions. + The cancellation token to use. + + + Get all role definitions that are applicable at scope and above. + The scope of the role definition. + The filter to apply on the operation. Use atScopeAndBelow filter to search below the given scope as well. + The cancellation token to use. + is null. + + + Get all role definitions that are applicable at scope and above. + The scope of the role definition. + The filter to apply on the operation. Use atScopeAndBelow filter to search below the given scope as well. + The cancellation token to use. + is null. + + + Initializes a new instance of RoleDefinitionsRestOperations. + The handler for diagnostic messaging in the client. + The HTTP pipeline for sending and receiving REST requests and responses. + server parameter. + + + Deletes a role definition. + The scope of the role definition. + The ID of the role definition to delete. + The cancellation token to use. + or is null. + + + Deletes a role definition. + The scope of the role definition. + The ID of the role definition to delete. + The cancellation token to use. + or is null. + + + Get role definition by name (GUID). + The scope of the role definition. + The ID of the role definition. + The cancellation token to use. + or is null. + + + Get role definition by name (GUID). + The scope of the role definition. + The ID of the role definition. + The cancellation token to use. + or is null. + + + Creates or updates a role definition. + The scope of the role definition. + The ID of the role definition. + The values for the role definition. + The cancellation token to use. + , , or is null. + + + Creates or updates a role definition. + The scope of the role definition. + The ID of the role definition. + The values for the role definition. + The cancellation token to use. + , , or is null. + + + Get all role definitions that are applicable at scope and above. + The scope of the role definition. + The filter to apply on the operation. Use atScopeAndBelow filter to search below the given scope as well. + The cancellation token to use. + is null. + + + Get all role definitions that are applicable at scope and above. + The scope of the role definition. + The filter to apply on the operation. Use atScopeAndBelow filter to search below the given scope as well. + The cancellation token to use. + is null. + + + Gets a role definition by ID. + The fully qualified role definition ID. Use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for subscription level role definitions, or /providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for tenant level role definitions. + The cancellation token to use. + is null. + + + Gets a role definition by ID. + The fully qualified role definition ID. Use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for subscription level role definitions, or /providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for tenant level role definitions. + The cancellation token to use. + is null. + + + Get all role definitions that are applicable at scope and above. + The URL to the next page of results. + The scope of the role definition. + The filter to apply on the operation. Use atScopeAndBelow filter to search below the given scope as well. + The cancellation token to use. + or is null. + + + Get all role definitions that are applicable at scope and above. + The URL to the next page of results. + The scope of the role definition. + The filter to apply on the operation. Use atScopeAndBelow filter to search below the given scope as well. + The cancellation token to use. + or is null. + + + + This implements the ARM scenarios for LROs. It is highly recommended to read the ARM spec prior to modifying this code: + https://github.com/Azure/azure-resource-manager-rpc/blob/master/v1.0/Addendum.md#asynchronous-operations + Other reference documents include: + https://github.com/Azure/autorest/blob/master/docs/extensions/readme.md#x-ms-long-running-operation + https://github.com/Azure/adx-documentation-pr/blob/master/sdks/LRO/LRO_AzureSDK.md + + The final result of the LRO. + + + + Gets or sets a coma separated list of additional model usage modes. Allowed values: model, error, intput, output. + + + + + Gets or sets a coma separated list of additional model serialization formats. + + + + + Represents a heap-based, array-backed output sink into which data can be written. + + + + + Creates an instance of an , in which data can be written to, + with the default initial capacity. + + + + + Creates an instance of an , in which data can be written to, + with an initial capacity specified. + + The minimum capacity with which to initialize the underlying buffer. + + Thrown when is not positive (i.e. less than or equal to 0). + + + + + Returns the data written to the underlying buffer so far, as a . + + + + + Returns the data written to the underlying buffer so far, as a . + + + + + Returns the amount of data written to the underlying buffer so far. + + + + + Returns the total amount of space within the underlying buffer. + + + + + Returns the amount of space available that can still be written into without forcing the underlying buffer to grow. + + + + + Clears the data written to the underlying buffer. + + + You must clear the before trying to re-use it. + + + + + Notifies that amount of data was written to the output /. + + + Thrown when is negative. + + + Thrown when attempting to advance past the end of the underlying buffer. + + + You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. + + + + + Returns a to write to that is at least the requested length (specified by ). + If no is provided (or it's equal to 0), some non-empty buffer is returned. + + + Thrown when is negative. + + + This will never return an empty . + + + There is no guarantee that successive calls will return the same buffer or the same-sized buffer. + + + You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. + + + + + Returns a to write to that is at least the requested length (specified by ). + If no is provided (or it's equal to 0), some non-empty buffer is returned. + + + Thrown when is negative. + + + This will never return an empty . + + + There is no guarantee that successive calls will return the same buffer or the same-sized buffer. + + + You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. + + + + + HACK HACK HACK. Some runtime environments like Azure.Functions downgrade System.Diagnostic.DiagnosticSource package version causing method not found exceptions in customer apps + This type is a temporary workaround to avoid the issue. + + + + + Both and are defined as public structs so that foreach can use duck typing + to call and avoid heap memory allocation. + Please don't delete this method and don't make these types private. + + + + + + This attribute should be set on all client assemblies with value of one of the resource providers + from the https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/azure-services-resource-providers list. + + + + diff --git a/sdk/resourcemanager/Proto.Client/compute/AvailabilitySet.cs b/sdk/resourcemanager/Proto.Client/compute/AvailabilitySet.cs new file mode 100644 index 0000000000000..07a3dd16ba7c2 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/AvailabilitySet.cs @@ -0,0 +1,39 @@ +using System.Threading.Tasks; +using Azure.ResourceManager.Core; + +namespace Proto.Compute +{ + /// + /// A class representing an availability set along with the instance operations that can be performed on it. + /// + public class AvailabilitySet : AvailabilitySetOperations + { + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The resource that is the target of operations. + internal AvailabilitySet(ResourceOperationsBase options, AvailabilitySetData resource) + : base(options, resource.Id) + { + Data = resource; + } + + /// + /// Gets or sets the availability set data. + /// + public AvailabilitySetData Data { get; private set; } + + /// + protected override AvailabilitySet GetResource() + { + return this; + } + + /// + protected override Task GetResourceAsync() + { + return Task.FromResult(this); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetContainer.cs b/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetContainer.cs new file mode 100644 index 0000000000000..0d114a16431ad --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetContainer.cs @@ -0,0 +1,143 @@ +using Azure; +using Azure.ResourceManager.Compute; +using Azure.ResourceManager.Core; +using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Core.Resources; +using System.Threading; +using System.Threading.Tasks; + +namespace Proto.Compute +{ + /// + /// A class representing collection of availability set and their operations over a resource group. + /// + public class AvailabilitySetContainer : ResourceContainerBase + { + /// + /// Initializes a new instance of the class. + /// + /// The parent resource group. + internal AvailabilitySetContainer(ResourceGroupOperations resourceGroup) + : base(resourceGroup) + { + } + + /// + protected override ResourceType ValidResourceType => ResourceGroupOperations.ResourceType; + + /// + public override ArmResponse CreateOrUpdate(string name, AvailabilitySetData resourceDetails) + { + var response = Operations.CreateOrUpdate(Id.ResourceGroup, name, resourceDetails.Model); + return new PhArmResponse( + response, + a => new AvailabilitySet(Parent, new AvailabilitySetData(a))); + } + + /// + public async override Task> CreateOrUpdateAsync(string name, AvailabilitySetData resourceDetails, CancellationToken cancellationToken = default) + { + var response = await Operations.CreateOrUpdateAsync(Id.ResourceGroup, name, resourceDetails.Model, cancellationToken).ConfigureAwait(false); + return new PhArmResponse( + response, + a => new AvailabilitySet(Parent, new AvailabilitySetData(a))); + } + + /// + public override ArmOperation StartCreateOrUpdate(string name, AvailabilitySetData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + Operations.CreateOrUpdate(Id.ResourceGroup, name, resourceDetails.Model, cancellationToken), + a => new AvailabilitySet(Parent, new AvailabilitySetData(a))); + } + + /// + public async override Task> StartCreateOrUpdateAsync(string name, AvailabilitySetData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + await Operations.CreateOrUpdateAsync(Id.ResourceGroup, name, resourceDetails.Model, cancellationToken).ConfigureAwait(false), + a => new AvailabilitySet(Parent, new AvailabilitySetData(a))); + } + + /// + /// Constructs an object used to create an availability set. + /// + /// The sku name of the resource. + /// The location of the resource. + /// A builder with and . + public ArmBuilder Construct(string skuName, LocationData location = null) + { + var parent = GetParentResource(); + var availabilitySet = new Azure.ResourceManager.Compute.Models.AvailabilitySet(location ?? parent.Data.Location) + { + PlatformUpdateDomainCount = 5, + PlatformFaultDomainCount = 2, + Sku = new Azure.ResourceManager.Compute.Models.Sku() { Name = skuName } + }; + + return new ArmBuilder(this, new AvailabilitySetData(availabilitySet)); + } + + /// + /// Filters the list of availabitlity set for this resource group represented as generic resources. + /// + /// The filter used in this operation. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of resource that may take multiple service requests to iterate over. + public Pageable ListByName(string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(AvailabilitySetData.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContext(Parent as ResourceGroupOperations, filters, top, cancellationToken); + } + + /// + /// Filters the list of availabitlity set for this resource group represented as generic resources. + /// + /// The filter used in this operation. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of resource that may take multiple service requests to iterate over. + public AsyncPageable ListByNameAsync(string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(AvailabilitySetData.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContextAsync(Parent as ResourceGroupOperations, filters, top, cancellationToken); + } + + /// + /// Filters the list of availabitlity set for this resource group. + /// Makes an additional network call to retrieve the full data model for each resource group. + /// + /// The filter used in this operation. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of availability set that may take multiple service requests to iterate over. + public Pageable ListByNameExpanded(string filter, int? top = null, CancellationToken cancellationToken = default) + { + var results = ListByName(filter, top, cancellationToken); + return new PhWrappingPageable(results, s => new AvailabilitySetOperations(s).Get().Value); + } + + /// + /// Filters the list of availabitlity set for this resource group. + /// Makes an additional network call to retrieve the full data model for each resource group. + /// + /// The filter used in this operation. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An asyc collection of availability set that may take multiple service requests to iterate over. + public AsyncPageable ListByNameExpandedAsync(string filter, int? top = null, CancellationToken cancellationToken = default) + { + var results = ListByNameAsync(filter, top, cancellationToken); + return new PhWrappingAsyncPageable(results, s => new AvailabilitySetOperations(s).Get().Value); + } + + private AvailabilitySetsOperations Operations => new ComputeManagementClient( + BaseUri, + Id.Subscription, + Credential, + ClientOptions.Convert()).AvailabilitySets; + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetOperations.cs b/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetOperations.cs new file mode 100644 index 0000000000000..c4bc67d7d6180 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetOperations.cs @@ -0,0 +1,339 @@ +using Azure; +using Azure.ResourceManager.Compute; +using Azure.ResourceManager.Compute.Models; +using Azure.ResourceManager.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Proto.Compute +{ + /// + /// A class representing the operations that can be performed over a specific availability set. + /// + public class AvailabilitySetOperations : ResourceOperationsBase, ITaggableResource, IDeletableResource + { + /// + /// Initializes a new instance of the class. + /// + /// An instance of that has an id for an availability set. + internal AvailabilitySetOperations(GenericResourceOperations genericOperations) + : base(genericOperations) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The name of the availability set to use. + internal AvailabilitySetOperations(ResourceGroupOperations resourceGroup, string availabilitySetName) + : base(resourceGroup, $"{resourceGroup.Id}/providers/Microsoft.Compute/availabilitySets/{availabilitySetName}") + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + protected AvailabilitySetOperations(ResourceOperationsBase options, ResourceIdentifier id) + : base(options, id) + { + } + + /// + /// Gets the resource type definition for an availability set. + /// + public static readonly ResourceType ResourceType = "Microsoft.Compute/availabilitySets"; + + /// + protected override ResourceType ValidResourceType => ResourceType; + + private AvailabilitySetsOperations Operations => new ComputeManagementClient( + BaseUri, + Id.Subscription, + Credential, + ClientOptions.Convert()).AvailabilitySets; + + /// + /// The operation to delete an availability set. + /// + /// A response with the operation for this resource. + public ArmResponse Delete() + { + return new ArmResponse(Operations.Delete(Id.ResourceGroup, Id.Name)); + } + + /// + /// The operation to delete an availability set. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public async Task> DeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmResponse(await Operations.DeleteAsync(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + /// The operation to delete an availability set. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// An that allows polling for completion of the operation. + public ArmOperation StartDelete(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(Operations.Delete(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + /// The operation to delete an availability set. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> StartDeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(await Operations.DeleteAsync(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + public override ArmResponse Get() + { + return new PhArmResponse( + Operations.Get(Id.ResourceGroup, Id.Name), + a => new AvailabilitySet(this, new AvailabilitySetData(a))); + } + + /// + public async override Task> GetAsync(CancellationToken cancellationToken = default) + { + return new PhArmResponse( + await Operations.GetAsync(Id.ResourceGroup, Id.Name, cancellationToken), + a => new AvailabilitySet(this, new AvailabilitySetData(a))); + } + + /// + /// The operation to update an availability set. + /// + /// The parameters to update. + /// The operation of the updated resource. + public ArmResponse Update(AvailabilitySetUpdate patchable) + { + return new PhArmResponse( + Operations.Update(Id.ResourceGroup, Id.Name, patchable), + a => new AvailabilitySet(this, new AvailabilitySetData(a))); + } + + /// + /// The operation to update an availability set. + /// + /// The parameters to update. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns the operation of the updated resource. + public async Task> UpdateAsync(AvailabilitySetUpdate patchable, CancellationToken cancellationToken = default) + { + return new PhArmResponse( + await Operations.UpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + a => new AvailabilitySet(this, new AvailabilitySetData(a))); + } + + /// + /// The operation to update an availability set. + /// + /// The parameters to update. + /// The operation of the updated resource. + public ArmOperation StartUpdate(AvailabilitySetUpdate patchable) + { + return new PhArmOperation( + Operations.Update(Id.ResourceGroup, Id.Name, patchable), + a => new AvailabilitySet(this, new AvailabilitySetData(a))); + } + + /// + /// The operation to update an availability set. + /// + /// The parameters to update. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns the operation of the updated resource. + public async Task> StartUpdateAsync(AvailabilitySetUpdate patchable, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + await Operations.UpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + a => new AvailabilitySet(this, new AvailabilitySetData(a))); + } + + /// + /// Adds a tag to an availability set. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// An that allows polling for completion of the operation. + public ArmResponse AddTag(string key, string value) + { + var resource = GetResource(); + var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return Update(patchable); + } + + /// + /// Adds a tag to an availability set. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return await UpdateAsync(patchable); + } + + /// + /// Adds a tag to an availability set. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// + /// Details on long running operation object. + /// + /// An that allows polling for completion of the operation. + public ArmOperation StartAddTag(string key, string value) + { + var resource = GetResource(); + var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return StartUpdate(patchable); + } + + /// + /// Adds a tag to an availability set. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return await StartUpdateAsync(patchable); + } + + /// + public ArmResponse SetTags(IDictionary tags) + { + var resource = GetResource(); + var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + ReplaceTags(tags, patchable.Tags); + return Update(patchable); + } + + /// + public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + ReplaceTags(tags, patchable.Tags); + return await UpdateAsync(patchable); + } + + /// + public ArmOperation StartSetTags(IDictionary tags) + { + var resource = GetResource(); + var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + ReplaceTags(tags, patchable.Tags); + return StartUpdate(patchable); + } + + /// + public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + ReplaceTags(tags, patchable.Tags); + return await StartUpdateAsync(patchable); + } + + /// + public ArmResponse RemoveTag(string key) + { + var resource = GetResource(); + var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + DeleteTag(key, patchable.Tags); + return Update(patchable); + } + + /// + public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + DeleteTag(key, patchable.Tags); + return await UpdateAsync(patchable); + } + + /// + public ArmOperation StartRemoveTag(string key) + { + var resource = GetResource(); + var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + DeleteTag(key, patchable.Tags); + return StartUpdate(patchable); + } + + /// + public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + DeleteTag(key, patchable.Tags); + return await StartUpdateAsync(patchable); + } + + /// + /// Lists all available geo-locations. + /// + /// A collection of location that may take multiple service requests to iterate over. + public IEnumerable ListAvailableLocations() + { + var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); + var availabilitySetProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var availabilitySetResource = availabilitySetProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return availabilitySetResource.Locations.Select(l => (LocationData)l); + } + + /// + /// Lists all available geo-locations. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of location that may take multiple service requests to iterate over. + /// The default subscription id is null. + public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) + { + var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); + var availabilitySetProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var availabilitySetResource = availabilitySetProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return availabilitySetResource.Locations.Select(l => (LocationData)l); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/ComputeRestApiVersions.cs b/sdk/resourcemanager/Proto.Client/compute/ComputeRestApiVersions.cs new file mode 100644 index 0000000000000..e9f12a385023e --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/ComputeRestApiVersions.cs @@ -0,0 +1,24 @@ +namespace Proto.Compute +{ + /// + /// A class representing which api version to use for each compute resource. + /// + public class ComputeRestApiVersions + { + internal ComputeRestApiVersions() + { + VirtualMachinesVersion = VirtualMachinesApiVersions.Default; + AvailabilitySetsVersion = AvailabilitySetsApiVersions.Default; + } + + /// + /// Gets or sets the virtual machine api version. + /// + public VirtualMachinesApiVersions VirtualMachinesVersion { get; set; } + + /// + /// Gets or sets the availability set api version. + /// + public AvailabilitySetsApiVersions AvailabilitySetsVersion { get; set; } + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/Convenience/VirtualMachineModelBuilder.cs b/sdk/resourcemanager/Proto.Client/compute/Convenience/VirtualMachineModelBuilder.cs new file mode 100644 index 0000000000000..79a31fecf2cf4 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/Convenience/VirtualMachineModelBuilder.cs @@ -0,0 +1,57 @@ +using Azure.ResourceManager.Core; +using System; + +namespace Proto.Compute.Convenience +{ + /// + /// A class representing a builder object to help create a virtual machine. + /// + public class VirtualMachineModelBuilder : VirtualMachineModelBuilderBase + { + /// + /// Initializes a new instance of the class. + /// + /// The container to create the virtual machine in. + /// The data model representing the virtual machine to create. + public VirtualMachineModelBuilder(VirtualMachineContainer containerOperations, VirtualMachineData vm): base(containerOperations, vm) + { + // TODO: GENERATOR Update Builder after models are incorporated in generated models + // _model.Name = vmName; + //_model = new VirtualMachine(location); + } + + /// + /// Attaches a disk to the virtual machine to be created. + /// + /// The disk to attach. + /// An instance of + public VirtualMachineModelBuilderBase AttachDataDisk(TrackedResource azureEntity) + { + throw new NotImplementedException(); + } + + /// + public override VirtualMachineModelBuilderBase UseWindowsImage(string adminUser, string password) + { + throw new NotImplementedException(); + } + + /// + public override VirtualMachineModelBuilderBase UseLinuxImage(string adminUser, string password) + { + throw new NotImplementedException(); + } + + /// + public override VirtualMachineModelBuilderBase RequiredNetworkInterface(ResourceIdentifier nicResourceId) + { + throw new NotImplementedException(); + } + + /// + public override VirtualMachineModelBuilderBase RequiredAvalabilitySet(ResourceIdentifier asetResourceId) + { + throw new NotImplementedException(); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/Convenience/VirtualMachineModelBuilderBase.cs b/sdk/resourcemanager/Proto.Client/compute/Convenience/VirtualMachineModelBuilderBase.cs new file mode 100644 index 0000000000000..b3ee5ee648f8d --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/Convenience/VirtualMachineModelBuilderBase.cs @@ -0,0 +1,50 @@ +using Azure.ResourceManager.Core; + +namespace Proto.Compute.Convenience +{ + /// + /// A class representing the base for a builder object to help create a virtual machine. + /// + public abstract class VirtualMachineModelBuilderBase : ArmBuilder + { + /// + /// Initializes a new instance of . + /// + /// The that the virtual machine will be built in. + /// The data model representing the virtual machine to create. + protected VirtualMachineModelBuilderBase(VirtualMachineContainer containerOperations, VirtualMachineData vm) + : base(containerOperations, vm) + { + } + + /// + /// Tells the builder to use a windows image. + /// + /// The admin username for the virtual machine. + /// The asmin password for the virtual machine. + /// An instance of . + public abstract VirtualMachineModelBuilderBase UseWindowsImage(string adminUser, string password); + + /// + /// Tells the builder to use a linux image. + /// + /// The admin username for the virtual machine. + /// The asmin password for the virtual machine. + /// An instance of . + public abstract VirtualMachineModelBuilderBase UseLinuxImage(string adminUser, string password); + + /// + /// Tells the builder to use a specific network interface. + /// + /// The network interface identifier. + /// An instance of . + public abstract VirtualMachineModelBuilderBase RequiredNetworkInterface(ResourceIdentifier nicResourceId); + + /// + /// Tells the builder to use a specific availability set. + /// + /// The availability set identifier. + /// An instance of . + public abstract VirtualMachineModelBuilderBase RequiredAvalabilitySet(ResourceIdentifier asetResourceId); + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/Extensions/ArmClientExtensions.cs b/sdk/resourcemanager/Proto.Client/compute/Extensions/ArmClientExtensions.cs new file mode 100644 index 0000000000000..a416f381c1c5e --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/Extensions/ArmClientExtensions.cs @@ -0,0 +1,46 @@ +using Azure.ResourceManager.Core; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Proto.Compute +{ + /// + /// A class to add extension methods to an ArmClient. + /// + public static class ArmClientExtensions + { + + /// + /// Gets the AvailabilitySetOperations. + /// + /// The instance the method will execute against. + /// The ResourceIdentifier of the resource that is the target of operations. + /// Returns an object representing the operations that can be performed over a specific . + /// ResourceIdentifier provided is not for an AvailabilitySet. + public static AvailabilitySetOperations GetAvailabilitySetOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) + { + if (resourceId.Type != AvailabilitySetOperations.ResourceType) + { + throw new ArgumentException("ResourceIdentifier provided is not for an AvailabilitySet."); + } + return client.GetSubscriptionOperations(resourceId.Subscription).GetResourceGroupOperations(resourceId.ResourceGroup).GetAvailabilitySetOperations(resourceId.Name); + } + + /// + /// Gets the VirtualMachineOperations. + /// + /// The instance the method will execute against. + /// The ResourceIdentifier of the resource that is the target of operations. + /// Returns an object representing the operations that can be performed over a specific . + /// ResourceIdentifier provided is not for a VirtualMachine. + public static VirtualMachineOperations GetVirtualMachineOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) + { + if (resourceId.Type != VirtualMachineOperations.ResourceType) + { + throw new ArgumentException("ResourceIdentifier provided is not for a VirtualMachine."); + } + return client.GetSubscriptionOperations(resourceId.Subscription).GetResourceGroupOperations(resourceId.ResourceGroup).GetVirtualMachineOperations(resourceId.Name); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/Extensions/AzureResourceManagerClientOptionsExtensions.cs b/sdk/resourcemanager/Proto.Client/compute/Extensions/AzureResourceManagerClientOptionsExtensions.cs new file mode 100644 index 0000000000000..de3bb64817512 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/Extensions/AzureResourceManagerClientOptionsExtensions.cs @@ -0,0 +1,20 @@ +using Azure.ResourceManager.Core; + +namespace Proto.Compute.Extensions +{ + /// + /// A class to add extension methods to AzureResourceManagerClientOptions. + /// + public static class AzureResourceManagerClientOptionsExtensions + { + /// + /// Adds a method to AzureResourceManagerClientOptions which returns all the versions to all resources inside the compute resource provider. + /// + /// The instance the method will execute against. + /// Returns a response with the operation for this resource. + public static ComputeRestApiVersions GetComputeRestApiVersions(this AzureResourceManagerClientOptions azureResourceManagerClientOptions) + { + return azureResourceManagerClientOptions.GetOverrideObject(() => new ComputeRestApiVersions()) as ComputeRestApiVersions; + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/Extensions/ResourceGroupExtensions.cs b/sdk/resourcemanager/Proto.Client/compute/Extensions/ResourceGroupExtensions.cs new file mode 100644 index 0000000000000..59099c393513b --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/Extensions/ResourceGroupExtensions.cs @@ -0,0 +1,56 @@ +using Azure.ResourceManager.Core; + +namespace Proto.Compute +{ + /// + /// A class to add extension methods to ResourceGroup. + /// + public static class ResourceGroupExtensions + { + #region VirtualMachines + /// + /// Gets an object representing the operations that can be performed over a specific VirtualMachine. + /// + /// The instance the method will execute against. + /// The name of the VirtualMachine. + /// Returns an object representing the operations that can be performed over a specific . + public static VirtualMachineOperations GetVirtualMachineOperations(this ResourceGroupOperations resourceGroup, string vmName) + { + return new VirtualMachineOperations(resourceGroup, vmName); + } + + /// + /// Gets an object representing a VirtualMachineContainer along with the instance operations that can be performed on it. + /// + /// The instance the method will execute against. + /// Returns a object. + public static VirtualMachineContainer GetVirtualMachineContainer(this ResourceGroupOperations resourceGroup) + { + return new VirtualMachineContainer(resourceGroup); + } + #endregion + + #region AvailabilitySets + /// + /// Gets an object representing the operations that can be performed over a specific AvailabilitySet. + /// + /// The instance the method will execute against. + /// The name of the AvailibilitySet. + /// Returns an object representing the operations that can be performed over a specific . + public static AvailabilitySetOperations GetAvailabilitySetOperations(this ResourceGroupOperations resourceGroup, string availabilitySetName) + { + return new AvailabilitySetOperations(resourceGroup, availabilitySetName); + } + + /// + /// Gets an object representing a AvailabilitySetContainer along with the instance operations that can be performed on it. + /// + /// The instance the method will execute against. + /// Returns an object. + public static AvailabilitySetContainer GetAvailabilitySetContainer(this ResourceGroupOperations resourceGroup) + { + return new AvailabilitySetContainer(resourceGroup); + } + #endregion + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/Extensions/SubscriptionExtensions.cs b/sdk/resourcemanager/Proto.Client/compute/Extensions/SubscriptionExtensions.cs new file mode 100644 index 0000000000000..8a9c3d619395a --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/Extensions/SubscriptionExtensions.cs @@ -0,0 +1,117 @@ +using Azure; +using Azure.ResourceManager.Compute; +using Azure.ResourceManager.Core; +using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Core.Resources; +using System.Threading; + +namespace Proto.Compute +{ + /// + /// Extension methods for convenient access on SubscriptionOperations in a client + /// + public static class SubscriptionExtensions + { + #region Virtual Machine List Operations + /// + /// Lists the VirtualMachines for this SubscriptionOperations. + /// + /// The instance the method will execute against. + /// A collection of resource operations that may take multiple service requests to iterate over. + public static Pageable ListVirtualMachines(this SubscriptionOperations subscription) + { + ComputeManagementClient computeClient = GetComputeClient(subscription); + var vmOperations = computeClient.VirtualMachines; + var result = vmOperations.ListAll(); + return new PhWrappingPageable( + result, + s => new VirtualMachine(subscription, new VirtualMachineData(s))); + } + + private static ComputeManagementClient GetComputeClient(SubscriptionOperations subscription) + { + return new ComputeManagementClient( + subscription.BaseUri, + subscription.Id.Subscription, + subscription.Credential, + subscription.ClientOptions.Convert()); + } + + /// + /// Lists the VirtualMachines for this SubscriptionOperations. + /// + /// The instance the method will execute against. + /// An async collection of resource operations that may take multiple service requests to iterate over. + public static AsyncPageable ListVirtualMachinesAsync(this SubscriptionOperations subscription) + { + var vmOperations = GetComputeClient(subscription).VirtualMachines; + var result = vmOperations.ListAllAsync(); + return new PhWrappingAsyncPageable( + result, + s => new VirtualMachine(subscription, new VirtualMachineData(s))); + } + + /// + /// Filters the list of VMs for a SubscriptionOperations represented as generic resources. + /// + /// The instance the method will execute against. + /// The string to filter the list. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of resource operations that may take multiple service requests to iterate over. + public static Pageable ListVirtualMachinesByName(this SubscriptionOperations subscription, string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(VirtualMachineOperations.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContext(subscription, filters, top, cancellationToken); + } + + /// + /// Filters the list of VMs for a SubscriptionOperations represented as generic resources. + /// + /// The instance the method will execute against. + /// The string to filter the list. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of resource operations that may take multiple service requests to iterate over. + public static AsyncPageable ListVirtualMachinesByNameAsync(this SubscriptionOperations subscription, string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(VirtualMachineOperations.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContextAsync(subscription, filters, top, cancellationToken); + } + #endregion + + #region AvailabilitySet List Operations + /// + /// Lists the AvailabilitySets for this SubscriptionOperations. + /// + /// The instance the method will execute against. + /// A collection of resource operations that may take multiple service requests to iterate over. + public static Pageable ListAvailabilitySets(this SubscriptionOperations subscription) + { + ComputeManagementClient computeClient = GetComputeClient(subscription); + var availabilitySetOperations = computeClient.AvailabilitySets; + var result = availabilitySetOperations.ListBySubscription(); + return new PhWrappingPageable( + result, + s => new AvailabilitySet(subscription, new AvailabilitySetData(s))); + } + + /// + /// Lists the AvailabilitySets for this SubscriptionOperations. + /// + /// The instance the method will execute against. + /// An async collection of resource operations that may take multiple service requests to iterate over. + public static AsyncPageable ListAvailabilitySetsAsync(this SubscriptionOperations subscription) + { + ComputeManagementClient computeClient = GetComputeClient(subscription); + var availabilitySetOperations = computeClient.AvailabilitySets; + var result = availabilitySetOperations.ListBySubscriptionAsync(); + return new PhWrappingAsyncPageable( + result, + s => new AvailabilitySet(subscription, new AvailabilitySetData(s))); + } + #endregion + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/Placeholder/AvailabilitySetData.cs b/sdk/resourcemanager/Proto.Client/compute/Placeholder/AvailabilitySetData.cs new file mode 100644 index 0000000000000..eeb3884ddd7cc --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/Placeholder/AvailabilitySetData.cs @@ -0,0 +1,74 @@ +using Azure.ResourceManager.Compute.Models; +using Azure.ResourceManager.Core; +using System; +using System.Collections.Generic; + +namespace Proto.Compute +{ + /// + /// A class representing the availability set data model. + /// + public class AvailabilitySetData : TrackedResource + { + /// + /// Gets the resource type definition for an availability set. + /// + public static ResourceType ResourceType => "Microsoft.Compute/availabilitySets"; + + /// + /// Initializes a new instance of the class. + /// + /// The availability set to initialize. + public AvailabilitySetData(Azure.ResourceManager.Compute.Models.AvailabilitySet aset) : base(aset.Id, aset.Location, aset) + { + if (null == aset.Tags) + { + aset.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + } + } + + /// Resource tags. + public override IDictionary Tags => Model.Tags; + + /// Resource name. + public override string Name => Model.Name; + + /// Sku of the availability set, only name is required to be set. See AvailabilitySetSkuTypes for possible set of values. Use 'Aligned' for virtual machines with managed disks and 'Classic' for virtual machines with unmanaged disks. Default value is 'Classic'. + public Azure.ResourceManager.Compute.Models.Sku Sku + { + get => Model.Sku; + set => Model.Sku = value; + } + + /// The number of update domains that the availaility set can span. + public int? PlatformUpdateDomainCount + { + get => Model.PlatformUpdateDomainCount; + set => Model.PlatformUpdateDomainCount = value; + } + + /// The number of fault domains that the availaility set can span. + public int? PlatformFaultDomainCount + { + get => Model.PlatformFaultDomainCount; + set => Model.PlatformFaultDomainCount = value; + } + + /// A list of references to all virtual machines in the availability set. + public IList VirtualMachines + { + get => Model.VirtualMachines; + set => Model.VirtualMachines = value; + } + + /// Specifies information about the proximity placement group that the availability set should be assigned to. <br><br>Minimum api-version: 2018-04-01. + public SubResource ProximityPlacementGroup + { + get => Model.ProximityPlacementGroup; + set => Model.ProximityPlacementGroup = value; + } + + /// The resource status information. + public IList Statuses => Model.Statuses; + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/Placeholder/VirtualMachineData.cs b/sdk/resourcemanager/Proto.Client/compute/Placeholder/VirtualMachineData.cs new file mode 100644 index 0000000000000..85da7d80752d3 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/Placeholder/VirtualMachineData.cs @@ -0,0 +1,180 @@ +using Azure.ResourceManager.Compute.Models; +using Azure.ResourceManager.Core; +using System; +using System.Collections.Generic; + +namespace Proto.Compute +{ + /// + /// A class representing the VirtualMachine data model. + /// + public class VirtualMachineData : TrackedResource + { + /// + /// Initializes a new instance of the class. + /// + /// The virtual machine to initialize. + public VirtualMachineData(Azure.ResourceManager.Compute.Models.VirtualMachine vm) : base(vm.Id, vm.Location, vm) + { + if (null == vm.Tags) + { + vm.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + } + } + + /// Resource tags. + public override IDictionary Tags => Model.Tags; + + /// Resource name. + public override string Name => Model.Name; + + /// The virtual machine instance view. + public VirtualMachineInstanceView InstanceView => Model.InstanceView; + + /// The provisioning state, which only appears in the response. + public string ProvisioningState => Model.ProvisioningState; + + /// Specifies information about the dedicated host that the virtual machine resides in. <br><br>Minimum api-version: 2018-10-01. + public SubResource Host + { + get => Model.Host; + set => Model.Host = value; + } + + /// Specifies the billing related details of a Azure Spot virtual machine. <br><br>Minimum api-version: 2019-03-01. + public BillingProfile BillingProfile + { + get => Model.BillingProfile; + set => Model.BillingProfile = value; + } + + /// Specifies the eviction policy for the Azure Spot virtual machine and Azure Spot scale set. <br><br>For Azure Spot virtual machines, the only supported value is 'Deallocate' and the minimum api-version is 2019-03-01. <br><br>For Azure Spot scale sets, both 'Deallocate' and 'Delete' are supported and the minimum api-version is 2017-10-30-preview. + public VirtualMachineEvictionPolicyTypes? EvictionPolicy + { + get => Model.EvictionPolicy; + set => Model.EvictionPolicy = value; + } + + /// Specifies the priority for the virtual machine. <br><br>Minimum api-version: 2019-03-01. + public VirtualMachinePriorityTypes? Priority + { + get => Model.Priority; + set => Model.Priority = value; + } + + /// Specifies information about the proximity placement group that the virtual machine should be assigned to. <br><br>Minimum api-version: 2018-04-01. + public SubResource ProximityPlacementGroup + { + get => Model.ProximityPlacementGroup; + set => Model.ProximityPlacementGroup = value; + } + + /// Specifies information about the virtual machine scale set that the virtual machine should be assigned to. Virtual machines specified in the same virtual machine scale set are allocated to different nodes to maximize availability. Currently, a VM can only be added to virtual machine scale set at creation time. An existing VM cannot be added to a virtual machine scale set. <br><br>This property cannot exist along with a non-null properties.availabilitySet reference. <br><br>Minimum api‐version: 2019‐03‐01. + public SubResource VirtualMachineScaleSet + { + get => Model.VirtualMachineScaleSet; + set => Model.VirtualMachineScaleSet = value; + } + + /// Specifies information about the availability set that the virtual machine should be assigned to. Virtual machines specified in the same availability set are allocated to different nodes to maximize availability. For more information about availability sets, see [Manage the availability of virtual machines](https://docs.microsoft.com/azure/virtual-machines/virtual-machines-windows-manage-availability?toc=%2fazure%2fvirtual-machines%2fwindows%2ftoc.json). <br><br> For more information on Azure planned maintenance, see [Planned maintenance for virtual machines in Azure](https://docs.microsoft.com/azure/virtual-machines/virtual-machines-windows-planned-maintenance?toc=%2fazure%2fvirtual-machines%2fwindows%2ftoc.json) <br><br> Currently, a VM can only be added to availability set at creation time. The availability set to which the VM is being added should be under the same resource group as the availability set resource. An existing VM cannot be added to an availability set. <br><br>This property cannot exist along with a non-null properties.virtualMachineScaleSet reference. + public SubResource AvailabilitySet + { + get => Model.AvailabilitySet; + set => Model.AvailabilitySet = value; + } + + /// Specifies the boot diagnostic settings state. <br><br>Minimum api-version: 2015-06-15. + public DiagnosticsProfile DiagnosticsProfile + { + get => Model.DiagnosticsProfile; + set => Model.DiagnosticsProfile = value; + } + + /// Specifies the network interfaces of the virtual machine. + public NetworkProfile NetworkProfile + { + get => Model.NetworkProfile; + set => Model.NetworkProfile = value; + } + + /// Specifies the operating system settings used while creating the virtual machine. Some of the settings cannot be changed once VM is provisioned. + public OSProfile OsProfile + { + get => Model.OsProfile; + set => Model.OsProfile = value; + } + + /// Specifies additional capabilities enabled or disabled on the virtual machine. + public AdditionalCapabilities AdditionalCapabilities + { + get => Model.AdditionalCapabilities; + set => Model.AdditionalCapabilities = value; + } + + /// Specifies the storage settings for the virtual machine disks. + public StorageProfile StorageProfile + { + get => Model.StorageProfile; + set => Model.StorageProfile = value; + } + + /// Specifies the hardware settings for the virtual machine. + public HardwareProfile HardwareProfile + { + get => Model.HardwareProfile; + set => Model.HardwareProfile = value; + } + + /// The virtual machine zones. + public IList Zones + { + get => Model.Zones; + set => Model.Zones = value; + } + + /// The identity of the virtual machine, if configured. + public ResourceIdentity Identity + { + get => VmIdentityToIdentity(Model.Identity); + } + + private ResourceIdentity VmIdentityToIdentity(VirtualMachineIdentity vmIdentity) + { + SystemAssignedIdentity systemAssignedIdentity = new SystemAssignedIdentity(new Guid(vmIdentity.TenantId), new Guid(vmIdentity.PrincipalId)); + var userAssignedIdentities = new Dictionary(); + if (vmIdentity.UserAssignedIdentities != null) + { + foreach (var entry in vmIdentity.UserAssignedIdentities) + { + ResourceIdentifier resourceId = new ResourceIdentifier(entry.Key); + var userAssignedIdentity = new UserAssignedIdentity(new Guid(entry.Value.ClientId), new Guid(entry.Value.PrincipalId)); + userAssignedIdentities[resourceId] = userAssignedIdentity; + } + } + + return new ResourceIdentity(systemAssignedIdentity, userAssignedIdentities); + } + + /// + /// Gets the virtual machine extensions. + /// + public IList Resources => Model.Resources; + + /// Specifies information about the marketplace image used to create the virtual machine. This element is only used for marketplace images. Before you can use a marketplace image from an API, you must enable the image for programmatic use. In the Azure portal, find the marketplace image that you want to use and then click **Want to deploy programmatically, Get Started ->**. Enter any required information and then click **Save**. + public Azure.ResourceManager.Compute.Models.Plan Plan + { + get => Model.Plan; + set => Model.Plan = value; + } + + /// Specifies that the image or disk that is being used was licensed on-premises. This element is only used for images that contain the Windows Server operating system. <br><br> Possible values are: <br><br> Windows_Client <br><br> Windows_Server <br><br> If this element is included in a request for an update, the value must match the initial value. This value cannot be updated. <br><br> For more information, see [Azure Hybrid Use Benefit for Windows Server](https://docs.microsoft.com/azure/virtual-machines/virtual-machines-windows-hybrid-use-benefit-licensing?toc=%2fazure%2fvirtual-machines%2fwindows%2ftoc.json) <br><br> Minimum api-version: 2015-06-15. + public string LicenseType + { + get => Model.LicenseType; + set => Model.LicenseType = value; + } + + /// Specifies the VM unique ID which is a 128-bits identifier that is encoded and stored in all Azure IaaS VMs SMBIOS and can be read using platform BIOS commands. + public string VmId => Model.VmId; + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/Proto.Compute.csproj b/sdk/resourcemanager/Proto.Client/compute/Proto.Compute.csproj new file mode 100644 index 0000000000000..a54bd8ca5685e --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/Proto.Compute.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + Proto.Compute + latest + + + + + + + + + + + + + diff --git a/sdk/resourcemanager/Proto.Client/compute/VersionOverrides/AvailabilitySetsApiVersions.cs b/sdk/resourcemanager/Proto.Client/compute/VersionOverrides/AvailabilitySetsApiVersions.cs new file mode 100644 index 0000000000000..66c947505b17e --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/VersionOverrides/AvailabilitySetsApiVersions.cs @@ -0,0 +1,38 @@ +using Azure.ResourceManager.Core; + +namespace Proto.Compute +{ + /// + /// A class representing the valid api versions for an availability set. + /// + public class AvailabilitySetsApiVersions : ApiVersionsBase + { + /// + /// Api version for 2020/05/01. + /// + public static readonly AvailabilitySetsApiVersions V2020_05_01 = new AvailabilitySetsApiVersions("2020-05-01"); + + /// + /// Api version for 2019/12/01. + /// + public static readonly AvailabilitySetsApiVersions V2019_12_01 = new AvailabilitySetsApiVersions("2019-12-01"); + + /// + /// Default api version to use. + /// + public static readonly AvailabilitySetsApiVersions Default = V2020_05_01; + + private AvailabilitySetsApiVersions(string value) : base(value) { } + + /// + /// Converts an api version into a string. + /// + /// The api version instnace to convert. + public static implicit operator string(AvailabilitySetsApiVersions version) + { + if (version == null) + return null; + return version.ToString(); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/VersionOverrides/VirtualMachinesApiVersions.cs b/sdk/resourcemanager/Proto.Client/compute/VersionOverrides/VirtualMachinesApiVersions.cs new file mode 100644 index 0000000000000..adbfffde2c61c --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/VersionOverrides/VirtualMachinesApiVersions.cs @@ -0,0 +1,38 @@ +using Azure.ResourceManager.Core; + +namespace Proto.Compute +{ + /// + /// A class representing the valid api versions for a virtual machine. + /// + public class VirtualMachinesApiVersions : ApiVersionsBase + { + /// + /// Api version 2020/06/01 + /// + public static readonly VirtualMachinesApiVersions V2020_06_01 = new VirtualMachinesApiVersions("2020-06-01"); + + /// + /// Api version 2019/12/01 + /// + public static readonly VirtualMachinesApiVersions V2019_12_01 = new VirtualMachinesApiVersions("2019-12-01"); + + /// + /// Default api version to use. + /// + public static readonly VirtualMachinesApiVersions Default = V2020_06_01; + + private VirtualMachinesApiVersions(string value) : base(value) { } + + /// + /// Converts an api version into a string. + /// + /// The api version instnace to convert. + public static implicit operator string(VirtualMachinesApiVersions version) + { + if (version == null) + return null; + return version.ToString(); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/VirtualMachine.cs b/sdk/resourcemanager/Proto.Client/compute/VirtualMachine.cs new file mode 100644 index 0000000000000..f2397f51ec2ac --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/VirtualMachine.cs @@ -0,0 +1,39 @@ +using System.Threading.Tasks; +using Azure.ResourceManager.Core; + +namespace Proto.Compute +{ + /// + /// Class representing a VirtualMachine along with the instance operations that can be performed on it. + /// + public class VirtualMachine : VirtualMachineOperations + { + /// + /// Gets the data representing this VirtualMachine. + /// + public VirtualMachineData Data { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The resource that is the target of operations. + internal VirtualMachine(ResourceOperationsBase operations, VirtualMachineData resource) + : base(operations, resource.Id) + { + Data = resource; + } + + /// + protected override VirtualMachine GetResource() + { + return this; + } + + /// + protected override Task GetResourceAsync() + { + return Task.FromResult(this); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/VirtualMachineContainer.cs b/sdk/resourcemanager/Proto.Client/compute/VirtualMachineContainer.cs new file mode 100644 index 0000000000000..9dc5db0e1985d --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/VirtualMachineContainer.cs @@ -0,0 +1,225 @@ +using Azure; +using Azure.ResourceManager.Compute; +using Azure.ResourceManager.Compute.Models; +using Azure.ResourceManager.Core; +using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Core.Resources; +using Proto.Compute.Convenience; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Proto.Compute +{ + /// + /// A class representing collection of VirtualMachine and their operations over a ResourceGroup. + /// + public class VirtualMachineContainer : ResourceContainerBase + { + /// + /// Initializes a new instance of the class. + /// + /// The ResourceGroup that is the parent of the VirtualMachines. + internal VirtualMachineContainer(ResourceGroupOperations resourceGroup) + : base(resourceGroup) + { + } + + private VirtualMachinesOperations Operations => new ComputeManagementClient( + BaseUri, + Id.Subscription, + Credential, + ClientOptions.Convert()).VirtualMachines; + + /// + /// Gets the valid resource type for this object + /// + protected override ResourceType ValidResourceType => ResourceGroupOperations.ResourceType; + + /// + /// The operation to create a virtual machine. + /// + /// The name of the virtual machine. + /// Parameters supplied to the Create Virtual Machine operation. + /// A response with the operation for this resource. + public override ArmResponse CreateOrUpdate(string name, VirtualMachineData resourceDetails) + { + var operation = Operations.StartCreateOrUpdate(Id.ResourceGroup, name, resourceDetails.Model); + return new PhArmResponse( + operation.WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), + v => new VirtualMachine(Parent, new VirtualMachineData(v))); + } + + /// + /// The operation to create a virtual machine. + /// + /// The name of the virtual machine. + /// Parameters supplied to the Create Virtual Machine operation. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public async override Task> CreateOrUpdateAsync(string name, VirtualMachineData resourceDetails, CancellationToken cancellationToken = default) + { + var operation = await Operations.StartCreateOrUpdateAsync(Id.ResourceGroup, name, resourceDetails.Model, cancellationToken).ConfigureAwait(false); + return new PhArmResponse( + await operation.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + v => new VirtualMachine(Parent, new VirtualMachineData(v))); + } + + /// + /// The operation to create a virtual machine. + /// + /// The name of the virtual machine. + /// Parameters supplied to the Create Virtual Machine operation. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// An that allows polling for completion of the operation. + public override ArmOperation StartCreateOrUpdate(string name, VirtualMachineData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + Operations.StartCreateOrUpdate(Id.ResourceGroup, name, resourceDetails.Model, cancellationToken), + v => new VirtualMachine(Parent, new VirtualMachineData(v))); + } + + /// + /// The operation to create a virtual machine. + /// + /// The name of the virtual machine. + /// Parameters supplied to the Create Virtual Machine operation. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// A that on completion returns an that allows polling for completion of the operation. + public async override Task> StartCreateOrUpdateAsync(string name, VirtualMachineData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + await Operations.StartCreateOrUpdateAsync(Id.ResourceGroup, name, resourceDetails.Model, cancellationToken).ConfigureAwait(false), + v => new VirtualMachine(Parent, new VirtualMachineData(v))); + } + + /// + /// Construct an object used to create a VirtualMachine. + /// + /// The hostname for the virtual machine. + /// The admin username to use. + /// The admin password to use. + /// The network interface id to use. + /// The availability set id to use. + /// The location to create the Virtual Machine. + /// Object used to create a . + public VirtualMachineModelBuilder Construct(string hostName, string adminUser, string adminPassword, ResourceIdentifier networkInterfaceId, ResourceIdentifier availabilitySetId, LocationData location = null) + { + var parent = GetParentResource(); + var vm = new Azure.ResourceManager.Compute.Models.VirtualMachine(location ?? parent.Data.Location) + { + NetworkProfile = new NetworkProfile { NetworkInterfaces = new[] { new NetworkInterfaceReference() { Id = networkInterfaceId } } }, + OsProfile = new OSProfile + { + ComputerName = hostName, + AdminUsername = adminUser, + AdminPassword = adminPassword, + WindowsConfiguration = new WindowsConfiguration { TimeZone = "Pacific Standard Time", ProvisionVMAgent = true } + }, + StorageProfile = new StorageProfile() + { + ImageReference = new ImageReference() + { + Offer = "WindowsServer", + Publisher = "MicrosoftWindowsServer", + Sku = "2019-Datacenter", + Version = "latest" + }, + DataDisks = new List() + }, + HardwareProfile = new HardwareProfile() { VmSize = VirtualMachineSizeTypes.StandardB1Ms }, + AvailabilitySet = new SubResource() { Id = availabilitySetId } + }; + + return new VirtualMachineModelBuilder(this, new VirtualMachineData(vm)); + } + + /// + /// List the virtual machines for this resource group. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of that may take multiple service requests to iterate over. + public Pageable List(CancellationToken cancellationToken = default) + { + var result = Operations.List(Id.Name, cancellationToken); + return new PhWrappingPageable( + result, + s => new VirtualMachine(Parent, new VirtualMachineData(s))); + } + + /// + /// List the virtual machines for this resource group. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of that may take multiple service requests to iterate over. + public AsyncPageable ListAsync(CancellationToken cancellationToken = default) + { + var result = Operations.ListAsync(Id.Name, cancellationToken); + return new PhWrappingAsyncPageable( + result, + s => new VirtualMachine(Parent, new VirtualMachineData(s))); + } + + /// + /// Filters the list of virtual machines for this resource group represented as generic resources. + /// + /// The substring to filter by. + /// The number of items to truncate by. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of that may take multiple service requests to iterate over. + public Pageable ListByName(string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(VirtualMachineOperations.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContext(Parent as ResourceGroupOperations, filters, top, cancellationToken); + } + + /// + /// Filters the list of virtual machines for this resource group represented as generic resources. + /// + /// The substring to filter by. + /// The number of items to truncate by. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of that may take multiple service requests to iterate over. + public AsyncPageable ListByNameAsync(string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(VirtualMachineOperations.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContextAsync(Parent as ResourceGroupOperations, filters, top, cancellationToken); + } + + /// + /// Filters the list of virtual machines for this resource group represented as generic resources. + /// Makes an additional network call to retrieve the full data model for each virtual machine. + /// + /// The substring to filter by. + /// The number of items to truncate by. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of that may take multiple service requests to iterate over. + public Pageable ListByNameExpanded(string filter, int? top = null, CancellationToken cancellationToken = default) + { + var results = ListByName(filter, top, cancellationToken); + return new PhWrappingPageable(results, s => (new VirtualMachineOperations(s)).Get().Value); + } + + /// + /// Filters the list of virtual machines for this resource group represented as generic resources. + /// Makes an additional network call to retrieve the full data model for each virtual machine. + /// + /// The substring to filter by. + /// The number of items to truncate by. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of that may take multiple service requests to iterate over. + public AsyncPageable ListByNameExpandedAsync(string filter, int? top = null, CancellationToken cancellationToken = default) + { + var results = ListByNameAsync(filter, top, cancellationToken); + return new PhWrappingAsyncPageable(results, s => (new VirtualMachineOperations(s)).Get().Value); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/compute/VirtualMachineOperations.cs b/sdk/resourcemanager/Proto.Client/compute/VirtualMachineOperations.cs new file mode 100644 index 0000000000000..da686b9369118 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/VirtualMachineOperations.cs @@ -0,0 +1,449 @@ +using Azure; +using Azure.ResourceManager.Compute; +using Azure.ResourceManager.Compute.Models; +using Azure.ResourceManager.Core; +using Azure.ResourceManager.Core.Adapters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Proto.Compute +{ + /// + /// A class representing the operations that can be performed over a specific VirtualMachine. + /// + public class VirtualMachineOperations : ResourceOperationsBase, ITaggableResource, IDeletableResource + { + /// + /// Initializes a new instance of the class. + /// + /// An instance of that has an id for a virtual machine. + internal VirtualMachineOperations(GenericResourceOperations genericOperations) + : base(genericOperations) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + internal VirtualMachineOperations(ResourceGroupOperations resourceGroup, string vmName) + : base(resourceGroup, $"{resourceGroup.Id}/providers/Microsoft.Compute/virtualMachines/{vmName}") + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + protected VirtualMachineOperations(ResourceOperationsBase operation, ResourceIdentifier id) + : base(operation, id) + { + } + + /// + /// Gets the resource type definition for a virtual machine. + /// + public static readonly ResourceType ResourceType = "Microsoft.Compute/virtualMachines"; + + /// + /// Gets the valid resources for virtual machines. + /// + protected override ResourceType ValidResourceType => ResourceType; + + private VirtualMachinesOperations Operations => new ComputeManagementClient( + BaseUri, + Id.Subscription, + Credential, + ClientOptions.Convert()).VirtualMachines; + + /// + /// Initializes a new instance of the class from a . + /// + /// An instance of that has an id for a virtual machine. + /// A new instance of the class. + public static VirtualMachineOperations FromGeneric(GenericResourceOperations genericOperations) + { + return new VirtualMachineOperations(genericOperations); + } + + /// + /// The operation to delete a virtual machine. + /// + /// A response with the operation for this resource. + public ArmResponse Delete() + { + return new ArmResponse(Operations.StartDelete(Id.ResourceGroup, Id.Name).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// The operation to delete a virtual machine. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public async Task> DeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmResponse((await Operations.StartDeleteAsync(Id.ResourceGroup, Id.Name)).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// The operation to delete a virtual machine. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// An that allows polling for completion of the operation. + public ArmOperation StartDelete(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(Operations.StartDelete(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + /// The operation to delete a virtual machine. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> StartDeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(await Operations.StartDeleteAsync(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + #region PowerOn + /// + /// The operation to start a virtual machine. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A response with the operation for this resource. + public ArmResponse PowerOn(CancellationToken cancellationToken = default) + { + var operation = Operations.StartStart(Id.ResourceGroup, Id.Name, cancellationToken); + return new ArmResponse(operation.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// The operation to start a virtual machine. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public async Task> PowerOnAsync(CancellationToken cancellationToken = default) + { + var operation = await Operations.StartStartAsync(Id.ResourceGroup, Id.Name, cancellationToken).ConfigureAwait(false); + return new ArmResponse(await operation.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false)); + } + + /// + /// The operation to start a virtual machine. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An that allows polling for completion of the operation. + public ArmOperation StartPowerOn(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(Operations.StartStart(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + /// The operation to start a virtual machine. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> StartPowerOnAsync(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(await Operations.StartStartAsync(Id.ResourceGroup, Id.Name, cancellationToken).ConfigureAwait(false)); + } + #endregion + + #region PowerOff + /// + /// The operation to power off (stop) a virtual machine. The virtual machine can be restarted with the same provisioned resources. You are still charged for this virtual machine. + /// + /// The parameter to request non-graceful VM shutdown. True value for this flag indicates non-graceful shutdown whereas false indicates otherwise. Default value for this flag is false if not specified. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A response with the operation for this resource. + public ArmResponse PowerOff(bool? skipShutdown = null, CancellationToken cancellationToken = default) + { + var operation = Operations.StartPowerOff(Id.ResourceGroup, Id.Name, skipShutdown, cancellationToken); + return new ArmResponse(operation.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// The operation to power off (stop) a virtual machine. The virtual machine can be restarted with the same provisioned resources. You are still charged for this virtual machine. + /// + /// The parameter to request non-graceful VM shutdown. True value for this flag indicates non-graceful shutdown whereas false indicates otherwise. Default value for this flag is false if not specified. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public async Task> PowerOffAsync(bool? skipShutdown = null, CancellationToken cancellationToken = default) + { + var operation = await Operations.StartPowerOffAsync(Id.ResourceGroup, Id.Name, skipShutdown, cancellationToken).ConfigureAwait(false); + return new ArmResponse(await operation.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false)); + } + + /// + /// The operation to power off (stop) a virtual machine. The virtual machine can be restarted with the same provisioned resources. You are still charged for this virtual machine. + /// + /// The parameter to request non-graceful VM shutdown. True value for this flag indicates non-graceful shutdown whereas false indicates otherwise. Default value for this flag is false if not specified. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An that allows polling for completion of the operation. + public ArmOperation StartPowerOff(bool? skipShutdown = null, CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(Operations.StartPowerOff(Id.ResourceGroup, Id.Name, skipShutdown, cancellationToken)); + } + + /// + /// The operation to power off (stop) a virtual machine. The virtual machine can be restarted with the same provisioned resources. You are still charged for this virtual machine. + /// + /// The parameter to request non-graceful VM shutdown. True value for this flag indicates non-graceful shutdown whereas false indicates otherwise. Default value for this flag is false if not specified. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> StartPowerOffAsync(bool? skipShutdown = null, CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(await Operations.StartPowerOffAsync(Id.ResourceGroup, Id.Name, skipShutdown, cancellationToken).ConfigureAwait(false)); + } + #endregion + + /// + public override ArmResponse Get() + { + return new PhArmResponse( + Operations.Get(Id.ResourceGroup, Id.Name), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + public override async Task> GetAsync(CancellationToken cancellationToken = default) + { + return new PhArmResponse( + await Operations.GetAsync(Id.ResourceGroup, Id.Name, cancellationToken), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + /// The operation to update a virtual machine. Please note some properties can be set only during virtual machine creation. + /// + /// The parameters to update. + /// An that allows polling for completion of the operation. + public ArmOperation StartUpdate(VirtualMachineUpdate patchable) + { + return new PhArmOperation( + Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + /// The operation to update a virtual machine. Please note some properties can be set only during virtual machine creation. + /// + /// The parameters to update. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> StartUpdateAsync(VirtualMachineUpdate patchable, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + /// Add a tag to a virtual machine. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// An that allows polling for completion of the operation. + public ArmResponse AddTag(string key, string value) + { + var vm = GetResource(); + var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; + UpdateTags(key, value, patchable.Tags); + + return new PhArmResponse( + Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + /// Add a tag to a virtual machine. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + var vm = await GetResourceAsync(); + var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; + UpdateTags(key, value, patchable.Tags); + + return new PhArmResponse( + await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken).Result.WaitForCompletionAsync(), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + /// Add a tag to a virtual machine. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// An that allows polling for completion of the operation. + public ArmOperation StartAddTag(string key, string value) + { + var vm = GetResource(); + var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; + UpdateTags(key, value, patchable.Tags); + + return new PhArmOperation( + Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + /// Add a tag to a virtual machine. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + var vm = await GetResourceAsync(); + var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; + UpdateTags(key, value, patchable.Tags); + + return new PhArmOperation( + await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + public ArmResponse SetTags(IDictionary tags) + { + var vm = GetResource(); + var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; + ReplaceTags(tags, patchable.Tags); + + return new PhArmResponse( + Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var vm = await GetResourceAsync(); + var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; + ReplaceTags(tags, patchable.Tags); + + return new PhArmResponse( + await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken).Result.WaitForCompletionAsync(), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + public ArmOperation StartSetTags(IDictionary tags) + { + var vm = GetResource(); + var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; + ReplaceTags(tags, patchable.Tags); + + return new PhArmOperation( + Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var vm = await GetResourceAsync(); + var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; + ReplaceTags(tags, patchable.Tags); + + return new PhArmOperation( + await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken).Result.WaitForCompletionAsync(), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + public ArmResponse RemoveTag(string key) + { + var vm = GetResource(); + var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; + DeleteTag(key, patchable.Tags); + + return new PhArmResponse( + Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var vm = await GetResourceAsync(); + var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; + DeleteTag(key, patchable.Tags); + + return new PhArmResponse( + await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken).Result.WaitForCompletionAsync(), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + public ArmOperation StartRemoveTag(string key) + { + var vm = GetResource(); + var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; + DeleteTag(key, patchable.Tags); + + return new PhArmOperation( + Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var vm = await GetResourceAsync(); + var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; + DeleteTag(key, patchable.Tags); + + return new PhArmOperation( + await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken).Result.WaitForCompletionAsync(), + v => new VirtualMachine(this, new VirtualMachineData(v))); + } + + /// + /// Lists all available geo-locations. + /// + /// A collection of location that may take multiple service requests to iterate over. + public IEnumerable ListAvailableLocations() + { + var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); + var vmProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var vmResource = vmProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return vmResource.Locations.Select(l => (LocationData)l); + } + + /// + /// Lists all available geo-locations. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of location that may take multiple service requests to iterate over. + /// The default subscription id is null. + public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) + { + var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); + var vmProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var vmResource = vmProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return vmResource.Locations.Select(l => (LocationData)l); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/Extensions/ArmClientExtensions.cs b/sdk/resourcemanager/Proto.Client/network/Extensions/ArmClientExtensions.cs new file mode 100644 index 0000000000000..10ab18d7a7c79 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/Extensions/ArmClientExtensions.cs @@ -0,0 +1,108 @@ +using Azure.ResourceManager.Core; +using Azure.ResourceManager.Network.Models; +using Proto.Network; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Proto.Network +{ + /// + /// A class to add extension methods to an ArmClient. + /// + public static class ArmClientExtensions + { + + /// + /// Gets the NetworkInterfaceOperations. + /// + /// The instance the method will execute against. + /// The ResourceIdentifier of the resource that is the target of operations. + /// Returns an object representing the operations that can be performed over a specific . + /// ResourceIdentifier provided is not for a NetworkInterfaceOperations. + public static NetworkInterfaceOperations GetNetworkInterfaceOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) + { + if (resourceId.Type != NetworkInterfaceOperations.ResourceType) + { + throw new ArgumentException("ResourceIdentifier provided is not for a Network Interface."); + } + var subOps = client.GetSubscriptionOperations(resourceId.Subscription); + var rgOps = subOps.GetResourceGroupOperations(resourceId.ResourceGroup); + return rgOps.GetNetworkInterfaceOperations(resourceId.Name); + } + + /// + /// Gets the NetworkSecurityGroupOperations. + /// + /// The instance the method will execute against. + /// The ResourceIdentifier of the resource that is the target of operations. + /// Returns an object representing the operations that can be performed over a specific . + /// ResourceIdentifier provided is not for a NetworkSecurityGroup. + public static NetworkSecurityGroupOperations GetNetworkSecurityGroupOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) + { + if (resourceId.Type != NetworkSecurityGroupOperations.ResourceType) + { + throw new ArgumentException("ResourceIdentifier provided is not for a NetworkSecurityGroup."); + } + var subOps = client.GetSubscriptionOperations(resourceId.Subscription); + var rgOps = subOps.GetResourceGroupOperations(resourceId.ResourceGroup); + return rgOps.GetNetworkSecurityGroupOperations(resourceId.Name); + } + + /// + /// Gets the PublicIpAddressOperations. + /// + /// The instance the method will execute against. + /// The ResourceIdentifier of the resource that is the target of operations. + /// Returns an object representing the operations that can be performed over a specific . + /// ResourceIdentifier provided is not for a PublicIpAddress. + public static PublicIpAddressOperations GetPublicIpAddressOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) + { + if (resourceId.Type != PublicIpAddressOperations.ResourceType) + { + throw new ArgumentException("ResourceIdentifier provided is not for a PublicIpAddress."); + } + var subOps = client.GetSubscriptionOperations(resourceId.Subscription); + var rgOps = subOps.GetResourceGroupOperations(resourceId.ResourceGroup); + return rgOps.GetPublicIpAddressOperations(resourceId.Name); + } + + /// + /// Gets the SubnetOperations. + /// + /// The instance the method will execute against. + /// The ResourceIdentifier of the resource that is the target of operations. + /// Returns an object representing the operations that can be performed over a specific . + /// ResourceIdentifier provided is not for a Subnet. + public static SubnetOperations GetSubnetOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) + { + if (resourceId.Type != SubnetOperations.ResourceType) + { + throw new ArgumentException("ResourceIdentifier provided is not for a Subnet."); + } + var subOps = client.GetSubscriptionOperations(resourceId.Subscription); + var rgOps = subOps.GetResourceGroupOperations(resourceId.ResourceGroup); + var vnetOps = rgOps.GetVirtualNetworkOperations(resourceId.Parent.Name); + return vnetOps.GetSubnetOperations(resourceId.Name); + } + + /// + /// Gets the VirtualNetworkOperations. + /// + /// The instance the method will execute against. + /// The ResourceIdentifier of the resource that is the target of operations. + /// Returns an object representing the operations that can be performed over a specific . + /// ResourceIdentifier provided is not for a VirtualNetwork. + public static VirtualNetworkOperations GetVirtualNetworkOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) + { + if (resourceId.Type != VirtualNetworkOperations.ResourceType) + { + throw new ArgumentException("ResourceIdentifier provided is not for a VirtualNetwork."); + } + var subOps = client.GetSubscriptionOperations(resourceId.Subscription); + var rgOps = subOps.GetResourceGroupOperations(resourceId.ResourceGroup); + return rgOps.GetVirtualNetworkOperations(resourceId.Parent.Name); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/Extensions/ResourceGroupExtensions.cs b/sdk/resourcemanager/Proto.Client/network/Extensions/ResourceGroupExtensions.cs new file mode 100644 index 0000000000000..5d67c819ab652 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/Extensions/ResourceGroupExtensions.cs @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.ResourceManager.Core; + +namespace Proto.Network +{ + /// + /// A class to add extension methods to resource group. + /// + public static class ResourceGroupExtensions + { + #region Virtual Network Operations + /// + /// Gets a for a given resource under a . + /// + /// The instance the method will execute against. + /// The resource id of data model. + /// An instance of . + public static VirtualNetworkOperations GetVirtualNetworkOperations(this ResourceGroupOperations resourceGroup, string virtualNetwork) + { + return new VirtualNetworkOperations(resourceGroup, virtualNetwork); + } + + /// + /// Gets a under a . + /// + /// The instance the method will execute against. + /// An instance of . + public static VirtualNetworkContainer GetVirtualNetworkContainer(this ResourceGroupOperations resourceGroup) + { + return new VirtualNetworkContainer(resourceGroup); + } + #endregion + + #region Public IP Address Operations + /// + /// Gets a under a . + /// + /// The instance the method will execute against. + /// The resource id of data model. + /// An instance of . + public static PublicIpAddressOperations GetPublicIpAddressOperations(this ResourceGroupOperations resourceGroup, string publicIpAddress) + { + return new PublicIpAddressOperations(resourceGroup, publicIpAddress); + } + + /// + /// Gets a under a . + /// + /// The instance the method will execute against. + /// An instance of . + public static PublicIpAddressContainer GetPublicIpAddressContainer(this ResourceGroupOperations resourceGroup) + { + return new PublicIpAddressContainer(resourceGroup); + } + #endregion + + #region Network Interface (NIC) operations + /// + /// Gets the operations over a specific + /// + /// The operations over a specific resource group. + /// The network interface to target for operations. + /// A including the operations that can be peformed on it. + public static NetworkInterfaceOperations GetNetworkInterfaceOperations(this ResourceGroupOperations resourceGroup, string networkInterface) + { + return new NetworkInterfaceOperations(resourceGroup, networkInterface); + } + + /// + /// Gets the operations over the collection of contained in the resource group. + /// + /// The operations over a specific resource group. + /// A representing the collection of + public static NetworkInterfaceContainer GetNetworkInterfaceContainer(this ResourceGroupOperations resourceGroup) + { + return new NetworkInterfaceContainer(resourceGroup); + } + + /// + /// Gets the operations over the collection of contained in the resource group. + /// + /// The instance the method will execute against. + /// The resource id of data model. + /// An instance of . + public static NetworkSecurityGroupOperations GetNetworkSecurityGroupOperations(this ResourceGroupOperations resourceGroup, string networkSecurityGroup) + { + return new NetworkSecurityGroupOperations(resourceGroup, networkSecurityGroup); + } + + /// + /// Gets a under a . + /// + /// The instance the method will execute against. + /// An instance of . + public static NetworkSecurityGroupContainer GetNetworkSecurityGroupContainer(this ResourceGroupOperations resourceGroup) + { + return new NetworkSecurityGroupContainer(resourceGroup); + } + #endregion + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/Extensions/SubscriptionExtensions.cs b/sdk/resourcemanager/Proto.Client/network/Extensions/SubscriptionExtensions.cs new file mode 100644 index 0000000000000..84e66631beeaf --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/Extensions/SubscriptionExtensions.cs @@ -0,0 +1,161 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure; +using Azure.ResourceManager.Core; +using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Network; + +namespace Proto.Network +{ + /// + /// A class to add extension methods to Azure subscription. + /// + public static class SubscriptionExtensions + { + #region Virtual Network Operations + + private static NetworkManagementClient GetNetworkClient(SubscriptionOperations subscription) + { + return new NetworkManagementClient( + subscription.Id.Subscription, + subscription.BaseUri, + subscription.Credential, + subscription.ClientOptions.Convert()); + } + + /// + /// Lists the virtual networks for this subscription. + /// + /// The instance the method will execute against. + /// A collection of resource operations that may take multiple service requests to iterate over. + public static Pageable ListVnets(this SubscriptionOperations subscription) + { + NetworkManagementClient networkClient = GetNetworkClient(subscription); + var vmOperations = networkClient.VirtualNetworks; + var result = vmOperations.ListAll(); + return new PhWrappingPageable( + result, + s => new VirtualNetwork(subscription, new VirtualNetworkData(s))); + } + + /// + /// Lists the virtual networks for this subscription. + /// + /// The instance the method will execute against. + /// An async collection of resource operations that may take multiple service requests to iterate over. + public static AsyncPageable ListVnetsAsync(this SubscriptionOperations subscription) + { + NetworkManagementClient networkClient = GetNetworkClient(subscription); + var vmOperations = networkClient.VirtualNetworks; + var result = vmOperations.ListAllAsync(); + return new PhWrappingAsyncPageable( + result, + s => new VirtualNetwork(subscription, new VirtualNetworkData(s))); + } + + #endregion + + #region Public IP Address Operations + + /// + /// Lists the public IPs for this subscription. + /// + /// The instance the method will execute against. + /// A collection of resource operations that may take multiple service requests to iterate over. + public static Pageable ListPublicIps(this SubscriptionOperations subscription) + { + NetworkManagementClient networkClient = GetNetworkClient(subscription); + var publicIPAddressesOperations = networkClient.PublicIPAddresses; + var result = publicIPAddressesOperations.ListAll(); + return new PhWrappingPageable( + result, + s => new PublicIpAddress(subscription, new PublicIPAddressData(s))); + } + + /// + /// Lists the public IP addresses for this subscription. + /// + /// The instance the method will execute against. + /// An async collection of resource operations that may take multiple service requests to iterate over. + public static AsyncPageable ListPublicIpsAsync(this SubscriptionOperations subscription) + { + NetworkManagementClient networkClient = GetNetworkClient(subscription); + var publicIPAddressesOperations = networkClient.PublicIPAddresses; + var result = publicIPAddressesOperations.ListAllAsync(); + return new PhWrappingAsyncPageable( + result, + s => new PublicIpAddress(subscription, new PublicIPAddressData(s))); + } + + #endregion + + #region Network Interface (NIC) operations + + /// + /// Lists the for this . + /// + /// The to target for listing. + /// A collection of that may take multiple service requests to iterate over. + public static Pageable ListNics(this SubscriptionOperations subscription) + { + NetworkManagementClient networkClient = GetNetworkClient(subscription); + var networkInterfacesOperations = networkClient.NetworkInterfaces; + var result = networkInterfacesOperations.ListAll(); + return new PhWrappingPageable( + result, + s => new NetworkInterface(subscription, new NetworkInterfaceData(s))); + } + + /// + /// Lists the for this . + /// + /// The to target for listing. + /// An async collection of resource operations that may take multiple service requests to iterate over. + public static AsyncPageable ListNicsAsync(this SubscriptionOperations subscription) + { + NetworkManagementClient networkClient = GetNetworkClient(subscription); + var networkInterfacesOperations = networkClient.NetworkInterfaces; + var result = networkInterfacesOperations.ListAllAsync(); + return new PhWrappingAsyncPageable( + result, + s => new NetworkInterface(subscription, new NetworkInterfaceData(s))); + } + + #endregion + + #region Network Security Group operations + + /// + /// Lists the network security groups for this subscription. + /// + /// The instance the method will execute against. + /// A collection of resource operations that may take multiple service requests to iterate over. + public static Pageable ListNsgs(this SubscriptionOperations subscription) + { + NetworkManagementClient networkClient = GetNetworkClient(subscription); + var networkSecurityGroupsOperations = networkClient.NetworkSecurityGroups; + var result = networkSecurityGroupsOperations.ListAll(); + return new PhWrappingPageable( + result, + s => new NetworkSecurityGroup(subscription, new NetworkSecurityGroupData(s))); + } + + /// + /// Lists the network security groups for this subscription. + /// + /// The instance the method will execute against. + /// An async collection of resource operations that may take multiple service requests to iterate over. + public static AsyncPageable ListNsgsAsync(this SubscriptionOperations subscription) + { + NetworkManagementClient networkClient = GetNetworkClient(subscription); + var networkSecurityGroupsOperations = networkClient.NetworkSecurityGroups; + var result = networkSecurityGroupsOperations.ListAllAsync(); + return new PhWrappingAsyncPageable( + result, + s => new NetworkSecurityGroup(subscription, new NetworkSecurityGroupData(s))); + } + + #endregion + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkInterface.cs b/sdk/resourcemanager/Proto.Client/network/NetworkInterface.cs new file mode 100644 index 0000000000000..7af879e2f6473 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/NetworkInterface.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using Azure.ResourceManager.Core; + +namespace Proto.Network +{ + /// + /// A class that represents a network interface in a resource group and the operatiosn that can be performed on it. + /// + public class NetworkInterface : NetworkInterfaceOperations + { + internal NetworkInterface(ResourceOperationsBase options, NetworkInterfaceData resource) + : base(options, resource.Id) + { + Data = resource; + } + + /// + /// Gets the for this . + /// + public NetworkInterfaceData Data { get; private set; } + + /// + protected override NetworkInterface GetResource() + { + return this; + } + + /// + protected override Task GetResourceAsync() + { + return Task.FromResult(this); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceContainer.cs b/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceContainer.cs new file mode 100644 index 0000000000000..4a7e20cd9babb --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceContainer.cs @@ -0,0 +1,193 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Core.Resources; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using System; + +namespace Proto.Network +{ + /// + /// A class representing collection of and their operations over a . + /// + public class NetworkInterfaceContainer : ResourceContainerBase + { + internal NetworkInterfaceContainer(ResourceGroupOperations resourceGroup) + : base(resourceGroup) + { + } + + internal NetworkInterfacesOperations Operations => new NetworkManagementClient( + Id.Subscription, + BaseUri, + Credential, + ClientOptions.Convert()).NetworkInterfaces; + + /// + protected override ResourceType ValidResourceType => ResourceGroupOperations.ResourceType; + + /// + public override ArmResponse CreateOrUpdate(string name, NetworkInterfaceData resourceDetails) + { + var operation = Operations.StartCreateOrUpdate(Id.ResourceGroup, name, resourceDetails); + return new PhArmResponse( + operation.WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), + n => new NetworkInterface(Parent, new NetworkInterfaceData(n))); + } + + /// + public async override Task> CreateOrUpdateAsync(string name, NetworkInterfaceData resourceDetails, CancellationToken cancellationToken = default) + { + var operation = await Operations.StartCreateOrUpdateAsync(Id.ResourceGroup, name, resourceDetails, cancellationToken).ConfigureAwait(false); + return new PhArmResponse( + await operation.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + n => new NetworkInterface(Parent, new NetworkInterfaceData(n))); + } + + /// + public override ArmOperation StartCreateOrUpdate(string name, NetworkInterfaceData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + Operations.StartCreateOrUpdate(Id.ResourceGroup, name, resourceDetails, cancellationToken), + n => new NetworkInterface(Parent, new NetworkInterfaceData(n))); + } + + /// + public async override Task> StartCreateOrUpdateAsync(string name, NetworkInterfaceData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + await Operations.StartCreateOrUpdateAsync(Id.ResourceGroup, name, resourceDetails, cancellationToken).ConfigureAwait(false), + n => new NetworkInterface(Parent, new NetworkInterfaceData(n))); + } + + /// + /// Constructs an object used to create a . + /// + /// The public IP address of the . + /// The resource identifier of the subnet attached to this . + /// The that will contain the . + /// An object used to create a . + public ArmBuilder Construct(string subnetId, PublicIPAddressData ip = default, LocationData location = null) + { + var parent = GetParentResource(); + var nic = new Azure.ResourceManager.Network.Models.NetworkInterface() + { + Location = location ?? parent.Data.Location, + IpConfigurations = new List() + { + new NetworkInterfaceIPConfiguration() + { + Name = "Primary", + Primary = true, + Subnet = new Azure.ResourceManager.Network.Models.Subnet() { Id = subnetId }, + PrivateIPAllocationMethod = IPAllocationMethod.Dynamic, + } + } + }; + + if (ip != null) + nic.IpConfigurations[0].PublicIPAddress = new PublicIPAddress() { Id = ip.Id }; + + return new ArmBuilder(this, new NetworkInterfaceData(nic)); + } + + /// + /// Lists the for this . + /// + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A collection of that may take multiple service requests to iterate over. + public Pageable List(CancellationToken cancellationToken = default) + { + return new PhWrappingPageable( + Operations.List(Id.Name, cancellationToken), + this.convertor()); + } + + /// + /// Lists the for this . + /// + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// An async collection of that may take multiple service requests to iterate over. + public AsyncPageable ListAsync(CancellationToken cancellationToken = default) + { + var result = Operations.ListAsync(Id.Name, cancellationToken); + return new PhWrappingAsyncPageable( + result, + this.convertor()); + } + + /// + /// Filters the list of resources for this represented as generic resources. + /// + /// A string to filter the resources by name. + /// The number of results to return per page of data. + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A collection of resource operations that may take multiple service requests to iterate over. + public Pageable ListByName(string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(NetworkInterfaceData.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContext(Parent as ResourceGroupOperations, filters, top, cancellationToken); + } + + /// + /// Filters the list of resources for this represented as generic resources. + /// + /// A string to filter the resources by name. + /// The number of results to return per page of data. + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// An async collection of resource operations that may take multiple service requests to iterate over. + public AsyncPageable ListByNameAsync(string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(NetworkInterfaceData.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContextAsync(Parent as ResourceGroupOperations, filters, top, cancellationToken); + } + + /// + /// Filters the list of resources for this . + /// Makes an additional network call to retrieve the full data model for each . + /// + /// A string to filter the resources by name. + /// The number of results to return per page of data. + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A collection of resource operations that may take multiple service requests to iterate over. + public Pageable ListByNameExpanded(string filter, int? top = null, CancellationToken cancellationToken = default) + { + var results = ListByName(filter, top, cancellationToken); + return new PhWrappingPageable(results, s => new NetworkInterfaceOperations(s).Get().Value); + } + + /// + /// Filters the list of resources for this . + /// Makes an additional network call to retrieve the full data model for each . + /// + /// A string to filter the resources by name. + /// The number of results to return per page of data. + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// An async collection of resource operations that may take multiple service requests to iterate over. + public AsyncPageable ListByNameExpandedAsync(string filter, int? top = null, CancellationToken cancellationToken = default) + { + var results = ListByNameAsync(filter, top, cancellationToken); + return new PhWrappingAsyncPageable(results, s => new NetworkInterfaceOperations(s).Get().Value); + } + + private Func convertor() + { + return s => new NetworkInterface(Parent, new NetworkInterfaceData(s)); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceOperations.cs b/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceOperations.cs new file mode 100644 index 0000000000000..85d8f5fc407d6 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceOperations.cs @@ -0,0 +1,301 @@ +using Azure; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System; + +namespace Proto.Network +{ + /// + /// A class representing the operations that can be pefroemd over a specific . + /// + public class NetworkInterfaceOperations : ResourceOperationsBase, ITaggableResource, IDeletableResource + { + internal NetworkInterfaceOperations(GenericResourceOperations genericOperations) + : base(genericOperations) + { + } + + internal NetworkInterfaceOperations(ResourceGroupOperations resourceGroup, string nicName) + : base(resourceGroup, $"{resourceGroup.Id}/providers/Microsoft.Network/networkInterfaces/{nicName}") + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + protected NetworkInterfaceOperations(ResourceOperationsBase options, ResourceIdentifier id) + : base(options, id) + { + } + + /// + /// The resource type of a . + /// + public static readonly ResourceType ResourceType = "Microsoft.Network/networkInterfaces"; + + /// + protected override ResourceType ValidResourceType => ResourceType; + + internal NetworkInterfacesOperations Operations => new NetworkManagementClient( + Id.Subscription, + BaseUri, + Credential, + ClientOptions.Convert()).NetworkInterfaces; + + /// + /// Deletes a . + /// + /// An representing the service response to deletion. + public ArmResponse Delete() + { + return new ArmResponse(Operations.StartDelete(Id.ResourceGroup, Id.Name).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// Deletes a . + /// + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A that returns an when completed. + public async Task> DeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmResponse((await Operations.StartDeleteAsync(Id.ResourceGroup, Id.Name, cancellationToken)).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// Deletes a . + /// + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// An that allows polling for completion of the operation. + /// + /// Details on long running operation object. + /// + public ArmOperation StartDelete(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(Operations.StartDelete(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + /// Deletes a . + /// + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A that on completion returns an that allows polling for completion of the operation. + /// + /// Details on long running operation object. + /// + public async Task> StartDeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(await Operations.StartDeleteAsync(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + /// Gets details of the from the service. + /// + /// An . + public override ArmResponse Get() + { + return new PhArmResponse( + Operations.Get(Id.ResourceGroup, Id.Name), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + public async override Task> GetAsync(CancellationToken cancellationToken = default) + { + return new PhArmResponse( + await Operations.GetAsync(Id.ResourceGroup, Id.Name, null, cancellationToken), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + /// Add the given tag key and tag value to the resource. + /// + /// The tag key. + /// The Tag Value. + /// An that allows polling for completion of the operation. + public ArmResponse AddTag(string key, string value) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + /// Add the given tag key and tag value to the resource. + /// + /// The tag key. + /// The Tag Value. + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A that on completion returns a that allows polling for completion of the operation. + public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmResponse( + await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + /// Add the given tag key and tag value to the resource. + /// + /// The tag key. + /// The Tag Value. + /// An that allows polling for completion of the operation. + /// + /// Details on long running operation object. + /// + public ArmOperation StartAddTag(string key, string value) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + /// Add the given tag key and tag value to the resource. + /// + /// The tag key. + /// The Tag Value. + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A that on completion returns a that allows polling for completion of the operation. + /// + /// Details on long running operation object. + /// + public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmOperation( + await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + public ArmResponse SetTags(IDictionary tags) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, resource.Data.Tags); + return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, resource.Data.Tags); + return new PhArmResponse( + await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + public ArmOperation StartSetTags(IDictionary tags) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, resource.Data.Tags); + return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, resource.Data.Tags); + return new PhArmOperation( + await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + public ArmResponse RemoveTag(string key) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, resource.Data.Tags); + return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, resource.Data.Tags); + return new PhArmResponse( + await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + public ArmOperation StartRemoveTag(string key) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, resource.Data.Tags); + return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, resource.Data.Tags); + return new PhArmOperation( + await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new NetworkInterface(this, new NetworkInterfaceData(n))); + } + + /// + /// Lists all available geo-locations. + /// + /// A collection of location that may take multiple service requests to iterate over. + public IEnumerable ListAvailableLocations() + { + var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); + var networkInterfaceProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var networkInterfaceResource = networkInterfaceProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return networkInterfaceResource.Locations.Select(l => (LocationData)l); + } + + /// + /// Lists all available geo-locations. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of location that may take multiple service requests to iterate over. + /// The default subscription id is null. + public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) + { + var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); + var networkInterfaceProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var networkInterfaceResource = networkInterfaceProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return networkInterfaceResource.Locations.Select(l => (LocationData)l); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroup.cs b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroup.cs new file mode 100644 index 0000000000000..cdfeeaa6e2e62 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroup.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using System.Threading; +using System.Threading.Tasks; + +namespace Proto.Network +{ + /// + /// A class representing the operations that can be performed over a specific network security group. + /// + public class NetworkSecurityGroup : NetworkSecurityGroupOperations + { + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The resource that is the target of operations. + internal NetworkSecurityGroup(ResourceOperationsBase operations, NetworkSecurityGroupData resource) + : base(operations, resource.Id) + { + Data = resource; + } + + /// + /// Gets the data representing this network security group. + /// + public NetworkSecurityGroupData Data { get; private set; } + + /// + protected override NetworkSecurityGroup GetResource() + { + return this; + } + + /// + protected override Task GetResourceAsync() + { + return Task.FromResult(this); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupContainer.cs b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupContainer.cs new file mode 100644 index 0000000000000..cd47491d6a7fe --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupContainer.cs @@ -0,0 +1,225 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Core.Resources; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Proto.Network +{ + /// + /// A class representing collection of NetworkSecurityGroup and their operations over a resource group. + /// + public class NetworkSecurityGroupContainer : ResourceContainerBase + { + /// + /// Initializes a new instance of the class. + /// + /// An instance of that has an id for a [Resource]. + internal NetworkSecurityGroupContainer(GenericResourceOperations genericOperations) + : base(genericOperations) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The ResourceGroup that is the parent of the NetworkSecurityGroups. + internal NetworkSecurityGroupContainer(ResourceGroupOperations resourceGroup) + : base(resourceGroup) + { + } + + /// + /// Gets the valid resource type for network security groups. + /// + protected override ResourceType ValidResourceType => ResourceGroupOperations.ResourceType; + + private NetworkSecurityGroupsOperations Operations => new NetworkManagementClient( + Id.Subscription, + BaseUri, + Credential, + ClientOptions.Convert()).NetworkSecurityGroups; + + /// + public override ArmResponse CreateOrUpdate(string name, NetworkSecurityGroupData resourceDetails) + { + var operation = Operations.StartCreateOrUpdate(Id.ResourceGroup, name, resourceDetails.Model); + return new PhArmResponse( + operation.WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), + n => new NetworkSecurityGroup(Parent, new NetworkSecurityGroupData(n))); + } + + /// + public override async Task> CreateOrUpdateAsync(string name, NetworkSecurityGroupData resourceDetails, CancellationToken cancellationToken = default) + { + var operation = await Operations.StartCreateOrUpdateAsync(Id.ResourceGroup, name, resourceDetails.Model, cancellationToken).ConfigureAwait(false); + return new PhArmResponse( + await operation.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + n => new NetworkSecurityGroup(Parent, new NetworkSecurityGroupData(n))); + } + + /// + public override ArmOperation StartCreateOrUpdate(string name, NetworkSecurityGroupData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + Operations.StartCreateOrUpdate(Id.ResourceGroup, name, resourceDetails.Model, cancellationToken), + n => new NetworkSecurityGroup(Parent, new NetworkSecurityGroupData(n))); + } + + /// + public override async Task> StartCreateOrUpdateAsync(string name, NetworkSecurityGroupData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + await Operations.StartCreateOrUpdateAsync(Id.ResourceGroup, name, resourceDetails.Model, cancellationToken).ConfigureAwait(false), + n => new NetworkSecurityGroup(Parent, new NetworkSecurityGroupData(n))); + } + + /// + /// Construct an object used to create a network security group. + /// + /// The location to create the network security group. + /// The location to create the network security group. + /// Object used to create a . + public ArmBuilder Construct(LocationData locationData = null, params int[] openPorts) + { + var parent = GetParentResource(); + var nsg = new Azure.ResourceManager.Network.Models.NetworkSecurityGroup + { + Location = locationData ?? parent.Data.Location + }; + var index = 0; + nsg.SecurityRules = openPorts.Select(openPort => new SecurityRule + { + Name = $"Port{openPort}", + Priority = 1000 + (++index), + Protocol = SecurityRuleProtocol.Tcp, + Access = SecurityRuleAccess.Allow, + Direction = SecurityRuleDirection.Inbound, + SourcePortRange = "*", + SourceAddressPrefix = "*", + DestinationPortRange = $"{openPort}", + DestinationAddressPrefix = "*", + Description = $"Port_{openPort}" + }).ToList(); + + return new ArmBuilder(this, new NetworkSecurityGroupData(nsg)); + } + + /// + /// Construct an object used to create a network security group. + /// + /// The location to create the network security group. + /// Object used to create a . + public ArmBuilder Construct(params int[] openPorts) + { + var parent = GetParentResource(); + var nsg = new Azure.ResourceManager.Network.Models.NetworkSecurityGroup + { + Location = parent.Data.Location, + }; + var index = 0; + nsg.SecurityRules = openPorts.Select(openPort => new SecurityRule + { + Name = $"Port{openPort}", + Priority = 1000 + (++index), + Protocol = SecurityRuleProtocol.Tcp, + Access = SecurityRuleAccess.Allow, + Direction = SecurityRuleDirection.Inbound, + SourcePortRange = "*", + SourceAddressPrefix = "*", + DestinationPortRange = $"{openPort}", + DestinationAddressPrefix = "*", + Description = $"Port_{openPort}" + }).ToList(); + + return new ArmBuilder(this, new NetworkSecurityGroupData(nsg)); + } + + /// + /// List the network security groups for this resource group. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of that may take multiple service requests to iterate over. + public Pageable List(CancellationToken cancellationToken = default) + { + return new PhWrappingPageable( + Operations.List(Id.Name, cancellationToken), + r => new NetworkSecurityGroup(Parent, new NetworkSecurityGroupData(r))); + } + + /// + /// List the network security groups for this resource group. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of that may take multiple service requests to iterate over. + public AsyncPageable ListAsync(CancellationToken cancellationToken = default) + { + return new PhWrappingAsyncPageable( + Operations.ListAsync(Id.Name, cancellationToken), + r => new NetworkSecurityGroup(Parent, new NetworkSecurityGroupData(r))); + } + + /// + /// Filters the list of network security groups for this resource group represented as generic resources. + /// + /// The substring to filter by. + /// The number of items to truncate by. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of that may take multiple service requests to iterate over. + public Pageable ListByName(string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(NetworkSecurityGroupOperations.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContext(Parent as ResourceGroupOperations, filters, top, cancellationToken); + } + + /// + /// Filters the list of network security groups for this resource group represented as generic resources. + /// + /// The substring to filter by. + /// The number of items to truncate by. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of that may take multiple service requests to iterate over. + public AsyncPageable ListByNameAsync(string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(NetworkSecurityGroupOperations.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContextAsync(Parent as ResourceGroupOperations, filters, top, cancellationToken); + } + + /// + /// Filters the list of network security groups for this resource group represented as generic resources. + /// Makes an additional network call to retrieve the full data model for each network security group. + /// + /// The substring to filter by. + /// The number of items to truncate by. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of that may take multiple service requests to iterate over. + public Pageable ListByNameExpanded(string filter, int? top = null, CancellationToken cancellationToken = default) + { + var results = ListByName(filter, top, cancellationToken); + return new PhWrappingPageable(results, s => new NetworkSecurityGroupOperations(s).Get().Value); + } + + /// + /// Filters the list of network security groups for this resource group represented as generic resources. + /// Makes an additional network call to retrieve the full data model for each network security group. + /// + /// The substring to filter by. + /// The number of items to truncate by. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of that may take multiple service requests to iterate over. + public AsyncPageable ListByNameExpandedAsync(string filter, int? top = null, CancellationToken cancellationToken = default) + { + var results = ListByNameAsync(filter, top, cancellationToken); + return new PhWrappingAsyncPageable(results, s => new NetworkSecurityGroupOperations(s).Get().Value); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupOperations.cs b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupOperations.cs new file mode 100644 index 0000000000000..85998167c400a --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupOperations.cs @@ -0,0 +1,300 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using System; + +namespace Proto.Network +{ + /// + /// A class representing the operations that can be performed over a specific NetworkSecurityGroup. + /// + public class NetworkSecurityGroupOperations : ResourceOperationsBase, ITaggableResource, IDeletableResource + { + /// + /// Initializes a new instance of the class. + /// + /// An instance of that has an id for a virtual machine. + internal NetworkSecurityGroupOperations(GenericResourceOperations genericOperations) + : base(genericOperations) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The network security group name. + internal NetworkSecurityGroupOperations(ResourceGroupOperations resourceGroup, string nsgName) + : base(resourceGroup, $"{resourceGroup.Id}/providers/Microsoft.Network/networkSecurityGroups/{nsgName}") + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + protected NetworkSecurityGroupOperations(ResourceOperationsBase operation, ResourceIdentifier id) + : base(operation, id) + { + } + + /// + protected override ResourceType ValidResourceType => ResourceType; + + /// + /// Gets the resource type definition for a network security group. + /// + public static readonly ResourceType ResourceType = "Microsoft.Network/networkSecurityGroups"; + + private NetworkSecurityGroupsOperations Operations => new NetworkManagementClient( + Id.Subscription, + BaseUri, + Credential, + ClientOptions.Convert()).NetworkSecurityGroups; + + /// + /// Updates the network security group rules. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// The rules to be updated. + /// An that allows polling for completion of the operation. + public ArmOperation UpdateRules(CancellationToken cancellationToken = default, params SecurityRule[] rules) + { + var resource = GetResource(); + foreach (var rule in rules) + { + // Note that this makes use of the + var matchingRule = resource.Data.SecurityRules.FirstOrDefault(r => ResourceIdentifier.Equals(r.Id, rule.Id)); + if (matchingRule != null) + { + matchingRule.Access = rule.Access; + matchingRule.Description = rule.Description; + matchingRule.DestinationAddressPrefix = rule.DestinationAddressPrefix; + matchingRule.DestinationAddressPrefixes = rule.DestinationAddressPrefixes; + matchingRule.DestinationPortRange = rule.DestinationPortRange; + matchingRule.DestinationPortRanges = rule.DestinationPortRanges; + matchingRule.Direction = rule.Direction; + matchingRule.Priority = rule.Priority; + matchingRule.Protocol = rule.Protocol; + matchingRule.SourceAddressPrefix = rule.SourceAddressPrefix; + matchingRule.SourceAddressPrefixes = rule.SourceAddressPrefixes; + matchingRule.SourcePortRange = rule.SourcePortRange; + matchingRule.SourcePortRanges = rule.SourcePortRanges; + } + else + { + resource.Data.SecurityRules.Add(rule); + } + } + + return new PhArmOperation( + Operations.StartCreateOrUpdate(Id.ResourceGroup, Id.Name, resource.Data), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public override ArmResponse Get() + { + return new PhArmResponse(Operations.Get(Id.ResourceGroup, Id.Name), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public override async Task> GetAsync(CancellationToken cancellationToken = default) + { + return new PhArmResponse(await Operations.GetAsync(Id.ResourceGroup, Id.Name, null, cancellationToken), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public ArmResponse AddTag(string key, string value) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmResponse( + Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public ArmOperation StartAddTag(string key, string value) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmOperation( + Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public ArmResponse Delete() + { + return new ArmResponse(Operations.StartDelete(Id.ResourceGroup, Id.Name).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + public async Task> DeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmResponse((await Operations.StartDeleteAsync(Id.ResourceGroup, Id.Name, cancellationToken)).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + public ArmOperation StartDelete(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(Operations.StartDelete(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + public async Task> StartDeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(await Operations.StartDeleteAsync(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + protected override NetworkSecurityGroup GetResource() + { + return Get().Value; + } + + /// + public ArmResponse SetTags(IDictionary tags) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, resource.Data.Tags); + return new PhArmResponse( + Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, resource.Data.Tags); + return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public ArmOperation StartSetTags(IDictionary tags) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, resource.Data.Tags); + return new PhArmOperation( + Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, resource.Data.Tags); + return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public ArmResponse RemoveTag(string key) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, resource.Data.Tags); + return new PhArmResponse( + Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, resource.Data.Tags); + return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public ArmOperation StartRemoveTag(string key) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, resource.Data.Tags); + return new PhArmOperation( + Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, resource.Data.Tags); + return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); + } + + /// + /// Lists all available geo-locations. + /// + /// A collection of location that may take multiple service requests to iterate over. + public IEnumerable ListAvailableLocations() + { + var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); + var networkSecurityProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var networkSecurityResource = networkSecurityProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return networkSecurityResource.Locations.Select(l => (LocationData)l); + } + + /// + /// Lists all available geo-locations. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of location that may take multiple service requests to iterate over. + /// The default subscription id is null. + public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) + { + var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); + var networkSecurityProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var networkSecurityResource = networkSecurityProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return networkSecurityResource.Locations.Select(l => (LocationData)l); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkInterfaceData.cs b/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkInterfaceData.cs new file mode 100644 index 0000000000000..6d68276e9e9e5 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkInterfaceData.cs @@ -0,0 +1,130 @@ +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using System; +using System.Collections.Generic; + +namespace Proto.Network +{ + /// + /// A network interface in a resource group. + /// + public class NetworkInterfaceData : TrackedResource + { + /// + /// The ARM resource type for this . + /// + public static ResourceType ResourceType => "Microsoft.Network/networkInterfaces"; + + /// + /// Initializes a new instance of the class. + /// + /// The low level network interace model. + public NetworkInterfaceData(Azure.ResourceManager.Network.Models.NetworkInterface nic) : base(nic.Id, nic.Location, nic) + { + if (null == nic.Tags) + { + nic.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + } + } + + /// + /// Gets the resource tags. + /// + public override IDictionary Tags => Model.Tags; + + /// + /// Gets the resource name. + /// + public override string Name => Model.Name; + + /// + /// Gets a unique read-only string that changes whenever the resource is updated. + /// + public string Etag => Model.Etag; + + /// + /// Gets the reference to a linked virtual machine. + /// + public SubResource VirtualMachine => Model.VirtualMachine; + + /// + /// Gets the reference to the linked NetworkSecurityGroup resource. + /// + public Azure.ResourceManager.Network.Models.NetworkSecurityGroup NetworkSecurityGroup + { + get => Model.NetworkSecurityGroup; + set => Model.NetworkSecurityGroup = value; + } + + /// + /// Gets a reference to the private endpoint to which the network interface is linked. + /// + public PrivateEndpoint PrivateEndpoint => Model.PrivateEndpoint; + + /// + /// Gets or sets a list of IPConfigurations of the network interface. + /// + public IList IpConfigurations + { + get => Model.IpConfigurations; + set => Model.IpConfigurations = value; + } + + /// + /// Gets a list of TapConfigurations of the newtork interface. + /// + public IList TapConfigurations=> Model.TapConfigurations; + + /// + /// Gets or sets the DNS settings in network interface. + /// + public NetworkInterfaceDnsSettings DnsSettings + { + get => Model.DnsSettings; + set => Model.DnsSettings = value; + } + + /// + /// Gets the MAC address of the network interface. + /// + public string MacAddress => Model.MacAddress; + + /// + /// Gets a value indicating whether this is a primary network interface on a virtual machine. + /// + public bool? Primary => Model.Primary; + + /// + /// Gets or sets a value determining if the network interface is accelerated networking enabled. + /// + public bool? EnableAcceleratedNetworking + { + get => Model.EnableAcceleratedNetworking; + set => Model.EnableAcceleratedNetworking = value; + } + + /// + /// Gets or sets a value Indicating whether IP forwarding is enabled on this network interface. + /// + public bool? EnableIPForwarding + { + get => Model.EnableIPForwarding; + set => Model.EnableIPForwarding = value; + } + + /// + /// Gets a list of references to linked BareMetal resources. + /// + public IList HostedWorkloads => Model.HostedWorkloads; + + /// + /// Gets the resource GUID property of the network interface resource. + /// + public string ResourceGuid => Model.ResourceGuid; + + /// + /// Gets the provisioning state of the network interface resource. + /// + public ProvisioningState? ProvisioningState => Model.ProvisioningState; + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkSecurityGroupData.cs b/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkSecurityGroupData.cs new file mode 100644 index 0000000000000..a773444916554 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkSecurityGroupData.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using System; +using System.Collections.Generic; + +namespace Proto.Network +{ + /// + /// A class representing the NetworkSecurityGroup data model. + /// + public class NetworkSecurityGroupData : + TrackedResource + { + /// + /// Initializes a new instance of the class. + /// + /// The network security group to initialize. + public NetworkSecurityGroupData(Azure.ResourceManager.Network.Models.NetworkSecurityGroup nsg) + : base(nsg.Id, nsg.Location, nsg) + { + if (null == nsg.Tags) + { + nsg.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + } + } + + /// Resource tags. + public override IDictionary Tags => Model.Tags; + + /// The name property of the network security group resource. + public override string Name => Model.Name; + + /// A unique read-only string that changes whenever the resource is updated. + public string Etag => Model.Etag; + + /// A collection of security rules of the network security group. + public IList SecurityRules + { + get => Model.SecurityRules; + set => Model.SecurityRules = value; + } + + /// The default security rules of network security group. + public IList DefaultSecurityRules => Model.DefaultSecurityRules; + + /// A collection of references to network interfaces. + public IList NetworkInterfaces => Model.NetworkInterfaces; + + /// A collection of references to subnets. + public IList Subnets => Model.Subnets; + + /// A collection of references to flow log resources. + + public IList FlowLogs => Model.FlowLogs; + + /// The resource GUID property of the network security group resource. + public string ResourceGuid => Model.ResourceGuid; + + /// The provisioning state of the network security group resource. + public ProvisioningState? ProvisioningState => Model.ProvisioningState; + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/Placeholder/PublicIPAddressData.cs b/sdk/resourcemanager/Proto.Client/network/Placeholder/PublicIPAddressData.cs new file mode 100644 index 0000000000000..173bc6e3b2bab --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/Placeholder/PublicIPAddressData.cs @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using System; +using System.Collections.Generic; + +namespace Proto.Network +{ + /// + /// A class representing the public IP address data model. + /// + public class PublicIPAddressData : TrackedResource + { + /// + /// Gets the resource type definition for a public IP address. + /// + public static ResourceType ResourceType => "Microsoft.Network/publicIpAddresses"; + + /// + /// Initializes a new instance of the class. + /// + /// The public IP address to initialize. + public PublicIPAddressData(PublicIPAddress ip) : base(ip.Id, ip.Location, ip) + { + if (null == ip.Tags) + { + ip.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + } + } + + /// Resource tags. + public override IDictionary Tags => Model.Tags; + + /// The name property of the network security group resource. + public override string Name => Model.Name; + + /// The public IP address SKU. + public PublicIPAddressSku Sku + { + get => Model.Sku; + set => Model.Sku = value; + } + + /// A unique read-only string that changes whenever the resource is updated. + public string Etag => Model.Etag; + + /// A list of availability zones denoting the IP allocated for the resource needs to come from. + public IList Zones + { + get => Model.Zones; + set => Model.Zones = value; + } + + /// The public IP address allocation method. + public IPAllocationMethod? PublicIPAllocationMethod + { + get => Model.PublicIPAllocationMethod; + set => Model.PublicIPAllocationMethod = value; + } + + /// The public IP address version. + public IPVersion? PublicIPAddressVersion + { + get => Model.PublicIPAddressVersion; + set => Model.PublicIPAddressVersion = value; + } + + /// The IP configuration associated with the public IP address. + public IPConfiguration IpConfiguration => Model.IpConfiguration; + + /// The FQDN of the DNS record associated with the public IP address. + public PublicIPAddressDnsSettings DnsSettings + { + get => Model.DnsSettings; + set => Model.DnsSettings = value; + } + + /// The DDoS protection custom policy associated with the public IP address. + public DdosSettings DdosSettings + { + get => Model.DdosSettings; + set => Model.DdosSettings = value; + } + + /// The list of tags associated with the public IP address. + public IList IpTags + { + get => Model.IpTags; + set => Model.IpTags = value; + } + + /// The IP address associated with the public IP address resource. + public string IpAddress + { + get => Model.IpAddress; + set => Model.IpAddress = value; + } + + /// The Public IP Prefix this Public IP Address should be allocated from. + public SubResource PublicIPPrefix + { + get => Model.PublicIPPrefix; + set => Model.PublicIPPrefix = value; + } + + /// The idle timeout of the public IP address. + public int? IdleTimeoutInMinutes + { + get => Model.IdleTimeoutInMinutes; + set => Model.IdleTimeoutInMinutes = value; + } + + /// The resource GUID property of the public IP address resource. + public string ResourceGuid => Model.ResourceGuid; + + /// The provisioning state of the public IP address resource. + public ProvisioningState? ProvisioningState => Model.ProvisioningState; + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/Placeholder/SubnetData.cs b/sdk/resourcemanager/Proto.Client/network/Placeholder/SubnetData.cs new file mode 100644 index 0000000000000..c37382ed431b9 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/Placeholder/SubnetData.cs @@ -0,0 +1,129 @@ +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using System.Collections.Generic; + +namespace Proto.Network +{ + /// + /// A class representing the subnet data model. + /// + public class SubnetData : ProxyResource + { + /// + /// Initializes a new instance of the class. + /// + public SubnetData(Azure.ResourceManager.Network.Models.Subnet sub) : base(sub.Id, sub) + { + } + + /// + /// Gets the subnet id. + /// + public override string Name => Model.Name; + + /// + /// The provisioning state of the subnet resource. + /// + public ProvisioningState? ProvisioningState => Model.ProvisioningState; + + /// + /// A read-only string identifying the intention use for this subnet based on delegations and other user-defined properties. + /// + public string Purpose => Model.Purpose; + + /// An array of references to the delegations on the subnet. + public IList Delegations + { + get => Model.Delegations; + set => Model.Delegations = value; + } + + /// An array of references to services injecting into this subnet. + public IList ServiceAssociationLinks => Model.ServiceAssociationLinks; + + /// An array of references to the external resources using subnet. + public IList ResourceNavigationLinks => Model.ResourceNavigationLinks; + + /// Array of IpAllocation which reference this subnet. + public IList IpAllocations + { + get => Model.IpAllocations; + set => Model.IpAllocations = value; + } + + /// Array of IP configuration profiles which reference this subnet. + public IList IpConfigurationProfiles => Model.IpConfigurationProfiles; + + /// An array of references to the network interface IP configurations using subnet. + public IList IpConfigurations => Model.IpConfigurations; + + /// Enable or Disable apply network policies on private end point in the subnet. + public string PrivateEndpointNetworkPolicies + { + get => Model.PrivateEndpointNetworkPolicies; + set => Model.PrivateEndpointNetworkPolicies = value; + } + + /// An array of references to private endpoints. + public IList PrivateEndpoints => Model.PrivateEndpoints; + + /// An array of service endpoints. + public IList ServiceEndpoints + { + get => Model.ServiceEndpoints; + set => Model.ServiceEndpoints = value; + } + + /// Nat gateway associated with this subnet. + public SubResource NatGateway + { + get => Model.NatGateway; + set => Model.NatGateway = value; + } + + /// The reference to the RouteTable resource. + public RouteTable RouteTable + { + get => Model.RouteTable; + set => Model.RouteTable = value; + } + + /// The reference to the NetworkSecurityGroup resource. + public Azure.ResourceManager.Network.Models.NetworkSecurityGroup NetworkSecurityGroup + { + get => Model.NetworkSecurityGroup; + set => Model.NetworkSecurityGroup = value; + } + + /// List of address prefixes for the subnet. + public IList AddressPrefixes + { + get => Model.AddressPrefixes; + set => Model.AddressPrefixes = value; + } + + /// The address prefix for the subnet. + public string AddressPrefix + { + get => Model.AddressPrefix; + set => Model.AddressPrefix = value; + } + + /// A unique read-only string that changes whenever the resource is updated. + public string Etag => Model.Etag; + + /// An array of service endpoint policies. + public IList ServiceEndpointPolicies + { + get => Model.ServiceEndpointPolicies; + set => Model.ServiceEndpointPolicies = value; + } + + /// Enable or Disable apply network policies on private link service in the subnet. + public string PrivateLinkServiceNetworkPolicies + { + get => Model.PrivateLinkServiceNetworkPolicies; + set => Model.PrivateLinkServiceNetworkPolicies = value; + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/Placeholder/VirtualNetworkData.cs b/sdk/resourcemanager/Proto.Client/network/Placeholder/VirtualNetworkData.cs new file mode 100644 index 0000000000000..cfa6f92b13721 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/Placeholder/VirtualNetworkData.cs @@ -0,0 +1,109 @@ +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using System; +using System.Collections.Generic; + +namespace Proto.Network +{ + /// + /// A class representing the virtual nerwork data model. + /// + public class VirtualNetworkData : TrackedResource + { + /// + /// Gets the resource type definition for a virtual nerwork. + /// + public static ResourceType ResourceType => "Microsoft.Network/virtualNetworks"; + + /// + /// Initializes a new instance of the class. + /// + /// The virtual nerwork to initialize. + public VirtualNetworkData(Azure.ResourceManager.Network.Models.VirtualNetwork vnet) : base(vnet.Id, vnet.Location, vnet) + { + if (null == vnet.Tags) + { + vnet.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + } + } + + /// + public override IDictionary Tags => Model.Tags; + + /// + public override string Name => Model.Name; + + /// A unique read-only string that changes whenever the resource is updated. + public string Etag => Model.Etag; + + /// The AddressSpace that contains an array of IP address ranges that can be used by subnets. + public AddressSpace AddressSpace + { + get => Model.AddressSpace; + set => Model.AddressSpace = value; + } + + /// The dhcpOptions that contains an array of DNS servers available to VMs deployed in the virtual network. + public DhcpOptions DhcpOptions + { + get => Model.DhcpOptions; + set => Model.DhcpOptions = value; + } + + /// A list of subnet in a Virtual Network. + public IList Subnets + { + get => Model.Subnets; + set => Model.Subnets = value; + } + + /// A list of peering in a Virtual Network. + public IList VirtualNetworkPeerings + { + get => Model.VirtualNetworkPeerings; + set => Model.VirtualNetworkPeerings = value; + } + + /// The resourceGuid property of the Virtual Network resource. + public string ResourceGuid => Model.ResourceGuid; + + /// The provisioning state of the virtual network resource. + public ProvisioningState? ProvisioningState => Model.ProvisioningState; + + /// Indicates if DDoS protection is enabled for all the protected resources in the virtual network. It requires a DDoS protection plan associated with the resource. + public bool? EnableDdosProtection + { + get => Model.EnableDdosProtection; + set => Model.EnableDdosProtection = value; + } + + /// Indicates if VM protection is enabled for all the subnets in the virtual network. + public bool? EnableVmProtection + { + get => Model.EnableVmProtection; + set => Model.EnableVmProtection = value; + } + + /// The DDoS protection plan associated with the virtual network. + public SubResource DdosProtectionPlan + + { + get => Model.DdosProtectionPlan; + set => Model.DdosProtectionPlan = value; + } + + /// Bgp Communities sent over ExpressRoute with each route corresponding to a prefix in this VNET. + public VirtualNetworkBgpCommunities BgpCommunities + { + get => Model.BgpCommunities; + set => Model.BgpCommunities = value; + } + + /// Array of IpAllocation which reference this VNET. + public IList IpAllocations + { + get => Model.IpAllocations; + set => Model.IpAllocations = value; + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/Proto.Network.csproj b/sdk/resourcemanager/Proto.Client/network/Proto.Network.csproj new file mode 100644 index 0000000000000..09aa34832109b --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/Proto.Network.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + Proto.Network + + + + C:\Git\azure-proto-sdk\azure-proto-network\obj\azure-proto-network.xml + + + + + + + + + + + diff --git a/sdk/resourcemanager/Proto.Client/network/PublicIpAddress.cs b/sdk/resourcemanager/Proto.Client/network/PublicIpAddress.cs new file mode 100644 index 0000000000000..12be1f48000c6 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/PublicIpAddress.cs @@ -0,0 +1,39 @@ +using System.Threading.Tasks; +using Azure.ResourceManager.Core; + +namespace Proto.Network +{ + /// + /// Class representing a PublicIpAddress resource along with the instance operations that can be performed on it. + /// + public class PublicIpAddress : PublicIpAddressOperations + { + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The resource that is the target of operations. + internal PublicIpAddress(ResourceOperationsBase options, PublicIPAddressData resource) + : base(options, resource.Id) + { + Data = resource; + } + + /// + /// Gets the data representing this PublicIPAddress. + /// + public PublicIPAddressData Data { get; private set; } + + /// + protected override PublicIpAddress GetResource() + { + return this; + } + + /// + protected override Task GetResourceAsync() + { + return Task.FromResult(this); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/PublicIpAddressContainer.cs b/sdk/resourcemanager/Proto.Client/network/PublicIpAddressContainer.cs new file mode 100644 index 0000000000000..96859391d7b8d --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/PublicIpAddressContainer.cs @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Core.Resources; +using System.Threading; +using System.Threading.Tasks; +using System; + +namespace Proto.Network +{ + /// + /// A class representing collection of PublicIpAddress and their operations over a resource group. + /// + public class PublicIpAddressContainer : ResourceContainerBase + { + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + internal PublicIpAddressContainer(ResourceGroupOperations resourceGroup) + : base(resourceGroup) + { + } + + /// + /// Gets the valid resource type for this resource. + /// + protected override ResourceType ValidResourceType => ResourceGroupOperations.ResourceType; + + private PublicIPAddressesOperations Operations => new NetworkManagementClient( + Id.Subscription, + BaseUri, + Credential, + ClientOptions.Convert()).PublicIPAddresses; + + /// + public override ArmResponse CreateOrUpdate(string name, PublicIPAddressData resourceDetails) + { + var operation = Operations.StartCreateOrUpdate(Id.ResourceGroup, name, resourceDetails); + return new PhArmResponse( + operation.WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), + n => new PublicIpAddress(Parent, new PublicIPAddressData(n))); + } + + /// + public override async Task> CreateOrUpdateAsync(string name, PublicIPAddressData resourceDetails, CancellationToken cancellationToken = default) + { + var operation = await Operations.StartCreateOrUpdateAsync(Id.ResourceGroup, name, resourceDetails, cancellationToken).ConfigureAwait(false); + return new PhArmResponse( + await operation.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + n => new PublicIpAddress(Parent, new PublicIPAddressData(n))); + } + + /// + public override ArmOperation StartCreateOrUpdate(string name, PublicIPAddressData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + Operations.StartCreateOrUpdate(Id.ResourceGroup, name, resourceDetails, cancellationToken), + n => new PublicIpAddress(Parent, new PublicIPAddressData(n))); + } + + /// + public override async Task> StartCreateOrUpdateAsync(string name, PublicIPAddressData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + await Operations.StartCreateOrUpdateAsync(Id.ResourceGroup, name, resourceDetails, cancellationToken).ConfigureAwait(false), + n => new PublicIpAddress(Parent, new PublicIPAddressData(n))); + } + + /// + /// Construct an object used to create a public IP address. + /// + /// The location to create the network security group. + /// Object used to create a . + public ArmBuilder Construct(LocationData location = null) + { + var parent = GetParentResource(); + var ipAddress = new PublicIPAddress() + { + PublicIPAddressVersion = IPVersion.IPv4.ToString(), + PublicIPAllocationMethod = IPAllocationMethod.Dynamic, + Location = location ?? parent.Data.Location, + }; + + return new ArmBuilder(this, new PublicIPAddressData(ipAddress)); + } + + /// + /// List the public IP addresses for this resource group. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of that may take multiple service requests to iterate over. + public Pageable List(CancellationToken cancellationToken = default) + { + return new PhWrappingPageable( + Operations.List(Id.Name, cancellationToken), + Convertor()); + } + + /// + /// List the public IP addresses for this resource group. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of that may take multiple service requests to iterate over. + public AsyncPageable ListAsync(CancellationToken cancellationToken = default) + { + return new PhWrappingAsyncPageable( + Operations.ListAsync(Id.Name, cancellationToken), + Convertor()); + } + + /// + /// Filters the list of public IP addresses for this resource group represented as generic resources. + /// + /// The substring to filter by. + /// The number of items to truncate by. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of that may take multiple service requests to iterate over. + public Pageable ListByName(string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(PublicIPAddressData.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContext(Parent as ResourceGroupOperations, filters, top, cancellationToken); + } + + /// + /// Filters the list of public IP addresses for this resource group represented as generic resources. + /// + /// The substring to filter by. + /// The number of items to truncate by. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of that may take multiple service requests to iterate over. + public AsyncPageable ListByNameAsync(string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(PublicIPAddressData.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContextAsync(Parent as ResourceGroupOperations, filters, top, cancellationToken); + } + + /// + /// Filters the list of public IP addresses for this resource group represented as generic resources. + /// Makes an additional network call to retrieve the full data model for each network security group. + /// + /// The substring to filter by. + /// The number of items to truncate by. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of that may take multiple service requests to iterate over. + public Pageable ListByNameExpanded(string filter, int? top = null, CancellationToken cancellationToken = default) + { + var results = ListByName(filter, top, cancellationToken); + return new PhWrappingPageable(results, s => new PublicIpAddressOperations(s).Get().Value); + } + + /// + /// Filters the list of public IP addresses for this resource group represented as generic resources. + /// Makes an additional network call to retrieve the full data model for each network security group. + /// + /// The substring to filter by. + /// The number of items to truncate by. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of that may take multiple service requests to iterate over. + public AsyncPageable ListByNameExpandedAsync(string filter, int? top = null, CancellationToken cancellationToken = default) + { + var results = ListByNameAsync(filter, top, cancellationToken); + return new PhWrappingAsyncPageable(results, s => new PublicIpAddressOperations(s).Get().Value); + } + + private Func Convertor() + { + return s => new PublicIpAddress(Parent, new PublicIPAddressData(s)); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/PublicIpAddressOperations.cs b/sdk/resourcemanager/Proto.Client/network/PublicIpAddressOperations.cs new file mode 100644 index 0000000000000..b25ea1597672d --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/PublicIpAddressOperations.cs @@ -0,0 +1,295 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System; + +namespace Proto.Network +{ + /// + /// A class representing the operations that can be performed over a specific NetworkSecurityGroup. + /// + public class PublicIpAddressOperations : ResourceOperationsBase, ITaggableResource, IDeletableResource + { + /// + /// Initializes a new instance of the class. + /// + /// An instance of that has an id for a virtual machine. + internal PublicIpAddressOperations(GenericResourceOperations genericOperations) + : base(genericOperations) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The public ip address name. + internal PublicIpAddressOperations(ResourceGroupOperations resourceGroup, string publicIpName) + : base(resourceGroup, $"{resourceGroup.Id}/providers/Microsoft.Network/publicIpAddresses/{publicIpName}") + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + protected PublicIpAddressOperations(ResourceOperationsBase options, ResourceIdentifier id) + : base(options, id) + { + } + + /// + /// Gets the resource type definition for a public IP address. + /// + public static readonly ResourceType ResourceType = "Microsoft.Network/publicIPAddresses"; + + /// + protected override ResourceType ValidResourceType => ResourceType; + + private PublicIPAddressesOperations Operations => new NetworkManagementClient( + Id.Subscription, + BaseUri, + Credential, + ClientOptions.Convert()).PublicIPAddresses; + + /// + /// The operation to delete a public IP address. + /// + /// A response with the operation for this resource. + public ArmResponse Delete() + { + return new ArmResponse(Operations.StartDelete(Id.ResourceGroup, Id.Name).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// The operation to delete a public IP address. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public async Task> DeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmResponse((await Operations.StartDeleteAsync(Id.ResourceGroup, Id.Name, cancellationToken)).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// The operation to delete a public IP address. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// An that allows polling for completion of the operation. + public ArmOperation StartDelete(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(Operations.StartDelete(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + /// The operation to delete a public IP address. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> StartDeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(await Operations.StartDeleteAsync(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + public override ArmResponse Get() + { + return new PhArmResponse(Operations.Get(Id.ResourceGroup, Id.Name), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + public override async Task> GetAsync(CancellationToken cancellationToken = default) + { + return new PhArmResponse(await Operations.GetAsync(Id.ResourceGroup, Id.Name, null, cancellationToken), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + /// Add a tag to a public IP address. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// An that allows polling for completion of the operation. + public ArmResponse AddTag(string key, string value) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + /// Add a tag to a public IP address. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + /// Add a tag to a public IP address. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// An that allows polling for completion of the operation. + public ArmOperation StartAddTag(string key, string value) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + /// Add a tag to a public IP address. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + public ArmResponse SetTags(IDictionary tags) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, resource.Data.Tags); + return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, resource.Data.Tags); + return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + public ArmOperation StartSetTags(IDictionary tags) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, resource.Data.Tags); + return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, resource.Data.Tags); + return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + public ArmResponse RemoveTag(string key) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, resource.Data.Tags); + return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, resource.Data.Tags); + return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + public ArmOperation StartRemoveTag(string key) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, resource.Data.Tags); + return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, resource.Data.Tags); + return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new PublicIpAddress(this, new PublicIPAddressData(n))); + } + + /// + /// Lists all available geo-locations. + /// + /// A collection of location that may take multiple service requests to iterate over. + public IEnumerable ListAvailableLocations() + { + var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); + var publicIPProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var publicIPResource = publicIPProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return publicIPResource.Locations.Select(l => (LocationData)l); + } + + /// + /// Lists all available geo-locations. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of location that may take multiple service requests to iterate over. + /// The default subscription id is null. + public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) + { + var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); + var publicIPProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var publicIPResource = publicIPProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return publicIPResource.Locations.Select(l => (LocationData)l); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/Subnet.cs b/sdk/resourcemanager/Proto.Client/network/Subnet.cs new file mode 100644 index 0000000000000..dbbe6abe87eda --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/Subnet.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using Azure.ResourceManager.Core; + +namespace Proto.Network +{ + /// + /// A class representing a subnet along with the instance operations that can be performed on it. + /// + public class Subnet : SubnetOperations + { + /// + /// Initializes a new instance of the class. + /// + internal Subnet(ResourceOperationsBase options, SubnetData resource) + : base(options, resource.Id) + { + Data = resource; + } + + /// + /// Gets the data representing the subnet + /// + public SubnetData Data { get; private set; } + + /// + protected override Subnet GetResource() + { + return this; + } + + /// + protected override Task GetResourceAsync() + { + return Task.FromResult(this); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/SubnetBuilder.cs b/sdk/resourcemanager/Proto.Client/network/SubnetBuilder.cs new file mode 100644 index 0000000000000..b1c130ede655f --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/SubnetBuilder.cs @@ -0,0 +1,27 @@ +using Azure.ResourceManager.Core; + +namespace Proto.Network +{ + /// + /// A class representing a builder object to help create a subnet. + /// + public class SubnetBuilder : ArmBuilder + { + /// + /// Initializes a new instance of the class. + /// + /// The container to create the subnet in. + /// The data model representing the subnet to create. + internal SubnetBuilder(SubnetContainer container, SubnetData subnet) + : base(container, subnet) + { + + } + + /// + protected override void OnBeforeBuild() + { + Resource.Model.Name = ResourceName; + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/SubnetContainer.cs b/sdk/resourcemanager/Proto.Client/network/SubnetContainer.cs new file mode 100644 index 0000000000000..4f2ed2c670ec7 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/SubnetContainer.cs @@ -0,0 +1,118 @@ +using Azure; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Core; +using Azure.ResourceManager.Core.Adapters; +using System.Threading; +using System.Threading.Tasks; +using System; + +namespace Proto.Network +{ + /// + /// A class representing collection of Subnets and their operations over a VirtualNetwork. + /// + public class SubnetContainer : ResourceContainerBase + { + /// + /// Initializes a new instance of the class. + /// + /// The virtual network associate with this subnet + internal SubnetContainer(VirtualNetworkOperations virtualNetwork) + : base(virtualNetwork) + { + } + + /// + protected override ResourceType ValidResourceType => VirtualNetworkOperations.ResourceType; + + private SubnetsOperations Operations => new NetworkManagementClient( + Id.Subscription, + BaseUri, + Credential, + ClientOptions.Convert()).Subnets; + + /// + public override ArmResponse CreateOrUpdate(string name, SubnetData resourceDetails) + { + var operation = Operations.StartCreateOrUpdate(Id.ResourceGroup, Id.Name, name, resourceDetails.Model); + return new PhArmResponse( + operation.WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), + s => new Subnet(Parent, new SubnetData(s))); + } + + /// + public override async Task> CreateOrUpdateAsync(string name, SubnetData resourceDetails, CancellationToken cancellationToken = default) + { + var operation = await Operations.StartCreateOrUpdateAsync(Id.ResourceGroup, Id.Name, name, resourceDetails.Model, cancellationToken).ConfigureAwait(false); + return new PhArmResponse( + await operation.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + s => new Subnet(Parent, new SubnetData(s))); + } + + /// + public override ArmOperation StartCreateOrUpdate(string name, SubnetData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + Operations.StartCreateOrUpdate(Id.ResourceGroup, Id.Name, name, resourceDetails.Model, cancellationToken), + s => new Subnet(Parent, new SubnetData(s))); + } + + /// + public async override Task> StartCreateOrUpdateAsync(string name, SubnetData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + await Operations.StartCreateOrUpdateAsync(Id.ResourceGroup, Id.Name, name, resourceDetails.Model, cancellationToken).ConfigureAwait(false), + s => new Subnet(Parent, new SubnetData(s))); + } + + /// + /// Constructs an object used to create a subnet. + /// + /// The CIDR of the resource. + /// The network security group of the resource. + /// A builder with and . + public SubnetBuilder Construct(string subnetCidr, NetworkSecurityGroupData group = null) + { + var subnet = new Azure.ResourceManager.Network.Models.Subnet() + { + AddressPrefix = subnetCidr, + }; + + if (null != group) + { + subnet.NetworkSecurityGroup = group.Model; + } + + return new SubnetBuilder(this, new SubnetData(subnet)); + } + + /// + /// Lists the subnets for this virtual network. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of resource operations that may take multiple service requests to iterate over. + public Pageable List(CancellationToken cancellationToken = default) + { + return new PhWrappingPageable( + Operations.List(Id.ResourceGroup, Id.Name, cancellationToken), + this.convertor()); + } + + /// + /// Lists the subnets for this virtual network. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of resource operations that may take multiple service requests to iterate over. + public AsyncPageable ListAsync(CancellationToken cancellationToken = default) + { + return new PhWrappingAsyncPageable( + Operations.ListAsync(Id.ResourceGroup, Id.Name, cancellationToken), + this.convertor()); + } + + private Func convertor() + { + return s => new Subnet(Parent, new SubnetData(s)); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/SubnetOperations.cs b/sdk/resourcemanager/Proto.Client/network/SubnetOperations.cs new file mode 100644 index 0000000000000..ff99d20d914a6 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/SubnetOperations.cs @@ -0,0 +1,110 @@ +using Azure; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Core; +using System.Threading; +using System.Threading.Tasks; + +namespace Proto.Network +{ + /// + /// A class representing the operations that can be performed over a specific subnet. + /// + public class SubnetOperations : ResourceOperationsBase, IDeletableResource + { + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The name of the subnet. + internal SubnetOperations(VirtualNetworkOperations virtualNetwork, string subnetName) + : base(virtualNetwork, $"{virtualNetwork.Id}/subnets/{subnetName}") + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + protected SubnetOperations(ResourceOperationsBase options, ResourceIdentifier id) + : base(options, id) + { + } + + /// + /// Gets the resource type definition for a subnet. + /// + public static readonly ResourceType ResourceType = "Microsoft.Network/virtualNetworks/subnets"; + + /// + /// Gets the valid resource type definition for a subnet. + /// + protected override ResourceType ValidResourceType => ResourceType; + + private SubnetsOperations Operations => new NetworkManagementClient( + Id.Subscription, + BaseUri, + Credential, + ClientOptions.Convert()).Subnets; + + /// + /// The operation to delete a subnet. + /// + /// A response with the operation for this resource. + public ArmResponse Delete() + { + return new ArmResponse(Operations.StartDelete(Id.ResourceGroup, Id.Parent.Name, Id.Name).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// The operation to delete a subnet. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public async Task> DeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmResponse((await Operations.StartDeleteAsync(Id.ResourceGroup, Id.Parent.Name, Id.Name)).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + + /// + /// The operation to delete a subnet. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// An that allows polling for completion of the operation. + public ArmOperation StartDelete(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(Operations.StartDelete(Id.ResourceGroup, Id.Parent.Name, Id.Name)); + } + + /// + /// The operation to delete a subnet. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> StartDeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(await Operations.StartDeleteAsync(Id.ResourceGroup, Id.Parent.Name, Id.Name, cancellationToken)); + } + + /// + public override ArmResponse Get() + { + return new PhArmResponse(Operations.Get(Id.ResourceGroup, Id.Parent.Name, Id.Name), + n => new Subnet(this, new SubnetData(n))); + } + + /// + public override async Task> GetAsync(CancellationToken cancellationToken = default) + { + return new PhArmResponse(await Operations.GetAsync(Id.ResourceGroup, Id.Parent.Name, Id.Name, null, cancellationToken), + n => new Subnet(this, new SubnetData(n))); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/VirtualNetwork.cs b/sdk/resourcemanager/Proto.Client/network/VirtualNetwork.cs new file mode 100644 index 0000000000000..2606705790af9 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/VirtualNetwork.cs @@ -0,0 +1,39 @@ +using System.Threading.Tasks; +using Azure.ResourceManager.Core; + +namespace Proto.Network +{ + /// + /// A class representing a virtual nerwork along with the instance operations that can be performed on it. + /// + public class VirtualNetwork : VirtualNetworkOperations + { + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The resource that is the target of operations. + internal VirtualNetwork(ResourceOperationsBase options, VirtualNetworkData resource) + : base(options, resource.Id) + { + Data = resource; + } + + /// + /// Gets or sets the virtual nerwork data. + /// + public VirtualNetworkData Data { get; private set; } + + /// + protected override VirtualNetwork GetResource() + { + return this; + } + + /// + protected override Task GetResourceAsync() + { + return Task.FromResult(this); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/VirtualNetworkContainer.cs b/sdk/resourcemanager/Proto.Client/network/VirtualNetworkContainer.cs new file mode 100644 index 0000000000000..6071bafd9a6b7 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/VirtualNetworkContainer.cs @@ -0,0 +1,177 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Core.Resources; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using System; + +namespace Proto.Network +{ + /// + /// A class representing collection of virtual nerwork and their operations over a resource group. + /// + public class VirtualNetworkContainer : ResourceContainerBase + { + /// + /// Initializes a new instance of the class. + /// + /// The parent resource group. + internal VirtualNetworkContainer(ResourceGroupOperations resourceGroup) + : base(resourceGroup) + { + } + + /// + protected override ResourceType ValidResourceType => ResourceGroupOperations.ResourceType; + + private VirtualNetworksOperations Operations => new NetworkManagementClient( + Id.Subscription, + BaseUri, + Credential, + ClientOptions.Convert()).VirtualNetworks; + + /// + public override ArmResponse CreateOrUpdate(string name, VirtualNetworkData resourceDetails) + { + var operation = Operations.StartCreateOrUpdate(Id.ResourceGroup, name, resourceDetails); + return new PhArmResponse( + operation.WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), + n => new VirtualNetwork(Parent, new VirtualNetworkData(n))); + } + + /// + public async override Task> CreateOrUpdateAsync(string name, VirtualNetworkData resourceDetails, CancellationToken cancellationToken = default) + { + var operation = await Operations.StartCreateOrUpdateAsync(Id.ResourceGroup, name, resourceDetails, cancellationToken).ConfigureAwait(false); + return new PhArmResponse( + await operation.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + n => new VirtualNetwork(Parent, new VirtualNetworkData(n))); + } + + /// + public override ArmOperation StartCreateOrUpdate(string name, VirtualNetworkData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + Operations.StartCreateOrUpdate(Id.ResourceGroup, name, resourceDetails, cancellationToken), + n => new VirtualNetwork(Parent, new VirtualNetworkData(n))); + } + + /// + public async override Task> StartCreateOrUpdateAsync(string name, VirtualNetworkData resourceDetails, CancellationToken cancellationToken = default) + { + return new PhArmOperation( + await Operations.StartCreateOrUpdateAsync(Id.ResourceGroup, name, resourceDetails, cancellationToken).ConfigureAwait(false), + n => new VirtualNetwork(Parent, new VirtualNetworkData(n))); + } + + /// + /// Constructs an object used to create a virtual nerwork. + /// + /// The CIDR of the resource. + /// The location of the resource. + /// A builder with and . + public ArmBuilder Construct(string vnetCidr, LocationData location = null) + { + var parent = GetParentResource(); + var vnet = new Azure.ResourceManager.Network.Models.VirtualNetwork() + { + Location = location ?? parent.Data.Location, + AddressSpace = new AddressSpace() { AddressPrefixes = new List() { vnetCidr } }, + }; + + return new ArmBuilder(this, new VirtualNetworkData(vnet)); + } + + /// + /// Lists the virtual network for this resource group. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of resource operations that may take multiple service requests to iterate over. + public Pageable List(CancellationToken cancellationToken = default) + { + return new PhWrappingPageable( + Operations.List(Id.Name, cancellationToken), + Convertor()); + } + + /// + /// Lists the virtual network for this resource group. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of resource operations that may take multiple service requests to iterate over. + public AsyncPageable ListAsync(CancellationToken cancellationToken = default) + { + return new PhWrappingAsyncPageable( + Operations.ListAsync(Id.Name, cancellationToken), + Convertor()); + } + + /// + /// Filters the list of virtual nerwork for this resource group represented as generic resources. + /// + /// The filter used in this operation. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of resource that may take multiple service requests to iterate over. + public Pageable ListByName(string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(VirtualNetworkData.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContext(Parent as ResourceGroupOperations, filters, top, cancellationToken); + } + + /// + /// Filters the list of virtual nerwork for this resource group represented as generic resources. + /// + /// The filter used in this operation. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of resource that may take multiple service requests to iterate over. + public AsyncPageable ListByNameAsync(string filter, int? top = null, CancellationToken cancellationToken = default) + { + ResourceFilterCollection filters = new ResourceFilterCollection(VirtualNetworkData.ResourceType); + filters.SubstringFilter = filter; + return ResourceListOperations.ListAtContextAsync(Parent as ResourceGroupOperations, filters, top, cancellationToken); + } + + /// + /// Filters the list of virtual nerwork for this resource group. + /// Makes an additional network call to retrieve the full data model for each virtual nerwork. + /// + /// The filter used in this operation. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of resource that may take multiple service requests to iterate over. + public Pageable ListByNameExpanded(string filter, int? top = null, CancellationToken cancellationToken = default) + { + var results = ListByName(filter, top, cancellationToken); + return new PhWrappingPageable(results, s => new VirtualNetworkOperations(s).Get().Value); + } + + /// + /// Filters the list of virtual nerwork for this resource group. + /// Makes an additional network call to retrieve the full data model for each virtual nerwork. + /// + /// The filter used in this operation. + /// The number of results to return. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An asyc collection of availability set that may take multiple service requests to iterate over. + public AsyncPageable ListByNameExpandedAsync(string filter, int? top = null, CancellationToken cancellationToken = default) + { + var results = ListByNameAsync(filter, top, cancellationToken); + return new PhWrappingAsyncPageable(results, s => new VirtualNetworkOperations(s).Get().Value); + } + + private Func Convertor() + { + return s => new VirtualNetwork(Parent, new VirtualNetworkData(s)); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/network/VirtualNetworkOperations.cs b/sdk/resourcemanager/Proto.Client/network/VirtualNetworkOperations.cs new file mode 100644 index 0000000000000..e9827c17400bd --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/VirtualNetworkOperations.cs @@ -0,0 +1,325 @@ +using Azure; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Network.Models; +using Azure.ResourceManager.Core; +using System.Threading; +using System.Threading.Tasks; +using System.Linq; +using System.Collections.Generic; +using System; + +namespace Proto.Network +{ + /// + /// A class representing the operations that can be performed over a specific virtual nerwork. + /// + public class VirtualNetworkOperations : ResourceOperationsBase, ITaggableResource, IDeletableResource + { + /// + /// Initializes a new instance of the class. + /// + /// An instance of that has an id for a virtual nerwork. + internal VirtualNetworkOperations(GenericResourceOperations genericOperations) + : base(genericOperations) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The name of the virtual network to use. + internal VirtualNetworkOperations(ResourceGroupOperations resourceGroup, string vnetName) + : base(resourceGroup, $"{resourceGroup.Id}/providers/Microsoft.Network/virtualNetworks/{vnetName}") + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The client parameters to use in these operations. + /// The identifier of the resource that is the target of operations. + protected VirtualNetworkOperations(ResourceOperationsBase options, ResourceIdentifier id) + : base(options, id) + { + } + + /// + /// Gets the resource type definition for a virtual nerwork. + /// + public static readonly ResourceType ResourceType = "Microsoft.Network/virtualNetworks"; + + /// + /// Gets the valid resource type definition for a virtual nerwork. + /// + protected override ResourceType ValidResourceType => ResourceType; + + private VirtualNetworksOperations Operations => new NetworkManagementClient( + Id.Subscription, + BaseUri, + Credential, + ClientOptions.Convert()).VirtualNetworks; + + /// + /// The operation to delete a virtual nerwork. + /// + /// A response with the operation for this resource. + public ArmResponse Delete() + { + return new ArmResponse(Operations.StartDelete(Id.ResourceGroup, Id.Name).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// The operation to delete a virtual nerwork. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public async Task> DeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmResponse((await Operations.StartDeleteAsync(Id.ResourceGroup, Id.Name, cancellationToken)).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + /// + /// The operation to delete a virtual nerwork. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// An that allows polling for completion of the operation. + public ArmOperation StartDelete(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(Operations.StartDelete(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + /// The operation to delete a virtual nerwork. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> StartDeleteAsync(CancellationToken cancellationToken = default) + { + return new ArmVoidOperation(await Operations.StartDeleteAsync(Id.ResourceGroup, Id.Name, cancellationToken)); + } + + /// + public override ArmResponse Get() + { + return new PhArmResponse(Operations.Get(Id.ResourceGroup, Id.Name), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + public async override Task> GetAsync(CancellationToken cancellationToken = default) + { + return new PhArmResponse(await Operations.GetAsync(Id.ResourceGroup, Id.Name, null, cancellationToken), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + /// Adds a tag to a virtual network. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// + /// Details on long running operation object. + /// + /// An that allows polling for completion of the operation. + public ArmResponse AddTag(string key, string value) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + /// Adds a tag to a virtual network. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + /// Adds a tag to a virtual network. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// + /// Details on long running operation object. + /// + /// An that allows polling for completion of the operation. + public ArmOperation StartAddTag(string key, string value) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + /// Adds a tag to a virtual network. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// + /// Details on long running operation object. + /// + /// A that on completion returns an that allows polling for completion of the operation. + public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + patchable.Tags[key] = value; + return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + /// Gets a subnet in the virtual nerwork. + /// + /// The name of the subnet. + /// An instance of SubnetOperations. + public SubnetOperations GetSubnetOperations(string subnet) + { + return new SubnetOperations(this, subnet); + } + + /// + /// Gets a list of subnet in the virtual nerwork. + /// + /// An object representing collection of subnets and their operations over a virtual network. + public SubnetContainer GetSubnetContainer() + { + return new SubnetContainer(this); + } + + /// + public ArmResponse SetTags(IDictionary tags) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, patchable.Tags); + return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, patchable.Tags); + return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + public ArmOperation StartSetTags(IDictionary tags) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, patchable.Tags); + return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + ReplaceTags(tags, patchable.Tags); + return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + public ArmResponse RemoveTag(string key) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, patchable.Tags); + return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, patchable.Tags); + return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + public ArmOperation StartRemoveTag(string key) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, patchable.Tags); + return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) + { + var resource = GetResource(); + var patchable = new TagsObject() { Tags = resource.Data.Tags }; + DeleteTag(key, patchable.Tags); + return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), + n => new VirtualNetwork(this, new VirtualNetworkData(n))); + } + + /// + /// Lists all available geo-locations. + /// + /// A collection of location that may take multiple service requests to iterate over. + public IEnumerable ListAvailableLocations() + { + var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); + var vnProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var vnResource = vnProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return vnResource.Locations.Select(l => (LocationData)l); + } + + /// + /// Lists all available geo-locations. + /// + /// A token to allow the caller to cancel the call to the service. The default value is . + /// An async collection of location that may take multiple service requests to iterate over. + /// The default subscription id is null. + public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) + { + var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); + var vnProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + var vnResource = vnProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); + return vnResource.Locations.Select(l => (LocationData)l); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Common/ScenarioHelper.cs b/sdk/resourcemanager/Proto.Client/src/Common/ScenarioHelper.cs new file mode 100644 index 0000000000000..a440264bb3904 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Common/ScenarioHelper.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Proto.Client.Common +{ + internal class ScenarioHelper + { + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Program.cs b/sdk/resourcemanager/Proto.Client/src/Program.cs new file mode 100644 index 0000000000000..1e4f6ee5de7a0 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Program.cs @@ -0,0 +1,35 @@ +using Azure.ResourceManager.Core; +using System; + +namespace Proto.Client +{ + class Program + { + static void Main(string[] args) + { + Scenario scenario = null; + try + { + scenario = ScenarioFactory.GetScenario(Scenarios.CreateSingleVmExample); + scenario.Execute(); + } + finally + { + foreach (var rgId in Scenario.CleanUp) + { + ResourceIdentifier id = new ResourceIdentifier(rgId); + var rg = new AzureResourceManagerClient().GetSubscriptionOperations(id.Subscription).GetResourceGroupOperations(id.ResourceGroup); + Console.WriteLine($"--------Deleting {rg.Id.Name}--------"); + try + { + _ = rg.DeleteAsync(); + } + catch + { + //ignore exceptions in case the rg doesn't exist + } + } + } + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Proto.Client.csproj b/sdk/resourcemanager/Proto.Client/src/Proto.Client.csproj new file mode 100644 index 0000000000000..57df41d88b2fd --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Proto.Client.csproj @@ -0,0 +1,19 @@ + + + + Exe + netcoreapp3.1 + + + + DEBUG;TRACE + + + + + + + + + + diff --git a/sdk/resourcemanager/Proto.Client/src/Scenario.cs b/sdk/resourcemanager/Proto.Client/src/Scenario.cs new file mode 100644 index 0000000000000..a6929c4c622bf --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenario.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Proto.Client +{ + abstract class Scenario + { + public ScenarioContext Context { get; private set; } + + public readonly static HashSet CleanUp = new HashSet(); + + public abstract void Execute(); + + public Scenario() : this(new ScenarioContext()) { } + + public Scenario(ScenarioContext context) + { + Context = context; + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/ScenarioContext.cs b/sdk/resourcemanager/Proto.Client/src/ScenarioContext.cs new file mode 100644 index 0000000000000..966196ef6c2ad --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/ScenarioContext.cs @@ -0,0 +1,30 @@ +using Azure.ResourceManager.Core; +using System; + +namespace Proto.Client +{ + class ScenarioContext + { + public static readonly string AzureSdkSandboxId = "db1ab6f0-4769-4b27-930e-01e2ef9c123c"; + + public string Hostname => $"{Environment.UserName}"; + public string VmName => $"{Environment.UserName}"; + public string RgName { get; private set; } + public string NsgName => $"{Environment.UserName}-test-nsg"; + public string SubscriptionId { get; private set; } + public string Loc => LocationData.WestUS2; + public string SubnetName => $"{Environment.UserName}-subnet"; + + public string PrincipalId => "4c5ce728-7350-4ae5-bcf1-42e8e33b00fe"; + + public string RoleId => "acdd72a7-3385-48ef-bd42-f606fba81ae7"; + + public ScenarioContext() : this(AzureSdkSandboxId) { } + + public ScenarioContext(string subscriptionId) + { + RgName = $"{Environment.UserName}-{Environment.TickCount}-rg"; + SubscriptionId = subscriptionId; + } + } +} \ No newline at end of file diff --git a/sdk/resourcemanager/Proto.Client/src/ScenarioFactory.cs b/sdk/resourcemanager/Proto.Client/src/ScenarioFactory.cs new file mode 100644 index 0000000000000..6bf7856f4ed82 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/ScenarioFactory.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace Proto.Client +{ + enum Scenarios + { + All, + GetVMTaskExamples, + CreateSingleVmExample, + CreateSingleVMCheckLocation, + ShutdownVmsByName, + StartStopVm, + StartFromVm, + SetTagsOnVm, + ShutdownVmsByTag, + CreateMultipleVms, + GenericEntityLoop, + ShutdownVmsByLINQ, + ShutdownVmsByNameAcrossResourceGroups, + //ShutdownVmsByNameAcrossSubscriptions, + ListByNameExpanded, + ClientOptionsOverride, + GetSubscription, + NullDataValues, + //RoleAssignment, + //DeleteGeneric, + //AddTagToGeneric, + CheckResourceExists, + GetFromOperations, + CreateSingleVmExampleAsync, + StartCreateSingleVmExampleAsync, + StartCreateSingleVmExample, + DefaultSubscription, + SubscriptionExists, + UseParentLocation, + } + + class ScenarioFactory + { + public static Scenario GetScenario(Scenarios scenario) + { + switch(scenario) + { + default: + var type = Assembly.GetExecutingAssembly() + .GetTypes() + .Where(t => t.Name == scenario.ToString()) + .First(); + return Activator.CreateInstance(type) as Scenario; + } + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/AddTagToGeneric.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/AddTagToGeneric.cs new file mode 100644 index 0000000000000..3178f20698f8f --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/AddTagToGeneric.cs @@ -0,0 +1,30 @@ +using Azure.ResourceManager.Core; +using Proto.Compute; +using System; + +namespace Proto.Client +{ + class AddTagToGeneric : Scenario + { + public override void Execute() + { + var createVm = new CreateSingleVmExample(Context); + createVm.Execute(); + + var rgOp = new AzureResourceManagerClient().GetResourceGroupOperations(Context.SubscriptionId, Context.RgName); + foreach (var genericOp in rgOp.GetVirtualMachineContainer().ListByName(Context.VmName)) + { + Console.WriteLine($"Adding tag to {genericOp.Id}"); + genericOp.StartAddTag("tagKey", "tagVaue"); + } + + var vmOp = rgOp.GetVirtualMachineOperations(Context.VmName); + Console.WriteLine($"Getting {vmOp.Id}"); + var vm = vmOp.Get().Value; + + if(!vm.Data.Tags.ContainsKey("tagKey")) + throw new InvalidOperationException("Failed"); + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/All.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/All.cs new file mode 100644 index 0000000000000..c7ae7aeaa86af --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/All.cs @@ -0,0 +1,45 @@ +using Azure.ResourceManager.Core; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Proto.Client +{ + class All : Scenario + { + public override void Execute() + { + var list = Enum.GetValues(typeof(Scenarios)).Cast().ToList(); + try + { + foreach(var scenario in list) + { + if (scenario != Scenarios.All) + { + Console.WriteLine($"########## Starting Scenario {scenario} ##########"); + var executable = ScenarioFactory.GetScenario(scenario); + executable.Execute(); + Console.WriteLine($"########## Finished Scenario {scenario} ##########"); + } + } + } + finally + { + foreach (var rgId in CleanUp) + { + ResourceIdentifier id = new ResourceIdentifier(rgId); + var rg = new AzureResourceManagerClient().GetResourceGroupOperations(rgId); + Console.WriteLine($"--------Deleting {rg.Id.Name}--------"); + try + { + _ = rg.DeleteAsync(); + } + catch + { + //ignore exceptions in case rg doesn't exist + } + } + } + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/CheckResourceExists.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/CheckResourceExists.cs new file mode 100644 index 0000000000000..c5988fe7c6eb5 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/CheckResourceExists.cs @@ -0,0 +1,55 @@ +using Azure.ResourceManager.Core; +using Proto.Compute; +using System; + +namespace Proto.Client +{ + class CheckResourceExists : Scenario + { + public override void Execute() + { + var client = new AzureResourceManagerClient(); + var subOp = client.DefaultSubscription; + var rgContainer = subOp.GetResourceGroupContainer(); + + Console.WriteLine($"Making sure {Context.RgName} doesn't exist yet."); + if (rgContainer.DoesExist(Context.RgName)) + throw new Exception($"The resource group {Context.RgName} should not have existed."); + + Console.WriteLine($"Creating {Context.RgName}"); + _ = rgContainer.Construct(LocationData.Default).CreateOrUpdate(Context.RgName).Value; + + Console.WriteLine($"Making sure {Context.RgName} exists now."); + if (!rgContainer.DoesExist(Context.RgName)) + throw new Exception($"The resource group {Context.RgName} should have existed."); + + Console.WriteLine($"Using try get value to retrieve {Context.RgName}"); + ArmResponse rgOutput; + if(!rgContainer.TryGetValue(Context.RgName, out rgOutput)) + throw new Exception($"The resource group {Context.RgName} should have existed."); + + var rg = rgOutput.Value; + + var asetContainer = rg.GetAvailabilitySetContainer(); + var asetName = Context.VmName + "_aSet"; + + Console.WriteLine($"Making sure {asetName} doesn't exist yet."); + if (asetContainer.DoesExist(asetName)) + throw new Exception($"The availability set {asetName} should not have existed."); + + Console.WriteLine($"Creating {asetName}"); + var aset = asetContainer.Construct("Aligned").CreateOrUpdate(asetName).Value; + + Console.WriteLine($"Making sure {asetName} exists now."); + if (!asetContainer.DoesExist(asetName)) + throw new Exception($"The availability set {asetName} should have existed."); + + Console.WriteLine("Using try get value to retrieve the rg"); + ArmResponse asetOutput; + if (!asetContainer.TryGetValue(asetName, out asetOutput)) + throw new Exception($"The availability set {asetName} should have existed."); + + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/ClientOverrides.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/ClientOverrides.cs new file mode 100644 index 0000000000000..44e31dc3c1090 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/ClientOverrides.cs @@ -0,0 +1,62 @@ +using Azure.ResourceManager.Core; +using System; +using Azure.Core; +using Azure.Core.Pipeline; +using System.Threading; +using System.Diagnostics; + +namespace Proto.Client +{ + class ClientOptionsOverride : Scenario + { + public override void Execute() + { + + AzureResourceManagerClientOptions options1 = new AzureResourceManagerClientOptions(); + AzureResourceManagerClientOptions options2 = new AzureResourceManagerClientOptions(); + var dummyPolicy1 = new dummyPolicy(); + var dummyPolicy2 = new dummyPolicy2(); + options1.AddPolicy(dummyPolicy1, HttpPipelinePosition.PerCall); + options2.AddPolicy(dummyPolicy2, HttpPipelinePosition.PerCall); + var client1 = new AzureResourceManagerClient(options1); + var client2 = new AzureResourceManagerClient(options2); + + Console.WriteLine("-----Client 1-----"); + foreach (var sub in client1.GetSubscriptionContainer().List()) + { + Console.WriteLine($"Found {sub.Data.DisplayName}"); + } + + Console.WriteLine("-----Client 2-----"); + foreach (var sub in client2.GetSubscriptionContainer().List()) + { + Console.WriteLine($"Found {sub.Data.DisplayName}"); + } + + Debug.Assert(dummyPolicy1.numMsgGot * 2 == dummyPolicy2.numMsgGot); + Console.WriteLine("\nPASSED\n"); + + } + + private class dummyPolicy : HttpPipelineSynchronousPolicy + { + public int numMsgGot = 0; + + public override void OnReceivedResponse(HttpMessage message) + { + Interlocked.Increment(ref numMsgGot); + } + } + + private class dummyPolicy2 : HttpPipelineSynchronousPolicy + { + public int numMsgGot = 0; + + public override void OnReceivedResponse(HttpMessage message) + { + Interlocked.Add(ref numMsgGot, 2); + } + } + + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/CreateMultipleVms.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/CreateMultipleVms.cs new file mode 100644 index 0000000000000..b3eed9cac93e2 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/CreateMultipleVms.cs @@ -0,0 +1,70 @@ +using Proto.Compute; +using Azure.ResourceManager.Core; +using Proto.Network; +using System; +using System.Collections.Generic; + +namespace Proto.Client +{ + class CreateMultipleVms : Scenario + { + public CreateMultipleVms() : base() { } + + public CreateMultipleVms(ScenarioContext context) : base(context) { } + + public override void Execute() + { + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + + // Create Resource Group + Console.WriteLine($"--------Start create group {Context.RgName}--------"); + var resourceGroup = subscription.GetResourceGroupContainer().Construct(Context.Loc).CreateOrUpdate(Context.RgName).Value; + CleanUp.Add(resourceGroup.Id); + + // Create AvailabilitySet + Console.WriteLine("--------Start create AvailabilitySet--------"); + var aset = resourceGroup.GetAvailabilitySetContainer().Construct("Aligned").CreateOrUpdate(Context.VmName + "_aSet").Value; + + // Create VNet + Console.WriteLine("--------Start create VNet--------"); + string vnetName = Context.VmName + "_vnet"; + var vnet = resourceGroup.GetVirtualNetworkContainer().Construct("10.0.0.0/16").CreateOrUpdate(vnetName).Value; + + //create subnet + Console.WriteLine("--------Start create Subnet--------"); + var subnet = vnet.GetSubnetContainer().Construct("10.0.0.0/24").CreateOrUpdate(Context.SubnetName).Value; + + //create network security group + Console.WriteLine("--------Start create NetworkSecurityGroup--------"); + _ = resourceGroup.GetNetworkSecurityGroupContainer().Construct(80).CreateOrUpdate(Context.NsgName).Value; + + CreateVms(resourceGroup, aset, subnet); + + } + + private void CreateVms(ResourceGroup resourceGroup, AvailabilitySet aset, SubnetOperations subnet) + { + List> operations = new List>(); + for (int i = 0; i < 10; i++) + { + // Create Network Interface + Console.WriteLine("--------Start create Network Interface--------"); + var nic = resourceGroup.GetNetworkInterfaceContainer().Construct(subnet.Id).CreateOrUpdate($"{Context.VmName}_{i}_nic").Value; + + // Create VM + string num = i % 2 == 0 ? "-e" : "-o"; + string name = $"{Context.VmName}{i}{num}"; + Console.WriteLine("--------Start create VM {0}--------", i); + var vmOp = resourceGroup.GetVirtualMachineContainer().Construct(Context.Hostname, "admin-user", "!@#$%asdfA", nic.Id, aset.Id).StartCreateOrUpdate(name); + operations.Add(vmOp); + } + + foreach (var operation in operations) + { + var vm = operation.WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult().Value; + Console.WriteLine($"--------Finished creating VM {vm.Id.Name}"); + } + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/CreateSingleVMCheckLocation.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/CreateSingleVMCheckLocation.cs new file mode 100644 index 0000000000000..0308cbbbf9c7b --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/CreateSingleVMCheckLocation.cs @@ -0,0 +1,90 @@ +using Azure.ResourceManager.Core; +using Proto.Compute; +using Proto.Network; +using System; +using System.Linq; + +namespace Proto.Client +{ + class CreateSingleVMCheckLocation : Scenario + { + public CreateSingleVMCheckLocation() : base() { } + + public CreateSingleVMCheckLocation(ScenarioContext context) : base(context) { } + + public override void Execute() + { + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + + // Create Resource Group + Console.WriteLine($"--------Start create group {Context.RgName}--------"); + var resourceGroup = subscription.GetResourceGroupContainer().Construct(Context.Loc).CreateOrUpdate(Context.RgName).Value; + CleanUp.Add(resourceGroup.Id); + Console.WriteLine("\nResource Group List Available Locations: "); + var loc = resourceGroup.ListAvailableLocations(); + foreach(var l in loc) + { + Console.WriteLine(l.DisplayName); + } + + // Create AvailabilitySet + Console.WriteLine("--------Start create AvailabilitySet--------"); + var aset = resourceGroup.GetAvailabilitySetContainer().Construct("Aligned").CreateOrUpdate(Context.VmName + "_aSet").Value; + Console.WriteLine("\nAvailability Set List Available Locations: "); + loc = aset.ListAvailableLocations(); + foreach (var l in loc) + { + Console.WriteLine(l.DisplayName); + } + + // Create VNet + Console.WriteLine("--------Start create VNet--------"); + string vnetName = Context.VmName + "_vnet"; + var vnet = resourceGroup.GetVirtualNetworkContainer().Construct("10.0.0.0/16").CreateOrUpdate(vnetName).Value; + Console.WriteLine("\nVirtual Network List Available Locations: "); + loc = vnet.ListAvailableLocations(); + foreach (var l in loc) + { + Console.WriteLine(l.DisplayName); + } + + //create subnet + Console.WriteLine("--------Start create Subnet--------"); + var subnet = vnet.GetSubnetContainer().Construct("10.0.0.0/24").CreateOrUpdate(Context.SubnetName).Value; + + //create network security group + Console.WriteLine("--------Start create NetworkSecurityGroup--------"); + var nsg = resourceGroup.GetNetworkSecurityGroupContainer().Construct(80).CreateOrUpdate(Context.NsgName).Value; + Console.WriteLine("\nNetwork Security Group List Available Locations: "); + loc = nsg.ListAvailableLocations(); + foreach (var l in loc) + { + Console.WriteLine(l.DisplayName); + } + + // Create Network Interface + Console.WriteLine("--------Start create Network Interface--------"); + var nic = resourceGroup.GetNetworkInterfaceContainer().Construct(subnet.Id).CreateOrUpdate($"{Context.VmName}_nic").Value; + Console.WriteLine("\nNetwork Interface Container List Available Locations: "); + loc = nic.ListAvailableLocations(); + foreach (var l in loc) + { + Console.WriteLine(l.DisplayName); + } + + // Create VM + Console.WriteLine("--------Start create VM--------"); + var vm = resourceGroup.GetVirtualMachineContainer().Construct(Context.Hostname, "admin-user", "!@#$%asdfA", nic.Id, aset.Id).CreateOrUpdate(Context.VmName).Value; + Console.WriteLine("\nVirtual Machine List Available Locations: "); + loc = vm.ListAvailableLocations(); + foreach (var l in loc) + { + Console.WriteLine(l.DisplayName); + } + + Console.WriteLine("VM ID: " + vm.Id); + Console.WriteLine("--------Done create VM--------"); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/CreateSingleVmExample.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/CreateSingleVmExample.cs new file mode 100644 index 0000000000000..7ae8ff1d44db9 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/CreateSingleVmExample.cs @@ -0,0 +1,54 @@ +using Azure.ResourceManager.Core; +using Proto.Compute; +using Proto.Network; +using System; + +namespace Proto.Client +{ + class CreateSingleVmExample : Scenario + { + public CreateSingleVmExample() : base() { } + + public CreateSingleVmExample(ScenarioContext context) : base(context) { } + + public override void Execute() + { + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + + // Create Resource Group + Console.WriteLine($"--------Start create group {Context.RgName}--------"); + var resourceGroup = subscription.GetResourceGroupContainer().Construct(Context.Loc).CreateOrUpdate(Context.RgName).Value; + CleanUp.Add(resourceGroup.Id); + + // Create AvailabilitySet + Console.WriteLine("--------Start create AvailabilitySet--------"); + var aset = resourceGroup.GetAvailabilitySetContainer().Construct("Aligned").CreateOrUpdate(Context.VmName + "_aSet").Value; + + // Create VNet + Console.WriteLine("--------Start create VNet--------"); + string vnetName = Context.VmName + "_vnet"; + var vnet = resourceGroup.GetVirtualNetworkContainer().Construct("10.0.0.0/16").CreateOrUpdate(vnetName).Value; + + //create subnet + Console.WriteLine("--------Start create Subnet async--------"); + var subnet = vnet.GetSubnetContainer().Construct("10.0.0.0/24").CreateOrUpdateAsync(Context.SubnetName).ConfigureAwait(false).GetAwaiter().GetResult().Value; + + //create network security group + Console.WriteLine("--------Start create NetworkSecurityGroup--------"); + _ = resourceGroup.GetNetworkSecurityGroupContainer().Construct(80).CreateOrUpdate(Context.NsgName).Value; + + // Create Network Interface + Console.WriteLine("--------Start create Network Interface--------"); + var nic = resourceGroup.GetNetworkInterfaceContainer().Construct(subnet.Id).CreateOrUpdate($"{Context.VmName}_nic").Value; + + // Create VM + Console.WriteLine("--------Start create VM--------"); + var vm = resourceGroup.GetVirtualMachineContainer().Construct(Context.Hostname, "admin-user", "!@#$%asdfA", nic.Id, aset.Id).CreateOrUpdate(Context.VmName).Value; + + Console.WriteLine("VM ID: " + vm.Id); + Console.WriteLine("--------Done create VM--------"); + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/CreateSingleVmExampleAsync.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/CreateSingleVmExampleAsync.cs new file mode 100644 index 0000000000000..52218cbec5b69 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/CreateSingleVmExampleAsync.cs @@ -0,0 +1,58 @@ +using Azure.ResourceManager.Core; +using Proto.Compute; +using Proto.Network; +using System; + +namespace Proto.Client +{ + class CreateSingleVmExampleAsync : Scenario + { + public CreateSingleVmExampleAsync() : base() { } + + public CreateSingleVmExampleAsync(ScenarioContext context) : base(context) { } + + public override void Execute() + { + ExcuteAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + + private async System.Threading.Tasks.Task ExcuteAsync() + { + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + + // Create Resource Group + Console.WriteLine($"--------Start create group async {Context.RgName}--------"); + var resourceGroup = (await subscription.GetResourceGroupContainer().Construct(Context.Loc).CreateOrUpdateAsync(Context.RgName)).Value; + CleanUp.Add(resourceGroup.Id); + + // Create AvailabilitySet + Console.WriteLine("--------Start create AvailabilitySet async--------"); + var aset = (await resourceGroup.GetAvailabilitySetContainer().Construct("Aligned").CreateOrUpdateAsync(Context.VmName + "_aSet")).Value; + + // Create VNet + Console.WriteLine("--------Start create VNet async--------"); + string vnetName = Context.VmName + "_vnet"; + var vnet = (await resourceGroup.GetVirtualNetworkContainer().Construct("10.0.0.0/16").CreateOrUpdateAsync(vnetName)).Value; + + //create subnet + Console.WriteLine("--------Start create Subnet async--------"); + var subnet = (await vnet.GetSubnetContainer().Construct("10.0.0.0/24").CreateOrUpdateAsync(Context.SubnetName)).Value; + + //create network security group + Console.WriteLine("--------Start create NetworkSecurityGroup async--------"); + _ = (await resourceGroup.GetNetworkSecurityGroupContainer().Construct(80).CreateOrUpdateAsync(Context.NsgName)).Value; + + // Create Network Interface + Console.WriteLine("--------Start create Network Interface async--------"); + var nic = (await resourceGroup.GetNetworkInterfaceContainer().Construct(subnet.Id).CreateOrUpdateAsync($"{Context.VmName}_nic")).Value; + + // Create VM + Console.WriteLine("--------Start create VM async--------"); + var vm = (await resourceGroup.GetVirtualMachineContainer().Construct(Context.Hostname, "admin-user", "!@#$%asdfA", nic.Id, aset.Id).CreateOrUpdateAsync(Context.VmName)).Value; + + Console.WriteLine("VM ID: " + vm.Id); + Console.WriteLine("--------Done create VM--------"); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/DefaultSubscription.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/DefaultSubscription.cs new file mode 100644 index 0000000000000..b2a3692a0c416 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/DefaultSubscription.cs @@ -0,0 +1,30 @@ +using Azure.Identity; +using Azure.ResourceManager.Core; +using System; + +namespace Proto.Client +{ + class DefaultSubscription : Scenario + { + public override void Execute() + { + var client = new AzureResourceManagerClient(Context.SubscriptionId, new DefaultAzureCredential()); + + var sub = client.DefaultSubscription; + + if (sub.Data.SubscriptionGuid != Context.SubscriptionId) + throw new Exception($"Didn't get back expected subscription. Got {sub.Data.SubscriptionGuid} expected {Context.SubscriptionId}"); + + Console.WriteLine("Found correct subscription"); + + client = new AzureResourceManagerClient(); + + sub = client.DefaultSubscription; + + if (sub.Data.SubscriptionGuid != Context.SubscriptionId) + throw new Exception($"Didn't get back expected subscription. Got {sub.Data.SubscriptionGuid} expected {Context.SubscriptionId}"); + + Console.WriteLine("Found correct subscription"); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/DeleteGeneric.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/DeleteGeneric.cs new file mode 100644 index 0000000000000..12ca505e5a36d --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/DeleteGeneric.cs @@ -0,0 +1,37 @@ +using Azure; +using Azure.ResourceManager.Core; +using Proto.Compute; +using System; + +namespace Proto.Client +{ + class DeleteGeneric : Scenario + { + public override void Execute() + { + var createVm = new CreateSingleVmExample(Context); + createVm.Execute(); + + var rgOp = new AzureResourceManagerClient().GetResourceGroupOperations(Context.SubscriptionId, Context.RgName); + foreach(var genericOp in rgOp.GetVirtualMachineContainer().ListByName(Context.VmName)) + { + Console.WriteLine($"Deleting {genericOp.Id}"); + genericOp.Delete(); + } + + try + { + var vmOp = rgOp.GetVirtualMachineOperations(Context.VmName); + Console.WriteLine($"Trying to get {vmOp.Id}"); + var response = vmOp.Get(); + } + catch(RequestFailedException e) when (e.Status == 404) + { + Console.WriteLine("Got 404 returned as expected"); + + } + + throw new InvalidOperationException("Failed"); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/GenericEntityLoop.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/GenericEntityLoop.cs new file mode 100644 index 0000000000000..1676cde944b10 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/GenericEntityLoop.cs @@ -0,0 +1,23 @@ +using Proto.Compute; +using Azure.ResourceManager.Core; +using System; + +namespace Proto.Client +{ + class GenericEntityLoop : Scenario + { + public override void Execute() + { + var createVm = new CreateSingleVmExample(Context); + createVm.Execute(); + + var rgOp = new AzureResourceManagerClient().GetResourceGroupOperations(Context.SubscriptionId, Context.RgName); + foreach(var entity in rgOp.GetVirtualMachineContainer().List()) + { + Console.WriteLine($"{entity.Id.Name}"); + entity.StartAddTag("name", "Value"); + } + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/GetFromOperations.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/GetFromOperations.cs new file mode 100644 index 0000000000000..34041115bf2f7 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/GetFromOperations.cs @@ -0,0 +1,29 @@ +using Azure.ResourceManager.Core; +using Proto.Compute; +using Proto.Network; + +namespace Proto.Client +{ + class GetFromOperations : Scenario + { + public override void Execute() + { + var createVm = new CreateSingleVmExample(Context); + createVm.Execute(); + + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + + var resourceGroup = subscription.GetResourceGroupOperations(Context.RgName).Get().Value; + _ = resourceGroup.GetAvailabilitySetOperations(Context.VmName + "_aSet").Get().Value; + var vnet = resourceGroup.GetVirtualNetworkOperations(Context.VmName + "_vnet").Get().Value; + _ = vnet.GetSubnetOperations(Context.SubnetName).Get().Value; + _ = resourceGroup.GetNetworkSecurityGroupOperations(Context.NsgName).Get().Value; + _ = resourceGroup.GetPublicIpAddressOperations($"{Context.VmName}_ip").Get().Value; + _ = resourceGroup.GetNetworkInterfaceOperations($"{Context.VmName}_nic").Get().Value; + _ = resourceGroup.GetVirtualMachineOperations(Context.VmName).Get().Value; + + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/GetSubscription.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/GetSubscription.cs new file mode 100644 index 0000000000000..a60e07d9dac77 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/GetSubscription.cs @@ -0,0 +1,20 @@ +using Azure.ResourceManager.Core; +using System; +using System.Diagnostics; + +namespace Proto.Client +{ + class GetSubscription : Scenario + { + public override void Execute() + { + var sandboxId = "db1ab6f0-4769-4b27-930e-01e2ef9c123c"; + var expectDisplayName = "Azure SDK sandbox"; + var subOp = new AzureResourceManagerClient().GetSubscriptionOperations(sandboxId); + var result = subOp.Get(); + Debug.Assert(expectDisplayName == result.Value.Data.DisplayName); + Console.WriteLine("Passed, got " + result.Value.Data.DisplayName); + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/GetVMTaskExamples.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/GetVMTaskExamples.cs new file mode 100644 index 0000000000000..925854a92448a --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/GetVMTaskExamples.cs @@ -0,0 +1,54 @@ +using Azure.ResourceManager.Core; +using Azure.ResourceManager.Network; +using Proto.Compute; +using Proto.Network; +using System; +using System.Linq; + +namespace Proto.Client +{ + class GetVMTaskExamples : Scenario + { + public GetVMTaskExamples() : base() { } + + public GetVMTaskExamples(ScenarioContext context) : base(context) { } + + public override void Execute() + { + var createVm = new CreateSingleVmExample(Context); + createVm.Execute(); + + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + var resourceGroup = subscription.GetResourceGroupOperations(Context.RgName); + var vmId = resourceGroup.GetVirtualMachineOperations(Context.VmName).Id; + var vnId = resourceGroup.GetVirtualNetworkOperations(Context.VmName + "_vnet").Id; + var subnetId = resourceGroup.GetVirtualNetworkOperations(Context.VmName + "_vnet").GetSubnetOperations(Context.SubnetName).Id; + var asId = resourceGroup.GetAvailabilitySetOperations(Context.VmName + "_aSet").Id; + var nsgId = resourceGroup.GetNetworkSecurityGroupOperations(Context.NsgName).Id; + var niId = resourceGroup.GetNetworkInterfaceOperations(Context.VmName + "_nic").Id; + + var vmOps = client.GetVirtualMachineOperations(vmId); + Console.WriteLine("\nclient.GetVirtualMachineOperations(vmResourceId)"); + vmOps.PowerOff(); + Console.WriteLine("Option 1 vm is " + vmOps.Get().Value.Data.InstanceView.Statuses.Last().Code); + vmOps.PowerOn(); + Console.WriteLine("Option 1 vm is " + vmOps.Get().Value.Data.InstanceView.Statuses.Last().Code); + + var subnetOps = client.GetSubnetOperations(subnetId); + Console.WriteLine("Option 1 subnet is " + subnetOps.Id); + + var vnOps = client.GetVirtualNetworkOperations(vnId); + var nsgOps = client.GetNetworkSecurityGroupOperations(nsgId); + var niOps = client.GetNetworkInterfaceOperations(niId); + var asOps = client.GetAvailabilitySetOperations(asId); + + Console.WriteLine(vnOps.Id); + Console.WriteLine(nsgOps.Id); + Console.WriteLine(niOps.Id); + Console.WriteLine(asOps.Id); + + Console.WriteLine("Demo complete"); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/ListByNameExpanded.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/ListByNameExpanded.cs new file mode 100644 index 0000000000000..1b4db26be8cd8 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/ListByNameExpanded.cs @@ -0,0 +1,133 @@ +using Proto.Compute; +using Azure.ResourceManager.Core; +using Proto.Network; +using System; +using System.Threading.Tasks; + +namespace Proto.Client +{ + class ListByNameExpanded : Scenario + { + public override void Execute() + { + var createMultipleVms = new CreateMultipleVms(Context); + createMultipleVms.Execute(); + + var rg = new AzureResourceManagerClient().GetResourceGroupOperations(Context.SubscriptionId, Context.RgName).Get().Value; + foreach (var availabilitySet in rg.GetAvailabilitySetContainer().ListByName(Environment.UserName)) + { + Console.WriteLine($"--------AvailabilitySet operation id--------: {availabilitySet.Id}"); + } + + foreach (var availabilitySet in rg.GetAvailabilitySetContainer().ListByNameExpanded(Environment.UserName)) + { + Console.WriteLine($"--------AvailabilitySet id--------: {availabilitySet.Data.Id}"); + } + + foreach (var vm in rg.GetVirtualMachineContainer().ListByName(Environment.UserName)) + { + Console.WriteLine($"--------VM operation id--------: {vm.Id}"); + } + + foreach (var vm in rg.GetVirtualMachineContainer().ListByNameExpanded(Environment.UserName)) + { + Console.WriteLine($"--------VM id--------: {vm.Data.Id}"); + } + + foreach (var networkInterface in rg.GetNetworkInterfaceContainer().ListByName(Environment.UserName)) + { + Console.WriteLine($"--------NetworkInterface operation id--------: {networkInterface.Id}"); + } + + foreach (var networkInterface in rg.GetNetworkInterfaceContainer().ListByNameExpanded(Environment.UserName)) + { + Console.WriteLine($"--------NetworkInterface id--------: {networkInterface.Data.Id}"); + } + + foreach (var networkSecurityGroup in rg.GetNetworkSecurityGroupContainer().ListByName(Environment.UserName)) + { + Console.WriteLine($"--------NetworkSecurityGroup operation id--------: {networkSecurityGroup.Id}"); + } + + foreach (var networkSecurityGroup in rg.GetNetworkSecurityGroupContainer().ListByNameExpanded(Environment.UserName)) + { + Console.WriteLine($"--------NetworkSecurityGroup id--------: {networkSecurityGroup.Data.Id}"); + } + + foreach (var publicIpAddress in rg.GetNetworkSecurityGroupContainer().ListByNameExpanded(Environment.UserName)) + { + Console.WriteLine($"--------PublicIpAddress id--------: {publicIpAddress.Data.Id}"); + } + + foreach (var VNet in rg.GetVirtualNetworkContainer().ListByName(Environment.UserName)) + { + Console.WriteLine($"--------VNet operation id--------: {VNet.Id}"); + } + + foreach (var VNet in rg.GetVirtualNetworkContainer().ListByNameExpanded(Environment.UserName)) + { + Console.WriteLine($"--------VNet id--------: {VNet.Data.Id}"); + } + ExecuteAsync(rg).GetAwaiter().GetResult(); + + } + + private async Task ExecuteAsync(ResourceGroup rg) + { + await foreach (var availabilitySet in rg.GetAvailabilitySetContainer().ListByNameAsync(Environment.UserName)) + { + Console.WriteLine($"--------AvailabilitySet operation id--------: {availabilitySet.Id}"); + } + + await foreach (var availabilitySet in rg.GetAvailabilitySetContainer().ListByNameExpandedAsync(Environment.UserName)) + { + Console.WriteLine($"--------AvailabilitySet id--------: {availabilitySet.Data.Id}"); + } + + await foreach (var vm in rg.GetVirtualMachineContainer().ListByNameAsync(Environment.UserName)) + { + Console.WriteLine($"--------VM operation id--------: {vm.Id}"); + } + + await foreach (var vm in rg.GetVirtualMachineContainer().ListByNameExpandedAsync(Environment.UserName)) + { + Console.WriteLine($"--------VM id--------: {vm.Data.Id}"); + } + + await foreach (var networkInterface in rg.GetNetworkInterfaceContainer().ListByNameAsync(Environment.UserName)) + { + Console.WriteLine($"--------NetworkInterface operation id--------: {networkInterface.Id}"); + } + + await foreach (var networkInterface in rg.GetNetworkInterfaceContainer().ListByNameExpandedAsync(Environment.UserName)) + { + Console.WriteLine($"--------NetworkInterface id--------: {networkInterface.Data.Id}"); + } + + await foreach (var networkSecurityGroup in rg.GetNetworkSecurityGroupContainer().ListByNameAsync(Environment.UserName)) + { + Console.WriteLine($"--------NetworkSecurityGroup operation id--------: {networkSecurityGroup.Id}"); + } + + await foreach (var networkSecurityGroup in rg.GetNetworkSecurityGroupContainer().ListByNameExpandedAsync(Environment.UserName)) + { + Console.WriteLine($"--------NetworkSecurityGroup id--------: {networkSecurityGroup.Data.Id}"); + } + + await foreach (var publicIpAddress in rg.GetNetworkSecurityGroupContainer().ListByNameExpandedAsync(Environment.UserName)) + { + Console.WriteLine($"--------PublicIpAddress id--------: {publicIpAddress.Data.Id}"); + } + + await foreach (var VNet in rg.GetVirtualNetworkContainer().ListByNameAsync(Environment.UserName)) + { + Console.WriteLine($"--------VNet operation id--------: {VNet.Id}"); + } + + await foreach (var VNet in rg.GetVirtualNetworkContainer().ListByNameExpandedAsync(Environment.UserName)) + { + Console.WriteLine($"--------VNet id--------: {VNet.Data.Id}"); + } + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/NullDataValues.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/NullDataValues.cs new file mode 100644 index 0000000000000..474cfe26a0098 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/NullDataValues.cs @@ -0,0 +1,23 @@ +using Azure.ResourceManager.Core; +using System; +using Azure.ResourceManager.Resources.Models; +using Proto.Network; +using Proto.Compute; + +namespace Proto.Client +{ + class NullDataValues : Scenario + { + public override void Execute() + { + var rg = new Azure.ResourceManager.Resources.Models.ResourceGroup("East US"); + var resourceGroupData = new ResourceGroupData(rg); + var nic = new Azure.ResourceManager.Network.Models.NetworkInterface(); + var networkInterfaceData = new NetworkInterfaceData(nic); + var aset = new Azure.ResourceManager.Compute.Models.AvailabilitySet("East US"); + var availabilitySet = new AvailabilitySetData(aset); + + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/RoleAssignment.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/RoleAssignment.cs new file mode 100644 index 0000000000000..0b840914f8e2c --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/RoleAssignment.cs @@ -0,0 +1,68 @@ +using Azure.ResourceManager.Core; +using Proto.Authorization; +using Proto.Compute; +using Proto.Network; +using System; + +namespace Proto.Client +{ + class RoleAssignment : Scenario + { + public override void Execute() + { + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + + // Create Resource Group + Console.WriteLine($"--------Start create group {Context.RgName}--------"); + var resourceGroup = subscription.GetResourceGroupContainer().Construct(Context.Loc).CreateOrUpdate(Context.RgName).Value; + CleanUp.Add(resourceGroup.Id); + + Console.WriteLine("--------Start create Assignment--------"); + var input = new RoleAssignmentCreateParameters($"/subscriptions/{Context.SubscriptionId}/resourceGroups/{Context.RgName}/providers/Microsoft.Authorization/roleDefinitions/{Context.RoleId}", Context.PrincipalId); + var assign = resourceGroup.GetRoleAssignmentContainer().Create(Guid.NewGuid().ToString(), input).Value; + Console.WriteLine("--------Done create Assignment--------"); + + assign = assign.Get().Value; + + // Create AvailabilitySet + Console.WriteLine("--------Start create AvailabilitySet--------"); + var aset = resourceGroup.GetAvailabilitySetContainer().Construct("Aligned").CreateOrUpdate(Context.VmName + "_aSet").Value; + + // Create VNet + Console.WriteLine("--------Start create VNet--------"); + string vnetName = Context.VmName + "_vnet"; + var vnet = resourceGroup.GetVirtualNetworkContainer().Construct("10.0.0.0/16").CreateOrUpdate(vnetName).Value; + + //create subnet + Console.WriteLine("--------Start create Subnet--------"); + var subnet = vnet.GetSubnetContainer().Construct("10.0.0.0/24").CreateOrUpdate(Context.SubnetName).Value; + + //create network security group + Console.WriteLine("--------Start create NetworkSecurityGroup--------"); + _ = resourceGroup.GetNetworkSecurityGroupContainer().Construct(80).CreateOrUpdate(Context.NsgName).Value; + + // Create Network Interface + Console.WriteLine("--------Start create Network Interface--------"); + var nic = resourceGroup.GetNetworkInterfaceContainer().Construct(subnet.Id).CreateOrUpdate($"{Context.VmName}_nic").Value; + + // Create VM + Console.WriteLine("--------Start create VM--------"); + var vm = resourceGroup.GetVirtualMachineContainer().Construct(Context.Hostname, "admin-user", "!@#$%asdfA", nic.Id, aset.Id).CreateOrUpdate(Context.VmName).Value; + + Console.WriteLine("VM ID: " + vm.Id); + Console.WriteLine("--------Done create VM--------"); + + + Console.WriteLine("--------Start create Assignment--------"); + var input2 = new RoleAssignmentCreateParameters($"{vm.Id}/providers/Microsoft.Authorization/roleDefinitions/{Context.RoleId}", Context.PrincipalId); + var assign2 = vm.GetRoleAssignmentContainer().Create(Guid.NewGuid().ToString(), input2).Value; + Console.WriteLine("--------Done create Assignment--------"); + + assign2 = assign2.Get().Value; + Console.WriteLine($"Created assignment: '{assign.Data.Id}'"); + + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/SetTagsOnVm.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/SetTagsOnVm.cs new file mode 100644 index 0000000000000..f4d978ae48cae --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/SetTagsOnVm.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Azure.ResourceManager.Core; +using Proto.Compute; + +namespace Proto.Client +{ + class SetTagsOnVm : Scenario + { + public override void Execute() + { + var createVm = new CreateSingleVmExample(Context); + createVm.Execute(); + + ExecuteAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + + private void DumpDictionary(IDictionary dic) + { + Console.WriteLine(string.Join( + ", ", + dic.Select(kvp => kvp.Key + ":" + kvp.Value))); + } + + private async Task ExecuteAsync() + { + // Update Tag for a known resource + var rgOp = new AzureResourceManagerClient().GetResourceGroupOperations(Context.SubscriptionId, Context.RgName); + var vmOp = rgOp.GetVirtualMachineOperations(Context.VmName); + + Console.WriteLine($"Adding tags to {vmOp.Id.Name}"); + + var vm = (await vmOp.StartAddTag("key1", "value1").WaitForCompletionAsync()).Value; + Debug.Assert(vm.Data.Tags.Where(x => x.Key.StartsWith("key")).Count() == 1); + DumpDictionary(vm.Data.Tags); + + vm = (await vm.StartAddTag("key2", "value2").WaitForCompletionAsync()).Value; + Debug.Assert(vm.Data.Tags.Where(x => x.Key.StartsWith("key")).Count() == 2); + DumpDictionary(vm.Data.Tags); + + vm = (await (await vmOp.StartAddTagAsync("key3", "value3")).WaitForCompletionAsync()).Value; + Debug.Assert(vm.Data.Tags.Where(x => x.Key.StartsWith("key")).Count() == 3); + DumpDictionary(vm.Data.Tags); + + vm = (await vm.StartAddTagAsync("key4", "value4")).WaitForCompletionAsync().Result.Value; + Debug.Assert(vm.Data.Tags.Where(x => x.Key.StartsWith("key")).Count() == 4); + DumpDictionary(vm.Data.Tags); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByLINQ.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByLINQ.cs new file mode 100644 index 0000000000000..c0b0fda0dc4c1 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByLINQ.cs @@ -0,0 +1,49 @@ +using Proto.Compute; +using Azure.ResourceManager.Core; +using System; +using System.Linq; + +namespace Proto.Client +{ + class ShutdownVmsByLINQ : Scenario + { + public override void Execute() + { + var createMultipleVms = new CreateMultipleVms(Context); + createMultipleVms.Execute(); + + var client = new AzureResourceManagerClient(); + foreach (var sub in client.GetSubscriptionContainer().List()) + { + var vmList = sub.ListVirtualMachines(); + foreach (var vm in vmList.Where(armResource => armResource.Data.Name.Contains("-o"))) + { + Console.WriteLine($"In subscription list: Stopping {vm.Id}"); + vm.PowerOff(); + Console.WriteLine($"In subscription list: Starting {vm.Id}"); + vm.PowerOn(); + } + } + + var resourceGroup = new AzureResourceManagerClient().GetResourceGroupOperations(Context.SubscriptionId, Context.RgName); + + resourceGroup.GetVirtualMachineContainer().List().Select(vm => + { + var parts = vm.Id.Name.Split('-'); + var n = Convert.ToInt32(parts[0].Last()); + return (vm, n); + }) + .Where(tuple => tuple.n % 2 == 0) + .ToList() + .ForEach(tuple => + { + Console.WriteLine($"In resource group list: Stopping {tuple.vm.Id.Name}"); + tuple.vm.PowerOff(); + Console.WriteLine($"In resource group list: Starting {tuple.vm.Id.Name}"); + tuple.vm.PowerOn(); + }); + + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByName.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByName.cs new file mode 100644 index 0000000000000..e86d36e0837e5 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByName.cs @@ -0,0 +1,28 @@ +using Proto.Compute; +using Azure.ResourceManager.Core; +using System; + +namespace Proto.Client +{ + class ShutdownVmsByName: Scenario + { + public override void Execute() + { + var createMultipleVms = new CreateMultipleVms(Context); + createMultipleVms.Execute(); + + var sub = new AzureResourceManagerClient().GetSubscriptionOperations(Context.SubscriptionId); + + foreach(var armResource in sub.ListVirtualMachinesByName("-e")) + { + var vmOperations = VirtualMachineOperations.FromGeneric(armResource); + Console.WriteLine($"Stopping {armResource.Id.ResourceGroup} : {armResource.Id.Name}"); + vmOperations.PowerOff(); + Console.WriteLine($"Starting {armResource.Id.ResourceGroup} : {armResource.Id.Name}"); + vmOperations.PowerOn(); + } + + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByNameAcrossResourceGroups.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByNameAcrossResourceGroups.cs new file mode 100644 index 0000000000000..d9842ab8bdb8c --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByNameAcrossResourceGroups.cs @@ -0,0 +1,38 @@ +using Proto.Compute; +using Azure.ResourceManager.Core; +using System; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Proto.Client +{ + class ShutdownVmsByNameAcrossResourceGroups : Scenario + { + public override void Execute() + { + int numberOfRgs = 2; + var context = Context; + for (int i = 0; i < numberOfRgs; i++) + { + var createMultipleVms = new CreateMultipleVms(context); + createMultipleVms.Execute(); + context = new ScenarioContext(); + } + + var subscription = new AzureResourceManagerClient().GetSubscriptionOperations(Context.SubscriptionId); + + Regex reg = new Regex($"{Context.VmName}.*-e"); + Parallel.ForEach(subscription.ListVirtualMachines(), vm => + { + if (reg.IsMatch(vm.Id.Name)) + { + Console.WriteLine($"Stopping {vm.Id.ResourceGroup} {vm.Id.Name}"); + vm.PowerOff(); + Console.WriteLine($"Starting {vm.Id.ResourceGroup} {vm.Id.Name}"); + vm.PowerOn(); + } + }); + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByNameAcrossSubscriptions.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByNameAcrossSubscriptions.cs new file mode 100644 index 0000000000000..263ccdaa88154 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByNameAcrossSubscriptions.cs @@ -0,0 +1,57 @@ +using Azure.ResourceManager.Compute; +using Proto.Compute; +using Azure.ResourceManager.Core; +using System; +using System.Threading.Tasks; + +namespace Proto.Client +{ + class ShutdownVmsByNameAcrossSubscriptions : Scenario + { + public async void ShutdownAsync() + { + var client = new AzureResourceManagerClient(); + + await foreach (var subscription in client.GetSubscriptionContainer().ListAsync()) + { + await foreach (var armResource in subscription.ListVirtualMachinesByNameAsync("-e")) + { + var vmOperations = VirtualMachineOperations.FromGeneric(armResource); + await vmOperations.PowerOffAsync(); + await vmOperations.PowerOnAsync(); + } + } + } + + public async override void Execute() + { + #region SETUP + ScenarioContext[] contexts = new ScenarioContext[] { new ScenarioContext(), new ScenarioContext("c9cbd920-c00c-427c-852b-8aaf38badaeb") }; + ParallelOptions options = new ParallelOptions + { + MaxDegreeOfParallelism = 1 + }; + + Parallel.ForEach(contexts, options, context => + { + var createMultipleVms = new CreateMultipleVms(context); + createMultipleVms.Execute(); + }); + #endregion + + + var client = new AzureResourceManagerClient(); + foreach (var sub in client.GetSubscriptionContainer().List()) + { + await foreach (var armResource in sub.ListVirtualMachinesByNameAsync("-e")) + { + var vmOperations = VirtualMachineOperations.FromGeneric(armResource); + Console.WriteLine($"Stopping {vmOperations.Id.Subscription} {vmOperations.Id.ResourceGroup} {vmOperations.Id.Name}"); + vmOperations.PowerOff(); + Console.WriteLine($"Starting {vmOperations.Id.Subscription} {vmOperations.Id.ResourceGroup} {vmOperations.Id.Name}"); + vmOperations.PowerOn(); + } + } + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByTag.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByTag.cs new file mode 100644 index 0000000000000..b62af442f7102 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/ShutdownVmsByTag.cs @@ -0,0 +1,44 @@ +using Proto.Compute; +using Azure.ResourceManager.Core; +using System; +using System.Linq; + +namespace Proto.Client +{ + class ShutdownVmsByTag : Scenario + { + public override void Execute() + { + var createMultipleVms = new CreateMultipleVms(Context); + createMultipleVms.Execute(); + + var rg = new AzureResourceManagerClient().GetResourceGroupOperations(Context.SubscriptionId, Context.RgName).Get().Value; + + //set tags on random vms + Random rand = new Random(Environment.TickCount); + foreach (var generic in rg.GetVirtualMachineContainer().ListByName(Environment.UserName)) + { + var vm = VirtualMachineOperations.FromGeneric(generic); + if (rand.NextDouble() > 0.5) + { + Console.WriteLine("adding tag to {0}", vm.Id.Name); + vm.StartAddTag("tagkey", "tagvalue"); + } + } + + var filteredList = rg.GetVirtualMachineContainer().List().Where(vm => + { + string value; + return (vm.Data.Tags.TryGetValue("tagkey", out value) && value == "tagvalue"); + }); + + foreach (var vm in filteredList) + { + Console.WriteLine("--------Stopping VM {0}--------", vm.Id.Name); + vm.PowerOff(); + Console.WriteLine("--------Starting VM {0}--------", vm.Id.Name); + vm.PowerOn(); + } + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/StartCreateSingleVmExample.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/StartCreateSingleVmExample.cs new file mode 100644 index 0000000000000..69c7507675c19 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/StartCreateSingleVmExample.cs @@ -0,0 +1,58 @@ +using Azure.ResourceManager.Core; +using Proto.Compute; +using Proto.Network; +using System; + +namespace Proto.Client +{ + class StartCreateSingleVmExample : Scenario + { + public StartCreateSingleVmExample() : base() { } + + public StartCreateSingleVmExample(ScenarioContext context) : base(context) { } + + public override void Execute() + { + ExcuteAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + + private async System.Threading.Tasks.Task ExcuteAsync() + { + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + + // Create Resource Group + Console.WriteLine($"--------Start StartCreate group {Context.RgName}--------"); + var resourceGroup = (await(subscription.GetResourceGroupContainer().Construct(Context.Loc).StartCreateOrUpdate(Context.RgName)).WaitForCompletionAsync()).Value; + CleanUp.Add(resourceGroup.Id); + + // Create AvailabilitySet + Console.WriteLine("--------Start StartCreate AvailabilitySet async--------"); + var aset = (await(resourceGroup.GetAvailabilitySetContainer().Construct("Aligned").StartCreateOrUpdate(Context.VmName + "_aSet")).WaitForCompletionAsync()).Value; + + // Create VNet + Console.WriteLine("--------Start StartCreate VNet--------"); + string vnetName = Context.VmName + "_vnet"; + var vnet = (await(resourceGroup.GetVirtualNetworkContainer().Construct("10.0.0.0/16").StartCreateOrUpdate(vnetName)).WaitForCompletionAsync()).Value; + + //create subnet + Console.WriteLine("--------Start StartCreate Subnet--------"); + var subnet = (await(vnet.GetSubnetContainer().Construct("10.0.0.0/24").StartCreateOrUpdate(Context.SubnetName)).WaitForCompletionAsync()).Value; + + //create network security group + Console.WriteLine("--------Start StartCreate NetworkSecurityGroup--------"); + _ = (await(resourceGroup.GetNetworkSecurityGroupContainer().Construct(80).StartCreateOrUpdate(Context.NsgName)).WaitForCompletionAsync()).Value; + + // Create Network Interface + Console.WriteLine("--------Start StartCreate Network Interface--------"); + var nic = (await(resourceGroup.GetNetworkInterfaceContainer().Construct(subnet.Id).StartCreateOrUpdate($"{Context.VmName}_nic")).WaitForCompletionAsync()).Value; + + // Create VM + Console.WriteLine("--------Start StartCreate VM --------"); + var vm = (await(resourceGroup.GetVirtualMachineContainer().Construct(Context.Hostname, "admin-user", "!@#$%asdfA", nic.Id, aset.Id).StartCreateOrUpdate(Context.VmName)).WaitForCompletionAsync()).Value; + + Console.WriteLine("VM ID: " + vm.Id); + Console.WriteLine("--------Done StartCreate VM--------"); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/StartCreateSingleVmExampleAsync.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/StartCreateSingleVmExampleAsync.cs new file mode 100644 index 0000000000000..86937eae74768 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/StartCreateSingleVmExampleAsync.cs @@ -0,0 +1,57 @@ +using Azure.ResourceManager.Core; +using Proto.Compute; +using Proto.Network; +using System; + +namespace Proto.Client +{ + class StartCreateSingleVmExampleAsync : Scenario + { + public StartCreateSingleVmExampleAsync() : base() { } + + public StartCreateSingleVmExampleAsync(ScenarioContext context) : base(context) { } + + public override void Execute() + { + ExecuteAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + private async System.Threading.Tasks.Task ExecuteAsync() + { + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + + // Create Resource Group + Console.WriteLine($"--------Start StartCreate group async {Context.RgName}--------"); + var resourceGroup = (await (await subscription.GetResourceGroupContainer().Construct(Context.Loc).StartCreateOrUpdateAsync(Context.RgName)).WaitForCompletionAsync()).Value; + CleanUp.Add(resourceGroup.Id); + + // Create AvailabilitySet + Console.WriteLine("--------Start StartCreate AvailabilitySet async--------"); + var aset = (await (await resourceGroup.GetAvailabilitySetContainer().Construct("Aligned").StartCreateOrUpdateAsync(Context.VmName + "_aSet")).WaitForCompletionAsync()).Value; + + // Create VNet + Console.WriteLine("--------Start StartCreate VNet async--------"); + string vnetName = Context.VmName + "_vnet"; + var vnet = (await (await resourceGroup.GetVirtualNetworkContainer().Construct("10.0.0.0/16").StartCreateOrUpdateAsync(vnetName)).WaitForCompletionAsync()).Value; + + //create subnet + Console.WriteLine("--------Start StartCreate Subnet async--------"); + var subnet = (await (await vnet.GetSubnetContainer().Construct("10.0.0.0/24").StartCreateOrUpdateAsync(Context.SubnetName)).WaitForCompletionAsync()).Value; + + //create network security group + Console.WriteLine("--------Start StartCreate NetworkSecurityGroup async--------"); + _ = (await (await resourceGroup.GetNetworkSecurityGroupContainer().Construct(80).StartCreateOrUpdateAsync(Context.NsgName)).WaitForCompletionAsync()).Value; + + // Create Network Interface + Console.WriteLine("--------Start StartCreate Network Interface async--------"); + var nic = (await (await resourceGroup.GetNetworkInterfaceContainer().Construct(subnet.Id).StartCreateOrUpdateAsync($"{Context.VmName}_nic")).WaitForCompletionAsync()).Value; + + // Create VM + Console.WriteLine("--------Start StartCreate VM async--------"); + var vm = (await (await resourceGroup.GetVirtualMachineContainer().Construct(Context.Hostname, "admin-user", "!@#$%asdfA", nic.Id, aset.Id).StartCreateOrUpdateAsync(Context.VmName)).WaitForCompletionAsync()).Value; + + Console.WriteLine("VM ID: " + vm.Id); + Console.WriteLine("--------Done StartCreate VM--------"); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/StartFromVm.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/StartFromVm.cs new file mode 100644 index 0000000000000..f8aa2100cf50c --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/StartFromVm.cs @@ -0,0 +1,26 @@ +using Proto.Compute; +using Azure.ResourceManager.Core; +using System; + +namespace Proto.Client +{ + class StartFromVm : Scenario + { + public override void Execute() + { + var createVm = new CreateSingleVmExample(Context); + createVm.Execute(); + var client = new AzureResourceManagerClient(); + + //retrieve from lowest level, doesn't give ability to walk up and down the container structure + var vmOp = client.GetResourceOperations(Context.SubscriptionId, Context.RgName, Context.VmName); + var vm = vmOp.Get().Value.Data; + Console.WriteLine($"Found VM {vm.Id}"); + + //retrieve from lowest level inside management package gives ability to walk up and down + var rg = client.GetResourceGroupOperations(Context.SubscriptionId, Context.RgName); + var vm2 = rg.GetVirtualMachineOperations(Context.VmName).Get().Value.Data; + Console.WriteLine($"Found VM {vm2.Id}"); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/StartStopVm.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/StartStopVm.cs new file mode 100644 index 0000000000000..4f6dd70d71825 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/StartStopVm.cs @@ -0,0 +1,25 @@ +using Proto.Compute; +using Azure.ResourceManager.Core; +using System; + +namespace Proto.Client +{ + class StartStopVm : Scenario + { + public override void Execute() + { + var createVm = new CreateSingleVmExample(Context); + createVm.Execute(); + + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + var resourceGroup = subscription.GetResourceGroupOperations(Context.RgName); + var vm = resourceGroup.GetVirtualMachineOperations(Context.VmName); + Console.WriteLine($"Found VM {Context.VmName}"); + Console.WriteLine("--------Stopping VM--------"); + vm.PowerOff(); + Console.WriteLine("--------Starting VM--------"); + vm.PowerOn(); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/SubscriptionExists.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/SubscriptionExists.cs new file mode 100644 index 0000000000000..41ab8df56a553 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/SubscriptionExists.cs @@ -0,0 +1,21 @@ +using Azure.ResourceManager.Core; +using System; + +namespace Proto.Client +{ + class SubscriptionExists : Scenario + { + public override void Execute() + { + var client = new AzureResourceManagerClient(); + if(client.GetSubscriptionContainer().DoesExist(Context.SubscriptionId)) + { + Console.WriteLine($"Found {Context.SubscriptionId}"); + } + else + { + throw new Exception($"Did not find {Context.SubscriptionId}"); + } + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/UseParentLocation.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/UseParentLocation.cs new file mode 100644 index 0000000000000..636586e3a67a3 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/UseParentLocation.cs @@ -0,0 +1,60 @@ +using Azure.ResourceManager.Core; +using Proto.Compute; +using Proto.Network; +using System; + +namespace Proto.Client +{ + class UseParentLocation : Scenario + { + public override void Execute() + { + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + + // Create Resource Group + Console.WriteLine($"--------Start create group {Context.RgName}--------"); + var resourceGroup = subscription.GetResourceGroupContainer().Construct(Context.Loc).CreateOrUpdate(Context.RgName).Value; + CleanUp.Add(resourceGroup.Id); + + // Create AvailabilitySet + Console.WriteLine("--------Start create AvailabilitySet--------"); + var aset = resourceGroup.GetAvailabilitySetContainer().Construct("Aligned").CreateOrUpdate(Context.VmName + "_aSet").Value; + if (aset.Data.Location != Context.Loc) + throw new Exception($"aset was on the wrong location expected {Context.Loc} actual {aset.Data.Location}"); + + // Create VNet + Console.WriteLine("--------Start create VNet--------"); + string vnetName = Context.VmName + "_vnet"; + var vnet = resourceGroup.GetVirtualNetworkContainer().Construct("10.0.0.0/16").CreateOrUpdate(vnetName).Value; + if (vnet.Data.Location != Context.Loc) + throw new Exception($"vnet was on the wrong location expected {Context.Loc} actual {vnet.Data.Location}"); + + //create subnet + Console.WriteLine("--------Start create Subnet async--------"); + var subnet = vnet.GetSubnetContainer().Construct("10.0.0.0/24").CreateOrUpdateAsync(Context.SubnetName).ConfigureAwait(false).GetAwaiter().GetResult().Value; + + //create network security group + Console.WriteLine("--------Start create NetworkSecurityGroup--------"); + var nsg = resourceGroup.GetNetworkSecurityGroupContainer().Construct(80).CreateOrUpdate(Context.NsgName).Value; + if (nsg.Data.Location != Context.Loc) + throw new Exception($"nsg was on the wrong location expected {Context.Loc} actual {nsg.Data.Location}"); + + // Create Network Interface + Console.WriteLine("--------Start create Network Interface--------"); + var nic = resourceGroup.GetNetworkInterfaceContainer().Construct(subnet.Id).CreateOrUpdate($"{Context.VmName}_nic").Value; + if (nic.Data.Location != Context.Loc) + throw new Exception($"nic was on the wrong location expected {Context.Loc} actual {nic.Data.Location}"); + + // Create VM + Console.WriteLine("--------Start create VM--------"); + var vm = resourceGroup.GetVirtualMachineContainer().Construct(Context.Hostname, "admin-user", "!@#$%asdfA", nic.Id, aset.Id).CreateOrUpdate(Context.VmName).Value; + if (vm.Data.Location != Context.Loc) + throw new Exception($"vm was on the wrong location expected {Context.Loc} actual {vm.Data.Location}"); + + Console.WriteLine("VM ID: " + vm.Id); + Console.WriteLine("--------Done create VM--------"); + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/VmModelBuilder.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/VmModelBuilder.cs new file mode 100644 index 0000000000000..2f71a2cda5f21 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/VmModelBuilder.cs @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading.Tasks; +using Proto.Compute; +using Azure.ResourceManager.Core; +using Proto.Network; +using Proto.Compute.Convenience; + +namespace Proto.Client +{ + class VmModelBuilder : Scenario + { + public override void Execute() + { + throw new NotImplementedException(); + } + + private Task CreateVmWithBuilderAsync() + { + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + + // Create Resource Group + Console.WriteLine($"--------Start create group {Context.RgName}--------"); + var resourceGroup = subscription.GetResourceGroupContainer().Construct(Context.Loc).CreateOrUpdate(Context.RgName).Value; + + // Create AvailabilitySet + Console.WriteLine("--------Start create AvailabilitySet--------"); + var aset = resourceGroup.GetAvailabilitySetContainer().Construct("Aligned").CreateOrUpdate(Context.VmName + "_aSet").Value; + + // Create VNet + Console.WriteLine("--------Start create VNet--------"); + string vnetName = Context.VmName + "_vnet"; + var vnet = resourceGroup.GetVirtualNetworkContainer().Construct("10.0.0.0/16").CreateOrUpdate(vnetName).Value; + + //create subnet + Console.WriteLine("--------Start create Subnet--------"); + var nsg = resourceGroup.GetNetworkSecurityGroupContainer().Construct(80).CreateOrUpdate(Context.NsgName).Value; + var subnet = vnet.GetSubnetContainer().Construct("10.0.0.0/24").CreateOrUpdate(Context.SubnetName).Value; + + // Create Network Interface + Console.WriteLine("--------Start create Network Interface--------"); + var nic = resourceGroup.GetNetworkInterfaceContainer().Construct(subnet.Id).CreateOrUpdate($"{Context.VmName}_nic").Value; + + // Options: required parameters on in the constructor + VirtualMachineModelBuilder vmBuilder = new VirtualMachineModelBuilder( + resourceGroup.GetVirtualMachineContainer(), + new VirtualMachineData(new Azure.ResourceManager.Compute.Models.VirtualMachine(Context.Loc))); ; + var vm = vmBuilder.UseWindowsImage("admin-user", "!@#$%asdfA") + .RequiredNetworkInterface(nic.Id) + .RequiredAvalabilitySet(aset.Id) + .CreateOrUpdate(Context.VmName).Value; + + return Task.FromResult(vm); + } + } +} From 002848c2102edcd987467e550d23ca592ebe1d6e Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Wed, 24 Feb 2021 14:42:45 -0800 Subject: [PATCH 07/18] Add missing readmes for proto client --- sdk/resourcemanager/Proto.Client/README.MD | 3 +++ sdk/resourcemanager/Proto.Client/authorization/README.MD | 3 +++ sdk/resourcemanager/Proto.Client/compute/README.MD | 3 +++ sdk/resourcemanager/Proto.Client/network/README.MD | 3 +++ 4 files changed, 12 insertions(+) create mode 100644 sdk/resourcemanager/Proto.Client/README.MD create mode 100644 sdk/resourcemanager/Proto.Client/authorization/README.MD create mode 100644 sdk/resourcemanager/Proto.Client/compute/README.MD create mode 100644 sdk/resourcemanager/Proto.Client/network/README.MD diff --git a/sdk/resourcemanager/Proto.Client/README.MD b/sdk/resourcemanager/Proto.Client/README.MD new file mode 100644 index 0000000000000..efeef3da2460c --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/README.MD @@ -0,0 +1,3 @@ +# Proto Client + +Client used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file diff --git a/sdk/resourcemanager/Proto.Client/authorization/README.MD b/sdk/resourcemanager/Proto.Client/authorization/README.MD new file mode 100644 index 0000000000000..28c108283dd3d --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/README.MD @@ -0,0 +1,3 @@ +# Proto Authorization + +Proto type version of Azure.ResourceManager.Authorization used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file diff --git a/sdk/resourcemanager/Proto.Client/compute/README.MD b/sdk/resourcemanager/Proto.Client/compute/README.MD new file mode 100644 index 0000000000000..96824ba442876 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/compute/README.MD @@ -0,0 +1,3 @@ +# Proto Compute + +Proto type version of Azure.ResourceManager.Compute used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file diff --git a/sdk/resourcemanager/Proto.Client/network/README.MD b/sdk/resourcemanager/Proto.Client/network/README.MD new file mode 100644 index 0000000000000..3a3688b6e19b1 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/network/README.MD @@ -0,0 +1,3 @@ +# Proto Network + +Proto type version of Azure.ResourceManager.Network used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file From fc17340c335d2140078c8b0e990e1aa18df7dcb8 Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Thu, 25 Feb 2021 11:20:48 -0800 Subject: [PATCH 08/18] update to require linq 4.0.0 so that don't have to upgrade bcl --- eng/Packages.Data.props | 8 ++++---- .../src/Azure.ResourceManager.Core.csproj | 1 - sdk/resourcemanager/Proto.Client/src/Program.cs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/eng/Packages.Data.props b/eng/Packages.Data.props index c439dc541a144..5ff1b78487788 100644 --- a/eng/Packages.Data.props +++ b/eng/Packages.Data.props @@ -64,16 +64,16 @@ - + - + - - + + diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj index 364c9d64f576c..21b9bf649727b 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj @@ -16,7 +16,6 @@ - diff --git a/sdk/resourcemanager/Proto.Client/src/Program.cs b/sdk/resourcemanager/Proto.Client/src/Program.cs index 1e4f6ee5de7a0..cd40dc1c7bf1e 100644 --- a/sdk/resourcemanager/Proto.Client/src/Program.cs +++ b/sdk/resourcemanager/Proto.Client/src/Program.cs @@ -10,7 +10,7 @@ static void Main(string[] args) Scenario scenario = null; try { - scenario = ScenarioFactory.GetScenario(Scenarios.CreateSingleVmExample); + scenario = ScenarioFactory.GetScenario(Scenarios.CreateSingleVMCheckLocation); scenario.Execute(); } finally From 477b1241de16100ce2bb66e8b9a285fdbbdf0ae0 Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Thu, 25 Feb 2021 13:19:55 -0800 Subject: [PATCH 09/18] add changlog for CI --- sdk/resourcemanager/Azure.ResourceManager.Core/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/CHANGELOG.md diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/CHANGELOG.md b/sdk/resourcemanager/Azure.ResourceManager.Core/CHANGELOG.md new file mode 100644 index 0000000000000..5c312e4ea9bea --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/CHANGELOG.md @@ -0,0 +1,5 @@ +# Release History + +## 1.0.0-beta.1 (Unreleased) + +-Initial checkin From 69b81e66864c6a5dc3936a90b7d6a01f41b61b1a Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Thu, 25 Feb 2021 15:14:35 -0800 Subject: [PATCH 10/18] change system.linq.async to update --- eng/Packages.Data.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Packages.Data.props b/eng/Packages.Data.props index 5ff1b78487788..bce2bd466898c 100644 --- a/eng/Packages.Data.props +++ b/eng/Packages.Data.props @@ -72,7 +72,7 @@ - + From 47d537c35c19dc4f1a44fc753d708e1138fed473 Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Thu, 25 Feb 2021 16:01:24 -0800 Subject: [PATCH 11/18] update to use source vs binary for the temporary generation of Azure.ResourceManager.Authorization --- .../src/Azure.ResourceManager.Core.csproj | 1 + .../Proto.Client/Proto-Client.sln | 6 + ...Azure.ResourceManager.Authorization.csproj | 13 + .../Directory.Build.props | 6 + .../AuthorizationManagementClient.cs | 59 + .../AuthorizationManagementClientOptions.cs | 16 + .../ErrorAdditionalInfo.Serialization.cs | 40 + .../Generated/Models/ErrorAdditionalInfo.cs | 32 + .../Models/ErrorDetail.Serialization.cs | 74 + .../Generated/Models/ErrorDetail.cs | 49 + .../Models/ErrorResponse.Serialization.cs | 34 + .../Generated/Models/ErrorResponse.cs | 28 + .../Generated/Models/PrincipalType.cs | 75 + .../Models/RoleAssignment.Serialization.cs | 152 ++ .../Generated/Models/RoleAssignment.cs | 90 + ...ssignmentCreateParameters.Serialization.cs | 53 + .../Models/RoleAssignmentCreateParameters.cs | 49 + .../Generated/Models/RoleAssignmentFilter.cs | 23 + .../RoleAssignmentListResult.Serialization.cs | 46 + .../Models/RoleAssignmentListResult.cs | 36 + .../Generated/RoleAssignmentsOperations.cs | 653 ++++++ .../RoleAssignmentsRestOperations.cs | 1227 +++++++++++ .../Properties/AssemblyInfo.cs | 8 + .../README.MD | 3 + .../autorest.md | 11 + .../authorization/Proto.Authorization.csproj | 14 +- ...re.ResourceManager.Authorization.deps.json | 347 ---- .../Azure.ResourceManager.Authorization.dll | Bin 254976 -> 0 bytes .../Azure.ResourceManager.Authorization.xml | 1804 ----------------- 29 files changed, 2791 insertions(+), 2158 deletions(-) create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Azure.ResourceManager.Authorization.csproj create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Directory.Build.props create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/AuthorizationManagementClient.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/AuthorizationManagementClientOptions.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorAdditionalInfo.Serialization.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorAdditionalInfo.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorDetail.Serialization.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorDetail.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorResponse.Serialization.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorResponse.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/PrincipalType.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignment.Serialization.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignment.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentCreateParameters.Serialization.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentCreateParameters.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentFilter.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentListResult.Serialization.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentListResult.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/RoleAssignmentsOperations.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/RoleAssignmentsRestOperations.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Properties/AssemblyInfo.cs create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/README.MD create mode 100644 sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/autorest.md delete mode 100644 sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.deps.json delete mode 100644 sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.dll delete mode 100644 sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.xml diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj index 21b9bf649727b..364c9d64f576c 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Azure.ResourceManager.Core.csproj @@ -16,6 +16,7 @@ + diff --git a/sdk/resourcemanager/Proto.Client/Proto-Client.sln b/sdk/resourcemanager/Proto.Client/Proto-Client.sln index fab7da5acb646..0d78ec9861053 100644 --- a/sdk/resourcemanager/Proto.Client/Proto-Client.sln +++ b/sdk/resourcemanager/Proto.Client/Proto-Client.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Proto.Client", "src\Proto.C EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Proto.Authorization", "authorization\Proto.Authorization.csproj", "{540E8EF4-40B6-4F23-8744-2E6705186E7B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.ResourceManager.Authorization", "authorization\Azure.ResourceManager.Authorization\Azure.ResourceManager.Authorization.csproj", "{ADB32B3D-D6DE-49A3-80E1-59ADAA21F78F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {540E8EF4-40B6-4F23-8744-2E6705186E7B}.Debug|Any CPU.Build.0 = Debug|Any CPU {540E8EF4-40B6-4F23-8744-2E6705186E7B}.Release|Any CPU.ActiveCfg = Release|Any CPU {540E8EF4-40B6-4F23-8744-2E6705186E7B}.Release|Any CPU.Build.0 = Release|Any CPU + {ADB32B3D-D6DE-49A3-80E1-59ADAA21F78F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ADB32B3D-D6DE-49A3-80E1-59ADAA21F78F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ADB32B3D-D6DE-49A3-80E1-59ADAA21F78F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ADB32B3D-D6DE-49A3-80E1-59ADAA21F78F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Azure.ResourceManager.Authorization.csproj b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Azure.ResourceManager.Authorization.csproj new file mode 100644 index 0000000000000..d7a65183fe972 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Azure.ResourceManager.Authorization.csproj @@ -0,0 +1,13 @@ + + + 1.0.0-preview.1 + Azure.ResourceManager.Authorization + Azure Resource Manager client SDK for Azure resource provider Microsoft.Authorization + azure;management;arm;resource manager;authorization + + + + + $(NoWarn);AZC0001 + + diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Directory.Build.props b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Directory.Build.props new file mode 100644 index 0000000000000..1a9611bd49242 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Directory.Build.props @@ -0,0 +1,6 @@ + + + + diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/AuthorizationManagementClient.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/AuthorizationManagementClient.cs new file mode 100644 index 0000000000000..0332b8b67e2b0 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/AuthorizationManagementClient.cs @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using Azure.Core; +using Azure.Core.Pipeline; +using Azure.ResourceManager.Authorization; + +namespace Azure.ResourceManager.Authorization +{ + /// Authorization service management client. + public partial class AuthorizationManagementClient + { + private readonly ClientDiagnostics _clientDiagnostics; + private readonly HttpPipeline _pipeline; + private readonly string _subscriptionId; + private readonly Uri _endpoint; + + /// Initializes a new instance of AuthorizationManagementClient for mocking. + protected AuthorizationManagementClient() + { + } + + /// Initializes a new instance of AuthorizationManagementClient. + /// The ID of the target subscription. + /// The OAuth token for making client requests. + /// The options for configuring the client. + public AuthorizationManagementClient(string subscriptionId, TokenCredential tokenCredential, AuthorizationManagementClientOptions options = null) : this(subscriptionId, null, tokenCredential, options) + { + } + /// Initializes a new instance of AuthorizationManagementClient. + /// The ID of the target subscription. + /// server parameter. + /// The OAuth token for making client requests. + /// The options for configuring the client. + /// is null. + public AuthorizationManagementClient(string subscriptionId, Uri endpoint, TokenCredential tokenCredential, AuthorizationManagementClientOptions options = null) + { + if (subscriptionId == null) + { + throw new ArgumentNullException(nameof(subscriptionId)); + } + endpoint ??= new Uri("https://management.azure.com"); + + options ??= new AuthorizationManagementClientOptions(); + _clientDiagnostics = new ClientDiagnostics(options); + _pipeline = ManagementPipelineBuilder.Build(tokenCredential, endpoint, options); + _subscriptionId = subscriptionId; + _endpoint = endpoint; + } + + /// Returns an instance of RoleAssignmentsOperations. + public virtual RoleAssignmentsOperations RoleAssignments => new RoleAssignmentsOperations(_clientDiagnostics, _pipeline, _subscriptionId, _endpoint); + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/AuthorizationManagementClientOptions.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/AuthorizationManagementClientOptions.cs new file mode 100644 index 0000000000000..890946048a0b9 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/AuthorizationManagementClientOptions.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using Azure.Core; + +namespace Azure.ResourceManager.Authorization +{ + /// Client options for Authorization. + public partial class AuthorizationManagementClientOptions : ClientOptions + { + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorAdditionalInfo.Serialization.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorAdditionalInfo.Serialization.cs new file mode 100644 index 0000000000000..2186871caceca --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorAdditionalInfo.Serialization.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Text.Json; +using Azure.Core; + +namespace Azure.ResourceManager.Authorization.Models +{ + internal partial class ErrorAdditionalInfo + { + internal static ErrorAdditionalInfo DeserializeErrorAdditionalInfo(JsonElement element) + { + Optional type = default; + Optional info = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("type")) + { + type = property.Value.GetString(); + continue; + } + if (property.NameEquals("info")) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + property.ThrowNonNullablePropertyIsNull(); + continue; + } + info = property.Value.GetObject(); + continue; + } + } + return new ErrorAdditionalInfo(type.Value, info.Value); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorAdditionalInfo.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorAdditionalInfo.cs new file mode 100644 index 0000000000000..0dd0396180f6d --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorAdditionalInfo.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +namespace Azure.ResourceManager.Authorization.Models +{ + /// The resource management error additional info. + internal partial class ErrorAdditionalInfo + { + /// Initializes a new instance of ErrorAdditionalInfo. + internal ErrorAdditionalInfo() + { + } + + /// Initializes a new instance of ErrorAdditionalInfo. + /// The additional info type. + /// The additional info. + internal ErrorAdditionalInfo(string type, object info) + { + Type = type; + Info = info; + } + + /// The additional info type. + public string Type { get; } + /// The additional info. + public object Info { get; } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorDetail.Serialization.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorDetail.Serialization.cs new file mode 100644 index 0000000000000..536d7bed4ad88 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorDetail.Serialization.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Collections.Generic; +using System.Text.Json; +using Azure.Core; + +namespace Azure.ResourceManager.Authorization.Models +{ + internal partial class ErrorDetail + { + internal static ErrorDetail DeserializeErrorDetail(JsonElement element) + { + Optional code = default; + Optional message = default; + Optional target = default; + Optional> details = default; + Optional> additionalInfo = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("code")) + { + code = property.Value.GetString(); + continue; + } + if (property.NameEquals("message")) + { + message = property.Value.GetString(); + continue; + } + if (property.NameEquals("target")) + { + target = property.Value.GetString(); + continue; + } + if (property.NameEquals("details")) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + property.ThrowNonNullablePropertyIsNull(); + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(DeserializeErrorDetail(item)); + } + details = array; + continue; + } + if (property.NameEquals("additionalInfo")) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + property.ThrowNonNullablePropertyIsNull(); + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(ErrorAdditionalInfo.DeserializeErrorAdditionalInfo(item)); + } + additionalInfo = array; + continue; + } + } + return new ErrorDetail(code.Value, message.Value, target.Value, Optional.ToList(details), Optional.ToList(additionalInfo)); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorDetail.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorDetail.cs new file mode 100644 index 0000000000000..eba350afafc44 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorDetail.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Collections.Generic; +using Azure.Core; + +namespace Azure.ResourceManager.Authorization.Models +{ + /// The error detail. + internal partial class ErrorDetail + { + /// Initializes a new instance of ErrorDetail. + internal ErrorDetail() + { + Details = new ChangeTrackingList(); + AdditionalInfo = new ChangeTrackingList(); + } + + /// Initializes a new instance of ErrorDetail. + /// The error code. + /// The error message. + /// The error target. + /// The error details. + /// The error additional info. + internal ErrorDetail(string code, string message, string target, IReadOnlyList details, IReadOnlyList additionalInfo) + { + Code = code; + Message = message; + Target = target; + Details = details; + AdditionalInfo = additionalInfo; + } + + /// The error code. + public string Code { get; } + /// The error message. + public string Message { get; } + /// The error target. + public string Target { get; } + /// The error details. + public IReadOnlyList Details { get; } + /// The error additional info. + public IReadOnlyList AdditionalInfo { get; } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorResponse.Serialization.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorResponse.Serialization.cs new file mode 100644 index 0000000000000..9db192f0f694a --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorResponse.Serialization.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Text.Json; +using Azure.Core; + +namespace Azure.ResourceManager.Authorization.Models +{ + internal partial class ErrorResponse + { + internal static ErrorResponse DeserializeErrorResponse(JsonElement element) + { + Optional error = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("error")) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + property.ThrowNonNullablePropertyIsNull(); + continue; + } + error = ErrorDetail.DeserializeErrorDetail(property.Value); + continue; + } + } + return new ErrorResponse(error.Value); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorResponse.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorResponse.cs new file mode 100644 index 0000000000000..fbe1ef13920e1 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/ErrorResponse.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +namespace Azure.ResourceManager.Authorization.Models +{ + /// Common error response for all Azure Resource Manager APIs to return error details for failed operations. (This also follows the OData error response format.). + internal partial class ErrorResponse + { + /// Initializes a new instance of ErrorResponse. + internal ErrorResponse() + { + } + + /// Initializes a new instance of ErrorResponse. + /// The error object. + internal ErrorResponse(ErrorDetail error) + { + Error = error; + } + + /// The error object. + public ErrorDetail Error { get; } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/PrincipalType.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/PrincipalType.cs new file mode 100644 index 0000000000000..dd2733e32d45a --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/PrincipalType.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.ResourceManager.Authorization.Models +{ + /// The principal type of the assigned principal ID. + public readonly partial struct PrincipalType : IEquatable + { + private readonly string _value; + + /// Determines if two values are the same. + /// is null. + public PrincipalType(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string UserValue = "User"; + private const string GroupValue = "Group"; + private const string ServicePrincipalValue = "ServicePrincipal"; + private const string UnknownValue = "Unknown"; + private const string DirectoryRoleTemplateValue = "DirectoryRoleTemplate"; + private const string ForeignGroupValue = "ForeignGroup"; + private const string ApplicationValue = "Application"; + private const string MSIValue = "MSI"; + private const string DirectoryObjectOrGroupValue = "DirectoryObjectOrGroup"; + private const string EveryoneValue = "Everyone"; + + /// User. + public static PrincipalType User { get; } = new PrincipalType(UserValue); + /// Group. + public static PrincipalType Group { get; } = new PrincipalType(GroupValue); + /// ServicePrincipal. + public static PrincipalType ServicePrincipal { get; } = new PrincipalType(ServicePrincipalValue); + /// Unknown. + public static PrincipalType Unknown { get; } = new PrincipalType(UnknownValue); + /// DirectoryRoleTemplate. + public static PrincipalType DirectoryRoleTemplate { get; } = new PrincipalType(DirectoryRoleTemplateValue); + /// ForeignGroup. + public static PrincipalType ForeignGroup { get; } = new PrincipalType(ForeignGroupValue); + /// Application. + public static PrincipalType Application { get; } = new PrincipalType(ApplicationValue); + /// MSI. + public static PrincipalType MSI { get; } = new PrincipalType(MSIValue); + /// DirectoryObjectOrGroup. + public static PrincipalType DirectoryObjectOrGroup { get; } = new PrincipalType(DirectoryObjectOrGroupValue); + /// Everyone. + public static PrincipalType Everyone { get; } = new PrincipalType(EveryoneValue); + /// Determines if two values are the same. + public static bool operator ==(PrincipalType left, PrincipalType right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(PrincipalType left, PrincipalType right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator PrincipalType(string value) => new PrincipalType(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is PrincipalType other && Equals(other); + /// + public bool Equals(PrincipalType other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignment.Serialization.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignment.Serialization.cs new file mode 100644 index 0000000000000..ddb7e4809f65d --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignment.Serialization.cs @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using Azure.Core; + +namespace Azure.ResourceManager.Authorization.Models +{ + public partial class RoleAssignment + { + internal static RoleAssignment DeserializeRoleAssignment(JsonElement element) + { + Optional id = default; + Optional name = default; + Optional type = default; + Optional scope = default; + Optional roleDefinitionId = default; + Optional principalId = default; + Optional principalType = default; + Optional canDelegate = default; + Optional description = default; + Optional condition = default; + Optional conditionVersion = default; + Optional createdOn = default; + Optional updatedOn = default; + Optional createdBy = default; + Optional updatedBy = default; + Optional delegatedManagedIdentityResourceId = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("id")) + { + id = property.Value.GetString(); + continue; + } + if (property.NameEquals("name")) + { + name = property.Value.GetString(); + continue; + } + if (property.NameEquals("type")) + { + type = property.Value.GetString(); + continue; + } + if (property.NameEquals("properties")) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + property.ThrowNonNullablePropertyIsNull(); + continue; + } + foreach (var property0 in property.Value.EnumerateObject()) + { + if (property0.NameEquals("scope")) + { + scope = property0.Value.GetString(); + continue; + } + if (property0.NameEquals("roleDefinitionId")) + { + roleDefinitionId = property0.Value.GetString(); + continue; + } + if (property0.NameEquals("principalId")) + { + principalId = property0.Value.GetString(); + continue; + } + if (property0.NameEquals("principalType")) + { + if (property0.Value.ValueKind == JsonValueKind.Null) + { + property0.ThrowNonNullablePropertyIsNull(); + continue; + } + principalType = new PrincipalType(property0.Value.GetString()); + continue; + } + if (property0.NameEquals("canDelegate")) + { + if (property0.Value.ValueKind == JsonValueKind.Null) + { + property0.ThrowNonNullablePropertyIsNull(); + continue; + } + canDelegate = property0.Value.GetBoolean(); + continue; + } + if (property0.NameEquals("description")) + { + description = property0.Value.GetString(); + continue; + } + if (property0.NameEquals("condition")) + { + condition = property0.Value.GetString(); + continue; + } + if (property0.NameEquals("conditionVersion")) + { + conditionVersion = property0.Value.GetString(); + continue; + } + if (property0.NameEquals("createdOn")) + { + if (property0.Value.ValueKind == JsonValueKind.Null) + { + property0.ThrowNonNullablePropertyIsNull(); + continue; + } + createdOn = property0.Value.GetDateTimeOffset("O"); + continue; + } + if (property0.NameEquals("updatedOn")) + { + if (property0.Value.ValueKind == JsonValueKind.Null) + { + property0.ThrowNonNullablePropertyIsNull(); + continue; + } + updatedOn = property0.Value.GetDateTimeOffset("O"); + continue; + } + if (property0.NameEquals("createdBy")) + { + createdBy = property0.Value.GetString(); + continue; + } + if (property0.NameEquals("updatedBy")) + { + updatedBy = property0.Value.GetString(); + continue; + } + if (property0.NameEquals("delegatedManagedIdentityResourceId")) + { + delegatedManagedIdentityResourceId = property0.Value.GetString(); + continue; + } + } + continue; + } + } + return new RoleAssignment(id.Value, name.Value, type.Value, scope.Value, roleDefinitionId.Value, principalId.Value, Optional.ToNullable(principalType), Optional.ToNullable(canDelegate), description.Value, condition.Value, conditionVersion.Value, Optional.ToNullable(createdOn), Optional.ToNullable(updatedOn), createdBy.Value, updatedBy.Value, delegatedManagedIdentityResourceId.Value); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignment.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignment.cs new file mode 100644 index 0000000000000..dac8c754b295e --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignment.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; + +namespace Azure.ResourceManager.Authorization.Models +{ + /// Role Assignments. + public partial class RoleAssignment + { + /// Initializes a new instance of RoleAssignment. + internal RoleAssignment() + { + } + + /// Initializes a new instance of RoleAssignment. + /// The role assignment ID. + /// The role assignment name. + /// The role assignment type. + /// The role assignment scope. + /// The role definition ID. + /// The principal ID. + /// The principal type of the assigned principal ID. + /// The Delegation flag for the role assignment. + /// Description of role assignment. + /// The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase 'foo_storage_container'. + /// Version of the condition. Currently accepted value is '2.0'. + /// Time it was created. + /// Time it was updated. + /// Id of the user who created the assignment. + /// Id of the user who updated the assignment. + /// Id of the delegated managed identity resource. + internal RoleAssignment(string id, string name, string type, string scope, string roleDefinitionId, string principalId, PrincipalType? principalType, bool? canDelegate, string description, string condition, string conditionVersion, DateTimeOffset? createdOn, DateTimeOffset? updatedOn, string createdBy, string updatedBy, string delegatedManagedIdentityResourceId) + { + Id = id; + Name = name; + Type = type; + Scope = scope; + RoleDefinitionId = roleDefinitionId; + PrincipalId = principalId; + PrincipalType = principalType; + CanDelegate = canDelegate; + Description = description; + Condition = condition; + ConditionVersion = conditionVersion; + CreatedOn = createdOn; + UpdatedOn = updatedOn; + CreatedBy = createdBy; + UpdatedBy = updatedBy; + DelegatedManagedIdentityResourceId = delegatedManagedIdentityResourceId; + } + + /// The role assignment ID. + public string Id { get; } + /// The role assignment name. + public string Name { get; } + /// The role assignment type. + public string Type { get; } + /// The role assignment scope. + public string Scope { get; } + /// The role definition ID. + public string RoleDefinitionId { get; } + /// The principal ID. + public string PrincipalId { get; } + /// The principal type of the assigned principal ID. + public PrincipalType? PrincipalType { get; } + /// The Delegation flag for the role assignment. + public bool? CanDelegate { get; } + /// Description of role assignment. + public string Description { get; } + /// The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase 'foo_storage_container'. + public string Condition { get; } + /// Version of the condition. Currently accepted value is '2.0'. + public string ConditionVersion { get; } + /// Time it was created. + public DateTimeOffset? CreatedOn { get; } + /// Time it was updated. + public DateTimeOffset? UpdatedOn { get; } + /// Id of the user who created the assignment. + public string CreatedBy { get; } + /// Id of the user who updated the assignment. + public string UpdatedBy { get; } + /// Id of the delegated managed identity resource. + public string DelegatedManagedIdentityResourceId { get; } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentCreateParameters.Serialization.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentCreateParameters.Serialization.cs new file mode 100644 index 0000000000000..f7fc590e0ab0e --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentCreateParameters.Serialization.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Text.Json; +using Azure.Core; + +namespace Azure.ResourceManager.Authorization.Models +{ + public partial class RoleAssignmentCreateParameters : IUtf8JsonSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + writer.WritePropertyName("properties"); + writer.WriteStartObject(); + writer.WritePropertyName("roleDefinitionId"); + writer.WriteStringValue(RoleDefinitionId); + writer.WritePropertyName("principalId"); + writer.WriteStringValue(PrincipalId); + if (Optional.IsDefined(PrincipalType)) + { + writer.WritePropertyName("principalType"); + writer.WriteStringValue(PrincipalType.Value.ToString()); + } + if (Optional.IsDefined(CanDelegate)) + { + writer.WritePropertyName("canDelegate"); + writer.WriteBooleanValue(CanDelegate.Value); + } + if (Optional.IsDefined(Description)) + { + writer.WritePropertyName("description"); + writer.WriteStringValue(Description); + } + if (Optional.IsDefined(Condition)) + { + writer.WritePropertyName("condition"); + writer.WriteStringValue(Condition); + } + if (Optional.IsDefined(ConditionVersion)) + { + writer.WritePropertyName("conditionVersion"); + writer.WriteStringValue(ConditionVersion); + } + writer.WriteEndObject(); + writer.WriteEndObject(); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentCreateParameters.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentCreateParameters.cs new file mode 100644 index 0000000000000..265ddba80ff52 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentCreateParameters.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; + +namespace Azure.ResourceManager.Authorization.Models +{ + /// Role assignment create parameters. + public partial class RoleAssignmentCreateParameters + { + /// Initializes a new instance of RoleAssignmentCreateParameters. + /// The role definition ID used in the role assignment. + /// The principal ID assigned to the role. This maps to the ID inside the Active Directory. It can point to a user, service principal, or security group. + /// or is null. + public RoleAssignmentCreateParameters(string roleDefinitionId, string principalId) + { + if (roleDefinitionId == null) + { + throw new ArgumentNullException(nameof(roleDefinitionId)); + } + if (principalId == null) + { + throw new ArgumentNullException(nameof(principalId)); + } + + RoleDefinitionId = roleDefinitionId; + PrincipalId = principalId; + } + + /// The role definition ID used in the role assignment. + public string RoleDefinitionId { get; } + /// The principal ID assigned to the role. This maps to the ID inside the Active Directory. It can point to a user, service principal, or security group. + public string PrincipalId { get; } + /// The principal type of the assigned principal ID. + public PrincipalType? PrincipalType { get; set; } + /// The delegation flag used for creating a role assignment. + public bool? CanDelegate { get; set; } + /// Description of role assignment. + public string Description { get; set; } + /// The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase 'foo_storage_container'. + public string Condition { get; set; } + /// Version of the condition. Currently accepted value is '2.0'. + public string ConditionVersion { get; set; } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentFilter.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentFilter.cs new file mode 100644 index 0000000000000..3cb268cd2c647 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentFilter.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +namespace Azure.ResourceManager.Authorization.Models +{ + /// Role Assignments filter. + internal partial class RoleAssignmentFilter + { + /// Initializes a new instance of RoleAssignmentFilter. + internal RoleAssignmentFilter() + { + } + + /// Returns role assignment of the specific principal. + public string PrincipalId { get; } + /// The Delegation flag for the role assignment. + public bool? CanDelegate { get; } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentListResult.Serialization.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentListResult.Serialization.cs new file mode 100644 index 0000000000000..5c3d699538dd4 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentListResult.Serialization.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Collections.Generic; +using System.Text.Json; +using Azure.Core; + +namespace Azure.ResourceManager.Authorization.Models +{ + public partial class RoleAssignmentListResult + { + internal static RoleAssignmentListResult DeserializeRoleAssignmentListResult(JsonElement element) + { + Optional> value = default; + Optional nextLink = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("value")) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + property.ThrowNonNullablePropertyIsNull(); + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(RoleAssignment.DeserializeRoleAssignment(item)); + } + value = array; + continue; + } + if (property.NameEquals("nextLink")) + { + nextLink = property.Value.GetString(); + continue; + } + } + return new RoleAssignmentListResult(Optional.ToList(value), nextLink.Value); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentListResult.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentListResult.cs new file mode 100644 index 0000000000000..ec7afa7e5a8c2 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/Models/RoleAssignmentListResult.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Collections.Generic; +using Azure.Core; + +namespace Azure.ResourceManager.Authorization.Models +{ + /// Role assignment list operation result. + public partial class RoleAssignmentListResult + { + /// Initializes a new instance of RoleAssignmentListResult. + internal RoleAssignmentListResult() + { + Value = new ChangeTrackingList(); + } + + /// Initializes a new instance of RoleAssignmentListResult. + /// Role assignment list. + /// The URL to use for getting the next set of results. + internal RoleAssignmentListResult(IReadOnlyList value, string nextLink) + { + Value = value; + NextLink = nextLink; + } + + /// Role assignment list. + public IReadOnlyList Value { get; } + /// The URL to use for getting the next set of results. + public string NextLink { get; } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/RoleAssignmentsOperations.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/RoleAssignmentsOperations.cs new file mode 100644 index 0000000000000..6a263cd19bbee --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/RoleAssignmentsOperations.cs @@ -0,0 +1,653 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Threading; +using System.Threading.Tasks; +using Azure; +using Azure.Core; +using Azure.Core.Pipeline; +using Azure.ResourceManager.Authorization.Models; + +namespace Azure.ResourceManager.Authorization +{ + /// The RoleAssignments service client. + public partial class RoleAssignmentsOperations + { + private readonly ClientDiagnostics _clientDiagnostics; + private readonly HttpPipeline _pipeline; + internal RoleAssignmentsRestOperations RestClient { get; } + /// Initializes a new instance of RoleAssignmentsOperations for mocking. + protected RoleAssignmentsOperations() + { + } + /// Initializes a new instance of RoleAssignmentsOperations. + /// The handler for diagnostic messaging in the client. + /// The HTTP pipeline for sending and receiving REST requests and responses. + /// The ID of the target subscription. + /// server parameter. + /// Api Version. + internal RoleAssignmentsOperations(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string subscriptionId, Uri endpoint = null, string apiVersion = "2020-04-01-preview") + { + RestClient = new RoleAssignmentsRestOperations(clientDiagnostics, pipeline, subscriptionId, endpoint, apiVersion); + _clientDiagnostics = clientDiagnostics; + _pipeline = pipeline; + } + + /// Delete a role assignment. + /// The scope of the role assignment to delete. + /// The name of the role assignment to delete. + /// The cancellation token to use. + public virtual async Task> DeleteAsync(string scope, string roleAssignmentName, CancellationToken cancellationToken = default) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.Delete"); + scope0.Start(); + try + { + return await RestClient.DeleteAsync(scope, roleAssignmentName, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + + /// Delete a role assignment. + /// The scope of the role assignment to delete. + /// The name of the role assignment to delete. + /// The cancellation token to use. + public virtual Response Delete(string scope, string roleAssignmentName, CancellationToken cancellationToken = default) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.Delete"); + scope0.Start(); + try + { + return RestClient.Delete(scope, roleAssignmentName, cancellationToken); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + + /// Create a role assignment. + /// The scope of the role assignment to create. The scope can be any REST resource instance. For example, use '/subscriptions/{subscription-id}/' for a subscription, '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}' for a resource group, and '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/{resource-provider}/{resource-type}/{resource-name}' for a resource. + /// The name of the role assignment to create. It can be any valid GUID. + /// Parameters for the role assignment. + /// The cancellation token to use. + public virtual async Task> CreateAsync(string scope, string roleAssignmentName, RoleAssignmentCreateParameters parameters, CancellationToken cancellationToken = default) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.Create"); + scope0.Start(); + try + { + return await RestClient.CreateAsync(scope, roleAssignmentName, parameters, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + + /// Create a role assignment. + /// The scope of the role assignment to create. The scope can be any REST resource instance. For example, use '/subscriptions/{subscription-id}/' for a subscription, '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}' for a resource group, and '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/{resource-provider}/{resource-type}/{resource-name}' for a resource. + /// The name of the role assignment to create. It can be any valid GUID. + /// Parameters for the role assignment. + /// The cancellation token to use. + public virtual Response Create(string scope, string roleAssignmentName, RoleAssignmentCreateParameters parameters, CancellationToken cancellationToken = default) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.Create"); + scope0.Start(); + try + { + return RestClient.Create(scope, roleAssignmentName, parameters, cancellationToken); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + + /// Get the specified role assignment. + /// The scope of the role assignment. + /// The name of the role assignment to get. + /// The cancellation token to use. + public virtual async Task> GetAsync(string scope, string roleAssignmentName, CancellationToken cancellationToken = default) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.Get"); + scope0.Start(); + try + { + return await RestClient.GetAsync(scope, roleAssignmentName, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + + /// Get the specified role assignment. + /// The scope of the role assignment. + /// The name of the role assignment to get. + /// The cancellation token to use. + public virtual Response Get(string scope, string roleAssignmentName, CancellationToken cancellationToken = default) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.Get"); + scope0.Start(); + try + { + return RestClient.Get(scope, roleAssignmentName, cancellationToken); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + + /// Delete a role assignment. + /// The ID of the role assignment to delete. + /// The cancellation token to use. + public virtual async Task> DeleteByIdAsync(string roleId, CancellationToken cancellationToken = default) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.DeleteById"); + scope0.Start(); + try + { + return await RestClient.DeleteByIdAsync(roleId, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + + /// Delete a role assignment. + /// The ID of the role assignment to delete. + /// The cancellation token to use. + public virtual Response DeleteById(string roleId, CancellationToken cancellationToken = default) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.DeleteById"); + scope0.Start(); + try + { + return RestClient.DeleteById(roleId, cancellationToken); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + + /// Creates a role assignment by ID. + /// The ID of the role assignment to create. + /// Parameters for the role assignment. + /// The cancellation token to use. + public virtual async Task> CreateByIdAsync(string roleId, RoleAssignmentCreateParameters parameters, CancellationToken cancellationToken = default) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.CreateById"); + scope0.Start(); + try + { + return await RestClient.CreateByIdAsync(roleId, parameters, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + + /// Creates a role assignment by ID. + /// The ID of the role assignment to create. + /// Parameters for the role assignment. + /// The cancellation token to use. + public virtual Response CreateById(string roleId, RoleAssignmentCreateParameters parameters, CancellationToken cancellationToken = default) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.CreateById"); + scope0.Start(); + try + { + return RestClient.CreateById(roleId, parameters, cancellationToken); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + + /// Gets a role assignment by ID. + /// The ID of the role assignment to get. + /// The cancellation token to use. + public virtual async Task> GetByIdAsync(string roleId, CancellationToken cancellationToken = default) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.GetById"); + scope0.Start(); + try + { + return await RestClient.GetByIdAsync(roleId, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + + /// Gets a role assignment by ID. + /// The ID of the role assignment to get. + /// The cancellation token to use. + public virtual Response GetById(string roleId, CancellationToken cancellationToken = default) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.GetById"); + scope0.Start(); + try + { + return RestClient.GetById(roleId, cancellationToken); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + + /// List role assignments for a resource. + /// The name of the resource group. The name is case insensitive. + /// The namespace of the resource provider. + /// The parent resource identity. + /// The resource type of the resource. + /// The name of the resource to get role assignments for. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// , , , , or is null. + public virtual AsyncPageable ListForResourceAsync(string resourceGroupName, string resourceProviderNamespace, string parentResourcePath, string resourceType, string resourceName, string filter = null, CancellationToken cancellationToken = default) + { + if (resourceGroupName == null) + { + throw new ArgumentNullException(nameof(resourceGroupName)); + } + if (resourceProviderNamespace == null) + { + throw new ArgumentNullException(nameof(resourceProviderNamespace)); + } + if (parentResourcePath == null) + { + throw new ArgumentNullException(nameof(parentResourcePath)); + } + if (resourceType == null) + { + throw new ArgumentNullException(nameof(resourceType)); + } + if (resourceName == null) + { + throw new ArgumentNullException(nameof(resourceName)); + } + + async Task> FirstPageFunc(int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.ListForResource"); + scope0.Start(); + try + { + var response = await RestClient.ListForResourceAsync(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, filter, cancellationToken).ConfigureAwait(false); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + async Task> NextPageFunc(string nextLink, int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.ListForResource"); + scope0.Start(); + try + { + var response = await RestClient.ListForResourceNextPageAsync(nextLink, resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, filter, cancellationToken).ConfigureAwait(false); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + return PageableHelpers.CreateAsyncEnumerable(FirstPageFunc, NextPageFunc); + } + + /// List role assignments for a resource. + /// The name of the resource group. The name is case insensitive. + /// The namespace of the resource provider. + /// The parent resource identity. + /// The resource type of the resource. + /// The name of the resource to get role assignments for. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// , , , , or is null. + public virtual Pageable ListForResource(string resourceGroupName, string resourceProviderNamespace, string parentResourcePath, string resourceType, string resourceName, string filter = null, CancellationToken cancellationToken = default) + { + if (resourceGroupName == null) + { + throw new ArgumentNullException(nameof(resourceGroupName)); + } + if (resourceProviderNamespace == null) + { + throw new ArgumentNullException(nameof(resourceProviderNamespace)); + } + if (parentResourcePath == null) + { + throw new ArgumentNullException(nameof(parentResourcePath)); + } + if (resourceType == null) + { + throw new ArgumentNullException(nameof(resourceType)); + } + if (resourceName == null) + { + throw new ArgumentNullException(nameof(resourceName)); + } + + Page FirstPageFunc(int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.ListForResource"); + scope0.Start(); + try + { + var response = RestClient.ListForResource(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, filter, cancellationToken); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + Page NextPageFunc(string nextLink, int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.ListForResource"); + scope0.Start(); + try + { + var response = RestClient.ListForResourceNextPage(nextLink, resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, filter, cancellationToken); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + return PageableHelpers.CreateEnumerable(FirstPageFunc, NextPageFunc); + } + + /// List role assignments for a resource group. + /// The name of the resource group. The name is case insensitive. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// is null. + public virtual AsyncPageable ListForResourceGroupAsync(string resourceGroupName, string filter = null, CancellationToken cancellationToken = default) + { + if (resourceGroupName == null) + { + throw new ArgumentNullException(nameof(resourceGroupName)); + } + + async Task> FirstPageFunc(int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.ListForResourceGroup"); + scope0.Start(); + try + { + var response = await RestClient.ListForResourceGroupAsync(resourceGroupName, filter, cancellationToken).ConfigureAwait(false); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + async Task> NextPageFunc(string nextLink, int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.ListForResourceGroup"); + scope0.Start(); + try + { + var response = await RestClient.ListForResourceGroupNextPageAsync(nextLink, resourceGroupName, filter, cancellationToken).ConfigureAwait(false); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + return PageableHelpers.CreateAsyncEnumerable(FirstPageFunc, NextPageFunc); + } + + /// List role assignments for a resource group. + /// The name of the resource group. The name is case insensitive. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// is null. + public virtual Pageable ListForResourceGroup(string resourceGroupName, string filter = null, CancellationToken cancellationToken = default) + { + if (resourceGroupName == null) + { + throw new ArgumentNullException(nameof(resourceGroupName)); + } + + Page FirstPageFunc(int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.ListForResourceGroup"); + scope0.Start(); + try + { + var response = RestClient.ListForResourceGroup(resourceGroupName, filter, cancellationToken); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + Page NextPageFunc(string nextLink, int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.ListForResourceGroup"); + scope0.Start(); + try + { + var response = RestClient.ListForResourceGroupNextPage(nextLink, resourceGroupName, filter, cancellationToken); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + return PageableHelpers.CreateEnumerable(FirstPageFunc, NextPageFunc); + } + + /// Gets all role assignments for the subscription. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + public virtual AsyncPageable ListAsync(string filter = null, CancellationToken cancellationToken = default) + { + async Task> FirstPageFunc(int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.List"); + scope0.Start(); + try + { + var response = await RestClient.ListAsync(filter, cancellationToken).ConfigureAwait(false); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + async Task> NextPageFunc(string nextLink, int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.List"); + scope0.Start(); + try + { + var response = await RestClient.ListNextPageAsync(nextLink, filter, cancellationToken).ConfigureAwait(false); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + return PageableHelpers.CreateAsyncEnumerable(FirstPageFunc, NextPageFunc); + } + + /// Gets all role assignments for the subscription. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + public virtual Pageable List(string filter = null, CancellationToken cancellationToken = default) + { + Page FirstPageFunc(int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.List"); + scope0.Start(); + try + { + var response = RestClient.List(filter, cancellationToken); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + Page NextPageFunc(string nextLink, int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.List"); + scope0.Start(); + try + { + var response = RestClient.ListNextPage(nextLink, filter, cancellationToken); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + return PageableHelpers.CreateEnumerable(FirstPageFunc, NextPageFunc); + } + + /// Gets role assignments for a scope. + /// The scope of the role assignments. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// is null. + public virtual AsyncPageable ListForScopeAsync(string scope, string filter = null, CancellationToken cancellationToken = default) + { + if (scope == null) + { + throw new ArgumentNullException(nameof(scope)); + } + + async Task> FirstPageFunc(int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.ListForScope"); + scope0.Start(); + try + { + var response = await RestClient.ListForScopeAsync(scope, filter, cancellationToken).ConfigureAwait(false); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + async Task> NextPageFunc(string nextLink, int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.ListForScope"); + scope0.Start(); + try + { + var response = await RestClient.ListForScopeNextPageAsync(nextLink, scope, filter, cancellationToken).ConfigureAwait(false); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + return PageableHelpers.CreateAsyncEnumerable(FirstPageFunc, NextPageFunc); + } + + /// Gets role assignments for a scope. + /// The scope of the role assignments. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// is null. + public virtual Pageable ListForScope(string scope, string filter = null, CancellationToken cancellationToken = default) + { + if (scope == null) + { + throw new ArgumentNullException(nameof(scope)); + } + + Page FirstPageFunc(int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.ListForScope"); + scope0.Start(); + try + { + var response = RestClient.ListForScope(scope, filter, cancellationToken); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + Page NextPageFunc(string nextLink, int? pageSizeHint) + { + using var scope0 = _clientDiagnostics.CreateScope("RoleAssignmentsOperations.ListForScope"); + scope0.Start(); + try + { + var response = RestClient.ListForScopeNextPage(nextLink, scope, filter, cancellationToken); + return Page.FromValues(response.Value.Value, response.Value.NextLink, response.GetRawResponse()); + } + catch (Exception e) + { + scope0.Failed(e); + throw; + } + } + return PageableHelpers.CreateEnumerable(FirstPageFunc, NextPageFunc); + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/RoleAssignmentsRestOperations.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/RoleAssignmentsRestOperations.cs new file mode 100644 index 0000000000000..4851b1729f788 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Generated/RoleAssignmentsRestOperations.cs @@ -0,0 +1,1227 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Azure; +using Azure.Core; +using Azure.Core.Pipeline; +using Azure.ResourceManager.Authorization.Models; + +namespace Azure.ResourceManager.Authorization +{ + internal partial class RoleAssignmentsRestOperations + { + private string subscriptionId; + private Uri endpoint; + private string apiVersion; + private ClientDiagnostics _clientDiagnostics; + private HttpPipeline _pipeline; + + /// Initializes a new instance of RoleAssignmentsRestOperations. + /// The handler for diagnostic messaging in the client. + /// The HTTP pipeline for sending and receiving REST requests and responses. + /// The ID of the target subscription. + /// server parameter. + /// Api Version. + /// or is null. + public RoleAssignmentsRestOperations(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string subscriptionId, Uri endpoint = null, string apiVersion = "2020-04-01-preview") + { + if (subscriptionId == null) + { + throw new ArgumentNullException(nameof(subscriptionId)); + } + endpoint ??= new Uri("https://management.azure.com"); + if (apiVersion == null) + { + throw new ArgumentNullException(nameof(apiVersion)); + } + + this.subscriptionId = subscriptionId; + this.endpoint = endpoint; + this.apiVersion = apiVersion; + _clientDiagnostics = clientDiagnostics; + _pipeline = pipeline; + } + + internal HttpMessage CreateListForResourceRequest(string resourceGroupName, string resourceProviderNamespace, string parentResourcePath, string resourceType, string resourceName, string filter) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Get; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendPath("/subscriptions/", false); + uri.AppendPath(subscriptionId, true); + uri.AppendPath("/resourcegroups/", false); + uri.AppendPath(resourceGroupName, true); + uri.AppendPath("/providers/", false); + uri.AppendPath(resourceProviderNamespace, true); + uri.AppendPath("/", false); + uri.AppendPath(parentResourcePath, false); + uri.AppendPath("/", false); + uri.AppendPath(resourceType, false); + uri.AppendPath("/", false); + uri.AppendPath(resourceName, true); + uri.AppendPath("/providers/Microsoft.Authorization/roleAssignments", false); + if (filter != null) + { + uri.AppendQuery("$filter", filter, true); + } + uri.AppendQuery("api-version", apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// List role assignments for a resource. + /// The name of the resource group. The name is case insensitive. + /// The namespace of the resource provider. + /// The parent resource identity. + /// The resource type of the resource. + /// The name of the resource to get role assignments for. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// , , , , or is null. + public async Task> ListForResourceAsync(string resourceGroupName, string resourceProviderNamespace, string parentResourcePath, string resourceType, string resourceName, string filter = null, CancellationToken cancellationToken = default) + { + if (resourceGroupName == null) + { + throw new ArgumentNullException(nameof(resourceGroupName)); + } + if (resourceProviderNamespace == null) + { + throw new ArgumentNullException(nameof(resourceProviderNamespace)); + } + if (parentResourcePath == null) + { + throw new ArgumentNullException(nameof(parentResourcePath)); + } + if (resourceType == null) + { + throw new ArgumentNullException(nameof(resourceType)); + } + if (resourceName == null) + { + throw new ArgumentNullException(nameof(resourceName)); + } + + using var message = CreateListForResourceRequest(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, filter); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// List role assignments for a resource. + /// The name of the resource group. The name is case insensitive. + /// The namespace of the resource provider. + /// The parent resource identity. + /// The resource type of the resource. + /// The name of the resource to get role assignments for. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// , , , , or is null. + public Response ListForResource(string resourceGroupName, string resourceProviderNamespace, string parentResourcePath, string resourceType, string resourceName, string filter = null, CancellationToken cancellationToken = default) + { + if (resourceGroupName == null) + { + throw new ArgumentNullException(nameof(resourceGroupName)); + } + if (resourceProviderNamespace == null) + { + throw new ArgumentNullException(nameof(resourceProviderNamespace)); + } + if (parentResourcePath == null) + { + throw new ArgumentNullException(nameof(parentResourcePath)); + } + if (resourceType == null) + { + throw new ArgumentNullException(nameof(resourceType)); + } + if (resourceName == null) + { + throw new ArgumentNullException(nameof(resourceName)); + } + + using var message = CreateListForResourceRequest(resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, filter); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateListForResourceGroupRequest(string resourceGroupName, string filter) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Get; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendPath("/subscriptions/", false); + uri.AppendPath(subscriptionId, true); + uri.AppendPath("/resourceGroups/", false); + uri.AppendPath(resourceGroupName, true); + uri.AppendPath("/providers/Microsoft.Authorization/roleAssignments", false); + if (filter != null) + { + uri.AppendQuery("$filter", filter, true); + } + uri.AppendQuery("api-version", apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// List role assignments for a resource group. + /// The name of the resource group. The name is case insensitive. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// is null. + public async Task> ListForResourceGroupAsync(string resourceGroupName, string filter = null, CancellationToken cancellationToken = default) + { + if (resourceGroupName == null) + { + throw new ArgumentNullException(nameof(resourceGroupName)); + } + + using var message = CreateListForResourceGroupRequest(resourceGroupName, filter); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// List role assignments for a resource group. + /// The name of the resource group. The name is case insensitive. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// is null. + public Response ListForResourceGroup(string resourceGroupName, string filter = null, CancellationToken cancellationToken = default) + { + if (resourceGroupName == null) + { + throw new ArgumentNullException(nameof(resourceGroupName)); + } + + using var message = CreateListForResourceGroupRequest(resourceGroupName, filter); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateDeleteRequest(string scope, string roleAssignmentName) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Delete; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendPath("/", false); + uri.AppendPath(scope, false); + uri.AppendPath("/providers/Microsoft.Authorization/roleAssignments/", false); + uri.AppendPath(roleAssignmentName, true); + uri.AppendQuery("api-version", apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// Delete a role assignment. + /// The scope of the role assignment to delete. + /// The name of the role assignment to delete. + /// The cancellation token to use. + /// or is null. + public async Task> DeleteAsync(string scope, string roleAssignmentName, CancellationToken cancellationToken = default) + { + if (scope == null) + { + throw new ArgumentNullException(nameof(scope)); + } + if (roleAssignmentName == null) + { + throw new ArgumentNullException(nameof(roleAssignmentName)); + } + + using var message = CreateDeleteRequest(scope, roleAssignmentName); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + RoleAssignment value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignment.DeserializeRoleAssignment(document.RootElement); + return Response.FromValue(value, message.Response); + } + case 204: + return Response.FromValue(null, message.Response); + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// Delete a role assignment. + /// The scope of the role assignment to delete. + /// The name of the role assignment to delete. + /// The cancellation token to use. + /// or is null. + public Response Delete(string scope, string roleAssignmentName, CancellationToken cancellationToken = default) + { + if (scope == null) + { + throw new ArgumentNullException(nameof(scope)); + } + if (roleAssignmentName == null) + { + throw new ArgumentNullException(nameof(roleAssignmentName)); + } + + using var message = CreateDeleteRequest(scope, roleAssignmentName); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + RoleAssignment value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignment.DeserializeRoleAssignment(document.RootElement); + return Response.FromValue(value, message.Response); + } + case 204: + return Response.FromValue(null, message.Response); + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateCreateRequest(string scope, string roleAssignmentName, RoleAssignmentCreateParameters parameters) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Put; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendPath("/", false); + uri.AppendPath(scope, false); + uri.AppendPath("/providers/Microsoft.Authorization/roleAssignments/", false); + uri.AppendPath(roleAssignmentName, true); + uri.AppendQuery("api-version", apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + request.Headers.Add("Content-Type", "application/json"); + var content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteObjectValue(parameters); + request.Content = content; + return message; + } + + /// Create a role assignment. + /// The scope of the role assignment to create. The scope can be any REST resource instance. For example, use '/subscriptions/{subscription-id}/' for a subscription, '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}' for a resource group, and '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/{resource-provider}/{resource-type}/{resource-name}' for a resource. + /// The name of the role assignment to create. It can be any valid GUID. + /// Parameters for the role assignment. + /// The cancellation token to use. + /// , , or is null. + public async Task> CreateAsync(string scope, string roleAssignmentName, RoleAssignmentCreateParameters parameters, CancellationToken cancellationToken = default) + { + if (scope == null) + { + throw new ArgumentNullException(nameof(scope)); + } + if (roleAssignmentName == null) + { + throw new ArgumentNullException(nameof(roleAssignmentName)); + } + if (parameters == null) + { + throw new ArgumentNullException(nameof(parameters)); + } + + using var message = CreateCreateRequest(scope, roleAssignmentName, parameters); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 201: + { + RoleAssignment value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignment.DeserializeRoleAssignment(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// Create a role assignment. + /// The scope of the role assignment to create. The scope can be any REST resource instance. For example, use '/subscriptions/{subscription-id}/' for a subscription, '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}' for a resource group, and '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/{resource-provider}/{resource-type}/{resource-name}' for a resource. + /// The name of the role assignment to create. It can be any valid GUID. + /// Parameters for the role assignment. + /// The cancellation token to use. + /// , , or is null. + public Response Create(string scope, string roleAssignmentName, RoleAssignmentCreateParameters parameters, CancellationToken cancellationToken = default) + { + if (scope == null) + { + throw new ArgumentNullException(nameof(scope)); + } + if (roleAssignmentName == null) + { + throw new ArgumentNullException(nameof(roleAssignmentName)); + } + if (parameters == null) + { + throw new ArgumentNullException(nameof(parameters)); + } + + using var message = CreateCreateRequest(scope, roleAssignmentName, parameters); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 201: + { + RoleAssignment value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignment.DeserializeRoleAssignment(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateGetRequest(string scope, string roleAssignmentName) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Get; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendPath("/", false); + uri.AppendPath(scope, false); + uri.AppendPath("/providers/Microsoft.Authorization/roleAssignments/", false); + uri.AppendPath(roleAssignmentName, true); + uri.AppendQuery("api-version", apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// Get the specified role assignment. + /// The scope of the role assignment. + /// The name of the role assignment to get. + /// The cancellation token to use. + /// or is null. + public async Task> GetAsync(string scope, string roleAssignmentName, CancellationToken cancellationToken = default) + { + if (scope == null) + { + throw new ArgumentNullException(nameof(scope)); + } + if (roleAssignmentName == null) + { + throw new ArgumentNullException(nameof(roleAssignmentName)); + } + + using var message = CreateGetRequest(scope, roleAssignmentName); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + RoleAssignment value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignment.DeserializeRoleAssignment(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// Get the specified role assignment. + /// The scope of the role assignment. + /// The name of the role assignment to get. + /// The cancellation token to use. + /// or is null. + public Response Get(string scope, string roleAssignmentName, CancellationToken cancellationToken = default) + { + if (scope == null) + { + throw new ArgumentNullException(nameof(scope)); + } + if (roleAssignmentName == null) + { + throw new ArgumentNullException(nameof(roleAssignmentName)); + } + + using var message = CreateGetRequest(scope, roleAssignmentName); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + RoleAssignment value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignment.DeserializeRoleAssignment(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateDeleteByIdRequest(string roleId) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Delete; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendPath("/", false); + uri.AppendPath(roleId, false); + uri.AppendQuery("api-version", apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// Delete a role assignment. + /// The ID of the role assignment to delete. + /// The cancellation token to use. + /// is null. + public async Task> DeleteByIdAsync(string roleId, CancellationToken cancellationToken = default) + { + if (roleId == null) + { + throw new ArgumentNullException(nameof(roleId)); + } + + using var message = CreateDeleteByIdRequest(roleId); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + RoleAssignment value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignment.DeserializeRoleAssignment(document.RootElement); + return Response.FromValue(value, message.Response); + } + case 204: + return Response.FromValue(null, message.Response); + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// Delete a role assignment. + /// The ID of the role assignment to delete. + /// The cancellation token to use. + /// is null. + public Response DeleteById(string roleId, CancellationToken cancellationToken = default) + { + if (roleId == null) + { + throw new ArgumentNullException(nameof(roleId)); + } + + using var message = CreateDeleteByIdRequest(roleId); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + RoleAssignment value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignment.DeserializeRoleAssignment(document.RootElement); + return Response.FromValue(value, message.Response); + } + case 204: + return Response.FromValue(null, message.Response); + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateCreateByIdRequest(string roleId, RoleAssignmentCreateParameters parameters) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Put; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendPath("/", false); + uri.AppendPath(roleId, false); + uri.AppendQuery("api-version", apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + request.Headers.Add("Content-Type", "application/json"); + var content = new Utf8JsonRequestContent(); + content.JsonWriter.WriteObjectValue(parameters); + request.Content = content; + return message; + } + + /// Creates a role assignment by ID. + /// The ID of the role assignment to create. + /// Parameters for the role assignment. + /// The cancellation token to use. + /// or is null. + public async Task> CreateByIdAsync(string roleId, RoleAssignmentCreateParameters parameters, CancellationToken cancellationToken = default) + { + if (roleId == null) + { + throw new ArgumentNullException(nameof(roleId)); + } + if (parameters == null) + { + throw new ArgumentNullException(nameof(parameters)); + } + + using var message = CreateCreateByIdRequest(roleId, parameters); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 201: + { + RoleAssignment value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignment.DeserializeRoleAssignment(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// Creates a role assignment by ID. + /// The ID of the role assignment to create. + /// Parameters for the role assignment. + /// The cancellation token to use. + /// or is null. + public Response CreateById(string roleId, RoleAssignmentCreateParameters parameters, CancellationToken cancellationToken = default) + { + if (roleId == null) + { + throw new ArgumentNullException(nameof(roleId)); + } + if (parameters == null) + { + throw new ArgumentNullException(nameof(parameters)); + } + + using var message = CreateCreateByIdRequest(roleId, parameters); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 201: + { + RoleAssignment value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignment.DeserializeRoleAssignment(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateGetByIdRequest(string roleId) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Get; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendPath("/", false); + uri.AppendPath(roleId, false); + uri.AppendQuery("api-version", apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// Gets a role assignment by ID. + /// The ID of the role assignment to get. + /// The cancellation token to use. + /// is null. + public async Task> GetByIdAsync(string roleId, CancellationToken cancellationToken = default) + { + if (roleId == null) + { + throw new ArgumentNullException(nameof(roleId)); + } + + using var message = CreateGetByIdRequest(roleId); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + RoleAssignment value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignment.DeserializeRoleAssignment(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// Gets a role assignment by ID. + /// The ID of the role assignment to get. + /// The cancellation token to use. + /// is null. + public Response GetById(string roleId, CancellationToken cancellationToken = default) + { + if (roleId == null) + { + throw new ArgumentNullException(nameof(roleId)); + } + + using var message = CreateGetByIdRequest(roleId); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + RoleAssignment value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignment.DeserializeRoleAssignment(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateListRequest(string filter) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Get; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendPath("/subscriptions/", false); + uri.AppendPath(subscriptionId, true); + uri.AppendPath("/providers/Microsoft.Authorization/roleAssignments", false); + if (filter != null) + { + uri.AppendQuery("$filter", filter, true); + } + uri.AppendQuery("api-version", apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// Gets all role assignments for the subscription. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + public async Task> ListAsync(string filter = null, CancellationToken cancellationToken = default) + { + using var message = CreateListRequest(filter); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// Gets all role assignments for the subscription. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + public Response List(string filter = null, CancellationToken cancellationToken = default) + { + using var message = CreateListRequest(filter); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateListForScopeRequest(string scope, string filter) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Get; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendPath("/", false); + uri.AppendPath(scope, false); + uri.AppendPath("/providers/Microsoft.Authorization/roleAssignments", false); + if (filter != null) + { + uri.AppendQuery("$filter", filter, true); + } + uri.AppendQuery("api-version", apiVersion, true); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// Gets role assignments for a scope. + /// The scope of the role assignments. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// is null. + public async Task> ListForScopeAsync(string scope, string filter = null, CancellationToken cancellationToken = default) + { + if (scope == null) + { + throw new ArgumentNullException(nameof(scope)); + } + + using var message = CreateListForScopeRequest(scope, filter); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// Gets role assignments for a scope. + /// The scope of the role assignments. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// is null. + public Response ListForScope(string scope, string filter = null, CancellationToken cancellationToken = default) + { + if (scope == null) + { + throw new ArgumentNullException(nameof(scope)); + } + + using var message = CreateListForScopeRequest(scope, filter); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateListForResourceNextPageRequest(string nextLink, string resourceGroupName, string resourceProviderNamespace, string parentResourcePath, string resourceType, string resourceName, string filter) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Get; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendRawNextLink(nextLink, false); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// List role assignments for a resource. + /// The URL to the next page of results. + /// The name of the resource group. The name is case insensitive. + /// The namespace of the resource provider. + /// The parent resource identity. + /// The resource type of the resource. + /// The name of the resource to get role assignments for. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// , , , , , or is null. + public async Task> ListForResourceNextPageAsync(string nextLink, string resourceGroupName, string resourceProviderNamespace, string parentResourcePath, string resourceType, string resourceName, string filter = null, CancellationToken cancellationToken = default) + { + if (nextLink == null) + { + throw new ArgumentNullException(nameof(nextLink)); + } + if (resourceGroupName == null) + { + throw new ArgumentNullException(nameof(resourceGroupName)); + } + if (resourceProviderNamespace == null) + { + throw new ArgumentNullException(nameof(resourceProviderNamespace)); + } + if (parentResourcePath == null) + { + throw new ArgumentNullException(nameof(parentResourcePath)); + } + if (resourceType == null) + { + throw new ArgumentNullException(nameof(resourceType)); + } + if (resourceName == null) + { + throw new ArgumentNullException(nameof(resourceName)); + } + + using var message = CreateListForResourceNextPageRequest(nextLink, resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, filter); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// List role assignments for a resource. + /// The URL to the next page of results. + /// The name of the resource group. The name is case insensitive. + /// The namespace of the resource provider. + /// The parent resource identity. + /// The resource type of the resource. + /// The name of the resource to get role assignments for. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// , , , , , or is null. + public Response ListForResourceNextPage(string nextLink, string resourceGroupName, string resourceProviderNamespace, string parentResourcePath, string resourceType, string resourceName, string filter = null, CancellationToken cancellationToken = default) + { + if (nextLink == null) + { + throw new ArgumentNullException(nameof(nextLink)); + } + if (resourceGroupName == null) + { + throw new ArgumentNullException(nameof(resourceGroupName)); + } + if (resourceProviderNamespace == null) + { + throw new ArgumentNullException(nameof(resourceProviderNamespace)); + } + if (parentResourcePath == null) + { + throw new ArgumentNullException(nameof(parentResourcePath)); + } + if (resourceType == null) + { + throw new ArgumentNullException(nameof(resourceType)); + } + if (resourceName == null) + { + throw new ArgumentNullException(nameof(resourceName)); + } + + using var message = CreateListForResourceNextPageRequest(nextLink, resourceGroupName, resourceProviderNamespace, parentResourcePath, resourceType, resourceName, filter); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateListForResourceGroupNextPageRequest(string nextLink, string resourceGroupName, string filter) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Get; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendRawNextLink(nextLink, false); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// List role assignments for a resource group. + /// The URL to the next page of results. + /// The name of the resource group. The name is case insensitive. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// or is null. + public async Task> ListForResourceGroupNextPageAsync(string nextLink, string resourceGroupName, string filter = null, CancellationToken cancellationToken = default) + { + if (nextLink == null) + { + throw new ArgumentNullException(nameof(nextLink)); + } + if (resourceGroupName == null) + { + throw new ArgumentNullException(nameof(resourceGroupName)); + } + + using var message = CreateListForResourceGroupNextPageRequest(nextLink, resourceGroupName, filter); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// List role assignments for a resource group. + /// The URL to the next page of results. + /// The name of the resource group. The name is case insensitive. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// or is null. + public Response ListForResourceGroupNextPage(string nextLink, string resourceGroupName, string filter = null, CancellationToken cancellationToken = default) + { + if (nextLink == null) + { + throw new ArgumentNullException(nameof(nextLink)); + } + if (resourceGroupName == null) + { + throw new ArgumentNullException(nameof(resourceGroupName)); + } + + using var message = CreateListForResourceGroupNextPageRequest(nextLink, resourceGroupName, filter); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateListNextPageRequest(string nextLink, string filter) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Get; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendRawNextLink(nextLink, false); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// Gets all role assignments for the subscription. + /// The URL to the next page of results. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// is null. + public async Task> ListNextPageAsync(string nextLink, string filter = null, CancellationToken cancellationToken = default) + { + if (nextLink == null) + { + throw new ArgumentNullException(nameof(nextLink)); + } + + using var message = CreateListNextPageRequest(nextLink, filter); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// Gets all role assignments for the subscription. + /// The URL to the next page of results. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// is null. + public Response ListNextPage(string nextLink, string filter = null, CancellationToken cancellationToken = default) + { + if (nextLink == null) + { + throw new ArgumentNullException(nameof(nextLink)); + } + + using var message = CreateListNextPageRequest(nextLink, filter); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + + internal HttpMessage CreateListForScopeNextPageRequest(string nextLink, string scope, string filter) + { + var message = _pipeline.CreateMessage(); + var request = message.Request; + request.Method = RequestMethod.Get; + var uri = new RawRequestUriBuilder(); + uri.Reset(endpoint); + uri.AppendRawNextLink(nextLink, false); + request.Uri = uri; + request.Headers.Add("Accept", "application/json"); + return message; + } + + /// Gets role assignments for a scope. + /// The URL to the next page of results. + /// The scope of the role assignments. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// or is null. + public async Task> ListForScopeNextPageAsync(string nextLink, string scope, string filter = null, CancellationToken cancellationToken = default) + { + if (nextLink == null) + { + throw new ArgumentNullException(nameof(nextLink)); + } + if (scope == null) + { + throw new ArgumentNullException(nameof(scope)); + } + + using var message = CreateListForScopeNextPageRequest(nextLink, scope, filter); + await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellationToken).ConfigureAwait(false); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw await _clientDiagnostics.CreateRequestFailedExceptionAsync(message.Response).ConfigureAwait(false); + } + } + + /// Gets role assignments for a scope. + /// The URL to the next page of results. + /// The scope of the role assignments. + /// The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. + /// The cancellation token to use. + /// or is null. + public Response ListForScopeNextPage(string nextLink, string scope, string filter = null, CancellationToken cancellationToken = default) + { + if (nextLink == null) + { + throw new ArgumentNullException(nameof(nextLink)); + } + if (scope == null) + { + throw new ArgumentNullException(nameof(scope)); + } + + using var message = CreateListForScopeNextPageRequest(nextLink, scope, filter); + _pipeline.Send(message, cancellationToken); + switch (message.Response.Status) + { + case 200: + { + RoleAssignmentListResult value = default; + using var document = JsonDocument.Parse(message.Response.ContentStream); + value = RoleAssignmentListResult.DeserializeRoleAssignmentListResult(document.RootElement); + return Response.FromValue(value, message.Response); + } + default: + throw _clientDiagnostics.CreateRequestFailedException(message.Response); + } + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Properties/AssemblyInfo.cs b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000000..4cab9c49d0ebf --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Runtime.CompilerServices; + +[assembly: Azure.Core.AzureResourceProviderNamespace("Microsoft.Authorization")] + +[assembly: InternalsVisibleTo("Azure.ResourceManager.Authorization.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d15ddcb29688295338af4b7686603fe614abd555e09efba8fb88ee09e1f7b1ccaeed2e8f823fa9eef3fdd60217fc012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593daa7b11b4")] diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/README.MD b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/README.MD new file mode 100644 index 0000000000000..f48c165987686 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/README.MD @@ -0,0 +1,3 @@ +# Proto Azure ResourceManager Authorization + +Proto type version of Azure.ResourceManager.Authorization used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/autorest.md b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/autorest.md new file mode 100644 index 0000000000000..face655375676 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/autorest.md @@ -0,0 +1,11 @@ +# Generated code configuration + +Run `dotnet build /t:GenerateCode` to generate code. + +``` yaml + +azure-arm: true +require: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/f0a0a9f0ab2278885f9349ef03bf02d4790f28ec/specification/authorization/resource-manager/readme.md + + +``` \ No newline at end of file diff --git a/sdk/resourcemanager/Proto.Client/authorization/Proto.Authorization.csproj b/sdk/resourcemanager/Proto.Client/authorization/Proto.Authorization.csproj index 0f2bd6bccefe7..2027a495657b5 100644 --- a/sdk/resourcemanager/Proto.Client/authorization/Proto.Authorization.csproj +++ b/sdk/resourcemanager/Proto.Client/authorization/Proto.Authorization.csproj @@ -8,6 +8,12 @@ obj\docs.xml + + + + + + @@ -18,13 +24,7 @@ + - - - ..\lib\Azure.ResourceManager.Authorization.dll - - - - diff --git a/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.deps.json b/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.deps.json deleted file mode 100644 index 9156794a2e23f..0000000000000 --- a/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.deps.json +++ /dev/null @@ -1,347 +0,0 @@ -{ - "runtimeTarget": { - "name": ".NETStandard,Version=v2.0/", - "signature": "" - }, - "compilationOptions": {}, - "targets": { - ".NETStandard,Version=v2.0": {}, - ".NETStandard,Version=v2.0/": { - "Azure.ResourceManager.Authorization/1.0.0-alpha.20201115.1": { - "dependencies": { - "AutoRest.CSharp.V3": "1.0.0-alpha.20201013.3", - "Azure.ClientSdk.Analyzers": "0.1.1-dev.20200929.2", - "Azure.Core": "1.3.0", - "Microsoft.CodeAnalysis.FxCopAnalyzers": "2.6.2", - "Microsoft.DotNet.GenAPI": "5.0.0-beta.19552.1", - "Microsoft.SourceLink.GitHub": "1.0.0", - "NETStandard.Library": "2.0.3", - "StyleCop.Analyzers": "1.1.118", - "System.Text.Json": "4.6.0" - }, - "runtime": { - "Azure.ResourceManager.Authorization.dll": {} - } - }, - "AutoRest.CSharp.V3/1.0.0-alpha.20201013.3": {}, - "Azure.ClientSdk.Analyzers/0.1.1-dev.20200929.2": {}, - "Azure.Core/1.3.0": { - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.0.0", - "System.Buffers": "4.5.0", - "System.Diagnostics.DiagnosticSource": "4.6.0", - "System.Memory": "4.5.3", - "System.Numerics.Vectors": "4.5.0", - "System.Threading.Tasks.Extensions": "4.5.2" - }, - "runtime": { - "lib/netstandard2.0/Azure.Core.dll": { - "assemblyVersion": "1.3.0.0", - "fileVersion": "1.300.20.35202" - } - } - }, - "Microsoft.Bcl.AsyncInterfaces/1.0.0": { - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.2" - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Bcl.AsyncInterfaces.dll": { - "assemblyVersion": "1.0.0.0", - "fileVersion": "4.700.19.46214" - } - } - }, - "Microsoft.Build.Tasks.Git/1.0.0": {}, - "Microsoft.CodeAnalysis.FxCopAnalyzers/2.6.2": { - "dependencies": { - "Microsoft.CodeQuality.Analyzers": "2.6.2", - "Microsoft.NetCore.Analyzers": "2.6.2", - "Microsoft.NetFramework.Analyzers": "2.6.2", - "Text.Analyzers": "2.6.2" - } - }, - "Microsoft.CodeQuality.Analyzers/2.6.2": {}, - "Microsoft.DotNet.GenAPI/5.0.0-beta.19552.1": {}, - "Microsoft.NetCore.Analyzers/2.6.2": {}, - "Microsoft.NETCore.Platforms/1.1.0": {}, - "Microsoft.NetFramework.Analyzers/2.6.2": {}, - "Microsoft.SourceLink.Common/1.0.0": {}, - "Microsoft.SourceLink.GitHub/1.0.0": { - "dependencies": { - "Microsoft.Build.Tasks.Git": "1.0.0", - "Microsoft.SourceLink.Common": "1.0.0" - } - }, - "NETStandard.Library/2.0.3": { - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, - "StyleCop.Analyzers/1.1.118": {}, - "System.Buffers/4.5.0": { - "runtime": { - "lib/netstandard2.0/System.Buffers.dll": { - "assemblyVersion": "4.0.3.0", - "fileVersion": "4.6.26515.6" - } - } - }, - "System.Diagnostics.DiagnosticSource/4.6.0": { - "dependencies": { - "System.Memory": "4.5.3" - }, - "runtime": { - "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll": { - "assemblyVersion": "4.0.4.0", - "fileVersion": "4.700.19.46214" - } - } - }, - "System.Memory/4.5.3": { - "dependencies": { - "System.Buffers": "4.5.0", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.6.0" - }, - "runtime": { - "lib/netstandard2.0/System.Memory.dll": { - "assemblyVersion": "4.0.1.1", - "fileVersion": "4.6.27617.2" - } - } - }, - "System.Numerics.Vectors/4.5.0": { - "runtime": { - "lib/netstandard2.0/System.Numerics.Vectors.dll": { - "assemblyVersion": "4.1.4.0", - "fileVersion": "4.6.26515.6" - } - } - }, - "System.Runtime.CompilerServices.Unsafe/4.6.0": { - "runtime": { - "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": { - "assemblyVersion": "4.0.5.0", - "fileVersion": "4.700.19.46214" - } - } - }, - "System.Text.Encodings.Web/4.6.0": { - "dependencies": { - "System.Memory": "4.5.3" - }, - "runtime": { - "lib/netstandard2.0/System.Text.Encodings.Web.dll": { - "assemblyVersion": "4.0.4.0", - "fileVersion": "4.700.19.46214" - } - } - }, - "System.Text.Json/4.6.0": { - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.0.0", - "System.Buffers": "4.5.0", - "System.Memory": "4.5.3", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.6.0", - "System.Text.Encodings.Web": "4.6.0", - "System.Threading.Tasks.Extensions": "4.5.2" - }, - "runtime": { - "lib/netstandard2.0/System.Text.Json.dll": { - "assemblyVersion": "4.0.0.0", - "fileVersion": "4.700.19.46214" - } - } - }, - "System.Threading.Tasks.Extensions/4.5.2": { - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.6.0" - }, - "runtime": { - "lib/netstandard2.0/System.Threading.Tasks.Extensions.dll": { - "assemblyVersion": "4.2.0.0", - "fileVersion": "4.6.27129.4" - } - } - }, - "Text.Analyzers/2.6.2": {} - } - }, - "libraries": { - "Azure.ResourceManager.Authorization/1.0.0-alpha.20201115.1": { - "type": "project", - "serviceable": false, - "sha512": "" - }, - "AutoRest.CSharp.V3/1.0.0-alpha.20201013.3": { - "type": "package", - "serviceable": true, - "sha512": "sha512-cDkrad9n4HGxYslwAHeaKzZoMCvjY1r3VNU+ow+1njE9P5WPzzQ632NLaXw3OKhyRyAMYZYLlxlWsRQdvKyk2g==", - "path": "autorest.csharp.v3/1.0.0-alpha.20201013.3", - "hashPath": "autorest.csharp.v3.1.0.0-alpha.20201013.3.nupkg.sha512" - }, - "Azure.ClientSdk.Analyzers/0.1.1-dev.20200929.2": { - "type": "package", - "serviceable": true, - "sha512": "sha512-lfQaBxMYPEIVud0H8ieaJDpwQR/NhUNC9QUW7rJ0NoifkTE2pbay4I0Ujfk50B7FTynalk1NGycCCOuKbAJzqA==", - "path": "azure.clientsdk.analyzers/0.1.1-dev.20200929.2", - "hashPath": "azure.clientsdk.analyzers.0.1.1-dev.20200929.2.nupkg.sha512" - }, - "Azure.Core/1.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-OJLa0kwBYI3wH0g7xhkLoShUwmOCx5LkEVB2lWXqqTtQAklIKLs2znuVyFpqDICE9vJhvA5aVQHhbSz9+Hv+LQ==", - "path": "azure.core/1.3.0", - "hashPath": "azure.core.1.3.0.nupkg.sha512" - }, - "Microsoft.Bcl.AsyncInterfaces/1.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-K63Y4hORbBcKLWH5wnKgzyn7TOfYzevIEwIedQHBIkmkEBA9SCqgvom+XTuE+fAFGvINGkhFItaZ2dvMGdT5iw==", - "path": "microsoft.bcl.asyncinterfaces/1.0.0", - "hashPath": "microsoft.bcl.asyncinterfaces.1.0.0.nupkg.sha512" - }, - "Microsoft.Build.Tasks.Git/1.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-z2fpmmt+1Jfl+ZnBki9nSP08S1/tbEOxFdsK1rSR+LBehIJz1Xv9/6qOOoGNqlwnAGGVGis1Oj6S8Kt9COEYlQ==", - "path": "microsoft.build.tasks.git/1.0.0", - "hashPath": "microsoft.build.tasks.git.1.0.0.nupkg.sha512" - }, - "Microsoft.CodeAnalysis.FxCopAnalyzers/2.6.2": { - "type": "package", - "serviceable": true, - "sha512": "sha512-fub9nWKI08wCmC9hnARCCYDLEd+aAs/aqhAQ27BmaP2fq3Jh8WSgNFLNInic+9lXQmibU6r7SSKbMroh3aM3ig==", - "path": "microsoft.codeanalysis.fxcopanalyzers/2.6.2", - "hashPath": "microsoft.codeanalysis.fxcopanalyzers.2.6.2.nupkg.sha512" - }, - "Microsoft.CodeQuality.Analyzers/2.6.2": { - "type": "package", - "serviceable": true, - "sha512": "sha512-HEH7nPissPcvMhx0MOXXaARkmzkEP7hDFBz3o06ZDj3rpE+F0e5hVoGJDr+qPsbJaeDoAlbArzTYfGsbZPSlPA==", - "path": "microsoft.codequality.analyzers/2.6.2", - "hashPath": "microsoft.codequality.analyzers.2.6.2.nupkg.sha512" - }, - "Microsoft.DotNet.GenAPI/5.0.0-beta.19552.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-lfpxOPSnPK/2fmWQbDfSX6CbYl5w3t/uXO31VWRIOeIvaJC9eopXQQ8styZYgYERl6ETePNv4qMk5r+B3QftGw==", - "path": "microsoft.dotnet.genapi/5.0.0-beta.19552.1", - "hashPath": "microsoft.dotnet.genapi.5.0.0-beta.19552.1.nupkg.sha512" - }, - "Microsoft.NetCore.Analyzers/2.6.2": { - "type": "package", - "serviceable": true, - "sha512": "sha512-HUfJMvzy4xJ6b4X99NGer2Pxn5On/WU/JscTzxKODg9srhJdtlWNhdorbejl2igrBi7RX6ufBhfvp8aBAEMy1A==", - "path": "microsoft.netcore.analyzers/2.6.2", - "hashPath": "microsoft.netcore.analyzers.2.6.2.nupkg.sha512" - }, - "Microsoft.NETCore.Platforms/1.1.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==", - "path": "microsoft.netcore.platforms/1.1.0", - "hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512" - }, - "Microsoft.NetFramework.Analyzers/2.6.2": { - "type": "package", - "serviceable": true, - "sha512": "sha512-HdNe8zptYp3bl7VDPTXDzeKMqkQva8m9mUI0C1HFN2iiMsMGnV3gqOeDQenH6NCTgSwYRnokHFsvcOeEODMd0g==", - "path": "microsoft.netframework.analyzers/2.6.2", - "hashPath": "microsoft.netframework.analyzers.2.6.2.nupkg.sha512" - }, - "Microsoft.SourceLink.Common/1.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-G8DuQY8/DK5NN+3jm5wcMcd9QYD90UV7MiLmdljSJixi3U/vNaeBKmmXUqI4DJCOeWizIUEh4ALhSt58mR+5eg==", - "path": "microsoft.sourcelink.common/1.0.0", - "hashPath": "microsoft.sourcelink.common.1.0.0.nupkg.sha512" - }, - "Microsoft.SourceLink.GitHub/1.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-aZyGyGg2nFSxix+xMkPmlmZSsnGQ3w+mIG23LTxJZHN+GPwTQ5FpPgDo7RMOq+Kcf5D4hFWfXkGhoGstawX13Q==", - "path": "microsoft.sourcelink.github/1.0.0", - "hashPath": "microsoft.sourcelink.github.1.0.0.nupkg.sha512" - }, - "NETStandard.Library/2.0.3": { - "type": "package", - "serviceable": true, - "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "path": "netstandard.library/2.0.3", - "hashPath": "netstandard.library.2.0.3.nupkg.sha512" - }, - "StyleCop.Analyzers/1.1.118": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Onx6ovGSqXSK07n/0eM3ZusiNdB6cIlJdabQhWGgJp3Vooy9AaLS/tigeybOJAobqbtggTamoWndz72JscZBvw==", - "path": "stylecop.analyzers/1.1.118", - "hashPath": "stylecop.analyzers.1.1.118.nupkg.sha512" - }, - "System.Buffers/4.5.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A==", - "path": "system.buffers/4.5.0", - "hashPath": "system.buffers.4.5.0.nupkg.sha512" - }, - "System.Diagnostics.DiagnosticSource/4.6.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-mbBgoR0rRfl2uimsZ2avZY8g7Xnh1Mza0rJZLPcxqiMWlkGukjmRkuMJ/er+AhQuiRIh80CR/Hpeztr80seV5g==", - "path": "system.diagnostics.diagnosticsource/4.6.0", - "hashPath": "system.diagnostics.diagnosticsource.4.6.0.nupkg.sha512" - }, - "System.Memory/4.5.3": { - "type": "package", - "serviceable": true, - "sha512": "sha512-3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", - "path": "system.memory/4.5.3", - "hashPath": "system.memory.4.5.3.nupkg.sha512" - }, - "System.Numerics.Vectors/4.5.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==", - "path": "system.numerics.vectors/4.5.0", - "hashPath": "system.numerics.vectors.4.5.0.nupkg.sha512" - }, - "System.Runtime.CompilerServices.Unsafe/4.6.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-HxozeSlipUK7dAroTYwIcGwKDeOVpQnJlpVaOkBz7CM4TsE5b/tKlQBZecTjh6FzcSbxndYaxxpsBMz+wMJeyw==", - "path": "system.runtime.compilerservices.unsafe/4.6.0", - "hashPath": "system.runtime.compilerservices.unsafe.4.6.0.nupkg.sha512" - }, - "System.Text.Encodings.Web/4.6.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-BXgFO8Yi7ao7hVA/nklD0Hre1Bbce048ZqryGZVFifGNPuh+2jqF1i/jLJLMfFGZIzUOw+nCIeH24SQhghDSPw==", - "path": "system.text.encodings.web/4.6.0", - "hashPath": "system.text.encodings.web.4.6.0.nupkg.sha512" - }, - "System.Text.Json/4.6.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-4F8Xe+JIkVoDJ8hDAZ7HqLkjctN/6WItJIzQaifBwClC7wmoLSda/Sv2i6i1kycqDb3hWF4JCVbpAweyOKHEUA==", - "path": "system.text.json/4.6.0", - "hashPath": "system.text.json.4.6.0.nupkg.sha512" - }, - "System.Threading.Tasks.Extensions/4.5.2": { - "type": "package", - "serviceable": true, - "sha512": "sha512-BG/TNxDFv0svAzx8OiMXDlsHfGw623BZ8tCXw4YLhDFDvDhNUEV58jKYMGRnkbJNm7c3JNNJDiN7JBMzxRBR2w==", - "path": "system.threading.tasks.extensions/4.5.2", - "hashPath": "system.threading.tasks.extensions.4.5.2.nupkg.sha512" - }, - "Text.Analyzers/2.6.2": { - "type": "package", - "serviceable": true, - "sha512": "sha512-UcSpZtr7TXPWj59F9WGZd7Edcm+ppBW4OeA/VuqX0RUJ6t/qgCqPfbRAJ/WbT5hcMHaI2pOn7kMbG5gOayravQ==", - "path": "text.analyzers/2.6.2", - "hashPath": "text.analyzers.2.6.2.nupkg.sha512" - } - } -} \ No newline at end of file diff --git a/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.dll b/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.dll deleted file mode 100644 index 0d552971f9190386d80959f546d607206e1923cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 254976 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 YT7j@YbYQwJ=dzQTwd=0A{- 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 A*#H0l diff --git a/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.xml b/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.xml deleted file mode 100644 index 5bc60e4e6cada..0000000000000 --- a/sdk/resourcemanager/Proto.Client/authorization/lib/Azure.ResourceManager.Authorization.xml +++ /dev/null @@ -1,1804 +0,0 @@ - - - - Azure.ResourceManager.Authorization - - - - Authorization service management client. - - - Initializes a new instance of AuthorizationManagementClient for mocking. - - - Initializes a new instance of AuthorizationManagementClient. - The ID of the target subscription. - The OAuth token for making client requests. - The options for configuring the client. - - - Initializes a new instance of AuthorizationManagementClient. - The ID of the target subscription. - server parameter. - The OAuth token for making client requests. - The options for configuring the client. - is null. - - - Returns an instance of ClassicAdministratorsOperations. - - - Returns an instance of GlobalAdministratorOperations. - - - Returns an instance of ProviderOperationsMetadataOperations. - - - Returns an instance of RoleAssignmentsOperations. - - - Returns an instance of PermissionsOperations. - - - Returns an instance of RoleDefinitionsOperations. - - - Returns an instance of DenyAssignmentsOperations. - - - Client options for Authorization. - - - The ClassicAdministrators service client. - - - Initializes a new instance of ClassicAdministratorsOperations for mocking. - - - Initializes a new instance of ClassicAdministratorsOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - The ID of the target subscription. - server parameter. - - - Gets service administrator, account administrator, and co-administrators for the subscription. - The cancellation token to use. - - - Gets service administrator, account administrator, and co-administrators for the subscription. - The cancellation token to use. - - - Initializes a new instance of ClassicAdministratorsRestOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - The ID of the target subscription. - server parameter. - is null. - - - Gets service administrator, account administrator, and co-administrators for the subscription. - The cancellation token to use. - - - Gets service administrator, account administrator, and co-administrators for the subscription. - The cancellation token to use. - - - Gets service administrator, account administrator, and co-administrators for the subscription. - The URL to the next page of results. - The cancellation token to use. - is null. - - - Gets service administrator, account administrator, and co-administrators for the subscription. - The URL to the next page of results. - The cancellation token to use. - is null. - - - The DenyAssignments service client. - - - Initializes a new instance of DenyAssignmentsOperations for mocking. - - - Initializes a new instance of DenyAssignmentsOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - The ID of the target subscription. - server parameter. - - - Get the specified deny assignment. - The scope of the deny assignment. - The ID of the deny assignment to get. - The cancellation token to use. - - - Get the specified deny assignment. - The scope of the deny assignment. - The ID of the deny assignment to get. - The cancellation token to use. - - - Gets a deny assignment by ID. - The fully qualified deny assignment ID. For example, use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for subscription level deny assignments, or /providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for tenant level deny assignments. - The cancellation token to use. - - - Gets a deny assignment by ID. - The fully qualified deny assignment ID. For example, use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for subscription level deny assignments, or /providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for tenant level deny assignments. - The cancellation token to use. - - - Gets deny assignments for a resource. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get deny assignments for. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - , , , , or is null. - - - Gets deny assignments for a resource. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get deny assignments for. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - , , , , or is null. - - - Gets deny assignments for a resource group. - The name of the resource group. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - is null. - - - Gets deny assignments for a resource group. - The name of the resource group. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - is null. - - - Gets all deny assignments for the subscription. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - - - Gets all deny assignments for the subscription. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - - - Gets deny assignments for a scope. - The scope of the deny assignments. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - is null. - - - Gets deny assignments for a scope. - The scope of the deny assignments. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - is null. - - - Initializes a new instance of DenyAssignmentsRestOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - The ID of the target subscription. - server parameter. - is null. - - - Gets deny assignments for a resource. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get deny assignments for. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - , , , , or is null. - - - Gets deny assignments for a resource. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get deny assignments for. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - , , , , or is null. - - - Gets deny assignments for a resource group. - The name of the resource group. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - is null. - - - Gets deny assignments for a resource group. - The name of the resource group. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - is null. - - - Gets all deny assignments for the subscription. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - - - Gets all deny assignments for the subscription. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - - - Get the specified deny assignment. - The scope of the deny assignment. - The ID of the deny assignment to get. - The cancellation token to use. - or is null. - - - Get the specified deny assignment. - The scope of the deny assignment. - The ID of the deny assignment to get. - The cancellation token to use. - or is null. - - - Gets a deny assignment by ID. - The fully qualified deny assignment ID. For example, use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for subscription level deny assignments, or /providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for tenant level deny assignments. - The cancellation token to use. - is null. - - - Gets a deny assignment by ID. - The fully qualified deny assignment ID. For example, use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for subscription level deny assignments, or /providers/Microsoft.Authorization/denyAssignments/{denyAssignmentId} for tenant level deny assignments. - The cancellation token to use. - is null. - - - Gets deny assignments for a scope. - The scope of the deny assignments. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - is null. - - - Gets deny assignments for a scope. - The scope of the deny assignments. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - is null. - - - Gets deny assignments for a resource. - The URL to the next page of results. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get deny assignments for. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - , , , , , or is null. - - - Gets deny assignments for a resource. - The URL to the next page of results. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get deny assignments for. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - , , , , , or is null. - - - Gets deny assignments for a resource group. - The URL to the next page of results. - The name of the resource group. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - or is null. - - - Gets deny assignments for a resource group. - The URL to the next page of results. - The name of the resource group. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - or is null. - - - Gets all deny assignments for the subscription. - The URL to the next page of results. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - is null. - - - Gets all deny assignments for the subscription. - The URL to the next page of results. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - is null. - - - Gets deny assignments for a scope. - The URL to the next page of results. - The scope of the deny assignments. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - or is null. - - - Gets deny assignments for a scope. - The URL to the next page of results. - The scope of the deny assignments. - The filter to apply on the operation. Use $filter=atScope() to return all deny assignments at or above the scope. Use $filter=denyAssignmentName eq '{name}' to search deny assignments by name at specified scope. Use $filter=principalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. Use $filter=gdprExportPrincipalId eq '{id}' to return all deny assignments at, above and below the scope for the specified principal. This filter is different from the principalId filter as it returns not only those deny assignments that contain the specified principal is the Principals list but also those deny assignments that contain the specified principal is the ExcludePrincipals list. Additionally, when gdprExportPrincipalId filter is used, only the deny assignment name and description properties are returned. - The cancellation token to use. - or is null. - - - The GlobalAdministrator service client. - - - Initializes a new instance of GlobalAdministratorOperations for mocking. - - - Initializes a new instance of GlobalAdministratorOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - server parameter. - - - Elevates access for a Global Administrator. - The cancellation token to use. - - - Elevates access for a Global Administrator. - The cancellation token to use. - - - Initializes a new instance of GlobalAdministratorRestOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - server parameter. - - - Elevates access for a Global Administrator. - The cancellation token to use. - - - Elevates access for a Global Administrator. - The cancellation token to use. - - - Classic Administrators. - - - Initializes a new instance of ClassicAdministrator. - - - Initializes a new instance of ClassicAdministrator. - The ID of the administrator. - The name of the administrator. - The type of the administrator. - The email address of the administrator. - The role of the administrator. - - - The ID of the administrator. - - - The name of the administrator. - - - The type of the administrator. - - - The email address of the administrator. - - - The role of the administrator. - - - ClassicAdministrator list result information. - - - Initializes a new instance of ClassicAdministratorListResult. - - - Initializes a new instance of ClassicAdministratorListResult. - An array of administrators. - The URL to use for getting the next set of results. - - - An array of administrators. - - - The URL to use for getting the next set of results. - - - Deny Assignment. - - - Initializes a new instance of DenyAssignment. - - - Initializes a new instance of DenyAssignment. - The deny assignment ID. - The deny assignment name. - The deny assignment type. - The display name of the deny assignment. - The description of the deny assignment. - An array of permissions that are denied by the deny assignment. - The deny assignment scope. - Determines if the deny assignment applies to child scopes. Default value is false. - Array of principals to which the deny assignment applies. - Array of principals to which the deny assignment does not apply. - Specifies whether this deny assignment was created by Azure and cannot be edited or deleted. - - - The deny assignment ID. - - - The deny assignment name. - - - The deny assignment type. - - - The display name of the deny assignment. - - - The description of the deny assignment. - - - An array of permissions that are denied by the deny assignment. - - - The deny assignment scope. - - - Determines if the deny assignment applies to child scopes. Default value is false. - - - Array of principals to which the deny assignment applies. - - - Array of principals to which the deny assignment does not apply. - - - Specifies whether this deny assignment was created by Azure and cannot be edited or deleted. - - - Deny Assignments filter. - - - Initializes a new instance of DenyAssignmentFilter. - - - Return deny assignment with specified name. - - - Return all deny assignments where the specified principal is listed in the principals list of deny assignments. - - - Return all deny assignments where the specified principal is listed either in the principals list or exclude principals list of deny assignments. - - - Deny assignment list operation result. - - - Initializes a new instance of DenyAssignmentListResult. - - - Initializes a new instance of DenyAssignmentListResult. - Deny assignment list. - The URL to use for getting the next set of results. - - - Deny assignment list. - - - The URL to use for getting the next set of results. - - - Deny assignment permissions. - - - Initializes a new instance of DenyAssignmentPermission. - - - Initializes a new instance of DenyAssignmentPermission. - Actions to which the deny assignment does not grant access. - Actions to exclude from that the deny assignment does not grant access. - Data actions to which the deny assignment does not grant access. - Data actions to exclude from that the deny assignment does not grant access. - - - Actions to which the deny assignment does not grant access. - - - Actions to exclude from that the deny assignment does not grant access. - - - Data actions to which the deny assignment does not grant access. - - - Data actions to exclude from that the deny assignment does not grant access. - - - Role definition permissions. - - - Initializes a new instance of Permission. - - - Initializes a new instance of Permission. - Allowed actions. - Denied actions. - Allowed Data actions. - Denied Data actions. - - - Allowed actions. - - - Denied actions. - - - Allowed Data actions. - - - Denied Data actions. - - - Permissions information. - - - Initializes a new instance of PermissionGetResult. - - - Initializes a new instance of PermissionGetResult. - An array of permissions. - The URL to use for getting the next set of results. - - - An array of permissions. - - - The URL to use for getting the next set of results. - - - Deny assignment principal. - - - Initializes a new instance of Principal. - - - Initializes a new instance of Principal. - Object ID of the Azure AD principal (user, group, or service principal) to which the deny assignment applies. An empty guid '00000000-0000-0000-0000-000000000000' as principal id and principal type as 'Everyone' represents all users, groups and service principals. - Type of object represented by principal id (user, group, or service principal). An empty guid '00000000-0000-0000-0000-000000000000' as principal id and principal type as 'Everyone' represents all users, groups and service principals. - - - Object ID of the Azure AD principal (user, group, or service principal) to which the deny assignment applies. An empty guid '00000000-0000-0000-0000-000000000000' as principal id and principal type as 'Everyone' represents all users, groups and service principals. - - - Type of object represented by principal id (user, group, or service principal). An empty guid '00000000-0000-0000-0000-000000000000' as principal id and principal type as 'Everyone' represents all users, groups and service principals. - - - The principal type of the assigned principal ID. - - - Determines if two values are the same. - is null. - - - User. - - - Group. - - - ServicePrincipal. - - - Unknown. - - - DirectoryRoleTemplate. - - - ForeignGroup. - - - Application. - - - MSI. - - - DirectoryObjectOrGroup. - - - Everyone. - - - Determines if two values are the same. - - - Determines if two values are not the same. - - - Converts a string to a . - - - - - - - - - - - - - - - Operation. - - - Initializes a new instance of ProviderOperation. - - - Initializes a new instance of ProviderOperation. - The operation name. - The operation display name. - The operation description. - The operation origin. - The operation properties. - The dataAction flag to specify the operation type. - - - The operation name. - - - The operation display name. - - - The operation description. - - - The operation origin. - - - The operation properties. - - - The dataAction flag to specify the operation type. - - - Provider Operations metadata. - - - Initializes a new instance of ProviderOperationsMetadata. - - - Initializes a new instance of ProviderOperationsMetadata. - The provider id. - The provider name. - The provider type. - The provider display name. - The provider resource types. - The provider operations. - - - The provider id. - - - The provider name. - - - The provider type. - - - The provider display name. - - - The provider resource types. - - - The provider operations. - - - Provider operations metadata list. - - - Initializes a new instance of ProviderOperationsMetadataListResult. - - - Initializes a new instance of ProviderOperationsMetadataListResult. - The list of providers. - The URL to use for getting the next set of results. - - - The list of providers. - - - The URL to use for getting the next set of results. - - - Resource Type. - - - Initializes a new instance of ResourceType. - - - Initializes a new instance of ResourceType. - The resource type name. - The resource type display name. - The resource type operations. - - - The resource type name. - - - The resource type display name. - - - The resource type operations. - - - Role Assignments. - - - Initializes a new instance of RoleAssignment. - - - Initializes a new instance of RoleAssignment. - The role assignment ID. - The role assignment name. - The role assignment type. - The role assignment scope. - The role definition ID. - The principal ID. - The principal type of the assigned principal ID. - The Delegation flag for the role assignment. - - - The role assignment ID. - - - The role assignment name. - - - The role assignment type. - - - The role assignment scope. - - - The role definition ID. - - - The principal ID. - - - The principal type of the assigned principal ID. - - - The Delegation flag for the role assignment. - - - Role assignment create parameters. - - - Initializes a new instance of RoleAssignmentCreateParameters. - The role definition ID used in the role assignment. - The principal ID assigned to the role. This maps to the ID inside the Active Directory. It can point to a user, service principal, or security group. - or is null. - - - The role definition ID used in the role assignment. - - - The principal ID assigned to the role. This maps to the ID inside the Active Directory. It can point to a user, service principal, or security group. - - - The principal type of the assigned principal ID. - - - The delegation flag used for creating a role assignment. - - - Role Assignments filter. - - - Initializes a new instance of RoleAssignmentFilter. - - - Returns role assignment of the specific principal. - - - The Delegation flag for the role assignment. - - - Role assignment list operation result. - - - Initializes a new instance of RoleAssignmentListResult. - - - Initializes a new instance of RoleAssignmentListResult. - Role assignment list. - The URL to use for getting the next set of results. - - - Role assignment list. - - - The URL to use for getting the next set of results. - - - Role definition. - - - Initializes a new instance of RoleDefinition. - - - Initializes a new instance of RoleDefinition. - The role definition ID. - The role definition name. - The role definition type. - The role name. - The role definition description. - The role type. - Role definition permissions. - Role definition assignable scopes. - - - The role definition ID. - - - The role definition name. - - - The role definition type. - - - The role name. - - - The role definition description. - - - The role type. - - - Role definition permissions. - - - Role definition assignable scopes. - - - Role Definitions filter. - - - Initializes a new instance of RoleDefinitionFilter. - - - Returns role definition with the specific name. - - - Returns role definition with the specific type. - - - Role definition list operation result. - - - Initializes a new instance of RoleDefinitionListResult. - - - Initializes a new instance of RoleDefinitionListResult. - Role definition list. - The URL to use for getting the next set of results. - - - Role definition list. - - - The URL to use for getting the next set of results. - - - The Permissions service client. - - - Initializes a new instance of PermissionsOperations for mocking. - - - Initializes a new instance of PermissionsOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - The ID of the target subscription. - server parameter. - - - Gets all permissions the caller has for a resource group. - The name of the resource group. - The cancellation token to use. - is null. - - - Gets all permissions the caller has for a resource group. - The name of the resource group. - The cancellation token to use. - is null. - - - Gets all permissions the caller has for a resource. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get the permissions for. - The cancellation token to use. - , , , , or is null. - - - Gets all permissions the caller has for a resource. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get the permissions for. - The cancellation token to use. - , , , , or is null. - - - Initializes a new instance of PermissionsRestOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - The ID of the target subscription. - server parameter. - is null. - - - Gets all permissions the caller has for a resource group. - The name of the resource group. - The cancellation token to use. - is null. - - - Gets all permissions the caller has for a resource group. - The name of the resource group. - The cancellation token to use. - is null. - - - Gets all permissions the caller has for a resource. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get the permissions for. - The cancellation token to use. - , , , , or is null. - - - Gets all permissions the caller has for a resource. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get the permissions for. - The cancellation token to use. - , , , , or is null. - - - Gets all permissions the caller has for a resource group. - The URL to the next page of results. - The name of the resource group. - The cancellation token to use. - or is null. - - - Gets all permissions the caller has for a resource group. - The URL to the next page of results. - The name of the resource group. - The cancellation token to use. - or is null. - - - Gets all permissions the caller has for a resource. - The URL to the next page of results. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get the permissions for. - The cancellation token to use. - , , , , , or is null. - - - Gets all permissions the caller has for a resource. - The URL to the next page of results. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get the permissions for. - The cancellation token to use. - , , , , , or is null. - - - The ProviderOperationsMetadata service client. - - - Initializes a new instance of ProviderOperationsMetadataOperations for mocking. - - - Initializes a new instance of ProviderOperationsMetadataOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - server parameter. - - - Gets provider operations metadata for the specified resource provider. - The namespace of the resource provider. - Specifies whether to expand the values. - The cancellation token to use. - - - Gets provider operations metadata for the specified resource provider. - The namespace of the resource provider. - Specifies whether to expand the values. - The cancellation token to use. - - - Gets provider operations metadata for all resource providers. - Specifies whether to expand the values. - The cancellation token to use. - - - Gets provider operations metadata for all resource providers. - Specifies whether to expand the values. - The cancellation token to use. - - - Initializes a new instance of ProviderOperationsMetadataRestOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - server parameter. - - - Gets provider operations metadata for the specified resource provider. - The namespace of the resource provider. - Specifies whether to expand the values. - The cancellation token to use. - is null. - - - Gets provider operations metadata for the specified resource provider. - The namespace of the resource provider. - Specifies whether to expand the values. - The cancellation token to use. - is null. - - - Gets provider operations metadata for all resource providers. - Specifies whether to expand the values. - The cancellation token to use. - - - Gets provider operations metadata for all resource providers. - Specifies whether to expand the values. - The cancellation token to use. - - - Gets provider operations metadata for all resource providers. - The URL to the next page of results. - Specifies whether to expand the values. - The cancellation token to use. - is null. - - - Gets provider operations metadata for all resource providers. - The URL to the next page of results. - Specifies whether to expand the values. - The cancellation token to use. - is null. - - - The RoleAssignments service client. - - - Initializes a new instance of RoleAssignmentsOperations for mocking. - - - Initializes a new instance of RoleAssignmentsOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - The ID of the target subscription. - server parameter. - - - Deletes a role assignment. - The scope of the role assignment to delete. - The name of the role assignment to delete. - The cancellation token to use. - - - Deletes a role assignment. - The scope of the role assignment to delete. - The name of the role assignment to delete. - The cancellation token to use. - - - Creates a role assignment. - The scope of the role assignment to create. The scope can be any REST resource instance. For example, use '/subscriptions/{subscription-id}/' for a subscription, '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}' for a resource group, and '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/{resource-provider}/{resource-type}/{resource-name}' for a resource. - The name of the role assignment to create. It can be any valid GUID. - Parameters for the role assignment. - The cancellation token to use. - - - Creates a role assignment. - The scope of the role assignment to create. The scope can be any REST resource instance. For example, use '/subscriptions/{subscription-id}/' for a subscription, '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}' for a resource group, and '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/{resource-provider}/{resource-type}/{resource-name}' for a resource. - The name of the role assignment to create. It can be any valid GUID. - Parameters for the role assignment. - The cancellation token to use. - - - Get the specified role assignment. - The scope of the role assignment. - The name of the role assignment to get. - The cancellation token to use. - - - Get the specified role assignment. - The scope of the role assignment. - The name of the role assignment to get. - The cancellation token to use. - - - Deletes a role assignment. - The ID of the role assignment to delete. - The cancellation token to use. - - - Deletes a role assignment. - The ID of the role assignment to delete. - The cancellation token to use. - - - Creates a role assignment by ID. - The ID of the role assignment to create. - Parameters for the role assignment. - The cancellation token to use. - - - Creates a role assignment by ID. - The ID of the role assignment to create. - Parameters for the role assignment. - The cancellation token to use. - - - Gets a role assignment by ID. - The ID of the role assignment to get. - The cancellation token to use. - - - Gets a role assignment by ID. - The ID of the role assignment to get. - The cancellation token to use. - - - Gets role assignments for a resource. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get role assignments for. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - , , , , or is null. - - - Gets role assignments for a resource. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get role assignments for. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - , , , , or is null. - - - Gets role assignments for a resource group. - The name of the resource group. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - is null. - - - Gets role assignments for a resource group. - The name of the resource group. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - is null. - - - Gets all role assignments for the subscription. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - - - Gets all role assignments for the subscription. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - - - Gets role assignments for a scope. - The scope of the role assignments. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - is null. - - - Gets role assignments for a scope. - The scope of the role assignments. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - is null. - - - Initializes a new instance of RoleAssignmentsRestOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - The ID of the target subscription. - server parameter. - is null. - - - Gets role assignments for a resource. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get role assignments for. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - , , , , or is null. - - - Gets role assignments for a resource. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get role assignments for. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - , , , , or is null. - - - Gets role assignments for a resource group. - The name of the resource group. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - is null. - - - Gets role assignments for a resource group. - The name of the resource group. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - is null. - - - Deletes a role assignment. - The scope of the role assignment to delete. - The name of the role assignment to delete. - The cancellation token to use. - or is null. - - - Deletes a role assignment. - The scope of the role assignment to delete. - The name of the role assignment to delete. - The cancellation token to use. - or is null. - - - Creates a role assignment. - The scope of the role assignment to create. The scope can be any REST resource instance. For example, use '/subscriptions/{subscription-id}/' for a subscription, '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}' for a resource group, and '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/{resource-provider}/{resource-type}/{resource-name}' for a resource. - The name of the role assignment to create. It can be any valid GUID. - Parameters for the role assignment. - The cancellation token to use. - , , or is null. - - - Creates a role assignment. - The scope of the role assignment to create. The scope can be any REST resource instance. For example, use '/subscriptions/{subscription-id}/' for a subscription, '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}' for a resource group, and '/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}/providers/{resource-provider}/{resource-type}/{resource-name}' for a resource. - The name of the role assignment to create. It can be any valid GUID. - Parameters for the role assignment. - The cancellation token to use. - , , or is null. - - - Get the specified role assignment. - The scope of the role assignment. - The name of the role assignment to get. - The cancellation token to use. - or is null. - - - Get the specified role assignment. - The scope of the role assignment. - The name of the role assignment to get. - The cancellation token to use. - or is null. - - - Deletes a role assignment. - The ID of the role assignment to delete. - The cancellation token to use. - is null. - - - Deletes a role assignment. - The ID of the role assignment to delete. - The cancellation token to use. - is null. - - - Creates a role assignment by ID. - The ID of the role assignment to create. - Parameters for the role assignment. - The cancellation token to use. - or is null. - - - Creates a role assignment by ID. - The ID of the role assignment to create. - Parameters for the role assignment. - The cancellation token to use. - or is null. - - - Gets a role assignment by ID. - The ID of the role assignment to get. - The cancellation token to use. - is null. - - - Gets a role assignment by ID. - The ID of the role assignment to get. - The cancellation token to use. - is null. - - - Gets all role assignments for the subscription. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - - - Gets all role assignments for the subscription. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - - - Gets role assignments for a scope. - The scope of the role assignments. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - is null. - - - Gets role assignments for a scope. - The scope of the role assignments. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - is null. - - - Gets role assignments for a resource. - The URL to the next page of results. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get role assignments for. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - , , , , , or is null. - - - Gets role assignments for a resource. - The URL to the next page of results. - The name of the resource group. - The namespace of the resource provider. - The parent resource identity. - The resource type of the resource. - The name of the resource to get role assignments for. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - , , , , , or is null. - - - Gets role assignments for a resource group. - The URL to the next page of results. - The name of the resource group. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - or is null. - - - Gets role assignments for a resource group. - The URL to the next page of results. - The name of the resource group. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - or is null. - - - Gets all role assignments for the subscription. - The URL to the next page of results. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - is null. - - - Gets all role assignments for the subscription. - The URL to the next page of results. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - is null. - - - Gets role assignments for a scope. - The URL to the next page of results. - The scope of the role assignments. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - or is null. - - - Gets role assignments for a scope. - The URL to the next page of results. - The scope of the role assignments. - The filter to apply on the operation. Use $filter=atScope() to return all role assignments at or above the scope. Use $filter=principalId eq {id} to return all role assignments at, above or below the scope for the specified principal. - The cancellation token to use. - or is null. - - - The RoleDefinitions service client. - - - Initializes a new instance of RoleDefinitionsOperations for mocking. - - - Initializes a new instance of RoleDefinitionsOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - server parameter. - - - Deletes a role definition. - The scope of the role definition. - The ID of the role definition to delete. - The cancellation token to use. - - - Deletes a role definition. - The scope of the role definition. - The ID of the role definition to delete. - The cancellation token to use. - - - Get role definition by name (GUID). - The scope of the role definition. - The ID of the role definition. - The cancellation token to use. - - - Get role definition by name (GUID). - The scope of the role definition. - The ID of the role definition. - The cancellation token to use. - - - Creates or updates a role definition. - The scope of the role definition. - The ID of the role definition. - The values for the role definition. - The cancellation token to use. - - - Creates or updates a role definition. - The scope of the role definition. - The ID of the role definition. - The values for the role definition. - The cancellation token to use. - - - Gets a role definition by ID. - The fully qualified role definition ID. Use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for subscription level role definitions, or /providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for tenant level role definitions. - The cancellation token to use. - - - Gets a role definition by ID. - The fully qualified role definition ID. Use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for subscription level role definitions, or /providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for tenant level role definitions. - The cancellation token to use. - - - Get all role definitions that are applicable at scope and above. - The scope of the role definition. - The filter to apply on the operation. Use atScopeAndBelow filter to search below the given scope as well. - The cancellation token to use. - is null. - - - Get all role definitions that are applicable at scope and above. - The scope of the role definition. - The filter to apply on the operation. Use atScopeAndBelow filter to search below the given scope as well. - The cancellation token to use. - is null. - - - Initializes a new instance of RoleDefinitionsRestOperations. - The handler for diagnostic messaging in the client. - The HTTP pipeline for sending and receiving REST requests and responses. - server parameter. - - - Deletes a role definition. - The scope of the role definition. - The ID of the role definition to delete. - The cancellation token to use. - or is null. - - - Deletes a role definition. - The scope of the role definition. - The ID of the role definition to delete. - The cancellation token to use. - or is null. - - - Get role definition by name (GUID). - The scope of the role definition. - The ID of the role definition. - The cancellation token to use. - or is null. - - - Get role definition by name (GUID). - The scope of the role definition. - The ID of the role definition. - The cancellation token to use. - or is null. - - - Creates or updates a role definition. - The scope of the role definition. - The ID of the role definition. - The values for the role definition. - The cancellation token to use. - , , or is null. - - - Creates or updates a role definition. - The scope of the role definition. - The ID of the role definition. - The values for the role definition. - The cancellation token to use. - , , or is null. - - - Get all role definitions that are applicable at scope and above. - The scope of the role definition. - The filter to apply on the operation. Use atScopeAndBelow filter to search below the given scope as well. - The cancellation token to use. - is null. - - - Get all role definitions that are applicable at scope and above. - The scope of the role definition. - The filter to apply on the operation. Use atScopeAndBelow filter to search below the given scope as well. - The cancellation token to use. - is null. - - - Gets a role definition by ID. - The fully qualified role definition ID. Use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for subscription level role definitions, or /providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for tenant level role definitions. - The cancellation token to use. - is null. - - - Gets a role definition by ID. - The fully qualified role definition ID. Use the format, /subscriptions/{guid}/providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for subscription level role definitions, or /providers/Microsoft.Authorization/roleDefinitions/{roleDefinitionId} for tenant level role definitions. - The cancellation token to use. - is null. - - - Get all role definitions that are applicable at scope and above. - The URL to the next page of results. - The scope of the role definition. - The filter to apply on the operation. Use atScopeAndBelow filter to search below the given scope as well. - The cancellation token to use. - or is null. - - - Get all role definitions that are applicable at scope and above. - The URL to the next page of results. - The scope of the role definition. - The filter to apply on the operation. Use atScopeAndBelow filter to search below the given scope as well. - The cancellation token to use. - or is null. - - - - This implements the ARM scenarios for LROs. It is highly recommended to read the ARM spec prior to modifying this code: - https://github.com/Azure/azure-resource-manager-rpc/blob/master/v1.0/Addendum.md#asynchronous-operations - Other reference documents include: - https://github.com/Azure/autorest/blob/master/docs/extensions/readme.md#x-ms-long-running-operation - https://github.com/Azure/adx-documentation-pr/blob/master/sdks/LRO/LRO_AzureSDK.md - - The final result of the LRO. - - - - Gets or sets a coma separated list of additional model usage modes. Allowed values: model, error, intput, output. - - - - - Gets or sets a coma separated list of additional model serialization formats. - - - - - Represents a heap-based, array-backed output sink into which data can be written. - - - - - Creates an instance of an , in which data can be written to, - with the default initial capacity. - - - - - Creates an instance of an , in which data can be written to, - with an initial capacity specified. - - The minimum capacity with which to initialize the underlying buffer. - - Thrown when is not positive (i.e. less than or equal to 0). - - - - - Returns the data written to the underlying buffer so far, as a . - - - - - Returns the data written to the underlying buffer so far, as a . - - - - - Returns the amount of data written to the underlying buffer so far. - - - - - Returns the total amount of space within the underlying buffer. - - - - - Returns the amount of space available that can still be written into without forcing the underlying buffer to grow. - - - - - Clears the data written to the underlying buffer. - - - You must clear the before trying to re-use it. - - - - - Notifies that amount of data was written to the output /. - - - Thrown when is negative. - - - Thrown when attempting to advance past the end of the underlying buffer. - - - You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. - - - - - Returns a to write to that is at least the requested length (specified by ). - If no is provided (or it's equal to 0), some non-empty buffer is returned. - - - Thrown when is negative. - - - This will never return an empty . - - - There is no guarantee that successive calls will return the same buffer or the same-sized buffer. - - - You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. - - - - - Returns a to write to that is at least the requested length (specified by ). - If no is provided (or it's equal to 0), some non-empty buffer is returned. - - - Thrown when is negative. - - - This will never return an empty . - - - There is no guarantee that successive calls will return the same buffer or the same-sized buffer. - - - You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer. - - - - - HACK HACK HACK. Some runtime environments like Azure.Functions downgrade System.Diagnostic.DiagnosticSource package version causing method not found exceptions in customer apps - This type is a temporary workaround to avoid the issue. - - - - - Both and are defined as public structs so that foreach can use duck typing - to call and avoid heap memory allocation. - Please don't delete this method and don't make these types private. - - - - - - This attribute should be set on all client assemblies with value of one of the resource providers - from the https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/azure-services-resource-providers list. - - - - From 92d08aa4f38187ca133aba859bcbcbe3d387f628 Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Thu, 25 Feb 2021 16:14:23 -0800 Subject: [PATCH 12/18] Add changelog for azure.resourcemanager.authorization and update version to be beta --- .../Azure.ResourceManager.Authorization.csproj | 2 +- sdk/resourcemanager/Proto.Client/authorization/CHANGELOG.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 sdk/resourcemanager/Proto.Client/authorization/CHANGELOG.md diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Azure.ResourceManager.Authorization.csproj b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Azure.ResourceManager.Authorization.csproj index d7a65183fe972..00a1e88b84232 100644 --- a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Azure.ResourceManager.Authorization.csproj +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/Azure.ResourceManager.Authorization.csproj @@ -1,6 +1,6 @@  - 1.0.0-preview.1 + 1.0.0-beta.1 Azure.ResourceManager.Authorization Azure Resource Manager client SDK for Azure resource provider Microsoft.Authorization azure;management;arm;resource manager;authorization diff --git a/sdk/resourcemanager/Proto.Client/authorization/CHANGELOG.md b/sdk/resourcemanager/Proto.Client/authorization/CHANGELOG.md new file mode 100644 index 0000000000000..5c312e4ea9bea --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/authorization/CHANGELOG.md @@ -0,0 +1,5 @@ +# Release History + +## 1.0.0-beta.1 (Unreleased) + +-Initial checkin From 2cbde0cbcd844796d68f802fd05b1d83fae41c19 Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Thu, 25 Feb 2021 18:57:37 -0800 Subject: [PATCH 13/18] update typos in readme --- .../authorization/Azure.ResourceManager.Authorization/README.MD | 2 +- sdk/resourcemanager/Proto.Client/authorization/README.MD | 2 +- sdk/resourcemanager/Proto.Client/compute/README.MD | 2 +- sdk/resourcemanager/Proto.Client/network/README.MD | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/README.MD b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/README.MD index f48c165987686..b41873a90b179 100644 --- a/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/README.MD +++ b/sdk/resourcemanager/Proto.Client/authorization/Azure.ResourceManager.Authorization/README.MD @@ -1,3 +1,3 @@ # Proto Azure ResourceManager Authorization -Proto type version of Azure.ResourceManager.Authorization used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file +Prototype version of Azure.ResourceManager.Authorization used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file diff --git a/sdk/resourcemanager/Proto.Client/authorization/README.MD b/sdk/resourcemanager/Proto.Client/authorization/README.MD index 28c108283dd3d..dd11dbdd018a0 100644 --- a/sdk/resourcemanager/Proto.Client/authorization/README.MD +++ b/sdk/resourcemanager/Proto.Client/authorization/README.MD @@ -1,3 +1,3 @@ # Proto Authorization -Proto type version of Azure.ResourceManager.Authorization used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file +Prototype version of Azure.ResourceManager.Authorization used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file diff --git a/sdk/resourcemanager/Proto.Client/compute/README.MD b/sdk/resourcemanager/Proto.Client/compute/README.MD index 96824ba442876..c5d39b78c6da7 100644 --- a/sdk/resourcemanager/Proto.Client/compute/README.MD +++ b/sdk/resourcemanager/Proto.Client/compute/README.MD @@ -1,3 +1,3 @@ # Proto Compute -Proto type version of Azure.ResourceManager.Compute used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file +Prototype version of Azure.ResourceManager.Compute used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file diff --git a/sdk/resourcemanager/Proto.Client/network/README.MD b/sdk/resourcemanager/Proto.Client/network/README.MD index 3a3688b6e19b1..71ceaad3b35c1 100644 --- a/sdk/resourcemanager/Proto.Client/network/README.MD +++ b/sdk/resourcemanager/Proto.Client/network/README.MD @@ -1,3 +1,3 @@ # Proto Network -Proto type version of Azure.ResourceManager.Network used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file +Prototype version of Azure.ResourceManager.Network used to showcase and test the new prototype track 2 management plane SDK \ No newline at end of file From 6b28c3370c5f030bfd9b0c8d2c37824b8202c5a6 Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Thu, 25 Feb 2021 19:02:38 -0800 Subject: [PATCH 14/18] update name of proto client sln --- .../Proto.Client/{Proto-Client.sln => Proto.Client.sln} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sdk/resourcemanager/Proto.Client/{Proto-Client.sln => Proto.Client.sln} (100%) diff --git a/sdk/resourcemanager/Proto.Client/Proto-Client.sln b/sdk/resourcemanager/Proto.Client/Proto.Client.sln similarity index 100% rename from sdk/resourcemanager/Proto.Client/Proto-Client.sln rename to sdk/resourcemanager/Proto.Client/Proto.Client.sln From 98992733b50d8a96c0e628ce5184109eed7f6c6d Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Fri, 26 Feb 2021 15:10:19 -0800 Subject: [PATCH 15/18] merge in changes from proto repo this week --- .../src/GenericResource.cs | 3 + .../src/GenericResourceData.cs | 17 +- .../src/GenericResourceOperations.cs | 70 +++--- .../src/OperationsBase.cs | 2 +- .../src/ResourceContainerBase.cs | 15 ++ .../src/ResourceGroupContainer.cs | 30 ++- .../src/ResourceGroupOperations.cs | 228 ++++++++++++------ .../src/ResourceOperationsBase.cs | 65 +++-- .../src/Utils/UtilityExtensions.cs | 31 +++ .../tests/IdentityTests.cs | 26 +- .../tests/ResourceListOperationsTest.cs | 17 +- .../compute/AvailabilitySetContainer.cs | 18 +- .../compute/AvailabilitySetOperations.cs | 68 +++--- .../compute/Extensions/ArmClientExtensions.cs | 18 +- .../Extensions/ResourceGroupExtensions.cs | 7 + .../compute/VirtualMachineContainer.cs | 15 +- .../compute/VirtualMachineOperations.cs | 73 +++--- .../network/Extensions/ArmClientExtensions.cs | 44 ++-- .../Extensions/ResourceGroupExtensions.cs | 13 + .../network/NetworkInterfaceContainer.cs | 25 +- .../network/NetworkInterfaceOperations.cs | 63 +++-- .../network/NetworkSecurityGroupContainer.cs | 19 +- .../network/NetworkSecurityGroupOperations.cs | 60 +++-- .../network/PublicIpAddressContainer.cs | 18 +- .../network/PublicIpAddressOperations.cs | 69 +++--- .../Proto.Client/network/SubnetContainer.cs | 23 +- .../network/VirtualNetworkContainer.cs | 22 +- .../network/VirtualNetworkOperations.cs | 69 +++--- .../Proto.Client/src/Program.cs | 2 +- .../Proto.Client/src/ScenarioFactory.cs | 3 + .../Scenarios/CheckResourceGroupOpsAsync.cs | 144 +++++++++++ .../src/Scenarios/GetByContainerAsync.cs | 45 ++++ .../src/Scenarios/GetByContainers.cs | 40 +++ 33 files changed, 912 insertions(+), 450 deletions(-) create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Utils/UtilityExtensions.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/CheckResourceGroupOpsAsync.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/GetByContainerAsync.cs create mode 100644 sdk/resourcemanager/Proto.Client/src/Scenarios/GetByContainers.cs diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResource.cs index 3c09153daffdb..9a18087929a64 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResource.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResource.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.Threading.Tasks; +using Azure.Core; namespace Azure.ResourceManager.Core { @@ -15,6 +17,7 @@ public class GenericResource : GenericResourceOperations /// /// The operations object to copy the client parameters from. /// The data model representing the generic azure resource. + /// If or is null. internal GenericResource(ResourceOperationsBase operations, GenericResourceData resource) : base(operations, resource.Id) { diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceData.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceData.cs index 560b5649eb094..84f62ecfee69e 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceData.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceData.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Azure.ResourceManager.Resources.Models; using System; using System.Collections.Generic; @@ -16,15 +15,17 @@ public class GenericResourceData : TrackedResource class. /// /// The existing resource model to copy from. + /// + /// is null. + /// public GenericResourceData(ResourceManager.Resources.Models.GenericResource genericResource) : base(genericResource.Id, genericResource.Location, genericResource) { + if (genericResource is null) + throw new ArgumentNullException(nameof(genericResource)); + Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - Tags.Clear(); - foreach (var tag in genericResource.Tags) - { - Tags.Add(tag); - } + Tags.ReplaceWith(genericResource.Tags); if (Model.Sku != null) Sku = new Sku(Model.Sku); @@ -89,6 +90,10 @@ public GenericResourceData(ResourceIdentifier id, LocationData location) /// The tracked resource convert from. public static implicit operator ResourceManager.Resources.Models.GenericResource(GenericResourceData other) { + if (other is null) + return null; + + // Temp code. Following block will be removed other.Model.Tags.Clear(); foreach (var tag in other.Tags) { diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceOperations.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceOperations.cs index 317baddd8be2a..ba5051ee88dbd 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceOperations.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/GenericResourceOperations.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Azure.Core.Pipeline; -using Azure.ResourceManager.Resources; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Azure.Core.Pipeline; +using Azure.ResourceManager.Resources; namespace Azure.ResourceManager.Core { @@ -88,35 +88,44 @@ public async Task> StartDeleteAsync(CancellationToken can return new ArmVoidOperation(operation); } - /// - /// Add a tag to the resource - /// - /// The tag key. - /// The tag value. - /// An that allows the user to control polling and waiting for Tag completion. + /// + public ArmResponse AddTag(string key, string value) + { + GenericResource resource = GetResource(); + + // Potential optimization on tags set, remove NOOP to bypass the call. + resource.Data.Tags[key] = value; + return new PhArmResponse( + Operations.StartUpdateById(Id, _apiVersion, resource.Data).WaitForCompletionAsync().EnsureCompleted(), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// + public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + GenericResource resource = GetResource(); + resource.Data.Tags[key] = value; + var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false); + return new PhArmResponse( + await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), + v => new GenericResource(this, new GenericResourceData(v))); + } + + /// public ArmOperation StartAddTag(string key, string value) { GenericResource resource = GetResource(); - UpdateTags(key, value, resource.Data.Tags); + resource.Data.Tags[key] = value; return new PhArmOperation( Operations.StartUpdateById(Id, _apiVersion, resource.Data).WaitForCompletionAsync().EnsureCompleted(), v => new GenericResource(this, new GenericResourceData(v))); } - /// - /// Add a tag to the resource - /// - /// The tag key. - /// The tag value. - /// A token to allow the caller to cancel the call to the service. - /// The default value is . - /// A that performs the Tag operation. The Task yields an an - /// that allows the user to control polling and waiting for - /// Tag completion. + /// public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) { GenericResource resource = GetResource(); - UpdateTags(key, value, resource.Data.Tags); + resource.Data.Tags[key] = value; var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false); return new PhArmOperation( await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), @@ -142,13 +151,18 @@ await Operations.GetByIdAsync(Id, _apiVersion, cancellationToken).ConfigureAwait /// protected override void Validate(ResourceIdentifier identifier) { + // TODO: Reenable after Azure.ResourceManager.Resource model has been regenerated + // Currently test cases uses GenericResourceExpended that does not allow construction + // with id. + // if (identifier is null) + // throw new ArgumentNullException(nameof(identifier)); } /// public ArmResponse SetTags(IDictionary tags) { GenericResource resource = GetResource(); - ReplaceTags(tags, resource.Data.Tags); + resource.Data.Tags.ReplaceWith(tags); return new PhArmResponse( Operations.StartUpdateById(Id, _apiVersion, resource.Data).WaitForCompletionAsync().EnsureCompleted(), v => new GenericResource(this, new GenericResourceData(v))); @@ -158,7 +172,7 @@ public ArmResponse SetTags(IDictionary tags) public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { GenericResource resource = GetResource(); - ReplaceTags(tags, resource.Data.Tags); + resource.Data.Tags.ReplaceWith(tags); var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false); return new PhArmResponse( await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), @@ -169,7 +183,7 @@ await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), public ArmOperation StartSetTags(IDictionary tags) { GenericResource resource = GetResource(); - ReplaceTags(tags, resource.Data.Tags); + resource.Data.Tags.ReplaceWith(tags); return new PhArmOperation( Operations.StartUpdateById(Id, _apiVersion, resource.Data).WaitForCompletionAsync().EnsureCompleted(), v => new GenericResource(this, new GenericResourceData(v))); @@ -179,7 +193,7 @@ public ArmOperation StartSetTags(IDictionary ta public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { GenericResource resource = GetResource(); - ReplaceTags(tags, resource.Data.Tags); + resource.Data.Tags.ReplaceWith(tags); var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false); return new PhArmOperation( await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), @@ -190,7 +204,7 @@ await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), public ArmResponse RemoveTag(string key) { GenericResource resource = GetResource(); - DeleteTag(key, resource.Data.Tags); + resource.Data.Tags.Remove(key); return new PhArmResponse( Operations.StartUpdateById(Id, _apiVersion, resource.Data).WaitForCompletionAsync().EnsureCompleted(), v => new GenericResource(this, new GenericResourceData(v))); @@ -200,7 +214,7 @@ public ArmResponse RemoveTag(string key) public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) { GenericResource resource = GetResource(); - DeleteTag(key, resource.Data.Tags); + resource.Data.Tags.Remove(key); var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false); return new PhArmResponse( await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), @@ -211,7 +225,7 @@ await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), public ArmOperation StartRemoveTag(string key) { GenericResource resource = GetResource(); - DeleteTag(key, resource.Data.Tags); + resource.Data.Tags.Remove(key); return new PhArmOperation( Operations.StartUpdateById(Id, _apiVersion, resource.Data).WaitForCompletionAsync().EnsureCompleted(), v => new GenericResource(this, new GenericResourceData(v))); @@ -221,7 +235,7 @@ public ArmOperation StartRemoveTag(string key) public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) { GenericResource resource = GetResource(); - DeleteTag(key, resource.Data.Tags); + resource.Data.Tags.Remove(key); var op = await Operations.StartUpdateByIdAsync(Id, _apiVersion, resource.Data, cancellationToken).ConfigureAwait(false); return new PhArmOperation( await op.WaitForCompletionAsync(cancellationToken).ConfigureAwait(false), diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/OperationsBase.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/OperationsBase.cs index 526cd64b344bd..4786be3aa5641 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/OperationsBase.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/OperationsBase.cs @@ -67,7 +67,7 @@ protected OperationsBase(AzureResourceManagerClientOptions options, ResourceIden protected virtual void Validate(ResourceIdentifier identifier) { if (identifier?.Type != ValidResourceType) - throw new InvalidOperationException($"Invalid resource type {identifier?.Type} expected {ValidResourceType}"); + throw new ArgumentException($"Invalid resource type {identifier?.Type} expected {ValidResourceType}"); } } } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceContainerBase.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceContainerBase.cs index 97ca5518a14bc..46a89fa6a2b98 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceContainerBase.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceContainerBase.cs @@ -118,5 +118,20 @@ protected TParent GetParentResource() return _parentResource as TParent; } + + /// + /// Gets details for this resource from the service. + /// + /// The name of the resource to get. + /// A response with the operation for this resource. + public abstract ArmResponse Get(string resourceName); + + /// + /// Gets details for this resource from the service. + /// + /// The name of the resource to get. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + public abstract Task> GetAsync(string resourceName, CancellationToken cancellationToken = default); } } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupContainer.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupContainer.cs index cce890cff5766..8374a6c812561 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupContainer.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupContainer.cs @@ -1,11 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Azure.ResourceManager.Core.Adapters; -using Azure.ResourceManager.Resources; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Azure.ResourceManager.Resources; namespace Azure.ResourceManager.Core { @@ -43,12 +42,7 @@ public ArmBuilder Construct(LocationData locat { var model = new ResourceManager.Resources.Models.ResourceGroup(location); if (!(tags is null)) - { - foreach (var tag in tags) - { - model.Tags.Add(tag); - } - } + model.Tags.ReplaceWith(tags); model.ManagedBy = managedBy; return new ArmBuilder(this, new ResourceGroupData(model)); } @@ -110,5 +104,25 @@ public AsyncPageable ListAsync(CancellationToken cancellationToke Operations.ListAsync(null, null, cancellationToken), s => new ResourceGroup(Parent, new ResourceGroupData(s))); } + + /// + public override ArmResponse Get(string resourceGroupName) + { + return new PhArmResponse(Operations.Get(resourceGroupName), g => + { + return new ResourceGroup(Parent, new ResourceGroupData(g)); + }); + } + + /// + public override async Task> GetAsync(string resourceGroupName, CancellationToken cancellationToken = default) + { + return new PhArmResponse( + await Operations.GetAsync(resourceGroupName, cancellationToken).ConfigureAwait(false), + g => + { + return new ResourceGroup(Parent, new ResourceGroupData(g)); + }); + } } } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupOperations.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupOperations.cs index 0f8b59872b20d..295abe3e07362 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupOperations.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceGroupOperations.cs @@ -1,15 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Azure.Core.Pipeline; -using Azure.ResourceManager.Resources; -using Azure.ResourceManager.Resources.Models; using System; using System.Collections.Generic; -using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Azure.Core.Pipeline; +using Azure.ResourceManager.Resources; +using Azure.ResourceManager.Resources.Models; namespace Azure.ResourceManager.Core { @@ -33,7 +32,7 @@ internal ResourceGroupOperations(SubscriptionOperations options, string rgName) : base(options, $"{options.Id}/resourceGroups/{rgName}") { if (rgName.Length > 90) - throw new ArgumentOutOfRangeException($"{nameof(rgName)} cannot be longer than 90 characters."); + throw new ArgumentOutOfRangeException(nameof(rgName), "ResourceGroupName cannot be longer than 90 characters."); if (!ValidationPattern.IsMatch(rgName)) throw new ArgumentException("The name of the resource group can include alphanumeric, underscore, parentheses, hyphen, period (except at end), and Unicode characters that match the allowed characters."); @@ -117,7 +116,32 @@ public override ArmResponse Get() /// public override async Task> GetAsync(CancellationToken cancellationToken = default) { - return new PhArmResponse(await Operations.GetAsync(Id.Name, cancellationToken).ConfigureAwait(false), g => + return new PhArmResponse( + await Operations.GetAsync(Id.Name, cancellationToken).ConfigureAwait(false), + g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + /// Add a tag to a ResourceGroup. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. + /// The value for the tag. + /// A response with the operation for this resource. + /// Key cannot be null or a whitespace. + public ArmResponse AddTag(string key, string value) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentException($"{nameof(key)} provided cannot be null or a whitespace.", nameof(key)); + + var resource = GetResource(); + var patch = new ResourceGroupPatchable(); + patch.Tags.ReplaceWith(resource.Data.Tags); + patch.Tags[key] = value; + return new PhArmResponse(Operations.Update(Id.Name, patch), g => { return new ResourceGroup(this, new ResourceGroupData(g)); }); @@ -127,17 +151,47 @@ public override async Task> GetAsync(CancellationToke /// Add a tag to a ResourceGroup. /// If the tag already exists it will be modified. /// - /// The key for the tag. + /// The key for the tag. + /// The value for the tag. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A that on completion returns a response with the operation for this resource. + /// Key cannot be null or a whitespace. + public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentException($"{nameof(key)} provided cannot be null or a whitespace.", nameof(key)); + + var resource = GetResource(); + var patch = new ResourceGroupPatchable(); + patch.Tags.ReplaceWith(resource.Data.Tags); + patch.Tags[key] = value; + return new PhArmResponse( + await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); + } + + /// + /// Add a tag to a ResourceGroup. + /// If the tag already exists it will be modified. + /// + /// The key for the tag. /// The value for the tag. /// A response with the operation for this resource. /// /// Details on long running operation object. /// - public ArmOperation StartAddTag(string name, string value) + /// Key cannot be null or a whitespace. + public ArmOperation StartAddTag(string key, string value) { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentException($"{nameof(key)} provided cannot be null or a whitespace.", nameof(key)); + var resource = GetResource(); var patch = new ResourceGroupPatchable(); - patch.Tags[name] = value; + patch.Tags.ReplaceWith(resource.Data.Tags); + patch.Tags[key] = value; return new PhArmOperation(Operations.Update(Id.Name, patch), g => { return new ResourceGroup(this, new ResourceGroupData(g)); @@ -148,22 +202,29 @@ public ArmOperation StartAddTag(string name, string value) /// Add a tag to a ResourceGroup. /// If the tag already exists it will be modified. /// - /// The key for the tag. + /// The key for the tag. /// The value for the tag. /// A token to allow the caller to cancel the call to the service. The default value is . /// /// A that on completion returns a response with the operation for this resource. /// /// Details on long running operation object. /// - public async Task> StartAddTagAsync(string name, string value, CancellationToken cancellationToken = default) + /// Key cannot be null or a whitespace. + public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentException($"{nameof(key)} provided cannot be null or a whitespace.", nameof(key)); + var resource = GetResource(); var patch = new ResourceGroupPatchable(); - patch.Tags[name] = value; - return new PhArmOperation(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => - { - return new ResourceGroup(this, new ResourceGroupData(g)); - }); + patch.Tags.ReplaceWith(resource.Data.Tags); + patch.Tags[key] = value; + return new PhArmOperation( + await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), + g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); } /// @@ -171,28 +232,23 @@ public async Task> StartAddTagAsync(string name, str /// /// A string representing the name of the resource />. /// The model representing the object to create. />. - /// A Location of where to to host the resource. />. /// The type of the class containing the container for the specific resource. /// The type of the operations class for a specific resource. /// The type of the class containing properties for the underlying resource. /// Returns a response with the operation for this resource. - public ArmResponse CreateResource(string name, TResource model, LocationData location = default) + /// Name cannot be null or a whitespace. + /// Model cannot be null. + public ArmResponse CreateResource(string name, TResource model) where TResource : TrackedResource where TOperations : ResourceOperationsBase where TContainer : ResourceContainerBase { - var myResource = model as TrackedResource; - - if (myResource == null) - { - myResource = new GenericResourceData(Id); - } - - if (location != null) - { - myResource = new GenericResourceData(Id, location); - } + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentException($"{nameof(name)} provided cannot be null or a whitespace.", nameof(name)); + if (model is null) + throw new ArgumentNullException(nameof(model)); + var myResource = model as TrackedResource; TContainer container = Activator.CreateInstance(typeof(TContainer), ClientOptions, myResource) as TContainer; return container.CreateOrUpdate(name, model); @@ -203,28 +259,24 @@ public ArmResponse CreateResource /// A string representing the name of the resource />. /// The model representing the object to create. />. - /// A Location of where to to host the resource. />. /// A token to allow the caller to cancel the call to the service. The default value is . /// The type of the class containing the container for the specific resource. /// The type of the operations class for a specific resource. /// The type of the class containing properties for the underlying resource. /// A that on completion returns a response with the operation for this resource. - public Task> CreateResourceAsync(string name, TResource model, LocationData location = default, CancellationToken cancellationToken = default) + /// Name cannot be null or a whitespace. + /// Model cannot be null. + public Task> CreateResourceAsync(string name, TResource model, CancellationToken cancellationToken = default) where TResource : TrackedResource where TOperations : ResourceOperationsBase where TContainer : ResourceContainerBase { - var myResource = model as TrackedResource; + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentException($"{nameof(name)} provided cannot be null or a whitespace.", nameof(name)); + if (model is null) + throw new ArgumentNullException(nameof(model)); - if (myResource == null) - { - myResource = new GenericResourceData(Id); - } - - if (location != null) - { - myResource = new GenericResourceData(Id, location); - } + var myResource = model as TrackedResource; TContainer container = Activator.CreateInstance(typeof(TContainer), ClientOptions, myResource) as TContainer; @@ -234,9 +286,12 @@ public Task> CreateResourceAsync public ArmResponse SetTags(IDictionary tags) { + if (tags == null) + throw new ArgumentNullException(nameof(tags)); + var resource = GetResource(); var patch = new ResourceGroupPatchable(); - ReplaceTags(tags, patch.Tags); + patch.Tags.ReplaceWith(tags); return new PhArmResponse(Operations.Update(Id.Name, patch), g => { return new ResourceGroup(this, new ResourceGroupData(g)); @@ -246,21 +301,29 @@ public ArmResponse SetTags(IDictionary tags) /// public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { + if (tags == null) + throw new ArgumentNullException(nameof(tags)); + var resource = GetResource(); var patch = new ResourceGroupPatchable(); - ReplaceTags(tags, patch.Tags); - return new PhArmResponse(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => - { - return new ResourceGroup(this, new ResourceGroupData(g)); - }); + patch.Tags.ReplaceWith(tags); + return new PhArmResponse( + await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), + g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); } /// public ArmOperation StartSetTags(IDictionary tags) { + if (tags == null) + throw new ArgumentNullException(nameof(tags)); + var resource = GetResource(); var patch = new ResourceGroupPatchable(); - ReplaceTags(tags, patch.Tags); + patch.Tags.ReplaceWith(tags); return new PhArmOperation(Operations.Update(Id.Name, patch), g => { return new ResourceGroup(this, new ResourceGroupData(g)); @@ -270,21 +333,30 @@ public ArmOperation StartSetTags(IDictionary tags /// public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { + if (tags == null) + throw new ArgumentNullException(nameof(tags)); + var resource = GetResource(); var patch = new ResourceGroupPatchable(); - ReplaceTags(tags, patch.Tags); - return new PhArmOperation(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => - { - return new ResourceGroup(this, new ResourceGroupData(g)); - }); + patch.Tags.ReplaceWith(tags); + return new PhArmOperation( + await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), + g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); } /// public ArmResponse RemoveTag(string key) { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentException($"{nameof(key)} provided cannot be null or a whitespace.", nameof(key)); + var resource = GetResource(); var patch = new ResourceGroupPatchable(); - DeleteTag(key, patch.Tags); + patch.Tags.ReplaceWith(resource.Data.Tags); + patch.Tags.Remove(key); return new PhArmResponse(Operations.Update(Id.Name, patch), g => { return new ResourceGroup(this, new ResourceGroupData(g)); @@ -294,21 +366,31 @@ public ArmResponse RemoveTag(string key) /// public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentException($"{nameof(key)} provided cannot be null or a whitespace.", nameof(key)); + var resource = GetResource(); var patch = new ResourceGroupPatchable(); - DeleteTag(key, patch.Tags); - return new PhArmResponse(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => - { - return new ResourceGroup(this, new ResourceGroupData(g)); - }); + patch.Tags.ReplaceWith(resource.Data.Tags); + patch.Tags.Remove(key); + return new PhArmResponse( + await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), + g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); } /// public ArmOperation StartRemoveTag(string key) { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentException($"{nameof(key)} provided cannot be null or a whitespace.", nameof(key)); + var resource = GetResource(); var patch = new ResourceGroupPatchable(); - DeleteTag(key, patch.Tags); + patch.Tags.ReplaceWith(resource.Data.Tags); + patch.Tags.Remove(key); return new PhArmOperation(Operations.Update(Id.Name, patch), g => { return new ResourceGroup(this, new ResourceGroupData(g)); @@ -318,13 +400,19 @@ public ArmOperation StartRemoveTag(string key) /// public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) { + if (string.IsNullOrWhiteSpace(key)) + throw new ArgumentException($"{nameof(key)} provided cannot be null or a whitespace.", nameof(key)); + var resource = GetResource(); var patch = new ResourceGroupPatchable(); - DeleteTag(key, patch.Tags); - return new PhArmOperation(await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), g => - { - return new ResourceGroup(this, new ResourceGroupData(g)); - }); + patch.Tags.ReplaceWith(resource.Data.Tags); + patch.Tags.Remove(key); + return new PhArmOperation( + await Operations.UpdateAsync(Id.Name, patch, cancellationToken).ConfigureAwait(false), + g => + { + return new ResourceGroup(this, new ResourceGroupData(g)); + }); } /// @@ -333,10 +421,7 @@ public async Task> StartRemoveTagAsync(string key, C /// A collection of location that may take multiple service requests to iterate over. public IEnumerable ListAvailableLocations() { - var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); - var rgProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var rgResource = rgProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return rgResource.Locations.Select(l => (LocationData)l); + return ListAvailableLocations(ResourceType); } /// @@ -347,10 +432,7 @@ public IEnumerable ListAvailableLocations() /// The default subscription id is null. public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) { - var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); - var rgProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase), cancellationToken).ConfigureAwait(false); - var rgResource = rgProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return rgResource.Locations.Select(l => (LocationData)l); + return await ListAvailableLocationsAsync(ResourceType, cancellationToken).ConfigureAwait(false); } } } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceOperationsBase.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceOperationsBase.cs index 7cae4c30b7636..2f0e7c1792b6e 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceOperationsBase.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ResourceOperationsBase.cs @@ -1,11 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Azure.Core; using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using Azure.Core; namespace Azure.ResourceManager.Core { @@ -117,48 +118,40 @@ protected virtual async Task GetResourceAsync() } /// - /// Gets new dictionary of tags after adding the key value pair or updating the existing key value pair - /// - /// The key to update. - /// The value to update. - /// Existing tag dictionary to update. - protected void UpdateTags(string key, string value, IDictionary existingTags) - { - if (existingTags.ContainsKey(key)) - { - existingTags[key] = value; - } - else - { - existingTags.Add(key, value); - } - } - - /// - /// Gets new dictionary of tags after remove the one key value pair + /// Lists all available geo-locations. /// - /// The key to remove. - /// Existing tag dictionary to update. - protected void DeleteTag(string key, IDictionary existingTags) + /// The instance to use for the list. + /// A collection of location that may take multiple service requests to iterate over. + protected IEnumerable ListAvailableLocations(ResourceType resourceType) { - if (existingTags.ContainsKey(key)) - { - existingTags.Remove(key); - } + var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); + var resourcePageableProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, resourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); + if (resourcePageableProvider is null) + throw new InvalidOperationException($"{resourceType.Type} not found for {resourceType.Namespace}"); + var theResource = resourcePageableProvider.ResourceTypes.FirstOrDefault(r => resourceType.Type.Equals(r.ResourceType)); + if (theResource is null) + throw new InvalidOperationException($"{resourceType.Type} not found for {resourceType.Type}"); + return theResource.Locations.Select(l => (LocationData)l); } /// - /// Replace all the tags currently on the resource + /// Lists all available geo-locations. /// - /// List of tags. - /// Existing tag dictionary to update. - protected void ReplaceTags(IDictionary tags, IDictionary existingTags) + /// The instance to use for the list. + /// A token to allow the caller to cancel the call to the service. The default value is . + /// A collection of location that may take multiple service requests to iterate over. + protected async Task> ListAvailableLocationsAsync(ResourceType resourceType, CancellationToken cancellationToken = default) { - existingTags.Clear(); - foreach (var tag in tags) - { - existingTags.Add(tag); - } + var pageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); + var resourcePageableProvider = await pageableProvider.FirstOrDefaultAsync( + p => string.Equals(p.Namespace, resourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase), + cancellationToken).ConfigureAwait(false); + if (resourcePageableProvider is null) + throw new InvalidOperationException($"{resourceType.Type} not found for {resourceType.Namespace}"); + var theResource = resourcePageableProvider.ResourceTypes.FirstOrDefault(r => resourceType.Type.Equals(r.ResourceType)); + if (theResource is null) + throw new InvalidOperationException($"{resourceType.Type} not found for {resourceType.Type}"); + return theResource.Locations.Select(l => (LocationData)l); } } } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Utils/UtilityExtensions.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Utils/UtilityExtensions.cs new file mode 100644 index 0000000000000..6da86bd8fd7d3 --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Utils/UtilityExtensions.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; + +namespace Azure.ResourceManager.Core +{ + /// + /// A class containing utility extensions + /// + public static class UtilityExtensions + { + /// + /// An extension method for supporting replacing one dictionary content with another one. + /// This is used to support resource tags. + /// + /// The destination dictionary in which the content will be replaced. + /// The source dictionary from which the content is copied from. + /// The destination dictionary that has been altered. + public static IDictionary ReplaceWith(this IDictionary dest, IDictionary src) + { + dest.Clear(); + foreach (var kv in src) + { + dest.Add(kv.Key, kv.Value); + } + + return dest; + } + } +} diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/IdentityTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/IdentityTests.cs index 051064fb1384d..64d712367076e 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/IdentityTests.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/IdentityTests.cs @@ -1,10 +1,9 @@ -using Azure.Core.TestFramework; -using NUnit.Framework; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; +using NUnit.Framework; namespace Azure.ResourceManager.Core.Tests { @@ -29,7 +28,10 @@ public void CheckUserTrueConstructor(string resourceID, bool invalidParameter) if (invalidParameter) { - Assert.Throws(() => { dict1[resourceID] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); }); + if (resourceID is null) + Assert.Throws(() => { dict1[resourceID] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); }); + else + Assert.Throws(() => { dict1[resourceID] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); }); } else { @@ -54,7 +56,10 @@ public void CheckUserFalseConstructor(string resourceID, bool invalidParameter) if(invalidParameter) { - Assert.Throws(() => { dict1[resourceID] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); }); + if (resourceID is null) + Assert.Throws(() => { dict1[resourceID] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); }); + else + Assert.Throws(() => { dict1[resourceID] = new UserAssignedIdentity(Guid.Empty, Guid.Empty); }); } else { @@ -148,22 +153,17 @@ public void TestDeserializerInvalidDefaultJson() public JsonProperty DeserializerHelper(string filename) { - string json = GetFileText(filename); + var json = File.ReadAllText("./TestAssets/Identity/" + filename); JsonDocument document = JsonDocument.Parse(json); JsonElement rootElement = document.RootElement; return rootElement.EnumerateObject().First(); } - private static string GetFileText(string filename) - { - return File.ReadAllText(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestAssets", "Identity", filename)); - } - [TestCase] public void TestDeserializerInvalidNullType() { var identityJsonProperty = DeserializerHelper("InvalidTypeIsNull.json"); - Assert.Throws(delegate { ResourceIdentity.Deserialize(identityJsonProperty.Value); }); + Assert.Throws(delegate { ResourceIdentity.Deserialize(identityJsonProperty.Value); }); } [TestCase] @@ -219,7 +219,7 @@ public void TestDeserializerValidMiddleExtraField() [TestCase] public void TestDeserializerValidOuterExtraField() { - var json = GetFileText("SystemAndUserAssignedOuterExtraField.json"); + var json = File.ReadAllText("./TestAssets/Identity/SystemAndUserAssignedOuterExtraField.json"); JsonDocument document = JsonDocument.Parse(json); JsonElement rootElement = document.RootElement; var identityJsonProperty = rootElement.EnumerateObject().ElementAt(1); diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceListOperationsTest.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceListOperationsTest.cs index 6e58d141ddea1..2794fbf7573f1 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceListOperationsTest.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceListOperationsTest.cs @@ -1,9 +1,9 @@ -using Azure.Identity; -using Azure.ResourceManager.Resources.Models; -using NUnit.Framework; using System; using System.Collections.Generic; using System.Reflection; +using Azure.Identity; +using Azure.ResourceManager.Resources.Models; +using NUnit.Framework; namespace Azure.ResourceManager.Core.Tests { @@ -122,14 +122,11 @@ private static GenericResourceExpanded GetGenereicResource( string location) { var resource = new GenericResourceExpanded(); + + // See TODO in GenericResourceOperations.Valide(). + // resource.Id = "/subscriptions/{subscription-id}/resourceGroups/myResourceGroup"; resource.Location = location; - if (!(tags is null)) - { - foreach (var tag in tags) - { - resource.Tags.Add(tag); - } - } + resource.Tags.ReplaceWith(tags ?? new Dictionary()); resource.Sku = sku; resource.Plan = plan; resource.Kind = kind; diff --git a/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetContainer.cs b/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetContainer.cs index 0d114a16431ad..11e9b95f5f0b1 100644 --- a/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetContainer.cs +++ b/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetContainer.cs @@ -1,7 +1,6 @@ using Azure; using Azure.ResourceManager.Compute; using Azure.ResourceManager.Core; -using Azure.ResourceManager.Core.Adapters; using Azure.ResourceManager.Core.Resources; using System.Threading; using System.Threading.Tasks; @@ -137,7 +136,22 @@ public AsyncPageable ListByNameExpandedAsync(string filter, int private AvailabilitySetsOperations Operations => new ComputeManagementClient( BaseUri, Id.Subscription, - Credential, + Credential, ClientOptions.Convert()).AvailabilitySets; + + + /// + public override ArmResponse Get(string availabilitySetName) + { + return new PhArmResponse(Operations.Get(Id.ResourceGroup, availabilitySetName), + g => new AvailabilitySet(Parent, new AvailabilitySetData(g))); + } + + /// + public override async Task> GetAsync(string availabilitySetName, CancellationToken cancellationToken = default) + { + return new PhArmResponse(await Operations.GetAsync(Id.ResourceGroup, availabilitySetName, cancellationToken), + g => new AvailabilitySet(Parent, new AvailabilitySetData(g))); + } } } diff --git a/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetOperations.cs b/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetOperations.cs index c4bc67d7d6180..bcbfa46697e26 100644 --- a/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetOperations.cs +++ b/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetOperations.cs @@ -4,8 +4,6 @@ using Azure.ResourceManager.Core; using System; using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -71,7 +69,7 @@ public ArmResponse Delete() /// /// The operation to delete an availability set. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// A that on completion returns a response with the operation for this resource. public async Task> DeleteAsync(CancellationToken cancellationToken = default) { @@ -81,7 +79,7 @@ public async Task> DeleteAsync(CancellationToken cancellat /// /// The operation to delete an availability set. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// /// Details on long running operation object. /// @@ -94,7 +92,7 @@ public ArmOperation StartDelete(CancellationToken cancellationToken = /// /// The operation to delete an availability set. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// /// Details on long running operation object. /// @@ -136,7 +134,7 @@ public ArmResponse Update(AvailabilitySetUpdate patchable) /// The operation to update an availability set. /// /// The parameters to update. - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// A that on completion returns the operation of the updated resource. public async Task> UpdateAsync(AvailabilitySetUpdate patchable, CancellationToken cancellationToken = default) { @@ -161,7 +159,7 @@ public ArmOperation StartUpdate(AvailabilitySetUpdate patchable /// The operation to update an availability set. /// /// The parameters to update. - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// A that on completion returns the operation of the updated resource. public async Task> StartUpdateAsync(AvailabilitySetUpdate patchable, CancellationToken cancellationToken = default) { @@ -191,7 +189,7 @@ public ArmResponse AddTag(string key, string value) /// /// The key for the tag. /// The value for the tag. - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// A that on completion returns an that allows polling for completion of the operation. public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) { @@ -225,7 +223,7 @@ public ArmOperation StartAddTag(string key, string value) /// /// The key for the tag. /// The value for the tag. - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// /// Details on long running operation object. /// @@ -241,36 +239,32 @@ public async Task> StartAddTagAsync(string key, st /// public ArmResponse SetTags(IDictionary tags) { - var resource = GetResource(); - var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; - ReplaceTags(tags, patchable.Tags); + var patchable = new AvailabilitySetUpdate(); + patchable.Tags.ReplaceWith(tags); return Update(patchable); } /// public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { - var resource = GetResource(); - var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; - ReplaceTags(tags, patchable.Tags); + var patchable = new AvailabilitySetUpdate(); + patchable.Tags.ReplaceWith(tags); return await UpdateAsync(patchable); } /// public ArmOperation StartSetTags(IDictionary tags) { - var resource = GetResource(); - var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; - ReplaceTags(tags, patchable.Tags); + var patchable = new AvailabilitySetUpdate(); + patchable.Tags.ReplaceWith(tags); return StartUpdate(patchable); } /// public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { - var resource = GetResource(); - var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; - ReplaceTags(tags, patchable.Tags); + var patchable = new AvailabilitySetUpdate(); + patchable.Tags.ReplaceWith(tags); return await StartUpdateAsync(patchable); } @@ -278,8 +272,9 @@ public async Task> StartSetTagsAsync(IDictionary RemoveTag(string key) { var resource = GetResource(); - var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; - DeleteTag(key, patchable.Tags); + var patchable = new AvailabilitySetUpdate(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return Update(patchable); } @@ -287,8 +282,9 @@ public ArmResponse RemoveTag(string key) public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; - DeleteTag(key, patchable.Tags); + var patchable = new AvailabilitySetUpdate(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return await UpdateAsync(patchable); } @@ -296,8 +292,9 @@ public async Task> RemoveTagAsync(string key, Cance public ArmOperation StartRemoveTag(string key) { var resource = GetResource(); - var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; - DeleteTag(key, patchable.Tags); + var patchable = new AvailabilitySetUpdate(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return StartUpdate(patchable); } @@ -305,8 +302,9 @@ public ArmOperation StartRemoveTag(string key) public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; - DeleteTag(key, patchable.Tags); + var patchable = new AvailabilitySetUpdate(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return await StartUpdateAsync(patchable); } @@ -316,24 +314,18 @@ public async Task> StartRemoveTagAsync(string key, /// A collection of location that may take multiple service requests to iterate over. public IEnumerable ListAvailableLocations() { - var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); - var availabilitySetProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var availabilitySetResource = availabilitySetProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return availabilitySetResource.Locations.Select(l => (LocationData)l); + return ListAvailableLocations(ResourceType); } /// /// Lists all available geo-locations. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// An async collection of location that may take multiple service requests to iterate over. /// The default subscription id is null. public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) { - var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); - var availabilitySetProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var availabilitySetResource = availabilitySetProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return availabilitySetResource.Locations.Select(l => (LocationData)l); + return await ListAvailableLocationsAsync(ResourceType, cancellationToken); } } } diff --git a/sdk/resourcemanager/Proto.Client/compute/Extensions/ArmClientExtensions.cs b/sdk/resourcemanager/Proto.Client/compute/Extensions/ArmClientExtensions.cs index a416f381c1c5e..1601ce928cc87 100644 --- a/sdk/resourcemanager/Proto.Client/compute/Extensions/ArmClientExtensions.cs +++ b/sdk/resourcemanager/Proto.Client/compute/Extensions/ArmClientExtensions.cs @@ -1,7 +1,5 @@ using Azure.ResourceManager.Core; using System; -using System.Collections.Generic; -using System.Text; namespace Proto.Compute { @@ -18,12 +16,14 @@ public static class ArmClientExtensions /// The ResourceIdentifier of the resource that is the target of operations. /// Returns an object representing the operations that can be performed over a specific . /// ResourceIdentifier provided is not for an AvailabilitySet. + /// ResourceIdentifier cannot be null. public static AvailabilitySetOperations GetAvailabilitySetOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) { + if (resourceId is null) + throw new ArgumentNullException(nameof(resourceId)); if (resourceId.Type != AvailabilitySetOperations.ResourceType) - { - throw new ArgumentException("ResourceIdentifier provided is not for an AvailabilitySet."); - } + throw new ArgumentException($"{nameof(resourceId.Type)} provided is not for an AvailabilitySet.", nameof(resourceId.Type)); + return client.GetSubscriptionOperations(resourceId.Subscription).GetResourceGroupOperations(resourceId.ResourceGroup).GetAvailabilitySetOperations(resourceId.Name); } @@ -34,12 +34,14 @@ public static AvailabilitySetOperations GetAvailabilitySetOperations(this AzureR /// The ResourceIdentifier of the resource that is the target of operations. /// Returns an object representing the operations that can be performed over a specific . /// ResourceIdentifier provided is not for a VirtualMachine. + /// ResourceIdentifier cannot be null. public static VirtualMachineOperations GetVirtualMachineOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) { + if (resourceId is null) + throw new ArgumentNullException(nameof(resourceId)); if (resourceId.Type != VirtualMachineOperations.ResourceType) - { - throw new ArgumentException("ResourceIdentifier provided is not for a VirtualMachine."); - } + throw new ArgumentException($"{nameof(resourceId.Type)} provided is not for a VirtualMachine.", nameof(resourceId.Type)); + return client.GetSubscriptionOperations(resourceId.Subscription).GetResourceGroupOperations(resourceId.ResourceGroup).GetVirtualMachineOperations(resourceId.Name); } } diff --git a/sdk/resourcemanager/Proto.Client/compute/Extensions/ResourceGroupExtensions.cs b/sdk/resourcemanager/Proto.Client/compute/Extensions/ResourceGroupExtensions.cs index 59099c393513b..1031a20918536 100644 --- a/sdk/resourcemanager/Proto.Client/compute/Extensions/ResourceGroupExtensions.cs +++ b/sdk/resourcemanager/Proto.Client/compute/Extensions/ResourceGroupExtensions.cs @@ -1,4 +1,5 @@ using Azure.ResourceManager.Core; +using System; namespace Proto.Compute { @@ -14,8 +15,11 @@ public static class ResourceGroupExtensions /// The instance the method will execute against. /// The name of the VirtualMachine. /// Returns an object representing the operations that can be performed over a specific . + /// vmName cannot be null or a whitespace. public static VirtualMachineOperations GetVirtualMachineOperations(this ResourceGroupOperations resourceGroup, string vmName) { + if (string.IsNullOrWhiteSpace(vmName)) + throw new ArgumentException($"{nameof(vmName)} cannot be null or a whitespace.", nameof(vmName)); return new VirtualMachineOperations(resourceGroup, vmName); } @@ -37,8 +41,11 @@ public static VirtualMachineContainer GetVirtualMachineContainer(this ResourceGr /// The instance the method will execute against. /// The name of the AvailibilitySet. /// Returns an object representing the operations that can be performed over a specific . + /// availabilitySetName cannot be null or a whitespace. public static AvailabilitySetOperations GetAvailabilitySetOperations(this ResourceGroupOperations resourceGroup, string availabilitySetName) { + if (string.IsNullOrWhiteSpace(availabilitySetName)) + throw new ArgumentException($"{nameof(availabilitySetName)} cannot be null or a whitespace.", nameof(availabilitySetName)); return new AvailabilitySetOperations(resourceGroup, availabilitySetName); } diff --git a/sdk/resourcemanager/Proto.Client/compute/VirtualMachineContainer.cs b/sdk/resourcemanager/Proto.Client/compute/VirtualMachineContainer.cs index 9dc5db0e1985d..3aecf4a36761a 100644 --- a/sdk/resourcemanager/Proto.Client/compute/VirtualMachineContainer.cs +++ b/sdk/resourcemanager/Proto.Client/compute/VirtualMachineContainer.cs @@ -2,7 +2,6 @@ using Azure.ResourceManager.Compute; using Azure.ResourceManager.Compute.Models; using Azure.ResourceManager.Core; -using Azure.ResourceManager.Core.Adapters; using Azure.ResourceManager.Core.Resources; using Proto.Compute.Convenience; using System.Collections.Generic; @@ -221,5 +220,19 @@ public AsyncPageable ListByNameExpandedAsync(string filter, int? var results = ListByNameAsync(filter, top, cancellationToken); return new PhWrappingAsyncPageable(results, s => (new VirtualMachineOperations(s)).Get().Value); } + + /// + public override ArmResponse Get(string virtualMachineName) + { + return new PhArmResponse(Operations.Get(Id.ResourceGroup, virtualMachineName), + v => new VirtualMachine(Parent, new VirtualMachineData(v))); + } + + /// + public override async Task> GetAsync(string virtualMachineName, CancellationToken cancellationToken = default) + { + return new PhArmResponse(await Operations.GetAsync(Id.ResourceGroup, virtualMachineName, cancellationToken), + v => new VirtualMachine(Parent, new VirtualMachineData(v))); + } } } diff --git a/sdk/resourcemanager/Proto.Client/compute/VirtualMachineOperations.cs b/sdk/resourcemanager/Proto.Client/compute/VirtualMachineOperations.cs index da686b9369118..4e0b39c17e938 100644 --- a/sdk/resourcemanager/Proto.Client/compute/VirtualMachineOperations.cs +++ b/sdk/resourcemanager/Proto.Client/compute/VirtualMachineOperations.cs @@ -2,11 +2,8 @@ using Azure.ResourceManager.Compute; using Azure.ResourceManager.Compute.Models; using Azure.ResourceManager.Core; -using Azure.ResourceManager.Core.Adapters; using System; using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -260,8 +257,9 @@ await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancella public ArmResponse AddTag(string key, string value) { var vm = GetResource(); - var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; - UpdateTags(key, value, patchable.Tags); + var patchable = new VirtualMachineUpdate(); + patchable.Tags.ReplaceWith(vm.Data.Tags); + patchable.Tags[key] = value; return new PhArmResponse( Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), @@ -279,8 +277,9 @@ public ArmResponse AddTag(string key, string value) public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) { var vm = await GetResourceAsync(); - var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; - UpdateTags(key, value, patchable.Tags); + var patchable = new VirtualMachineUpdate(); + patchable.Tags.ReplaceWith(vm.Data.Tags); + patchable.Tags[key] = value; return new PhArmResponse( await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken).Result.WaitForCompletionAsync(), @@ -297,8 +296,9 @@ await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancella public ArmOperation StartAddTag(string key, string value) { var vm = GetResource(); - var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; - UpdateTags(key, value, patchable.Tags); + var patchable = new VirtualMachineUpdate(); + patchable.Tags.ReplaceWith(vm.Data.Tags); + patchable.Tags[key] = value; return new PhArmOperation( Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable), @@ -316,8 +316,9 @@ public ArmOperation StartAddTag(string key, string value) public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) { var vm = await GetResourceAsync(); - var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; - UpdateTags(key, value, patchable.Tags); + var patchable = new VirtualMachineUpdate(); + patchable.Tags.ReplaceWith(vm.Data.Tags); + patchable.Tags[key] = value; return new PhArmOperation( await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), @@ -327,9 +328,8 @@ await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancella /// public ArmResponse SetTags(IDictionary tags) { - var vm = GetResource(); - var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; - ReplaceTags(tags, patchable.Tags); + var patchable = new VirtualMachineUpdate(); + patchable.Tags.ReplaceWith(tags); return new PhArmResponse( Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), @@ -339,9 +339,8 @@ public ArmResponse SetTags(IDictionary tags) /// public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { - var vm = await GetResourceAsync(); - var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; - ReplaceTags(tags, patchable.Tags); + var patchable = new VirtualMachineUpdate(); + patchable.Tags.ReplaceWith(tags); return new PhArmResponse( await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken).Result.WaitForCompletionAsync(), @@ -351,9 +350,8 @@ await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancella /// public ArmOperation StartSetTags(IDictionary tags) { - var vm = GetResource(); - var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; - ReplaceTags(tags, patchable.Tags); + var patchable = new VirtualMachineUpdate(); + patchable.Tags.ReplaceWith(tags); return new PhArmOperation( Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), @@ -363,9 +361,8 @@ public ArmOperation StartSetTags(IDictionary tag /// public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { - var vm = await GetResourceAsync(); - var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; - ReplaceTags(tags, patchable.Tags); + var patchable = new VirtualMachineUpdate(); + patchable.Tags.ReplaceWith(tags); return new PhArmOperation( await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken).Result.WaitForCompletionAsync(), @@ -376,8 +373,9 @@ await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancella public ArmResponse RemoveTag(string key) { var vm = GetResource(); - var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; - DeleteTag(key, patchable.Tags); + var patchable = new VirtualMachineUpdate(); + patchable.Tags.ReplaceWith(vm.Data.Tags); + patchable.Tags.Remove(key); return new PhArmResponse( Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), @@ -388,8 +386,9 @@ public ArmResponse RemoveTag(string key) public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) { var vm = await GetResourceAsync(); - var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; - DeleteTag(key, patchable.Tags); + var patchable = new VirtualMachineUpdate(); + patchable.Tags.ReplaceWith(vm.Data.Tags); + patchable.Tags.Remove(key); return new PhArmResponse( await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken).Result.WaitForCompletionAsync(), @@ -400,8 +399,9 @@ await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancella public ArmOperation StartRemoveTag(string key) { var vm = GetResource(); - var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; - DeleteTag(key, patchable.Tags); + var patchable = new VirtualMachineUpdate(); + patchable.Tags.ReplaceWith(vm.Data.Tags); + patchable.Tags.Remove(key); return new PhArmOperation( Operations.StartUpdate(Id.ResourceGroup, Id.Name, patchable).WaitForCompletionAsync().ConfigureAwait(false).GetAwaiter().GetResult(), @@ -412,8 +412,9 @@ public ArmOperation StartRemoveTag(string key) public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) { var vm = await GetResourceAsync(); - var patchable = new VirtualMachineUpdate { Tags = vm.Data.Tags }; - DeleteTag(key, patchable.Tags); + var patchable = new VirtualMachineUpdate(); + patchable.Tags.ReplaceWith(vm.Data.Tags); + patchable.Tags.Remove(key); return new PhArmOperation( await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken).Result.WaitForCompletionAsync(), @@ -426,10 +427,7 @@ await Operations.StartUpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancella /// A collection of location that may take multiple service requests to iterate over. public IEnumerable ListAvailableLocations() { - var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); - var vmProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var vmResource = vmProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return vmResource.Locations.Select(l => (LocationData)l); + return ListAvailableLocations(ResourceType); } /// @@ -440,10 +438,7 @@ public IEnumerable ListAvailableLocations() /// The default subscription id is null. public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) { - var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); - var vmProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var vmResource = vmProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return vmResource.Locations.Select(l => (LocationData)l); + return await ListAvailableLocationsAsync(ResourceType, cancellationToken); } } } diff --git a/sdk/resourcemanager/Proto.Client/network/Extensions/ArmClientExtensions.cs b/sdk/resourcemanager/Proto.Client/network/Extensions/ArmClientExtensions.cs index 10ab18d7a7c79..1b5103d57f996 100644 --- a/sdk/resourcemanager/Proto.Client/network/Extensions/ArmClientExtensions.cs +++ b/sdk/resourcemanager/Proto.Client/network/Extensions/ArmClientExtensions.cs @@ -1,10 +1,5 @@ using Azure.ResourceManager.Core; -using Azure.ResourceManager.Network.Models; -using Proto.Network; using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text; namespace Proto.Network { @@ -21,12 +16,14 @@ public static class ArmClientExtensions /// The ResourceIdentifier of the resource that is the target of operations. /// Returns an object representing the operations that can be performed over a specific . /// ResourceIdentifier provided is not for a NetworkInterfaceOperations. + /// ResourceIdentifier cannot be null. public static NetworkInterfaceOperations GetNetworkInterfaceOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) { + if (resourceId is null) + throw new ArgumentNullException(nameof(resourceId)); if (resourceId.Type != NetworkInterfaceOperations.ResourceType) - { - throw new ArgumentException("ResourceIdentifier provided is not for a Network Interface."); - } + throw new ArgumentException($"{nameof(resourceId.Type)} provided is not for a NetworkInterface.", nameof(resourceId.Type)); + var subOps = client.GetSubscriptionOperations(resourceId.Subscription); var rgOps = subOps.GetResourceGroupOperations(resourceId.ResourceGroup); return rgOps.GetNetworkInterfaceOperations(resourceId.Name); @@ -39,12 +36,14 @@ public static NetworkInterfaceOperations GetNetworkInterfaceOperations(this Azur /// The ResourceIdentifier of the resource that is the target of operations. /// Returns an object representing the operations that can be performed over a specific . /// ResourceIdentifier provided is not for a NetworkSecurityGroup. + /// ResourceIdentifier cannot be null. public static NetworkSecurityGroupOperations GetNetworkSecurityGroupOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) { + if (resourceId is null) + throw new ArgumentNullException(nameof(resourceId)); if (resourceId.Type != NetworkSecurityGroupOperations.ResourceType) - { - throw new ArgumentException("ResourceIdentifier provided is not for a NetworkSecurityGroup."); - } + throw new ArgumentException($"{nameof(resourceId.Type)} provided is not for a NetworkSecurityGroup.", nameof(resourceId.Type)); + var subOps = client.GetSubscriptionOperations(resourceId.Subscription); var rgOps = subOps.GetResourceGroupOperations(resourceId.ResourceGroup); return rgOps.GetNetworkSecurityGroupOperations(resourceId.Name); @@ -57,12 +56,14 @@ public static NetworkSecurityGroupOperations GetNetworkSecurityGroupOperations(t /// The ResourceIdentifier of the resource that is the target of operations. /// Returns an object representing the operations that can be performed over a specific . /// ResourceIdentifier provided is not for a PublicIpAddress. + /// ResourceIdentifier cannot be null. public static PublicIpAddressOperations GetPublicIpAddressOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) { + if (resourceId is null) + throw new ArgumentNullException(nameof(resourceId)); if (resourceId.Type != PublicIpAddressOperations.ResourceType) - { - throw new ArgumentException("ResourceIdentifier provided is not for a PublicIpAddress."); - } + throw new ArgumentException($"{nameof(resourceId.Type)} provided is not for a PublicIpAddress.", nameof(resourceId.Type)); + var subOps = client.GetSubscriptionOperations(resourceId.Subscription); var rgOps = subOps.GetResourceGroupOperations(resourceId.ResourceGroup); return rgOps.GetPublicIpAddressOperations(resourceId.Name); @@ -75,12 +76,13 @@ public static PublicIpAddressOperations GetPublicIpAddressOperations(this AzureR /// The ResourceIdentifier of the resource that is the target of operations. /// Returns an object representing the operations that can be performed over a specific . /// ResourceIdentifier provided is not for a Subnet. + /// ResourceIdentifier cannot be null. public static SubnetOperations GetSubnetOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) { + if (resourceId is null) + throw new ArgumentNullException(nameof(resourceId)); if (resourceId.Type != SubnetOperations.ResourceType) - { - throw new ArgumentException("ResourceIdentifier provided is not for a Subnet."); - } + throw new ArgumentException($"{nameof(resourceId.Type)} provided is not for a Subnet.", nameof(resourceId.Type)); var subOps = client.GetSubscriptionOperations(resourceId.Subscription); var rgOps = subOps.GetResourceGroupOperations(resourceId.ResourceGroup); var vnetOps = rgOps.GetVirtualNetworkOperations(resourceId.Parent.Name); @@ -94,12 +96,14 @@ public static SubnetOperations GetSubnetOperations(this AzureResourceManagerClie /// The ResourceIdentifier of the resource that is the target of operations. /// Returns an object representing the operations that can be performed over a specific . /// ResourceIdentifier provided is not for a VirtualNetwork. + /// ResourceIdentifier cannot be null. public static VirtualNetworkOperations GetVirtualNetworkOperations(this AzureResourceManagerClient client, ResourceIdentifier resourceId) { + if (resourceId is null) + throw new ArgumentNullException(nameof(resourceId)); if (resourceId.Type != VirtualNetworkOperations.ResourceType) - { - throw new ArgumentException("ResourceIdentifier provided is not for a VirtualNetwork."); - } + throw new ArgumentException($"{nameof(resourceId.Type)} provided is not for a VirtualNetwork.", nameof(resourceId.Type)); + var subOps = client.GetSubscriptionOperations(resourceId.Subscription); var rgOps = subOps.GetResourceGroupOperations(resourceId.ResourceGroup); return rgOps.GetVirtualNetworkOperations(resourceId.Parent.Name); diff --git a/sdk/resourcemanager/Proto.Client/network/Extensions/ResourceGroupExtensions.cs b/sdk/resourcemanager/Proto.Client/network/Extensions/ResourceGroupExtensions.cs index 5d67c819ab652..d112c63016045 100644 --- a/sdk/resourcemanager/Proto.Client/network/Extensions/ResourceGroupExtensions.cs +++ b/sdk/resourcemanager/Proto.Client/network/Extensions/ResourceGroupExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using Azure.ResourceManager.Core; +using System; namespace Proto.Network { @@ -17,8 +18,11 @@ public static class ResourceGroupExtensions /// The instance the method will execute against. /// The resource id of data model. /// An instance of . + /// virtualNetwork cannot be null or a whitespace. public static VirtualNetworkOperations GetVirtualNetworkOperations(this ResourceGroupOperations resourceGroup, string virtualNetwork) { + if (string.IsNullOrWhiteSpace(virtualNetwork)) + throw new ArgumentException($"{nameof(virtualNetwork)} cannot be null or a whitespace.", nameof(virtualNetwork)); return new VirtualNetworkOperations(resourceGroup, virtualNetwork); } @@ -40,8 +44,11 @@ public static VirtualNetworkContainer GetVirtualNetworkContainer(this ResourceGr /// The instance the method will execute against. /// The resource id of data model. /// An instance of . + /// publicIpAddress cannot be null or a whitespace. public static PublicIpAddressOperations GetPublicIpAddressOperations(this ResourceGroupOperations resourceGroup, string publicIpAddress) { + if (string.IsNullOrWhiteSpace(publicIpAddress)) + throw new ArgumentException($"{nameof(publicIpAddress)} cannot be null or a whitespace.", nameof(publicIpAddress)); return new PublicIpAddressOperations(resourceGroup, publicIpAddress); } @@ -63,8 +70,11 @@ public static PublicIpAddressContainer GetPublicIpAddressContainer(this Resource /// The operations over a specific resource group. /// The network interface to target for operations. /// A including the operations that can be peformed on it. + /// networkInterface cannot be null or a whitespace. public static NetworkInterfaceOperations GetNetworkInterfaceOperations(this ResourceGroupOperations resourceGroup, string networkInterface) { + if (string.IsNullOrWhiteSpace(networkInterface)) + throw new ArgumentException($"{nameof(networkInterface)} cannot be null or a whitespace.", nameof(networkInterface)); return new NetworkInterfaceOperations(resourceGroup, networkInterface); } @@ -84,8 +94,11 @@ public static NetworkInterfaceContainer GetNetworkInterfaceContainer(this Resour /// The instance the method will execute against. /// The resource id of data model. /// An instance of . + /// networkSecurityGroup cannot be null or a whitespace. public static NetworkSecurityGroupOperations GetNetworkSecurityGroupOperations(this ResourceGroupOperations resourceGroup, string networkSecurityGroup) { + if (string.IsNullOrWhiteSpace(networkSecurityGroup)) + throw new ArgumentException($"{nameof(networkSecurityGroup)} cannot be null or a whitespace.", nameof(networkSecurityGroup)); return new NetworkSecurityGroupOperations(resourceGroup, networkSecurityGroup); } diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceContainer.cs b/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceContainer.cs index 4a7e20cd9babb..0411de68c29fe 100644 --- a/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceContainer.cs +++ b/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceContainer.cs @@ -2,15 +2,14 @@ // Licensed under the MIT License. using Azure; -using Azure.ResourceManager.Network; -using Azure.ResourceManager.Network.Models; using Azure.ResourceManager.Core; -using Azure.ResourceManager.Core.Adapters; using Azure.ResourceManager.Core.Resources; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Network.Models; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using System; namespace Proto.Network { @@ -27,7 +26,7 @@ internal NetworkInterfaceContainer(ResourceGroupOperations resourceGroup) internal NetworkInterfacesOperations Operations => new NetworkManagementClient( Id.Subscription, BaseUri, - Credential, + Credential, ClientOptions.Convert()).NetworkInterfaces; /// @@ -184,10 +183,24 @@ public AsyncPageable ListByNameExpandedAsync(string filter, in var results = ListByNameAsync(filter, top, cancellationToken); return new PhWrappingAsyncPageable(results, s => new NetworkInterfaceOperations(s).Get().Value); } - + private Func convertor() { return s => new NetworkInterface(Parent, new NetworkInterfaceData(s)); } + + /// + public override ArmResponse Get(string networkInterfaceName) + { + return new PhArmResponse(Operations.Get(Id.ResourceGroup, networkInterfaceName), + g => new NetworkInterface(Parent, new NetworkInterfaceData(g))); + } + + /// + public override async Task> GetAsync(string networkInterfaceName, CancellationToken cancellationToken = default) + { + return new PhArmResponse(await Operations.GetAsync(Id.ResourceGroup, networkInterfaceName, null, cancellationToken), + g => new NetworkInterface(Parent, new NetworkInterfaceData(g))); + } } } diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceOperations.cs b/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceOperations.cs index 85d8f5fc407d6..aef48c4d088b5 100644 --- a/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceOperations.cs +++ b/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceOperations.cs @@ -1,12 +1,11 @@ using Azure; +using Azure.ResourceManager.Core; using Azure.ResourceManager.Network; using Azure.ResourceManager.Network.Models; -using Azure.ResourceManager.Core; +using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using System.Collections.Generic; -using System.Linq; -using System; namespace Proto.Network { @@ -62,7 +61,7 @@ public ArmResponse Delete() /// Deletes a . /// /// A token to allow the caller to cancel the call to the service. - /// The default value is . + /// The default value is . /// A that returns an when completed. public async Task> DeleteAsync(CancellationToken cancellationToken = default) { @@ -87,7 +86,7 @@ public ArmOperation StartDelete(CancellationToken cancellationToken = /// Deletes a . /// /// A token to allow the caller to cancel the call to the service. - /// The default value is . + /// The default value is . /// A that on completion returns an that allows polling for completion of the operation. /// /// Details on long running operation object. @@ -191,9 +190,8 @@ await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellat /// public ArmResponse SetTags(IDictionary tags) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new NetworkInterface(this, new NetworkInterfaceData(n))); } @@ -201,9 +199,8 @@ public ArmResponse SetTags(IDictionary tags) /// public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmResponse( await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new NetworkInterface(this, new NetworkInterfaceData(n))); @@ -212,9 +209,8 @@ await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellat /// public ArmOperation StartSetTags(IDictionary tags) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new NetworkInterface(this, new NetworkInterfaceData(n))); } @@ -222,9 +218,8 @@ public ArmOperation StartSetTags(IDictionary t /// public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmOperation( await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new NetworkInterface(this, new NetworkInterfaceData(n))); @@ -234,8 +229,9 @@ await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellat public ArmResponse RemoveTag(string key) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new NetworkInterface(this, new NetworkInterfaceData(n))); } @@ -244,8 +240,9 @@ public ArmResponse RemoveTag(string key) public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmResponse( await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new NetworkInterface(this, new NetworkInterfaceData(n))); @@ -255,8 +252,9 @@ await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellat public ArmOperation StartRemoveTag(string key) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new NetworkInterface(this, new NetworkInterfaceData(n))); } @@ -265,8 +263,9 @@ public ArmOperation StartRemoveTag(string key) public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmOperation( await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new NetworkInterface(this, new NetworkInterfaceData(n))); @@ -278,24 +277,18 @@ await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellat /// A collection of location that may take multiple service requests to iterate over. public IEnumerable ListAvailableLocations() { - var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); - var networkInterfaceProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var networkInterfaceResource = networkInterfaceProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return networkInterfaceResource.Locations.Select(l => (LocationData)l); + return ListAvailableLocations(ResourceType); } /// /// Lists all available geo-locations. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// An async collection of location that may take multiple service requests to iterate over. /// The default subscription id is null. public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) { - var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); - var networkInterfaceProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var networkInterfaceResource = networkInterfaceProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return networkInterfaceResource.Locations.Select(l => (LocationData)l); + return await ListAvailableLocationsAsync(ResourceType, cancellationToken); } } } diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupContainer.cs b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupContainer.cs index cd47491d6a7fe..8ed56d4ab33ad 100644 --- a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupContainer.cs +++ b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupContainer.cs @@ -2,11 +2,10 @@ // Licensed under the MIT License. using Azure; -using Azure.ResourceManager.Network; -using Azure.ResourceManager.Network.Models; using Azure.ResourceManager.Core; -using Azure.ResourceManager.Core.Adapters; using Azure.ResourceManager.Core.Resources; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Network.Models; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -221,5 +220,19 @@ public AsyncPageable ListByNameExpandedAsync(string filter var results = ListByNameAsync(filter, top, cancellationToken); return new PhWrappingAsyncPageable(results, s => new NetworkSecurityGroupOperations(s).Get().Value); } + + /// + public override ArmResponse Get(string networkSecurityGroup) + { + return new PhArmResponse(Operations.Get(Id.ResourceGroup, networkSecurityGroup), + g => new NetworkSecurityGroup(Parent, new NetworkSecurityGroupData(g))); + } + + /// + public override async Task> GetAsync(string networkSecurityGroup, CancellationToken cancellationToken = default) + { + return new PhArmResponse(await Operations.GetAsync(Id.ResourceGroup, networkSecurityGroup, null, cancellationToken), + g => new NetworkSecurityGroup(Parent, new NetworkSecurityGroupData(g))); + } } } diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupOperations.cs b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupOperations.cs index 85998167c400a..728ce64016911 100644 --- a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupOperations.cs +++ b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupOperations.cs @@ -2,14 +2,14 @@ // Licensed under the MIT License. using Azure; +using Azure.ResourceManager.Core; using Azure.ResourceManager.Network; using Azure.ResourceManager.Network.Models; -using Azure.ResourceManager.Core; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Collections.Generic; -using System; namespace Proto.Network { @@ -64,7 +64,7 @@ protected NetworkSecurityGroupOperations(ResourceOperationsBase operation, Resou /// /// Updates the network security group rules. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// The rules to be updated. /// An that allows polling for completion of the operation. public ArmOperation UpdateRules(CancellationToken cancellationToken = default, params SecurityRule[] rules) @@ -190,9 +190,8 @@ protected override NetworkSecurityGroup GetResource() /// public ArmResponse SetTags(IDictionary tags) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmResponse( Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); @@ -201,9 +200,8 @@ public ArmResponse SetTags(IDictionary tag /// public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); } @@ -211,9 +209,8 @@ public async Task> SetTagsAsync(IDictionary public ArmOperation StartSetTags(IDictionary tags) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmOperation( Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); @@ -222,9 +219,8 @@ public ArmOperation StartSetTags(IDictionary public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); } @@ -233,8 +229,9 @@ public async Task> StartSetTagsAsync(IDiction public ArmResponse RemoveTag(string key) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmResponse( Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); @@ -244,8 +241,9 @@ public ArmResponse RemoveTag(string key) public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); } @@ -254,8 +252,9 @@ public async Task> RemoveTagAsync(string key, public ArmOperation StartRemoveTag(string key) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmOperation( Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); @@ -265,8 +264,9 @@ public ArmOperation StartRemoveTag(string key) public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); } @@ -277,24 +277,18 @@ public async Task> StartRemoveTagAsync(string /// A collection of location that may take multiple service requests to iterate over. public IEnumerable ListAvailableLocations() { - var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); - var networkSecurityProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var networkSecurityResource = networkSecurityProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return networkSecurityResource.Locations.Select(l => (LocationData)l); + return ListAvailableLocations(ResourceType); } /// /// Lists all available geo-locations. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// An async collection of location that may take multiple service requests to iterate over. /// The default subscription id is null. public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) { - var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); - var networkSecurityProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var networkSecurityResource = networkSecurityProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return networkSecurityResource.Locations.Select(l => (LocationData)l); + return await ListAvailableLocationsAsync(ResourceType, cancellationToken); } } } diff --git a/sdk/resourcemanager/Proto.Client/network/PublicIpAddressContainer.cs b/sdk/resourcemanager/Proto.Client/network/PublicIpAddressContainer.cs index 96859391d7b8d..6a0ab4e5dadbb 100644 --- a/sdk/resourcemanager/Proto.Client/network/PublicIpAddressContainer.cs +++ b/sdk/resourcemanager/Proto.Client/network/PublicIpAddressContainer.cs @@ -2,14 +2,13 @@ // Licensed under the MIT License. using Azure; -using Azure.ResourceManager.Network; -using Azure.ResourceManager.Network.Models; using Azure.ResourceManager.Core; -using Azure.ResourceManager.Core.Adapters; using Azure.ResourceManager.Core.Resources; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Network.Models; +using System; using System.Threading; using System.Threading.Tasks; -using System; namespace Proto.Network { @@ -174,5 +173,16 @@ private Func Convertor() { return s => new PublicIpAddress(Parent, new PublicIPAddressData(s)); } + /// + public override ArmResponse Get(string publicIpAddressesName) + { + return new PhArmResponse(Operations.Get(Id.ResourceGroup, publicIpAddressesName), Convertor()); + } + + /// + public override async Task> GetAsync(string publicIpAddressesName, CancellationToken cancellationToken = default) + { + return new PhArmResponse(await Operations.GetAsync(Id.ResourceGroup, publicIpAddressesName), Convertor()); + } } } diff --git a/sdk/resourcemanager/Proto.Client/network/PublicIpAddressOperations.cs b/sdk/resourcemanager/Proto.Client/network/PublicIpAddressOperations.cs index b25ea1597672d..179fc85b486e0 100644 --- a/sdk/resourcemanager/Proto.Client/network/PublicIpAddressOperations.cs +++ b/sdk/resourcemanager/Proto.Client/network/PublicIpAddressOperations.cs @@ -2,14 +2,13 @@ // Licensed under the MIT License. using Azure; +using Azure.ResourceManager.Core; using Azure.ResourceManager.Network; using Azure.ResourceManager.Network.Models; -using Azure.ResourceManager.Core; +using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using System.Collections.Generic; -using System.Linq; -using System; namespace Proto.Network { @@ -73,7 +72,7 @@ public ArmResponse Delete() /// /// The operation to delete a public IP address. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// A that on completion returns a response with the operation for this resource. public async Task> DeleteAsync(CancellationToken cancellationToken = default) { @@ -83,7 +82,7 @@ public async Task> DeleteAsync(CancellationToken cancellat /// /// The operation to delete a public IP address. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// /// Details on long running operation object. /// @@ -96,7 +95,7 @@ public ArmOperation StartDelete(CancellationToken cancellationToken = /// /// The operation to delete a public IP address. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// /// Details on long running operation object. /// @@ -142,7 +141,7 @@ public ArmResponse AddTag(string key, string value) /// /// The key for the tag. /// The value for the tag. - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// A that on completion returns an that allows polling for completion of the operation. public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) { @@ -175,7 +174,7 @@ public ArmOperation StartAddTag(string key, string value) /// /// The key for the tag. /// The value for the tag. - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// A that on completion returns an that allows polling for completion of the operation. public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) { @@ -189,9 +188,8 @@ public async Task> StartAddTagAsync(string key, st /// public ArmResponse SetTags(IDictionary tags) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new PublicIpAddress(this, new PublicIPAddressData(n))); } @@ -199,9 +197,8 @@ public ArmResponse SetTags(IDictionary tags) /// public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new PublicIpAddress(this, new PublicIPAddressData(n))); } @@ -209,9 +206,8 @@ public async Task> SetTagsAsync(IDictionary public ArmOperation StartSetTags(IDictionary tags) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new PublicIpAddress(this, new PublicIPAddressData(n))); } @@ -219,9 +215,8 @@ public ArmOperation StartSetTags(IDictionary ta /// public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new PublicIpAddress(this, new PublicIPAddressData(n))); } @@ -230,8 +225,9 @@ public async Task> StartSetTagsAsync(IDictionary RemoveTag(string key) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new PublicIpAddress(this, new PublicIPAddressData(n))); } @@ -240,8 +236,9 @@ public ArmResponse RemoveTag(string key) public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new PublicIpAddress(this, new PublicIPAddressData(n))); } @@ -250,8 +247,9 @@ public async Task> RemoveTagAsync(string key, Cance public ArmOperation StartRemoveTag(string key) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new PublicIpAddress(this, new PublicIPAddressData(n))); } @@ -260,8 +258,9 @@ public ArmOperation StartRemoveTag(string key) public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, resource.Data.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new PublicIpAddress(this, new PublicIPAddressData(n))); } @@ -272,24 +271,18 @@ public async Task> StartRemoveTagAsync(string key, /// A collection of location that may take multiple service requests to iterate over. public IEnumerable ListAvailableLocations() { - var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); - var publicIPProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var publicIPResource = publicIPProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return publicIPResource.Locations.Select(l => (LocationData)l); + return ListAvailableLocations(ResourceType); } /// /// Lists all available geo-locations. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// An async collection of location that may take multiple service requests to iterate over. /// The default subscription id is null. public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) { - var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); - var publicIPProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var publicIPResource = publicIPProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return publicIPResource.Locations.Select(l => (LocationData)l); + return await ListAvailableLocationsAsync(ResourceType, cancellationToken); } } } diff --git a/sdk/resourcemanager/Proto.Client/network/SubnetContainer.cs b/sdk/resourcemanager/Proto.Client/network/SubnetContainer.cs index 4f2ed2c670ec7..51da32cf51e44 100644 --- a/sdk/resourcemanager/Proto.Client/network/SubnetContainer.cs +++ b/sdk/resourcemanager/Proto.Client/network/SubnetContainer.cs @@ -1,10 +1,9 @@ using Azure; -using Azure.ResourceManager.Network; using Azure.ResourceManager.Core; -using Azure.ResourceManager.Core.Adapters; +using Azure.ResourceManager.Network; +using System; using System.Threading; using System.Threading.Tasks; -using System; namespace Proto.Network { @@ -95,7 +94,7 @@ public Pageable List(CancellationToken cancellationToken = def { return new PhWrappingPageable( Operations.List(Id.ResourceGroup, Id.Name, cancellationToken), - this.convertor()); + convertor()); } /// @@ -107,12 +106,26 @@ public AsyncPageable ListAsync(CancellationToken cancellationT { return new PhWrappingAsyncPageable( Operations.ListAsync(Id.ResourceGroup, Id.Name, cancellationToken), - this.convertor()); + convertor()); } private Func convertor() { return s => new Subnet(Parent, new SubnetData(s)); } + + /// + public override ArmResponse Get(string subnetName) + { + return new PhArmResponse(Operations.Get(Id.ResourceGroup, Id.Name, subnetName), + n => new Subnet(Parent, new SubnetData(n))); + } + + /// + public override async Task> GetAsync(string subnetName, CancellationToken cancellationToken = default) + { + return new PhArmResponse(await Operations.GetAsync(Id.ResourceGroup, Id.Name, subnetName, null, cancellationToken), + n => new Subnet(Parent, new SubnetData(n))); + } } } diff --git a/sdk/resourcemanager/Proto.Client/network/VirtualNetworkContainer.cs b/sdk/resourcemanager/Proto.Client/network/VirtualNetworkContainer.cs index 6071bafd9a6b7..0f7792c3f3d1b 100644 --- a/sdk/resourcemanager/Proto.Client/network/VirtualNetworkContainer.cs +++ b/sdk/resourcemanager/Proto.Client/network/VirtualNetworkContainer.cs @@ -2,15 +2,14 @@ // Licensed under the MIT License. using Azure; -using Azure.ResourceManager.Network; -using Azure.ResourceManager.Network.Models; using Azure.ResourceManager.Core; -using Azure.ResourceManager.Core.Adapters; using Azure.ResourceManager.Core.Resources; +using Azure.ResourceManager.Network; +using Azure.ResourceManager.Network.Models; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using System; namespace Proto.Network { @@ -173,5 +172,20 @@ public AsyncPageable ListByNameExpandedAsync(string filter, int? { return s => new VirtualNetwork(Parent, new VirtualNetworkData(s)); } + + /// + public override ArmResponse Get(string virtualNetworkName) + { + return new PhArmResponse(Operations.Get(Id.ResourceGroup, virtualNetworkName), + Convertor()); + } + + /// + public override async Task> GetAsync(string virtualNetworkName, CancellationToken cancellationToken = default) + { + return new PhArmResponse(await Operations.GetAsync(Id.ResourceGroup, virtualNetworkName, null, cancellationToken), + Convertor()); + } + } } diff --git a/sdk/resourcemanager/Proto.Client/network/VirtualNetworkOperations.cs b/sdk/resourcemanager/Proto.Client/network/VirtualNetworkOperations.cs index e9827c17400bd..c94059468ad1c 100644 --- a/sdk/resourcemanager/Proto.Client/network/VirtualNetworkOperations.cs +++ b/sdk/resourcemanager/Proto.Client/network/VirtualNetworkOperations.cs @@ -1,12 +1,11 @@ using Azure; +using Azure.ResourceManager.Core; using Azure.ResourceManager.Network; using Azure.ResourceManager.Network.Models; -using Azure.ResourceManager.Core; +using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using System.Linq; -using System.Collections.Generic; -using System; namespace Proto.Network { @@ -72,7 +71,7 @@ public ArmResponse Delete() /// /// The operation to delete a virtual nerwork. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// A that on completion returns a response with the operation for this resource. public async Task> DeleteAsync(CancellationToken cancellationToken = default) { @@ -82,7 +81,7 @@ public async Task> DeleteAsync(CancellationToken cancellat /// /// The operation to delete a virtual nerwork. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// /// Details on long running operation object. /// @@ -95,7 +94,7 @@ public ArmOperation StartDelete(CancellationToken cancellationToken = /// /// The operation to delete a virtual nerwork. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// /// Details on long running operation object. /// @@ -144,7 +143,7 @@ public ArmResponse AddTag(string key, string value) /// /// The key for the tag. /// The value for the tag. - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// /// Details on long running operation object. /// @@ -183,7 +182,7 @@ public ArmOperation StartAddTag(string key, string value) /// /// The key for the tag. /// The value for the tag. - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// /// Details on long running operation object. /// @@ -219,9 +218,8 @@ public SubnetContainer GetSubnetContainer() /// public ArmResponse SetTags(IDictionary tags) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, patchable.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new VirtualNetwork(this, new VirtualNetworkData(n))); } @@ -229,9 +227,8 @@ public ArmResponse SetTags(IDictionary tags) /// public async Task> SetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, patchable.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new VirtualNetwork(this, new VirtualNetworkData(n))); } @@ -239,9 +236,8 @@ public async Task> SetTagsAsync(IDictionary public ArmOperation StartSetTags(IDictionary tags) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, patchable.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new VirtualNetwork(this, new VirtualNetworkData(n))); } @@ -249,9 +245,8 @@ public ArmOperation StartSetTags(IDictionary tag /// public async Task> StartSetTagsAsync(IDictionary tags, CancellationToken cancellationToken = default) { - var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - ReplaceTags(tags, patchable.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(tags); return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new VirtualNetwork(this, new VirtualNetworkData(n))); } @@ -260,8 +255,9 @@ public async Task> StartSetTagsAsync(IDictionary RemoveTag(string key) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, patchable.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new VirtualNetwork(this, new VirtualNetworkData(n))); } @@ -270,8 +266,9 @@ public ArmResponse RemoveTag(string key) public async Task> RemoveTagAsync(string key, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, patchable.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new VirtualNetwork(this, new VirtualNetworkData(n))); } @@ -280,8 +277,9 @@ public async Task> RemoveTagAsync(string key, Cancel public ArmOperation StartRemoveTag(string key) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, patchable.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new VirtualNetwork(this, new VirtualNetworkData(n))); } @@ -290,8 +288,9 @@ public ArmOperation StartRemoveTag(string key) public async Task> StartRemoveTagAsync(string key, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; - DeleteTag(key, patchable.Tags); + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); + patchable.Tags.Remove(key); return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new VirtualNetwork(this, new VirtualNetworkData(n))); } @@ -302,24 +301,18 @@ public async Task> StartRemoveTagAsync(string key, /// A collection of location that may take multiple service requests to iterate over. public IEnumerable ListAvailableLocations() { - var pageableProvider = ResourcesClient.Providers.List(expand: "metadata"); - var vnProvider = pageableProvider.FirstOrDefault(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var vnResource = vnProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return vnResource.Locations.Select(l => (LocationData)l); + return ListAvailableLocations(ResourceType); } /// /// Lists all available geo-locations. /// - /// A token to allow the caller to cancel the call to the service. The default value is . + /// A token to allow the caller to cancel the call to the service. The default value is . /// An async collection of location that may take multiple service requests to iterate over. /// The default subscription id is null. public async Task> ListAvailableLocationsAsync(CancellationToken cancellationToken = default) { - var asyncpageableProvider = ResourcesClient.Providers.ListAsync(expand: "metadata", cancellationToken: cancellationToken); - var vnProvider = await asyncpageableProvider.FirstOrDefaultAsync(p => string.Equals(p.Namespace, ResourceType?.Namespace, StringComparison.InvariantCultureIgnoreCase)); - var vnResource = vnProvider.ResourceTypes.FirstOrDefault(r => ResourceType.Type.Equals(r.ResourceType)); - return vnResource.Locations.Select(l => (LocationData)l); + return await ListAvailableLocationsAsync(ResourceType, cancellationToken); } } } diff --git a/sdk/resourcemanager/Proto.Client/src/Program.cs b/sdk/resourcemanager/Proto.Client/src/Program.cs index cd40dc1c7bf1e..1e4f6ee5de7a0 100644 --- a/sdk/resourcemanager/Proto.Client/src/Program.cs +++ b/sdk/resourcemanager/Proto.Client/src/Program.cs @@ -10,7 +10,7 @@ static void Main(string[] args) Scenario scenario = null; try { - scenario = ScenarioFactory.GetScenario(Scenarios.CreateSingleVMCheckLocation); + scenario = ScenarioFactory.GetScenario(Scenarios.CreateSingleVmExample); scenario.Execute(); } finally diff --git a/sdk/resourcemanager/Proto.Client/src/ScenarioFactory.cs b/sdk/resourcemanager/Proto.Client/src/ScenarioFactory.cs index 6bf7856f4ed82..fb0a13747bad3 100644 --- a/sdk/resourcemanager/Proto.Client/src/ScenarioFactory.cs +++ b/sdk/resourcemanager/Proto.Client/src/ScenarioFactory.cs @@ -35,6 +35,9 @@ enum Scenarios DefaultSubscription, SubscriptionExists, UseParentLocation, + GetByContainers, + GetByContainersAsync, + CheckResourceGroupOpsAsync } class ScenarioFactory diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/CheckResourceGroupOpsAsync.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/CheckResourceGroupOpsAsync.cs new file mode 100644 index 0000000000000..b0263b2c00e16 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/CheckResourceGroupOpsAsync.cs @@ -0,0 +1,144 @@ +using Azure.ResourceManager.Core; +using Proto.Compute; +using System; +using System.Threading.Tasks; + +namespace Proto.Client +{ + class CheckResourceGroupOpsAsync : Scenario + { + public CheckResourceGroupOpsAsync() : base() { } + + public CheckResourceGroupOpsAsync(ScenarioContext context) : base(context) { } + + public override void Execute() + { + ExecuteAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + + private async System.Threading.Tasks.Task ExecuteAsync() + { + var client = new AzureResourceManagerClient(); + var subscription = client.GetSubscriptionOperations(Context.SubscriptionId); + + // Create Resource Group + Console.WriteLine($"--------Start create group {Context.RgName}--------"); + var resourceGroup = subscription.GetResourceGroupContainer().Construct(Context.Loc).CreateOrUpdate(Context.RgName).Value; + CleanUp.Add(resourceGroup.Id); + var rgOps = subscription.GetResourceGroupOperations(Context.RgName); + + ShouldThrow( + () => rgOps.AddTag("", ""), + "AddTag with empty string didn't throw", + "AddTag"); + + await ShouldThrowAsync( + async () => await rgOps.AddTagAsync(null, null), + "AddTagAsync with null string didn't throw", + "AddTagAsync"); + + ShouldThrow( + () => rgOps.StartAddTag("", null), + "StartAddTag with empty string didn't throw", + "StartAddTag"); + + await ShouldThrowAsync( + async () => await rgOps.StartAddTagAsync(" ", "test"), + "StartAddTagAsync with whitespaces only string didn't throw", + "StartAddTagAsync"); + + // Create AvailabilitySet + Console.WriteLine("--------Create AvailabilitySet async--------"); + var aset = (await (await resourceGroup.GetAvailabilitySetContainer().Construct("Aligned").StartCreateOrUpdateAsync(Context.VmName + "_aSet")).WaitForCompletionAsync()).Value; + var data = aset.Get().Value.Data; + + ShouldThrow( + () => rgOps.CreateResource("", data), + "CreateResource with empty string didn't throw", + "CreateResource"); + + await ShouldThrowAsync( + async () => await rgOps.CreateResourceAsync(" ", data), + "CreateResourceAsync with whitespaces string didn't throw", + "CreateResourceAsync"); + + ShouldThrow( + () => rgOps.SetTags(null), + "SetTags with null didn't throw", + "SetTags"); + + await ShouldThrowAsync( + async () => await rgOps.SetTagsAsync(null), + "SetTagsAsync with null didn't throw", + "SetTagsAsync"); + + ShouldThrow( + () => rgOps.StartSetTags(null), + "StartSetTags with null didn't throw", + "StartSetTags"); + + await ShouldThrowAsync( + async () => await rgOps.StartSetTagsAsync(null), + "StartSetTagsAsync with null didn't throw", + "StartSetTagsAsync"); + + ShouldThrow( + () => rgOps.RemoveTag(""), + "RemoveTag with empty string didn't throw", + "RemoveTag"); + + await ShouldThrowAsync( + async () => await rgOps.RemoveTagAsync(null), + "RemoveTagAsync with null didn't throw", + "RemoveTagAsync"); + + ShouldThrow( + () => rgOps.StartRemoveTag(" "), + "StartRemoveTag with whitespace string didn't throw", + "StartRemoveTag"); + + await ShouldThrowAsync( + async () => await rgOps.StartRemoveTagAsync(null), + "StartRemoveTagAsync with null didn't throw", + "StartRemoveTagAsync"); + + ShouldThrow( + () => rgOps.CreateResource("tester", null), + "CreateResource model exception not thrown", + "CreateResource"); + + await ShouldThrowAsync( + async () => await rgOps.CreateResourceAsync("tester", null), + "CreateResourceAsync model exception not thrown", + "CreateResourceAsync"); + + Console.WriteLine("--------Done--------"); + } + + private static void ShouldThrow(Action lambda, string failMessage, string method) + { + try + { + lambda(); + throw new Exception(failMessage); + } + catch (Exception e) when (e.GetType() == typeof(T)) + { + Console.WriteLine($"{method} Exception was thrown as expected."); + } + } + + private static async Task ShouldThrowAsync(Func lambda, string failMessage, string method) + { + try + { + await lambda(); + throw new Exception(failMessage); + } + catch (Exception e) when (e.GetType() == typeof(T)) + { + Console.WriteLine($"{method} Exception was thrown as expected."); + } + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/GetByContainerAsync.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/GetByContainerAsync.cs new file mode 100644 index 0000000000000..13c50526d4e50 --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/GetByContainerAsync.cs @@ -0,0 +1,45 @@ +using Azure.ResourceManager.Core; +using Proto.Compute; +using Proto.Network; +using System; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace Proto.Client +{ + class GetByContainersAsync: Scenario + { + public override void Execute() + { + ExecuteAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + private async Task ExecuteAsync() + { + var createMultipleVms = new CreateMultipleVms(Context); + createMultipleVms.Execute(); + + var sub = new AzureResourceManagerClient().GetSubscriptionOperations(Context.SubscriptionId); + var rg = sub.GetResourceGroupOperations(Context.RgName); + var virtualMachineContainer = rg.GetVirtualMachineContainer(); + await foreach (var response in virtualMachineContainer.ListAsync()) + { + var virtualMachine = await virtualMachineContainer.GetAsync(response.Data.Id.Name); + Debug.Assert(virtualMachine.Value.Data.Name.Equals(response.Data.Id.Name)); + } + var virtualNetworkContainer = rg.GetVirtualNetworkContainer(); + await foreach (var response in virtualNetworkContainer.ListAsync()) + { + var virtualNetwork = await virtualNetworkContainer.GetAsync(response.Data.Id.Name); + Debug.Assert(virtualNetwork.Value.Data.Name.Equals(response.Data.Id.Name)); + await foreach (var subnetResponse in response.GetSubnetContainer().ListAsync()) + { + var actualSubnet = await subnetResponse.GetAsync(); + var subnets = await response.GetSubnetContainer().GetAsync(actualSubnet.Value.Data.Name); + Debug.Assert(subnets.Value.Data.Name.Equals(actualSubnet.Value.Data.Name)); + } + } + Console.WriteLine("\nDone all asserts passed ..."); + + } + } +} diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/GetByContainers.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/GetByContainers.cs new file mode 100644 index 0000000000000..6358e890ab3da --- /dev/null +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/GetByContainers.cs @@ -0,0 +1,40 @@ +using Azure.ResourceManager.Core; +using Proto.Compute; +using Proto.Network; +using System; +using System.Diagnostics; + +namespace Proto.Client +{ + class GetByContainers: Scenario + { + public override void Execute() + { + var createMultipleVms = new CreateMultipleVms(Context); + createMultipleVms.Execute(); + + var sub = new AzureResourceManagerClient().GetSubscriptionOperations(Context.SubscriptionId); + var rg = sub.GetResourceGroupOperations(Context.RgName); + var virtualMachineContainer = rg.GetVirtualMachineContainer(); + foreach (var response in virtualMachineContainer.List()) + { + var virtualMachine = virtualMachineContainer.Get(response.Data.Id.Name); + Debug.Assert(virtualMachine.Value.Data.Name.Equals(response.Data.Id.Name)); + } + var virtualNetworkContainer = rg.GetVirtualNetworkContainer(); + foreach (var response in virtualNetworkContainer.List()) + { + var virtualNetwork = virtualNetworkContainer.Get(response.Data.Id.Name); + Debug.Assert(virtualNetwork.Value.Data.Name.Equals(response.Data.Id.Name)); + foreach (var subnetResponse in response.GetSubnetContainer().List()) + { + var actualSubnet = subnetResponse.Get(); + var subnets = response.GetSubnetContainer().Get(actualSubnet.Value.Data.Name); + Debug.Assert(subnets.Value.Data.Name.Equals(actualSubnet.Value.Data.Name)); + } + } + Console.WriteLine("\nDone all asserts passed ..."); + + } + } +} From 09b790e3260bc60949aac29d4bdc24cb53f9d33d Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Fri, 26 Feb 2021 17:20:56 -0800 Subject: [PATCH 16/18] fixed unit test failures missed a few files from the merge from proto repo updated compute and network sdk version to use latest azure.core with readonly list types --- .../src/ITaggableResource.cs | 27 +++- .../src/Resources/ResourceIdentifier.cs | 10 +- .../src/SubscriptionOperations.cs | 7 +- .../tests/IdentityTests.cs | 6 +- .../tests/ResourceIdentifierTests.cs | 117 ++++++++++++++++++ .../tests/SubscriptionOperationsTests.cs | 4 - .../tests/TaggableResourceTests.cs | 11 +- .../compute/AvailabilitySetOperations.cs | 12 +- .../Placeholder/AvailabilitySetData.cs | 10 +- .../compute/Placeholder/VirtualMachineData.cs | 7 +- .../Proto.Client/compute/Proto.Compute.csproj | 2 +- .../compute/VirtualMachineContainer.cs | 4 +- .../network/NetworkInterfaceContainer.cs | 18 ++- .../network/NetworkInterfaceOperations.cs | 12 +- .../network/NetworkSecurityGroup.cs | 2 - .../network/NetworkSecurityGroupContainer.cs | 56 +++++---- .../network/NetworkSecurityGroupOperations.cs | 20 +-- .../Placeholder/NetworkInterfaceData.cs | 9 +- .../Placeholder/NetworkSecurityGroupData.cs | 16 +-- .../Placeholder/PublicIPAddressData.cs | 6 - .../network/Placeholder/SubnetData.cs | 15 +-- .../network/Placeholder/VirtualNetworkData.cs | 7 -- .../Proto.Client/network/Proto.Network.csproj | 2 +- .../network/PublicIpAddressOperations.cs | 12 +- .../network/VirtualNetworkContainer.cs | 3 +- .../network/VirtualNetworkOperations.cs | 12 +- 26 files changed, 268 insertions(+), 139 deletions(-) diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ITaggableResource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ITaggableResource.cs index abb4ba72f9a0d..5008ecd50eef2 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/ITaggableResource.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/ITaggableResource.cs @@ -15,7 +15,30 @@ public interface ITaggableResource where TOperations : ResourceOperationsBase { /// - /// Add a tag to the resource + /// Add a tag to the resource. + /// + /// The tag key. + /// The tag value. + /// An that allows the user to control polling and waiting for Tag completion. + ArmResponse AddTag(string key, string value); + + /// + /// Add a tag to the resource. + /// + /// The tag key. + /// The tag value. + /// A token to allow the caller to cancel the call to the service. + /// The default value is . + /// A that performs the Tag operation. The Task yields an an + /// that allows the user to control polling and waiting for + /// Tag completion. + Task> AddTagAsync( + string key, + string value, + CancellationToken cancellationToken = default); + + /// + /// Add a tag to the resource. /// /// The tag key. /// The tag value. @@ -23,7 +46,7 @@ public interface ITaggableResource ArmOperation StartAddTag(string key, string value); /// - /// Add a tag to the resource + /// Add a tag to the resource. /// /// The tag key. /// The tag value. diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentifier.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentifier.cs index 692e861638f79..1657a5d7ea269 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentifier.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentifier.cs @@ -9,7 +9,7 @@ namespace Azure.ResourceManager.Core { /// - /// Canonical Representation of a Resource Identity + /// Canonical Representation of a Resource Identity. /// public sealed class ResourceIdentifier : IEquatable, @@ -71,7 +71,10 @@ public ResourceIdentifier(string id) /// String to be implicit converted into a object. public static implicit operator ResourceIdentifier(string other) { - return new ResourceIdentifier(other); // will null check in PR #119 + if (other is null) + return null; + + return new ResourceIdentifier(other); } /// @@ -80,6 +83,9 @@ public static implicit operator ResourceIdentifier(string other) /// object to be implicit converted into an string. public static implicit operator string(ResourceIdentifier other) { + if (other is null) + return null; + return other.Id; } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/SubscriptionOperations.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/SubscriptionOperations.cs index 4975f46565b81..58d527f3c6f42 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/SubscriptionOperations.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/SubscriptionOperations.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Azure.Core; @@ -85,7 +86,7 @@ public LocationContainer GetLocationContainer() /// public override ArmResponse Get() { - return new PhArmResponse( + return new PhArmResponse( SubscriptionsClient.Get(Id.Name), Converter()); } @@ -93,12 +94,12 @@ public override ArmResponse Get() /// public override async Task> GetAsync(CancellationToken cancellationToken = default) { - return new PhArmResponse( + return new PhArmResponse( await SubscriptionsClient.GetAsync(Id.Name, cancellationToken).ConfigureAwait(false), Converter()); } - private Func Converter() + private Func Converter() { return s => new Subscription(this, new SubscriptionData(s)); } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/IdentityTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/IdentityTests.cs index 64d712367076e..c21977993ee47 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/IdentityTests.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/IdentityTests.cs @@ -153,7 +153,7 @@ public void TestDeserializerInvalidDefaultJson() public JsonProperty DeserializerHelper(string filename) { - var json = File.ReadAllText("./TestAssets/Identity/" + filename); + var json = File.ReadAllText(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestAssets", "Identity", filename)); JsonDocument document = JsonDocument.Parse(json); JsonElement rootElement = document.RootElement; return rootElement.EnumerateObject().First(); @@ -163,7 +163,7 @@ public JsonProperty DeserializerHelper(string filename) public void TestDeserializerInvalidNullType() { var identityJsonProperty = DeserializerHelper("InvalidTypeIsNull.json"); - Assert.Throws(delegate { ResourceIdentity.Deserialize(identityJsonProperty.Value); }); + Assert.Throws(delegate { ResourceIdentity.Deserialize(identityJsonProperty.Value); }); } [TestCase] @@ -219,7 +219,7 @@ public void TestDeserializerValidMiddleExtraField() [TestCase] public void TestDeserializerValidOuterExtraField() { - var json = File.ReadAllText("./TestAssets/Identity/SystemAndUserAssignedOuterExtraField.json"); + var json = File.ReadAllText(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestAssets", "Identity", "SystemAndUserAssignedOuterExtraField.json")); JsonDocument document = JsonDocument.Parse(json); JsonElement rootElement = document.RootElement; var identityJsonProperty = rootElement.EnumerateObject().ElementAt(1); diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceIdentifierTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceIdentifierTests.cs index 39e95ccf9893c..d3763223a8715 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceIdentifierTests.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/ResourceIdentifierTests.cs @@ -18,6 +18,64 @@ public void Setup() { } + [TestCase("")] + [TestCase(" ")] + [TestCase("asdfghj")] + [TestCase("123456")] + [TestCase("!@#$%^&*/")] + [TestCase("/subscriptions/")] + [TestCase("/0c2f6471-1bf0-4dda-aec3-cb9272f09575/myRg/")] + public void InvalidRPIds(string invalidID) + { + Assert.Throws(() => { ResourceIdentifier subject = invalidID; }); + Assert.Throws(() => { ResourceIdentifier subject = new ResourceIdentifier(invalidID); }); + } + + [TestCase (null)] + [TestCase (TrackedResourceId)] + [TestCase(ChildResourceId)] + [TestCase(ResourceGroupResourceId)] + [TestCase(LocationResourceId)] + [TestCase(SubscriptionResourceId)] + public void ImplicitConstructor(string resourceProviderID) + { + string x = resourceProviderID; + string y; + ResourceIdentifier z; + + z = x; + y = z; + + if (resourceProviderID is null) + { + Assert.IsNull(z); + Assert.IsNull(y); + } + else + { + Assert.AreEqual(resourceProviderID, y); + } + } + + [TestCase (TrackedResourceId)] + [TestCase (ChildResourceId)] + [TestCase (ResourceGroupResourceId)] + [TestCase (LocationResourceId)] + [TestCase (SubscriptionResourceId)] + [TestCase(null)] + public void PublicConstructor(string resourceProviderID) + { + if (resourceProviderID is null) + { + Assert.Throws(() => { ResourceIdentifier myResource = new ResourceIdentifier(resourceProviderID); }); + } + else + { + ResourceIdentifier myResource = new ResourceIdentifier(resourceProviderID); + Assert.AreEqual(myResource.ToString(), resourceProviderID); + } + } + [TestCase("0c2f6471-1bf0-4dda-aec3-cb9272f09575", "myRg", "Microsoft.Compute", "virtualMachines", "myVM")] [TestCase("0c2f6471-1bf0-4dda-aec3-cb9272f09575", "!@#$%^&*()-_+=;:'\",<.>/?", "Microsoft.Network", "virtualNetworks", "MvVM_vnet")] [TestCase("0c2f6471-1bf0-4dda-aec3-cb9272f09575", "myRg", "Microsoft.Network", "publicIpAddresses", "!@#$%^&*()-_+=;:'\",<.>/?")] @@ -49,6 +107,17 @@ public void CanParseExtensionResourceIds(string baseId, string extensionNamespac Assert.AreEqual(targetResourceId, subject.Parent); } + [TestCase ("0c2f6471-1bf0-4dda-aec3-cb9272f09575", "myRg", "Microsoft.Web","appServices/myApp/config", "appServices/config")] + public void CanParseProxyResource(string subscription, string rg, string resourceNamespace, string resource, string type) + { + string id = $"/subscriptions/{subscription}/resourceGroups/{rg}/providers/{resourceNamespace}/{resource}"; + ResourceIdentifier subject = id; + Assert.AreEqual(subject.ToString(), id); + Assert.AreEqual(subject.Subscription, subscription); + Assert.AreEqual(subject.Type.Namespace, resourceNamespace); + Assert.AreEqual(subject.Type.Type, type); + } + [Test] public void CanParseSubscriptions() { @@ -119,5 +188,53 @@ public void CheckHashCode(bool expected, string resourceId1, string resourceId2) ResourceIdentifier resourceIdentifier2 = new ResourceIdentifier(resourceId2); Assert.AreEqual(expected, resourceIdentifier1.GetHashCode() == resourceIdentifier2.GetHashCode()); } + + [TestCase(TrackedResourceId, TrackedResourceId, true)] + [TestCase(ChildResourceId, ChildResourceId, true)] + [TestCase(null, null, true)] + [TestCase(TrackedResourceId, ChildResourceId, false)] + [TestCase(ChildResourceId, TrackedResourceId, false)] + [TestCase(null, TrackedResourceId, false)] + public void Equals(string resourceProviderID1, string resourceProviderID2, bool expected) + { + ResourceIdentifier a = resourceProviderID1; + ResourceIdentifier b = resourceProviderID2; + if(a != null) + Assert.AreEqual(expected, a.Equals(b)); + + Assert.AreEqual(expected, ResourceIdentifier.Equals(a,b)); + } + + [TestCase(TrackedResourceId, TrackedResourceId, 0)] + [TestCase(TrackedResourceId, ChildResourceId, -1)] + [TestCase(ChildResourceId, TrackedResourceId, 1)] + [TestCase(TrackedResourceId, null, 1)] + [TestCase(null, TrackedResourceId, -1)] + [TestCase(null, null, 0)] + public void CompareToResourceProvider(string resourceProviderID1, string resourceProviderID2, int expected) + { + ResourceIdentifier a = resourceProviderID1; + ResourceIdentifier b = resourceProviderID2; + if (a != null) + Assert.AreEqual(expected, a.CompareTo(b)); + + Assert.AreEqual(expected, ResourceIdentifier.CompareTo(a, b)); + } + + [TestCase(TrackedResourceId, TrackedResourceId, 0)] + [TestCase(TrackedResourceId, ChildResourceId, -1)] + [TestCase(ChildResourceId, TrackedResourceId, 1)] + [TestCase(TrackedResourceId, null, 1)] + [TestCase(null, TrackedResourceId, -1)] + [TestCase(null, null, 0)] + public void CompareToString(string resourceProviderID1, string resourceProviderID2, int expected) + { + ResourceIdentifier a = resourceProviderID1; + string b = resourceProviderID2; + if (a != null) + Assert.AreEqual(expected, a.CompareTo(b)); + + Assert.AreEqual(expected, ResourceIdentifier.CompareTo(a, b)); + } } } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs index 9489d4925482f..452b0192edf15 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs @@ -10,7 +10,6 @@ public class SubscriptionOperationsTests { [TestCase(null)] [TestCase("")] - [Ignore("Will remove after ADO 5122")] public void TestGetResourceGroupOpsArgNullException(string resourceGroupName) { var client = new AzureResourceManagerClient(); @@ -23,7 +22,6 @@ public void TestGetResourceGroupOpsArgNullException(string resourceGroupName) [TestCase("te$st")] [TestCase("te#st")] [TestCase("te#st")] - [Ignore("Will remove after ADO 5122")] public void TestGetResourceGroupOpsArgException(string resourceGroupName) { var client = new AzureResourceManagerClient(); @@ -32,7 +30,6 @@ public void TestGetResourceGroupOpsArgException(string resourceGroupName) } [TestCase("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] - [Ignore("Will remove after ADO 5122")] public void TestGetResourceGroupOpsOutOfRangeArgException(string resourceGroupName) { var client = new AzureResourceManagerClient(); @@ -45,7 +42,6 @@ public void TestGetResourceGroupOpsOutOfRangeArgException(string resourceGroupNa [TestCase("t")] [TestCase("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] [TestCase("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] - [Ignore("Will remove after ADO 5122")] public void TestGetResourceGroupOpsValid(string resourceGroupName) { var client = new AzureResourceManagerClient(); diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TaggableResourceTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TaggableResourceTests.cs index 94023cf197e68..028930db4fbbd 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TaggableResourceTests.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/TaggableResourceTests.cs @@ -1,16 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Azure.Identity; -using NUnit.Framework; using System; using System.Collections.Generic; using System.Threading.Tasks; +using NUnit.Framework; namespace Azure.ResourceManager.Core.Tests { - [TestFixture] [Ignore("Will remove after ADO 5122")] + [TestFixture] public class TaggableResourceTests { private static readonly IDictionary UpdateTags = new Dictionary { { "UpdateKey1", "UpdateValue1" }, { "UpdateKey2", "UpdateValue2" } }; @@ -19,13 +18,13 @@ public class TaggableResourceTests private ResourceGroup _rg; [SetUp] - public async Task GlobalSetUp() + public void GlobalSetUp() { var armClient = new AzureResourceManagerClient(); _rg = armClient.DefaultSubscription.GetResourceGroupContainer().Construct(LocationData.WestUS2).CreateOrUpdate($"{Environment.UserName}-rg-{Environment.TickCount}").Value; - await _rg.StartAddTag("key1", "value1").WaitForCompletionAsync(); - await _rg.StartAddTag("key2", "value2").WaitForCompletionAsync(); + _rg = _rg.AddTag("key1", "value1"); + _rg = _rg.AddTag("key2", "value2"); } [TearDown] diff --git a/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetOperations.cs b/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetOperations.cs index bcbfa46697e26..2fa2d675a44e8 100644 --- a/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetOperations.cs +++ b/sdk/resourcemanager/Proto.Client/compute/AvailabilitySetOperations.cs @@ -178,7 +178,8 @@ await Operations.UpdateAsync(Id.ResourceGroup, Id.Name, patchable, cancellationT public ArmResponse AddTag(string key, string value) { var resource = GetResource(); - var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + var patchable = new AvailabilitySetUpdate(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return Update(patchable); } @@ -194,7 +195,8 @@ public ArmResponse AddTag(string key, string value) public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + var patchable = new AvailabilitySetUpdate(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return await UpdateAsync(patchable); } @@ -212,7 +214,8 @@ public async Task> AddTagAsync(string key, string v public ArmOperation StartAddTag(string key, string value) { var resource = GetResource(); - var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + var patchable = new AvailabilitySetUpdate(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return StartUpdate(patchable); } @@ -231,7 +234,8 @@ public ArmOperation StartAddTag(string key, string value) public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new AvailabilitySetUpdate() { Tags = resource.Data.Tags }; + var patchable = new AvailabilitySetUpdate(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return await StartUpdateAsync(patchable); } diff --git a/sdk/resourcemanager/Proto.Client/compute/Placeholder/AvailabilitySetData.cs b/sdk/resourcemanager/Proto.Client/compute/Placeholder/AvailabilitySetData.cs index eeb3884ddd7cc..57b1f66a3602a 100644 --- a/sdk/resourcemanager/Proto.Client/compute/Placeholder/AvailabilitySetData.cs +++ b/sdk/resourcemanager/Proto.Client/compute/Placeholder/AvailabilitySetData.cs @@ -19,12 +19,9 @@ public class AvailabilitySetData : TrackedResource class. /// /// The availability set to initialize. - public AvailabilitySetData(Azure.ResourceManager.Compute.Models.AvailabilitySet aset) : base(aset.Id, aset.Location, aset) + public AvailabilitySetData(Azure.ResourceManager.Compute.Models.AvailabilitySet aset) + : base(aset.Id, aset.Location, aset) { - if (null == aset.Tags) - { - aset.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - } } /// Resource tags. @@ -58,7 +55,6 @@ public int? PlatformFaultDomainCount public IList VirtualMachines { get => Model.VirtualMachines; - set => Model.VirtualMachines = value; } /// Specifies information about the proximity placement group that the availability set should be assigned to. <br><br>Minimum api-version: 2018-04-01. @@ -69,6 +65,6 @@ public SubResource ProximityPlacementGroup } /// The resource status information. - public IList Statuses => Model.Statuses; + public IReadOnlyList Statuses => Model.Statuses; } } diff --git a/sdk/resourcemanager/Proto.Client/compute/Placeholder/VirtualMachineData.cs b/sdk/resourcemanager/Proto.Client/compute/Placeholder/VirtualMachineData.cs index 85da7d80752d3..65f46a4961fc5 100644 --- a/sdk/resourcemanager/Proto.Client/compute/Placeholder/VirtualMachineData.cs +++ b/sdk/resourcemanager/Proto.Client/compute/Placeholder/VirtualMachineData.cs @@ -16,10 +16,6 @@ public class VirtualMachineData : TrackedResource The virtual machine to initialize. public VirtualMachineData(Azure.ResourceManager.Compute.Models.VirtualMachine vm) : base(vm.Id, vm.Location, vm) { - if (null == vm.Tags) - { - vm.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - } } /// Resource tags. @@ -129,7 +125,6 @@ public HardwareProfile HardwareProfile public IList Zones { get => Model.Zones; - set => Model.Zones = value; } /// The identity of the virtual machine, if configured. @@ -158,7 +153,7 @@ private ResourceIdentity VmIdentityToIdentity(VirtualMachineIdentity vmIdentity) /// /// Gets the virtual machine extensions. /// - public IList Resources => Model.Resources; + public IReadOnlyList Resources => Model.Resources; /// Specifies information about the marketplace image used to create the virtual machine. This element is only used for marketplace images. Before you can use a marketplace image from an API, you must enable the image for programmatic use. In the Azure portal, find the marketplace image that you want to use and then click **Want to deploy programmatically, Get Started ->**. Enter any required information and then click **Save**. public Azure.ResourceManager.Compute.Models.Plan Plan diff --git a/sdk/resourcemanager/Proto.Client/compute/Proto.Compute.csproj b/sdk/resourcemanager/Proto.Client/compute/Proto.Compute.csproj index a54bd8ca5685e..c0592e2e8b519 100644 --- a/sdk/resourcemanager/Proto.Client/compute/Proto.Compute.csproj +++ b/sdk/resourcemanager/Proto.Client/compute/Proto.Compute.csproj @@ -7,7 +7,7 @@ - + diff --git a/sdk/resourcemanager/Proto.Client/compute/VirtualMachineContainer.cs b/sdk/resourcemanager/Proto.Client/compute/VirtualMachineContainer.cs index 3aecf4a36761a..aa8e158fd3496 100644 --- a/sdk/resourcemanager/Proto.Client/compute/VirtualMachineContainer.cs +++ b/sdk/resourcemanager/Proto.Client/compute/VirtualMachineContainer.cs @@ -113,7 +113,7 @@ public VirtualMachineModelBuilder Construct(string hostName, string adminUser, s var parent = GetParentResource(); var vm = new Azure.ResourceManager.Compute.Models.VirtualMachine(location ?? parent.Data.Location) { - NetworkProfile = new NetworkProfile { NetworkInterfaces = new[] { new NetworkInterfaceReference() { Id = networkInterfaceId } } }, + NetworkProfile = new NetworkProfile(), OsProfile = new OSProfile { ComputerName = hostName, @@ -130,11 +130,11 @@ public VirtualMachineModelBuilder Construct(string hostName, string adminUser, s Sku = "2019-Datacenter", Version = "latest" }, - DataDisks = new List() }, HardwareProfile = new HardwareProfile() { VmSize = VirtualMachineSizeTypes.StandardB1Ms }, AvailabilitySet = new SubResource() { Id = availabilitySetId } }; + vm.NetworkProfile.NetworkInterfaces.Add(new NetworkInterfaceReference() { Id = networkInterfaceId }); return new VirtualMachineModelBuilder(this, new VirtualMachineData(vm)); } diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceContainer.cs b/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceContainer.cs index 0411de68c29fe..40755a1598727 100644 --- a/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceContainer.cs +++ b/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceContainer.cs @@ -79,18 +79,16 @@ public ArmBuilder Construct(string subne var nic = new Azure.ResourceManager.Network.Models.NetworkInterface() { Location = location ?? parent.Data.Location, - IpConfigurations = new List() - { - new NetworkInterfaceIPConfiguration() - { - Name = "Primary", - Primary = true, - Subnet = new Azure.ResourceManager.Network.Models.Subnet() { Id = subnetId }, - PrivateIPAllocationMethod = IPAllocationMethod.Dynamic, - } - } }; + nic.IpConfigurations.Add(new NetworkInterfaceIPConfiguration() + { + Name = "Primary", + Primary = true, + Subnet = new Azure.ResourceManager.Network.Models.Subnet() { Id = subnetId }, + PrivateIPAllocationMethod = IPAllocationMethod.Dynamic, + }); + if (ip != null) nic.IpConfigurations[0].PublicIPAddress = new PublicIPAddress() { Id = ip.Id }; diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceOperations.cs b/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceOperations.cs index aef48c4d088b5..04e4e780218b0 100644 --- a/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceOperations.cs +++ b/sdk/resourcemanager/Proto.Client/network/NetworkInterfaceOperations.cs @@ -124,7 +124,8 @@ await Operations.GetAsync(Id.ResourceGroup, Id.Name, null, cancellationToken), public ArmResponse AddTag(string key, string value) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new NetworkInterface(this, new NetworkInterfaceData(n))); @@ -141,7 +142,8 @@ public ArmResponse AddTag(string key, string value) public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmResponse( await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), @@ -160,7 +162,8 @@ await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellat public ArmOperation StartAddTag(string key, string value) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new NetworkInterface(this, new NetworkInterfaceData(n))); @@ -180,7 +183,8 @@ public ArmOperation StartAddTag(string key, string value) public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmOperation( await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroup.cs b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroup.cs index cdfeeaa6e2e62..c006c06c247ca 100644 --- a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroup.cs +++ b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroup.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Azure.ResourceManager.Network.Models; using Azure.ResourceManager.Core; -using System.Threading; using System.Threading.Tasks; namespace Proto.Network diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupContainer.cs b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupContainer.cs index 8ed56d4ab33ad..325c75e596718 100644 --- a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupContainer.cs +++ b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupContainer.cs @@ -94,19 +94,23 @@ public ArmBuilder Construct(Loca Location = locationData ?? parent.Data.Location }; var index = 0; - nsg.SecurityRules = openPorts.Select(openPort => new SecurityRule + foreach(int port in openPorts) { - Name = $"Port{openPort}", - Priority = 1000 + (++index), - Protocol = SecurityRuleProtocol.Tcp, - Access = SecurityRuleAccess.Allow, - Direction = SecurityRuleDirection.Inbound, - SourcePortRange = "*", - SourceAddressPrefix = "*", - DestinationPortRange = $"{openPort}", - DestinationAddressPrefix = "*", - Description = $"Port_{openPort}" - }).ToList(); + var securityRule = new SecurityRule + { + Name = $"Port{port}", + Priority = 1000 + (++index), + Protocol = SecurityRuleProtocol.Tcp, + Access = SecurityRuleAccess.Allow, + Direction = SecurityRuleDirection.Inbound, + SourcePortRange = "*", + SourceAddressPrefix = "*", + DestinationPortRange = $"{port}", + DestinationAddressPrefix = "*", + Description = $"Port_{port}" + }; + nsg.SecurityRules.Add(securityRule); + } return new ArmBuilder(this, new NetworkSecurityGroupData(nsg)); } @@ -124,19 +128,23 @@ public ArmBuilder Construct(para Location = parent.Data.Location, }; var index = 0; - nsg.SecurityRules = openPorts.Select(openPort => new SecurityRule + foreach (int port in openPorts) { - Name = $"Port{openPort}", - Priority = 1000 + (++index), - Protocol = SecurityRuleProtocol.Tcp, - Access = SecurityRuleAccess.Allow, - Direction = SecurityRuleDirection.Inbound, - SourcePortRange = "*", - SourceAddressPrefix = "*", - DestinationPortRange = $"{openPort}", - DestinationAddressPrefix = "*", - Description = $"Port_{openPort}" - }).ToList(); + var securityRule = new SecurityRule + { + Name = $"Port{port}", + Priority = 1000 + (++index), + Protocol = SecurityRuleProtocol.Tcp, + Access = SecurityRuleAccess.Allow, + Direction = SecurityRuleDirection.Inbound, + SourcePortRange = "*", + SourceAddressPrefix = "*", + DestinationPortRange = $"{port}", + DestinationAddressPrefix = "*", + Description = $"Port_{port}" + }; + nsg.SecurityRules.Add(securityRule); + } return new ArmBuilder(this, new NetworkSecurityGroupData(nsg)); } diff --git a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupOperations.cs b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupOperations.cs index 728ce64016911..9ab60bf661a49 100644 --- a/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupOperations.cs +++ b/sdk/resourcemanager/Proto.Client/network/NetworkSecurityGroupOperations.cs @@ -79,16 +79,16 @@ public ArmOperation UpdateRules(CancellationToken cancella matchingRule.Access = rule.Access; matchingRule.Description = rule.Description; matchingRule.DestinationAddressPrefix = rule.DestinationAddressPrefix; - matchingRule.DestinationAddressPrefixes = rule.DestinationAddressPrefixes; + //matchingRule.DestinationAddressPrefixes = rule.DestinationAddressPrefixes; matchingRule.DestinationPortRange = rule.DestinationPortRange; - matchingRule.DestinationPortRanges = rule.DestinationPortRanges; + //matchingRule.DestinationPortRanges = rule.DestinationPortRanges; matchingRule.Direction = rule.Direction; matchingRule.Priority = rule.Priority; matchingRule.Protocol = rule.Protocol; matchingRule.SourceAddressPrefix = rule.SourceAddressPrefix; - matchingRule.SourceAddressPrefixes = rule.SourceAddressPrefixes; + //matchingRule.SourceAddressPrefixes = rule.SourceAddressPrefixes; matchingRule.SourcePortRange = rule.SourcePortRange; - matchingRule.SourcePortRanges = rule.SourcePortRanges; + //matchingRule.SourcePortRanges = rule.SourcePortRanges; } else { @@ -119,7 +119,8 @@ public override async Task> GetAsync(Cancellat public ArmResponse AddTag(string key, string value) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmResponse( Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), @@ -130,7 +131,8 @@ public ArmResponse AddTag(string key, string value) public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); @@ -140,7 +142,8 @@ public async Task> AddTagAsync(string key, str public ArmOperation StartAddTag(string key, string value) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmOperation( Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), @@ -151,7 +154,8 @@ public ArmOperation StartAddTag(string key, string value) public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new NetworkSecurityGroup(this, new NetworkSecurityGroupData(n))); diff --git a/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkInterfaceData.cs b/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkInterfaceData.cs index 6d68276e9e9e5..c69b4442041fd 100644 --- a/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkInterfaceData.cs +++ b/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkInterfaceData.cs @@ -21,10 +21,6 @@ public class NetworkInterfaceData : TrackedResource The low level network interace model. public NetworkInterfaceData(Azure.ResourceManager.Network.Models.NetworkInterface nic) : base(nic.Id, nic.Location, nic) { - if (null == nic.Tags) - { - nic.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - } } /// @@ -67,13 +63,12 @@ public Azure.ResourceManager.Network.Models.NetworkSecurityGroup NetworkSecurity public IList IpConfigurations { get => Model.IpConfigurations; - set => Model.IpConfigurations = value; } /// /// Gets a list of TapConfigurations of the newtork interface. /// - public IList TapConfigurations=> Model.TapConfigurations; + public IReadOnlyList TapConfigurations=> Model.TapConfigurations; /// /// Gets or sets the DNS settings in network interface. @@ -115,7 +110,7 @@ public bool? EnableIPForwarding /// /// Gets a list of references to linked BareMetal resources. /// - public IList HostedWorkloads => Model.HostedWorkloads; + public IReadOnlyList HostedWorkloads => Model.HostedWorkloads; /// /// Gets the resource GUID property of the network interface resource. diff --git a/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkSecurityGroupData.cs b/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkSecurityGroupData.cs index a773444916554..bfb5dfadc68db 100644 --- a/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkSecurityGroupData.cs +++ b/sdk/resourcemanager/Proto.Client/network/Placeholder/NetworkSecurityGroupData.cs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Azure.ResourceManager.Network.Models; using Azure.ResourceManager.Core; -using System; +using Azure.ResourceManager.Network.Models; using System.Collections.Generic; namespace Proto.Network @@ -21,10 +20,6 @@ public class NetworkSecurityGroupData : public NetworkSecurityGroupData(Azure.ResourceManager.Network.Models.NetworkSecurityGroup nsg) : base(nsg.Id, nsg.Location, nsg) { - if (null == nsg.Tags) - { - nsg.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - } } /// Resource tags. @@ -40,21 +35,20 @@ public NetworkSecurityGroupData(Azure.ResourceManager.Network.Models.NetworkSecu public IList SecurityRules { get => Model.SecurityRules; - set => Model.SecurityRules = value; } /// The default security rules of network security group. - public IList DefaultSecurityRules => Model.DefaultSecurityRules; + public IReadOnlyList DefaultSecurityRules => Model.DefaultSecurityRules; /// A collection of references to network interfaces. - public IList NetworkInterfaces => Model.NetworkInterfaces; + public IReadOnlyList NetworkInterfaces => Model.NetworkInterfaces; /// A collection of references to subnets. - public IList Subnets => Model.Subnets; + public IReadOnlyList Subnets => Model.Subnets; /// A collection of references to flow log resources. - public IList FlowLogs => Model.FlowLogs; + public IReadOnlyList FlowLogs => Model.FlowLogs; /// The resource GUID property of the network security group resource. public string ResourceGuid => Model.ResourceGuid; diff --git a/sdk/resourcemanager/Proto.Client/network/Placeholder/PublicIPAddressData.cs b/sdk/resourcemanager/Proto.Client/network/Placeholder/PublicIPAddressData.cs index 173bc6e3b2bab..84303c1f83fc3 100644 --- a/sdk/resourcemanager/Proto.Client/network/Placeholder/PublicIPAddressData.cs +++ b/sdk/resourcemanager/Proto.Client/network/Placeholder/PublicIPAddressData.cs @@ -24,10 +24,6 @@ public class PublicIPAddressData : TrackedResource /// The public IP address to initialize. public PublicIPAddressData(PublicIPAddress ip) : base(ip.Id, ip.Location, ip) { - if (null == ip.Tags) - { - ip.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - } } /// Resource tags. @@ -50,7 +46,6 @@ public PublicIPAddressSku Sku public IList Zones { get => Model.Zones; - set => Model.Zones = value; } /// The public IP address allocation method. @@ -88,7 +83,6 @@ public DdosSettings DdosSettings public IList IpTags { get => Model.IpTags; - set => Model.IpTags = value; } /// The IP address associated with the public IP address resource. diff --git a/sdk/resourcemanager/Proto.Client/network/Placeholder/SubnetData.cs b/sdk/resourcemanager/Proto.Client/network/Placeholder/SubnetData.cs index c37382ed431b9..66fec3350b717 100644 --- a/sdk/resourcemanager/Proto.Client/network/Placeholder/SubnetData.cs +++ b/sdk/resourcemanager/Proto.Client/network/Placeholder/SubnetData.cs @@ -35,27 +35,25 @@ public SubnetData(Azure.ResourceManager.Network.Models.Subnet sub) : base(sub.Id public IList Delegations { get => Model.Delegations; - set => Model.Delegations = value; } /// An array of references to services injecting into this subnet. - public IList ServiceAssociationLinks => Model.ServiceAssociationLinks; + public IReadOnlyList ServiceAssociationLinks => Model.ServiceAssociationLinks; /// An array of references to the external resources using subnet. - public IList ResourceNavigationLinks => Model.ResourceNavigationLinks; + public IReadOnlyList ResourceNavigationLinks => Model.ResourceNavigationLinks; /// Array of IpAllocation which reference this subnet. public IList IpAllocations { get => Model.IpAllocations; - set => Model.IpAllocations = value; } /// Array of IP configuration profiles which reference this subnet. - public IList IpConfigurationProfiles => Model.IpConfigurationProfiles; + public IReadOnlyList IpConfigurationProfiles => Model.IpConfigurationProfiles; /// An array of references to the network interface IP configurations using subnet. - public IList IpConfigurations => Model.IpConfigurations; + public IReadOnlyList IpConfigurations => Model.IpConfigurations; /// Enable or Disable apply network policies on private end point in the subnet. public string PrivateEndpointNetworkPolicies @@ -65,13 +63,12 @@ public string PrivateEndpointNetworkPolicies } /// An array of references to private endpoints. - public IList PrivateEndpoints => Model.PrivateEndpoints; + public IReadOnlyList PrivateEndpoints => Model.PrivateEndpoints; /// An array of service endpoints. public IList ServiceEndpoints { get => Model.ServiceEndpoints; - set => Model.ServiceEndpoints = value; } /// Nat gateway associated with this subnet. @@ -99,7 +96,6 @@ public Azure.ResourceManager.Network.Models.NetworkSecurityGroup NetworkSecurity public IList AddressPrefixes { get => Model.AddressPrefixes; - set => Model.AddressPrefixes = value; } /// The address prefix for the subnet. @@ -116,7 +112,6 @@ public string AddressPrefix public IList ServiceEndpointPolicies { get => Model.ServiceEndpointPolicies; - set => Model.ServiceEndpointPolicies = value; } /// Enable or Disable apply network policies on private link service in the subnet. diff --git a/sdk/resourcemanager/Proto.Client/network/Placeholder/VirtualNetworkData.cs b/sdk/resourcemanager/Proto.Client/network/Placeholder/VirtualNetworkData.cs index cfa6f92b13721..07e62b448ca98 100644 --- a/sdk/resourcemanager/Proto.Client/network/Placeholder/VirtualNetworkData.cs +++ b/sdk/resourcemanager/Proto.Client/network/Placeholder/VirtualNetworkData.cs @@ -21,10 +21,6 @@ public class VirtualNetworkData : TrackedResource The virtual nerwork to initialize. public VirtualNetworkData(Azure.ResourceManager.Network.Models.VirtualNetwork vnet) : base(vnet.Id, vnet.Location, vnet) { - if (null == vnet.Tags) - { - vnet.Tags = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - } } /// @@ -54,14 +50,12 @@ public DhcpOptions DhcpOptions public IList Subnets { get => Model.Subnets; - set => Model.Subnets = value; } /// A list of peering in a Virtual Network. public IList VirtualNetworkPeerings { get => Model.VirtualNetworkPeerings; - set => Model.VirtualNetworkPeerings = value; } /// The resourceGuid property of the Virtual Network resource. @@ -103,7 +97,6 @@ public VirtualNetworkBgpCommunities BgpCommunities public IList IpAllocations { get => Model.IpAllocations; - set => Model.IpAllocations = value; } } } diff --git a/sdk/resourcemanager/Proto.Client/network/Proto.Network.csproj b/sdk/resourcemanager/Proto.Client/network/Proto.Network.csproj index 09aa34832109b..5bee3360da488 100644 --- a/sdk/resourcemanager/Proto.Client/network/Proto.Network.csproj +++ b/sdk/resourcemanager/Proto.Client/network/Proto.Network.csproj @@ -10,7 +10,7 @@ - + diff --git a/sdk/resourcemanager/Proto.Client/network/PublicIpAddressOperations.cs b/sdk/resourcemanager/Proto.Client/network/PublicIpAddressOperations.cs index 179fc85b486e0..e4fb92f73b81b 100644 --- a/sdk/resourcemanager/Proto.Client/network/PublicIpAddressOperations.cs +++ b/sdk/resourcemanager/Proto.Client/network/PublicIpAddressOperations.cs @@ -129,7 +129,8 @@ public override async Task> GetAsync(CancellationTo public ArmResponse AddTag(string key, string value) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new PublicIpAddress(this, new PublicIPAddressData(n))); @@ -146,7 +147,8 @@ public ArmResponse AddTag(string key, string value) public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new PublicIpAddress(this, new PublicIPAddressData(n))); @@ -162,7 +164,8 @@ public async Task> AddTagAsync(string key, string v public ArmOperation StartAddTag(string key, string value) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new PublicIpAddress(this, new PublicIPAddressData(n))); @@ -179,7 +182,8 @@ public ArmOperation StartAddTag(string key, string value) public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new PublicIpAddress(this, new PublicIPAddressData(n))); diff --git a/sdk/resourcemanager/Proto.Client/network/VirtualNetworkContainer.cs b/sdk/resourcemanager/Proto.Client/network/VirtualNetworkContainer.cs index 0f7792c3f3d1b..8d4bb9475e1ae 100644 --- a/sdk/resourcemanager/Proto.Client/network/VirtualNetworkContainer.cs +++ b/sdk/resourcemanager/Proto.Client/network/VirtualNetworkContainer.cs @@ -82,8 +82,9 @@ public ArmBuilder Construct(string vnetCidr, var vnet = new Azure.ResourceManager.Network.Models.VirtualNetwork() { Location = location ?? parent.Data.Location, - AddressSpace = new AddressSpace() { AddressPrefixes = new List() { vnetCidr } }, + AddressSpace = new AddressSpace(), }; + vnet.AddressSpace.AddressPrefixes.Add(vnetCidr); return new ArmBuilder(this, new VirtualNetworkData(vnet)); } diff --git a/sdk/resourcemanager/Proto.Client/network/VirtualNetworkOperations.cs b/sdk/resourcemanager/Proto.Client/network/VirtualNetworkOperations.cs index c94059468ad1c..a1157d5864747 100644 --- a/sdk/resourcemanager/Proto.Client/network/VirtualNetworkOperations.cs +++ b/sdk/resourcemanager/Proto.Client/network/VirtualNetworkOperations.cs @@ -131,7 +131,8 @@ public async override Task> GetAsync(CancellationTok public ArmResponse AddTag(string key, string value) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmResponse(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new VirtualNetwork(this, new VirtualNetworkData(n))); @@ -151,7 +152,8 @@ public ArmResponse AddTag(string key, string value) public async Task> AddTagAsync(string key, string value, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmResponse(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new VirtualNetwork(this, new VirtualNetworkData(n))); @@ -170,7 +172,8 @@ public async Task> AddTagAsync(string key, string va public ArmOperation StartAddTag(string key, string value) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmOperation(Operations.UpdateTags(Id.ResourceGroup, Id.Name, patchable), n => new VirtualNetwork(this, new VirtualNetworkData(n))); @@ -190,7 +193,8 @@ public ArmOperation StartAddTag(string key, string value) public async Task> StartAddTagAsync(string key, string value, CancellationToken cancellationToken = default) { var resource = GetResource(); - var patchable = new TagsObject() { Tags = resource.Data.Tags }; + var patchable = new TagsObject(); + patchable.Tags.ReplaceWith(resource.Data.Tags); patchable.Tags[key] = value; return new PhArmOperation(await Operations.UpdateTagsAsync(Id.ResourceGroup, Id.Name, patchable, cancellationToken), n => new VirtualNetwork(this, new VirtualNetworkData(n))); From 1e06b9682bd4323c90210b95f7bbbacd82ef81a5 Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Fri, 26 Feb 2021 17:48:14 -0800 Subject: [PATCH 17/18] ignore tests which require live azure until 5122 is closed --- .../tests/SubscriptionOperationsTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs index 452b0192edf15..9489d4925482f 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/SubscriptionOperationsTests.cs @@ -10,6 +10,7 @@ public class SubscriptionOperationsTests { [TestCase(null)] [TestCase("")] + [Ignore("Will remove after ADO 5122")] public void TestGetResourceGroupOpsArgNullException(string resourceGroupName) { var client = new AzureResourceManagerClient(); @@ -22,6 +23,7 @@ public void TestGetResourceGroupOpsArgNullException(string resourceGroupName) [TestCase("te$st")] [TestCase("te#st")] [TestCase("te#st")] + [Ignore("Will remove after ADO 5122")] public void TestGetResourceGroupOpsArgException(string resourceGroupName) { var client = new AzureResourceManagerClient(); @@ -30,6 +32,7 @@ public void TestGetResourceGroupOpsArgException(string resourceGroupName) } [TestCase("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [Ignore("Will remove after ADO 5122")] public void TestGetResourceGroupOpsOutOfRangeArgException(string resourceGroupName) { var client = new AzureResourceManagerClient(); @@ -42,6 +45,7 @@ public void TestGetResourceGroupOpsOutOfRangeArgException(string resourceGroupNa [TestCase("t")] [TestCase("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] [TestCase("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + [Ignore("Will remove after ADO 5122")] public void TestGetResourceGroupOpsValid(string resourceGroupName) { var client = new AzureResourceManagerClient(); From 7fd9d1ab66ed5be4382b1f92bb4b5c92e6b4d8d9 Mon Sep 17 00:00:00 2001 From: m-nash <64171366+m-nash@users.noreply.github.com> Date: Fri, 26 Feb 2021 21:20:48 -0800 Subject: [PATCH 18/18] Update failing scenario test --- .../Proto.Client/src/Scenarios/GetFromOperations.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/sdk/resourcemanager/Proto.Client/src/Scenarios/GetFromOperations.cs b/sdk/resourcemanager/Proto.Client/src/Scenarios/GetFromOperations.cs index 34041115bf2f7..a8b5cbaf027c0 100644 --- a/sdk/resourcemanager/Proto.Client/src/Scenarios/GetFromOperations.cs +++ b/sdk/resourcemanager/Proto.Client/src/Scenarios/GetFromOperations.cs @@ -19,11 +19,8 @@ public override void Execute() var vnet = resourceGroup.GetVirtualNetworkOperations(Context.VmName + "_vnet").Get().Value; _ = vnet.GetSubnetOperations(Context.SubnetName).Get().Value; _ = resourceGroup.GetNetworkSecurityGroupOperations(Context.NsgName).Get().Value; - _ = resourceGroup.GetPublicIpAddressOperations($"{Context.VmName}_ip").Get().Value; _ = resourceGroup.GetNetworkInterfaceOperations($"{Context.VmName}_nic").Get().Value; _ = resourceGroup.GetVirtualMachineOperations(Context.VmName).Get().Value; - - } } }