Skip to content

Commit 21dc96b

Browse files
committed
Handle invalid locations for Get-AzureRmResourceProvider.
1 parent 0258384 commit 21dc96b

File tree

8 files changed

+3288
-250
lines changed

8 files changed

+3288
-250
lines changed

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Extensions/StringExtensions.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Extensions
1717
using System;
1818
using System.Collections.Generic;
1919
using System.Globalization;
20+
using System.Linq;
2021

2122
/// <summary>
2223
/// String extension methods
@@ -94,5 +95,24 @@ public static bool IsDateTime(this string source, string format, DateTimeStyles
9495
DateTime parsedDateTime;
9596
return DateTime.TryParseExact(s: source, format: format, provider: null, style: styles, result: out parsedDateTime);
9697
}
98+
99+
/// <summary>
100+
/// Normalize a location string.
101+
/// </summary>
102+
/// <param name="location">The location string.</param>
103+
public static string ToNormalizedLocation(this string location)
104+
{
105+
return location == null ? null : new string(location.Where(c => char.IsLetterOrDigit(c)).ToArray());
106+
}
107+
108+
/// <summary>
109+
/// Compare two location strings.
110+
/// </summary>
111+
/// <param name="location1">The first location string.</param>
112+
/// <param name="location2">The second location string.</param>
113+
public static bool EqualsAsLocation(this string location1, string location2)
114+
{
115+
return location1.ToNormalizedLocation().EqualsInsensitively(location2.ToNormalizedLocation());
116+
}
97117
}
98118
}

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Implementation/Providers/GetAzureProviderCmdlet.cs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414

1515
namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation
1616
{
17-
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels;
1817
using System.Linq;
1918
using System.Management.Automation;
19+
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Extensions;
20+
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkExtensions;
21+
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels;
2022

2123
/// <summary>
2224
/// Get an existing resource.
@@ -59,7 +61,7 @@ public class GetAzureProviderCmdlet : ResourceManagerCmdletBase
5961
/// </summary>
6062
public override void ExecuteCmdlet()
6163
{
62-
var providers = this.ResourceManagerSdkClient.ListPSResourceProviders(providerName: this.ProviderNamespace, listAvailable: this.ListAvailable, location: this.Location);
64+
var providers = this.ListPSResourceProviders();
6365

6466
if (!string.IsNullOrEmpty(this.ProviderNamespace))
6567
{
@@ -89,5 +91,38 @@ public override void ExecuteCmdlet()
8991
this.WriteObject(providers, enumerateCollection: true);
9092
}
9193
}
94+
95+
private PSResourceProvider[] ListPSResourceProviders()
96+
{
97+
var providers = this.ResourceManagerSdkClient.ListResourceProviders(
98+
providerName: this.ProviderNamespace,
99+
listAvailable: this.ListAvailable);
100+
101+
if (string.IsNullOrEmpty(this.Location))
102+
{
103+
return providers
104+
.Select(provider => provider.ToPSResourceProvider())
105+
.ToArray();
106+
}
107+
108+
var validLocations = this.SubscriptionSdkClient.ListLocations(DefaultContext.Subscription.Id.ToString());
109+
110+
if (!validLocations.Any(loc => loc.Name.EqualsAsLocation(this.Location)))
111+
{
112+
return new PSResourceProvider[] { };
113+
}
114+
115+
foreach (var provider in providers)
116+
{
117+
provider.ResourceTypes = provider.ResourceTypes
118+
.Where(type => !type.Locations.Any() || type.Locations.Any(loc => loc.EqualsAsLocation(this.Location)))
119+
.ToList();
120+
}
121+
122+
return providers
123+
.Where(provider => provider.ResourceTypes.Any())
124+
.Select(provider => provider.ToPSResourceProvider())
125+
.ToArray();
126+
}
92127
}
93128
}

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Implementation/Resource/MoveAzureResourceCmdlet.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,6 @@ public class MoveAzureResourceCommand : ResourceManagerCmdletBase
5252
[Alias("Id", "SubscriptionId")]
5353
public Guid? DestinationSubscriptionId { get; set; }
5454

