diff --git a/setup/azurecmdfiles.wxi b/setup/azurecmdfiles.wxi
index 30f644ac672e..2ebf49d36061 100644
--- a/setup/azurecmdfiles.wxi
+++ b/setup/azurecmdfiles.wxi
@@ -127,6 +127,15 @@
+
+
+
+
+
+
+
+
+
@@ -145,6 +154,9 @@
+
+
+
@@ -157,6 +169,9 @@
+
+
+
@@ -718,15 +733,6 @@
-
-
-
-
-
-
-
-
-
@@ -814,9 +820,6 @@
-
-
-
@@ -1059,15 +1062,6 @@
-
-
-
-
-
-
-
-
-
@@ -1181,6 +1175,9 @@
+
+
+
@@ -1222,15 +1219,6 @@
-
-
-
-
-
-
-
-
-
@@ -1252,9 +1240,6 @@
-
-
-
@@ -1359,6 +1344,9 @@
+
+
+
@@ -1374,9 +1362,6 @@
-
-
-
@@ -1448,6 +1433,15 @@
+
+
+
+
+
+
+
+
+
@@ -1472,6 +1466,9 @@
+
+
+
@@ -1487,6 +1484,9 @@
+
+
+
@@ -1516,6 +1516,15 @@
+
+
+
+
+
+
+
+
+
@@ -1546,6 +1555,9 @@
+
+
+
@@ -1558,6 +1570,9 @@
+
+
+
@@ -1587,6 +1602,15 @@
+
+
+
+
+
+
+
+
+
@@ -1617,6 +1641,9 @@
+
+
+
@@ -1629,6 +1656,9 @@
+
+
+
@@ -1646,6 +1676,15 @@
+
+
+
+
+
+
+
+
+
@@ -1655,6 +1694,9 @@
+
+
+
@@ -1664,6 +1706,9 @@
+
+
+
@@ -2029,6 +2074,15 @@
+
+
+
+
+
+
+
+
+
@@ -2053,6 +2107,9 @@
+
+
+
@@ -2068,6 +2125,9 @@
+
+
+
@@ -2097,15 +2157,6 @@
-
-
-
-
-
-
-
-
-
@@ -2151,9 +2202,6 @@
-
-
-
@@ -2275,6 +2323,15 @@
+
+
+
+
+
+
+
+
+
@@ -2287,12 +2344,18 @@
+
+
+
+
+
+
@@ -2319,6 +2382,15 @@
+
+
+
+
+
+
+
+
+
@@ -2337,12 +2409,18 @@
+
+
+
+
+
+
@@ -2378,6 +2456,15 @@
+
+
+
+
+
+
+
+
+
@@ -2402,6 +2489,9 @@
+
+
+
@@ -2414,6 +2504,9 @@
+
+
+
@@ -2444,6 +2537,15 @@
+
+
+
+
+
+
+
+
+
@@ -2468,6 +2570,9 @@
+
+
+
@@ -2480,6 +2585,9 @@
+
+
+
@@ -2709,6 +2817,15 @@
+
+
+
+
+
+
+
+
+
@@ -2742,6 +2859,9 @@
+
+
+
@@ -2754,6 +2874,9 @@
+
+
+
@@ -3422,6 +3545,15 @@
+
+
+
+
+
+
+
+
+
@@ -3449,6 +3581,9 @@
+
+
+
@@ -3464,6 +3599,9 @@
+
+
+
@@ -4253,6 +4391,15 @@
+
+
+
+
+
+
+
+
+
@@ -4292,6 +4439,9 @@
+
+
+
@@ -4304,6 +4454,9 @@
+
+
+
@@ -4363,6 +4516,9 @@
+
+
+
@@ -4425,16 +4581,21 @@
+
+
+
+
+
@@ -4610,9 +4771,6 @@
-
-
-
@@ -4642,7 +4800,6 @@
-
@@ -4713,9 +4870,6 @@
-
-
-
@@ -4753,6 +4907,7 @@
+
@@ -4766,9 +4921,6 @@
-
-
-
@@ -4776,7 +4928,6 @@
-
@@ -4811,12 +4962,12 @@
+
-
@@ -4840,6 +4991,9 @@
+
+
+
@@ -4848,11 +5002,13 @@
+
+
@@ -4862,6 +5018,9 @@
+
+
+
@@ -4872,10 +5031,12 @@
+
+
@@ -4885,6 +5046,9 @@
+
+
+
@@ -4895,21 +5059,28 @@
+
+
+
+
+
+
+
@@ -5029,6 +5200,9 @@
+
+
+
@@ -5037,11 +5211,13 @@
+
+
@@ -5051,9 +5227,6 @@
-
-
-
@@ -5069,7 +5242,6 @@
-
@@ -5109,12 +5281,17 @@
+
+
+
+
+
@@ -5123,14 +5300,19 @@
+
+
+
+
+
@@ -5142,6 +5324,9 @@
+
+
+
@@ -5150,10 +5335,12 @@
+
+
@@ -5162,6 +5349,9 @@
+
+
+
@@ -5170,10 +5360,12 @@
+
+
@@ -5249,6 +5441,9 @@
+
+
+
@@ -5260,10 +5455,12 @@
+
+
@@ -5474,6 +5671,9 @@
+
+
+
@@ -5483,11 +5683,13 @@
+
+
@@ -5737,6 +5939,9 @@
+
+
+
@@ -5750,10 +5955,12 @@
+
+
@@ -5773,6 +5980,7 @@
+
diff --git a/src/Common/Commands.Common/Commands.Common.csproj b/src/Common/Commands.Common/Commands.Common.csproj
index fe1e8a7a42d5..10af73bb0fbb 100644
--- a/src/Common/Commands.Common/Commands.Common.csproj
+++ b/src/Common/Commands.Common/Commands.Common.csproj
@@ -103,12 +103,16 @@
False
..\..\packages\Microsoft.WindowsAzure.Management.4.1.1\lib\net40\Microsoft.WindowsAzure.Management.dll
+
+ ..\..\packages\WindowsAzure.Storage.4.0.0\lib\net40\Microsoft.WindowsAzure.Storage.dll
+
..\..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll
True
+
@@ -145,6 +149,14 @@
+
+
+
+
+
+
+
+
@@ -182,6 +194,7 @@
PublicResXFileCodeGenerator
Resources.Designer.cs
+ Designer
@@ -189,6 +202,7 @@
Designer
+
diff --git a/src/Common/Commands.Common/Extensions/DSC/DscExtensionCmdletConstants.cs b/src/Common/Commands.Common/Extensions/DSC/DscExtensionCmdletConstants.cs
new file mode 100644
index 000000000000..1530c0cbebc9
--- /dev/null
+++ b/src/Common/Commands.Common/Extensions/DSC/DscExtensionCmdletConstants.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC
+{
+ public static class DscExtensionCmdletConstants
+ {
+ //common extension constants
+ internal static readonly string ExtensionPublishedNamespace = "Microsoft.Powershell";
+ internal static readonly string ExtensionPublishedName = "DSC";
+ internal const string DefaultContainerName = "windows-powershell-dsc";
+
+ internal const int MinMajorPowerShellVersion = 4;
+ internal const string ZipFileExtension = ".zip";
+ internal const string Ps1FileExtension = ".ps1";
+ internal const string Psm1FileExtension = ".psm1";
+
+
+ internal static readonly HashSet UploadArchiveAllowedFileExtensions =
+ new HashSet(StringComparer.OrdinalIgnoreCase) { Ps1FileExtension, Psm1FileExtension, ZipFileExtension };
+ internal static readonly HashSet CreateArchiveAllowedFileExtensions =
+ new HashSet(StringComparer.OrdinalIgnoreCase) { Ps1FileExtension, Psm1FileExtension };
+ }
+}
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPrivateSettings.cs b/src/Common/Commands.Common/Extensions/DSC/DscExtensionPrivateSettings.cs
similarity index 96%
rename from src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPrivateSettings.cs
rename to src/Common/Commands.Common/Extensions/DSC/DscExtensionPrivateSettings.cs
index f4c9b6d8cbef..d009de5e1e53 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPrivateSettings.cs
+++ b/src/Common/Commands.Common/Extensions/DSC/DscExtensionPrivateSettings.cs
@@ -12,9 +12,8 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
-namespace Microsoft.Azure.Commands.Compute.Extension.DSC
+namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC
{
-
using System.Collections;
///
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPublicSettings.cs b/src/Common/Commands.Common/Extensions/DSC/DscExtensionPublicSettings.cs
similarity index 88%
rename from src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPublicSettings.cs
rename to src/Common/Commands.Common/Extensions/DSC/DscExtensionPublicSettings.cs
index b22b874f974e..faa7abaec85b 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPublicSettings.cs
+++ b/src/Common/Commands.Common/Extensions/DSC/DscExtensionPublicSettings.cs
@@ -16,7 +16,7 @@
using System.Collections;
using System.Collections.Generic;
-namespace Microsoft.Azure.Commands.Compute.Extension.DSC
+namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC
{
///
/// Represents public settings. Serialized representation of this object stored as a plain text string on the VM.
@@ -40,21 +40,21 @@ internal class Version1
///
public DscExtensionPublicSettings ToCurrentVersion()
{
- List properties = new List();
- foreach (DictionaryEntry p in this.Properties)
+ var properties = new List();
+ foreach (DictionaryEntry p in Properties)
{
- properties.Add(new Property()
+ properties.Add(new Property
{
Name = p.Key.ToString(),
TypeName = p.Value.GetType().ToString(),
Value = p.Value
});
}
- return new DscExtensionPublicSettings()
+ return new DscExtensionPublicSettings
{
- SasToken = this.SasToken,
- ModulesUrl = this.ModulesUrl,
- ConfigurationFunction = this.ConfigurationFunction,
+ SasToken = SasToken,
+ ModulesUrl = ModulesUrl,
+ ConfigurationFunction = ConfigurationFunction,
Properties = properties.ToArray(),
ProtocolVersion = new Version(1, 0, 0, 0)
};
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscSettingsSerializer.cs b/src/Common/Commands.Common/Extensions/DSC/DscExtensionSettingsSerializer.cs
similarity index 91%
rename from src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscSettingsSerializer.cs
rename to src/Common/Commands.Common/Extensions/DSC/DscExtensionSettingsSerializer.cs
index 84b1f8834df5..ade27215d831 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscSettingsSerializer.cs
+++ b/src/Common/Commands.Common/Extensions/DSC/DscExtensionSettingsSerializer.cs
@@ -12,7 +12,7 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
-
+using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -20,20 +20,19 @@
using System.Management.Automation;
using System.Runtime.InteropServices;
using System.Security;
-using Newtonsoft.Json;
-namespace Microsoft.Azure.Commands.Compute.Extension.DSC
+namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC
{
- public class DscSettingsSerializer
+ public class DscExtensionSettingsSerializer
{
///
- /// Serialize DscPublicSettings to string.
+ /// Serialize DscExtensionPublicSettings to string.
///
- ///
+ ///
///
- public static string SerializePublicSettings(DscExtensionPublicSettings publicSettings)
+ public static string SerializePublicSettings(DscExtensionPublicSettings extensionPublicSettings)
{
- return JsonConvert.SerializeObject(publicSettings);
+ return JsonConvert.SerializeObject(extensionPublicSettings);
}
///
@@ -48,10 +47,10 @@ public static string SerializePrivateSettings(DscExtensionPrivateSettings privat
public static DscExtensionPublicSettings DeserializePublicSettings(string publicSettingsString)
{
- DscExtensionPublicSettings publicSettings;
+ DscExtensionPublicSettings extensionPublicSettings;
try
{
- publicSettings = string.IsNullOrEmpty(publicSettingsString)
+ extensionPublicSettings = string.IsNullOrEmpty(publicSettingsString)
? null
: JsonConvert.DeserializeObject(publicSettingsString);
}
@@ -62,14 +61,14 @@ public static DscExtensionPublicSettings DeserializePublicSettings(string public
{
DscExtensionPublicSettings.Version1 publicSettingsV1 =
JsonConvert.DeserializeObject(publicSettingsString);
- publicSettings = publicSettingsV1.ToCurrentVersion();
+ extensionPublicSettings = publicSettingsV1.ToCurrentVersion();
}
catch (JsonException)
{
throw;
}
}
- return publicSettings;
+ return extensionPublicSettings;
}
///
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/GetDscResourceException.cs b/src/Common/Commands.Common/Extensions/DSC/Exceptions/GetDscResourceException.cs
similarity index 93%
rename from src/ResourceManager/Compute/Commands.Compute/Extension/DSC/GetDscResourceException.cs
rename to src/Common/Commands.Common/Extensions/DSC/Exceptions/GetDscResourceException.cs
index 8613f5ac844f..fa13da095eda 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/GetDscResourceException.cs
+++ b/src/Common/Commands.Common/Extensions/DSC/Exceptions/GetDscResourceException.cs
@@ -15,7 +15,8 @@
using System;
using System.Globalization;
-namespace Microsoft.Azure.Commands.Compute.Extension.DSC
+
+namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC.Exceptions
{
[Serializable]
public class GetDscResourceException : UnauthorizedAccessException
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParseResult.cs b/src/Common/Commands.Common/Extensions/DSC/Publish/ConfigurationParseResult.cs
similarity index 87%
rename from src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParseResult.cs
rename to src/Common/Commands.Common/Extensions/DSC/Publish/ConfigurationParseResult.cs
index 62f60f98bb31..35ad2d0af816 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParseResult.cs
+++ b/src/Common/Commands.Common/Extensions/DSC/Publish/ConfigurationParseResult.cs
@@ -12,17 +12,16 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
-using System;
using System.Collections.Generic;
using System.Management.Automation.Language;
-namespace Microsoft.Azure.Commands.Compute.Extension.DSC
+namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC.Publish
{
public class ConfigurationParseResult
{
public string Path { get; set; }
public ParseError[] Errors { get; set; }
- public List RequiredModules { get; set; }
+ public Dictionary RequiredModules { get; set; }
}
}
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/ConfigurationParsingHelper.cs b/src/Common/Commands.Common/Extensions/DSC/Publish/ConfigurationParsingHelper.cs
similarity index 74%
rename from src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/ConfigurationParsingHelper.cs
rename to src/Common/Commands.Common/Extensions/DSC/Publish/ConfigurationParsingHelper.cs
index 9040556fbdde..e231129f6b14 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/ConfigurationParsingHelper.cs
+++ b/src/Common/Commands.Common/Extensions/DSC/Publish/ConfigurationParsingHelper.cs
@@ -12,7 +12,7 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
-
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC.Exceptions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -22,7 +22,7 @@
using System.Management.Automation.Language;
using System.Management.Automation.Runspaces;
-namespace Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC
+namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC.Publish
{
public static class ConfigurationParsingHelper
@@ -32,7 +32,7 @@ public static class ConfigurationParsingHelper
private static bool IsParameterName(CommandElementAst ast, string name)
{
- CommandParameterAst constantAst = ast as CommandParameterAst;
+ var constantAst = ast as CommandParameterAst;
if (constantAst == null)
{
return false;
@@ -40,14 +40,14 @@ private static bool IsParameterName(CommandElementAst ast, string name)
return String.Equals(constantAst.ParameterName, name, StringComparison.OrdinalIgnoreCase);
}
- private static List GetLegacyTopLevelParametersFromAst(CommandAst ast, string parameterName)
+ private static IEnumerable GetLegacyTopLevelParametersFromAst(CommandAst ast, string parameterName)
{
- List parameters = new List();
+ var parameters = new List();
IEnumerable commandElement =
ast.CommandElements.Where(x => IsParameterName(x, parameterName)).OfType();
foreach (var commandElementAst in commandElement)
{
- ArrayLiteralAst arrayLiteralAst = commandElementAst.Argument as ArrayLiteralAst;
+ var arrayLiteralAst = commandElementAst.Argument as ArrayLiteralAst;
if (arrayLiteralAst != null)
{
parameters.AddRange(arrayLiteralAst.Elements.OfType().Select(x => x.Value));
@@ -61,10 +61,9 @@ private static bool IsCandidateForImportDscResourceAst(Ast ast, int startOffset)
{
return ast.Extent.StartOffset == startOffset && !(ast is StatementBlockAst) && !(ast is NamedBlockAst);
}
- private static List GetSingleAstRequiredModules(Ast ast, Token[] tokens)
+ private static Dictionary GetSingleAstRequiredModules(Ast ast, IEnumerable tokens, Dictionary modules)
{
- List modules = new List();
- List resources = new List();
+ var resources = new List();
var imports = tokens.Where(token =>
String.Compare(token.Text, "Import-DscResource", StringComparison.OrdinalIgnoreCase) == 0);
@@ -73,11 +72,33 @@ private static List GetSingleAstRequiredModules(Ast ast, Token[] tokens)
// argument function binding to emulate Import-DscResource argument binding.
//
InitialSessionState initialSessionState = InitialSessionState.Create();
- SessionStateFunctionEntry importDscResourcefunctionEntry = new SessionStateFunctionEntry(
+ var importDscResourcefunctionEntry = new SessionStateFunctionEntry(
"Import-DscResource", @"param($Name, $ModuleName)
if ($ModuleName)
{
- foreach ($m in $ModuleName) { $global:modules.Add($m) }
+ foreach ($module in $ModuleName) {
+ if($module.GetType().FullName -eq 'System.Collections.Hashtable'){
+ $mVersion = ""
+ $mName = ""
+ foreach($modulekey in $module.Keys){
+ if($modulekey -eq 'ModuleName'){
+ $mName = $module[$modulekey]
+ }
+ elseif($modulekey -eq 'ModuleVersion' -or $modulekey -eq 'RequiredVersion'){
+ $mVersion = $module[$modulekey]
+ }
+ }
+
+ if(!$global:modules.ContainsKey($mName)){
+ $global:modules.Add($mName,$mVersion)
+ }
+ }
+ else{
+ if(!$global:modules.ContainsKey($module)){
+ $global:modules.Add($module,"""")
+ }
+ }
+ }
} else {
foreach ($n in $Name) { $global:resources.Add($n) }
}
@@ -118,7 +139,16 @@ private static List GetSingleAstRequiredModules(Ast ast, Token[] tokens)
}
}
}
- modules.AddRange(resources.Select(GetModuleNameForDscResource));
+
+ var modulesFromDscResource = resources.Select(GetModuleNameForDscResource);
+ foreach (var moduleName in modulesFromDscResource)
+ {
+ if (!modules.ContainsKey(moduleName))
+ {
+ modules.Add(moduleName, "");
+ }
+ }
+
return modules;
}
@@ -147,9 +177,9 @@ public static string GetModuleNameForDscResource(string resourceName)
return moduleName;
}
- private static List GetRequiredModulesFromAst(Ast ast, Token[] tokens)
+ private static Dictionary GetRequiredModulesFromAst(Ast ast, IEnumerable tokens)
{
- List modules = new List();
+ var modules = new Dictionary();
// We use System.Management.Automation.Language.Parser to extract required modules from ast,
// but format of ast is a bit tricky and have changed in time.
@@ -186,26 +216,42 @@ private static List GetRequiredModulesFromAst(Ast ast, Token[] tokens)
// So we process everything: ModuleDefinition and ResourceDefinition.
// Example: Import-DscResource -Module xPSDesiredStateConfiguration
- modules.AddRange(GetLegacyTopLevelParametersFromAst(legacyConfigurationAst, "ModuleDefinition"));
+
+ var moduleParams = GetLegacyTopLevelParametersFromAst(legacyConfigurationAst, "ModuleDefinition");
+ foreach (var param in moduleParams)
+ {
+ if (!modules.ContainsKey(param))
+ {
+ modules.Add(param, "");
+ }
+ }
+
// Example: Import-DscResource -Name MSFT_xComputer
- modules.AddRange(GetLegacyTopLevelParametersFromAst(legacyConfigurationAst, "ResourceDefinition").Select(GetModuleNameForDscResource));
+ var resourceParams = GetLegacyTopLevelParametersFromAst(legacyConfigurationAst, "ResourceDefinition").Select(GetModuleNameForDscResource);
+ foreach (var param in resourceParams)
+ {
+ if (!modules.ContainsKey(param))
+ {
+ modules.Add(param, "");
+ }
+ }
}
// Both cases in new version and 2nd case in old version:
- modules.AddRange(GetSingleAstRequiredModules(ast, tokens));
-
- return modules.Distinct().ToList();
+ modules = GetSingleAstRequiredModules(ast, tokens, modules);
+
+ return modules;
}
private static bool IsLegacyAstConfiguration(Ast node)
{
- CommandAst commandNode = node as CommandAst;
+ var commandNode = node as CommandAst;
if (commandNode == null)
{
return false;
}
// TODO: Add case when configuration name is not a StringConstant, but a variable.
- StringConstantExpressionAst commandParameter = (commandNode.CommandElements[0] as StringConstantExpressionAst);
+ var commandParameter = (commandNode.CommandElements[0] as StringConstantExpressionAst);
if (commandParameter == null)
{
return false;
@@ -229,10 +275,10 @@ public static ConfigurationParseResult ParseConfiguration(string path)
// file may still successfully define one or more configurations. We don't process
// required modules in case of parsing errors to avoid script injection.
ScriptBlockAst ast = Parser.ParseFile(fullPath, out tokens, out errors);
- List requiredModules = new List();
+ var requiredModules = new Dictionary();
if (!errors.Any())
{
- requiredModules = GetRequiredModulesFromAst(ast, tokens).Distinct().ToList();
+ requiredModules = GetRequiredModulesFromAst(ast, tokens);
}
return new ConfigurationParseResult()
{
diff --git a/src/Common/Commands.Common/Extensions/DSC/Publish/DscExtensionPublishCmdletCommonBase.cs b/src/Common/Commands.Common/Extensions/DSC/Publish/DscExtensionPublishCmdletCommonBase.cs
new file mode 100644
index 000000000000..ce73fb54eff0
--- /dev/null
+++ b/src/Common/Commands.Common/Extensions/DSC/Publish/DscExtensionPublishCmdletCommonBase.cs
@@ -0,0 +1,425 @@
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC.Exceptions;
+using Microsoft.WindowsAzure.Commands.Utilities.Common;
+using Microsoft.WindowsAzure.Storage;
+using Microsoft.WindowsAzure.Storage.Auth;
+using Microsoft.WindowsAzure.Storage.Blob;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Management.Automation;
+
+
+namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC.Publish
+{
+ public class DscExtensionPublishCmdletCommonBase : AzurePSCmdlet
+ {
+ //Publish
+ private const string CreateArchiveParameterSetName = "CreateArchive";
+ private const string UploadArchiveParameterSetName = "UploadArchive";
+ private readonly List _temporaryFilesToDelete = new List();
+ private readonly List _temporaryDirectoriesToDelete = new List();
+
+ public void ValidatePsVersion()
+ {
+ using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
+ {
+ powershell.AddScript("$PSVersionTable.PSVersion.Major");
+ int major = powershell.Invoke().FirstOrDefault();
+ if (major < DscExtensionCmdletConstants.MinMajorPowerShellVersion)
+ {
+ ThrowTerminatingError(
+ new ErrorRecord(
+ new InvalidOperationException(
+ string.Format(
+ CultureInfo.CurrentUICulture,
+ Properties.Resources.PublishVMDscExtensionRequiredPsVersion,
+ DscExtensionCmdletConstants.MinMajorPowerShellVersion,
+ major)),
+ "InvalidPowerShellVersion",
+ ErrorCategory.InvalidOperation,
+ null));
+ }
+ }
+ }
+
+ public void ValidateConfigurationPath(String configurationPath, String paramaterSetName)
+ {
+ if (!File.Exists(configurationPath))
+ {
+ ThrowInvalidArgumentError(
+ Properties.Resources.PublishVMDscExtensionUploadArchiveConfigFileNotExist,
+ configurationPath);
+ }
+
+ var configurationFileExtension = Path.GetExtension(configurationPath);
+
+ if (paramaterSetName == UploadArchiveParameterSetName)
+ {
+ if (!DscExtensionCmdletConstants.UploadArchiveAllowedFileExtensions.Contains(Path.GetExtension(configurationFileExtension)))
+ {
+ ThrowInvalidArgumentError(Properties.Resources.PublishVMDscExtensionUploadArchiveConfigFileInvalidExtension, configurationPath);
+ }
+ }
+ else if (paramaterSetName == CreateArchiveParameterSetName)
+ {
+ if (!DscExtensionCmdletConstants.CreateArchiveAllowedFileExtensions.Contains(Path.GetExtension(configurationFileExtension)))
+ {
+ ThrowInvalidArgumentError(Properties.Resources.PublishVMDscExtensionCreateArchiveConfigFileInvalidExtension, configurationPath);
+ }
+ }
+ }
+
+ public string CreateConfigurationArchive(
+ String configurationPath,
+ String configurationArchivePath,
+ Boolean force,
+ String parameterSetName)
+ {
+ WriteVerbose(String.Format(CultureInfo.CurrentUICulture, Properties.Resources.AzureVMDscParsingConfiguration, configurationPath));
+ ConfigurationParseResult parseResult = null;
+ try
+ {
+ parseResult = ConfigurationParsingHelper.ParseConfiguration(configurationPath);
+ }
+ catch (GetDscResourceException e)
+ {
+ ThrowTerminatingError(new ErrorRecord(e, "CannotAccessDscResource", ErrorCategory.PermissionDenied, null));
+ }
+
+ if (parseResult.Errors.Any())
+ {
+ ThrowTerminatingError(
+ new ErrorRecord(
+ new ParseException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ Properties.Resources.PublishVMDscExtensionStorageParserErrors,
+ configurationPath,
+ String.Join("\n", parseResult.Errors.Select(error => error.ToString())))),
+ "DscConfigurationParseError",
+ ErrorCategory.ParserError,
+ null));
+ }
+
+ var requiredModules = parseResult.RequiredModules;
+ //Since LCM always uses the latest module there is no need to copy PSDesiredStateConfiguration
+ if (requiredModules.ContainsKey("PSDesiredStateConfiguration"))
+ {
+ requiredModules.Remove("PSDesiredStateConfiguration");
+ }
+
+ WriteVerbose(String.Format(CultureInfo.CurrentUICulture,
+ Properties.Resources.PublishVMDscExtensionRequiredModulesVerbose, String.Join(", ", requiredModules)));
+
+ // Create a temporary directory for uploaded zip file
+ var tempZipFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ WriteVerbose(String.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionTempFolderVerbose, tempZipFolder));
+ Directory.CreateDirectory(tempZipFolder);
+ _temporaryDirectoriesToDelete.Add(tempZipFolder);
+
+ // CopyConfiguration
+ var configurationName = Path.GetFileName(configurationPath);
+ var configurationDestination = Path.Combine(tempZipFolder, configurationName);
+ WriteVerbose(String.Format(
+ CultureInfo.CurrentUICulture,
+ Properties.Resources.PublishVMDscExtensionCopyFileVerbose,
+ configurationPath,
+ configurationDestination));
+ File.Copy(configurationPath, configurationDestination);
+
+ // CopyRequiredModules
+ foreach (var module in requiredModules)
+ {
+ using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
+ {
+ // Wrapping script in a function to prevent script injection via $module variable.
+ powershell.AddScript(
+ @"function Copy-Module([string]$moduleName, [string]$moduleVersion, [string]$tempZipFolder)
+ {
+ if([String]::IsNullOrEmpty($moduleVersion))
+ {
+ $module = Get-Module -List -Name $moduleName
+ }
+ else
+ {
+ $module = (Get-Module -List -Name $moduleName) | Where-Object{$_.Version -eq $moduleVersion}
+ }
+
+ $moduleFolder = Split-Path $module.Path
+ Copy-Item -Recurse -Path $moduleFolder -Destination ""$tempZipFolder\$($module.Name)""
+ }"
+ );
+ powershell.Invoke();
+ powershell.Commands.Clear();
+ powershell.AddCommand("Copy-Module")
+ .AddParameter("moduleName", module.Key)
+ .AddParameter("moduleVersion", module.Value)
+ .AddParameter("tempZipFolder", tempZipFolder);
+ WriteVerbose(String.Format(
+ CultureInfo.CurrentUICulture,
+ Properties.Resources.PublishVMDscExtensionCopyModuleVerbose,
+ module,
+ tempZipFolder));
+ powershell.Invoke();
+ }
+ }
+
+ //
+ // Zip the directory
+ //
+ string archive;
+ if (parameterSetName == CreateArchiveParameterSetName)
+ {
+ archive = configurationArchivePath;
+
+ if (!force && File.Exists(archive))
+ {
+ ThrowTerminatingError(
+ new ErrorRecord(
+ new UnauthorizedAccessException(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.AzureVMDscArchiveAlreadyExists, archive)),
+ "FileAlreadyExists",
+ ErrorCategory.PermissionDenied,
+ null));
+ }
+ }
+ else
+ {
+ string tempArchiveFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ WriteVerbose(String.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionTempFolderVerbose, tempArchiveFolder));
+ Directory.CreateDirectory(tempArchiveFolder);
+ _temporaryDirectoriesToDelete.Add(tempArchiveFolder);
+ archive = Path.Combine(tempArchiveFolder, configurationName + DscExtensionCmdletConstants.ZipFileExtension);
+ _temporaryFilesToDelete.Add(archive);
+ }
+
+ if (File.Exists(archive))
+ {
+ File.Delete(archive);
+ }
+
+ ZipFile.CreateFromDirectory(tempZipFolder, archive);
+
+ return archive;
+ }
+
+ ///
+ /// Publish the configuration and its modules
+ ///
+ public void PublishConfiguration(
+ String configurationPath,
+ String outputArchivePath,
+ Boolean force,
+ StorageCredentials storageCredentials,
+ String storageEndpointSuffix,
+ String containerName,
+ String parameterSetName)
+ {
+ if (parameterSetName == CreateArchiveParameterSetName)
+ {
+ ConfirmAction(
+ true,
+ string.Empty,
+ Properties.Resources.AzureVMDscCreateArchiveAction,
+ outputArchivePath, () => CreateConfigurationArchive(
+ configurationPath,
+ outputArchivePath,
+ force,
+ parameterSetName));
+ }
+ else
+ {
+ var archivePath = string.Compare(
+ Path.GetExtension(configurationPath),
+ DscExtensionCmdletConstants.ZipFileExtension,
+ StringComparison.OrdinalIgnoreCase) == 0
+ ? configurationPath
+ : CreateConfigurationArchive(
+ configurationPath,
+ outputArchivePath,
+ force,
+ parameterSetName);
+
+ UploadConfigurationArchive(storageCredentials, storageEndpointSuffix, containerName, archivePath, force);
+ }
+ }
+
+ private void UploadConfigurationArchive(
+ StorageCredentials storageCredentials,
+ String storageEndpointSuffix,
+ String containerName,
+ String archivePath,
+ Boolean force)
+ {
+ CloudBlockBlob modulesBlob =
+ GetBlobReference(storageCredentials, storageEndpointSuffix, containerName, archivePath);
+
+ ConfirmAction(
+ true,
+ string.Empty,
+ string.Format(CultureInfo.CurrentUICulture, Properties.Resources.AzureVMDscUploadToBlobStorageAction, archivePath),
+ modulesBlob.Uri.AbsoluteUri, () =>
+ {
+ if (!force && modulesBlob.Exists())
+ {
+ ThrowTerminatingError(
+ new ErrorRecord(
+ new UnauthorizedAccessException(
+ string.Format(
+ CultureInfo.CurrentUICulture,
+ Properties.Resources.AzureVMDscStorageBlobAlreadyExists, modulesBlob.Uri.AbsoluteUri)),
+ "StorageBlobAlreadyExists",
+ ErrorCategory.PermissionDenied,
+ null));
+ }
+
+ modulesBlob.UploadFromFile(archivePath, FileMode.Open);
+
+ WriteVerbose(string.Format(
+ CultureInfo.CurrentUICulture,
+ Properties.Resources.PublishVMDscExtensionArchiveUploadedMessage, modulesBlob.Uri.AbsoluteUri));
+
+ WriteObject(modulesBlob.Uri.AbsoluteUri);
+ });
+ }
+
+ public void DeleteTemporaryFiles()
+ {
+ foreach (var file in _temporaryFilesToDelete)
+ {
+ try
+ {
+ DeleteFile(file);
+ WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionDeletedFileMessage, file));
+ }
+ catch (Exception e)
+ {
+ WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionDeleteErrorMessage, file, e.Message));
+ }
+ }
+ foreach (var directory in _temporaryDirectoriesToDelete)
+ {
+ try
+ {
+ DeleteDirectory(directory);
+ WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionDeletedFileMessage, directory));
+ }
+ catch (Exception e)
+ {
+ WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionDeleteErrorMessage, directory, e.Message));
+ }
+ }
+ }
+
+ private CloudBlockBlob GetBlobReference(
+ StorageCredentials storageCredentials, String storageEndpointSuffix, String containerName, String archivePath)
+ {
+ var storageAccount = new CloudStorageAccount(storageCredentials, storageEndpointSuffix, true);
+ var blobClient = storageAccount.CreateCloudBlobClient();
+
+ CloudBlobContainer containerReference = blobClient.GetContainerReference(containerName);
+ containerReference.CreateIfNotExists();
+
+ var blobName = Path.GetFileName(archivePath);
+
+ return containerReference.GetBlockBlobReference(blobName);
+ }
+
+ /** ///
+ /// Asks for confirmation before executing the action.
+ ///
+ /// Do not ask for confirmation
+ /// Message to describe the action
+ /// Message to prompt after the active is performed.
+ /// The target name.
+ /// The action code
+ private void ConfirmAction(bool force, string actionMessage, string processMessage, string target, Action action)
+ {
+ if (force || ShouldContinue(actionMessage, ""))
+ {
+ if (ShouldProcess(target, processMessage))
+ {
+ action();
+ }
+ }
+ }**/
+
+ protected void ThrowInvalidArgumentError(String format, params object[] args)
+ {
+ ThrowTerminatingError(
+ new ErrorRecord(
+ new ArgumentException(string.Format(CultureInfo.CurrentUICulture, format, args)),
+ "InvalidArgument",
+ ErrorCategory.InvalidArgument,
+ null));
+ }
+
+ private static void DeleteFile(string path)
+ {
+ try
+ {
+ File.Delete(path);
+ }
+ catch (UnauthorizedAccessException)
+ {
+ // the exception may have occurred due to a read-only file
+ DeleteReadOnlyFile(path);
+ }
+ }
+
+ ///
+ /// Turns off the ReadOnly attribute from the given file and then attempts to delete it
+ ///
+ private static void DeleteReadOnlyFile(string path)
+ {
+ var attributes = File.GetAttributes(path);
+
+ if ((attributes & FileAttributes.ReadOnly) != 0)
+ {
+ File.SetAttributes(path, attributes & ~FileAttributes.ReadOnly);
+ }
+
+ File.Delete(path);
+ }
+
+ private static void DeleteDirectory(string path)
+ {
+ try
+ {
+ Directory.Delete(path, true);
+ }
+ catch (UnauthorizedAccessException)
+ {
+ // the exception may have occurred due to a read-only file or directory
+ DeleteReadOnlyDirectory(path);
+ }
+ }
+
+ ///
+ /// Recursively turns off the ReadOnly attribute from the given directory and then attemps to delete it
+ ///
+ private static void DeleteReadOnlyDirectory(string path)
+ {
+ var directory = new DirectoryInfo(path);
+
+ foreach (var child in directory.GetDirectories())
+ {
+ DeleteReadOnlyDirectory(child.FullName);
+ }
+
+ foreach (var child in directory.GetFiles())
+ {
+ DeleteReadOnlyFile(child.FullName);
+ }
+
+ if ((directory.Attributes & FileAttributes.ReadOnly) != 0)
+ {
+ directory.Attributes &= ~FileAttributes.ReadOnly;
+ }
+
+ directory.Delete();
+ }
+ }
+}
diff --git a/src/Common/Commands.Common/Properties/AssemblyInfo.cs b/src/Common/Commands.Common/Properties/AssemblyInfo.cs
index d1a1d4ab1791..3f06c165c06f 100644
--- a/src/Common/Commands.Common/Properties/AssemblyInfo.cs
+++ b/src/Common/Commands.Common/Properties/AssemblyInfo.cs
@@ -39,6 +39,8 @@
[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.Commands.WAPackIaaS.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.Commands.WAPackIaaS.FunctionalTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.Commands.Common.Storage, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
+[assembly: InternalsVisibleTo("Microsoft.Azure.Commands.Compute, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
+[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.Commands.ServiceManagement, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
#else
[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.Commands")]
[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.Commands.CloudService")]
@@ -51,4 +53,6 @@
[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.Commands.WAPackIaaS.Test")]
[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.Commands.WAPackIaaS.FunctionalTest")]
[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.Commands.Common.Storage")]
+[assembly: InternalsVisibleTo("Microsoft.Azure.Commands.Compute")]
+[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.Commands.ServiceManagement")]
#endif
diff --git a/src/Common/Commands.Common/Properties/Resources.Designer.cs b/src/Common/Commands.Common/Properties/Resources.Designer.cs
index 110270310161..1b2caf4f5f8e 100644
--- a/src/Common/Commands.Common/Properties/Resources.Designer.cs
+++ b/src/Common/Commands.Common/Properties/Resources.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
-// Runtime Version:4.0.30319.34014
+// Runtime Version:4.0.30319.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -276,6 +276,51 @@ public static string AzureSdkDirectory {
}
}
+ ///
+ /// Looks up a localized string similar to File '{0}' already exists. Use the -Force parameter to overwrite it..
+ ///
+ public static string AzureVMDscArchiveAlreadyExists {
+ get {
+ return ResourceManager.GetString("AzureVMDscArchiveAlreadyExists", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Create Archive.
+ ///
+ public static string AzureVMDscCreateArchiveAction {
+ get {
+ return ResourceManager.GetString("AzureVMDscCreateArchiveAction", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Parsing configuration script: {0}.
+ ///
+ public static string AzureVMDscParsingConfiguration {
+ get {
+ return ResourceManager.GetString("AzureVMDscParsingConfiguration", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Storage Blob '{0}' already exists. Use the -Force parameter to overwrite it..
+ ///
+ public static string AzureVMDscStorageBlobAlreadyExists {
+ get {
+ return ResourceManager.GetString("AzureVMDscStorageBlobAlreadyExists", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Upload '{0}'.
+ ///
+ public static string AzureVMDscUploadToBlobStorageAction {
+ get {
+ return ResourceManager.GetString("AzureVMDscUploadToBlobStorageAction", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Base Uri was empty..
///
@@ -2224,6 +2269,130 @@ public static string PublishVerifyingStorageMessage {
}
}
+ ///
+ /// Looks up a localized string similar to Configuration published to {0}.
+ ///
+ public static string PublishVMDscExtensionArchiveUploadedMessage {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionArchiveUploadedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy '{0}' to '{1}'..
+ ///
+ public static string PublishVMDscExtensionCopyFileVerbose {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionCopyFileVerbose", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy the module '{0}' to '{1}'..
+ ///
+ public static string PublishVMDscExtensionCopyModuleVerbose {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionCopyModuleVerbose", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid configuration file: {0}.
+ ///The file needs to be a PowerShell script (.ps1 or .psm1)..
+ ///
+ public static string PublishVMDscExtensionCreateArchiveConfigFileInvalidExtension {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionCreateArchiveConfigFileInvalidExtension", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Deleted '{0}'.
+ ///
+ public static string PublishVMDscExtensionDeletedFileMessage {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionDeletedFileMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot delete '{0}': {1}.
+ ///
+ public static string PublishVMDscExtensionDeleteErrorMessage {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionDeleteErrorMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot get module for DscResource '{0}'. Possible solutions:
+ ///1) Specify -ModuleName for Import-DscResource in your configuration.
+ ///2) Unblock module that contains resource.
+ ///3) Move Import-DscResource inside Node block.
+ ///.
+ ///
+ public static string PublishVMDscExtensionGetDscResourceFailed {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionGetDscResourceFailed", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to List of required modules: [{0}]..
+ ///
+ public static string PublishVMDscExtensionRequiredModulesVerbose {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionRequiredModulesVerbose", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Your current PowerShell version {1} is less then required by this cmdlet {0}. Consider download and install latest PowerShell version..
+ ///
+ public static string PublishVMDscExtensionRequiredPsVersion {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionRequiredPsVersion", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Configuration script '{0}' contained parse errors:
+ ///{1}.
+ ///
+ public static string PublishVMDscExtensionStorageParserErrors {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionStorageParserErrors", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Temp folder '{0}' created..
+ ///
+ public static string PublishVMDscExtensionTempFolderVerbose {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionTempFolderVerbose", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid configuration file: {0}.
+ ///The file needs to be a PowerShell script (.ps1 or .psm1) or a ZIP archive (.zip)..
+ ///
+ public static string PublishVMDscExtensionUploadArchiveConfigFileInvalidExtension {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionUploadArchiveConfigFileInvalidExtension", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Configuration file '{0}' not found..
+ ///
+ public static string PublishVMDscExtensionUploadArchiveConfigFileNotExist {
+ get {
+ return ResourceManager.GetString("PublishVMDscExtensionUploadArchiveConfigFileNotExist", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Replace current deployment with '{0}' Id ?.
///
diff --git a/src/Common/Commands.Common/Properties/Resources.resx b/src/Common/Commands.Common/Properties/Resources.resx
index 9d4beb951135..7616e8b49007 100644
--- a/src/Common/Commands.Common/Properties/Resources.resx
+++ b/src/Common/Commands.Common/Properties/Resources.resx
@@ -1361,7 +1361,86 @@ use and privacy statement at <url> and (c) agree to sharing my contact inf
The Switch-AzureMode cmdlet is deprecated and will be removed in a future release.
-
- OperationID : '{0}'
+
+ OperationID : '{0}'
+
+
+
+ Cannot get module for DscResource '{0}'. Possible solutions:
+1) Specify -ModuleName for Import-DscResource in your configuration.
+2) Unblock module that contains resource.
+3) Move Import-DscResource inside Node block.
+
+ 0 = name of DscResource
+
+
+ Your current PowerShell version {1} is less then required by this cmdlet {0}. Consider download and install latest PowerShell version.
+ {0} = minimal required PS version, {1} = current PS version
+
+
+ Parsing configuration script: {0}
+ {0} is the path to a script file
+
+
+ Configuration script '{0}' contained parse errors:
+{1}
+ 0 = path to the configuration script, 1 = parser errors
+
+
+ List of required modules: [{0}].
+ {0} = list of modules
+
+
+ Temp folder '{0}' created.
+ {0} = temp folder path
+
+
+ Copy '{0}' to '{1}'.
+ {0} = source, {1} = destination
+
+
+ Copy the module '{0}' to '{1}'.
+ {0} = source, {1} = destination
+
+
+ File '{0}' already exists. Use the -Force parameter to overwrite it.
+ {0} is the path to a file
+
+
+ Configuration file '{0}' not found.
+ 0 = path to the configuration file
+
+
+ Invalid configuration file: {0}.
+The file needs to be a PowerShell script (.ps1 or .psm1) or a ZIP archive (.zip).
+ 0 = path to the configuration file
+
+
+ Invalid configuration file: {0}.
+The file needs to be a PowerShell script (.ps1 or .psm1).
+ 0 = path to the configuration file
+
+
+ Create Archive
+
+
+ Upload '{0}'
+ {0} is the name of an storage blob
+
+
+ Storage Blob '{0}' already exists. Use the -Force parameter to overwrite it.
+ {0} is the name of an storage blob
+
+
+ Configuration published to {0}
+ {0} is an URI
+
+
+ Deleted '{0}'
+ {0} is the path of a file
+
+
+ Cannot delete '{0}': {1}
+ {0} is the path of a file, {1} is an error message
\ No newline at end of file
diff --git a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj
index 8ef60d6acfe3..076a768a6a8a 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj
+++ b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj
@@ -161,17 +161,11 @@
-
-
-
-
-
-
+
-
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParsingHelper.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParsingHelper.cs
deleted file mode 100644
index 3b8955128e20..000000000000
--- a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParsingHelper.cs
+++ /dev/null
@@ -1,245 +0,0 @@
-// ----------------------------------------------------------------------------------
-//
-// 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.IO;
-using System.Linq;
-using System.Management.Automation;
-using System.Management.Automation.Language;
-using System.Management.Automation.Runspaces;
-
-namespace Microsoft.Azure.Commands.Compute.Extension.DSC
-{
-
- public static class ConfigurationParsingHelper
- {
- private static readonly ConcurrentDictionary _resourceName2ModuleNameCache =
- new ConcurrentDictionary();
-
- private static bool IsParameterName(CommandElementAst ast, string name)
- {
- CommandParameterAst constantAst = ast as CommandParameterAst;
- if (constantAst == null)
- {
- return false;
- }
- return String.Equals(constantAst.ParameterName, name, StringComparison.OrdinalIgnoreCase);
- }
-
- private static List GetLegacyTopLevelParametersFromAst(CommandAst ast, string parameterName)
- {
- List parameters = new List();
- IEnumerable commandElement =
- ast.CommandElements.Where(x => IsParameterName(x, parameterName)).OfType();
- foreach (var commandElementAst in commandElement)
- {
- ArrayLiteralAst arrayLiteralAst = commandElementAst.Argument as ArrayLiteralAst;
- if (arrayLiteralAst != null)
- {
- parameters.AddRange(arrayLiteralAst.Elements.OfType().Select(x => x.Value));
- }
- }
- return parameters;
- }
-
-
- private static bool IsCandidateForImportDscResourceAst(Ast ast, int startOffset)
- {
- return ast.Extent.StartOffset == startOffset && !(ast is StatementBlockAst) && !(ast is NamedBlockAst);
- }
- private static List GetSingleAstRequiredModules(Ast ast, Token[] tokens)
- {
- List modules = new List();
- List resources = new List();
- var imports = tokens.Where(token =>
- String.Compare(token.Text, "Import-DscResource", StringComparison.OrdinalIgnoreCase) == 0);
-
- //
- // Create a function with the same name as Import-DscResource keyword and use powershell
- // argument function binding to emulate Import-DscResource argument binding.
- //
- InitialSessionState initialSessionState = InitialSessionState.Create();
- SessionStateFunctionEntry importDscResourcefunctionEntry = new SessionStateFunctionEntry(
- "Import-DscResource", @"param($Name, $ModuleName)
- if ($ModuleName)
- {
- foreach ($m in $ModuleName) { $global:modules.Add($m) }
- } else {
- foreach ($n in $Name) { $global:resources.Add($n) }
- }
- ");
- initialSessionState.Commands.Add(importDscResourcefunctionEntry);
- initialSessionState.LanguageMode = PSLanguageMode.RestrictedLanguage;
- var moduleVarEntry = new SessionStateVariableEntry("modules", modules, "");
- var resourcesVarEntry = new SessionStateVariableEntry("resources", resources, "");
- initialSessionState.Variables.Add(moduleVarEntry);
- initialSessionState.Variables.Add(resourcesVarEntry);
-
- using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create(initialSessionState))
- {
- foreach (var import in imports)
- {
- int startOffset = import.Extent.StartOffset;
- var asts = ast.FindAll(a => IsCandidateForImportDscResourceAst(a, startOffset), true);
- int longestLen = -1;
- Ast longestCandidate = null;
- foreach (var candidatAst in asts)
- {
- int curLen = candidatAst.Extent.EndOffset - candidatAst.Extent.StartOffset;
- if (curLen > longestLen)
- {
- longestCandidate = candidatAst;
- longestLen = curLen;
- }
- }
- // longestCandidate should contain AST for import-dscresource, like "Import-DSCResource -Module x -Name y".
- if (longestCandidate != null)
- {
- string importText = longestCandidate.Extent.Text;
- // We invoke-command "importText" here. Script injection is prevented:
- // We checked that file represents a valid AST without errors.
- powerShell.AddScript(importText);
- powerShell.Invoke();
- powerShell.Commands.Clear();
- }
- }
- }
- modules.AddRange(resources.Select(GetModuleNameForDscResource));
- return modules;
- }
-
- public static string GetModuleNameForDscResource(string resourceName)
- {
- string moduleName;
- if (!_resourceName2ModuleNameCache.TryGetValue(resourceName, out moduleName))
- {
- try
- {
- using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
- {
- powershell.AddCommand("Get-DscResource").
- AddCommand("Where-Object").AddParameter("Property", "ResourceType").AddParameter("Value", resourceName).AddParameter("EQ", true).
- AddCommand("Foreach-Object").AddParameter("MemberName", "Module").
- AddCommand("Foreach-Object").AddParameter("MemberName", "Name");
- moduleName = powershell.Invoke().First();
- }
- }
- catch (InvalidOperationException e)
- {
- throw new GetDscResourceException(resourceName, e);
- }
- _resourceName2ModuleNameCache.TryAdd(resourceName, moduleName);
- }
- return moduleName;
- }
-
- private static List GetRequiredModulesFromAst(Ast ast, Token[] tokens)
- {
- List modules = new List();
-
- // We use System.Management.Automation.Language.Parser to extract required modules from ast,
- // but format of ast is a bit tricky and have changed in time.
- //
- // There are two place where 'Import-DscResource' keyword can appear:
- // 1)
- // Configuration Foo {
- // Import-DscResource .... # outside node
- // Node Bar {...}
- // }
- // 2)
- // Configuration Foo {
- // Node Bar {
- // Import-DscResource .... # inside node
- // ...
- // }
- // }
- //
- // The old version of System.Management.Automation.Language.Parser produces slightly different AST for the first case.
- // In new version, Configuration corresponds to ConfigurationDefinitionAst.
- // In old version is's a generic CommandAst with specific commandElements which capture top-level Imports (case 1).
- // In new version all imports correspond to their own CommandAsts, same for case 2 in old version.
-
- // Old version, case 1:
- IEnumerable legacyConfigurationAsts = ast.FindAll(IsLegacyAstConfiguration, true).Select(x => (CommandAst)x);
- foreach (var legacyConfigurationAst in legacyConfigurationAsts)
- {
- // Note: these two sequences are translated to same AST:
- //
- // Import-DscResource -Module xComputerManagement; Import-DscResource -Name xComputer
- // Import-DscResource -Module xComputerManagement -Name xComputer
- //
- // We cannot distinguish different imports => cannot ignore resource names for imports with specified modules.
- // So we process everything: ModuleDefinition and ResourceDefinition.
-
- // Example: Import-DscResource -Module xPSDesiredStateConfiguration
- modules.AddRange(GetLegacyTopLevelParametersFromAst(legacyConfigurationAst, "ModuleDefinition"));
- // Example: Import-DscResource -Name MSFT_xComputer
- modules.AddRange(GetLegacyTopLevelParametersFromAst(legacyConfigurationAst, "ResourceDefinition").Select(GetModuleNameForDscResource));
- }
-
- // Both cases in new version and 2nd case in old version:
- modules.AddRange(GetSingleAstRequiredModules(ast, tokens));
-
- return modules.Distinct().ToList();
- }
-
- private static bool IsLegacyAstConfiguration(Ast node)
- {
- CommandAst commandNode = node as CommandAst;
- if (commandNode == null)
- {
- return false;
- }
- // TODO: Add case when configuration name is not a StringConstant, but a variable.
- StringConstantExpressionAst commandParameter = (commandNode.CommandElements[0] as StringConstantExpressionAst);
- if (commandParameter == null)
- {
- return false;
- }
- // Find the AST nodes defining configurations. These nodes will be CommandAst nodes
- // with 7 or 8 command elements (8 if the configuration requires any custom modules.)
- return
- commandNode.CommandElements.Count >= 7 &&
- String.Equals(commandParameter.Extent.Text, "configuration", StringComparison.OrdinalIgnoreCase) &&
- String.Equals(commandParameter.Value, @"PSDesiredStateConfiguration\Configuration",
- StringComparison.OrdinalIgnoreCase);
- }
-
- public static ConfigurationParseResult ParseConfiguration(string path)
- {
- // Get the resolved script path. This will throw an exception if the file is not found.
- string fullPath = Path.GetFullPath(path);
- Token[] tokens;
- ParseError[] errors;
- // Parse the script into an AST, capturing parse errors. Note - even with errors, the
- // file may still successfully define one or more configurations. We don't process
- // required modules in case of parsing errors to avoid script injection.
- ScriptBlockAst ast = Parser.ParseFile(fullPath, out tokens, out errors);
- List requiredModules = new List();
- if (!errors.Any())
- {
- requiredModules = GetRequiredModulesFromAst(ast, tokens).Distinct().ToList();
- }
- return new ConfigurationParseResult()
- {
- Path = fullPath,
- Errors = errors,
- RequiredModules = requiredModules,
- };
- }
- }
-}
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionCmdletCommonBase.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionCmdletCommonBase.cs
new file mode 100644
index 000000000000..a3cc9e74a336
--- /dev/null
+++ b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionCmdletCommonBase.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Globalization;
+using System.Management.Automation;
+using Microsoft.Azure.Commands.Management.Storage;
+using Microsoft.Azure.Management.Storage;
+using Microsoft.WindowsAzure.Commands.Utilities.Common;
+using Microsoft.WindowsAzure.Storage.Auth;
+
+namespace Microsoft.Azure.Commands.Compute.Extension.DSC
+{
+ static class DscExtensionCmdletCommonBase
+ {
+ private static StorageManagementClientWrapper _storageClientWrapper;
+
+ private static IStorageManagementClient GetStorageClient(this AzurePSCmdlet cmdlet)
+ {
+ if (_storageClientWrapper == null)
+ {
+ _storageClientWrapper = new StorageManagementClientWrapper(cmdlet.Profile.Context);
+ }
+
+ return _storageClientWrapper.StorageManagementClient;
+ }
+
+ internal static StorageCredentials GetStorageCredentials(this AzurePSCmdlet cmdlet, String resourceGroupName, String storageAccountName)
+ {
+ StorageCredentials credentials = null;
+ var storageClient = GetStorageClient(cmdlet);
+
+ if (storageClient != null && storageClient.StorageAccounts != null)
+ {
+ var keys = storageClient.StorageAccounts.ListKeys(resourceGroupName, storageAccountName);
+
+ if (keys != null && keys.StorageAccountKeys != null)
+ {
+ var storageAccountKey = string.IsNullOrEmpty(keys.StorageAccountKeys.Key1) ? keys.StorageAccountKeys.Key2 : keys.StorageAccountKeys.Key1;
+
+ credentials = new StorageCredentials(storageAccountName, storageAccountKey);
+ }
+ }
+
+ if (credentials == null)
+ {
+ cmdlet.ThrowTerminatingError(
+ new ErrorRecord(
+ new UnauthorizedAccessException(Properties.Resources.AzureVMDscDefaultStorageCredentialsNotFound),
+ "CredentialsNotFound",
+ ErrorCategory.PermissionDenied,
+ null));
+ }
+
+ if (string.IsNullOrEmpty(credentials.AccountName))
+ {
+ ThrowInvalidArgumentError(cmdlet, Properties.Resources.AzureVMDscStorageContextMustIncludeAccountName);
+ }
+
+ return credentials;
+ }
+
+ internal static void ThrowInvalidArgumentError(this AzurePSCmdlet cmdlet, string format, params object[] args)
+ {
+ cmdlet.ThrowTerminatingError(
+ new ErrorRecord(
+ new ArgumentException(string.Format(CultureInfo.CurrentUICulture, format, args)),
+ "InvalidArgument",
+ ErrorCategory.InvalidArgument,
+ null));
+ }
+ }
+}
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/GetAzureVMDscExtensionCommand.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/GetAzureVMDscExtensionCommand.cs
index 0a995359e9fb..50e5bf2af51f 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/GetAzureVMDscExtensionCommand.cs
+++ b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/GetAzureVMDscExtensionCommand.cs
@@ -1,11 +1,13 @@
-using System;
-using System.Collections;
-using System.Linq;
-using System.Management.Automation;
-using Microsoft.Azure.Commands.Compute.Common;
+using Microsoft.Azure.Commands.Compute.Common;
using Microsoft.Azure.Commands.Compute.Models;
using Microsoft.Azure.Management.Compute;
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC;
using Newtonsoft.Json;
+using System;
+using System.Collections;
+using System.Globalization;
+using System.Linq;
+using System.Management.Automation;
namespace Microsoft.Azure.Commands.Compute.Extension.DSC
{
@@ -15,7 +17,7 @@ namespace Microsoft.Azure.Commands.Compute.Extension.DSC
DefaultParameterSetName = GetDscExtensionParamSetName),
OutputType(
typeof(VirtualMachineDscExtensionContext))]
- public class GetAzureVMDscExtensionCommand : VirtualMachineDscExtensionBaseCmdlet
+ public class GetAzureVMDscExtensionCommand : VirtualMachineExtensionBaseCmdlet
{
private const string GetDscExtensionParamSetName = "GetDscExtension";
@@ -54,7 +56,7 @@ public override void ExecuteCmdlet()
if (String.IsNullOrEmpty(Name))
{
- Name = ExtensionNamespace + "." + ExtensionName;
+ Name = DscExtensionCmdletConstants.ExtensionPublishedNamespace + "." + DscExtensionCmdletConstants.ExtensionPublishedName;
}
if (Status)
@@ -63,9 +65,9 @@ public override void ExecuteCmdlet()
var extension = result.ToPSVirtualMachineExtension(ResourceGroupName);
if (
- extension.Publisher.Equals(ExtensionNamespace,
+ extension.Publisher.Equals(DscExtensionCmdletConstants.ExtensionPublishedNamespace,
StringComparison.InvariantCultureIgnoreCase) &&
- extension.ExtensionType.Equals(ExtensionName,
+ extension.ExtensionType.Equals(DscExtensionCmdletConstants.ExtensionPublishedName,
StringComparison.InvariantCultureIgnoreCase))
{
WriteObject(GetDscExtensionContext(extension));
@@ -81,9 +83,11 @@ public override void ExecuteCmdlet()
var extension = result.ToPSVirtualMachineExtension(ResourceGroupName);
if (
- extension.Publisher.Equals(ExtensionNamespace,
+ extension.Publisher.Equals(
+ DscExtensionCmdletConstants.ExtensionPublishedNamespace,
StringComparison.InvariantCultureIgnoreCase) &&
- extension.ExtensionType.Equals(ExtensionName,
+ extension.ExtensionType.Equals(
+ DscExtensionCmdletConstants.ExtensionPublishedName,
StringComparison.InvariantCultureIgnoreCase))
{
WriteObject(GetDscExtensionContext(extension));
@@ -113,28 +117,27 @@ private VirtualMachineDscExtensionContext GetDscExtensionContext(PSVirtualMachin
Statuses = extension.Statuses
};
- DscExtensionPublicSettings publicSettings;
+ DscExtensionPublicSettings extensionPublicSettings = null;
try
{
- publicSettings = string.IsNullOrEmpty(extension.PublicSettings) ? null
- : JsonConvert.DeserializeObject(extension.PublicSettings);
+ extensionPublicSettings = DscExtensionSettingsSerializer.DeserializePublicSettings(extension.PublicSettings);
}
- catch (Exception)
+ catch (JsonException e)
{
- // Try deserialize as version 1.0
- try
- {
- var publicSettingsV1 =
- JsonConvert.DeserializeObject(extension.PublicSettings);
- publicSettings = publicSettingsV1.ToCurrentVersion();
- }
- catch (JsonException)
- {
- throw;
- }
+ ThrowTerminatingError(
+ new ErrorRecord(
+ new JsonException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ Properties.Resources.AzureVMDscWrongSettingsFormat,
+ extension.PublicSettings),
+ e),
+ string.Empty,
+ ErrorCategory.ParserError,
+ null));
}
-
- if (publicSettings == null)
+
+ if (extensionPublicSettings == null)
{
context.ModulesUrl = string.Empty;
context.ConfigurationFunction = string.Empty;
@@ -142,9 +145,9 @@ private VirtualMachineDscExtensionContext GetDscExtensionContext(PSVirtualMachin
}
else
{
- context.ModulesUrl = publicSettings.ModulesUrl;
- context.ConfigurationFunction = publicSettings.ConfigurationFunction;
- context.Properties = new Hashtable(publicSettings.Properties.ToDictionary( x => x.Name, x => x.Value ));
+ context.ModulesUrl = extensionPublicSettings.ModulesUrl;
+ context.ConfigurationFunction = extensionPublicSettings.ConfigurationFunction;
+ context.Properties = new Hashtable(extensionPublicSettings.Properties.ToDictionary(x => x.Name, x => x.Value));
}
return context;
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/PublishAzureVMDscConfigurationCommand.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/PublishAzureVMDscConfigurationCommand.cs
index fd7f1a07276d..95d2a1afb29f 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/PublishAzureVMDscConfigurationCommand.cs
+++ b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/PublishAzureVMDscConfigurationCommand.cs
@@ -1,14 +1,10 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.IO.Compression;
-using System.Linq;
-using System.Management.Automation;
using Microsoft.Azure.Commands.Compute.Common;
-using Microsoft.WindowsAzure.Storage;
+using Microsoft.Azure.Common.Authentication.Models;
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC;
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC.Publish;
using Microsoft.WindowsAzure.Storage.Auth;
-using Microsoft.WindowsAzure.Storage.Blob;
+using System;
+using System.Management.Automation;
namespace Microsoft.Azure.Commands.Compute.Extension.DSC
{
@@ -24,7 +20,7 @@ namespace Microsoft.Azure.Commands.Compute.Extension.DSC
DefaultParameterSetName = UploadArchiveParameterSetName),
OutputType(
typeof(String))]
- public class PublishAzureVMDscConfigurationCommand : VirtualMachineDscExtensionBaseCmdlet
+ public class PublishAzureVMDscConfigurationCommand : DscExtensionPublishCmdletCommonBase
{
private const string CreateArchiveParameterSetName = "CreateArchive";
private const string UploadArchiveParameterSetName = "UploadArchive";
@@ -89,6 +85,16 @@ public class PublishAzureVMDscConfigurationCommand : VirtualMachineDscExtensionB
[ValidateNotNullOrEmpty]
public string OutputArchivePath { get; set; }
+ ///
+ /// Suffix for the storage end point, e.g. core.windows.net
+ ///
+ [Parameter(
+ ValueFromPipelineByPropertyName = true,
+ ParameterSetName = UploadArchiveParameterSetName,
+ HelpMessage = "Suffix for the storage end point, e.g. core.windows.net")]
+ [ValidateNotNullOrEmpty]
+ public string StorageEndpointSuffix { get; set; }
+
///
/// By default Publish-AzureVMDscConfiguration will not overwrite any existing blobs.
/// Use -Force to overwrite them.
@@ -104,20 +110,6 @@ public class PublishAzureVMDscConfigurationCommand : VirtualMachineDscExtensionB
///
private StorageCredentials _storageCredentials;
- private const string Ps1FileExtension = ".ps1";
- private const string Psm1FileExtension = ".psm1";
- private const string ZipFileExtension = ".zip";
-
- private static readonly HashSet UploadArchiveAllowedFileExtensions =
- new HashSet(StringComparer.OrdinalIgnoreCase) { Ps1FileExtension, Psm1FileExtension, ZipFileExtension };
- private static readonly HashSet CreateArchiveAllowedFileExtensions =
- new HashSet(StringComparer.OrdinalIgnoreCase) { Ps1FileExtension, Psm1FileExtension };
-
- private const int MinMajorPowerShellVersion = 4;
-
- private readonly List _temporaryFilesToDelete = new List();
- private readonly List _temporaryDirectoriesToDelete = new List();
-
public override void ExecuteCmdlet()
{
base.ExecuteCmdlet();
@@ -125,336 +117,40 @@ public override void ExecuteCmdlet()
try
{
ValidatePsVersion();
- ValidateParameters();
- PublishConfiguration();
- }
- finally
- {
- foreach (var file in _temporaryFilesToDelete)
- {
- try
- {
- DeleteFile(file);
- WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionDeletedFileMessage, file));
- }
- catch (Exception e)
- {
- WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionDeleteErrorMessage, file, e.Message));
- }
- }
- foreach (var directory in _temporaryDirectoriesToDelete)
- {
- try
- {
- DeleteDirectory(directory);
- WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionDeletedFileMessage, directory));
- }
- catch (Exception e)
- {
- WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionDeleteErrorMessage, directory, e.Message));
- }
- }
- }
- }
-
- protected void ValidatePsVersion()
- {
- using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
- {
- powershell.AddScript("$PSVersionTable.PSVersion.Major");
- int major = powershell.Invoke().FirstOrDefault();
- if (major < MinMajorPowerShellVersion)
- {
- ThrowTerminatingError(
- new ErrorRecord(
- new InvalidOperationException(
- string.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionRequiredPsVersion, MinMajorPowerShellVersion, major)),
- "InvalidPowerShellVersion",
- ErrorCategory.InvalidOperation,
- null));
- }
- }
- }
-
- protected void ValidateParameters()
- {
- ConfigurationPath = GetUnresolvedProviderPathFromPSPath(ConfigurationPath);
- if (!File.Exists(ConfigurationPath))
- {
- ThrowInvalidArgumentError(Properties.Resources.PublishVMDscExtensionUploadArchiveConfigFileNotExist, ConfigurationPath);
- }
-
- var configurationFileExtension = Path.GetExtension(ConfigurationPath);
-
- if (ParameterSetName == UploadArchiveParameterSetName)
- {
- // Check that ConfigurationPath points to a valid file
- if (!File.Exists(ConfigurationPath))
- {
- ThrowInvalidArgumentError(Properties.Resources.PublishVMDscExtensionConfigFileNotFound, ConfigurationPath);
- }
- if (!UploadArchiveAllowedFileExtensions.Contains(Path.GetExtension(configurationFileExtension)))
- {
- ThrowInvalidArgumentError(Properties.Resources.PublishVMDscExtensionUploadArchiveConfigFileInvalidExtension, ConfigurationPath);
- }
-
- _storageCredentials = GetStorageCredentials(ResourceGroupName, StorageAccountName);
-
- if (ContainerName == null)
- {
- ContainerName = DefaultContainerName;
- }
- }
- else if (ParameterSetName == CreateArchiveParameterSetName)
- {
- if (!CreateArchiveAllowedFileExtensions.Contains(Path.GetExtension(configurationFileExtension)))
- {
- ThrowInvalidArgumentError(Properties.Resources.PublishVMDscExtensionCreateArchiveConfigFileInvalidExtension, ConfigurationPath);
- }
-
- OutputArchivePath = GetUnresolvedProviderPathFromPSPath(OutputArchivePath);
- }
- }
-
- ///
- /// Publish the configuration and its modules
- ///
- protected void PublishConfiguration()
- {
- if (ParameterSetName == CreateArchiveParameterSetName)
- {
- ConfirmAction(true, string.Empty, Properties.Resources.AzureVMDscCreateArchiveAction, OutputArchivePath, () => CreateConfigurationArchive());
- }
- else
- {
- var archivePath = string.Compare(Path.GetExtension(ConfigurationPath), ZipFileExtension, StringComparison.OrdinalIgnoreCase) == 0 ?
- ConfigurationPath
- :
- CreateConfigurationArchive();
-
- UploadConfigurationArchive(archivePath);
- }
- }
-
- //Private Methods
- private string CreateConfigurationArchive()
- {
- WriteVerbose(String.Format(CultureInfo.CurrentUICulture, Properties.Resources.AzureVMDscParsingConfiguration, ConfigurationPath));
- ConfigurationParseResult parseResult = null;
- try
- {
- parseResult = ConfigurationParsingHelper.ParseConfiguration(ConfigurationPath);
- }
- catch (GetDscResourceException e)
- {
- ThrowTerminatingError(new ErrorRecord(e, "CannotAccessDscResource", ErrorCategory.PermissionDenied, null));
- }
-
- if (parseResult.Errors.Any())
- {
- ThrowTerminatingError(
- new ErrorRecord(
- new ParseException(
- String.Format(
- CultureInfo.CurrentUICulture,
- Properties.Resources.PublishVMDscExtensionStorageParserErrors,
- ConfigurationPath,
- String.Join("\n", parseResult.Errors.Select(error => error.ToString())))),
- "DscConfigurationParseError",
- ErrorCategory.ParserError,
- null));
- }
- List requiredModules = parseResult.RequiredModules;
- //Since LCM always uses the latest module there is no need to copy PSDesiredStateConfiguration
- if (requiredModules.Contains("PSDesiredStateConfiguration"))
- {
- requiredModules.Remove("PSDesiredStateConfiguration");
- }
-
- WriteVerbose(String.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionRequiredModulesVerbose, String.Join(", ", requiredModules)));
- // Create a temporary directory for uploaded zip file
- string tempZipFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
- WriteVerbose(String.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionTempFolderVerbose, tempZipFolder));
- Directory.CreateDirectory(tempZipFolder);
- _temporaryDirectoriesToDelete.Add(tempZipFolder);
+ //validate cmdlet params
+ ConfigurationPath = GetUnresolvedProviderPathFromPSPath(ConfigurationPath);
- // CopyConfiguration
- var configurationName = Path.GetFileName(ConfigurationPath);
- var configurationDestination = Path.Combine(tempZipFolder, configurationName);
- WriteVerbose(String.Format(
- CultureInfo.CurrentUICulture,
- Properties.Resources.PublishVMDscExtensionCopyFileVerbose,
- ConfigurationPath,
- configurationDestination));
- File.Copy(ConfigurationPath, configurationDestination);
+ ValidateConfigurationPath(ConfigurationPath, ParameterSetName);
- // CopyRequiredModules
- foreach (var module in requiredModules)
- {
- using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
+ switch (ParameterSetName)
{
- // Wrapping script in a function to prevent script injection via $module variable.
- powershell.AddScript(
- @"function Copy-Module([string]$module, [string]$tempZipFolder)
+ case CreateArchiveParameterSetName:
+ OutputArchivePath = GetUnresolvedProviderPathFromPSPath(OutputArchivePath);
+ break;
+ case UploadArchiveParameterSetName:
+ _storageCredentials = this.GetStorageCredentials(ResourceGroupName,StorageAccountName);
+ if (ContainerName == null)
{
- $mi = Get-Module -List -Name $module;
- $moduleFolder = Split-Path $mi.Path;
- Copy-Item -Recurse -Path $moduleFolder -Destination ""$tempZipFolder\$($mi.Name)""
- }"
- );
- powershell.Invoke();
- powershell.Commands.Clear();
- powershell.AddCommand("Copy-Module")
- .AddParameter("module", module)
- .AddParameter("tempZipFolder", tempZipFolder);
- WriteVerbose(String.Format(
- CultureInfo.CurrentUICulture,
- Properties.Resources.PublishVMDscExtensionCopyModuleVerbose,
- module,
- tempZipFolder));
- powershell.Invoke();
- }
- }
-
- //
- // Zip the directory
- //
- string archive;
-
- if (ParameterSetName == CreateArchiveParameterSetName)
- {
- archive = OutputArchivePath;
-
- if (!Force && File.Exists(archive))
- {
- ThrowTerminatingError(
- new ErrorRecord(
- new UnauthorizedAccessException(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.AzureVMDscArchiveAlreadyExists, archive)),
- "FileAlreadyExists",
- ErrorCategory.PermissionDenied,
- null));
- }
- }
- else
- {
- string tempArchiveFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
- WriteVerbose(String.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionTempFolderVerbose, tempArchiveFolder));
- Directory.CreateDirectory(tempArchiveFolder);
- _temporaryDirectoriesToDelete.Add(tempArchiveFolder);
- archive = Path.Combine(tempArchiveFolder, configurationName + ZipFileExtension);
- _temporaryFilesToDelete.Add(archive);
- }
-
- if (File.Exists(archive))
- {
- File.Delete(archive);
- }
-
- ZipFile.CreateFromDirectory(tempZipFolder, archive);
- return archive;
- }
-
- private void UploadConfigurationArchive(string archivePath)
- {
- CloudBlobContainer cloudBlobContainer = GetStorageContainer();
-
- var blobName = Path.GetFileName(archivePath);
-
- CloudBlockBlob modulesBlob = cloudBlobContainer.GetBlockBlobReference(blobName);
-
- ConfirmAction(true, string.Empty, string.Format(CultureInfo.CurrentUICulture, Properties.Resources.AzureVMDscUploadToBlobStorageAction, archivePath), modulesBlob.Uri.AbsoluteUri, () =>
- {
- if (!Force && modulesBlob.Exists())
- {
- ThrowTerminatingError(
- new ErrorRecord(
- new UnauthorizedAccessException(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.AzureVMDscStorageBlobAlreadyExists, modulesBlob.Uri.AbsoluteUri)),
- "StorageBlobAlreadyExists",
- ErrorCategory.PermissionDenied,
- null));
+ ContainerName = DscExtensionCmdletConstants.DefaultContainerName;
+ }
+ if (StorageEndpointSuffix == null)
+ {
+ StorageEndpointSuffix =
+ Profile.Context.Environment.GetEndpoint(AzureEnvironment.Endpoint.StorageEndpointSuffix);
+ }
+ break;
}
- modulesBlob.UploadFromFile(archivePath, FileMode.Open);
-
- WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.PublishVMDscExtensionArchiveUploadedMessage, modulesBlob.Uri.AbsoluteUri));
- WriteObject(modulesBlob.Uri.AbsoluteUri);
- });
- }
-
- private CloudBlobContainer GetStorageContainer()
- {
- var storageAccount = new CloudStorageAccount(_storageCredentials, true);
- var blobClient = storageAccount.CreateCloudBlobClient();
- CloudBlobContainer containerReference = blobClient.GetContainerReference(ContainerName);
- containerReference.CreateIfNotExists();
- return containerReference;
- }
-
- private static void DeleteFile(string path)
- {
- try
- {
- File.Delete(path);
- }
- catch (UnauthorizedAccessException)
- {
- // the exception may have occurred due to a read-only file
- DeleteReadOnlyFile(path);
- }
- }
-
- ///
- /// Turns off the ReadOnly attribute from the given file and then attempt to delete it
- ///
- private static void DeleteReadOnlyFile(string path)
- {
- var attributes = File.GetAttributes(path);
-
- if ((attributes & FileAttributes.ReadOnly) != 0)
- {
- File.SetAttributes(path, attributes & ~FileAttributes.ReadOnly);
- }
-
- File.Delete(path);
- }
-
- private static void DeleteDirectory(string path)
- {
- try
- {
- Directory.Delete(path, true);
- }
- catch (UnauthorizedAccessException)
- {
- // the exception may have occurred due to a read-only file or directory
- DeleteReadOnlyDirectory(path);
- }
- }
-
- ///
- /// Recusively turns off the ReadOnly attribute from the given directory and then attemps to delete it
- ///
- private static void DeleteReadOnlyDirectory(string path)
- {
- var directory = new DirectoryInfo(path);
-
- foreach (var child in directory.GetDirectories())
- {
- DeleteReadOnlyDirectory(child.FullName);
- }
-
- foreach (var child in directory.GetFiles())
- {
- DeleteReadOnlyFile(child.FullName);
+ PublishConfiguration(
+ ConfigurationPath, OutputArchivePath, Force.IsPresent, _storageCredentials, StorageEndpointSuffix, ContainerName, ParameterSetName
+ );
}
-
- if ((directory.Attributes & FileAttributes.ReadOnly) != 0)
+ finally
{
- directory.Attributes &= ~FileAttributes.ReadOnly;
+ DeleteTemporaryFiles();
}
-
- directory.Delete();
}
}
}
+
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/RemoveAzureVMDscExtensionCommand.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/RemoveAzureVMDscExtensionCommand.cs
index 3a5b651bc1ef..9065e2b15393 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/RemoveAzureVMDscExtensionCommand.cs
+++ b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/RemoveAzureVMDscExtensionCommand.cs
@@ -1,11 +1,12 @@
-using System;
-using System.Globalization;
-using AutoMapper;
+using AutoMapper;
using Microsoft.Azure.Commands.Compute.Common;
using Microsoft.Azure.Commands.Compute.Models;
using Microsoft.Azure.Management.Compute;
-using System.Management.Automation;
using Microsoft.Azure.Management.Compute.Models;
+using System;
+using System.Globalization;
+using System.Management.Automation;
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC;
namespace Microsoft.Azure.Commands.Compute.Extension.DSC
{
@@ -17,7 +18,7 @@ namespace Microsoft.Azure.Commands.Compute.Extension.DSC
ProfileNouns.VirtualMachineDscExtension,
SupportsShouldProcess = true)]
[OutputType(typeof(PSComputeLongRunningOperation))]
- public class RemoveAzureVMDscExtensionCommand : VirtualMachineDscExtensionBaseCmdlet
+ public class RemoveAzureVMDscExtensionCommand : VirtualMachineExtensionBaseCmdlet
{
[Parameter(
Mandatory = true,
@@ -48,7 +49,7 @@ public override void ExecuteCmdlet()
if (String.IsNullOrEmpty(Name))
{
- Name = ExtensionNamespace + "." + ExtensionName;
+ Name = DscExtensionCmdletConstants.ExtensionPublishedName + "." + DscExtensionCmdletConstants.ExtensionPublishedName;
}
if (ShouldProcess(string.Format(CultureInfo.CurrentUICulture, Properties.Resources.DscExtensionRemovalConfirmation, Name), Properties.Resources.DscExtensionRemovalCaption))
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/SetAzureVMDscExtensionCommand.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/SetAzureVMDscExtensionCommand.cs
index 1f1ae2211aa5..16785bbaaeb4 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/SetAzureVMDscExtensionCommand.cs
+++ b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/SetAzureVMDscExtensionCommand.cs
@@ -1,17 +1,19 @@
-using System;
-using System.Collections;
-using System.Globalization;
-using System.IO;
-using System.Management.Automation;
-using System.Text.RegularExpressions;
using AutoMapper;
using Microsoft.Azure.Commands.Compute.Common;
using Microsoft.Azure.Commands.Compute.Models;
+using Microsoft.Azure.Common.Authentication.Models;
using Microsoft.Azure.Management.Compute;
using Microsoft.Azure.Management.Compute.Models;
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+using System.Management.Automation;
+using System.Text.RegularExpressions;
namespace Microsoft.Azure.Commands.Compute.Extension.DSC
{
@@ -21,7 +23,7 @@ namespace Microsoft.Azure.Commands.Compute.Extension.DSC
SupportsShouldProcess = true,
DefaultParameterSetName = AzureBlobDscExtensionParamSet)]
[OutputType(typeof(PSComputeLongRunningOperation))]
- public class SetAzureVMDscExtensionCommand : VirtualMachineDscExtensionBaseCmdlet
+ public class SetAzureVMDscExtensionCommand : VirtualMachineExtensionBaseCmdlet
{
protected const string AzureBlobDscExtensionParamSet = "AzureBlobDscExtension";
@@ -204,11 +206,11 @@ private void ValidateParameters()
if (ConfigurationName != null || ConfigurationArgument != null
|| ConfigurationData != null)
{
- ThrowInvalidArgumentError(Properties.Resources.AzureVMDscNullArchiveNoConfiguragionParameters);
+ this.ThrowInvalidArgumentError(Properties.Resources.AzureVMDscNullArchiveNoConfiguragionParameters);
}
if (ArchiveContainerName != null || ArchiveStorageEndpointSuffix != null)
{
- ThrowInvalidArgumentError(Properties.Resources.AzureVMDscNullArchiveNoStorageParameters);
+ this.ThrowInvalidArgumentError(Properties.Resources.AzureVMDscNullArchiveNoStorageParameters);
}
}
else
@@ -218,7 +220,7 @@ private void ValidateParameters()
ArchiveBlobName,
StringComparison.InvariantCultureIgnoreCase) != 0)
{
- ThrowInvalidArgumentError(Properties.Resources.AzureVMDscConfigurationDataFileShouldNotIncludePath);
+ this.ThrowInvalidArgumentError(Properties.Resources.AzureVMDscConfigurationDataFileShouldNotIncludePath);
}
if (ConfigurationData != null)
@@ -227,7 +229,7 @@ private void ValidateParameters()
if (!File.Exists(ConfigurationData))
{
- ThrowInvalidArgumentError(
+ this.ThrowInvalidArgumentError(
Properties.Resources.AzureVMDscCannotFindConfigurationDataFile,
ConfigurationData);
}
@@ -236,7 +238,7 @@ private void ValidateParameters()
".psd1",
StringComparison.InvariantCultureIgnoreCase) != 0)
{
- ThrowInvalidArgumentError(Properties.Resources.AzureVMDscInvalidConfigurationDataFile);
+ this.ThrowInvalidArgumentError(Properties.Resources.AzureVMDscInvalidConfigurationDataFile);
}
}
@@ -245,7 +247,7 @@ private void ValidateParameters()
ArchiveResourceGroupName = ResourceGroupName;
}
- _storageCredentials = GetStorageCredentials(ArchiveResourceGroupName, ArchiveStorageAccountName);
+ _storageCredentials = this.GetStorageCredentials(ArchiveResourceGroupName, ArchiveStorageAccountName);
if (ConfigurationName == null)
{
@@ -254,12 +256,18 @@ private void ValidateParameters()
if (ArchiveContainerName == null)
{
- ArchiveContainerName = DefaultContainerName;
+ ArchiveContainerName = DscExtensionCmdletConstants.DefaultContainerName;
+ }
+
+ if (ArchiveStorageEndpointSuffix == null)
+ {
+ ArchiveStorageEndpointSuffix =
+ Profile.Context.Environment.GetEndpoint(AzureEnvironment.Endpoint.StorageEndpointSuffix);
}
if (!(Regex.Match(Version, VersionRegexExpr).Success))
{
- ThrowInvalidArgumentError(Properties.Resources.AzureVMDscExtensionInvalidVersion);
+ this.ThrowInvalidArgumentError(Properties.Resources.AzureVMDscExtensionInvalidVersion);
}
}
}
@@ -281,7 +289,7 @@ private void CreateConfiguration()
Path.GetFileNameWithoutExtension(ArchiveBlobName),
ConfigurationName);
Tuple settings =
- DscSettingsSerializer.SeparatePrivateItems(ConfigurationArgument);
+ DscExtensionSettingsSerializer.SeparatePrivateItems(ConfigurationArgument);
publicSettings.Properties = settings.Item1;
privateSettings.Items = settings.Item2;
@@ -291,16 +299,16 @@ private void CreateConfiguration()
var parameters = new VirtualMachineExtension
{
Location = Location,
- Name = Name ?? ExtensionNamespace + "." + ExtensionName,
+ Name = Name ?? DscExtensionCmdletConstants.ExtensionPublishedNamespace + "." + DscExtensionCmdletConstants.ExtensionPublishedName,
Type = VirtualMachineExtensionType,
- Publisher = ExtensionNamespace,
- ExtensionType = ExtensionName,
+ Publisher = DscExtensionCmdletConstants.ExtensionPublishedNamespace,
+ ExtensionType = DscExtensionCmdletConstants.ExtensionPublishedName,
TypeHandlerVersion = Version,
// Define the public and private property bags that will be passed to the extension.
- Settings = DscSettingsSerializer.SerializePublicSettings(publicSettings),
+ Settings = DscExtensionSettingsSerializer.SerializePublicSettings(publicSettings),
//PrivateConfuguration contains sensitive data in a plain text
- ProtectedSettings = DscSettingsSerializer.SerializePrivateSettings(privateSettings),
- AutoUpgradeMinorVersion = AutoUpdate.IsPresent ? true : false
+ ProtectedSettings = DscExtensionSettingsSerializer.SerializePrivateSettings(privateSettings),
+ AutoUpgradeMinorVersion = AutoUpdate.IsPresent
};
//Add retry logic due to CRP service restart known issue CRP bug: 3564713
@@ -335,12 +343,7 @@ private ConfigurationUris UploadConfigurationDataToBlob()
//
// Get a reference to the container in blob storage
//
- var storageAccount = string.IsNullOrEmpty(ArchiveStorageEndpointSuffix)
- ? new CloudStorageAccount(_storageCredentials, true)
- : new CloudStorageAccount(
- _storageCredentials,
- ArchiveStorageEndpointSuffix,
- true);
+ var storageAccount = new CloudStorageAccount(_storageCredentials, ArchiveStorageEndpointSuffix, true);
var blobClient = storageAccount.CreateCloudBlobClient();
@@ -367,6 +370,18 @@ private ConfigurationUris UploadConfigurationDataToBlob()
if (ConfigurationData != null)
{
+ var guid = Guid.NewGuid();
+ // there may be multiple VMs using the same configuration
+
+ var configurationDataBlobName = string.Format(
+ CultureInfo.InvariantCulture,
+ "{0}-{1}.psd1",
+ ConfigurationName,
+ guid);
+
+ var configurationDataBlobReference =
+ containerReference.GetBlockBlobReference(configurationDataBlobName);
+
ConfirmAction(
true,
string.Empty,
@@ -374,21 +389,9 @@ private ConfigurationUris UploadConfigurationDataToBlob()
CultureInfo.CurrentUICulture,
Properties.Resources.AzureVMDscUploadToBlobStorageAction,
ConfigurationData),
- configurationBlobReference.Uri.AbsoluteUri,
+ configurationDataBlobReference.Uri.AbsoluteUri,
() =>
{
- var guid = Guid.NewGuid();
- // there may be multiple VMs using the same configuration
-
- var configurationDataBlobName = string.Format(
- CultureInfo.InvariantCulture,
- "{0}-{1}.psd1",
- ConfigurationName,
- guid);
-
- var configurationDataBlobReference =
- containerReference.GetBlockBlobReference(configurationDataBlobName);
-
if (!Force && configurationDataBlobReference.Exists())
{
ThrowTerminatingError(
@@ -432,3 +435,4 @@ private class ConfigurationUris
}
}
}
+
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/VirtualMachineDscExtensionBaseCmdlet.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/VirtualMachineDscExtensionBaseCmdlet.cs
deleted file mode 100644
index 0b2af7d7cc25..000000000000
--- a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/VirtualMachineDscExtensionBaseCmdlet.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Management.Automation;
-using Microsoft.Azure.Commands.Management.Storage;
-using Microsoft.Azure.Common.Authentication.Models;
-using Microsoft.Azure.Management.Storage;
-using Microsoft.Azure.Management.Storage.Models;
-using Microsoft.WindowsAzure.Commands.Common.Storage;
-using Microsoft.WindowsAzure.Storage.Auth;
-
-namespace Microsoft.Azure.Commands.Compute.Extension.DSC
-{
- public class VirtualMachineDscExtensionBaseCmdlet : VirtualMachineExtensionBaseCmdlet
- {
- private StorageManagementClientWrapper _storageClientWrapper;
-
- protected string ExtensionNamespace = "Microsoft.Powershell";
- protected string ExtensionName = "DSC";
- protected string DefaultContainerName = "windows-powershell-dsc";
-
- private IStorageManagementClient StorageClient
- {
- get
- {
- if (_storageClientWrapper == null)
- {
- _storageClientWrapper = new StorageManagementClientWrapper(Profile.Context)
- {
- VerboseLogger = WriteVerboseWithTimestamp,
- ErrorLogger = WriteErrorWithTimestamp
- };
- }
-
- return _storageClientWrapper.StorageManagementClient;
- }
-
- set { _storageClientWrapper = new StorageManagementClientWrapper(value); }
- }
-
- protected StorageCredentials GetStorageCredentials(String resourceGroupName, String storageAccountName)
- {
- StorageCredentials credentials = null;
-
- if (StorageClient != null && StorageClient.StorageAccounts != null)
- {
- var keys = StorageClient.StorageAccounts.ListKeys(resourceGroupName, storageAccountName);
-
- if (keys != null && keys.StorageAccountKeys != null)
- {
- var storageAccountKey = string.IsNullOrEmpty(keys.StorageAccountKeys.Key1) ? keys.StorageAccountKeys.Key2 : keys.StorageAccountKeys.Key1;
-
- credentials = new StorageCredentials(storageAccountName, storageAccountKey);
- }
- }
-
- if (credentials == null)
- {
- ThrowTerminatingError(
- new ErrorRecord(
- new UnauthorizedAccessException(Properties.Resources.AzureVMDscDefaultStorageCredentialsNotFound),
- "CredentialsNotFound",
- ErrorCategory.PermissionDenied,
- null));
- }
-
- if (string.IsNullOrEmpty(credentials.AccountName))
- {
- ThrowInvalidArgumentError(Properties.Resources.AzureVMDscStorageContextMustIncludeAccountName);
- }
-
- return credentials;
- }
-
- //TODO: Move this to a common Exception class for DSC
- protected void ThrowInvalidArgumentError(string format, params object[] args)
- {
- ThrowTerminatingError(
- new ErrorRecord(
- new ArgumentException(string.Format(CultureInfo.CurrentUICulture, format, args)),
- "InvalidArgument",
- ErrorCategory.InvalidArgument,
- null));
- }
- }
-}
diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/VirtualMachineDscExtensionContext.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/VirtualMachineDscExtensionContext.cs
index 01172fe6642d..a38a9c0524a6 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/VirtualMachineDscExtensionContext.cs
+++ b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/VirtualMachineDscExtensionContext.cs
@@ -12,8 +12,10 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
-using System.Collections;
using Microsoft.Azure.Commands.Compute.Models;
+using Newtonsoft.Json;
+using System;
+using System.Collections;
namespace Microsoft.Azure.Commands.Compute.Extension.DSC
{
@@ -24,5 +26,14 @@ public class VirtualMachineDscExtensionContext : PSVirtualMachineExtension
public string ConfigurationFunction { get; set; }
public Hashtable Properties { get; set; }
+ [JsonIgnore]
+ public string StatusesText
+ {
+ get
+ {
+ var status = JsonConvert.SerializeObject(Statuses, Formatting.Indented);
+ return "null".Equals(status) ? String.Empty : status;
+ }
+ }
}
}
diff --git a/src/ResourceManager/Compute/Commands.Compute/Microsoft.Azure.Commands.Compute.dll-Help.xml b/src/ResourceManager/Compute/Commands.Compute/Microsoft.Azure.Commands.Compute.dll-Help.xml
index 001a1b62e0af..e6b2b1574f74 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Microsoft.Azure.Commands.Compute.dll-Help.xml
+++ b/src/ResourceManager/Compute/Commands.Compute/Microsoft.Azure.Commands.Compute.dll-Help.xml
@@ -3076,7 +3076,14 @@ $ext = Get-AzureVMCustomScriptExtension -ResourceGroupName $rgname -VMName $vmna
String
-
+
+ StorageEndpointSuffix
+
+ Suffix for the storage end point, e.g. core.windows.net
+
+ string
+
+
WhatIf
Describes what would happen if you executed the command without actually executing the command.
@@ -3187,7 +3194,19 @@ When this parameter is used, the configuration script is not uploaded to Azure b
-
+
+ StorageEndpointSuffix
+
+ Suffix for the storage end point, e.g. core.windows.net
+
+ string
+
+ string
+
+
+
+
+
WhatIf
Describes what would happen if you executed the command without actually executing the command.
@@ -13021,4 +13040,4 @@ Set-AzureVMSourceImage -VM $vm -Name $img -DestinationVhdsContainer $vhdContaine
-
\ No newline at end of file
+
diff --git a/src/ResourceManager/Compute/Commands.Compute/Microsoft.Azure.Commands.Compute.format.ps1xml b/src/ResourceManager/Compute/Commands.Compute/Microsoft.Azure.Commands.Compute.format.ps1xml
index faaed11d201d..9c1b4c5c02f0 100644
--- a/src/ResourceManager/Compute/Commands.Compute/Microsoft.Azure.Commands.Compute.format.ps1xml
+++ b/src/ResourceManager/Compute/Commands.Compute/Microsoft.Azure.Commands.Compute.format.ps1xml
@@ -481,5 +481,68 @@
+
+
+ Microsoft.Azure.Commands.Compute.Extension.DSC.VirtualMachineDscExtensionContext
+
+ Microsoft.Azure.Commands.Compute.Extension.DSC.VirtualMachineDscExtensionContext
+
+
+
+
+
+
+
+ ResourceGroupName
+
+
+
+ Name
+
+
+
+ Publisher
+
+
+
+ ExtensionType
+
+
+
+ TypeHandlerVersion
+
+
+
+ ProvisioningState
+
+
+
+ Location
+
+
+
+ Id
+
+
+
+ ModulesUrl
+
+
+
+ ConfigurationFunction
+
+
+
+ Properties
+
+
+
+ StatusesText
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement.Extensions.Test/DSC/DscExtensionConfigurationParsingHelperTests.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement.Extensions.Test/DSC/DscExtensionConfigurationParsingHelperTests.cs
index 4f570635d7d0..8855bc21ebd1 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement.Extensions.Test/DSC/DscExtensionConfigurationParsingHelperTests.cs
+++ b/src/ServiceManagement/Compute/Commands.ServiceManagement.Extensions.Test/DSC/DscExtensionConfigurationParsingHelperTests.cs
@@ -17,6 +17,7 @@
using System.IO.Compression;
using System.Linq;
using Microsoft.WindowsAzure.Commands.ScenarioTest;
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC.Publish;
using Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC;
using Xunit;
@@ -93,7 +94,7 @@ public void TestExtractConfigurationNames1()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(CorporateClientConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(1, results.RequiredModules.Count);
- Assert.Equal("xComputerManagement", results.RequiredModules[0]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xComputerManagement"));
}
[Fact]
@@ -103,8 +104,8 @@ public void TestExtractConfigurationNames2()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(DomainControllerConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(2, results.RequiredModules.Count);
- Assert.Equal("xComputerManagement", results.RequiredModules[0]);
- Assert.Equal("xActiveDirectory", results.RequiredModules[1]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xComputerManagement"));
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xActiveDirectory"));
}
[Fact]
@@ -114,7 +115,7 @@ public void TestExtractConfigurationNames3()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(VisualStudioPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(1, results.RequiredModules.Count);
- Assert.Equal("xPSDesiredStateConfiguration", results.RequiredModules[0]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xPSDesiredStateConfiguration"));
}
[Fact]
@@ -124,9 +125,9 @@ public void TestExtractConfigurationNamesMulti()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(ShMulptiConfigurationsPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(3, results.RequiredModules.Count);
- Assert.Equal("xComputerManagement", results.RequiredModules[0]);
- Assert.Equal("xNetworking", results.RequiredModules[1]);
- Assert.Equal("xPSDesiredStateConfiguration", results.RequiredModules[2]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xComputerManagement"));
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xNetworking"));
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xPSDesiredStateConfiguration"));
}
[Fact]
@@ -136,8 +137,8 @@ public void TestNameImportListInsideNode()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(NameImportListInsideNodeConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(2, results.RequiredModules.Count);
- Assert.Equal("xComputerManagement", results.RequiredModules[0]);
- Assert.Equal("xActiveDirectory", results.RequiredModules[1]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xComputerManagement"));
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xActiveDirectory"));
}
[Fact]
@@ -147,8 +148,8 @@ public void TestNameImportListOutsideNode()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(NameImportListOutsideNodeConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(2, results.RequiredModules.Count);
- Assert.Equal("xComputerManagement", results.RequiredModules[0]);
- Assert.Equal("xActiveDirectory", results.RequiredModules[1]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xComputerManagement"));
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xActiveDirectory"));
}
[Fact]
@@ -158,7 +159,7 @@ public void TestNameImportSingleInsideNode()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(NameImportSingleInsideNodeConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(1, results.RequiredModules.Count);
- Assert.Equal("xComputerManagement", results.RequiredModules[0]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xComputerManagement"));
}
[Fact]
@@ -168,7 +169,7 @@ public void TestNameImportSingleOutsideNode()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(NameImportSingleOutsideNodeConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(1, results.RequiredModules.Count);
- Assert.Equal("xComputerManagement", results.RequiredModules[0]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xComputerManagement"));
}
[Fact]
@@ -178,7 +179,7 @@ public void TestNameModuleImportSingleInsideNode()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(NameModuleImportSingleInsideNodeConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(1, results.RequiredModules.Count);
- Assert.Equal("xComputerManagement", results.RequiredModules[0]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xComputerManagement"));
}
[Fact]
@@ -188,8 +189,8 @@ public void TestModuleImportListInsideNode()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(ModuleImportListInsideNodeConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(2, results.RequiredModules.Count);
- Assert.Equal("xPSDesiredStateConfiguration", results.RequiredModules[0]);
- Assert.Equal("xNetworking", results.RequiredModules[1]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xPSDesiredStateConfiguration"));
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xNetworking"));
}
[Fact]
@@ -199,8 +200,8 @@ public void TestModuleImportListOutsideNode()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(ModuleImportListOutsideNodeConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(2, results.RequiredModules.Count);
- Assert.Equal("xPSDesiredStateConfiguration", results.RequiredModules[0]);
- Assert.Equal("xNetworking", results.RequiredModules[1]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xPSDesiredStateConfiguration"));
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xNetworking"));
}
[Fact]
@@ -210,7 +211,7 @@ public void TestModuleImportSingleInsideNode()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(ModuleImportSingleInsideNodeConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(1, results.RequiredModules.Count);
- Assert.Equal("xNetworking", results.RequiredModules[0]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xNetworking"));
}
[Fact]
@@ -220,7 +221,7 @@ public void TestModuleImportSingleOutsideNode()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(ModuleImportSingleOutsideNodeConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(1, results.RequiredModules.Count);
- Assert.Equal("xNetworking", results.RequiredModules[0]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xNetworking"));
}
[Fact]
@@ -230,7 +231,7 @@ public void TestIeeScGood()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(IeeScGoodConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(1, results.RequiredModules.Count);
- Assert.Equal("xSystemSecurity", results.RequiredModules[0]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xSystemSecurity"));
}
[Fact]
@@ -240,7 +241,7 @@ public void TestIeeScBad()
ConfigurationParseResult results = ConfigurationParsingHelper.ParseConfiguration(IeeScBadConfigurationPath);
Assert.Equal(0, results.Errors.Count());
Assert.Equal(1, results.RequiredModules.Count);
- Assert.Equal("xSystemSecurity", results.RequiredModules[0]);
+ Assert.Equal(true, results.RequiredModules.ContainsKey("xSystemSecurity"));
}
}
}
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement.Extensions.Test/DSC/DscExtensionSettingsSerializerTests.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement.Extensions.Test/DSC/DscExtensionSettingsSerializerTests.cs
index a3bac6b68c50..2b2887d08830 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement.Extensions.Test/DSC/DscExtensionSettingsSerializerTests.cs
+++ b/src/ServiceManagement/Compute/Commands.ServiceManagement.Extensions.Test/DSC/DscExtensionSettingsSerializerTests.cs
@@ -19,6 +19,7 @@
using System.Management.Automation;
using System.Security;
using Microsoft.WindowsAzure.Commands.ScenarioTest;
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC;
using Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions;
using Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC;
using Xunit;
@@ -28,7 +29,7 @@
namespace Microsoft.WindowsAzure.Commands.ServiceManagement.Extensions.Test.DSC
{
///
- /// Tests for class.
+ /// Tests for class.
///
public class DscExtensionSettingsSerializerTests
{
@@ -42,7 +43,7 @@ public void TestPsCredential()
Hashtable configurationArguments = new Hashtable();
configurationArguments.Add(credentialParameterName, new PSCredential(userName, String2SecureString(password)));
- DscPrivateSettings privateSettings;
+ DscExtensionPrivateSettings privateSettings;
var publicSettings = GetPublicPrivateAfterDeseriazlization(configurationArguments, out privateSettings);
Assert.Equal(1, publicSettings.Properties.Count());
@@ -76,7 +77,7 @@ public void TestString()
Hashtable configurationArguments = new Hashtable();
configurationArguments.Add(arg, value);
- DscPrivateSettings privateSettings;
+ DscExtensionPrivateSettings privateSettings;
var publicSettings = GetPublicPrivateAfterDeseriazlization(configurationArguments, out privateSettings);
Assert.Equal(1, publicSettings.Properties.Count());
@@ -95,7 +96,7 @@ public void TestInt()
Hashtable configurationArguments = new Hashtable();
configurationArguments.Add(arg, value);
- DscPrivateSettings privateSettings;
+ DscExtensionPrivateSettings privateSettings;
var publicSettings = GetPublicPrivateAfterDeseriazlization(configurationArguments, out privateSettings);
Assert.Equal(1, publicSettings.Properties.Count());
@@ -113,7 +114,7 @@ public void TestBool()
const bool value = true;
var configurationArguments = new Hashtable {{arg, true}};
- DscPrivateSettings privateSettings;
+ DscExtensionPrivateSettings privateSettings;
var publicSettings = GetPublicPrivateAfterDeseriazlization(configurationArguments, out privateSettings);
Assert.Equal(1, publicSettings.Properties.Count());
@@ -132,7 +133,7 @@ public void TestChar()
Hashtable configurationArguments = new Hashtable();
configurationArguments.Add(arg, value);
- DscPrivateSettings privateSettings;
+ DscExtensionPrivateSettings privateSettings;
var publicSettings = GetPublicPrivateAfterDeseriazlization(configurationArguments, out privateSettings);
Assert.Equal(1, publicSettings.Properties.Count());
@@ -148,23 +149,23 @@ public void TestChar()
///
///
///
- private static DscPublicSettings GetPublicPrivateAfterDeseriazlization(
+ private static DscExtensionPublicSettings GetPublicPrivateAfterDeseriazlization(
Hashtable configurationArguments,
- out DscPrivateSettings privateSettings)
+ out DscExtensionPrivateSettings privateSettings)
{
- Tuple separatedSettings =
- DscSettingsSerializer.SeparatePrivateItems(configurationArguments);
- DscPublicSettings publicSettings = new DscPublicSettings();
- privateSettings = new DscPrivateSettings();
- publicSettings.Properties = separatedSettings.Item1;
+ Tuple separatedSettings =
+ DscExtensionSettingsSerializer.SeparatePrivateItems(configurationArguments);
+ DscExtensionPublicSettings extensionPublicSettings = new DscExtensionPublicSettings();
+ privateSettings = new DscExtensionPrivateSettings();
+ extensionPublicSettings.Properties = separatedSettings.Item1;
privateSettings.Items = separatedSettings.Item2;
- string serializedPublic = DscSettingsSerializer.SerializePublicSettings(publicSettings);
- string serializedPrivate = DscSettingsSerializer.SerializePrivateSettings(privateSettings);
+ string serializedPublic = DscExtensionSettingsSerializer.SerializePublicSettings(extensionPublicSettings);
+ string serializedPrivate = DscExtensionSettingsSerializer.SerializePrivateSettings(privateSettings);
- publicSettings = DscSettingsSerializer.DeserializePublicSettings(serializedPublic);
+ extensionPublicSettings = DscExtensionSettingsSerializer.DeserializePublicSettings(serializedPublic);
privateSettings = DeserializePrivateSettings(serializedPrivate);
- return publicSettings;
+ return extensionPublicSettings;
}
///
@@ -188,9 +189,9 @@ static private SecureString String2SecureString(string s)
///
///
///
- static private DscPrivateSettings DeserializePrivateSettings(string s)
+ static private DscExtensionPrivateSettings DeserializePrivateSettings(string s)
{
- return JsonConvert.DeserializeObject(s);
+ return JsonConvert.DeserializeObject(s);
}
}
}
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/Commands.ServiceManagement.csproj b/src/ServiceManagement/Compute/Commands.ServiceManagement/Commands.ServiceManagement.csproj
index 0d18b2e995a4..e14a64a3c070 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/Commands.ServiceManagement.csproj
+++ b/src/ServiceManagement/Compute/Commands.ServiceManagement/Commands.ServiceManagement.csproj
@@ -202,19 +202,12 @@
-
-
-
-
-
-
-
-
+
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/ConfigurationParseResult.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/ConfigurationParseResult.cs
deleted file mode 100644
index 5fd3ca25ea5e..000000000000
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/ConfigurationParseResult.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-// ----------------------------------------------------------------------------------
-//
-// 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.Management.Automation.Language;
-
-namespace Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC
-{
- public class ConfigurationParseResult
- {
- public string Path { get; set; }
- public ParseError[] Errors { get; set; }
- public List RequiredModules { get; set; }
-
- }
-}
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/DSCPublicSettings.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/DSCPublicSettings.cs
deleted file mode 100644
index f18875f8d365..000000000000
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/DSCPublicSettings.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-// ----------------------------------------------------------------------------------
-//
-// 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;
-using System.Collections.Generic;
-
-namespace Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions
-{
- ///
- /// Represents public settings. Serialized representation of this object stored as a plain text string on the VM.
- /// Part of the protocol between Set-AzureVMDscExtension cmdlet and DSC Extension handler.
- ///
- public class DscPublicSettings
- {
- ///
- /// Version 1.0.0.0 of DscPublicSettings. We keep it for backward compatability.
- ///
- internal class Version1
- {
- public string SasToken { get; set; }
- public string ModulesUrl { get; set; }
- public string ConfigurationFunction { get; set; }
- public Hashtable Properties { get; set; }
-
- ///
- /// Converting to the current version of DscPublicSettings.
- ///
- ///
- public DscPublicSettings ToCurrentVersion()
- {
- List properties = new List();
- foreach (DictionaryEntry p in this.Properties)
- {
- properties.Add(new Property()
- {
- Name = p.Key.ToString(),
- TypeName = p.Value.GetType().ToString(),
- Value = p.Value
- });
- }
- return new DscPublicSettings()
- {
- SasToken = this.SasToken,
- ModulesUrl = this.ModulesUrl,
- ConfigurationFunction = this.ConfigurationFunction,
- Properties = properties.ToArray(),
- ProtocolVersion = new Version(1, 0, 0, 0)
- };
- }
- }
-
- ///
- /// Defines an entry of DscPublicSettings.Properties array.
- ///
- public class Property
- {
- public string TypeName { get; set; }
- public string Name { get; set; }
- public object Value { get; set; }
- }
-
- ///
- /// SharedAccessSignature token that allows access of azure blob storage files.
- ///
- public string SasToken { get; set; }
-
- ///
- /// Url for archive with modules and configuration in azure blob storage.
- ///
- public string ModulesUrl { get; set; }
-
- ///
- /// String to define configuration in the format: "Module\NameOfConfiguration", where
- /// Module can be a path to the root of the module or .ps1 file or .psm1 file.
- ///
- public string ConfigurationFunction { get; set; }
-
- ///
- /// Configuration parameters
- ///
- public Property[] Properties { get; set; }
-
- ///
- /// Version of the protocol (DscPublicSettings and DscPrivateSettings mostly).
- ///
- public Version ProtocolVersion { get; set; }
- }
-}
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/ServiceManagementBaseCmdletExtentions.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/DscExtensionCmdletCommonBase.cs
similarity index 75%
rename from src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/ServiceManagementBaseCmdletExtentions.cs
rename to src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/DscExtensionCmdletCommonBase.cs
index f2f1c14da975..2b9c1e280a1c 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/ServiceManagementBaseCmdletExtentions.cs
+++ b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/DscExtensionCmdletCommonBase.cs
@@ -15,6 +15,7 @@
using System;
using System.Globalization;
using System.Management.Automation;
+using Microsoft.Azure.Common.Authentication;
using Microsoft.Azure.Common.Authentication.Models;
using Microsoft.WindowsAzure.Commands.Common.Storage;
using Microsoft.WindowsAzure.Commands.ServiceManagement.Properties;
@@ -24,13 +25,16 @@
namespace Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC
{
- static class ServiceManagementBaseCmdletExtentions
+ public static class DscExtensionCmdletCommonBase
{
+ internal const string VirtualMachineDscExtensionCmdletNoun = "AzureVMDscExtension";
+ internal static readonly string DefaultExtensionVersion = "1.*";
+
///
/// Attempts to get the user's credentials from the given Storage Context or the current subscription, if the former is null.
/// Throws a terminating error if the credentials cannot be determined.
///
- public static StorageCredentials GetStorageCredentials(this ServiceManagementBaseCmdlet cmdlet, AzureStorageContext storageContext)
+ internal static StorageCredentials GetStorageCredentials(this AzurePSCmdlet cmdlet, AzureStorageContext storageContext)
{
StorageCredentials credentials = null;
@@ -41,10 +45,13 @@ public static StorageCredentials GetStorageCredentials(this ServiceManagementBas
else
{
var storageAccountName = cmdlet.Profile.Context.Subscription.GetProperty(AzureSubscription.Property.StorageAccount);
-
- if (!string.IsNullOrEmpty(storageAccountName))
+
+ var storageClient = AzureSession.ClientFactory.CreateClient(
+ cmdlet.Profile, cmdlet.Profile.Context.Subscription, AzureEnvironment.Endpoint.ServiceManagement);
+
+ if (!string.IsNullOrEmpty(storageAccountName) && storageClient != null)
{
- var keys = cmdlet.StorageClient.StorageAccounts.GetKeys(storageAccountName);
+ var keys = storageClient.StorageAccounts.GetKeys(storageAccountName);
if (keys != null)
{
@@ -67,13 +74,13 @@ public static StorageCredentials GetStorageCredentials(this ServiceManagementBas
if (string.IsNullOrEmpty(credentials.AccountName))
{
- cmdlet.ThrowInvalidArgumentError(Resources.AzureVMDscStorageContextMustIncludeAccountName);
+ ThrowInvalidArgumentError(cmdlet, Resources.AzureVMDscStorageContextMustIncludeAccountName);
}
return credentials;
}
- public static void ThrowInvalidArgumentError(this ServiceManagementBaseCmdlet cmdlet, string format, params object[] args)
+ internal static void ThrowInvalidArgumentError(this AzurePSCmdlet cmdlet, string format, params object[] args)
{
cmdlet.ThrowTerminatingError(
new ErrorRecord(
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/DscPrivateSettings.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/DscPrivateSettings.cs
deleted file mode 100644
index 6964b255d2e4..000000000000
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/DscPrivateSettings.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// ----------------------------------------------------------------------------------
-//
-// 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.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions
-{
- using System.Collections;
-
- ///
- /// Represents private/protected settings. Serialized representation of this object stored as an encrypted string on the VM.
- /// Part of the protocol between Set-AzureVMDscExtension cmdlet and DSC Extension handler.
- ///
- public class DscPrivateSettings
- {
- ///
- /// Url to the blob storage with ConfigurationData .psd1 file.
- ///
- public string DataBlobUri { get; set; }
-
- ///
- /// This hashtable contains parameters that needs to be encrypted on target VM, like PSCredential.
- /// are not encrypted on target VM.
- ///
- public Hashtable Items { get; set; }
- }
-}
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/DscSettingsSerializer.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/DscSettingsSerializer.cs
deleted file mode 100644
index 9dac6c85073e..000000000000
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/DscSettingsSerializer.cs
+++ /dev/null
@@ -1,182 +0,0 @@
-// ----------------------------------------------------------------------------------
-//
-// 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;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Management.Automation;
-using System.Runtime.InteropServices;
-using System.Security;
-using System.Text;
-using System.Threading.Tasks;
-
-using Newtonsoft.Json;
-
-namespace Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC
-{
- public class DscSettingsSerializer
- {
- ///
- /// Serialize DscPublicSettings to string.
- ///
- ///
- ///
- public static string SerializePublicSettings(DscPublicSettings publicSettings)
- {
- return JsonConvert.SerializeObject(publicSettings);
- }
-
- ///
- /// Serialize DscPrivateSettings to string.
- ///
- ///
- ///
- public static string SerializePrivateSettings(DscPrivateSettings privateSettings)
- {
- return JsonConvert.SerializeObject(privateSettings);
- }
-
- public static DscPublicSettings DeserializePublicSettings(string publicSettingsString)
- {
- DscPublicSettings publicSettings = null;
- try
- {
- publicSettings = string.IsNullOrEmpty(publicSettingsString)
- ? null
- : JsonConvert.DeserializeObject(publicSettingsString);
- }
- catch (JsonException)
- {
- // Try deserialize as version 1.0
- try
- {
- DscPublicSettings.Version1 publicSettingsV1 =
- JsonConvert.DeserializeObject(publicSettingsString);
- publicSettings = publicSettingsV1.ToCurrentVersion();
- }
- catch (JsonException)
- {
- throw;
- }
- }
- return publicSettings;
- }
-
- ///
- /// Convert hashtable of public settings into two parts:
- /// 1) Array of public settings in format:
- /// [
- /// {
- /// "Name": "String Parameter",
- /// "Value": "String Value",
- /// "TypeName": "System.String"
- /// }
- /// ]
- /// 2) Private settings hashtable. We extract all sensitive information (like password from PSCredential)
- /// and store it in private settings. Public settings will reference them in form:
- /// {
- /// "Name": "AdminCredential",
- /// "Value":
- /// {
- /// "Password" : "PrivateSettings:28AC4D36-A99B-41DE-8421-2BCC1C7C1A3B"
- /// "UserName" : "DOMAIN\LOGIN"
- /// },
- /// "TypeName": "System.Management.Automation.PSCredential"
- /// }
- /// and private hashtable will look like that:
- /// {
- /// "28AC4D36-A99B-41DE-8421-2BCC1C7C1A3B" : "password"
- /// }
- ///
- ///
- /// tuple of array (public settings) and hashtable (private settings)
- public static Tuple SeparatePrivateItems(Hashtable arguments)
- {
- var publicSettings = new List();
- var privateSettings = new Hashtable();
- if (arguments != null)
- {
- foreach (DictionaryEntry argument in arguments)
- {
- object entryValue = argument.Value;
- string entryType = argument.Value == null ? "null" : argument.Value.GetType().ToString();
- string entryName = argument.Key.ToString();
- // Special case for PSCredential
- PSCredential credential = argument.Value as PSCredential;
- if (credential == null)
- {
- PSObject psObject = argument.Value as PSObject;
- if (psObject != null)
- {
- credential = psObject.BaseObject as PSCredential;
- }
- }
- if (credential != null)
- {
- // plainTextPassword is a string object with sensitive information in plain text.
- // We pass it to 3rd party serializer which may create copies of the string.
- string plainTextPassword = ConvertToUnsecureString(credential.Password);
- string userName = credential.UserName;
- string passwordRef = Guid.NewGuid().ToString();
- privateSettings.Add(passwordRef, plainTextPassword);
- var newValue = new Hashtable();
- newValue["UserName"] = String.Format(CultureInfo.InvariantCulture, userName);
- newValue["Password"] = String.Format(CultureInfo.InvariantCulture, "PrivateSettingsRef:{0}",
- passwordRef);
- entryValue = newValue;
- entryType = typeof (PSCredential).ToString();
- }
-
- var entry = new DscPublicSettings.Property()
- {
- Name = entryName,
- TypeName = entryType,
- Value = entryValue,
- };
- publicSettings.Add(entry);
- }
- }
- return new Tuple(publicSettings.ToArray(), privateSettings);
- }
-
- ///
- /// Converte SecureString to String.
- ///
- ///
- /// This method creates a managed object with sensitive information and undetermined lifecycle.
- ///
- ///
- ///
- private static string ConvertToUnsecureString(SecureString source)
- {
- if (source == null)
- throw new ArgumentNullException("source");
-
- IntPtr unmanagedString = IntPtr.Zero;
- try
- {
- unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(source);
- return Marshal.PtrToStringUni(unmanagedString);
- }
- finally
- {
- Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
- }
- }
-
- }
-}
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/GetAzureVMDscExtension.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/GetAzureVMDscExtension.cs
index 4dd837b7a06b..60ba1d2ae8c9 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/GetAzureVMDscExtension.cs
+++ b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/GetAzureVMDscExtension.cs
@@ -12,38 +12,50 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC;
+using Microsoft.WindowsAzure.Commands.ServiceManagement.Helpers;
+using Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC;
+using Microsoft.WindowsAzure.Commands.ServiceManagement.Model;
+using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Management.Automation;
-using Microsoft.WindowsAzure.Commands.ServiceManagement.Helpers;
-using Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC;
-using Microsoft.WindowsAzure.Commands.ServiceManagement.Model;
-using Newtonsoft.Json;
namespace Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions
{
///
/// Gets the settings of the DSC extension on a particular VM.
///
- [Cmdlet(VerbsCommon.Get, VirtualMachineDscExtensionCmdletNoun),
+ [Cmdlet(
+ VerbsCommon.Get,
+ DscExtensionCmdletCommonBase.VirtualMachineDscExtensionCmdletNoun),
OutputType(typeof(VirtualMachineDscExtensionContext))]
- public class GetAzureVMDscExtensionCommand : VirtualMachineDscExtensionCmdletBase
+ public class GetAzureVMDscExtensionCommand : VirtualMachineExtensionCmdletBase
{
- internal void ExecuteCommand()
+ protected override void ProcessRecord()
+ {
+ base.ProcessRecord();
+ ExecuteCommand();
+ }
+
+ private void ExecuteCommand()
{
+ extensionName = DscExtensionCmdletConstants.ExtensionPublishedName;
+ publisherName = DscExtensionCmdletConstants.ExtensionPublishedNamespace;
+
List extensionRefs = GetPredicateExtensionList();
WriteObject(
extensionRefs == null ? null : extensionRefs.Select(
r =>
{
GetExtensionValues(r);
- DscPublicSettings publicSettings = null;
+ DscExtensionPublicSettings extensionPublicSettings = null;
try
{
- publicSettings = DscSettingsSerializer.DeserializePublicSettings(PublicConfiguration);
+ extensionPublicSettings = DscExtensionSettingsSerializer.DeserializePublicSettings(PublicConfiguration);
}
catch (JsonException e)
{
@@ -71,7 +83,7 @@ internal void ExecuteCommand()
PrivateConfiguration = SecureStringHelper.GetSecureString(PrivateConfiguration)
};
- if (publicSettings == null)
+ if (extensionPublicSettings == null)
{
context.ModulesUrl = string.Empty;
context.ConfigurationFunction = string.Empty;
@@ -79,9 +91,9 @@ internal void ExecuteCommand()
}
else
{
- context.ModulesUrl = publicSettings.ModulesUrl;
- context.ConfigurationFunction = publicSettings.ConfigurationFunction;
- context.Properties = new Hashtable(publicSettings.Properties.ToDictionary( x => x.Name, x => x.Value ));
+ context.ModulesUrl = extensionPublicSettings.ModulesUrl;
+ context.ConfigurationFunction = extensionPublicSettings.ConfigurationFunction;
+ context.Properties = new Hashtable(extensionPublicSettings.Properties.ToDictionary( x => x.Name, x => x.Value ));
}
return context;
@@ -89,13 +101,6 @@ internal void ExecuteCommand()
).FirstOrDefault());
}
-
- protected override void ProcessRecord()
- {
- base.ProcessRecord();
- ExecuteCommand();
- }
-
}
}
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/GetAzureVMDscExtensionStatus.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/GetAzureVMDscExtensionStatus.cs
index dd2be01b9731..fd84f72fdb3e 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/GetAzureVMDscExtensionStatus.cs
+++ b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/GetAzureVMDscExtensionStatus.cs
@@ -12,16 +12,17 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
+using Hyak.Common;
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC;
+using Microsoft.WindowsAzure.Commands.ServiceManagement.Model;
+using Microsoft.WindowsAzure.Commands.ServiceManagement.Properties;
+using Microsoft.WindowsAzure.Management.Compute;
using System;
-using System.Net;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Management.Automation;
-using Hyak.Common;
-using Microsoft.WindowsAzure.Commands.ServiceManagement.Model;
-using Microsoft.WindowsAzure.Commands.ServiceManagement.Properties;
-using Microsoft.WindowsAzure.Management.Compute;
+using System.Net;
namespace Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions
@@ -209,8 +210,8 @@ internal List GetVirtualMachineDscStatu
internal VirtualMachineDscExtensionStatusContext CreateDscStatusContext(NSM.Role vmRole, NSM.RoleInstance roleInstance)
{
var message = string.Empty;
- var extension = VirtualMachineDscExtensionCmdletBase.ExtensionPublishedNamespace + "."
- + VirtualMachineDscExtensionCmdletBase.ExtensionPublishedName;
+ var extension = DscExtensionCmdletConstants.ExtensionPublishedNamespace + "."
+ + DscExtensionCmdletConstants.ExtensionPublishedName;
NSM.ResourceExtensionConfigurationStatus extensionSettingStatus = null;
if (roleInstance != null && roleInstance.ResourceExtensionStatusList != null)
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/GetDscResourceException.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/GetDscResourceException.cs
deleted file mode 100644
index fd8623d4db58..000000000000
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/GetDscResourceException.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// ----------------------------------------------------------------------------------
-//
-// 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.Globalization;
-using Microsoft.WindowsAzure.Commands.ServiceManagement.Properties;
-
-namespace Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC
-{
- [Serializable]
- public class GetDscResourceException : UnauthorizedAccessException
- {
- public GetDscResourceException(string resourceName, Exception e) :
- base(String.Format(CultureInfo.CurrentUICulture, Resources.PublishVMDscExtensionGetDscResourceFailed, resourceName), e) { }
- }
-}
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/PublishAzureVMDscConfiguration.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/PublishAzureVMDscConfiguration.cs
index 99bc307c3c49..d8605d6f514f 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/PublishAzureVMDscConfiguration.cs
+++ b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/PublishAzureVMDscConfiguration.cs
@@ -12,29 +12,29 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Management.Automation;
+using Microsoft.Azure.Common.Authentication.Models;
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC;
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC.Publish;
using Microsoft.WindowsAzure.Commands.Common.Storage;
-using Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC;
-using Microsoft.WindowsAzure.Commands.ServiceManagement.Properties;
-using Microsoft.WindowsAzure.Commands.Utilities.Common;
-using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
-using Microsoft.WindowsAzure.Storage.Blob;
+using System;
+using System.Management.Automation;
-namespace Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions
+namespace Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC
{
///
/// Uploads a Desired State Configuration script to Azure blob storage, which
/// later can be applied to Azure Virtual Machines using the
/// Set-AzureVMDscExtension cmdlet.
///
- [Cmdlet(VerbsData.Publish, "AzureVMDscConfiguration", SupportsShouldProcess = true, DefaultParameterSetName = UploadArchiveParameterSetName)]
- public class PublishAzureVMDscConfigurationCommand : ServiceManagementBaseCmdlet
+ [Cmdlet(
+ VerbsData.Publish,
+ "AzureVMDscConfiguration",
+ SupportsShouldProcess = true,
+ DefaultParameterSetName = UploadArchiveParameterSetName),
+ OutputType(
+ typeof(String))]
+ public class PublishAzureVMDscConfigurationCommand : DscExtensionPublishCmdletCommonBase
{
private const string CreateArchiveParameterSetName = "CreateArchive";
private const string UploadArchiveParameterSetName = "UploadArchive";
@@ -93,364 +93,72 @@ public class PublishAzureVMDscConfigurationCommand : ServiceManagementBaseCmdlet
[ValidateNotNullOrEmpty]
public string ConfigurationArchivePath { get; set; }
+ ///
+ /// Suffix for the storage end point, e.g. core.windows.net
+ ///
+ [Parameter(
+ ValueFromPipelineByPropertyName = true,
+ ParameterSetName = UploadArchiveParameterSetName,
+ HelpMessage = "Suffix for the storage end point, e.g. core.windows.net")]
+ [ValidateNotNullOrEmpty]
+ public string StorageEndpointSuffix { get; set; }
+
///
/// Credentials used to access Azure Storage
///
private StorageCredentials _storageCredentials;
- private const string Ps1FileExtension = ".ps1";
- private const string Psm1FileExtension = ".psm1";
- private const string ZipFileExtension = ".zip";
- private static readonly HashSet UploadArchiveAllowedFileExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { Ps1FileExtension, Psm1FileExtension, ZipFileExtension };
- private static readonly HashSet CreateArchiveAllowedFileExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { Ps1FileExtension, Psm1FileExtension};
-
- private const int MinMajorPowerShellVersion = 4;
-
- private List _temporaryFilesToDelete = new List();
- private List _temporaryDirectoriesToDelete = new List();
-
protected override void ProcessRecord()
{
try
{
// Create a cloud context, only in case of upload.
- if (this.ParameterSetName == UploadArchiveParameterSetName)
+ if (ParameterSetName == UploadArchiveParameterSetName)
{
base.ProcessRecord();
}
+
ExecuteCommand();
}
finally
{
- foreach (var file in this._temporaryFilesToDelete)
- {
- try
- {
- DeleteFile(file);
- WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Resources.PublishVMDscExtensionDeletedFileMessage, file));
- }
- catch (Exception e)
- {
- WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Resources.PublishVMDscExtensionDeleteErrorMessage, file, e.Message));
- }
- }
- foreach (var directory in this._temporaryDirectoriesToDelete)
- {
- try
- {
- DeleteDirectory(directory);
- WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Resources.PublishVMDscExtensionDeletedFileMessage, directory));
- }
- catch (Exception e)
- {
- WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Resources.PublishVMDscExtensionDeleteErrorMessage, directory, e.Message));
- }
- }
+ DeleteTemporaryFiles();
}
}
- internal void ExecuteCommand()
+ private void ExecuteCommand()
{
+ //check the PS version
ValidatePsVersion();
- ValidateParameters();
- PublishConfiguration();
- }
-
- protected void ValidatePsVersion()
- {
- using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
- {
- powershell.AddScript("$PSVersionTable.PSVersion.Major");
- int major = powershell.Invoke().FirstOrDefault();
- if (major < MinMajorPowerShellVersion)
- {
- this.ThrowTerminatingError(
- new ErrorRecord(
- new InvalidOperationException(
- string.Format(CultureInfo.CurrentUICulture, Resources.PublishVMDscExtensionRequiredPsVersion, MinMajorPowerShellVersion, major)),
- "InvalidPowerShellVersion",
- ErrorCategory.InvalidOperation,
- null));
- }
- }
- }
-
- protected void ValidateParameters()
- {
- this.ConfigurationPath = this.GetUnresolvedProviderPathFromPSPath(this.ConfigurationPath);
- if (!File.Exists(this.ConfigurationPath))
- {
- this.ThrowInvalidArgumentError(Resources.PublishVMDscExtensionUploadArchiveConfigFileNotExist, this.ConfigurationPath);
- }
-
- var configurationFileExtension = Path.GetExtension(this.ConfigurationPath);
- if (this.ParameterSetName == UploadArchiveParameterSetName)
- {
- // Check that ConfigurationPath points to a valid file
- if (!File.Exists(this.ConfigurationPath))
- {
- this.ThrowInvalidArgumentError(Resources.PublishVMDscExtensionConfigFileNotFound, this.ConfigurationPath);
- }
- if (!UploadArchiveAllowedFileExtensions.Contains(Path.GetExtension(configurationFileExtension)))
- {
- this.ThrowInvalidArgumentError(Resources.PublishVMDscExtensionUploadArchiveConfigFileInvalidExtension, this.ConfigurationPath);
- }
-
- this._storageCredentials = this.GetStorageCredentials(this.StorageContext);
-
- if (this.ContainerName == null)
- {
- this.ContainerName = VirtualMachineDscExtensionCmdletBase.DefaultContainerName;
- }
- }
- else if (this.ParameterSetName == CreateArchiveParameterSetName)
- {
- if (!CreateArchiveAllowedFileExtensions.Contains(Path.GetExtension(configurationFileExtension)))
- {
- this.ThrowInvalidArgumentError(Resources.PublishVMDscExtensionCreateArchiveConfigFileInvalidExtension, this.ConfigurationPath);
- }
-
- this.ConfigurationArchivePath = this.GetUnresolvedProviderPathFromPSPath(this.ConfigurationArchivePath);
- }
- }
-
- ///
- /// Publish the configuration and its modules
- ///
- protected void PublishConfiguration()
- {
- if (this.ParameterSetName == CreateArchiveParameterSetName)
- {
- this.ConfirmAction(true, string.Empty, Resources.AzureVMDscCreateArchiveAction, this.ConfigurationArchivePath, ()=> CreateConfigurationArchive());
- }
- else
- {
- var archivePath = string.Compare(Path.GetExtension(this.ConfigurationPath), ZipFileExtension, StringComparison.OrdinalIgnoreCase) == 0 ?
- this.ConfigurationPath
- :
- CreateConfigurationArchive();
-
- UploadConfigurationArchive(archivePath);
- }
- }
-
- private string CreateConfigurationArchive()
- {
- WriteVerbose(String.Format(CultureInfo.CurrentUICulture, Resources.AzureVMDscParsingConfiguration, this.ConfigurationPath));
- ConfigurationParseResult parseResult = null;
- try
- {
- parseResult = ConfigurationParsingHelper.ParseConfiguration(this.ConfigurationPath);
- }
- catch (GetDscResourceException e)
- {
- ThrowTerminatingError(new ErrorRecord(e, "CannotAccessDscResource", ErrorCategory.PermissionDenied, null));
- }
- if (parseResult.Errors.Any())
- {
- ThrowTerminatingError(
- new ErrorRecord(
- new ParseException(
- String.Format(
- CultureInfo.CurrentUICulture,
- Resources.PublishVMDscExtensionStorageParserErrors,
- this.ConfigurationPath,
- String.Join("\n", parseResult.Errors.Select(error => error.ToString())))),
- "DscConfigurationParseError",
- ErrorCategory.ParserError,
- null));
- }
- List requiredModules = parseResult.RequiredModules;
- //Since LCM always uses the latest module there is no need to copy PSDesiredStateConfiguration
- if (requiredModules.Contains("PSDesiredStateConfiguration"))
- {
- requiredModules.Remove("PSDesiredStateConfiguration");
- }
+ //validate cmdlet params
+ ConfigurationPath = GetUnresolvedProviderPathFromPSPath(ConfigurationPath);
- WriteVerbose(String.Format(CultureInfo.CurrentUICulture, Resources.PublishVMDscExtensionRequiredModulesVerbose, String.Join(", ", requiredModules)));
+ ValidateConfigurationPath(ConfigurationPath, ParameterSetName);
- // Create a temporary directory for uploaded zip file
- string tempZipFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
- WriteVerbose(String.Format(CultureInfo.CurrentUICulture, Resources.PublishVMDscExtensionTempFolderVerbose, tempZipFolder));
- Directory.CreateDirectory(tempZipFolder);
- this._temporaryDirectoriesToDelete.Add(tempZipFolder);
-
- // CopyConfiguration
- string configurationName = Path.GetFileName(this.ConfigurationPath);
- string configurationDestination = Path.Combine(tempZipFolder, configurationName);
- WriteVerbose(String.Format(
- CultureInfo.CurrentUICulture,
- Resources.PublishVMDscExtensionCopyFileVerbose,
- this.ConfigurationPath,
- configurationDestination));
- File.Copy(this.ConfigurationPath, configurationDestination);
-
- // CopyRequiredModules
- foreach (var module in requiredModules)
+ switch (ParameterSetName)
{
- using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
- {
- // Wrapping script in a function to prevent script injection via $module variable.
- powershell.AddScript(
- @"function Copy-Module([string]$module, [string]$tempZipFolder)
- {
- $mi = Get-Module -List -Name $module;
- $moduleFolder = Split-Path $mi.Path;
- Copy-Item -Recurse -Path $moduleFolder -Destination ""$tempZipFolder\$($mi.Name)""
- }"
- );
- powershell.Invoke();
- powershell.Commands.Clear();
- powershell.AddCommand("Copy-Module")
- .AddParameter("module", module)
- .AddParameter("tempZipFolder", tempZipFolder);
- WriteVerbose(String.Format(
- CultureInfo.CurrentUICulture,
- Resources.PublishVMDscExtensionCopyModuleVerbose,
- module,
- tempZipFolder));
- powershell.Invoke();
- }
- }
-
- //
- // Zip the directory
- //
- string archive;
-
- if (this.ParameterSetName == CreateArchiveParameterSetName)
- {
- archive = this.ConfigurationArchivePath;
-
- if (!this.Force && System.IO.File.Exists(archive))
- {
- this.ThrowTerminatingError(
- new ErrorRecord(
- new UnauthorizedAccessException(string.Format(CultureInfo.CurrentUICulture, Resources.AzureVMDscArchiveAlreadyExists, archive)),
- "FileAlreadyExists",
- ErrorCategory.PermissionDenied,
- null));
- }
- }
- else
- {
- string tempArchiveFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
- WriteVerbose(String.Format(CultureInfo.CurrentUICulture, Resources.PublishVMDscExtensionTempFolderVerbose, tempArchiveFolder));
- Directory.CreateDirectory(tempArchiveFolder);
- this._temporaryDirectoriesToDelete.Add(tempArchiveFolder);
- archive = Path.Combine(tempArchiveFolder, configurationName + ZipFileExtension);
- this._temporaryFilesToDelete.Add(archive);
- }
-
- if (File.Exists(archive))
- {
- File.Delete(archive);
- }
-
- System.IO.Compression.ZipFile.CreateFromDirectory(tempZipFolder, archive);
- return archive;
- }
-
- private void UploadConfigurationArchive(string archivePath)
- {
- CloudBlobContainer cloudBlobContainer = GetStorageContainer();
-
- var blobName = Path.GetFileName(archivePath);
-
- CloudBlockBlob modulesBlob = cloudBlobContainer.GetBlockBlobReference(blobName);
-
- this.ConfirmAction(true, string.Empty, string.Format(CultureInfo.CurrentUICulture, Resources.AzureVMDscUploadToBlobStorageAction, archivePath), modulesBlob.Uri.AbsoluteUri, () =>
- {
- if (!this.Force && modulesBlob.Exists())
- {
- this.ThrowTerminatingError(
- new ErrorRecord(
- new UnauthorizedAccessException(string.Format(CultureInfo.CurrentUICulture, Resources.AzureVMDscStorageBlobAlreadyExists, modulesBlob.Uri.AbsoluteUri)),
- "StorageBlobAlreadyExists",
- ErrorCategory.PermissionDenied,
- null));
- }
-
- modulesBlob.UploadFromFile(archivePath, FileMode.Open);
-
- WriteVerbose(string.Format(CultureInfo.CurrentUICulture, Resources.PublishVMDscExtensionArchiveUploadedMessage, modulesBlob.Uri.AbsoluteUri));
- });
- }
-
- private CloudBlobContainer GetStorageContainer()
- {
- var storageAccount = new CloudStorageAccount(this._storageCredentials, true);
- var blobClient = storageAccount.CreateCloudBlobClient();
- CloudBlobContainer containerReference = blobClient.GetContainerReference(this.ContainerName);
- containerReference.CreateIfNotExists();
- return containerReference;
- }
-
- private static void DeleteFile(string path)
- {
- try
- {
- File.Delete(path);
- }
- catch (System.UnauthorizedAccessException)
- {
- // the exception may have occurred due to a read-only file
- DeleteReadOnlyFile(path);
- }
- }
-
- ///
- /// Turns off the ReadOnly attribute from the given file and then attemps to delete it
- ///
- private static void DeleteReadOnlyFile(string path)
- {
- var attributes = System.IO.File.GetAttributes(path);
-
- if ((attributes & FileAttributes.ReadOnly) != 0)
- {
- File.SetAttributes(path, attributes & ~FileAttributes.ReadOnly);
- }
-
- File.Delete(path);
- }
-
- private static void DeleteDirectory(string path)
- {
- try
- {
- Directory.Delete(path, true);
- }
- catch (System.UnauthorizedAccessException)
- {
- // the exception may have occurred due to a read-only file or directory
- DeleteReadOnlyDirectory(path);
- }
- }
-
- ///
- /// Recusively turns off the ReadOnly attribute from the given directory and then attemps to delete it
- ///
- private static void DeleteReadOnlyDirectory(string path)
- {
- var directory = new DirectoryInfo(path);
-
- foreach (var child in directory.GetDirectories())
- {
- DeleteReadOnlyDirectory(child.FullName);
- }
-
- foreach (var child in directory.GetFiles())
- {
- DeleteReadOnlyFile(child.FullName);
- }
-
- if ((directory.Attributes & FileAttributes.ReadOnly) != 0)
- {
- directory.Attributes &= ~FileAttributes.ReadOnly;
+ case CreateArchiveParameterSetName:
+ ConfigurationArchivePath = GetUnresolvedProviderPathFromPSPath(ConfigurationArchivePath);
+ break;
+ case UploadArchiveParameterSetName:
+ _storageCredentials = this.GetStorageCredentials(StorageContext);
+ if (ContainerName == null)
+ {
+ ContainerName = DscExtensionCmdletConstants.DefaultContainerName;
+ }
+ if (StorageEndpointSuffix == null)
+ {
+ StorageEndpointSuffix =
+ Profile.Context.Environment.GetEndpoint(AzureEnvironment.Endpoint.StorageEndpointSuffix);
+ }
+ break;
}
- directory.Delete();
+ PublishConfiguration(
+ ConfigurationPath, ConfigurationArchivePath, Force.IsPresent, _storageCredentials, StorageEndpointSuffix, ContainerName, ParameterSetName
+ );
}
}
}
+
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/RemoveAzureVMDscExtension.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/RemoveAzureVMDscExtension.cs
index af8f535d74f1..2d01aae8ba7c 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/RemoveAzureVMDscExtension.cs
+++ b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/RemoveAzureVMDscExtension.cs
@@ -12,8 +12,9 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
-using System.Globalization;
using System.Management.Automation;
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC;
+using Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC;
using Microsoft.WindowsAzure.Commands.ServiceManagement.Model;
using Microsoft.WindowsAzure.Commands.ServiceManagement.Properties;
@@ -30,18 +31,26 @@ namespace Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions
/// $vm = Get-AzureVM -ServiceName service -Name VM-name
/// Remove-AzureVMDscExtension -VM $vm | Update-AzureVM -Verbose
///
- [Cmdlet(VerbsCommon.Remove,
- VirtualMachineDscExtensionCmdletNoun,
+ [Cmdlet(
+ VerbsCommon.Remove,
+ DscExtensionCmdletCommonBase.VirtualMachineDscExtensionCmdletNoun,
SupportsShouldProcess = true),
OutputType(typeof(IPersistentVM))]
- public class RemoveAzureVMDscExtensionCommand : VirtualMachineDscExtensionCmdletBase
+ public class RemoveAzureVMDscExtensionCommand : VirtualMachineExtensionCmdletBase
{
-
- internal void ExecuteCommand()
+ protected override void ProcessRecord()
{
+ base.ProcessRecord();
+ ExecuteCommand();
+ }
+ private void ExecuteCommand()
+ {
+ extensionName = DscExtensionCmdletConstants.ExtensionPublishedName;
+ publisherName = DscExtensionCmdletConstants.ExtensionPublishedNamespace;
+
//this parameter needs to be true for remove to work
- this.Uninstall = true;
- this.Version = DefaultExtensionVersion;
+ Uninstall = true;
+ Version = DscExtensionCmdletCommonBase.DefaultExtensionVersion;
if (ShouldProcess(Resources.DscExtensionRemovalConfirmation,
Resources.DscExtensionRemovalCaption))
@@ -51,12 +60,6 @@ internal void ExecuteCommand()
WriteObject(VM);
}
}
-
- protected override void ProcessRecord()
- {
- base.ProcessRecord();
- ExecuteCommand();
- }
}
}
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/SetAzureVMDscExtension.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/SetAzureVMDscExtension.cs
index cc21d434495d..995d5656011d 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/SetAzureVMDscExtension.cs
+++ b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/SetAzureVMDscExtension.cs
@@ -12,13 +12,8 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Management.Automation;
+using Microsoft.Azure.Common.Authentication.Models;
+using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC;
using Microsoft.WindowsAzure.Commands.Common.Storage;
using Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC;
using Microsoft.WindowsAzure.Commands.ServiceManagement.Model;
@@ -26,13 +21,21 @@
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+using System.Management.Automation;
namespace Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions
{
- [Cmdlet(VerbsCommon.Set, VirtualMachineDscExtensionCmdletNoun, SupportsShouldProcess = true)]
+ [Cmdlet(
+ VerbsCommon.Set,
+ DscExtensionCmdletCommonBase.VirtualMachineDscExtensionCmdletNoun,
+ SupportsShouldProcess = true)]
[OutputType(typeof(IPersistentVM))]
- public class SetAzureVMDscExtension : VirtualMachineDscExtensionCmdletBase
+ public class SetAzureVMDscExtension : VirtualMachineExtensionCmdletBase
{
///
/// The Extension Reference Name
@@ -139,10 +142,9 @@ private class ConfigurationUris
{
public string SasToken { get; set; }
public string DataBlobUri { get; set; }
-
public string ModulesUrl { get; set; }
}
-
+
protected override void ProcessRecord()
{
base.ProcessRecord();
@@ -151,17 +153,20 @@ protected override void ProcessRecord()
internal void ExecuteCommand()
{
+ extensionName = DscExtensionCmdletConstants.ExtensionPublishedName;
+ publisherName = DscExtensionCmdletConstants.ExtensionPublishedNamespace;
+
ValidateParameters();
CreateConfiguration();
- this.ConfirmAction(
+ ConfirmAction(
true,
string.Empty,
string.Format(
CultureInfo.CurrentUICulture,
Resources.AzureVMDscApplyConfigurationAction,
- this.ConfigurationName),
+ ConfigurationName),
"VM",
() =>
{
@@ -179,14 +184,14 @@ protected override void ValidateParameters()
//
// Validate parameters
//
- if (string.IsNullOrEmpty(this.ConfigurationArchive))
+ if (string.IsNullOrEmpty(ConfigurationArchive))
{
- if (this.ConfigurationName != null || this.ConfigurationArgument != null
- || this.ConfigurationDataPath != null)
+ if (ConfigurationName != null || ConfigurationArgument != null
+ || ConfigurationDataPath != null)
{
this.ThrowInvalidArgumentError(Resources.AzureVMDscNullArchiveNoConfiguragionParameters);
}
- if (this.StorageContext != null || this.ContainerName != null || this.StorageEndpointSuffix != null)
+ if (StorageContext != null || ContainerName != null || StorageEndpointSuffix != null)
{
this.ThrowInvalidArgumentError(Resources.AzureVMDscNullArchiveNoStorageParameters);
}
@@ -194,25 +199,25 @@ protected override void ValidateParameters()
else
{
if (string.Compare(
- Path.GetFileName(this.ConfigurationArchive),
- this.ConfigurationArchive,
+ Path.GetFileName(ConfigurationArchive),
+ ConfigurationArchive,
StringComparison.InvariantCultureIgnoreCase) != 0)
{
this.ThrowInvalidArgumentError(Resources.AzureVMDscConfigurationDataFileShouldNotIncludePath);
}
- if (this.ConfigurationDataPath != null)
+ if (ConfigurationDataPath != null)
{
- this.ConfigurationDataPath = this.GetUnresolvedProviderPathFromPSPath(this.ConfigurationDataPath);
+ ConfigurationDataPath = GetUnresolvedProviderPathFromPSPath(ConfigurationDataPath);
- if (!File.Exists(this.ConfigurationDataPath))
+ if (!File.Exists(ConfigurationDataPath))
{
this.ThrowInvalidArgumentError(
Resources.AzureVMDscCannotFindConfigurationDataFile,
- this.ConfigurationDataPath);
+ ConfigurationDataPath);
}
if (string.Compare(
- Path.GetExtension(this.ConfigurationDataPath),
+ Path.GetExtension(ConfigurationDataPath),
".psd1",
StringComparison.InvariantCultureIgnoreCase) != 0)
{
@@ -220,27 +225,33 @@ protected override void ValidateParameters()
}
}
- this._storageCredentials = this.GetStorageCredentials(this.StorageContext);
+ _storageCredentials = this.GetStorageCredentials(StorageContext);
+
+ if (ConfigurationName == null)
+ {
+ ConfigurationName = Path.GetFileNameWithoutExtension(ConfigurationArchive);
+ }
- if (this.ConfigurationName == null)
+ if (ContainerName == null)
{
- this.ConfigurationName = Path.GetFileNameWithoutExtension(this.ConfigurationArchive);
+ ContainerName = DscExtensionCmdletConstants.DefaultContainerName;
}
- if (this.ContainerName == null)
+ if (StorageEndpointSuffix == null)
{
- this.ContainerName = DefaultContainerName;
+ StorageEndpointSuffix =
+ Profile.Context.Environment.GetEndpoint(AzureEnvironment.Endpoint.StorageEndpointSuffix);
}
}
- if (this.Version == null)
+ if (Version == null)
{
- this.Version = DefaultExtensionVersion;
+ Version = DscExtensionCmdletCommonBase.DefaultExtensionVersion;
}
- if (this.ReferenceName == null)
+ if (ReferenceName == null)
{
- this.ReferenceName = ExtensionPublishedName;
+ ReferenceName = DscExtensionCmdletConstants.ExtensionPublishedName;
}
}
@@ -253,28 +264,28 @@ private ConfigurationUris UploadConfigurationDataToBlob()
//
// Get a reference to the container in blob storage
//
- var storageAccount = string.IsNullOrEmpty(this.StorageEndpointSuffix)
- ? new CloudStorageAccount(this._storageCredentials, true)
+ var storageAccount = string.IsNullOrEmpty(StorageEndpointSuffix)
+ ? new CloudStorageAccount(_storageCredentials, true)
: new CloudStorageAccount(
- this._storageCredentials,
- this.StorageEndpointSuffix,
+ _storageCredentials,
+ StorageEndpointSuffix,
true);
var blobClient = storageAccount.CreateCloudBlobClient();
- var containerReference = blobClient.GetContainerReference(this.ContainerName);
+ var containerReference = blobClient.GetContainerReference(ContainerName);
//
// Get a reference to the configuration blob and create a SAS token to access it
//
- var blobAccessPolicy = new SharedAccessBlobPolicy()
+ var blobAccessPolicy = new SharedAccessBlobPolicy
{
SharedAccessExpiryTime =
DateTime.UtcNow.AddHours(1),
Permissions = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Delete
};
- var configurationBlobName = this.ConfigurationArchive;
+ var configurationBlobName = ConfigurationArchive;
var configurationBlobReference = containerReference.GetBlockBlobReference(configurationBlobName);
var configurationBlobSasToken = configurationBlobReference.GetSharedAccessSignature(blobAccessPolicy);
@@ -283,33 +294,33 @@ private ConfigurationUris UploadConfigurationDataToBlob()
//
string configurationDataBlobUri = null;
- if (this.ConfigurationDataPath != null)
+ if (ConfigurationDataPath != null)
{
- this.ConfirmAction(
+ var guid = Guid.NewGuid();
+ // there may be multiple VMs using the same configuration
+
+ var configurationDataBlobName = string.Format(
+ CultureInfo.InvariantCulture,
+ "{0}-{1}.psd1",
+ ConfigurationName,
+ guid);
+
+ var configurationDataBlobReference =
+ containerReference.GetBlockBlobReference(configurationDataBlobName);
+
+ ConfirmAction(
true,
string.Empty,
string.Format(
CultureInfo.CurrentUICulture,
Resources.AzureVMDscUploadToBlobStorageAction,
- this.ConfigurationDataPath),
- configurationBlobReference.Uri.AbsoluteUri,
+ ConfigurationDataPath),
+ configurationDataBlobReference.Uri.AbsoluteUri,
() =>
{
- var guid = Guid.NewGuid();
- // there may be multiple VMs using the same configuration
-
- var configurationDataBlobName = string.Format(
- CultureInfo.InvariantCulture,
- "{0}-{1}.psd1",
- this.ConfigurationName,
- guid);
-
- var configurationDataBlobReference =
- containerReference.GetBlockBlobReference(configurationDataBlobName);
-
- if (!this.Force && configurationDataBlobReference.Exists())
+ if (!Force && configurationDataBlobReference.Exists())
{
- this.ThrowTerminatingError(
+ ThrowTerminatingError(
new ErrorRecord(
new UnauthorizedAccessException(
string.Format(
@@ -321,7 +332,7 @@ private ConfigurationUris UploadConfigurationDataToBlob()
null));
}
- configurationDataBlobReference.UploadFromFile(this.ConfigurationDataPath, FileMode.Open);
+ configurationDataBlobReference.UploadFromFile(ConfigurationDataPath, FileMode.Open);
var configurationDataBlobSasToken =
configurationDataBlobReference.GetSharedAccessSignature(blobAccessPolicy);
@@ -331,7 +342,7 @@ private ConfigurationUris UploadConfigurationDataToBlob()
+ configurationDataBlobSasToken;
});
}
- return new ConfigurationUris()
+ return new ConfigurationUris
{
ModulesUrl = configurationBlobReference.StorageUri.PrimaryUri.AbsoluteUri,
SasToken = configurationBlobSasToken,
@@ -341,11 +352,10 @@ private ConfigurationUris UploadConfigurationDataToBlob()
private void CreateConfiguration()
{
- var publicSettings = new DscPublicSettings();
- var privateSettings = new DscPrivateSettings();
- publicSettings.ProtocolVersion = CurrentProtocolVersion;
-
- if (!string.IsNullOrEmpty(this.ConfigurationArchive))
+ var publicSettings = new DscExtensionPublicSettings();
+ var privateSettings = new DscExtensionPrivateSettings();
+
+ if (!string.IsNullOrEmpty(ConfigurationArchive))
{
ConfigurationUris configurationUris = UploadConfigurationDataToBlob();
@@ -354,10 +364,10 @@ private void CreateConfiguration()
publicSettings.ConfigurationFunction = string.Format(
CultureInfo.InvariantCulture,
"{0}\\{1}",
- Path.GetFileNameWithoutExtension(this.ConfigurationArchive),
- this.ConfigurationName);
- Tuple settings =
- DscSettingsSerializer.SeparatePrivateItems(this.ConfigurationArgument);
+ Path.GetFileNameWithoutExtension(ConfigurationArchive),
+ ConfigurationName);
+ Tuple settings =
+ DscExtensionSettingsSerializer.SeparatePrivateItems(ConfigurationArgument);
publicSettings.Properties = settings.Item1;
privateSettings.Items = settings.Item2;
@@ -367,11 +377,12 @@ private void CreateConfiguration()
//
// Define the public and private property bags that will be passed to the extension.
//
- this.PublicConfiguration = DscSettingsSerializer.SerializePublicSettings(publicSettings);
+ PublicConfiguration = DscExtensionSettingsSerializer.SerializePublicSettings(publicSettings);
//
// PrivateConfuguration contains sensitive data in a plain text.
//
- this.PrivateConfiguration = DscSettingsSerializer.SerializePrivateSettings(privateSettings);
+ PrivateConfiguration = DscExtensionSettingsSerializer.SerializePrivateSettings(privateSettings);
}
}
}
+
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/VirtualMachineDscExtensionCmdletBase.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/VirtualMachineDscExtensionCmdletBase.cs
deleted file mode 100644
index b1e1ffdb3789..000000000000
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/IaaS/Extensions/DSC/VirtualMachineDscExtensionCmdletBase.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// ----------------------------------------------------------------------------------
-//
-// 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.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions
-{
- public class VirtualMachineDscExtensionCmdletBase : VirtualMachineExtensionCmdletBase
- {
- internal static readonly string ExtensionPublishedNamespace = "Microsoft.Powershell";
- internal static readonly string ExtensionPublishedName = "DSC";
- internal static readonly string DefaultContainerName = "windows-powershell-dsc";
- internal static readonly string DefaultExtensionVersion = "1.*";
- internal static readonly Version CurrentProtocolVersion = new Version(2, 0, 0, 0);
-
- protected const string VirtualMachineDscExtensionCmdletNoun = "AzureVMDscExtension";
-
- public VirtualMachineDscExtensionCmdletBase()
- {
- this.extensionName = ExtensionPublishedName;
- this.publisherName = ExtensionPublishedNamespace;
- }
- }
-}
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/Microsoft.WindowsAzure.Commands.ServiceManagement.dll-Help.xml b/src/ServiceManagement/Compute/Commands.ServiceManagement/Microsoft.WindowsAzure.Commands.ServiceManagement.dll-Help.xml
index 96a2e9516e98..006160cbe9e9 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/Microsoft.WindowsAzure.Commands.ServiceManagement.dll-Help.xml
+++ b/src/ServiceManagement/Compute/Commands.ServiceManagement/Microsoft.WindowsAzure.Commands.ServiceManagement.dll-Help.xml
@@ -17456,6 +17456,13 @@ New-AzureDeployment -ServiceName $cloudSvc -Slot Production -Package $pkg -Confi
AzureStorageContext
+
+ StorageEndpointSuffix
+
+ Suffix for the storage end point, e.g. core.windows.net
+
+ string
+
WhatIf
@@ -17560,6 +17567,18 @@ When this parameter is used, the configuration script is not uploaded to Azure b
+
+ StorageEndpointSuffix
+
+ Suffix for the storage end point, e.g. core.windows.net
+
+ string
+
+ string
+
+
+
+
WhatIf
@@ -35928,4 +35947,4 @@ Get-AzureVM -ServiceName service -Name vmName| Set-AzureVMSqlServerExtension -D
-
\ No newline at end of file
+
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/Properties/Resources.Designer.cs b/src/ServiceManagement/Compute/Commands.ServiceManagement/Properties/Resources.Designer.cs
index 0d0566b13cfb..fef58e13e34c 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/Properties/Resources.Designer.cs
+++ b/src/ServiceManagement/Compute/Commands.ServiceManagement/Properties/Resources.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
-// Runtime Version:4.0.30319.34014
+// Runtime Version:4.0.30319.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -159,15 +159,6 @@ public static string AzureVMDscApplyConfigurationAction {
}
}
- ///
- /// Looks up a localized string similar to File '{0}' already exists. Use the -Force parameter to overwrite it..
- ///
- public static string AzureVMDscArchiveAlreadyExists {
- get {
- return ResourceManager.GetString("AzureVMDscArchiveAlreadyExists", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Cannot find configuration data file: {0}.
///
@@ -186,15 +177,6 @@ public static string AzureVMDscConfigurationDataFileShouldNotIncludePath {
}
}
- ///
- /// Looks up a localized string similar to Create Archive.
- ///
- public static string AzureVMDscCreateArchiveAction {
- get {
- return ResourceManager.GetString("AzureVMDscCreateArchiveAction", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Can not find your azure storage credential. Please specify an storage context using the -StorageContext parameter, or set the current storage account using "Set-AzureSubscription", or set the "AZURE_STORAGE_CONNECTION_STRING" environment variable..
///
@@ -231,15 +213,6 @@ public static string AzureVMDscNullArchiveNoStorageParameters {
}
}
- ///
- /// Looks up a localized string similar to Parsing configuration script: {0}.
- ///
- public static string AzureVMDscParsingConfiguration {
- get {
- return ResourceManager.GetString("AzureVMDscParsingConfiguration", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Storage Blob '{0}' already exists. Use the -Force parameter to overwrite it..
///
@@ -1321,15 +1294,6 @@ public static string PublicIPNotFoundInNetworkConfiguration {
}
}
- ///
- /// Looks up a localized string similar to Configuration published to {0}.
- ///
- public static string PublishVMDscExtensionArchiveUploadedMessage {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionArchiveUploadedMessage", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Cannot find configuration file: {0}..
///
@@ -1339,34 +1303,6 @@ public static string PublishVMDscExtensionConfigFileNotFound {
}
}
- ///
- /// Looks up a localized string similar to Copy '{0}' to '{1}'..
- ///
- public static string PublishVMDscExtensionCopyFileVerbose {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionCopyFileVerbose", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Copy the module '{0}' to '{1}'..
- ///
- public static string PublishVMDscExtensionCopyModuleVerbose {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionCopyModuleVerbose", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Invalid configuration file: {0}.
- ///The file needs to be a PowerShell script (.ps1 or .psm1)..
- ///
- public static string PublishVMDscExtensionCreateArchiveConfigFileInvalidExtension {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionCreateArchiveConfigFileInvalidExtension", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Create a zip file '{0}' from directory '{1}'..
///
@@ -1376,93 +1312,6 @@ public static string PublishVMDscExtensionCreateZipVerbose {
}
}
- ///
- /// Looks up a localized string similar to Deleted '{0}'.
- ///
- public static string PublishVMDscExtensionDeletedFileMessage {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionDeletedFileMessage", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Cannot delete '{0}': {1}.
- ///
- public static string PublishVMDscExtensionDeleteErrorMessage {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionDeleteErrorMessage", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Cannot get module for DscResource '{0}'. Possible solutions:
- ///1) Specify -ModuleName for Import-DscResource in your configuration.
- ///2) Unblock module that contains resource.
- ///3) Move Import-DscResource inside Node block.
- ///.
- ///
- public static string PublishVMDscExtensionGetDscResourceFailed {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionGetDscResourceFailed", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to List of required modules: [{0}]..
- ///
- public static string PublishVMDscExtensionRequiredModulesVerbose {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionRequiredModulesVerbose", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Your current PowerShell version {1} is less then required by this cmdlet {0}. Consider download and install latest PowerShell version..
- ///
- public static string PublishVMDscExtensionRequiredPsVersion {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionRequiredPsVersion", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Configuration script '{0}' contained parse errors:
- ///{1}.
- ///
- public static string PublishVMDscExtensionStorageParserErrors {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionStorageParserErrors", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Temp folder '{0}' created..
- ///
- public static string PublishVMDscExtensionTempFolderVerbose {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionTempFolderVerbose", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Invalid configuration file: {0}.
- ///The file needs to be a PowerShell script (.ps1 or .psm1) or a ZIP archive (.zip)..
- ///
- public static string PublishVMDscExtensionUploadArchiveConfigFileInvalidExtension {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionUploadArchiveConfigFileInvalidExtension", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Configuration file '{0}' not found..
- ///
- public static string PublishVMDscExtensionUploadArchiveConfigFileNotExist {
- get {
- return ResourceManager.GetString("PublishVMDscExtensionUploadArchiveConfigFileNotExist", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Begin Operation: {0}.
///
diff --git a/src/ServiceManagement/Compute/Commands.ServiceManagement/Properties/Resources.resx b/src/ServiceManagement/Compute/Commands.ServiceManagement/Properties/Resources.resx
index 0c7541325275..143942ad19e9 100644
--- a/src/ServiceManagement/Compute/Commands.ServiceManagement/Properties/Resources.resx
+++ b/src/ServiceManagement/Compute/Commands.ServiceManagement/Properties/Resources.resx
@@ -649,28 +649,6 @@
Cannot find configuration file: {0}.
0 = path to the configuration file
-
- Invalid configuration file: {0}.
-The file needs to be a PowerShell script (.ps1 or .psm1) or a ZIP archive (.zip).
- 0 = path to the configuration file
-
-
- Configuration file '{0}' not found.
- 0 = path to the configuration file
-
-
- Cannot get module for DscResource '{0}'. Possible solutions:
-1) Specify -ModuleName for Import-DscResource in your configuration.
-2) Unblock module that contains resource.
-3) Move Import-DscResource inside Node block.
-
- 0 = name of DscResource
-
-
- Configuration script '{0}' contained parse errors:
-{1}
- 0 = path to the configuration script, 1 = parser errors
-
The storage context must include an storage account.
@@ -696,71 +674,19 @@ The file needs to be a PowerShell script (.ps1 or .psm1) or a ZIP archive (.zip)
Apply configuration '{0}'
{0} is the name of a PowerShell DSC Configuration function
-
- Parsing configuration script: {0}
- {0} is the path to a script file
-
-
- Storage Blob '{0}' already exists. Use the -Force parameter to overwrite it.
- {0} is the name of an storage blob
-
-
- Upload '{0}'
- {0} is the name of an storage blob
-
-
- Invalid configuration file: {0}.
-The file needs to be a PowerShell script (.ps1 or .psm1).
- 0 = path to the configuration file
-
If the ConfigurationArchive argument is null, then the ConfigurationName, ConfigurationArgument, and ConfigurationDataPath parameters must not be specified
If the ConfigurationArchive argument is null, then the StorageContext, ContainerName, and StorageEndpointSuffix parameters must not be specified
-
- Create Archive
-
-
- File '{0}' already exists. Use the -Force parameter to overwrite it.
- {0} is the path to a file
-
-
- Your current PowerShell version {1} is less then required by this cmdlet {0}. Consider download and install latest PowerShell version.
- {0} = minimal required PS version, {1} = current PS version
-
Can not find your azure storage credential. Please specify an storage context using the -StorageContext parameter, or set the current storage account using "Set-AzureSubscription", or set the "AZURE_STORAGE_CONNECTION_STRING" environment variable.
-
- List of required modules: [{0}].
- {0} = list of modules
-
-
- Temp folder '{0}' created.
- {0} = temp folder path
-
-
- Copy '{0}' to '{1}'.
- {0} = source, {1} = destination
-
-
- Copy the module '{0}' to '{1}'.
- {0} = source, {1} = destination
-
Create a zip file '{0}' from directory '{1}'.
{0} = target zip, {1} = source
-
- Deleted '{0}'
- {0} is the path of a file
-
-
- Cannot delete '{0}': {1}
- {0} is the path of a file, {1} is an error message
-
Cannot deserialize settings string from DSC extension. Updating your Azure PowerShell SDK to the latest version may solve this problem. Settings string:
{0}
@@ -777,10 +703,6 @@ The file needs to be a PowerShell script (.ps1 or .psm1).
"An exception occurred when calling the ServiceManagement API. HTTP Status Code: {0}. Service Management Error Code: {1}. Message: {2}. Operation Tracking ID: {3}."
{0} is the HTTP status code. {1} is the Service Management Error Code. {2} is the Service Management Error message. {3} is the operation tracking ID.
-
- Configuration published to {0}
- {0} is an URI
-
Subnet should be specified when deploying VMs in a VNET: {0}
@@ -823,4 +745,12 @@ The file needs to be a PowerShell script (.ps1 or .psm1).
{0}
+
+ Storage Blob '{0}' already exists. Use the -Force parameter to overwrite it.
+ {0} is the name of an storage blob
+
+
+ Upload '{0}'
+ {0} is the name of an storage blob
+
\ No newline at end of file