diff --git a/src/ResourceManager/Common/Commands.ResourceManager.Common/ArgumentCompleters/ResourceGroupCompleter.cs b/src/ResourceManager/Common/Commands.ResourceManager.Common/ArgumentCompleters/ResourceGroupCompleter.cs
new file mode 100644
index 000000000000..447e3a7ba776
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.ResourceManager.Common/ArgumentCompleters/ResourceGroupCompleter.cs
@@ -0,0 +1,158 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+namespace Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters
+{
+ using Commands.Common.Authentication;
+ using Commands.Common.Authentication.Abstractions;
+ using Management.Internal.Resources;
+ using Management.Internal.Resources.Models;
+ using Properties;
+ using Rest.Azure;
+ using System;
+ using System.Collections.Concurrent;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Management.Automation;
+
+ ///
+ /// This attribute will allow the user to autocomplete the -ResourceGroup parameter of a cmdlet with valid resource groups
+ ///
+ public class ResourceGroupCompleterAttribute : ArgumentCompleterAttribute
+ {
+ private static IDictionary> _resourceGroupNamesDictionary = new ConcurrentDictionary>();
+ private static readonly object _lock = new object();
+
+ protected static IList ResourceGroupNames
+ {
+ get
+ {
+ lock (_lock)
+ {
+ IAzureContext context = AzureRmProfileProvider.Instance.Profile.DefaultContext;
+ var contextHash = HashContext(context);
+ if (!_resourceGroupNamesDictionary.ContainsKey(contextHash))
+ {
+ var tempResourceGroupList = new List();
+ try
+ {
+ var instance = AzureSession.Instance;
+ var client = instance.ClientFactory.CreateCustomArmClient(
+ context.Environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager),
+ instance.AuthenticationFactory.GetServiceClientCredentials(context, AzureEnvironment.Endpoint.ResourceManager),
+ instance.ClientFactory.GetCustomHandlers());
+ client.SubscriptionId = context.Subscription.Id;
+ // Retrieve only the first page of ResourceGroups to display to the user
+ var resourceGroups = client.ResourceGroups.ListAsync();
+ if (resourceGroups.Wait(TimeSpan.FromSeconds(5)))
+ {
+ tempResourceGroupList = CreateResourceGroupList(resourceGroups.Result);
+ if (resourceGroups.Result != null)
+ {
+ _resourceGroupNamesDictionary[contextHash] = tempResourceGroupList;
+ }
+ }
+#if DEBUG
+ else
+ {
+ throw new Exception("client.ResourceGroups call timed out");
+ }
+#endif
+ }
+
+ catch (Exception ex)
+ {
+#if DEBUG
+ throw ex;
+#endif
+ }
+
+ return tempResourceGroupList;
+ }
+
+ else
+ {
+ return _resourceGroupNamesDictionary[contextHash];
+ }
+ }
+ }
+ }
+
+ ///
+ /// This class will provide a list of resource groups that are available to the user (with default resource group first if it exists). This will then be available to the user to tab through.
+ /// Example: [Parameter(ParameterSetName = ListByNameInTenantParameterSet, ValueFromPipelineByPropertyName = true, Mandatory = false), ResourceGroupCompleter()]
+ ///
+ ///
+ public ResourceGroupCompleterAttribute() : base(CreateScriptBlock())
+ {
+ }
+
+ public static string[] GetResourceGroups()
+ {
+ IAzureContext context = AzureRmProfileProvider.Instance.Profile.DefaultContext;
+ var resourceGroupNamesCopy = ResourceGroupNames;
+ if (context.IsPropertySet(Resources.DefaultResourceGroupKey))
+ {
+ return GetResourceGroups(resourceGroupNamesCopy, context.ExtendedProperties[Resources.DefaultResourceGroupKey]);
+ }
+ return GetResourceGroups(resourceGroupNamesCopy, null);
+ }
+
+ public static string[] GetResourceGroups(IList resourceGroupNames, string defaultResourceGroup)
+ {
+ if (defaultResourceGroup != null)
+ {
+ if (resourceGroupNames.Contains(defaultResourceGroup))
+ {
+ resourceGroupNames.Remove(defaultResourceGroup);
+ }
+ resourceGroupNames.Insert(0, defaultResourceGroup);
+ }
+ return resourceGroupNames.ToArray();
+ }
+
+ private static ScriptBlock CreateScriptBlock()
+ {
+ string script = "param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)\n" +
+ "$locations = [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceGroupCompleterAttribute]::GetResourceGroups()\n" +
+ "$locations | Where-Object { $_ -Like \"$wordToComplete*\" } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }";
+ ScriptBlock scriptBlock = ScriptBlock.Create(script);
+ return scriptBlock;
+ }
+
+ private static int HashContext(IAzureContext context)
+ {
+ return (context.Account.Id + context.Environment.Name + context.Subscription.Id + context.Tenant.Id).GetHashCode();
+ }
+
+ public static List CreateResourceGroupList(IPage result)
+ {
+ var tempResourceGroupList = new List();
+ if (result != null)
+ {
+ foreach (ResourceGroup resourceGroup in result)
+ {
+ tempResourceGroupList.Add(resourceGroup.Name);
+ }
+ }
+#if DEBUG
+ else
+ {
+ throw new Exception("Result from client.ResourceGroups is null");
+ }
+#endif
+ return tempResourceGroupList;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ResourceManager/Common/Commands.ResourceManager.Common/Commands.ResourceManager.Common.csproj b/src/ResourceManager/Common/Commands.ResourceManager.Common/Commands.ResourceManager.Common.csproj
index 2571ef4028ef..37834ab7db84 100644
--- a/src/ResourceManager/Common/Commands.ResourceManager.Common/Commands.ResourceManager.Common.csproj
+++ b/src/ResourceManager/Common/Commands.ResourceManager.Common/Commands.ResourceManager.Common.csproj
@@ -185,6 +185,7 @@
True
Resources.resx
+
diff --git a/src/ResourceManager/Common/Commands.ResourceManager.Common/Properties/Resources.Designer.cs b/src/ResourceManager/Common/Commands.ResourceManager.Common/Properties/Resources.Designer.cs
index e302b959a4cb..abcfc8e6ae08 100644
--- a/src/ResourceManager/Common/Commands.ResourceManager.Common/Properties/Resources.Designer.cs
+++ b/src/ResourceManager/Common/Commands.ResourceManager.Common/Properties/Resources.Designer.cs
@@ -137,6 +137,15 @@ public static string DataCollectionSaveFileInformation {
}
}
+ ///
+ /// Looks up a localized string similar to Default Resource Group.
+ ///
+ public static string DefaultResourceGroupKey {
+ get {
+ return ResourceManager.GetString("DefaultResourceGroupKey", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to No default subscription has been designated. Use Select-AzureSubscription -Default <subscriptionName> to set the default subscription..
///
diff --git a/src/ResourceManager/Common/Commands.ResourceManager.Common/Properties/Resources.resx b/src/ResourceManager/Common/Commands.ResourceManager.Common/Properties/Resources.resx
index 458e32bcf32a..18733a6bb2bc 100644
--- a/src/ResourceManager/Common/Commands.ResourceManager.Common/Properties/Resources.resx
+++ b/src/ResourceManager/Common/Commands.ResourceManager.Common/Properties/Resources.resx
@@ -216,4 +216,7 @@ The data is collected by Microsoft.
Use the Disable-AzureDataCollection cmdlet to turn the feature Off. The cmdlet can be found in the Azure module. To disable data collection: PS > Disable-AzureDataCollection.
Use the Enable-AzureDataCollection cmdlet to turn the feature On. The cmdlet can be found in the Azure module. To enable data collection: PS > Enable-AzureDataCollection.
+
+ Default Resource Group
+
\ No newline at end of file
diff --git a/src/ResourceManager/Profile/Commands.Profile.Test/Commands.Profile.Test.csproj b/src/ResourceManager/Profile/Commands.Profile.Test/Commands.Profile.Test.csproj
index 5597a6bf7702..046805ce092d 100644
--- a/src/ResourceManager/Profile/Commands.Profile.Test/Commands.Profile.Test.csproj
+++ b/src/ResourceManager/Profile/Commands.Profile.Test/Commands.Profile.Test.csproj
@@ -203,6 +203,7 @@
+
@@ -286,4 +287,4 @@
-
+
\ No newline at end of file
diff --git a/src/ResourceManager/Profile/Commands.Profile.Test/ResourceGroupCompleterUnitTests.cs b/src/ResourceManager/Profile/Commands.Profile.Test/ResourceGroupCompleterUnitTests.cs
new file mode 100644
index 000000000000..0e090ece3fcb
--- /dev/null
+++ b/src/ResourceManager/Profile/Commands.Profile.Test/ResourceGroupCompleterUnitTests.cs
@@ -0,0 +1,95 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters;
+using Microsoft.WindowsAzure.Commands.ScenarioTest;
+using System;
+using System.Collections.Generic;
+using Xunit;
+
+namespace Microsoft.Azure.Commands.Profile.Test
+{
+ public class ResourceGroupCompleterUnitTests
+ {
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void ReturnsEmptyListWhenNoResourceGroupsExist()
+ {
+ IList resourceGroupsReturned = new List();
+ Assert.Collection(ResourceGroupCompleterAttribute.GetResourceGroups(resourceGroupsReturned, null));
+ }
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void OneResourceGroupNoDefault()
+ {
+ IList resourceGroupsReturned = new List();
+ resourceGroupsReturned.Add("test1");
+ Assert.Collection(ResourceGroupCompleterAttribute.GetResourceGroups(resourceGroupsReturned, null), e1 => Assert.Equal("test1", e1));
+ }
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void OneResourceGroupWithDefault()
+ {
+ IList resourceGroupsReturned = new List();
+ resourceGroupsReturned.Add("test1");
+ Assert.Collection(ResourceGroupCompleterAttribute.GetResourceGroups(resourceGroupsReturned, "test1"), e1 => Assert.Equal("test1", e1));
+ }
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void OneResourceGroupWithInvalidDefault()
+ {
+ IList resourceGroupsReturned = new List();
+ resourceGroupsReturned.Add("test1");
+ Assert.Collection(ResourceGroupCompleterAttribute.GetResourceGroups(resourceGroupsReturned, "defaultOutOfPage"), e1 => Assert.Equal("defaultOutOfPage", e1),
+ e2 => Assert.Equal("test1", e2));
+ }
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void MultipleResourceGroupsNoDefault()
+ {
+ IList resourceGroupsReturned = new List();
+ resourceGroupsReturned.Add("test1");
+ resourceGroupsReturned.Add("test2");
+ resourceGroupsReturned.Add("test3");
+ resourceGroupsReturned.Add("test4");
+ Assert.Collection(ResourceGroupCompleterAttribute.GetResourceGroups(resourceGroupsReturned, null), e1 => Assert.Equal("test1", e1),
+ e2 => Assert.Equal("test2", e2), e3 => Assert.Equal("test3", e3), e4 => Assert.Equal("test4", e4));
+ }
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void MultipleResourceGroupsWithDefault()
+ {
+ IList resourceGroupsReturned = new List();
+ resourceGroupsReturned.Add("test1");
+ resourceGroupsReturned.Add("test2");
+ resourceGroupsReturned.Add("test3");
+ resourceGroupsReturned.Add("test4");
+ Assert.Collection(ResourceGroupCompleterAttribute.GetResourceGroups(resourceGroupsReturned, "test3"), e1 => Assert.Equal("test3", e1),
+ e2 => Assert.Equal("test1", e2), e3 => Assert.Equal("test2", e3), e4 => Assert.Equal("test4", e4));
+ }
+
+ [Fact]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ public void ThrowsErrorWhenResultNull()
+ {
+ var ex = Assert.Throws(() => ResourceGroupCompleterAttribute.CreateResourceGroupList(null));
+ Assert.Equal(ex.Message, "Result from client.ResourceGroups is null");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ResourceManager/Profile/Commands.Profile/Default/SetAzureRMDefault.cs b/src/ResourceManager/Profile/Commands.Profile/Default/SetAzureRMDefault.cs
index 1992cd4c96d1..b3b4b6e23db0 100644
--- a/src/ResourceManager/Profile/Commands.Profile/Default/SetAzureRMDefault.cs
+++ b/src/ResourceManager/Profile/Commands.Profile/Default/SetAzureRMDefault.cs
@@ -19,6 +19,7 @@
using Microsoft.Azure.Commands.Profile.Models;
using Microsoft.Azure.Commands.Profile.Properties;
using Microsoft.Azure.Commands.ResourceManager.Common;
+using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters;
using Microsoft.Azure.Management.Internal.Resources;
using Microsoft.Azure.Management.Internal.Resources.Models;
using System.Management.Automation;
@@ -36,6 +37,7 @@ public class SetAzureRMDefaultCommand : AzureContextModificationCmdlet
private const string ResourceGroupNameParameterSet = "ResourceGroupName";
[Parameter(ParameterSetName = ResourceGroupNameParameterSet, Mandatory = false, HelpMessage = "Name of the resource group being set as default", ValueFromPipelineByPropertyName = true)]
+ [ResourceGroupCompleter()]
[ValidateNotNullOrEmpty]
public string ResourceGroupName { get; set; }