55-
/// <summary>
56-
/// Gets or sets a value that indicates if the user should be prompted for confirmation.
57-
/// </summary>
58-
[Parameter(Mandatory = false, HelpMessage = "Do not ask for confirmation.")]
59-
public SwitchParameter Force { get; set; }
60-
6155
/// <summary>
6256
/// Gets or sets the ids of the resources to move.
6357
/// </summary>
@@ -66,6 +60,12 @@ public class MoveAzureResourceCommand : ResourceManagerCmdletBase
6660
[ValidateNotNullOrEmpty]
6761
public string[] ResourceId { get; set; }
6862

63+
/// <summary>
64+
/// Gets or sets a value that indicates if the user should be prompted for confirmation.
65+
/// </summary>
66+
[Parameter(Mandatory = false, HelpMessage = "Do not ask for confirmation.")]
67+
public SwitchParameter Force { get; set; }
68+
6969
/// <summary>
7070
/// Collects subscription ids from the pipeline.
7171
/// </summary>

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Microsoft.Azure.Commands.ResourceManager.Cmdlets.format.ps1xml

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -282,34 +282,34 @@
282282
</ListControl>
283283
</View>
284284
<View>
285-
<Name>Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceProviderType</Name>
285+
<Name>Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceProvider</Name>
286286
<ViewSelectedBy>
287-
<TypeName>Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceProviderType</TypeName>
287+
<TypeName>Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceProvider</TypeName>
288288
</ViewSelectedBy>
289-
<TableControl>
290-
<TableHeaders>
291-
<TableColumnHeader>
292-
<Label>Name</Label>
293-
<Width>50</Width>
294-
</TableColumnHeader>
295-
<TableColumnHeader>
296-
<Label>Locations</Label>
297-
</TableColumnHeader>
298-
</TableHeaders>
299-
<TableRowEntries>
300-
<TableRowEntry>
301-
<Wrap/>
302-
<TableColumnItems>
303-
<TableColumnItem>
304-
<ScriptBlock>$_.Name</ScriptBlock>
305-
</TableColumnItem>
306-
<TableColumnItem>
307-
<ScriptBlock>$_.LocationsString</ScriptBlock>
308-
</TableColumnItem>
309-
</TableColumnItems>
310-
</TableRowEntry>
311-
</TableRowEntries>
312-
</TableControl>
289+
<ListControl>
290+
<ListEntries>
291+
<ListEntry>
292+
<ListItems>
293+
<ListItem>
294+
<Label>ProviderNamespace</Label>
295+
<PropertyName>ProviderNamespace</PropertyName>
296+
</ListItem>
297+
<ListItem>
298+
<Label>RegistrationState</Label>
299+
<PropertyName>RegistrationState</PropertyName>
300+
</ListItem>
301+
<ListItem>
302+
<Label>ResourceTypes</Label>
303+
<PropertyName>ResourceTypes</PropertyName>
304+
</ListItem>
305+
<ListItem>
306+
<Label>Locations</Label>
307+
<PropertyName>Locations</PropertyName>
308+
</ListItem>
309+
</ListItems>
310+
</ListEntry>
311+
</ListEntries>
312+
</ListControl>
313313
</View>
314314
</ViewDefinitions>
315315
</Configuration>

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/SdkClient/ResourceManagerSdkClient.cs

Lines changed: 2 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Microsoft.Azure.Commands.Common.Authentication.Models;
1717
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Components;
1818
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Entities;
19+
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Extensions;
1920
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkExtensions;
2021
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels;
2122
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Utilities;
@@ -430,30 +431,6 @@ private TemplateValidationInfo CheckBasicDeploymentErrors(string resourceGroup,
430431
return new TemplateValidationInfo(validationResult);
431432
}
432433

433-
public virtual PSResourceProvider[] ListPSResourceProviders(string providerName = null, bool listAvailable = false, string location = null)
434-
{
435-
var providers = this.ListResourceProviders(providerName: providerName, listAvailable: listAvailable);
436-
437-
if (string.IsNullOrEmpty(location))
438-
{
439-
return providers
440-
.Select(provider => provider.ToPSResourceProvider())
441-
.ToArray();
442-
}
443-
444-
foreach (var provider in providers)
445-
{
446-
provider.ResourceTypes = provider.ResourceTypes
447-
.Where(type => !type.Locations.Any() || this.ContainsNormalizedLocation(type.Locations.ToArray(), location))
448-
.ToList();
449-
}
450-
451-
return providers
452-
.Where(provider => provider.ResourceTypes.Any())
453-
.Select(provider => provider.ToPSResourceProvider())
454-
.ToArray();
455-
}
456-
457434
public virtual List<Provider> ListResourceProviders(string providerName = null, bool listAvailable = true)
458435
{
459436
if (!string.IsNullOrEmpty(providerName))
@@ -485,16 +462,6 @@ public virtual List<Provider> ListResourceProviders(string providerName = null,
485462
}
486463
}
487464

