LCM/dsc/engine/ca/psinfrastructure/PsPluginManager.cs (1,414 lines of code) (raw):
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Native;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System;
using System.Security;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
// ReSharper disable RedundantUsingDirective
using System.IO.Compression;
// ReSharper restore RedundantUsingDirective
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using Microsoft.PowerShell.Commands;
namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal
{
/// <summary>
/// DownloadManagerBase handles the interaction between CA and the Powershell download managers.
/// </summary>
public static class DownloadManagerBase
{
#region PLUGIN_CONSTANTS
// Cmdlets
private const string DownloadManagerGetConfiguration = "Get-DscDocument";
private const string DownloadManagerGetModules = "Get-DscModule";
private const string DownloadManagerGetAction = "Get-DscAction";
private const string WebDownloadManagerName = "WebDownloadManager";
private const string WebDownloadManagerLoadPath = "PSDesiredStateConfiguration\\WebDownloadManager";
private const string FileDownloadManagerName = "DSCFileDownloadManager";
private const string FileDownloadManagerLoadPath = "PSDesiredStateConfiguration\\DownloadManager\\DSCFileDownloadManager";
private static readonly Dictionary<string, string> DownloadManagerMap;
// PS DSC module cmdlets
private const string PsDscCmdletGet = "Get-TargetResource";
private const string PsDscCmdletSet = "Set-TargetResource";
private const string PsDscCmdletTest = "Test-TargetResource";
private const string BaseResourceClass = "OMI_BaseResource";
// Parameters
private const string ParamCredential = "Credential";
private const string ParamCredentialUserName = "UserName";
private const string ParamCredentialPassword = "Password";
private const string ParamCredentialDomain = "Domain";
private const string ParamConfigurationId = "ConfigurationID";
private const string ParamDestinationPath = "DestinationPath";
private const string ParamModules = "Module";
private const string ParamFileHash = "FileHash";
private const string ParamStatusCode = "StatusCode";
private const string ParamNotCompliant = "NotCompliant";
//Meta Config
private const string MetaConfigConfigurationId = "ConfigurationID";
private const string UseSystemUUIDValue = "UseSystemUUID";
private const string MetaConfigDownloadManagerName = "DownloadManagerName";
private const string MetaConfigDownloadManagerCustomData = "DownloadManagerCustomData"; //This is array of MSFT_KeyValuePair.
//static readonly string MetaConfig_Credential = "Credential";
private const string MetaConfigAllowModuleOverwrite = "AllowModuleOverwrite";
// Infrastructure defaults
private const int SchemaValidationOption = 4; //Ignore
//static readonly string BASE_RESOURCE_CLASSNAME = "OMI_BaseResource";
private const string BaseDocumentClassname = "OMI_ConfigurationDocument";
static readonly string[] NativeModules = { "MSFT_FileDirectoryConfiguration" };
// status result
private const string StatusOk = "OK";
private const string StatusGetMof = "GETCONFIGURATION";
private const string StatusRetry = "RETRY";
private const string WriteQualifier = "write";
#endregion PLUGIN_CONSTANTS
#region PLUGIN_STATE
//This is initialized only once and cached until process is unloaded.
// ReSharper disable FieldCanBeMadeReadOnly.Local
static private Runspace _pluginRunspace;
static private Runspace _validationRunspace;
// ReSharper restore FieldCanBeMadeReadOnly.Local
static private readonly bool PluginRunspaceInitialized;
static private string _pluginModuleName;
/// <summary>
/// Enumeration for Get Action Status Code
/// </summary>
internal enum GetActionStatusCodeTypes
{
Success = 0,
DownloadManagerInitializationFailure = 1,
GetConfigurationCommandFailure = 2,
UnexpectedGetConfigurationResponseFromServer = 3,
ConfigurationChecksumFileReadFailure = 4,
ConfigurationChecksumValidationFailure = 5,
InvalidConfigurationFile = 6,
AvailableModulesCheckFailure = 7,
InvalidConfigurationIdInMetaConfig = 8,
InvalidDownloadManagerCustomDataInMetaConfig = 9,
GetDscModuleCommandFailure = 10,
GetDscModuleInvalidOutput = 11,
ModuleChecksumFileNotFound = 12,
InvalidModuleFile = 13,
ModuleChecksumValidationFailure = 14,
ModuleExtractionFailure = 15,
ModuleValidationFailure = 16,
InvalidDownloadedModule = 17,
ConfigurationFileNotFound = 18,
MultipleConfigurationFilesFound = 19,
ConfigurationChecksumFileNotFound = 20,
ModuleNotFound = 21,
InvalidModuleVersionFormat = 22,
InvalidConfigurationIdFormat = 23,
GetDscActionCommandFailure = 24,
InvalidChecksumAlgorithm = 25,
};
#endregion PLUGIN_STATE
#region PLUGIN_CONSTRUCTORS
static DownloadManagerBase()
{
DownloadManagerMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
DownloadManagerMap.Add(WebDownloadManagerName, WebDownloadManagerLoadPath);
DownloadManagerMap.Add(FileDownloadManagerName, FileDownloadManagerLoadPath);
_pluginRunspace = RunspaceFactory.CreateRunspace(InitialSessionState.CreateDefault2());
_pluginRunspace.Open();
_validationRunspace = RunspaceFactory.CreateRunspace(InitialSessionState.CreateDefault2());
_validationRunspace.Open();
PluginRunspaceInitialized = false;
}
#endregion PLUGIN_CONSTRUCTORS
#region PLUGIN_GETCONFIGURATION
/*If this succeeds, the first object in the collection is a valid object.*/
private static MiResult ValidateGetConfigurationResult(System.Management.Automation.PowerShell powershell, Collection<PSObject> providerImportResult,
string downloadManagerName, out string importModuleResult, out ErrorRecord errorRecord,
out UInt32 getActionStatusCode)
{
var result = ValidateGetResult(powershell, providerImportResult, downloadManagerName, out importModuleResult,
out errorRecord, out getActionStatusCode);
if (result != MiResult.OK)
{
return result;
}
if (!(string.Compare(importModuleResult, StatusOk, StringComparison.OrdinalIgnoreCase) == 0 ||
string.Compare(importModuleResult, StatusRetry, StringComparison.OrdinalIgnoreCase) == 0))
{
errorRecord = GetErrorRecord("PsPluginManagerGetConfUnexpectedResult", ErrorCategory.InvalidResult,
PSinfrastructureStrings.PsPluginManagerGetConfUnexpectedResult, importModuleResult, _pluginModuleName);
getActionStatusCode = (int)GetActionStatusCodeTypes.UnexpectedGetConfigurationResponseFromServer;
return MiResult.FAILED;
}
return result;
}
/*If this succeeds, the first object in the collection is a valid object.*/
private static MiResult ValidateGetResult(System.Management.Automation.PowerShell powershell, Collection<PSObject> providerImportResult,
string downloadManagerName, out string importModuleResult, out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
importModuleResult = null;
HandleNonTerminatingErrors(powershell, downloadManagerName, DownloadManagerGetConfiguration, out errorRecord);
if (errorRecord != null)
{
getActionStatusCode = (int) GetActionStatusCodeTypes.GetConfigurationCommandFailure;
return MiResult.FAILED;
}
if (providerImportResult == null || providerImportResult.Count != 1)
{
errorRecord = GetErrorRecord("GetConfigurationResultNotExpected", ErrorCategory.ObjectNotFound,
PSinfrastructureStrings.GetConfigurationResultCountUnexpected, downloadManagerName);
getActionStatusCode = (int)GetActionStatusCodeTypes.GetConfigurationCommandFailure;
return MiResult.NOT_FOUND;
}
try
{
importModuleResult = LanguagePrimitives.ConvertTo<string>(providerImportResult[0]);
}
catch (PSInvalidCastException ex)
{
errorRecord = GetErrorRecord("GetConfigurationResultNotExpected", ex, ErrorCategory.InvalidType,
PSinfrastructureStrings.GetConfigurationResultCountUnexpected, downloadManagerName);
getActionStatusCode = (int)GetActionStatusCodeTypes.GetConfigurationCommandFailure;
return MiResult.NOT_FOUND;
}
if (importModuleResult == null || string.IsNullOrEmpty(importModuleResult))
{
errorRecord = GetErrorRecord("GetConfigurationResultNotExpected", ErrorCategory.ObjectNotFound,
PSinfrastructureStrings.GetConfigurationResultCountUnexpected, downloadManagerName);
getActionStatusCode = (int)GetActionStatusCodeTypes.GetConfigurationCommandFailure;
return MiResult.NOT_FOUND;
}
getActionStatusCode = (int)GetActionStatusCodeTypes.Success;
return MiResult.OK;
}
/*If this succeeds, the first object in the collection is a valid object.*/
private static MiResult ValidateGetActionResult(System.Management.Automation.PowerShell powershell, Collection<PSObject> providerImportResult,
string downloadManagerName, out string importModuleResult, out ErrorRecord errorRecord,
out UInt32 getActionStatusCode)
{
var result = ValidateGetResult(powershell, providerImportResult, downloadManagerName, out importModuleResult,
out errorRecord, out getActionStatusCode);
if (result != MiResult.OK)
{
return result;
}
if (!(string.Compare(importModuleResult, StatusOk, StringComparison.OrdinalIgnoreCase) == 0 ||
string.Compare(importModuleResult, StatusGetMof, StringComparison.OrdinalIgnoreCase) == 0))
{
errorRecord = GetErrorRecord("PluginGetActionUnsuccessful", ErrorCategory.InvalidResult,
PSinfrastructureStrings.PluginGetActionUnsuccessful, importModuleResult, _pluginModuleName);
return MiResult.FAILED;
}
return result;
}
#endregion PLUGIN_GETCONFIGURATION
#region PLUGIN_GETMODULE
/*If this succeeds, the first object in the collection is a valid object.*/
private static MiResult ValidateModuleForVersion(System.Management.Automation.PowerShell powershell,
IEnumerable<PSModuleInfo> availableModules,
string downloadManagerName,
string moduleVersion,
out bool isModuleAvailable,
out ErrorRecord errorRecord)
{
isModuleAvailable = false;
HandleNonTerminatingErrors(powershell, downloadManagerName, "Import-Module", out errorRecord);
if (errorRecord != null)
{
return MiResult.FAILED;
}
if (availableModules == null)
{
return MiResult.OK; // cached module not available.
}
// ReSharper disable LoopCanBeConvertedToQuery
foreach (PSModuleInfo localModule in availableModules)
// ReSharper restore LoopCanBeConvertedToQuery
{
if (string.IsNullOrEmpty(moduleVersion) ||
string.Compare(moduleVersion, localModule.Version.ToString(), StringComparison.OrdinalIgnoreCase) == 0)
{
// Module is a default module available in system cache.
S_DscCoreR.EventWriteLCMPullModuleSkippedAsModuleIsAvailable(S_DscCoreR.JobGuidString, localModule.Name, localModule.Version.ToString(), localModule.Path);
isModuleAvailable = true;
return MiResult.OK;
}
}
return MiResult.OK;
}
private static ModuleSpecification[] GetModuleSpecification(Dictionary<string, Tuple<string, List<string>>> moduleVersionTable)
{
List<ModuleSpecification> moduleSpecifications = new List<ModuleSpecification>();
foreach (var entry in moduleVersionTable)
{
if (string.IsNullOrEmpty(entry.Value.Item1))
{
moduleSpecifications.Add(new ModuleSpecification(entry.Key));
}
else
{
Hashtable h = new Hashtable(StringComparer.OrdinalIgnoreCase);
h.Add("ModuleName", entry.Key);
h.Add("ModuleVersion", entry.Value.Item1);
moduleSpecifications.Add(new ModuleSpecification(h));
}
}
return moduleSpecifications.ToArray();
}
private static MiResult GetGetModuleParams(IntPtr metaConfigHandle, out PSCredential pscredential,
Hashtable arguments, out string configurationId,
out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
//Get info from MetaConfig
MiResult statusCode = GetMetaConfigParams(metaConfigHandle, out pscredential, arguments, out configurationId, out errorRecord, out getActionStatusCode);
return statusCode;
}
private static MiResult GetModuleNameVersionTable(string mofFileLocation, Dictionary<string, Tuple<string, List<string>>> moduleNameTable, out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
Debug.Assert(moduleNameTable != null, "moduleNameTable can not be null");
//Load the mof file.
errorRecord = null;
List<CimInstance> cimInstances;
try
{
CimClassCache.InitializeInfraStructureMof();
cimInstances = CimClassCache.ImportInstances(mofFileLocation, SchemaValidationOption);
}
catch (CimException exception)
{
errorRecord = GetErrorRecord("ConfigurationFileInvalid", ErrorCategory.InvalidResult,
PSinfrastructureStrings.ConfigurationFileInvalid, _pluginModuleName);
getActionStatusCode = (int)GetActionStatusCodeTypes.InvalidConfigurationFile;
return (MiResult)exception.StatusCode;
}
foreach (var inst in cimInstances)
{
string moduleName = inst.CimSystemProperties.ClassName;
string moduleVersion = "";
string providerClassName = inst.CimSystemProperties.ClassName;
// check for resource class and get module Name and Version
if (string.Compare(inst.CimSystemProperties.ClassName, BaseDocumentClassname, StringComparison.OrdinalIgnoreCase) != 0)
{
if (inst.CimInstanceProperties["ModuleName"] != null && inst.CimInstanceProperties["ModuleName"].Value != null)
{
moduleName = inst.CimInstanceProperties["ModuleName"].Value.ToString();
}
if (inst.CimInstanceProperties["ModuleVersion"] != null && inst.CimInstanceProperties["ModuleVersion"].Value != null)
{
moduleVersion = inst.CimInstanceProperties["ModuleVersion"].Value.ToString();
}
if (!string.IsNullOrEmpty(moduleName) && (string.IsNullOrEmpty(moduleVersion) || IsModuleVersionValidFormat(moduleVersion)))
{
Tuple<string, List<string>> moduleEntry;
if (moduleNameTable.ContainsKey(moduleName))
{
moduleEntry = moduleNameTable[moduleName];
if (!moduleEntry.Item2.Contains(providerClassName))
{
moduleEntry.Item2.Add(providerClassName);
}
}
else
{
moduleEntry = new Tuple<string, List<string>>(moduleVersion, new List<string>());
moduleEntry.Item2.Add(providerClassName);
moduleNameTable[moduleName] = moduleEntry;
}
}
else
{
getActionStatusCode = (int) GetActionStatusCodeTypes.InvalidModuleVersionFormat;
S_DscCoreR.EventWriteLCMPullModuleInvalidVersionFormat(S_DscCoreR.JobGuidString, moduleName, moduleVersion);
errorRecord = GetErrorRecord("InvalidModuleVersionFormat", null, ErrorCategory.InvalidArgument, PSinfrastructureStrings.InvalidModuleVersionFormat, moduleVersion, moduleName);
return MiResult.INVALID_PARAMETER;
}
}
}
// check with cached modules and remove them from the list.
return FilterUsingCachedModules(moduleNameTable, out errorRecord, out getActionStatusCode);
}
private static bool IsModuleVersionValidFormat(string moduleVersion)
{
Version throwAwayVersion;
return Version.TryParse(moduleVersion, out throwAwayVersion);
}
private static MiResult FilterUsingCachedModules(Dictionary<string, Tuple<string, List<string>>> moduleNameTable, out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
errorRecord = null;
// if it is known Native Module, remove it from the list.
foreach (string nativeModule in NativeModules)
{
if (moduleNameTable.ContainsKey(nativeModule))
{
//remove it
moduleNameTable.Remove(nativeModule);
}
}
// Find if modules are present locally
var cachedModules = new List<string>();
foreach (var entry in moduleNameTable)
{
// Find if modules are present as internal DSC module
var corePsProvidersModuleRelativePath = Path.Combine(Constants.CorePsProvidersRelativePath, entry.Key);
var corePsProvidersModulePath = Path.Combine(Environment.SystemDirectory, corePsProvidersModuleRelativePath);
if (Directory.Exists(corePsProvidersModulePath))
{
S_DscCoreR.EventWriteLCMPullModuleSkippedAsModuleIsAvailable(S_DscCoreR.JobGuidString, entry.Key, entry.Value.Item1, corePsProvidersModulePath);
cachedModules.Add(entry.Key);
continue;
}
System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create();
powershell.Runspace = _pluginRunspace;
Collection<PSModuleInfo> availableModules;
try
{
// check if the module exists.
powershell.AddCommand("Get-Module").AddParameter("Name", entry.Key).AddParameter("ListAvailable");
powershell.Streams.Error.Clear();
availableModules = powershell.Invoke<PSModuleInfo>();
if (powershell.Streams.Error.Count > 0)
{
errorRecord = GetErrorRecord("GetModuleListAvailableError", ErrorCategory.InvalidResult,
PSinfrastructureStrings.GetModuleListAvailableError, entry.Key);
powershell.Dispose();
getActionStatusCode = (int)GetActionStatusCodeTypes.AvailableModulesCheckFailure;
return MiResult.FAILED;
}
}
catch (Exception ex)
{
errorRecord = GetErrorRecord("GetModuleListAvailableError", ex, ErrorCategory.InvalidResult,
PSinfrastructureStrings.GetModuleListAvailableError, entry.Key);
getActionStatusCode = (int)GetActionStatusCodeTypes.AvailableModulesCheckFailure;
return MiResult.FAILED;
}
bool isModuleAvailable;
var statusCode = ValidateModuleForVersion(powershell, availableModules, entry.Key, entry.Value.Item1, out isModuleAvailable, out errorRecord);
powershell.Dispose();
if (statusCode != MiResult.OK)
{
getActionStatusCode = (int)GetActionStatusCodeTypes.AvailableModulesCheckFailure;
return statusCode;
}
if (isModuleAvailable)
{
cachedModules.Add(entry.Key);
}
}
// Remove the cached item from the list so we don't need to pull those
foreach (var cachedModule in cachedModules)
{
moduleNameTable.Remove(cachedModule);
}
getActionStatusCode = (int)GetActionStatusCodeTypes.Success;
return MiResult.OK;
}
private static MiResult ValidatePsdscModule(string moduleName, string schemaFileName, out UInt32 getActionStatusCode, out ErrorRecord errorRecord)
{
errorRecord = null;
CimClassCache.InitializeInfraStructureMof();
List<CimClass> newCimClasses;
PSModuleInfo moduleInfo;
if (!File.Exists(schemaFileName))
{
getActionStatusCode = (int) GetActionStatusCodeTypes.ModuleValidationFailure;
errorRecord = Utility.CreateErrorRecord(
"ProviderSchemaFileNotFound",
ErrorCategory.ObjectNotFound,
null,
PSinfrastructureStrings.SchemaFileNotFound,
new object[] { moduleName, schemaFileName });
return MiResult.NOT_FOUND;
}
try
{
newCimClasses = CimClassCache.ImportClasses(schemaFileName);
// Get Module Info by importing module.
using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create())
{
ps.Runspace = _validationRunspace;
moduleInfo = ps.AddCommand("Import-Module").AddParameter("Name", moduleName).AddParameter("Force", true).AddParameter("PassThru", true).Invoke<PSModuleInfo>().FirstOrDefault();
_validationRunspace.ResetRunspaceState();
}
}
catch (Exception e)
{
getActionStatusCode = (int)GetActionStatusCodeTypes.ModuleValidationFailure;
errorRecord = GetErrorRecord(
"InvalidModuleOrSchema",
e,
ErrorCategory.InvalidOperation,
PSinfrastructureStrings.InvalidModuleFileOrMOF, moduleName, schemaFileName);
return MiResult.NOT_FOUND;
}
if (moduleInfo == null)
{
errorRecord = GetErrorRecord(
"CannotLoadModuleFileForValidation",
null,
ErrorCategory.InvalidOperation,
PSinfrastructureStrings.MissingModuleFileForValidation, moduleName);
getActionStatusCode = (int)GetActionStatusCodeTypes.ModuleValidationFailure;
return MiResult.NO_SUCH_PROPERTY;
}
// Validate schema.
var resourceClass = newCimClasses.FirstOrDefault(
cimClass => cimClass.CimSuperClassName != null &&
string.Compare(cimClass.CimSuperClassName, BaseResourceClass, StringComparison.OrdinalIgnoreCase) == 0);
if (resourceClass == null)
{
errorRecord = GetErrorRecord(
"InvalidModuleMissingSchemaClass",
null,
ErrorCategory.InvalidOperation,
PSinfrastructureStrings.InvalidModuleMissingSchemaClass, moduleName, schemaFileName);
getActionStatusCode = (int)GetActionStatusCodeTypes.ModuleValidationFailure;
return MiResult.NOT_FOUND;
}
// Validate required exported commands.
CommandInfo exportedCommand;
if (!moduleInfo.ExportedCommands.TryGetValue(PsDscCmdletGet, out exportedCommand))
{
errorRecord = GetErrorRecord(
"InvalidModuleMissingCommand",
null,
ErrorCategory.InvalidOperation,
PSinfrastructureStrings.InvalidModuleMissingCommand, moduleName, PsDscCmdletGet);
getActionStatusCode = (int)GetActionStatusCodeTypes.ModuleValidationFailure;
return MiResult.NO_SUCH_PROPERTY;
}
if (!moduleInfo.ExportedCommands.TryGetValue(PsDscCmdletSet, out exportedCommand))
{
errorRecord = GetErrorRecord(
"InvalidModuleMissingCommand",
null,
ErrorCategory.InvalidOperation,
PSinfrastructureStrings.InvalidModuleMissingCommand, moduleName, PsDscCmdletSet);
getActionStatusCode = (int)GetActionStatusCodeTypes.ModuleValidationFailure;
return MiResult.NO_SUCH_PROPERTY;
}
if (!moduleInfo.ExportedCommands.TryGetValue(PsDscCmdletTest, out exportedCommand))
{
errorRecord = GetErrorRecord(
"InvalidModuleMissingCommand",
null,
ErrorCategory.InvalidOperation,
PSinfrastructureStrings.InvalidModuleMissingCommand, moduleName, PsDscCmdletTest);
getActionStatusCode = (int)GetActionStatusCodeTypes.ModuleValidationFailure;
return MiResult.NO_SUCH_PROPERTY;
}
// Validate required exported command parameters.
return ValidateExportedCommandParameters(resourceClass, moduleInfo, out getActionStatusCode, out errorRecord);
}
private static MiResult ValidateExportedCommandParameters(
CimClass resourceClass,
PSModuleInfo moduleInfo,
out UInt32 getActionStatusCode,
out ErrorRecord errorRecord)
{
// Get key and write parameter data from MOF schema.
List<CimPropertyDeclaration> keyProperties;
List<CimPropertyDeclaration> writeProperties;
List<CimPropertyDeclaration> requiredProperties;
Utility.GetKeyWriteRequiredProperties(resourceClass, out keyProperties, out writeProperties, out requiredProperties);
// Validate Get command against key parameters.
string moduleName = moduleInfo.Name;
CommandInfo cmdInfo = moduleInfo.ExportedCommands[PsDscCmdletGet];
MiResult result = ValidateCommandParameters(cmdInfo, keyProperties, moduleName, out getActionStatusCode, out errorRecord);
if (result != MiResult.OK) { return result; }
// Validate Set command against key and write parameters.
cmdInfo = moduleInfo.ExportedCommands[PsDscCmdletSet];
result = ValidateCommandParameters(cmdInfo, keyProperties, moduleName, out getActionStatusCode, out errorRecord);
if (result != MiResult.OK) { return result; }
result = ValidateCommandParameters(cmdInfo, writeProperties, moduleName, out getActionStatusCode, out errorRecord);
if (result != MiResult.OK) { return result; }
// Validate Test command against key and write parameters.
cmdInfo = moduleInfo.ExportedCommands[PsDscCmdletTest];
result = ValidateCommandParameters(cmdInfo, keyProperties, moduleName, out getActionStatusCode, out errorRecord);
if (result != MiResult.OK) { return result; }
return ValidateCommandParameters(cmdInfo, writeProperties, moduleName, out getActionStatusCode, out errorRecord);
}
private static MiResult ValidateCommandParameters(
CommandInfo cmdInfo,
List<CimPropertyDeclaration> keyOrWriteProperties,
string moduleName,
out UInt32 getActionStatusCode,
out ErrorRecord errorRecord)
{
errorRecord = null;
foreach (var currentKeyOrWriteProperty in keyOrWriteProperties)
{
if (!cmdInfo.Parameters.ContainsKey(currentKeyOrWriteProperty.Name))
{
errorRecord = GetErrorRecord(
"KeyParameterNotImplemented",
null,
ErrorCategory.InvalidOperation,
PSinfrastructureStrings.MismatchedPsModuleCommandParameterWithSchema, moduleName, cmdInfo.Name, currentKeyOrWriteProperty.Name);
getActionStatusCode = (uint) GetActionStatusCodeTypes.ModuleValidationFailure;
return MiResult.INVALID_PARAMETER;
}
}
getActionStatusCode = (uint) GetActionStatusCodeTypes.Success;
return MiResult.OK;
}
private static MiResult ValidateModuleWithChecksum(string moduleFileName, string checksumFileName,
out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
errorRecord = null;
// Read mof file content and get the checksum.
byte[] moduleContent;
byte[] checkSumContent;
try
{
using (var fs = File.OpenRead(moduleFileName))
{
moduleContent = new byte[fs.Length];
fs.Read(moduleContent, 0, Convert.ToInt32(fs.Length));
fs.Close();
}
using (var fs = File.OpenRead(checksumFileName))
{
checkSumContent = new byte[fs.Length];
fs.Read(checkSumContent, 0, Convert.ToInt32(fs.Length));
fs.Close();
}
}
catch (Exception exception)
{
errorRecord = GetErrorRecord("ModuleFileInvalid", exception, ErrorCategory.InvalidResult,
PSinfrastructureStrings.ModuleFileInvalid, moduleFileName, _pluginModuleName);
getActionStatusCode = (int)GetActionStatusCodeTypes.InvalidModuleFile;
return MiResult.FAILED;
}
//Compute checksum of mof file.
var sha = new SHA256Managed();
byte[] computedHash = sha.ComputeHash(moduleContent);
var hashStringFromMof = BitConverter.ToString(computedHash).Replace("-", "");
var hashStringFromChecksum = System.Text.Encoding.Default.GetString(checkSumContent);
if (string.Compare(hashStringFromMof, hashStringFromChecksum, StringComparison.OrdinalIgnoreCase) != 0)
{
errorRecord = GetErrorRecord("ChecksumValidationModuleFailed", ErrorCategory.InvalidResult,
PSinfrastructureStrings.ChecksumValidationModuleFailed, moduleFileName, _pluginModuleName);
getActionStatusCode = (int)GetActionStatusCodeTypes.ModuleChecksumValidationFailure;
return MiResult.FAILED;
}
getActionStatusCode = (int) GetActionStatusCodeTypes.Success;
return MiResult.OK;
}
private static MiResult ValidateMofWithChecksum(string mofFileName, string checksumFileName,
out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
errorRecord = null;
// Read mof file content and get the checksum.
byte[] mofContent;
byte[] checkSumContent;
try
{
using (var fs = File.OpenRead(mofFileName))
{
mofContent = new byte[fs.Length];
fs.Read(mofContent, 0, Convert.ToInt32(fs.Length));
fs.Close();
}
using (var fs = File.OpenRead(checksumFileName))
{
checkSumContent = new byte[fs.Length];
fs.Read(checkSumContent, 0, Convert.ToInt32(fs.Length));
fs.Close();
}
}
catch (Exception exception)
{
errorRecord = GetErrorRecord("ConfigurationFileInvalid", exception, ErrorCategory.InvalidResult,
PSinfrastructureStrings.ConfigurationFileInvalid, _pluginModuleName);
getActionStatusCode = (int)GetActionStatusCodeTypes.ConfigurationChecksumFileReadFailure;
return MiResult.FAILED;
}
//Compute checksum of mof file.
var sha = new SHA256Managed();
byte[] computedHash = sha.ComputeHash(mofContent);
var hashStringFromMof = BitConverter.ToString(computedHash).Replace("-", "");
var hashStringFromChecksum = System.Text.Encoding.Default.GetString(checkSumContent);
if (string.Compare(hashStringFromMof, hashStringFromChecksum, StringComparison.OrdinalIgnoreCase) != 0)
{
errorRecord = GetErrorRecord("ChecksumValidationConfigurationFailed", ErrorCategory.InvalidResult,
PSinfrastructureStrings.ChecksumValidationConfigurationFailed, _pluginModuleName);
getActionStatusCode = (int)GetActionStatusCodeTypes.ConfigurationChecksumValidationFailure;
return MiResult.FAILED;
}
getActionStatusCode = (int) GetActionStatusCodeTypes.Success;
return MiResult.OK;
}
private static MiResult ValidateConfigurationChecksum(string downloadLocation,
out string mofFileName, out ErrorRecord errorRecord,
out UInt32 getActionStatusCode)
{
mofFileName = null;
// DownloadLocation contain .mof file and .mof.checksum file.
// We will fail if both the files are not available.
string mofFileLocation = null;
try
{
string[] mofFiles = Directory.GetFiles(downloadLocation, "*.mof");
if (mofFiles.Length == 0)
{
errorRecord = GetErrorRecord("ConfigurationFileNotExist", ErrorCategory.InvalidResult,
PSinfrastructureStrings.ConfigurationFileNotExist, _pluginModuleName);
getActionStatusCode = (int) GetActionStatusCodeTypes.ConfigurationFileNotFound;
return MiResult.NOT_FOUND;
}
if (mofFiles.Length > 1)
{
errorRecord = GetErrorRecord("ConfigurationFileMultipleExist", ErrorCategory.InvalidResult,
PSinfrastructureStrings.ConfigurationFileMultipleExist, _pluginModuleName);
getActionStatusCode = (int)GetActionStatusCodeTypes.MultipleConfigurationFilesFound;
return MiResult.FAILED;
}
mofFileLocation = mofFiles[0];
//get the checksum now.
if (!File.Exists(mofFiles[0] + ".checksum"))
{
errorRecord = GetErrorRecord("ConfigurationChecksumFileNotExist", ErrorCategory.InvalidResult,
PSinfrastructureStrings.ConfigurationChecksumFileNotExist, _pluginModuleName, mofFileLocation);
getActionStatusCode = (int)GetActionStatusCodeTypes.ConfigurationChecksumFileNotFound;
return MiResult.NOT_FOUND;
}
//compute checksum
MiResult statusCode = ValidateMofWithChecksum(mofFileLocation, mofFiles[0] + ".checksum", out errorRecord, out getActionStatusCode);
S_DscCoreR.EventWriteLCMPullConfigurationChecksumValidationResult(S_DscCoreR.JobGuidString, mofFileLocation, (uint)statusCode);
if (statusCode != MiResult.OK)
{
return statusCode;
}
}
catch (Exception ex)
{
errorRecord = GetErrorRecord("ConfigurationFileGenericFailure", ex, ErrorCategory.InvalidResult,
PSinfrastructureStrings.ConfigurationFileGenericFailure, _pluginModuleName);
getActionStatusCode = (int) GetActionStatusCodeTypes.ConfigurationChecksumValidationFailure;
return MiResult.FAILED;
}
mofFileName = mofFileLocation;
return MiResult.OK;
}
private static MiResult InstallModules(IntPtr metaConfigHandle, string downloadLocation, string mofFileName, bool allowModuleOverwrite, out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
// Get module Names with Version and required resource names
var moduleNameVersionTable = new Dictionary<string, Tuple<string, List<string>>>();
MiResult statusCode = GetModuleNameVersionTable(mofFileName, moduleNameVersionTable, out errorRecord, out getActionStatusCode);
if (statusCode != MiResult.OK)
{
return statusCode;
}
if (moduleNameVersionTable.Count == 0)
{
S_DscCoreR.EventWriteLCMPullModuleSkippedAsAllModulesAreAvailable(S_DscCoreR.JobGuidString, mofFileName);
return MiResult.OK;
}
string configurationId;
// This holds the download manager specific properties - ServerUrl, SourcePath, etc.
Hashtable arguments;
// We use this collection to hold the PSObjects that we create from arguments. We pass this down to the download manager cmdlets (The parameters have ValueFromPipelineByPropertyName specified)
Collection<PSObject> argumentParameters;
ModuleSpecification[] moduleSpecifications;
using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create(InitialSessionState.CreateDefault2()))
{
PSCommand powershellCommand;
statusCode = DoInitializationForGetModule(metaConfigHandle, moduleNameVersionTable, downloadLocation,
out configurationId, out moduleSpecifications, powershell,
out powershellCommand, out arguments, out argumentParameters,
out errorRecord, out getActionStatusCode);
if (statusCode != MiResult.OK)
{
return statusCode;
}
for (int i = 0; i < moduleNameVersionTable.Count; i++)
{
string downloadedModule;
Collection<PSObject> pullOneModuleResult;
powershell.Commands.Clear();
powershell.Commands = powershellCommand;
powershell.Streams.Error.Clear();
statusCode = PullOneModule(powershell, moduleSpecifications[i], configurationId, downloadLocation,
arguments, argumentParameters, out downloadedModule,
out pullOneModuleResult, out errorRecord, out getActionStatusCode);
if (statusCode != MiResult.OK)
{
return statusCode;
}
// This validation is maybe not necessary.
// This validation checks if the module that we got from the Pull Server is what we asked for
// This used to make sense when we were downloading a bunch of modules.
// TODO : Remove this
statusCode = ValidateForExpectedModule(powershell, pullOneModuleResult, moduleNameVersionTable,
out errorRecord, out getActionStatusCode);
if (statusCode != MiResult.OK)
{
return statusCode;
}
var moduleSpecification = moduleSpecifications[i];
statusCode = ValidateAndInstallOneModule(downloadLocation, moduleSpecification,
moduleNameVersionTable[moduleSpecification.Name].Item2, allowModuleOverwrite,
out errorRecord, out getActionStatusCode);
if (statusCode != (UInt32)MiResult.OK)
{
return statusCode;
}
}
}
// Log to ETW Channel:Microsoft-Windows-DSC/Operational
S_DscCoreR.EventWriteLCMPullGetModuleSuccess(S_DscCoreR.JobGuidString, _pluginModuleName);
return MiResult.OK;
}
// Does the initialization for Get-DscModule. This initialization needs to only happen once for all the modules.
// As part of this initialization, the following is done
// 1) Download manager is imported
// 2) Configuration is parsed to get the list of modules and the download-manager specific properties
// 3) For the download manager specific properties, we create PSObjects so we can pass those down to the download manager cmdlets.
// 4) We construct a PowerShell that has all the parameters added (except for the Module parameter. This parameter gets added for each module)
private static MiResult DoInitializationForGetModule(IntPtr metaConfigHandle, Dictionary<string, Tuple<string, List<string>>> moduleNameVersionTable,
string downloadLocation, out string configurationId, out ModuleSpecification[] moduleSpecifications,
System.Management.Automation.PowerShell powershell, out PSCommand powershellCommand, out Hashtable arguments, out Collection<PSObject> argumentParameters,
out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
configurationId = null;
powershellCommand = null;
moduleSpecifications = null;
argumentParameters = null;
arguments = new Hashtable();
// Initialize DownloadManager
MiResult statusCode = InitializePlugin(metaConfigHandle, out errorRecord);
if (statusCode != MiResult.OK)
{
getActionStatusCode = (int)GetActionStatusCodeTypes.DownloadManagerInitializationFailure;
return statusCode;
}
//Get parameters for Get-Module cmdlet
PSCredential pscredential;
statusCode = GetGetModuleParams(metaConfigHandle, out pscredential, arguments, out configurationId, out errorRecord, out getActionStatusCode);
if (statusCode != MiResult.OK)
{
return statusCode;
}
moduleSpecifications = GetModuleSpecification(moduleNameVersionTable);
powershell.Runspace = _pluginRunspace;
if (arguments.Count > 0)
{
argumentParameters = powershell.AddCommand("New-Object").AddParameter("Type", "psobject").AddParameter("Property", arguments).Invoke();
powershell.Commands.Clear();
}
powershellCommand = new PSCommand();
powershellCommand.AddCommand(_pluginModuleName + "\\" + DownloadManagerGetModules);
powershellCommand.AddParameter(ParamConfigurationId, configurationId);
powershellCommand.AddParameter(ParamDestinationPath, downloadLocation);
if (pscredential != null)
{
powershellCommand.AddParameter(ParamCredential, pscredential);
}
return MiResult.OK;
}
// Powershell object passed here will be disposed off by the caller
private static MiResult PullOneModule(System.Management.Automation.PowerShell powershell, ModuleSpecification moduleSpecification, string configurationId, string downloadLocation,
Hashtable arguments, Collection<PSObject> argumentParameters, out string downloadedModule, out Collection<PSObject> pullOneModuleResult,
out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
pullOneModuleResult = null;
downloadedModule = null;
string moduleVersionString = GetModuleVersionStringFromModuleSpecification(moduleSpecification);
try
{
powershell.AddParameter(ParamModules, moduleSpecification);
// Log to ETW Channel:Microsoft-Windows-DSC/Operational
S_DscCoreR.EventWriteLCMPullGetModuleAttempt(S_DscCoreR.JobGuidString, _pluginModuleName, configurationId, moduleVersionString);
powershell.Streams.Error.Clear();
pullOneModuleResult = arguments.Count > 0 ? powershell.Invoke(argumentParameters) : powershell.Invoke();
if (powershell.Streams.Error.Count > 0)
{
errorRecord = powershell.Streams.Error[0];
powershell.Dispose();
if( string.Compare(errorRecord.FullyQualifiedErrorId, "WebDownloadManagerUnknownChecksumAlgorithm", StringComparison.OrdinalIgnoreCase) == 0 )
{
getActionStatusCode = (int)GetActionStatusCodeTypes.InvalidChecksumAlgorithm;
}
else
{
getActionStatusCode = (int)GetActionStatusCodeTypes.GetDscModuleCommandFailure;
}
return MiResult.FAILED;
}
}
catch (Exception ex)
{
errorRecord = GetErrorRecord("GetModuleExecutionFailure", ex, ErrorCategory.InvalidType,
PSinfrastructureStrings.GetModuleExecutionFailure, _pluginModuleName);
getActionStatusCode = (int)GetActionStatusCodeTypes.GetDscModuleCommandFailure;
return MiResult.FAILED;
}
S_DscCoreR.EventWriteLCMPullModuleDownloadLocation(S_DscCoreR.JobGuidString, moduleVersionString, downloadLocation);
errorRecord = null;
getActionStatusCode = (int)GetActionStatusCodeTypes.Success;
return MiResult.OK;
}
// Validates that the module we have is the module we asked for from the Pull Server
// This validation is maybe not necessary.
// This used to make sense when we were downloading a bunch of modules.
// TODO : Remove this
private static MiResult ValidateForExpectedModule(System.Management.Automation.PowerShell powershell, Collection<PSObject> pullOneModuleResult,
Dictionary<string, Tuple<string, List<string>>> moduleNameVersionTable,
out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
ModuleSpecification[] pullOneModuleInternalResult;
HandleNonTerminatingErrors(powershell, _pluginModuleName, DownloadManagerGetConfiguration, out errorRecord);
if (errorRecord != null)
{
getActionStatusCode = (int)GetActionStatusCodeTypes.GetDscModuleCommandFailure;
return MiResult.FAILED;
}
if (pullOneModuleResult == null || pullOneModuleResult.Count == 0)
{
getActionStatusCode = (int)GetActionStatusCodeTypes.GetDscModuleCommandFailure;
errorRecord = GetErrorRecord("GetModuleResultNotExpected", ErrorCategory.InvalidResult,
PSinfrastructureStrings.GetModuleResultCountUnexpected, _pluginModuleName);
return MiResult.NOT_FOUND;
}
try
{
pullOneModuleInternalResult = LanguagePrimitives.ConvertTo<ModuleSpecification[]>(pullOneModuleResult);
}
catch (PSInvalidCastException ex)
{
getActionStatusCode = (int)GetActionStatusCodeTypes.GetDscModuleInvalidOutput;
errorRecord = GetErrorRecord("GetModuleResultNotExpected", ex, ErrorCategory.InvalidResult,
PSinfrastructureStrings.GetModuleResultCountUnexpected, _pluginModuleName);
return MiResult.NOT_FOUND;
}
// We are always downloading only one module at at a time
if (pullOneModuleInternalResult == null || pullOneModuleInternalResult.Count() > 1)
{
getActionStatusCode = (int)GetActionStatusCodeTypes.GetDscModuleInvalidOutput;
errorRecord = GetErrorRecord("GetModuleResultNotExpected", ErrorCategory.InvalidResult,
PSinfrastructureStrings.GetModuleResultCountUnexpected, _pluginModuleName);
return MiResult.NOT_FOUND;
}
if (!moduleNameVersionTable.ContainsKey(pullOneModuleInternalResult[0].Name))
{
getActionStatusCode = (int)GetActionStatusCodeTypes.GetDscModuleInvalidOutput;
errorRecord = GetErrorRecord("GetModuleResultNotExpected", ErrorCategory.InvalidResult,
PSinfrastructureStrings.GetModuleResultCountUnexpected, _pluginModuleName);
return MiResult.NO_SUCH_PROPERTY;
}
getActionStatusCode = (int)GetActionStatusCodeTypes.Success;
return MiResult.OK;
}
private static MiResult ValidateAndInstallOneModule(string downloadLocation, ModuleSpecification moduleSpecification,
IEnumerable<string> requiredResources,
bool allowModuleOverwrite, out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
// Powershell modules are not supported by Linux DSC.
return MiResult.NOT_SUPPORTED;
}
#endregion PLUGIN_GETMODULE
#region PLUGIN_COMMONHELPERS
private static string GetModuleVersionStringFromModuleSpecification(ModuleSpecification moduleSpecification)
{
StringBuilder result = new StringBuilder();
result.Append("(").Append(moduleSpecification.Name);
if (moduleSpecification.Version != null)
{
result.Append(",").Append(moduleSpecification.Version).Append(")");
}
else
{
result.Append(")");
}
return result.ToString();
}
internal static ErrorRecord GetErrorRecord(string errorId, ErrorCategory errorCategory, string errorResourceId, params object[] errorResourceIdParams)
{
return Utility.CreateErrorRecord(errorId, errorCategory, null, errorResourceId, errorResourceIdParams);
}
internal static ErrorRecord GetErrorRecord(string errorId, Exception exception, ErrorCategory errorCategory, string errorResourceId, params object[] errorResourceIdParams)
{
return Utility.CreateErrorRecord(errorId, errorCategory, exception, errorResourceId, errorResourceIdParams);
}
// ReSharper disable UnusedMember.Local
private static string GetPsProgramFilesModulePath()
// ReSharper restore UnusedMember.Local
{
//return @"c:\Program Files\Microsoft\Windows\Powershell\Modules\";
return Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + @"\WindowsPowerShell\Modules\";
}
private static void HandleNonTerminatingErrors(System.Management.Automation.PowerShell powerShell,
string providerName,
string operationCmd,
out ErrorRecord errorRecord)
{
errorRecord = null;
if (powerShell != null &&
powerShell.Streams != null &&
powerShell.Streams.Error != null &&
powerShell.Streams.Error.Count > 0 &&
!string.IsNullOrEmpty(providerName) &&
!string.IsNullOrEmpty(operationCmd))
{
string errorMessage = string.Format(
CultureInfo.CurrentCulture,
PSinfrastructureStrings.NonTerminatingErrorFromProvider,
providerName,
operationCmd);
InvalidOperationException invalidOperationException;
// If there is only one non-terminting error then the surface it's inner execetion (if it exists) to the commandline.
if (powerShell.Streams.Error.Count == 1 && powerShell.Streams.Error[0].Exception != null)
{
invalidOperationException = new InvalidOperationException(errorMessage, powerShell.Streams.Error[0].Exception);
}
else
{
invalidOperationException = new InvalidOperationException(errorMessage);
}
errorRecord = new ErrorRecord(
invalidOperationException,
"NonTerminatingErrorFromProvider",
ErrorCategory.InvalidOperation,
null);
}
}
private static string GetSystemUuid()
{
var cimSession = CimSession.Create("localhost");
var cimInstance = cimSession.EnumerateInstances(@"root/cimv2", "Win32_ComputerSystemProduct").First();
if (cimInstance.CimInstanceProperties["UUID"] != null)
{
return cimInstance.CimInstanceProperties["UUID"].Value.ToString();
}
return String.Empty;
}
private static MiResult GetAllowModuleOverwriteFromMetaConfig(IntPtr metaConfigHandle, out Boolean allowModuleOverwrite, out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
errorRecord = null;
allowModuleOverwrite = false;
var metaConfigInstance = new CimInstance(new InstanceHandle(metaConfigHandle, false), null);
if (metaConfigInstance.CimInstanceProperties[MetaConfigAllowModuleOverwrite] != null &&
metaConfigInstance.CimInstanceProperties[MetaConfigAllowModuleOverwrite].Value != null)
{
LanguagePrimitives.TryConvertTo(metaConfigInstance.CimInstanceProperties[MetaConfigAllowModuleOverwrite].Value, out allowModuleOverwrite);
}
getActionStatusCode = (int)GetActionStatusCodeTypes.Success;
return MiResult.OK;
}
private static MiResult GetMetaConfigParams(IntPtr metaConfigHandle, out PSCredential pscredential, Hashtable arguments, out string configurationId, out ErrorRecord errorRecord, out UInt32 getActionStatusCode)
{
configurationId = null;
errorRecord = null;
pscredential = null;
var metaConfigInstance = new CimInstance(new InstanceHandle(metaConfigHandle, false), null);
// Get ConfigurationID.
try
{
if (metaConfigInstance.CimInstanceProperties[MetaConfigConfigurationId] != null &&
metaConfigInstance.CimInstanceProperties[MetaConfigConfigurationId].Value != null)
{
string tempConfigurationId = metaConfigInstance.CimInstanceProperties[MetaConfigConfigurationId].Value.ToString();
/* We will use System UUID only if user has explicitly asked for.*/
if (string.Compare(tempConfigurationId, UseSystemUUIDValue, StringComparison.OrdinalIgnoreCase) == 0)
{
tempConfigurationId = GetSystemUuid();
}
Guid throwAwayGuid = Guid.Empty;
if (Guid.TryParse(tempConfigurationId, out throwAwayGuid))
{
configurationId = tempConfigurationId;
}
else
{
getActionStatusCode = (int) GetActionStatusCodeTypes.InvalidConfigurationIdFormat;
errorRecord = GetErrorRecord("InvalidConfigurationIdFormat", null, ErrorCategory.InvalidArgument, PSinfrastructureStrings.InvalidConfigurationIdFormat, tempConfigurationId);
return MiResult.INVALID_PARAMETER;
}
}
else
{
getActionStatusCode = (int) GetActionStatusCodeTypes.InvalidConfigurationIdFormat;
errorRecord = GetErrorRecord("ConfigurationIdNotSpecified", null, ErrorCategory.InvalidArgument, PSinfrastructureStrings.ConfigurationIdNotSpecified);
return MiResult.INVALID_PARAMETER;
}
}
catch (CimException exception)
{
errorRecord = GetErrorRecord("MetaConfigConfigurationIdInvalid", exception, ErrorCategory.InvalidResult,
PSinfrastructureStrings.MetaConfigConfigurationIdInvalid,
_pluginModuleName);
getActionStatusCode = (int)GetActionStatusCodeTypes.InvalidConfigurationIdInMetaConfig;
return (MiResult)exception.StatusCode;
}
//Get PS Credential
if (metaConfigInstance.CimInstanceProperties[ParamCredential] != null &&
metaConfigInstance.CimInstanceProperties[ParamCredential].Value != null)
{
string userName = null;
string password = null;
string domain = null;
var cimCredentialInstance = metaConfigInstance.CimInstanceProperties[ParamCredential].Value as CimInstance;
if (cimCredentialInstance != null)
{
if (cimCredentialInstance.CimInstanceProperties[ParamCredentialUserName] != null &&
cimCredentialInstance.CimInstanceProperties[ParamCredentialUserName].Value != null)
{
userName = cimCredentialInstance.CimInstanceProperties[ParamCredentialUserName].Value.ToString();
}
if (cimCredentialInstance.CimInstanceProperties[ParamCredentialPassword] != null &&
cimCredentialInstance.CimInstanceProperties[ParamCredentialPassword].Value != null)
{
password = cimCredentialInstance.CimInstanceProperties[ParamCredentialPassword].Value.ToString();
}
if (cimCredentialInstance.CimInstanceProperties[ParamCredentialDomain] != null &&
cimCredentialInstance.CimInstanceProperties[ParamCredentialDomain].Value != null)
{
domain = cimCredentialInstance.CimInstanceProperties[ParamCredentialDomain].Value.ToString();
}
if (userName != null &&
password != null)
{
// Extract the password into a SecureString.
var securePassword = new SecureString();
foreach (var t in password)
{
securePassword.AppendChar(t);
}
securePassword.MakeReadOnly();
if (!string.IsNullOrEmpty(domain))
{
userName = Path.Combine(domain, userName);
}
pscredential = new PSCredential(userName, securePassword);
}
}
}
// Get cmdlet arguments as hashtable.
try
{
if (metaConfigInstance.CimInstanceProperties[MetaConfigDownloadManagerCustomData] != null &&
metaConfigInstance.CimInstanceProperties[MetaConfigDownloadManagerCustomData].Value != null)
{
var cimInstances = metaConfigInstance.CimInstanceProperties[MetaConfigDownloadManagerCustomData].Value as CimInstance[];
if (cimInstances != null)
{
foreach (var inst in cimInstances)
{
arguments.Add(inst.CimInstanceProperties["Key"].Value.ToString(), inst.CimInstanceProperties["Value"].Value.ToString());
}
}
}
}
catch (CimException exception)
{
errorRecord = GetErrorRecord("MetaConfigCustomDataInvalid", exception, ErrorCategory.InvalidResult,
PSinfrastructureStrings.MetaConfigCustomDataInvalid,
_pluginModuleName);
getActionStatusCode = (int)GetActionStatusCodeTypes.InvalidDownloadManagerCustomDataInMetaConfig;
return (MiResult)exception.StatusCode;
}
getActionStatusCode = (int) GetActionStatusCodeTypes.Success;
return MiResult.OK;
}
private static MiResult InitializePlugin(IntPtr metaConfigHandle, out ErrorRecord errorRecord)
{
errorRecord = null;
if (PluginRunspaceInitialized == false)
{
MiResult statusCode = ImportDownloadManager(metaConfigHandle, out errorRecord);
if (statusCode != MiResult.OK)
{
return statusCode;
}
}
return MiResult.OK;
}
private static MiResult ImportDownloadManager(IntPtr metaConfigHandle, out ErrorRecord errorRecord)
{
string downloadManagerName;
// Get Module Name
MiResult statusCode = GetDownloadManagerModuleName(metaConfigHandle, out downloadManagerName, out errorRecord);
if (statusCode != MiResult.OK)
{
return statusCode;
}
// if it is webDownloadManager, it is implemented inside PSDesiredStateConfiguration\WebDownloadManager.
// if it is FileDownloadManager, it is implemented inside PSDesiredStateConfiguration\DownloadManager\DSCFileDownloadManager.
string downloadManagerPath = GetDownloadManagerModuleLoadPath(downloadManagerName);
try
{
using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create())
{
powershell.Runspace = _pluginRunspace;
// Load the module
powershell.Streams.Error.Clear();
powershell.AddCommand("Import-Module")
.AddParameter("Name", downloadManagerPath)
.AddParameter("PassThru", true)
.Invoke<PSModuleInfo>()
.FirstOrDefault();
if (powershell.Streams.Error.Count > 0)
{
errorRecord = GetErrorRecord("ImportDownloadManagerException", powershell.Streams.Error[0].Exception, ErrorCategory.InvalidResult,
PSinfrastructureStrings.ImportDownloadManagerException, _pluginModuleName);
return MiResult.FAILED;
}
}
}
catch (Exception exception)
{
errorRecord = GetErrorRecord("ImportDownloadManagerException", exception, ErrorCategory.InvalidResult,
PSinfrastructureStrings.ImportDownloadManagerException, _pluginModuleName);
return MiResult.FAILED;
}
if (statusCode != MiResult.OK)
{
return statusCode;
}
_pluginModuleName = downloadManagerName;
return MiResult.OK;
}
private static string GetDownloadManagerModuleLoadPath(string downloadManagerName)
{
string downloadManagerLoadPath;
if (!DownloadManagerMap.TryGetValue(downloadManagerName, out downloadManagerLoadPath))
{
downloadManagerLoadPath = downloadManagerName;
}
return downloadManagerLoadPath;
}
private static MiResult GetDownloadManagerModuleName(IntPtr metaConfigHandle, out string downloadManagerName,
out ErrorRecord errorRecord)
{
downloadManagerName = null;
errorRecord = null;
var metaConfigInstance = new CimInstance(new InstanceHandle(metaConfigHandle, false), null);
try
{
if (metaConfigInstance.CimInstanceProperties[MetaConfigDownloadManagerName] != null &&
metaConfigInstance.CimInstanceProperties[MetaConfigDownloadManagerName].Value != null)
{
downloadManagerName = metaConfigInstance.CimInstanceProperties[MetaConfigDownloadManagerName].Value.ToString();
}
else
{
errorRecord = GetErrorRecord("MetaConfigDownloadManagerInvalid", ErrorCategory.InvalidResult,
PSinfrastructureStrings.MetaConfigDownloadManagerInvalid);
return MiResult.NOT_FOUND;
}
}
catch (CimException exception)
{
errorRecord = GetErrorRecord("MetaConfigDownloadManagerInvalid", exception, ErrorCategory.InvalidResult,
PSinfrastructureStrings.MetaConfigDownloadManagerInvalid);
return (MiResult)exception.StatusCode;
}
return MiResult.OK;
}
#endregion PLUGIN_COMMONHELPERS
#region PLUGIN_PUBLICMETHODS
/// <summary>
/// Install API used to install the modules as necessary from the mof file..
/// </summary>
/// <param name="metaConfigHandle"></param>
/// <param name="downloadLocation"></param>
/// <param name="mofFileName"></param>
/// <param name="allowModuleOverwrite"></param>
/// <param name="errorInstanceHandle"></param>
/// <param name="getActionStatusCode"></param>
/// <returns>If InstallModules operation is successful, 0 is returned or else a status code indicating the error is returned.</returns>
public static UInt32 GetDscModule(IntPtr metaConfigHandle, string downloadLocation, string mofFileName, bool allowModuleOverwrite, out IntPtr errorInstanceHandle, out UInt32 getActionStatusCode)
{
ErrorRecord errorRecord;
errorInstanceHandle = IntPtr.Zero;
MiResult statusCode = InstallModules(metaConfigHandle, downloadLocation, mofFileName, allowModuleOverwrite, out errorRecord, out getActionStatusCode);
if (statusCode != MiResult.OK)
{
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)statusCode, errorRecord);
return (uint)statusCode;
}
return (uint)MiResult.OK;
}
/// <summary>
/// Get Api facilitates to execute Get-Configuration functionality on the provider mentioned in the meta configuration..
/// </summary>
/// <param name="metaConfigHandle"></param>
/// <param name="fileHash"></param>
/// <param name="complianceStatus"></param>
/// <param name="lastGetActionStatusCode"></param>
/// <param name="errorInstanceHandle"></param>
/// <param name="outputResult"></param>
/// <param name="getActionStatusCode"></param>
/// <returns>If GetDscAction operation is successful, 0 is returned or else a status code indicating the error is returned.</returns>
public static UInt32 GetDscAction(IntPtr metaConfigHandle, string fileHash, bool complianceStatus, UInt32 lastGetActionStatusCode, out IntPtr errorInstanceHandle, out string outputResult, out UInt32 getActionStatusCode)
{
ErrorRecord errorRecord;
errorInstanceHandle = IntPtr.Zero;
MiResult statusCode = InitializePlugin(metaConfigHandle, out errorRecord);
outputResult = null;
if (statusCode != MiResult.OK)
{
getActionStatusCode = (int) GetActionStatusCodeTypes.DownloadManagerInitializationFailure;
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)statusCode, errorRecord);
return (uint)statusCode;
}
//Get parameters for Get-Status cmdlet
PSCredential pscredential;
var arguments = new Hashtable();
string configurationId;
statusCode = GetMetaConfigParams(metaConfigHandle, out pscredential, arguments, out configurationId, out errorRecord, out getActionStatusCode);
if (getActionStatusCode != (uint)GetActionStatusCodeTypes.Success)
{
lastGetActionStatusCode = getActionStatusCode;
}
if (statusCode != MiResult.OK)
{
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)statusCode, errorRecord);
return (uint)statusCode;
}
System.Management.Automation.PowerShell powershell;
Collection<PSObject> providerResult;
//Create PowerShell object and invoke the command.
try
{
powershell = System.Management.Automation.PowerShell.Create();
powershell.Runspace = _pluginRunspace;
Collection<PSObject> argumentParameters = null;
if (arguments.Count > 0)
{
argumentParameters = powershell.AddCommand("New-Object").AddParameter("Type", "psobject").AddParameter("Property", arguments).Invoke();
powershell.Commands.Clear();
}
powershell.Commands.AddCommand(_pluginModuleName + "\\" + DownloadManagerGetAction);
powershell.AddParameter(ParamConfigurationId, configurationId);
powershell.AddParameter(ParamNotCompliant, !complianceStatus);
powershell.AddParameter(ParamFileHash, fileHash);
powershell.AddParameter(ParamStatusCode, lastGetActionStatusCode);
if (pscredential != null)
{
powershell.AddParameter(ParamCredential, pscredential);
}
// Log to ETW Channel:Microsoft-Windows-DSC/Operational
S_DscCoreR.EventWriteLCMPullGetActionAttempt(S_DscCoreR.JobGuidString,
_pluginModuleName, configurationId, fileHash, complianceStatus);
powershell.Streams.Error.Clear();
providerResult = arguments.Count > 0 ? powershell.Invoke(argumentParameters) : powershell.Invoke();
if (powershell.Streams.Error.Count > 0)
{
errorRecord = powershell.Streams.Error[0];
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)MiResult.FAILED, errorRecord);
powershell.Dispose();
getActionStatusCode = (int) GetActionStatusCodeTypes.GetDscActionCommandFailure;
return (UInt32)MiResult.FAILED;
}
}
catch (Exception exception)
{
errorRecord = GetErrorRecord("GetActionException", exception, ErrorCategory.InvalidResult,
PSinfrastructureStrings.GetActionException, _pluginModuleName);
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)MiResult.FAILED, errorRecord);
getActionStatusCode = (int)GetActionStatusCodeTypes.GetDscActionCommandFailure;
return (UInt32)MiResult.FAILED;
}
statusCode = ValidateGetActionResult(powershell, providerResult, _pluginModuleName, out outputResult, out errorRecord, out getActionStatusCode);
powershell.Dispose();
if (statusCode != MiResult.OK)
{
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)statusCode, errorRecord);
return (uint)statusCode;
}
// Log to ETW Channel:Microsoft-Windows-DSC/Operational
S_DscCoreR.EventWriteLCMPullGetActionSuccess(S_DscCoreR.JobGuidString,
outputResult,
_pluginModuleName);
return (uint)MiResult.OK;
}
/// <summary>
/// Get Api facilitates to execute Get-Configuration functionality on the provider mentioned in the meta configuration..
/// </summary>
/// <param name="metaConfigHandle"></param>
/// <param name="errorInstanceHandle"></param>
/// <param name="mofFileName"></param>
/// <param name="outputResult"></param>
/// <param name="getActionStatusCode"></param>
/// <returns>If GetDscDocument operation is successful, 0 is returned or else a status code indicating the error is returned.</returns>
public static UInt32 GetDscDocument(IntPtr metaConfigHandle, out IntPtr errorInstanceHandle, out string mofFileName, out string outputResult, out UInt32 getActionStatusCode)
{
ErrorRecord errorRecord;
errorInstanceHandle = IntPtr.Zero;
string destinationPath = Path.GetTempPath() + "\\" + DateTime.Now.Ticks;
MiResult statusCode = InitializePlugin(metaConfigHandle, out errorRecord);
outputResult = null;
mofFileName = null;
if (statusCode != MiResult.OK)
{
getActionStatusCode = (int)GetActionStatusCodeTypes.DownloadManagerInitializationFailure;
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)statusCode, errorRecord);
return (uint)statusCode;
}
if (Directory.Exists(destinationPath))
{
errorRecord = GetErrorRecord("DownloadLocationDirectoryExists", ErrorCategory.InvalidResult,
PSinfrastructureStrings.DownloadLocationDirectoryExists);
getActionStatusCode = (int)GetActionStatusCodeTypes.DownloadManagerInitializationFailure;
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((UInt32)MiResult.ALREADY_EXISTS, errorRecord);
return (uint)MiResult.ALREADY_EXISTS;
}
Directory.CreateDirectory(destinationPath);
//Get parameters for Get-Configuration cmdlet
PSCredential pscredential;
var arguments = new Hashtable(StringComparer.OrdinalIgnoreCase);
string configurationId;
statusCode = GetMetaConfigParams(metaConfigHandle, out pscredential, arguments, out configurationId, out errorRecord, out getActionStatusCode);
if (statusCode != MiResult.OK)
{
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)statusCode, errorRecord);
return (uint)statusCode;
}
bool allowModuleOverwrite;
statusCode = GetAllowModuleOverwriteFromMetaConfig(metaConfigHandle, out allowModuleOverwrite, out errorRecord, out getActionStatusCode);
if (statusCode != MiResult.OK)
{
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)statusCode, errorRecord);
return (uint)statusCode;
}
System.Management.Automation.PowerShell powershell;
Collection<PSObject> providerResult;
//Create PowerShell object and invoke the command.
try
{
powershell = System.Management.Automation.PowerShell.Create();
powershell.Runspace = _pluginRunspace;
Collection<PSObject> argumentParameters = null;
if (arguments.Count > 0)
{
argumentParameters = powershell.AddCommand("New-Object").AddParameter("Type", "psobject").AddParameter("Property", arguments).Invoke();
powershell.Commands.Clear();
}
powershell.Commands.AddCommand(_pluginModuleName + "\\" + DownloadManagerGetConfiguration);
powershell.AddParameter(ParamConfigurationId, configurationId);
powershell.AddParameter(ParamDestinationPath, destinationPath);
if (pscredential != null)
{
powershell.AddParameter(ParamCredential, pscredential);
}
// Log to ETW Channel:Microsoft-Windows-DSC/Operational
S_DscCoreR.EventWriteLCMPullGetConfigAttempt(S_DscCoreR.JobGuidString,
_pluginModuleName, configurationId);
powershell.Streams.Error.Clear();
providerResult = arguments.Count > 0 ? powershell.Invoke(argumentParameters) : powershell.Invoke();
if (powershell.Streams.Error.Count > 0)
{
Directory.Delete(destinationPath, true);
errorRecord = powershell.Streams.Error[0];
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)MiResult.FAILED, errorRecord);
powershell.Dispose();
if( string.Compare(errorRecord.FullyQualifiedErrorId, "WebDownloadManagerUnknownChecksumAlgorithm", StringComparison.OrdinalIgnoreCase) == 0 )
{
getActionStatusCode = (int)GetActionStatusCodeTypes.InvalidChecksumAlgorithm;
}
else
{
getActionStatusCode = (int)GetActionStatusCodeTypes.GetConfigurationCommandFailure;
}
return (uint)MiResult.FAILED;
}
}
catch (Exception exception)
{
Directory.Delete(destinationPath, true);
errorRecord = GetErrorRecord("GetConfigurationException", exception, ErrorCategory.InvalidResult,
PSinfrastructureStrings.GetConfigurationException, _pluginModuleName);
getActionStatusCode = (int)GetActionStatusCodeTypes.GetConfigurationCommandFailure;
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)MiResult.FAILED, errorRecord);
return (uint)MiResult.FAILED;
}
statusCode = ValidateGetConfigurationResult(powershell, providerResult, _pluginModuleName, out outputResult, out errorRecord, out getActionStatusCode);
S_DscCoreR.EventWriteLCMPullConfigurationChecksumValidationResult(S_DscCoreR.JobGuidString, configurationId, (uint)statusCode);
powershell.Dispose();
if (statusCode != MiResult.OK)
{
Directory.Delete(destinationPath, true);
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)statusCode, errorRecord);
return (uint)statusCode;
}
// if it is ok, we will validate checksum here.
if (string.Compare(outputResult, StatusOk, StringComparison.OrdinalIgnoreCase) == 0)
{
statusCode = ValidateConfigurationChecksum(destinationPath, out mofFileName, out errorRecord, out getActionStatusCode);
}
if (statusCode != MiResult.OK)
{
Directory.Delete(destinationPath, true);
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)statusCode, errorRecord);
return (uint)statusCode;
}
// If checksum validation passes, we go on to pull the module
statusCode = InstallModules(metaConfigHandle, destinationPath, mofFileName, allowModuleOverwrite, out errorRecord, out getActionStatusCode);
if (statusCode != MiResult.OK)
{
Directory.Delete(destinationPath, true);
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)statusCode, errorRecord);
return (uint)statusCode;
}
// Log to ETW Channel:Microsoft-Windows-DSC/Operational
S_DscCoreR.EventWriteLCMPullGetConfigSuccess(S_DscCoreR.JobGuidString,
_pluginModuleName);
return (uint)MiResult.OK;
}
/// <summary>
/// This API installs a private certificate (.pfx).
/// </summary>
/// <param name="certificatePath"></param>
/// <param name="password"></param>
/// <param name="certificateThumbprint"></param>
/// <param name="errorInstanceHandle"></param>
/// <returns>If successfully installed return 0 and certificateThumbprint or else a status code indicating the error is returned.</returns>
public static uint InstallCertificate(string certificatePath, SecureString password,
out string certificateThumbprint, out IntPtr errorInstanceHandle)
{
certificateThumbprint = null;
errorInstanceHandle = IntPtr.Zero;
X509Store store = null;
try
{
var cert = new X509Certificate2(certificatePath, password);
// open certificate store
store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
// install certificate.
if (!store.Certificates.Contains(cert))
{
store.Add(cert);
}
certificateThumbprint = cert.Thumbprint;
}
catch (Exception ex)
{
var errorRecord = GetErrorRecord("InstallCertificateException", ex, ErrorCategory.InvalidType,
PSinfrastructureStrings.InstallCertificateException, certificatePath);
errorInstanceHandle = Utility.ConvertErrorRecordToMiInstance((uint)MiResult.FAILED, errorRecord);
return (uint)MiResult.FAILED;
}
finally
{
if (store != null)
{
store.Close();
}
}
return (uint)MiResult.OK;
}
#endregion PLUGIN_PUBLICMETHODS
}
}