diff --git a/.gitignore b/.gitignore
index 4dbf1c158517..1d7bdd04deda 100644
--- a/.gitignore
+++ b/.gitignore
@@ -211,4 +211,5 @@ FakesAssemblies/
/tools/*.dll
*.GhostDoc.xml
pingme.txt
-groupMapping*.json
\ No newline at end of file
+groupMapping*.json
+.vscode/
\ No newline at end of file
diff --git a/setup/azurecmdfiles.wxi b/setup/azurecmdfiles.wxi
index 328afceaf728..eb1f3f283574 100644
--- a/setup/azurecmdfiles.wxi
+++ b/setup/azurecmdfiles.wxi
@@ -271,6 +271,9 @@
+
+
+
@@ -3148,6 +3151,7 @@
+
diff --git a/src/Common/Commands.Common/AzureLongRunningJob.cs b/src/Common/Commands.Common/AzureLongRunningJob.cs
index 1d1f61314cb0..0652e289a5f5 100644
--- a/src/Common/Commands.Common/AzureLongRunningJob.cs
+++ b/src/Common/Commands.Common/AzureLongRunningJob.cs
@@ -191,11 +191,13 @@ public static U CopyCmdlet(U cmdlet) where U : AzurePSCmdlet
returnValue.MyInvocation.BoundParameters.Add(parameter.Key, parameter.Value);
}
- foreach (var field in returnType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
+
+ foreach (var field in returnType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
field.SafeCopyValue(source: cmdlet, target: returnValue);
}
+ cmdlet.SafeCopyParameterSet(returnValue);
return returnValue as U;
}
@@ -829,6 +831,9 @@ private bool InvokeShouldMethodAndWaitForResults(Func shouldMethod
}
}
+ ///
+ /// Stop job execution
+ ///
public override void StopJob()
{
ShouldMethodStreamItem stream;
diff --git a/src/Common/Commands.Common/Extensions/CmdletExtensions.cs b/src/Common/Commands.Common/Extensions/CmdletExtensions.cs
index a93c4e0021f5..8d09a038932f 100644
--- a/src/Common/Commands.Common/Extensions/CmdletExtensions.cs
+++ b/src/Common/Commands.Common/Extensions/CmdletExtensions.cs
@@ -178,6 +178,30 @@ public static void SafeCopyValue(this FieldInfo field, T source, T target)
}
}
+ ///
+ /// Safely copy the selected parameter set from one cmdlet to another
+ ///
+ /// The cmdlet type
+ /// The cmdlet to copy the parameter set name from
+ /// The cmdlet to copy to
+ public static void SafeCopyParameterSet(this T source, T target) where T: AzurePSCmdlet
+ {
+ if (source != null && target != null)
+ {
+ if (!string.IsNullOrWhiteSpace(source.ParameterSetName))
+ {
+ try
+ {
+ target.SetParameterSet(source.ParameterSetName);
+ }
+ catch
+ {
+
+ }
+ }
+ }
+ }
+
public static string AsAbsoluteLocation(this string realtivePath)
{
return Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, realtivePath));
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/Commands.Common.Strategies.UnitTest.csproj b/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/Commands.Common.Strategies.UnitTest.csproj
new file mode 100644
index 000000000000..3e19b17f3158
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/Commands.Common.Strategies.UnitTest.csproj
@@ -0,0 +1,89 @@
+
+
+
+ Debug
+ AnyCPU
+ {5C052681-DA3C-4FEA-8E02-7DFBB2A18B22}
+ Library
+ Properties
+ Microsoft.Azure.Commands.Common.Strategies.UnitTest
+ Microsoft.Azure.Commands.Common.Strategies.UnitTest
+ v4.5.2
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {eea69772-d41b-482a-9252-2b4595c59e53}
+ Commands.Common.Strategies
+
+
+
+
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/Properties/AssemblyInfo.cs b/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000000..6d1737f580a8
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Commands.Common.Strategies.UnitTest")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Commands.Common.Strategies.UnitTest")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("5c052681-da3c-4fea-8e02-7dfbb2a18b22")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/TimeSlotTest.cs b/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/TimeSlotTest.cs
new file mode 100644
index 000000000000..8aca6597a420
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies.UnitTest/TimeSlotTest.cs
@@ -0,0 +1,72 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Microsoft.Azure.Commands.Common.Strategies.UnitTest
+{
+ [TestClass]
+ public class TimeSlotTest
+ {
+ [TestMethod]
+ public void AddTest()
+ {
+ var first = new TimeSlot();
+
+ Assert.AreEqual(0, first.Duration);
+ Assert.AreEqual(0, first.TaskCount);
+ Assert.AreEqual(null, first.Next);
+
+ var next = first.AddTask(50);
+
+ Assert.AreEqual(50, first.Duration);
+ Assert.AreEqual(1, first.TaskCount);
+ Assert.AreEqual(next, first.Next);
+
+ Assert.AreEqual(10.0, first.GetTaskProgress(10));
+
+ Assert.AreEqual(0, next.Duration);
+ Assert.AreEqual(0, next.TaskCount);
+ Assert.AreEqual(null, next.Next);
+
+ var next2 = first.AddTask(50);
+
+ Assert.AreEqual(50, first.Duration);
+ Assert.AreEqual(2, first.TaskCount);
+ Assert.AreEqual(next2, first.Next);
+ Assert.AreEqual(next, first.Next);
+
+ Assert.AreEqual(20.0, first.GetTaskProgress(40));
+
+ Assert.AreEqual(0, next2.Duration);
+ Assert.AreEqual(0, next2.TaskCount);
+ Assert.AreEqual(null, next2.Next);
+
+ var next3 = first.AddTask(30);
+ Assert.AreEqual(30, first.Duration);
+ Assert.AreEqual(3, first.TaskCount);
+ Assert.AreEqual(next3, first.Next);
+
+ Assert.AreEqual(3.0, first.GetTaskProgress(9));
+
+ Assert.AreEqual(20, next3.Duration);
+ Assert.AreEqual(2, next3.TaskCount);
+ Assert.AreEqual(next2, next3.Next);
+ Assert.AreEqual(next, next3.Next);
+
+ Assert.AreEqual(10.0 + 5, first.GetTaskProgress(40));
+
+ Assert.AreEqual(0, next2.Duration);
+ Assert.AreEqual(0, next2.TaskCount);
+ Assert.AreEqual(null, next2.Next);
+
+ var next4 = first.AddTask(75);
+ Assert.AreEqual(25, next2.Duration);
+ Assert.AreEqual(1, next2.TaskCount);
+ Assert.AreEqual(next4, next2.Next);
+
+ Assert.AreEqual((30.0 / 4) + (20.0 / 3) + 20, first.GetTaskProgress(70));
+
+ Assert.AreEqual(0, next4.Duration);
+ Assert.AreEqual(0, next4.TaskCount);
+ Assert.AreEqual(null, next4.Next);
+ }
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/Commands.Common.Strategies.csproj b/src/ResourceManager/Common/Commands.Common.Strategies/Commands.Common.Strategies.csproj
new file mode 100644
index 000000000000..35b3c5524a97
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/Commands.Common.Strategies.csproj
@@ -0,0 +1,104 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {EEA69772-D41B-482A-9252-2B4595C59E53}
+ Library
+ Properties
+ Microsoft.Azure.Commands.Common.Strategies
+ Microsoft.Azure.Commands.Common.Strategies
+ v4.5.2
+ 512
+
+
+ true
+ full
+ false
+ ..\..\..\Package\Debug\ResourceManager\AzureResourceManager\AzureRM.Compute\
+ TRACE;DEBUG
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE;SIGN
+ prompt
+ 4
+ true
+ true
+ MSSharedLibKey.snk
+
+
+
+ ..\..\..\packages\Microsoft.Rest.ClientRuntime.2.3.10\lib\net452\Microsoft.Rest.ClientRuntime.dll
+ True
+
+
+ ..\..\..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.10\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll
+ True
+
+
+ ..\..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/Common.Strategies.Netcore.csproj b/src/ResourceManager/Common/Commands.Common.Strategies/Common.Strategies.Netcore.csproj
new file mode 100644
index 000000000000..562c9416f9bc
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/Common.Strategies.Netcore.csproj
@@ -0,0 +1,30 @@
+
+
+
+
+
+ netcoreapp2.0
+ Microsoft.Azure.Commands.Common.Strategies
+ win10-x64
+ Microsoft.Azure.Commands.Common.Strategies
+ false
+
+
+
+ True
+ True
+ MSSharedLibKey.snk
+ TRACE;RELEASE;NETSTANDARD;SIGN
+
+
+
+ TRACE;DEBUG;NETSTANDARD
+ false
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/CreateOrUpdateAsyncParams.cs b/src/ResourceManager/Common/Commands.Common.Strategies/CreateOrUpdateAsyncParams.cs
new file mode 100644
index 000000000000..3c37cc1d1e01
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/CreateOrUpdateAsyncParams.cs
@@ -0,0 +1,56 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System.Threading;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public static class CreateOrUpdateAsyncParams
+ {
+ public static CreateOrUpdateAsyncParams Create(
+ string resourceGroupName,
+ string name,
+ TModel model,
+ CancellationToken cancellationToken)
+ => new CreateOrUpdateAsyncParams(
+ resourceGroupName, name, model, cancellationToken);
+ }
+
+ ///
+ /// Parameters for CreateOrUpdateAsync functions.
+ ///
+ ///
+ public sealed class CreateOrUpdateAsyncParams
+ {
+ public string ResourceGroupName { get; }
+
+ public string Name { get; }
+
+ public CancellationToken CancellationToken { get; }
+
+ public TModel Model { get; }
+
+ public CreateOrUpdateAsyncParams(
+ string resourceGroupName,
+ string name,
+ TModel model,
+ CancellationToken cancellationToken)
+ {
+ ResourceGroupName = resourceGroupName;
+ Name = name;
+ Model = model;
+ CancellationToken = cancellationToken;
+ }
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/EntityConfigExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/EntityConfigExtensions.cs
new file mode 100644
index 000000000000..85c5df027a9f
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/EntityConfigExtensions.cs
@@ -0,0 +1,27 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System.Collections.Generic;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public static class EntityConfigExtensions
+ {
+ public static string IdToString(this IEnumerable id)
+ => "/" + string.Join("/", id);
+
+ public static string DefaultIdStr(this IEntityConfig config)
+ => config.GetId(string.Empty).IdToString();
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/Extensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/Extensions.cs
new file mode 100644
index 000000000000..d4f9fb81a3e1
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/Extensions.cs
@@ -0,0 +1,41 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public static class Extensions
+ {
+ public static IEnumerable EmptyIfNull(this IEnumerable value)
+ => value ?? Enumerable.Empty();
+
+ public static TValue GetOrNull(
+ this IDictionary dictionary, TKey key)
+ where TValue : class
+ {
+ TValue result;
+ dictionary.TryGetValue(key, out result);
+ return result;
+ }
+
+ public static T GetOrAddWithCast(
+ this ConcurrentDictionary dictionary, TKey key, Func add)
+ where T : TBase
+ => (T)dictionary.GetOrAdd(key, _ => add());
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/GetAsyncParams.cs b/src/ResourceManager/Common/Commands.Common.Strategies/GetAsyncParams.cs
new file mode 100644
index 000000000000..d6f934bb8d23
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/GetAsyncParams.cs
@@ -0,0 +1,40 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System.Threading;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ ///
+ /// Parameters for GetAsync functions.
+ ///
+ public sealed class GetAsyncParams
+ {
+ public string ResourceGroupName { get; }
+
+ public string Name { get; }
+
+ public CancellationToken CancellationToken { get; }
+
+ public GetAsyncParams(
+ string resourceGroupName,
+ string name,
+ CancellationToken cancellationToken)
+ {
+ ResourceGroupName = resourceGroupName;
+ Name = name;
+ CancellationToken = cancellationToken;
+ }
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/GetStateExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/GetStateExtensions.cs
new file mode 100644
index 000000000000..81689a49b7e2
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/GetStateExtensions.cs
@@ -0,0 +1,72 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public static class GetStateExtensions
+ {
+ ///
+ /// Returns a current Azure state for the given resource (config).
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async Task GetStateAsync(
+ this ResourceConfig config,
+ IClient client,
+ CancellationToken cancellationToken)
+ where TModel : class
+ {
+ var context = new StateOperationContext(client, cancellationToken);
+ await context.GetStateAsyncDispatch(config);
+ return context.Result;
+ }
+
+ static Task GetStateAsyncDispatch(this StateOperationContext context, IResourceConfig config)
+ => config.Accept(new GetStateAsyncVisitor(), context);
+
+ static async Task GetStateAsync(
+ this StateOperationContext context, ResourceConfig config)
+ where TModel : class
+ => await context.GetOrAdd(
+ config,
+ async () =>
+ {
+ var info = await config.GetAsync(context.Client, context.CancellationToken);
+ // Get state of dependencies if the resource doesn't exist
+ if (info == null)
+ {
+ var tasks = config
+ .GetResourceDependencies()
+ .Select(context.GetStateAsyncDispatch);
+ await Task.WhenAll(tasks);
+ }
+ return info;
+ });
+
+ sealed class GetStateAsyncVisitor : IResourceConfigVisitor
+ {
+ public Task Visit(
+ ResourceConfig config, StateOperationContext context)
+ where TModel : class
+ => context.GetStateAsync(config);
+ }
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IClient.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IClient.cs
new file mode 100644
index 000000000000..7e3a58416526
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/IClient.cs
@@ -0,0 +1,24 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Rest;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public interface IClient
+ {
+ T GetClient()
+ where T : ServiceClient;
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IEntityConfig.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IEntityConfig.cs
new file mode 100644
index 000000000000..7092a440ebc4
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/IEntityConfig.cs
@@ -0,0 +1,42 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System.Collections.Generic;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ ///
+ /// Base interface for ResourceConfig and NestedResourceConfig.
+ ///
+ public interface IEntityConfig
+ {
+ IEntityStrategy Strategy { get; }
+
+ string Name { get; }
+
+ IResourceConfig Resource { get; }
+
+ TResult Accept(
+ IEntityConfigVisitor visitor, TContext context);
+
+ IEnumerable GetId(string subscription);
+ }
+
+ public interface IEntityConfig : IEntityConfig
+ where TModel : class
+ {
+ TResult Accept(
+ IEntityConfigVisitor visitor, TContext context);
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IEntityConfigVisitor.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IEntityConfigVisitor.cs
new file mode 100644
index 000000000000..a275ab01dd86
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/IEntityConfigVisitor.cs
@@ -0,0 +1,36 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Common.Strategies
+{
+ public interface IEntityConfigVisitor
+ {
+ TResult Visit(ResourceConfig config, TContext context)
+ where TModel : class;
+
+ TResult Visit(
+ NestedResourceConfig config, TContext context)
+ where TModel : class
+ where TParentModel : class;
+ }
+
+ public interface IEntityConfigVisitor
+ where TModel : class
+ {
+ TResult Visit(ResourceConfig config, TContext context);
+
+ TResult Visit(NestedResourceConfig config, TContext context)
+ where TParentModel : class;
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IEntityStrategy.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IEntityStrategy.cs
new file mode 100644
index 000000000000..84f1e199668d
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/IEntityStrategy.cs
@@ -0,0 +1,23 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Common.Strategies
+{
+ ///
+ /// Base interface for ResourceStrategy and NestedResourceStrategy classes.
+ ///
+ public interface IEntityStrategy
+ {
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IReportProgress.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IReportProgress.cs
new file mode 100644
index 000000000000..9090a5391843
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/IReportProgress.cs
@@ -0,0 +1,25 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Common.Strategies
+{
+ public interface IProgressReport
+ {
+ void Start(ResourceConfig config)
+ where TModel : class;
+
+ void Done(ResourceConfig config, double progress)
+ where TModel : class;
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IResourceConfig.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IResourceConfig.cs
new file mode 100644
index 000000000000..657d9069b413
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/IResourceConfig.cs
@@ -0,0 +1,33 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System.Collections.Generic;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ ///
+ /// Base interface for ResourceConfig[].
+ ///
+ public interface IResourceConfig : IEntityConfig
+ {
+ new IResourceStrategy Strategy { get; }
+
+ string ResourceGroupName { get; }
+
+ IEnumerable Dependencies { get; }
+
+ TResult Accept(
+ IResourceConfigVisitor visitor, TContext context);
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IResourceConfigVisitor.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IResourceConfigVisitor.cs
new file mode 100644
index 000000000000..a837efdd355b
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/IResourceConfigVisitor.cs
@@ -0,0 +1,22 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Common.Strategies
+{
+ public interface IResourceConfigVisitor
+ {
+ TResult Visit(ResourceConfig config, TContext context)
+ where TModel : class;
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IResourceStrategy.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IResourceStrategy.cs
new file mode 100644
index 000000000000..8898e119c6c6
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/IResourceStrategy.cs
@@ -0,0 +1,10 @@
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ ///
+ /// Base interface for ResourceStrategy[].
+ ///
+ public interface IResourceStrategy : IEntityStrategy
+ {
+ string Type { get; }
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IShouldProcess.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IShouldProcess.cs
new file mode 100644
index 000000000000..423c1d22bc1a
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/IShouldProcess.cs
@@ -0,0 +1,24 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System.Threading.Tasks;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public interface IShouldProcess
+ {
+ Task ShouldCreate(ResourceConfig config, TModel model)
+ where TModel : class;
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/IState.cs b/src/ResourceManager/Common/Commands.Common.Strategies/IState.cs
new file mode 100644
index 000000000000..8179a8e7e5df
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/IState.cs
@@ -0,0 +1,27 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Common.Strategies
+{
+ ///
+ /// An Azure state which is a dictionary of models.
+ ///
+ public interface IState
+ {
+ TModel Get(ResourceConfig config)
+ where TModel : class;
+
+ bool Contains(IResourceConfig config);
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/LocationExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/LocationExtensions.cs
new file mode 100644
index 000000000000..8397d327a3e0
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/LocationExtensions.cs
@@ -0,0 +1,88 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System.Linq;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public static class LocationExtensions
+ {
+ ///
+ /// Get the best location for the given entity from the given state.
+ ///
+ ///
+ ///
+ ///
+ public static string GetLocation(this IState state, IResourceConfig config)
+ => state.GetDependencyLocationDispatch(config)?.Location;
+
+ static DependencyLocation GetDependencyLocationDispatch(this IState state, IResourceConfig config)
+ => config.Accept(new GetDependencyLocationVisitor(), state);
+
+ static DependencyLocation GetDependencyLocation(
+ this IState state, ResourceConfig config)
+ where TModel : class
+ {
+ var info = state.Get(config);
+ return info != null
+ ? new DependencyLocation(
+ config.Strategy.GetLocation(info),
+ config.Strategy.CompulsoryLocation)
+ : config
+ .GetResourceDependencies()
+ .Select(state.GetDependencyLocationDispatch)
+ .Aggregate(null as DependencyLocation, Merge);
+ }
+
+ sealed class GetDependencyLocationVisitor : IResourceConfigVisitor
+ {
+ public DependencyLocation Visit(ResourceConfig config, IState state)
+ where TModel : class
+ => state.GetDependencyLocation(config);
+ }
+
+ sealed class DependencyLocation
+ {
+ public string Location { get; }
+
+ public bool IsCompulsory { get; }
+
+ public DependencyLocation(string location, bool isCompulsory)
+ {
+ Location = location;
+ IsCompulsory = isCompulsory;
+ }
+ }
+
+ static DependencyLocation Merge(this DependencyLocation a, DependencyLocation b)
+ {
+ if (a == null)
+ {
+ return b;
+ }
+ if (b == null)
+ {
+ return a;
+ }
+
+ if (a.IsCompulsory != b.IsCompulsory)
+ {
+ return a.IsCompulsory ? b : a;
+ }
+
+ // a.IsCompulsory == b.IsCompulsory
+ return a.Location == b.Location ? a : new DependencyLocation(null, a.IsCompulsory);
+ }
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/MSSharedLibKey.snk b/src/ResourceManager/Common/Commands.Common.Strategies/MSSharedLibKey.snk
new file mode 100644
index 000000000000..695f1b38774e
Binary files /dev/null and b/src/ResourceManager/Common/Commands.Common.Strategies/MSSharedLibKey.snk differ
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceConfig.cs b/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceConfig.cs
new file mode 100644
index 000000000000..90819a81e97d
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceConfig.cs
@@ -0,0 +1,68 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ ///
+ /// Nested resource configuration. Fro example, Subnet.
+ ///
+ ///
+ ///
+ public sealed class NestedResourceConfig : IEntityConfig
+ where TModel : class
+ where TParenModel : class
+ {
+ public NestedResourceStrategy Strategy { get; }
+
+ public string Name { get; }
+
+ ///
+ /// Parent. For example, VirtualNetwork is a parent of Subnet.
+ ///
+ public IEntityConfig Parent { get; }
+
+ public Func CreateModel { get; }
+
+ public IResourceConfig Resource => Parent.Resource;
+
+ IEntityStrategy IEntityConfig.Strategy => Strategy;
+
+ public NestedResourceConfig(
+ NestedResourceStrategy strategy,
+ IEntityConfig parent,
+ string name,
+ Func createModel)
+ {
+ Strategy = strategy;
+ Name = name;
+ Parent = parent;
+ CreateModel = createModel;
+ }
+
+ public IEnumerable GetId(string subscription)
+ => Parent.GetId(subscription).Concat(Strategy.GetId(Name));
+
+ TResult IEntityConfig.Accept(
+ IEntityConfigVisitor visitor, TContext context)
+ => visitor.Visit(this, context);
+
+ TResult IEntityConfig.Accept(
+ IEntityConfigVisitor visitor, TContext context)
+ => visitor.Visit(this, context);
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceConfigExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceConfigExtensions.cs
new file mode 100644
index 000000000000..2c762667068a
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceConfigExtensions.cs
@@ -0,0 +1,30 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public static class NestedResourceConfigExtensions
+ {
+ public static NestedResourceConfig CreateConfig(
+ this NestedResourceStrategy strategy,
+ IEntityConfig parent,
+ string name,
+ Func createModel)
+ where TModel : class
+ where TParenModel : class
+ => new NestedResourceConfig(strategy, parent, name, createModel);
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceStrategy.cs b/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceStrategy.cs
new file mode 100644
index 000000000000..8c559f2f547a
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/NestedResourceStrategy.cs
@@ -0,0 +1,52 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System;
+using System.Collections.Generic;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public sealed class NestedResourceStrategy : IEntityStrategy
+ {
+ public Func> GetId { get; }
+
+ public Func Get { get; }
+
+ public Action CreateOrUpdate { get; }
+
+ public NestedResourceStrategy(
+ Func> getId,
+ Func get,
+ Action createOrUpdate)
+ {
+ GetId = getId;
+ Get = get;
+ CreateOrUpdate = createOrUpdate;
+ }
+ }
+
+ public static class NestedResourceStrategy
+ {
+ public static NestedResourceStrategy Create(
+ string header,
+ Func get,
+ Action createOrUpdate)
+ where TModel : class
+ where TParentModel : class
+ => new NestedResourceStrategy(
+ name => new[] { header, name},
+ get,
+ createOrUpdate);
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/ProgressMap.cs b/src/ResourceManager/Common/Commands.Common.Strategies/ProgressMap.cs
new file mode 100644
index 000000000000..043f4e52c729
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/ProgressMap.cs
@@ -0,0 +1,45 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System;
+using System.Collections.Generic;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public class ProgressMap
+ {
+ readonly Dictionary> _Map
+ = new Dictionary>();
+
+ readonly int _Duration;
+
+ public ProgressMap(Dictionary> map, int duration)
+ {
+ _Map = map;
+ _Duration = duration;
+ }
+
+ ///
+ /// Returns a value of [0..1] range which is used to increment a progress bar when the
+ /// resource is created.
+ ///
+ /// a resource configuration.
+ ///
+ public double Get(IResourceConfig config)
+ {
+ var x = _Map.GetOrNull(config);
+ return x.Item1.GetTaskProgress(x.Item2) / _Duration;
+ }
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/Properties/AssemblyInfo.cs b/src/ResourceManager/Common/Commands.Common.Strategies/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000000..338b456f0726
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Commands.Common.Strategies")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Commands.Common.Strategies")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("eea69772-d41b-482a-9252-2b4595c59e53")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/ResourceConfig.cs b/src/ResourceManager/Common/Commands.Common.Strategies/ResourceConfig.cs
new file mode 100644
index 000000000000..c2addb183ba1
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/ResourceConfig.cs
@@ -0,0 +1,81 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ ///
+ /// Resource configuration. It contains information to create a resource,
+ /// including name, resource group name, dependencies, model creation function, etc.
+ ///
+ ///
+ public sealed class ResourceConfig : IEntityConfig, IResourceConfig
+ where TModel : class
+ {
+ public ResourceStrategy Strategy { get; }
+
+ public string ResourceGroupName { get; }
+
+ public string Name { get; }
+
+ public Func CreateModel { get; }
+
+ public IEnumerable Dependencies { get; }
+
+ IEntityStrategy IEntityConfig.Strategy => Strategy;
+
+ IResourceStrategy IResourceConfig.Strategy => Strategy;
+
+ IResourceConfig IEntityConfig.Resource => this;
+
+ public ResourceConfig(
+ ResourceStrategy strategy,
+ string resourceGroupName,
+ string name,
+ Func createModel,
+ IEnumerable dependencies)
+ {
+ Strategy = strategy;
+ ResourceGroupName = resourceGroupName;
+ Name = name;
+ CreateModel = createModel;
+ Dependencies = dependencies;
+ }
+
+ public IEnumerable GetId(string subscription)
+ => new[]
+ {
+ "subscriptions",
+ subscription,
+ "resourceGroups",
+ ResourceGroupName
+ }
+ .Concat(Strategy.GetId(Name));
+
+ TResult IEntityConfig.Accept(
+ IEntityConfigVisitor visitor, TContext context)
+ => visitor.Visit(this, context);
+
+ TResult IEntityConfig.Accept(
+ IEntityConfigVisitor visitor, TContext context)
+ => visitor.Visit(this, context);
+
+ TResult IResourceConfig.Accept(
+ IResourceConfigVisitor visitor, TContext context)
+ => visitor.Visit(this, context);
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/ResourceConfigExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/ResourceConfigExtensions.cs
new file mode 100644
index 000000000000..de590730bde2
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/ResourceConfigExtensions.cs
@@ -0,0 +1,78 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Rest.Azure;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public static class ResourceConfigExtensions
+ {
+ public static ResourceConfig CreateConfig(
+ this ResourceStrategy strategy,
+ string resourceGroupName,
+ string name,
+ Func createModel = null,
+ IEnumerable dependencies = null)
+ where TModel : class, new()
+ => new ResourceConfig(
+ strategy,
+ resourceGroupName,
+ name,
+ createModel ?? (_ => new TModel()),
+ dependencies.EmptyIfNull());
+
+ public static async Task GetAsync(
+ this ResourceConfig config,
+ IClient client,
+ CancellationToken cancellationToken)
+ where TModel : class
+ {
+ try
+ {
+ return await config.Strategy.GetAsync(
+ client,
+ new GetAsyncParams(config.ResourceGroupName, config.Name, cancellationToken));
+ }
+ catch (CloudException e)
+ when (e.Response.StatusCode == HttpStatusCode.NotFound)
+ {
+ return null;
+ }
+ }
+
+ public static Task CreateOrUpdateAsync(
+ this ResourceConfig config,
+ IClient client,
+ TModel model,
+ CancellationToken cancellationToken)
+ where TModel : class
+ => config.Strategy.CreateOrUpdateAsync(
+ client,
+ CreateOrUpdateAsyncParams.Create(
+ config.ResourceGroupName,
+ config.Name,
+ model,
+ cancellationToken));
+
+ public static IEnumerable GetResourceDependencies(
+ this IResourceConfig config)
+ => config.Dependencies.Select(d => d.Resource);
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/ResourceStrategy.cs b/src/ResourceManager/Common/Commands.Common.Strategies/ResourceStrategy.cs
new file mode 100644
index 000000000000..28768d51b15f
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/ResourceStrategy.cs
@@ -0,0 +1,111 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Rest;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public sealed class ResourceStrategy : IResourceStrategy
+ {
+ public string Type { get; }
+
+ public Func> GetId { get; }
+
+ public Func> GetAsync { get; }
+
+ public Func, Task> CreateOrUpdateAsync
+ { get; }
+
+ public Func GetLocation { get; }
+
+ public Action SetLocation { get; }
+
+ public Func CreateTime { get; }
+
+ public bool CompulsoryLocation { get; }
+
+ public ResourceStrategy(
+ string type,
+ Func> getId,
+ Func> getAsync,
+ Func, Task> createOrUpdateAsync,
+ Func getLocation,
+ Action setLocation,
+ Func createTime,
+ bool compulsoryLocation)
+ {
+ Type = type;
+ GetId = getId;
+ GetAsync = getAsync;
+ CreateOrUpdateAsync = createOrUpdateAsync;
+ GetLocation = getLocation;
+ SetLocation = setLocation;
+ CreateTime = createTime;
+ CompulsoryLocation = compulsoryLocation;
+ }
+ }
+
+ public static class ResourceStrategy
+ {
+ public static ResourceStrategy Create(
+ string type,
+ Func> getId,
+ Func getOperations,
+ Func> getAsync,
+ Func, Task> createOrUpdateAsync,
+ Func getLocation,
+ Action setLocation,
+ Func createTime,
+ bool compulsoryLocation)
+ where TClient : ServiceClient
+ {
+ Func toOperations = client => getOperations(client.GetClient());
+ return new ResourceStrategy(
+ type,
+ getId,
+ (client, p) => getAsync(toOperations(client), p),
+ (client, p) => createOrUpdateAsync(toOperations(client), p),
+ getLocation,
+ setLocation,
+ createTime,
+ compulsoryLocation);
+ }
+
+ public static ResourceStrategy Create(
+ string type,
+ IEnumerable headers,
+ Func getOperations,
+ Func> getAsync,
+ Func, Task> createOrUpdateAsync,
+ Func getLocation,
+ Action setLocation,
+ Func createTime,
+ bool compulsoryLocation)
+ where TClient : ServiceClient
+ => Create(
+ type,
+ name => new[] { "providers" }.Concat(headers).Concat(new[] { name }),
+ getOperations,
+ getAsync,
+ createOrUpdateAsync,
+ getLocation,
+ setLocation,
+ createTime,
+ compulsoryLocation);
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/State.cs b/src/ResourceManager/Common/Commands.Common.Strategies/State.cs
new file mode 100644
index 000000000000..744934980e0a
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/State.cs
@@ -0,0 +1,39 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System;
+using System.Collections.Concurrent;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ ///
+ /// Azure State. It contains information (models) of Azure resources.
+ ///
+ sealed class State : IState
+ {
+ readonly ConcurrentDictionary _Map
+ = new ConcurrentDictionary();
+
+ public TModel Get(ResourceConfig config)
+ where TModel : class
+ => _Map.GetOrNull(config.DefaultIdStr()) as TModel;
+
+ public TModel GetOrAdd(ResourceConfig config, Func f)
+ where TModel : class
+ => _Map.GetOrAddWithCast(config.DefaultIdStr(), f);
+
+ public bool Contains(IResourceConfig config)
+ => _Map.ContainsKey(config.DefaultIdStr());
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/StateExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/StateExtensions.cs
new file mode 100644
index 000000000000..17d584040d7c
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/StateExtensions.cs
@@ -0,0 +1,82 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Common.Strategies
+{
+ public static class StateExtensions
+ {
+ ///
+ /// Get a model of the given nested resource config from the given state.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TModel Get(
+ this IState state, NestedResourceConfig config)
+ where TModel : class
+ where TParentModel : class
+ {
+ var parentModel = state.GetDispatch(config.Parent);
+ return parentModel == null ? null : config.Strategy.Get(parentModel, config.Name);
+ }
+
+ ///
+ /// Get a model of the given entity model from the given state.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TModel GetDispatch(
+ this IState state, IEntityConfig config)
+ where TModel : class
+ => config.Accept(new GetVisitor(), state);
+
+ public static bool Contains(
+ this IState state, NestedResourceConfig config)
+ where TModel : class
+ where TParentModel : class
+ => state.Get(config) != null;
+
+ public static bool ContainsDispatch(this IState state, IEntityConfig config)
+ => config.Accept(new ContainsDispatchVisitor(), state);
+
+ sealed class GetVisitor : IEntityConfigVisitor
+ where TModel : class
+ {
+ public TModel Visit(ResourceConfig config, IState state)
+ => state.Get(config);
+
+ public TModel Visit(
+ NestedResourceConfig config, IState state)
+ where TParentModel : class
+ => state.Get(config);
+ }
+
+ sealed class ContainsDispatchVisitor : IEntityConfigVisitor
+ {
+ public bool Visit(ResourceConfig config, IState context)
+ where TModel : class
+ => context.Contains(config);
+
+ public bool Visit(
+ NestedResourceConfig config, IState context)
+ where TModel : class
+ where TParentModel : class
+ => context.Contains(config);
+ }
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/StateOperationContext.cs b/src/ResourceManager/Common/Commands.Common.Strategies/StateOperationContext.cs
new file mode 100644
index 000000000000..d6d34a5b9418
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/StateOperationContext.cs
@@ -0,0 +1,60 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System;
+using System.Collections.Concurrent;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ ///
+ /// Context for asyncronous operations, such as GetAsync or CreateOrUpdateAsync.
+ ///
+ public sealed class StateOperationContext
+ {
+ public IClient Client { get; }
+
+ public CancellationToken CancellationToken { get; }
+
+ public IState Result => _Result;
+
+ readonly State _Result = new State();
+
+ readonly ConcurrentDictionary _TaskMap
+ = new ConcurrentDictionary();
+
+ public StateOperationContext(IClient client, CancellationToken cancellationToken)
+ {
+ Client = client;
+ CancellationToken = cancellationToken;
+ }
+
+ public async Task GetOrAdd(
+ ResourceConfig config, Func> operation)
+ where TModel : class
+ => await _TaskMap.GetOrAddWithCast(
+ config.DefaultIdStr(),
+ async () =>
+ {
+ var model = await operation();
+ if (model != null)
+ {
+ // add the operation result to a result.
+ _Result.GetOrAdd(config, () => model);
+ }
+ return model;
+ });
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/SyncTaskScheduler.cs b/src/ResourceManager/Common/Commands.Common.Strategies/SyncTaskScheduler.cs
new file mode 100644
index 000000000000..67989355cef3
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/SyncTaskScheduler.cs
@@ -0,0 +1,65 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System;
+using System.Collections.Concurrent;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public sealed class SyncTaskScheduler
+ {
+ readonly ConcurrentQueue _Tasks = new ConcurrentQueue();
+
+ public async Task Invoke(Func func)
+ {
+ var task = new Task(func);
+ _Tasks.Enqueue(task);
+ // note: don't use 'await' keyword for the 'task' because it may start the task in
+ // another thread.
+ while (!task.IsCompleted)
+ {
+ await Task.Yield();
+ }
+ return task.Result;
+ }
+
+ public void BeginInvoke(Action action)
+ => _Tasks.Enqueue(new Task(action));
+
+ public void Wait(Task task)
+ {
+ while (!task.IsCompleted)
+ {
+ HandleActions();
+ Thread.Yield();
+ }
+ HandleActions();
+ if (task.IsFaulted)
+ {
+ throw task.Exception.InnerException;
+ }
+ }
+
+ void HandleActions()
+ {
+ Task task;
+ while (_Tasks.TryDequeue(out task))
+ {
+ task.RunSynchronously();
+ }
+ }
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/TargetStateExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/TargetStateExtensions.cs
new file mode 100644
index 000000000000..1de0ab970582
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/TargetStateExtensions.cs
@@ -0,0 +1,125 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public static class TargetStateExtensions
+ {
+ public static IEnumerable GetTargetDependencies(
+ this IResourceConfig config, IState target)
+ => config.GetResourceDependencies().Where(target.Contains);
+
+ public static IState GetTargetState(
+ this ResourceConfig config,
+ IState current,
+ string subscription,
+ string location)
+ where TModel : class
+ {
+ var context = new Context(current, subscription, location);
+ context.AddIfRequired(config);
+ return context.Target;
+ }
+
+ sealed class Context
+ {
+ public State Target { get; } = new State();
+
+ public IState Current { get; }
+
+ public string Subscription { get; }
+
+ public string Location { get; }
+
+ public Context(IState current, string subscription, string location)
+ {
+ Current = current;
+ Subscription = subscription;
+ Location = location;
+ }
+
+ public void AddIfRequired(IEntityConfig config)
+ {
+ if (!Current.ContainsDispatch(config))
+ {
+ config.Accept(new AddVisitor(), this);
+ }
+ }
+
+ public TModel GetOrAdd(ResourceConfig config)
+ where TModel : class
+ => Target.GetOrAdd(
+ config,
+ () =>
+ {
+ foreach (var dependency in config.Dependencies)
+ {
+ AddIfRequired(dependency);
+ }
+ var model = config.CreateModel(Subscription);
+ config.Strategy.SetLocation(model, Location);
+ return model;
+ });
+
+ public TModel GetOrAdd(
+ NestedResourceConfig config)
+ where TModel : class
+ where TParentModel : class
+ {
+ var parentModel = config.Parent.Accept(new GetOrAddVisitor(), this);
+ var model = config.Strategy.Get(parentModel, config.Name);
+ if (model == null)
+ {
+ model = config.CreateModel();
+ config.Strategy.CreateOrUpdate(parentModel, config.Name, model);
+ }
+ return model;
+ }
+ }
+
+ sealed class AddVisitor : IEntityConfigVisitor
+ {
+ public Void Visit(ResourceConfig config, Context context)
+ where TModel : class
+ {
+ context.GetOrAdd(config);
+ return new Void();
+ }
+
+ public Void Visit(
+ NestedResourceConfig config, Context context)
+ where TModel : class
+ where TParentModel : class
+ {
+ context.GetOrAdd(config);
+ return new Void();
+ }
+ }
+
+ sealed class GetOrAddVisitor : IEntityConfigVisitor
+ where TModel : class
+ {
+ public TModel Visit(ResourceConfig config, Context context)
+ => context.GetOrAdd(config);
+
+ public TModel Visit(
+ NestedResourceConfig config, Context context)
+ where TParenModel : class
+ => context.GetOrAdd(config);
+ }
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/TimeSlot.cs b/src/ResourceManager/Common/Commands.Common.Strategies/TimeSlot.cs
new file mode 100644
index 000000000000..57310436f72a
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/TimeSlot.cs
@@ -0,0 +1,80 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Common.Strategies
+{
+ ///
+ /// TimeSlot is a node of a singly linked list of TimeSlots.
+ /// The last node of the list is always an empty time slot:
+ /// - Duration = 0.
+ /// - Next = null.
+ /// - TaskCount = 0.
+ ///
+ public sealed class TimeSlot
+ {
+ public int Duration { get; private set; }
+
+ public int TaskCount { get; private set; }
+
+ public TimeSlot Next { get; private set; }
+
+ public bool IsLast => Next == null;
+
+ public TimeSlot() : this(0, 0, null) { }
+
+ TimeSlot(int duration, int taskCount, TimeSlot next)
+ {
+ Duration = duration;
+ TaskCount = taskCount;
+ Next = next;
+ }
+
+ public TimeSlot AddTask(int duration)
+ {
+ if (duration <= 0)
+ {
+ return this;
+ }
+ else if (IsLast)
+ {
+ Next = new TimeSlot();
+ Duration = duration;
+ TaskCount = 1;
+ return Next;
+ }
+ else if (duration < Duration)
+ {
+ Next = new TimeSlot(Duration - duration, TaskCount, Next);
+ Duration = duration;
+ TaskCount++;
+ return Next;
+ }
+ else // if (Duration <= duration)
+ {
+ TaskCount++;
+ return Next.AddTask(duration - Duration);
+ }
+ }
+
+ public double GetTaskProgress(int duration)
+ => IsLast
+ ? duration
+ : duration <= Duration
+ ? GetTimeSlotProgress(duration)
+ : GetTimeSlotProgress(Duration) + Next.GetTaskProgress(duration - Duration);
+
+ double GetTimeSlotProgress(double duration)
+ => duration / TaskCount;
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/TimeSlotExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/TimeSlotExtensions.cs
new file mode 100644
index 000000000000..f979cb98cd4a
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/TimeSlotExtensions.cs
@@ -0,0 +1,80 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public static class TimeSlotExtensions
+ {
+ public static ProgressMap GetProgressMap(
+ this ResourceConfig config, IState state)
+ where TModel : class
+ {
+ var context = new Context(state);
+ var duration = context.GetTimeSlotAndDuration(config).Item2;
+ return new ProgressMap(context.BeginMap, duration);
+ }
+
+ sealed class Context
+ {
+ public TimeSlot Begin { get; } = new TimeSlot();
+
+ public Dictionary> BeginMap { get; }
+ = new Dictionary>();
+
+ readonly IState _State;
+
+ readonly ConcurrentDictionary> _Map
+ = new ConcurrentDictionary>();
+
+ public Context(IState state)
+ {
+ _State = state;
+ }
+
+ public Tuple GetTimeSlotAndDuration(
+ ResourceConfig config)
+ where TModel : class
+ => _Map.GetOrAdd(
+ config,
+ _ =>
+ {
+ var tupleBegin = config
+ .GetTargetDependencies(_State)
+ .Select(GetTimeSlotAndDurationDispatch)
+ .Aggregate(Tuple.Create(Begin, 0), (a, b) => a.Item2 > b.Item2 ? a : b);
+ var duration = config.Strategy.CreateTime(_State.Get(config));
+ BeginMap.Add(config, Tuple.Create(tupleBegin.Item1, duration));
+ return Tuple.Create(
+ tupleBegin.Item1.AddTask(duration), tupleBegin.Item2 + duration);
+ });
+
+ Tuple GetTimeSlotAndDurationDispatch(IResourceConfig config)
+ => config.Accept(new GetTimeSlotAndDurationVisitor(), this);
+ }
+
+ sealed class GetTimeSlotAndDurationVisitor :
+ IResourceConfigVisitor>
+ {
+ public Tuple Visit(
+ ResourceConfig config, Context context)
+ where TModel : class
+ => context.GetTimeSlotAndDuration(config);
+ }
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/UpdateStateExtensions.cs b/src/ResourceManager/Common/Commands.Common.Strategies/UpdateStateExtensions.cs
new file mode 100644
index 000000000000..0828473d826e
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/UpdateStateExtensions.cs
@@ -0,0 +1,115 @@
+// ----------------------------------------------------------------------------------
+//
+// 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 System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.Azure.Commands.Common.Strategies
+{
+ public static class UpdateStateExtensions
+ {
+ public static async Task UpdateStateAsync(
+ this ResourceConfig config,
+ IClient client,
+ IState target,
+ CancellationToken cancellationToken,
+ IShouldProcess shouldProcess,
+ IProgressReport progressReport)
+ where TModel : class
+ {
+ var context = new Context(
+ new StateOperationContext(client, cancellationToken),
+ target,
+ shouldProcess,
+ progressReport,
+ config.GetProgressMap(target));
+ await context.UpdateStateAsync(config);
+ return context.Result;
+ }
+
+ sealed class Context
+ {
+ public IState Result => _OperationContext.Result;
+
+ readonly StateOperationContext _OperationContext;
+
+ readonly IState _Target;
+
+ readonly IShouldProcess _ShouldProcess;
+
+ readonly IProgressReport _ProgressReport;
+
+ readonly ProgressMap _ProgressMap;
+
+ public Context(
+ StateOperationContext operationContext,
+ IState target,
+ IShouldProcess shouldProcess,
+ IProgressReport progressReport,
+ ProgressMap progressMap)
+ {
+ _OperationContext = operationContext;
+ _Target = target;
+ _ShouldProcess = shouldProcess;
+ _ProgressReport = progressReport;
+ _ProgressMap = progressMap;
+ }
+
+ public async Task UpdateStateAsync(ResourceConfig config)
+ where TModel : class
+ {
+ var model = _Target.Get(config);
+ if (model != null)
+ {
+ await _OperationContext.GetOrAdd(
+ config,
+ async () =>
+ {
+ // wait for all dependencies
+ var tasks = config
+ .GetResourceDependencies()
+ .Select(UpdateStateAsyncDispatch);
+ await Task.WhenAll(tasks);
+ // call the CreateOrUpdateAsync function for the resource.
+ if (await _ShouldProcess.ShouldCreate(config, model))
+ {
+ _ProgressReport.Start(config);
+ var result = await config.CreateOrUpdateAsync(
+ _OperationContext.Client,
+ model,
+ _OperationContext.CancellationToken);
+ _ProgressReport.Done(config, _ProgressMap.Get(config));
+ return result;
+ }
+ else
+ {
+ return null;
+ }
+ });
+ }
+ }
+
+ public Task UpdateStateAsyncDispatch(IResourceConfig config)
+ => config.Accept(new UpdateStateAsyncVisitor(), this);
+ }
+
+ sealed class UpdateStateAsyncVisitor : IResourceConfigVisitor
+ {
+ public Task Visit(ResourceConfig config, Context context)
+ where TModel : class
+ => context.UpdateStateAsync(config);
+ }
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/Void.cs b/src/ResourceManager/Common/Commands.Common.Strategies/Void.cs
new file mode 100644
index 000000000000..403652f72153
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/Void.cs
@@ -0,0 +1,20 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Common.Strategies
+{
+ public struct Void
+ {
+ }
+}
diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/packages.config b/src/ResourceManager/Common/Commands.Common.Strategies/packages.config
new file mode 100644
index 000000000000..c658da2ce8bb
--- /dev/null
+++ b/src/ResourceManager/Common/Commands.Common.Strategies/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ResourceManager/Compute/AzureRM.Compute.psd1 b/src/ResourceManager/Compute/AzureRM.Compute.psd1
index ee0d5d1ebe8e..f4c0c8aebedc 100644
--- a/src/ResourceManager/Compute/AzureRM.Compute.psd1
+++ b/src/ResourceManager/Compute/AzureRM.Compute.psd1
@@ -64,7 +64,8 @@ RequiredAssemblies = '.\AutoMapper.dll',
'.\Microsoft.WindowsAzure.Commands.Sync.dll',
'.\Microsoft.WindowsAzure.Commands.Tools.Vhd.dll',
'.\Microsoft.WindowsAzure.Storage.dll',
- '.\System.Spatial.dll'
+ '.\System.Spatial.dll',
+ '.\Microsoft.Azure.Commands.Common.Strategies.dll'
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
# ScriptsToProcess = @()
diff --git a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.Netcore.csproj b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.Netcore.csproj
index bec57e10c5d8..6234e13a0cf3 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.Netcore.csproj
+++ b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.Netcore.csproj
@@ -140,6 +140,7 @@
+
diff --git a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj
index bffe3c5fef00..acc1d845e369 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj
+++ b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj
@@ -64,6 +64,14 @@
..\..\..\packages\Microsoft.Azure.Management.KeyVault.2.3.0-preview\lib\net452\Microsoft.Azure.Management.KeyVault.dll
True
+
+ ..\..\..\packages\Microsoft.Azure.Management.Network.15.1.0-preview\lib\net452\Microsoft.Azure.Management.Network.dll
+ True
+
+
+ ..\..\..\packages\Microsoft.Azure.Management.ResourceManager.1.6.0-preview\lib\net452\Microsoft.Azure.Management.ResourceManager.dll
+ True
+
..\..\..\packages\Microsoft.Azure.Management.Storage.4.1.0-preview\lib\net45\Microsoft.Azure.Management.Storage.dll
@@ -79,10 +87,22 @@
..\..\..\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.dll
True
+
+ ..\..\..\packages\Microsoft.Rest.ClientRuntime.2.3.10\lib\net452\Microsoft.Rest.ClientRuntime.dll
+ True
+
+
+ ..\..\..\packages\Microsoft.Rest.ClientRuntime.Azure.3.3.10\lib\net452\Microsoft.Rest.ClientRuntime.Azure.dll
+ True
+
..\..\..\packages\WindowsAzure.Storage.8.1.1\lib\net45\Microsoft.WindowsAzure.Storage.dll
True
+
+ ..\..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll
+ True
+
@@ -270,6 +290,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -329,6 +366,10 @@
{5ee72c53-1720-4309-b54b-5fb79703195f}
Commands.Common
+
+ {eea69772-d41b-482a-9252-2b4595c59e53}
+ Commands.Common.Strategies
+
{3819d8a7-c62c-4c47-8ddd-0332d9ce1252}
Commands.ResourceManager.Common
diff --git a/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClient.cs b/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClient.cs
index f3878d69a5f3..b55df4f718b5 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClient.cs
+++ b/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClient.cs
@@ -29,7 +29,8 @@ public class ComputeClient
public Action ErrorLogger { get; set; }
public ComputeClient(IAzureContext context)
- : this(AzureSession.Instance.ClientFactory.CreateArmClient(context, AzureEnvironment.Endpoint.ResourceManager))
+ : this(AzureSession.Instance.ClientFactory.CreateArmClient(
+ context, AzureEnvironment.Endpoint.ResourceManager))
{
}
diff --git a/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClientBaseCmdlet.cs b/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClientBaseCmdlet.cs
index bbecc9bf91f5..65bec91a94c3 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClientBaseCmdlet.cs
+++ b/src/ResourceManager/Compute/Commands.Compute/Common/ComputeClientBaseCmdlet.cs
@@ -22,10 +22,7 @@ public abstract class ComputeClientBaseCmdlet : AzureRMCmdlet
{
protected const string VirtualMachineExtensionType = "Microsoft.Compute/virtualMachines/extensions";
- protected override bool IsUsageMetricEnabled
- {
- get { return true; }
- }
+ protected override bool IsUsageMetricEnabled => true;
private ComputeClient computeClient;
diff --git a/src/ResourceManager/Compute/Commands.Compute/Generated/VirtualMachineScaleSet/VirtualMachineScaleSetCreateOrUpdateMethod.cs b/src/ResourceManager/Compute/Commands.Compute/Generated/VirtualMachineScaleSet/VirtualMachineScaleSetCreateOrUpdateMethod.cs
index 724ca7b598e2..fee6343c74cb 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Generated/VirtualMachineScaleSet/VirtualMachineScaleSetCreateOrUpdateMethod.cs
+++ b/src/ResourceManager/Compute/Commands.Compute/Generated/VirtualMachineScaleSet/VirtualMachineScaleSetCreateOrUpdateMethod.cs
@@ -167,4 +167,4 @@ public override void ExecuteCmdlet()
[AllowNull]
public PSVirtualMachineScaleSet VirtualMachineScaleSet { get; set; }
}
-}
+}
\ No newline at end of file
diff --git a/src/ResourceManager/Compute/Commands.Compute/Models/PSVirtualMachine.cs b/src/ResourceManager/Compute/Commands.Compute/Models/PSVirtualMachine.cs
index 60ee62321a79..ca66e3d02f6e 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Models/PSVirtualMachine.cs
+++ b/src/ResourceManager/Compute/Commands.Compute/Models/PSVirtualMachine.cs
@@ -98,5 +98,7 @@ public string ResourceGroupName
// Gets or sets the virtual machine zones.
public IList Zones { get; set; }
+
+ public string Fqdn { get; set; }
}
}
diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/AsyncCmdletExtensions.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/AsyncCmdletExtensions.cs
new file mode 100644
index 000000000000..3d7913045f60
--- /dev/null
+++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/AsyncCmdletExtensions.cs
@@ -0,0 +1,59 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Common.Strategies;
+using System;
+using System.Management.Automation;
+using System.Threading.Tasks;
+
+namespace Microsoft.Azure.Commands.Compute.Strategies
+{
+ static class AsyncCmdletExtensions
+ {
+ ///
+ /// Note: the function must be called in the main PowerShell thread.
+ ///
+ ///
+ ///
+ public static void StartAndWait(this Cmdlet cmdlet, Func createAndStartTask)
+ {
+ var asyncCmdlet = new AsyncCmdlet(cmdlet);
+ asyncCmdlet.Scheduler.Wait(createAndStartTask(asyncCmdlet));
+ }
+
+ sealed class AsyncCmdlet : IAsyncCmdlet
+ {
+ public SyncTaskScheduler Scheduler { get; } = new SyncTaskScheduler();
+
+ readonly Cmdlet _Cmdlet;
+
+ public AsyncCmdlet(Cmdlet cmdlet)
+ {
+ _Cmdlet = cmdlet;
+ }
+
+ public void WriteVerbose(string message)
+ => Scheduler.BeginInvoke(() => _Cmdlet.WriteVerbose(message));
+
+ public Task ShouldProcessAsync(string target, string action)
+ => Scheduler.Invoke(() => _Cmdlet.ShouldProcess(target, action));
+
+ public void WriteObject(object value)
+ => Scheduler.BeginInvoke(() => _Cmdlet.WriteObject(value));
+
+ public void WriteProgress(ProgressRecord progressRecord)
+ => Scheduler.BeginInvoke(() => _Cmdlet.WriteProgress(progressRecord));
+ }
+ }
+}
diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Client.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Client.cs
new file mode 100644
index 000000000000..fd9280bfa2fa
--- /dev/null
+++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Client.cs
@@ -0,0 +1,39 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Common.Authentication;
+using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
+using Microsoft.Azure.Commands.Common.Strategies;
+using Microsoft.Rest;
+
+namespace Microsoft.Azure.Commands.Compute.Strategies
+{
+ sealed class Client : IClient
+ {
+ public string SubscriptionId { get; }
+
+ IAzureContext Context { get; }
+
+ public Client(IAzureContext context)
+ {
+ Context = context;
+ SubscriptionId = Context.Subscription.Id;
+ }
+
+ public T GetClient()
+ where T : ServiceClient
+ => AzureSession.Instance.ClientFactory.CreateArmClient(
+ Context, AzureEnvironment.Endpoint.ResourceManager);
+ }
+}
diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/ComputeStrategy.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/ComputeStrategy.cs
new file mode 100644
index 000000000000..738faaf904c6
--- /dev/null
+++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Compute/ComputeStrategy.cs
@@ -0,0 +1,43 @@
+// ----------------------------------------------------------------------------------
+//
+// 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.Management.Compute;
+using Microsoft.Azure.Management.Compute.Models;
+using System;
+using System.Threading.Tasks;
+
+namespace Microsoft.Azure.Commands.Common.Strategies.Compute
+{
+ static class ComputePolicy
+ {
+ public static ResourceStrategy