488-
private bool ContainsNormalizedLocation(string[] locations, string location)
489-
{
490-
return locations.Any(existingLocation => this.NormalizeLetterOrDigitToUpperInvariant(existingLocation).Equals(this.NormalizeLetterOrDigitToUpperInvariant(location)));
491-
}
492-
493-
private string NormalizeLetterOrDigitToUpperInvariant(string value)
494-
{
495-
return value != null ? new string(value.Where(c => char.IsLetterOrDigit(c)).ToArray()).ToUpperInvariant() : null;
496-
}
497-
498465
private bool IsProviderRegistered(Provider provider)
499466
{
500467
return string.Equals(
@@ -540,64 +507,6 @@ public ResourceIdentifier[] ParseResourceIds(string[] resourceIds)
540507
.ToArray();
541508
}
542509

543-
/// <summary>
544-
/// Get a mapping of Resource providers that support the operations API (/operations) to the operations api-version supported for that RP
545-
/// (Current logic is to prefer the latest "non-test' api-version. If there are no such version, choose the latest one)
546-
/// </summary>
547-
public Dictionary<string, string> GetResourceProvidersWithOperationsSupport()
548-
{
549-
PSResourceProvider[] allProviders = this.ListPSResourceProviders(listAvailable: true);
550-
551-
Dictionary<string, string> providersSupportingOperations = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
552-
PSResourceProviderResourceType[] providerResourceTypes = null;
553-
554-
foreach (PSResourceProvider provider in allProviders)
555-
{
556-
providerResourceTypes = provider.ResourceTypes;
557-
if (providerResourceTypes != null && providerResourceTypes.Any())
558-
{
559-
PSResourceProviderResourceType operationsResourceType = providerResourceTypes.Where(r => r != null && r.ResourceTypeName == ResourceManagerSdkClient.Operations).FirstOrDefault();
560-
if (operationsResourceType != null &&
561-
operationsResourceType.ApiVersions != null &&
562-
operationsResourceType.ApiVersions.Any())
563-
{
564-
string[] allowedTestPrefixes = new[] { "-preview", "-alpha", "-beta", "-rc", "-privatepreview" };
565-
List<string> nonTestApiVersions = new List<string>();
566-
567-
foreach (string apiVersion in operationsResourceType.ApiVersions)
568-
{
569-
bool isTestApiVersion = false;
570-
foreach (string testPrefix in allowedTestPrefixes)
571-
{
572-
if (apiVersion.EndsWith(testPrefix, StringComparison.InvariantCultureIgnoreCase))
573-
{
574-
isTestApiVersion = true;
575-
break;
576-
}
577-
}
578-
579-
if (isTestApiVersion == false && !nonTestApiVersions.Contains(apiVersion))
580-
{
581-
nonTestApiVersions.Add(apiVersion);
582-
}
583-
}
584-
585-
if (nonTestApiVersions.Any())
586-
{
587-
string latestNonTestApiVersion = nonTestApiVersions.OrderBy(o => o).Last();
588-
providersSupportingOperations.Add(provider.ProviderNamespace, latestNonTestApiVersion);
589-
}
590-
else
591-
{
592-
providersSupportingOperations.Add(provider.ProviderNamespace, operationsResourceType.ApiVersions.OrderBy(o => o).Last());
593-
}
594-
}
595-
}
596-
}
597-
598-
return providersSupportingOperations;
599-
}
600-
601510
/// <summary>
602511
/// Creates a new resource group
603512
/// </summary>
@@ -667,7 +576,7 @@ public virtual List<PSResourceGroup> FilterResourceGroups(string name, Hashtable
667576
}
668577

669578
resourceGroups = !string.IsNullOrEmpty(location)
670-
? resourceGroups.Where(resourceGroup => this.NormalizeLetterOrDigitToUpperInvariant(resourceGroup.Location).Equals(this.NormalizeLetterOrDigitToUpperInvariant(location))).ToList()
579+
? resourceGroups.Where(resourceGroup => resourceGroup.Location.EqualsAsLocation(location)).ToList()
671580
: resourceGroups;
672581

673582
// TODO: Replace with server side filtering when available

src/ResourceManager/Resources/Commands.Resources.Test/Providers/GetAzureProviderCmdletTests.cs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,31 +49,40 @@ public class GetAzureProviderCmdletTests : RMTestBase
4949
private MockCommandRuntime mockRuntime;
5050

5151
/// <summary>
52-
/// A mock of the client
52+
/// A mock of the IProvidersOperations
5353
/// </summary>
5454
private readonly Mock<IProvidersOperations> providerOperationsMock;
5555

56+
/// <summary>
57+
/// A mock of the ISubscriptionsOperations
58+
/// </summary>
59+
private readonly Mock<ISubscriptionsOperations> subscriptionsOperationsMock;
60+
5661
/// <summary>
5762
/// Initializes a new instance of the <see cref="GetAzureProviderCmdletTests"/> class.
5863
/// </summary>
5964
public GetAzureProviderCmdletTests(ITestOutputHelper output)
6065
{
6166
this.providerOperationsMock = new Mock<IProvidersOperations>();
67+
this.subscriptionsOperationsMock = new Mock<ISubscriptionsOperations>();
6268
XunitTracingInterceptor.AddToContext(new XunitTracingInterceptor(output));
6369
var resourceManagementClient = new Mock<Microsoft.Azure.Management.ResourceManager.IResourceManagementClient>();
70+
var subscriptionClient = new Mock<Microsoft.Azure.Management.ResourceManager.ISubscriptionClient>();
6471

6572
resourceManagementClient
6673
.SetupGet(client => client.Providers)
6774
.Returns(() => this.providerOperationsMock.Object);
6875

76+
subscriptionClient
77+
.SetupGet(client => client.Subscriptions)
78+
.Returns(() => this.subscriptionsOperationsMock.Object);
79+
6980
this.commandRuntimeMock = new Mock<ICommandRuntime>();
7081
this.cmdlet = new GetAzureProviderCmdletTest
7182
{
7283
//CommandRuntime = commandRuntimeMock.Object,
73-
ResourceManagerSdkClient = new ResourceManagerSdkClient
74-
{
75-
ResourceManagementClient = resourceManagementClient.Object,
76-
}
84+
ResourceManagerSdkClient = new ResourceManagerSdkClient(resourceManagementClient.Object),
85+
SubscriptionSdkClient = new SubscriptionSdkClient(subscriptionClient.Object)
7786
};
7887
PSCmdletExtensions.SetCommandRuntimeMock(cmdlet, commandRuntimeMock.Object);
7988
mockRuntime = new MockCommandRuntime();
@@ -134,6 +143,25 @@ public void GetsResourceProviderTests()
134143
.Setup(f => f.ListWithHttpMessagesAsync(null, null, It.IsAny<CancellationToken>()))
135144
.Returns(() => Task.FromResult(result));
136145

146+
var locationList = new List<Location>
147+
{
148+
new Location
149+
{
150+
Name = "southus",
151+
DisplayName = "South US",
152+
}
153+
};
154+
var pagableLocations = new Page<Location>();
155+
pagableLocations.SetItemValue<Location>(locationList);
156+
var locationsResult = new AzureOperationResponse<IPage<Location>>()
157+
{
158+
Body = pagableLocations
159+
};
160+
this.subscriptionsOperationsMock
161+
.Setup(f => f.ListLocationsWithHttpMessagesAsync(It.IsAny<string>(), null, It.IsAny<CancellationToken>()))
162+
.Returns(() => Task.FromResult(locationsResult));
163+
164+
137165
// 1. List only registered providers
138166
this.commandRuntimeMock
139167
.Setup(m => m.WriteObject(It.IsAny<object>()))

src/ResourceManager/Resources/Commands.Resources.Test/ScenarioTests/ProviderTests.ps1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ function Test-AzureProvider
2626

2727
Assert-True { $allProviders.Length -gt $defaultProviders.Length }
2828

29+
$nonProviders = Get-AzureRmResourceProvider -Location "abc"
30+
31+
Assert-True { $nonProviders.Length -eq 0 }
32+
2933
Register-AzureRmResourceProvider -ProviderName "Microsoft.ApiManagement" -Force
3034

3135
$endTime = [DateTime]::UtcNow.AddMinutes(5)

0 commit comments

Comments
 (0)