diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Computer.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Computer.cs
index 88a2ee2b16..2f5b7539e4 100644
--- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Computer.cs
+++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Computer.cs
@@ -1,3 +1,5 @@
+#if !LINUX
+
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
@@ -7384,3 +7386,5 @@ $result
#endregion
}//End namespace
+
+#endif
diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/TimeZoneCommands.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/TimeZoneCommands.cs
new file mode 100644
index 0000000000..a1f5f20820
--- /dev/null
+++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/TimeZoneCommands.cs
@@ -0,0 +1,866 @@
+#if !LINUX
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Management.Automation;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Globalization;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.PowerShell.Commands
+{
+ ///
+ /// A cmdlet to retrieve time zone information.
+ ///
+ [Cmdlet(VerbsCommon.Get, "TimeZone", DefaultParameterSetName = "Name",
+ HelpUri = "http://go.microsoft.com/fwlink/?LinkId=799468")]
+ [Alias("gtz")]
+ public class GetTimeZoneCommand : PSCmdlet
+ {
+ #region Parameters
+
+ ///
+ /// A list of the local time zone ids that the cmdlet should look up.
+ ///
+ [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Id")]
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
+ public string[] Id { get; set; }
+
+ ///
+ /// Specifies that the cmdlet should produce a collection of the
+ /// TimeZoneInfo objects that are available on the system.
+ ///
+ [Parameter(Mandatory = true, ParameterSetName = "ListAvailable")]
+ public SwitchParameter ListAvailable { get; set; }
+
+ ///
+ /// A list of the local time zone names that the cmdlet should look up.
+ ///
+ [Parameter(Position = 0, ValueFromPipeline = true, ParameterSetName = "Name")]
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
+ public string[] Name { get; set; }
+
+ #endregion Parameters
+
+ ///
+ /// Implementation of the ProcessRecord method for Get-TimeZone
+ ///
+ protected override void ProcessRecord()
+ {
+ // make sure we've got the latest time zone settings
+ TimeZoneHelper.ClearCachedData();
+
+ if (this.ParameterSetName.Equals("ListAvailable", StringComparison.OrdinalIgnoreCase))
+ {
+ // output the list of all available time zones
+ WriteObject(TimeZoneInfo.GetSystemTimeZones(), true);
+ }
+ else if (this.ParameterSetName.Equals("Id", StringComparison.OrdinalIgnoreCase))
+ {
+ // lookup each time zone id
+ foreach (string tzid in Id)
+ {
+ try
+ {
+ WriteObject(TimeZoneInfo.FindSystemTimeZoneById(tzid));
+ }
+#if CORECLR
+ // TimeZoneNotFoundException is thrown by TimeZoneInfo, but not
+ // publicly visible (so can't be caught), so for now we're catching
+ // the parent exception time. This should be removed once the more
+ // specific exception is available.
+ catch (Exception e)
+#else
+ catch (TimeZoneNotFoundException e)
+#endif
+ {
+ WriteError(new ErrorRecord(e, TimeZoneHelper.TimeZoneNotFoundError,
+ ErrorCategory.InvalidArgument, "Id"));
+ }
+ }
+ }
+ else // ParameterSetName == "Name"
+ {
+ if (null != Name)
+ {
+ // lookup each time zone name (or wildcard pattern)
+ foreach (string tzname in Name)
+ {
+ TimeZoneInfo[] timeZones = TimeZoneHelper.LookupSystemTimeZoneInfoByName(tzname);
+ if (0 < timeZones.Length)
+ {
+ // manually process each object in the array, so if there is only a single
+ // entry then the returned type is TimeZoneInfo and not TimeZoneInfo[], and
+ // it can be pipelined to Set-TimeZone more easily
+ foreach (TimeZoneInfo timeZone in timeZones)
+ {
+ WriteObject(timeZone);
+ }
+ }
+ else
+ {
+ string message = string.Format(CultureInfo.InvariantCulture,
+ TimeZoneResources.TimeZoneNameNotFound, tzname);
+#if CORECLR
+ // Because .NET Core does not currently expose the TimeZoneNotFoundException
+ // we need to throw the more generic parent exception class for the time being.
+ // This should be removed once the correct exception class is available.
+ Exception e = new Exception(message);
+#else
+ Exception e = new TimeZoneNotFoundException(message);
+#endif
+ WriteError(new ErrorRecord(e, TimeZoneHelper.TimeZoneNotFoundError,
+ ErrorCategory.InvalidArgument, "Name"));
+ }
+ }
+ }
+ else
+ {
+ // return the current system local time zone
+ WriteObject(TimeZoneInfo.Local);
+ }
+ }
+ }
+ }
+
+
+ ///
+ /// A cmdlet to set the system's local time zone.
+ ///
+ [Cmdlet(VerbsCommon.Set, "TimeZone",
+ SupportsShouldProcess = true,
+ DefaultParameterSetName = "Name",
+ HelpUri = "http://go.microsoft.com/fwlink/?LinkId=799469")]
+ [Alias("stz")]
+ public class SetTimeZoneCommand : PSCmdlet
+ {
+ #region string constants
+
+ private const string TimeZoneTarget = "Local System";
+
+ #endregion string constants
+
+ #region Parameters
+
+ ///
+ /// The name of the local time zone that the system should use.
+ ///
+ [Parameter(Mandatory = true, ParameterSetName = "Id", ValueFromPipelineByPropertyName = true)]
+ public string Id { get; set; }
+
+ ///
+ /// A TimeZoneInfo object identifying the local time zone that the system should use.
+ ///
+ [Parameter(Mandatory = true, Position = 0, ParameterSetName = "InputObject", ValueFromPipeline = true)]
+ public TimeZoneInfo InputObject { get; set; }
+
+ ///
+ /// The name of the local time zone that the system should use.
+ ///
+ [Parameter(Mandatory = true, Position = 0, ParameterSetName = "Name")]
+ public string Name { get; set; }
+
+ ///
+ /// Request return of the new local time zone as a TimeZoneInfo object
+ ///
+ [Parameter]
+ public SwitchParameter PassThru { get; set; }
+
+ #endregion Parameters
+
+ ///
+ /// Implementation of the ProcessRecord method for Get-TimeZone
+ ///
+ [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "Since Name is not a parameter of this method, it confuses FXCop. It is the appropriate value for the exception.")]
+ protected override void ProcessRecord()
+ {
+ // make sure we've got fresh data, in case the requested time zone was added
+ // to the system (registry) after our process was started
+ TimeZoneHelper.ClearCachedData();
+
+ // acquire a TimeZoneInfo if one wasn't supplied.
+ if (this.ParameterSetName.Equals("Id", StringComparison.OrdinalIgnoreCase))
+ {
+ try
+ {
+ InputObject = TimeZoneInfo.FindSystemTimeZoneById(Id);
+ }
+#if CORECLR
+ // TimeZoneNotFoundException is thrown by TimeZoneInfo, but not
+ // publicly visible (so can't be caught), so for now we're catching
+ // the parent exception time. This should be removed once the more
+ // specific exception is available.
+ catch (Exception e)
+#else
+ catch (TimeZoneNotFoundException e)
+#endif
+ {
+ ThrowTerminatingError(new ErrorRecord(
+ e,
+ TimeZoneHelper.TimeZoneNotFoundError,
+ ErrorCategory.InvalidArgument,
+ "Id"));
+ }
+ }
+ else if (this.ParameterSetName.Equals("Name", StringComparison.OrdinalIgnoreCase))
+ {
+ // lookup the time zone name and make sure we have one (and only one) match
+ TimeZoneInfo[] timeZones = TimeZoneHelper.LookupSystemTimeZoneInfoByName(Name);
+ if (0 == timeZones.Length)
+ {
+ string message = string.Format(CultureInfo.InvariantCulture,
+ TimeZoneResources.TimeZoneNameNotFound, Name);
+#if CORECLR
+ // Because .NET Core does not currently expose the TimeZoneNotFoundException
+ // we need to throw the more generic parent exception class for the time being.
+ // This should be removed once the correct exception class is available.
+ Exception e = new Exception(message);
+#else
+ Exception e = new TimeZoneNotFoundException(message);
+#endif
+ ThrowTerminatingError(new ErrorRecord(e,
+ TimeZoneHelper.TimeZoneNotFoundError,
+ ErrorCategory.InvalidArgument,
+ "Name"));
+ }
+ else if (1 < timeZones.Length)
+ {
+ string message = string.Format(CultureInfo.InvariantCulture,
+ TimeZoneResources.MultipleMatchingTimeZones, Name);
+ ThrowTerminatingError(new ErrorRecord(
+ new PSArgumentException(message, "Name"),
+ TimeZoneHelper.MultipleMatchingTimeZonesError,
+ ErrorCategory.InvalidArgument,
+ "Name"));
+ }
+ else
+ {
+ InputObject = timeZones[0];
+ }
+ }
+ else // ParameterSetName == "InputObject"
+ {
+ try
+ {
+ // a TimeZoneInfo object was supplied, so use it to make sure we can find
+ // a backing system time zone, otherwise it's an error condition
+ InputObject = TimeZoneInfo.FindSystemTimeZoneById(InputObject.Id);
+ }
+#if CORECLR
+ // TimeZoneNotFoundException is thrown by TimeZoneInfo, but not
+ // publicly visible (so can't be caught), so for now we're catching
+ // the parent exception time. This should be removed once the more
+ // specific exception is available.
+ catch (Exception e)
+#else
+ catch (TimeZoneNotFoundException e)
+#endif
+ {
+ ThrowTerminatingError(new ErrorRecord(
+ e,
+ TimeZoneHelper.TimeZoneNotFoundError,
+ ErrorCategory.InvalidArgument,
+ "InputObject"));
+ }
+ }
+
+ if (ShouldProcess(TimeZoneTarget))
+ {
+ bool acquireAccess = false;
+ try
+ {
+ // check to see if permission to set the time zone is already enabled for this process
+ if (!HasAccess)
+ {
+ // acquire permissions to set the timezone
+ SetAccessToken(true);
+ acquireAccess = true;
+ }
+ }
+ catch (Win32Exception e)
+ {
+ ThrowTerminatingError(new ErrorRecord(e,
+ TimeZoneHelper.InsufficientPermissionsError,
+ ErrorCategory.PermissionDenied, null));
+ }
+
+ try
+ {
+ // construct and populate a new DYNAMIC_TIME_ZONE_INFORMATION structure
+ NativeMethods.DYNAMIC_TIME_ZONE_INFORMATION dtzi = new NativeMethods.DYNAMIC_TIME_ZONE_INFORMATION();
+ dtzi.Bias -= (int)InputObject.BaseUtcOffset.TotalMinutes;
+ dtzi.StandardName = InputObject.StandardName;
+ dtzi.DaylightName = InputObject.DaylightName;
+ dtzi.TimeZoneKeyName = InputObject.Id;
+
+ // Request time zone transition information for the current year
+ NativeMethods.TIME_ZONE_INFORMATION tzi = new NativeMethods.TIME_ZONE_INFORMATION();
+ if (!NativeMethods.GetTimeZoneInformationForYear((ushort)DateTime.Now.Year, ref dtzi, ref tzi))
+ {
+ ThrowWin32Error();
+ }
+
+ // copy over the transition times
+ dtzi.StandardBias = tzi.StandardBias;
+ dtzi.StandardDate = tzi.StandardDate;
+ dtzi.DaylightBias = tzi.DaylightBias;
+ dtzi.DaylightDate = tzi.DaylightDate;
+
+ // set the new local time zone for the system
+ if (!NativeMethods.SetDynamicTimeZoneInformation(ref dtzi))
+ {
+ ThrowWin32Error();
+ }
+
+#if !CORECLR
+ // broadcast a WM_SETTINGCHANGE notification message to all top-level windows so that they
+ // know to update their notion of the current system time (and time zone) if applicable
+ int result = 0;
+ NativeMethods.SendMessageTimeout((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_SETTINGCHANGE,
+ (IntPtr)0, "intl", NativeMethods.SMTO_ABORTIFHUNG, 5000, ref result);
+#endif
+
+ // clear the time zone data or this PowerShell session
+ // will not recognize the new time zone settings
+ TimeZoneHelper.ClearCachedData();
+
+ if (PassThru.IsPresent)
+ {
+ // return the TimeZoneInfo object for the (new) current local time zone
+ WriteObject(TimeZoneInfo.Local);
+ }
+ }
+ catch (Win32Exception e)
+ {
+ ThrowTerminatingError(new ErrorRecord(e,
+ TimeZoneHelper.SetTimeZoneFailedError,
+ ErrorCategory.FromStdErr, null));
+ }
+ finally
+ {
+ if (acquireAccess)
+ {
+ // reset the permissions
+ SetAccessToken(false);
+ }
+ }
+ }
+ else
+ {
+ if (PassThru.IsPresent)
+ {
+ // show the user the time zone settings that would have been used.
+ WriteObject(InputObject);
+ }
+ }
+ }
+
+ #region Helper functions
+
+ ///
+ /// True if the current process has access to change the time zone setting.
+ ///
+ protected bool HasAccess
+ {
+ get
+ {
+ bool hasAccess = false;
+
+ // open the access token for the current process
+ IntPtr hToken = IntPtr.Zero;
+ IntPtr hProcess = NativeMethods.GetCurrentProcess();
+ if (!NativeMethods.OpenProcessToken(hProcess,
+ NativeMethods.TOKEN_ADJUST_PRIVILEGES | NativeMethods.TOKEN_QUERY, ref hToken))
+ {
+ ThrowWin32Error();
+ }
+
+ try
+ {
+ // setup the privileges being checked
+ NativeMethods.PRIVILEGE_SET ps = new NativeMethods.PRIVILEGE_SET()
+ {
+ PrivilegeCount = 1,
+ Control = 1,
+ Luid = 0,
+ Attributes = NativeMethods.SE_PRIVILEGE_ENABLED,
+ };
+
+ // lookup the Luid of the SeTimeZonePrivilege
+ if (!NativeMethods.LookupPrivilegeValue(null, NativeMethods.SE_TIME_ZONE_NAME, ref ps.Luid))
+ {
+ ThrowWin32Error();
+ }
+
+ // set the privilege for the open access token
+ if (!NativeMethods.PrivilegeCheck(hToken, ref ps, ref hasAccess))
+ {
+ ThrowWin32Error();
+ }
+ }
+ finally
+ {
+ NativeMethods.CloseHandle(hToken);
+ }
+
+ return (hasAccess);
+ }
+ }
+
+ ///
+ /// Set the SeTimeZonePrivilege, which controls access to the SetDynamicTimeZoneInformation API
+ ///
+ /// Set to true to enable (or false to disable) the privilege.
+ protected void SetAccessToken(bool enable)
+ {
+ // open the access token for the current process
+ IntPtr hToken = IntPtr.Zero;
+ IntPtr hProcess = NativeMethods.GetCurrentProcess();
+ if (!NativeMethods.OpenProcessToken(hProcess,
+ NativeMethods.TOKEN_ADJUST_PRIVILEGES | NativeMethods.TOKEN_QUERY, ref hToken))
+ {
+ ThrowWin32Error();
+ }
+
+ try
+ {
+ // setup the privileges being requested
+ NativeMethods.TOKEN_PRIVILEGES tp = new NativeMethods.TOKEN_PRIVILEGES()
+ {
+ PrivilegeCount = 1,
+ Luid = 0,
+ Attributes = (enable ? NativeMethods.SE_PRIVILEGE_ENABLED : 0),
+ };
+
+ // lookup the Luid of the SeTimeZonePrivilege
+ if (!NativeMethods.LookupPrivilegeValue(null, NativeMethods.SE_TIME_ZONE_NAME, ref tp.Luid))
+ {
+ ThrowWin32Error();
+ }
+
+ // set the privilege for the open access token
+ if (!NativeMethods.AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero))
+ {
+ ThrowWin32Error();
+ }
+ }
+ finally
+ {
+ NativeMethods.CloseHandle(hToken);
+ }
+ }
+
+ ///
+ /// Get the Win32 error code from GetLastError and throw an exception.
+ ///
+ protected void ThrowWin32Error()
+ {
+ int error = Marshal.GetLastWin32Error();
+ throw new Win32Exception(error);
+ }
+
+ #endregion Helper functions
+
+ #region Win32 interop helper
+
+ internal class NativeMethods
+ {
+ ///
+ /// Private constructor to prevent instantiation
+ ///
+ private NativeMethods()
+ {
+ }
+
+ #region Native DLL locations
+
+#if CORECLR
+ private const string SetDynamicTimeZoneApiDllName = "api-ms-win-core-timezone-l1-1-0.dll";
+ private const string GetTimeZoneInformationForYearApiDllName = "api-ms-win-core-timezone-l1-1-0.dll";
+ private const string GetCurrentProcessApiDllName = "api-ms-win-downlevel-kernel32-l1-1-0.dll";
+ private const string OpenProcessTokenApiDllName = "api-ms-win-downlevel-advapi32-l1-1-1.dll";
+ private const string LookupPrivilegeTokenApiDllName = "api-ms-win-downlevel-advapi32-l4-1-0.dll";
+ private const string PrivilegeCheckApiDllName = "api-ms-win-downlevel-advapi32-l1-1-1.dll";
+ private const string AdjustTokenPrivilegesApiDllName = "api-ms-win-downlevel-advapi32-l1-1-1.dll";
+ private const string CloseHandleApiDllName = "api-ms-win-downlevel-kernel32-l1-1-0.dll";
+ private const string SendMessageTimeoutApiDllName = "ext-ms-win-rtcore-ntuser-window-ext-l1-1-0.dll";
+#else
+ private const string SetDynamicTimeZoneApiDllName = "kernel32.dll";
+ private const string GetTimeZoneInformationForYearApiDllName = "kernel32.dll";
+ private const string GetCurrentProcessApiDllName = "kernel32.dll";
+ private const string OpenProcessTokenApiDllName = "advapi32.dll";
+ private const string LookupPrivilegeTokenApiDllName = "advapi32.dll";
+ private const string PrivilegeCheckApiDllName = "advapi32.dll";
+ private const string AdjustTokenPrivilegesApiDllName = "advapi32.dll";
+ private const string CloseHandleApiDllName = "kernel32.dll";
+ private const string SendMessageTimeoutApiDllName = "user32.dll";
+#endif
+
+ #endregion Native DLL locations
+
+ #region Win32 SetDynamicTimeZoneInformation imports
+
+ ///
+ /// Used to marshal win32 SystemTime structure to managed code layer
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SystemTime
+ {
+ ///
+ /// The year.
+ ///
+ [MarshalAs(UnmanagedType.U2)]
+ public short Year;
+ ///
+ /// The month.
+ ///
+ [MarshalAs(UnmanagedType.U2)]
+ public short Month;
+ ///
+ /// The day of the week.
+ ///
+ [MarshalAs(UnmanagedType.U2)]
+ public short DayOfWeek;
+ ///
+ /// The day of the month.
+ ///
+ [MarshalAs(UnmanagedType.U2)]
+ public short Day;
+ ///
+ /// The hour.
+ ///
+ [MarshalAs(UnmanagedType.U2)]
+ public short Hour;
+ ///
+ /// The minute.
+ ///
+ [MarshalAs(UnmanagedType.U2)]
+ public short Minute;
+ ///
+ /// The second.
+ ///
+ [MarshalAs(UnmanagedType.U2)]
+ public short Second;
+ ///
+ /// The millisecond.
+ ///
+ [MarshalAs(UnmanagedType.U2)]
+ public short Milliseconds;
+ }
+
+ ///
+ /// Used to marshal win32 DYNAMIC_TIME_ZONE_INFORMATION structure to managed code layer
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ public struct DYNAMIC_TIME_ZONE_INFORMATION
+ {
+ ///
+ /// The current bias for local time translation on this computer, in minutes.
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int Bias;
+ ///
+ /// A description for standard time.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
+ public string StandardName;
+ ///
+ /// A SystemTime structure that contains a date and local time when the transition from daylight saving time to standard time occurs on this operating system.
+ ///
+ public SystemTime StandardDate;
+ ///
+ /// The bias value to be used during local time translations that occur during standard time.
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int StandardBias;
+ ///
+ /// A description for daylight saving time (DST).
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
+ public string DaylightName;
+ ///
+ /// A SystemTime structure that contains a date and local time when the transition from standard time to daylight saving time occurs on this operating system.
+ ///
+ public SystemTime DaylightDate;
+ ///
+ /// The bias value to be used during local time translations that occur during daylight saving time.
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int DaylightBias;
+ ///
+ /// The name of the time zone registry key on the local computer.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x80)]
+ public string TimeZoneKeyName;
+ ///
+ /// Indicates whether dynamic daylight saving time is disabled.
+ ///
+ [MarshalAs(UnmanagedType.U1)]
+ public bool DynamicDaylightTimeDisabled;
+ }
+
+ ///
+ /// Used to marshal win32 TIME_ZONE_INFORMATION structure to managed code layer
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ public struct TIME_ZONE_INFORMATION
+ {
+ ///
+ /// The current bias for local time translation on this computer, in minutes.
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int Bias;
+ ///
+ /// A description for standard time.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
+ public string StandardName;
+ ///
+ /// A SystemTime structure that contains a date and local time when the transition from daylight saving time to standard time occurs on this operating system.
+ ///
+ public SystemTime StandardDate;
+ ///
+ /// The bias value to be used during local time translations that occur during standard time.
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int StandardBias;
+ ///
+ /// A description for daylight saving time (DST).
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
+ public string DaylightName;
+ ///
+ /// A SystemTime structure that contains a date and local time when the transition from standard time to daylight saving time occurs on this operating system.
+ ///
+ public SystemTime DaylightDate;
+ ///
+ /// The bias value to be used during local time translations that occur during daylight saving time.
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int DaylightBias;
+ }
+
+ ///
+ /// PInvoke SetDynamicTimeZoneInformation API
+ ///
+ /// A DYNAMIC_TIME_ZONE_INFORMATION structure representing the desired local time zone.
+ ///
+ [DllImport(SetDynamicTimeZoneApiDllName, SetLastError = true)]
+ public static extern bool SetDynamicTimeZoneInformation([In] ref DYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation);
+
+ [DllImport(GetTimeZoneInformationForYearApiDllName, SetLastError = true)]
+ public static extern bool GetTimeZoneInformationForYear([In] ushort wYear, [In] ref DYNAMIC_TIME_ZONE_INFORMATION pdtzi, ref TIME_ZONE_INFORMATION ptzi);
+
+ #endregion Win32 SetDynamicTimeZoneInformation imports
+
+ #region Win32 AdjustTokenPrivilege imports
+
+ ///
+ /// Definition of TOKEN_QUERY constant from Win32 API
+ ///
+ public const int TOKEN_QUERY = 0x00000008;
+
+ ///
+ /// Definition of TOKEN_ADJUST_PRIVILEGES constant from Win32 API
+ ///
+ public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
+
+ ///
+ /// Definition of SE_PRIVILEGE_ENABLED constant from Win32 API
+ ///
+ public const int SE_PRIVILEGE_ENABLED = 0x00000002;
+
+ ///
+ /// Definition of SE_TIME_ZONE_NAME constant from Win32 API
+ ///
+ public const string SE_TIME_ZONE_NAME = "SeTimeZonePrivilege"; //http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx
+
+ ///
+ /// PInvoke GetCurrentProcess API
+ ///
+ ///
+ [DllImport(GetCurrentProcessApiDllName, ExactSpelling = true)]
+ public static extern IntPtr GetCurrentProcess();
+
+ ///
+ /// PInvoke OpenProcessToken API
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport(OpenProcessTokenApiDllName, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);
+
+ ///
+ /// PInvoke LookupPrivilegeValue API
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport(LookupPrivilegeTokenApiDllName, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref long lpLuid);
+
+ ///
+ /// PInvoke PrivilegeCheck API
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport(PrivilegeCheckApiDllName, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool PrivilegeCheck(IntPtr ClientToken, ref PRIVILEGE_SET RequiredPrivileges, ref bool pfResult);
+
+
+ ///
+ /// PInvoke AdjustTokenPrivilege API
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport(AdjustTokenPrivilegesApiDllName, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivilegs,
+ ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength);
+
+ ///
+ /// PInvoke CloseHandle API
+ ///
+ ///
+ ///
+ [DllImport(CloseHandleApiDllName, ExactSpelling = true, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool CloseHandle(IntPtr hObject);
+
+ ///
+ /// Used to marshal win32 PRIVILEGE_SET structure to managed code layer
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct PRIVILEGE_SET
+ {
+ public int PrivilegeCount;
+ public int Control;
+ public long Luid;
+ public int Attributes;
+ }
+
+ ///
+ /// Used to marshal win32 TOKEN_PRIVILEGES structure to managed code layer
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct TOKEN_PRIVILEGES
+ {
+ public int PrivilegeCount;
+ public long Luid;
+ public int Attributes;
+ }
+
+ #endregion Win32 AdjustTokenPrivilege imports
+
+ #region Win32 SendMessage imports
+
+ ///
+ /// Definition of WM_SETTINGCHANGE constant from Win32 API
+ ///
+ public const int WM_SETTINGCHANGE = 0x001A;
+
+ ///
+ /// Definition of HWND_BROADCAST constant from Win32 API
+ ///
+ public const int HWND_BROADCAST = (-1);
+
+ ///
+ /// Definition of SMTO_ABORTIFHUNG constant from Win32 API
+ ///
+ public const int SMTO_ABORTIFHUNG = 0x0002;
+
+ ///
+ /// PInvoke SendMessageTimeout API
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport(SendMessageTimeoutApiDllName, SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern IntPtr SendMessageTimeout(IntPtr hWnd, int Msg, IntPtr wParam, string lParam, int fuFlags, int uTimeout, ref int lpdwResult);
+
+ #endregion Win32 SendMessage imports
+ }
+
+ #endregion Win32 interop helper
+ }
+
+
+ ///
+ /// static Helper class for working with system time zones.
+ ///
+ internal static class TimeZoneHelper
+ {
+ #region Error Ids
+
+ internal const string TimeZoneNotFoundError = "TimeZoneNotFound";
+ internal const string MultipleMatchingTimeZonesError = "MultipleMatchingTimeZones";
+ internal const string InsufficientPermissionsError = "InsufficientPermissions";
+ internal const string SetTimeZoneFailedError = "SetTimeZoneFailed";
+
+ #endregion Error Ids
+
+ ///
+ /// Clear the cached TimeZoneInfo data. Note that since TimeZoneInfo.ClearCachedData
+ /// does not exist in .NET Core there is an alternative mechanism (which doesn't seem
+ /// to affect clearing out the cache under the full desktop version of .NET)
+ ///
+ internal static void ClearCachedData()
+ {
+#if CORECLR
+ // NOTE: .NET Core does not expose the ClearCachedData method, but executing
+ // the specific ConvertTime request below should have the same effect.
+ TimeZoneInfo.ConvertTime(new DateTime(0), TimeZoneInfo.Local);
+#else
+ TimeZoneInfo.ClearCachedData();
+#endif
+ }
+
+ ///
+ /// Find the system time zone by checking first against StandardName and then,
+ /// if no matches were found, against the DaylightName.
+ ///
+ /// The name (or wildcard pattern) of the system time zone to find.
+ /// A TimeZoneInfo object array containing information about the specified system time zones.
+ internal static TimeZoneInfo[] LookupSystemTimeZoneInfoByName(string name)
+ {
+ WildcardPattern namePattern = new WildcardPattern(name, WildcardOptions.IgnoreCase);
+ List tzi = new List();
+
+ // get the available system time zones
+ ReadOnlyCollection zones = TimeZoneInfo.GetSystemTimeZones();
+
+ // check against the standard and daylight names for each TimeZoneInfo
+ foreach (TimeZoneInfo zone in zones)
+ {
+ if (namePattern.IsMatch(zone.StandardName) || namePattern.IsMatch(zone.DaylightName))
+ {
+ tzi.Add(zone);
+ }
+ }
+
+ return (tzi.ToArray());
+ }
+ }
+}
+
+#endif
diff --git a/src/Microsoft.PowerShell.Commands.Management/map.json b/src/Microsoft.PowerShell.Commands.Management/map.json
index 3e0402ac79..95929d0b19 100644
--- a/src/Microsoft.PowerShell.Commands.Management/map.json
+++ b/src/Microsoft.PowerShell.Commands.Management/map.json
@@ -85,6 +85,8 @@
"monad/src/cimSupport/cmdletization/cim/cimCmdletInvocationContext.cs": "cimSupport/cmdletization/cim/cimCmdletInvocationContext.cs",
"monad/src/commands/management/ParsePathCommand.cs": "commands/management/ParsePathCommand.cs",
"monad/src/commands/management/CopyPropertyCommand.cs": "commands/management/CopyPropertyCommand.cs",
+ "monad/src/commands/management/TimeZoneCommands.cs": "commands/management/TimeZoneCommands.cs",
+ "monad/src/commands/management/resources/TimeZoneResources.resx": "resources/TimeZoneResources.resx",
"monad/src/cimSupport/cmdletization/cim/TerminatingErrorTracker.cs": "cimSupport/cmdletization/cim/TerminatingErrorTracker.cs",
"monad/src/cimSupport/cmdletization/cim/cimChildJobBase.cs": "cimSupport/cmdletization/cim/cimChildJobBase.cs"
}
diff --git a/src/Microsoft.PowerShell.Commands.Management/project.json b/src/Microsoft.PowerShell.Commands.Management/project.json
index 8f42c2664f..72c13be937 100644
--- a/src/Microsoft.PowerShell.Commands.Management/project.json
+++ b/src/Microsoft.PowerShell.Commands.Management/project.json
@@ -10,6 +10,14 @@
"allowUnsafe": true
},
+ "configurations": {
+ "Linux": {
+ "buildOptions": {
+ "define": [ "LINUX" ]
+ }
+ }
+ },
+
"dependencies": {
"Microsoft.PowerShell.Security": "1.0.0-*"
},
@@ -24,7 +32,6 @@
"commands/management/ClearRecycleBinCommand.cs",
"commands/management/ControlPanelItemCommand.cs",
"commands/management/CommitTransactionCommand.cs",
- "commands/management/Computer.cs",
"commands/management/Eventlog.cs",
"commands/management/GetClipboardCommand.cs",
"commands/management/GetTransactionCommand.cs",
@@ -42,11 +49,25 @@
"commands/management/WebServiceProxy.cs",
"singleshell/installer/MshManagementMshSnapin.cs"
]
+ },
+ "embed": {
+ "exclude": [
+ "resources/EventlogResources.resx",
+ "resources/TransactionResources.resx",
+ "resources/WebServiceResources.resx",
+ "resources/HotFixResources.resx",
+ "resources/ControlPanelResources.resx",
+ "resources/WmiResources.resx",
+ "resources/ManagementMshSnapInResources.resx",
+ "resources/ClearRecycleBinResources.resx",
+ "resources/ClipboardResources.resx"
+ ]
}
},
"dependencies": {
"System.ServiceProcess.ServiceController": "4.1.0",
- "System.Net.NameResolution": "4.0.0"
+ "System.Net.NameResolution": "4.0.0",
+ "System.Net.Ping": "4.0.0"
}
},
"net451": {
@@ -64,6 +85,11 @@
"exclude": [
"commands/management/ControlPanelItemCommand.cs"
]
+ },
+ "embed": {
+ "exclude": [
+ "resources/ControlPanelResources.resx"
+ ]
}
}
}
diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/TimeZoneResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/TimeZoneResources.resx
new file mode 100644
index 0000000000..1118c6c567
--- /dev/null
+++ b/src/Microsoft.PowerShell.Commands.Management/resources/TimeZoneResources.resx
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Cannot set the local time zone because the name '{0}' resolves to multiple entries.
+
+
+ The time zone name '{0}' was not found on the local computer.
+
+
\ No newline at end of file
diff --git a/src/Microsoft.PowerShell.Commands.Utility/project.json b/src/Microsoft.PowerShell.Commands.Utility/project.json
index 3086becd3c..5025f4d3b9 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/project.json
+++ b/src/Microsoft.PowerShell.Commands.Utility/project.json
@@ -41,12 +41,16 @@
"commands/utility/ShowCommand/ShowCommandProxy.cs",
"commands/utility/WebCmdlet/BasicHtmlWebResponseObject.cs",
"commands/utility/WebCmdlet/ContentHelper.cs",
+ "commands/utility/WebCmdlet/FormObject.cs",
+ "commands/utility/WebCmdlet/FormObjectCollection.cs",
"commands/utility/WebCmdlet/HtmlWebResponseObject.cs",
"commands/utility/WebCmdlet/InvokeRestMethodCommand.cs",
"commands/utility/WebCmdlet/InvokeWebRequestCommand.cs",
"commands/utility/WebCmdlet/JsonObjectTypeResolver.cs",
"commands/utility/WebCmdlet/PSUserAgent.cs",
"commands/utility/WebCmdlet/StreamHelper.cs",
+ "commands/utility/WebCmdlet/WebCmdletElementCollection.cs",
+ "commands/utility/WebCmdlet/WebRequestMethod.cs",
"commands/utility/WebCmdlet/WebRequestPSCmdlet.cs",
"commands/utility/WebCmdlet/WebRequestSession.cs",
"commands/utility/WebCmdlet/WebResponseHelper.cs",
@@ -56,6 +60,21 @@
"commands/utility/update-list.cs",
"singleshell/installer/MshUtilityMshSnapin.cs"
]
+ },
+ "embed": {
+ "exclude": [
+ "resources/FormatAndOut_out_gridview.resx",
+ "resources/ImplicitRemotingStrings.resx",
+ "resources/UtilityMshSnapinResources.resx",
+ "resources/OutPrinterDisplayStrings.resx",
+ "resources/TraceCommandStrings.resx",
+ "resources/UpdateListStrings.resx",
+ "resources/SendMailMessageStrings.resx",
+ "resources/ConvertFromStringResources.resx",
+ "resources/ConvertStringResources.resx",
+ "resources/FlashExtractStrings.resx",
+ "resources/ImmutableStrings.resx"
+ ]
}
},
"imports": [ "dnxcore50", "portable-net45+win8" ],
diff --git a/src/Microsoft.PowerShell.ConsoleHost/project.json b/src/Microsoft.PowerShell.ConsoleHost/project.json
index 297cdfa423..7b8e57207e 100644
--- a/src/Microsoft.PowerShell.ConsoleHost/project.json
+++ b/src/Microsoft.PowerShell.ConsoleHost/project.json
@@ -68,7 +68,18 @@
"netstandard1.6": {
"imports": [ "dnxcore50", "portable-net45+win8" ],
"buildOptions": {
- "define": [ "CORECLR", "PORTABLE" ]
+ "define": [ "CORECLR" ],
+ "compile": {
+ "exclude": [
+ "singleshell/installer/EngineInstaller.cs",
+ "singleshell/installer/MshHostMshSnapin.cs"
+ ]
+ },
+ "embed": {
+ "exclude": [
+ "resources/HostMshSnapinResources.resx"
+ ]
+ }
},
"dependencies": {
"Newtonsoft.Json": "7.0.1",
diff --git a/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/EngineInstaller.cs b/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/EngineInstaller.cs
index 3e7971855f..bcc5467e64 100644
--- a/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/EngineInstaller.cs
+++ b/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/EngineInstaller.cs
@@ -1,4 +1,3 @@
-#if !PORTABLE
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
@@ -87,4 +86,3 @@ namespace Microsoft.PowerShell
}
}
}
-#endif
diff --git a/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/MshHostMshSnapin.cs b/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/MshHostMshSnapin.cs
index 0b9a0f65e2..615d5dfc26 100644
--- a/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/MshHostMshSnapin.cs
+++ b/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/MshHostMshSnapin.cs
@@ -1,4 +1,3 @@
-#if !PORTABLE
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
@@ -93,4 +92,3 @@ namespace Microsoft.PowerShell
}
}
}
-#endif
diff --git a/src/Microsoft.PowerShell.Security/map.json b/src/Microsoft.PowerShell.Security/map.json
index 28288e6dd5..5a7c57668f 100644
--- a/src/Microsoft.PowerShell.Security/map.json
+++ b/src/Microsoft.PowerShell.Security/map.json
@@ -17,5 +17,6 @@
"monad/src/security/ExecutionPolicyCommands.cs": "security/ExecutionPolicyCommands.cs",
"monad/src/security/CmsCommands.cs": "security/CmsCommands.cs",
"monad/src/security/AclCommands.cs": "security/AclCommands.cs",
+ "monad/src/security/CatalogCommands.cs": "security/CatalogCommands.cs",
"monad/src/security/resources/SignatureCommands.resx": "resources/SignatureCommands.resx"
}
diff --git a/src/Microsoft.PowerShell.Security/project.json b/src/Microsoft.PowerShell.Security/project.json
index 1ad6818a8d..f1795bc00d 100644
--- a/src/Microsoft.PowerShell.Security/project.json
+++ b/src/Microsoft.PowerShell.Security/project.json
@@ -10,6 +10,14 @@
"allowUnsafe": true
},
+ "configurations": {
+ "Linux": {
+ "buildOptions": {
+ "define": [ "LINUX" ]
+ }
+ }
+ },
+
"dependencies": {
"System.Management.Automation": "1.0.0-*"
},
@@ -25,6 +33,13 @@
"security/CmsCommands.cs",
"singleshell/installer/MshSecurityMshSnapin.cs"
]
+ },
+ "embed": {
+ "exclude": [
+ "resources/CertificateCommands.resx",
+ "resources/CmsCommands.resx",
+ "resources/SecurityMshSnapinResources.resx"
+ ]
}
}
},
diff --git a/src/Microsoft.PowerShell.Security/security/CatalogCommands.cs b/src/Microsoft.PowerShell.Security/security/CatalogCommands.cs
new file mode 100644
index 0000000000..76656c6f4f
--- /dev/null
+++ b/src/Microsoft.PowerShell.Security/security/CatalogCommands.cs
@@ -0,0 +1,286 @@
+#if !LINUX
+
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using System;
+using System.Management.Automation;
+using Dbg=System.Management.Automation.Diagnostics;
+using System.Collections;
+using System.IO;
+using System.Management.Automation.Provider;
+using System.Runtime.InteropServices;
+using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.PowerShell.Commands
+{
+ ///
+ /// Defines the base class from which all catalog commands are derived.
+ ///
+ public abstract class CatalogCommandsBase : PSCmdlet
+ {
+ ///
+ /// Path of folder/file to generate or validate the catalog file
+ ///
+ [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByPath")]
+ public string CatalogFilePath
+ {
+ get
+ {
+ return catalogFilePath;
+ }
+ set
+ {
+ catalogFilePath = value;
+ }
+ }
+ private string catalogFilePath;
+
+ ///
+ /// Path of folder/file to generate or validate the catalog file
+ ///
+ [Parameter(Position = 1, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByPath")]
+ public string[] Path
+ {
+ get
+ {
+ return path;
+ }
+ set
+ {
+ path = value;
+ }
+ }
+ private string[] path;
+ //
+ // name of this command
+ //
+ string commandName;
+
+ ///
+ /// Initializes a new instance of the CatalogCommandsBase class,
+ /// using the given command name.
+ ///
+ ///
+ /// The name of the command.
+ ///
+ protected CatalogCommandsBase(string name) : base()
+ {
+ commandName = name;
+ }
+
+ private CatalogCommandsBase() : base() {}
+
+ ///
+ /// Processes records from the input pipeline.
+ /// For each input object, the command either generate the Catalog or
+ /// Validates the existing Catalog
+ ///
+ protected override void ProcessRecord()
+ {
+ //
+ // this cannot happen as we have specified the Path
+ // property to be mandatory parameter
+ //
+ Dbg.Assert((CatalogFilePath != null) && (CatalogFilePath.Length > 0),
+ "CatalogCommands: Param binder did not bind catalogFilePath");
+
+ Collection paths = new Collection();
+
+ if (Path != null)
+ {
+ foreach (string p in Path)
+ {
+ foreach (PathInfo tempPath in SessionState.Path.GetResolvedPSPathFromPSPath(p))
+ {
+ paths.Add(tempPath.ProviderPath);
+ }
+ }
+ }
+
+ string drive = null;
+
+ // resolve catalog destination Path
+ if (!SessionState.Path.IsPSAbsolute(catalogFilePath, out drive) && !System.IO.Path.IsPathRooted(catalogFilePath))
+ {
+ catalogFilePath = SessionState.Path.GetUnresolvedProviderPathFromPSPath(catalogFilePath);
+ }
+
+ PerformAction(paths, catalogFilePath);
+ }
+
+ ///
+ /// Performs the action i.e. Generate or Validate the Windows Catalog File
+ ///
+ ///
+ /// The name of the Folder or file on which to perform the action.
+ ///
+ ///
+ /// Path to Catalog
+ ///
+ protected abstract void PerformAction(Collection path, string catalogFilePath);
+ }
+
+ ///
+ /// Defines the implementation of the 'New-FileCatalog' cmdlet.
+ /// This cmdlet generates the catalog for File or Folder
+ ///
+ [Cmdlet(VerbsCommon.New, "FileCatalog", SupportsShouldProcess = true, DefaultParameterSetName = "ByPath",
+ HelpUri = "http://go.microsoft.com/fwlink/?LinkId=786749")]
+ [OutputType(typeof(FileInfo))]
+ public sealed class NewFileCatalogCommand : CatalogCommandsBase
+ {
+ ///
+ /// Initializes a new instance of the New-FileCatalog class.
+ ///
+ public NewFileCatalogCommand() : base("New-FileCatalog") { }
+
+ ///
+ /// Catalog version
+ ///
+ [Parameter()]
+ public int CatalogVersion
+ {
+ get
+ {
+ return catalogVersion;
+ }
+ set
+ {
+ catalogVersion = value;
+ }
+ }
+
+ // Based on the Catalog version we will decide which hashing Algorithm to use
+ private int catalogVersion = 1;
+
+ ///
+ /// Generate the Catalog for the Path
+ ///
+ ///
+ /// File or Folder Path
+ ///
+ ///
+ /// Path to Catalog
+ ///
+ ///
+ /// True if able to Create Catalog or else False
+ ///
+ protected override void PerformAction(Collection path,string catalogFilePath)
+ {
+ if (path.Count == 0)
+ {
+ // if user has not provided the path use current directory to generate catalog
+ path.Add(SessionState.Path.CurrentFileSystemLocation.Path);
+ }
+
+ FileInfo catalogFileInfo = new FileInfo(catalogFilePath);
+
+ // If Path points to the expected cat file make sure
+ // parent Directory exists other wise CryptoAPI fails to create a .cat file
+ if (catalogFileInfo.Extension.Equals(".cat", StringComparison.Ordinal))
+ {
+ System.IO.Directory.CreateDirectory(catalogFileInfo.Directory.FullName);
+ }
+ else
+ {
+ // This only creates Directory if it does not exists, Append a default name
+ System.IO.Directory.CreateDirectory(catalogFilePath);
+ catalogFilePath = System.IO.Path.Combine(catalogFilePath, "catalog.cat");
+ }
+
+ FileInfo catalogFile = CatalogHelper.GenerateCatalog(this, path, catalogFilePath, catalogVersion);
+
+ if (catalogFile != null)
+ {
+ WriteObject(catalogFile);
+ }
+ }
+ }
+
+ ///
+ /// Defines the implementation of the 'Test-FileCatalog' cmdlet.
+ /// This cmdlet validaes the Intgerity of catalog
+ ///
+ [Cmdlet(VerbsDiagnostic.Test, "FileCatalog", SupportsShouldProcess = true, DefaultParameterSetName = "ByPath",
+ HelpUri = "http://go.microsoft.com/fwlink/?LinkId=786750")]
+ [OutputType(typeof(CatalogValidationStatus))]
+ [OutputType(typeof(CatalogInformation))]
+ public sealed class TestFileCatalogCommand : CatalogCommandsBase
+ {
+ ///
+ /// Initializes a new instance of the New-FileCatalog class.
+ ///
+ public TestFileCatalogCommand() : base("Test-FileCatalog") { }
+
+ ///
+ ///
+ [Parameter()]
+ public SwitchParameter Detailed
+ {
+ get { return detailed; }
+ set { detailed = value; }
+ }
+ private bool detailed = false;
+
+ ///
+ /// Patterns used to exclude files from DiskPaths and Catalog
+ ///
+ [Parameter()]
+ public string[] FilesToSkip
+ {
+ get
+ {
+ return filesToSkip;
+ }
+ set
+ {
+ filesToSkip = value;
+ this.excludedPatterns = new WildcardPattern[filesToSkip.Length];
+ for (int i = 0; i < filesToSkip.Length; i++)
+ {
+ this.excludedPatterns[i] = WildcardPattern.Get(filesToSkip[i], WildcardOptions.IgnoreCase);
+ }
+ }
+ }
+ string[] filesToSkip = null;
+ internal WildcardPattern[] excludedPatterns = null;
+
+ ///
+ /// Validate the Integrity of given Catalog
+ ///
+ ///
+ /// File or Folder Path
+ ///
+ ///
+ /// Path to Catalog
+ ///
+ ///
+ /// True if able to Validate the Catalog and its not tampered or else False
+ ///
+ protected override void PerformAction(Collection path, string catalogFilePath)
+ {
+ if (path.Count == 0)
+ {
+ // if user has not provided the path use the path of catalog file itself.
+ path.Add(new FileInfo(catalogFilePath).Directory.FullName);
+ }
+
+ CatalogInformation catalogInfo = CatalogHelper.ValidateCatalog(this, path, catalogFilePath, excludedPatterns);
+
+ if (detailed)
+ {
+ WriteObject(catalogInfo);
+ }
+ else
+ {
+ WriteObject(catalogInfo.Status);
+ }
+ }
+ }
+
+}
+
+#endif
diff --git a/src/System.Management.Automation/map.json b/src/System.Management.Automation/map.json
index 686728edb2..75cdaf758b 100644
--- a/src/System.Management.Automation/map.json
+++ b/src/System.Management.Automation/map.json
@@ -743,6 +743,8 @@
"monad/src/namespaces/Buffer.cs": "namespaces/Buffer.cs",
"monad/src/engine/interpreter/LocalAccess.cs": "engine/interpreter/LocalAccess.cs",
"monad/src/security/SecuritySupport.cs": "security/SecuritySupport.cs",
+ "monad/src/security/CatalogHelper.cs": "security/CatalogHelper.cs",
+ "monad/src/engine/resources/CatalogStrings.resx": "resources/CatalogStrings.resx",
"monad/src/engine/remoting/client/remotepipeline.cs": "engine/remoting/client/remotepipeline.cs",
"monad/src/engine/remoting/common/remotingexceptions.cs": "engine/remoting/common/remotingexceptions.cs",
"monad/src/help/BaseCommandHelpInfo.cs": "help/BaseCommandHelpInfo.cs",
diff --git a/src/System.Management.Automation/project.json b/src/System.Management.Automation/project.json
index 4ea22be7f2..fad49661a2 100644
--- a/src/System.Management.Automation/project.json
+++ b/src/System.Management.Automation/project.json
@@ -125,6 +125,12 @@
"utils/perfCounters/CounterSetRegistrarBase.cs",
"utils/perfCounters/PSPerfCountersMgr.cs"
]
+ },
+ "embed": {
+ "exclude": [
+ "resources/CoreMshSnapinResources.resx",
+ "resources/ErrorPackageRemoting.resx"
+ ]
}
},
"dependencies": {
@@ -164,6 +170,11 @@
}
},
"net451": {
+ "buildOptions": {
+ "embed": {
+ "exclude": [ "resources/CoreClrStubResources.resx" ]
+ }
+ },
"frameworkAssemblies": {
"System.Runtime": "",
"System.Xml": "",
diff --git a/src/System.Management.Automation/resources/CatalogStrings.resx b/src/System.Management.Automation/resources/CatalogStrings.resx
new file mode 100644
index 0000000000..a9d5706f5e
--- /dev/null
+++ b/src/System.Management.Automation/resources/CatalogStrings.resx
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Unable to generate catalog defintion file.
+
+
+ Adding file '{0}' to the catalog. Relative path of file in catalog is '{1}'.
+
+
+ Skipping validating file {0} from catalog.
+
+
+ Found file {0} in the catalog with hash of {1}.
+
+
+ Paths for catalog contains multiple files with same relative path {0}.
+
+
+ Found file {0} on disk with hash of {1}.
+
+
+ Skipping validating file {0} from path.
+
+
+ Unable to acquire a handle to a catalog administrator context for a given hash algorithm {0}.
+
+
+ Unable to create the hash for file {0}.
+
+
+ Unable to open catalog file {0}.
+
+
+ Catalog version is not valid. We only support catalog version {0} and version {1}.
+
+
+ Unable to open catalog defintion file.
+
+
+ Found multiple entries of file member {0} in the catalog.
+
+
+ Unable to find file name or path for catalog member {0}.
+
+
+ Unable to find file {0} to hash.
+
+
+ Unable to read file {0} to calculate its hash.
+
+
\ No newline at end of file
diff --git a/src/System.Management.Automation/security/CatalogHelper.cs b/src/System.Management.Automation/security/CatalogHelper.cs
new file mode 100644
index 0000000000..d3b65f5566
--- /dev/null
+++ b/src/System.Management.Automation/security/CatalogHelper.cs
@@ -0,0 +1,848 @@
+#if !LINUX
+
+/********************************************************************++
+Copyright (c) Microsoft Corporation. All rights reserved.
+--********************************************************************/
+
+using Dbg = System.Management.Automation;
+using System;
+using System.Text;
+using System.Security.Cryptography;
+using System.Collections.Generic;
+using System.Collections;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Management.Automation.Internal;
+using System.Management.Automation.Provider;
+using System.Management.Automation.Security;
+using System.Runtime.InteropServices;
+using DWORD = System.UInt32;
+
+namespace System.Management.Automation
+{
+
+ ///
+ /// Defines the possible status when validating integrity of catalog
+ ///
+ public enum CatalogValidationStatus
+ {
+ ///
+ /// Status when catalog is not tampered.
+ ///
+ Valid,
+
+ ///
+ /// Status when catalog is tampered.
+ ///
+ ValidationFailed
+ }
+
+ ///
+ /// Object returned by Catalog Cmdlets
+ ///
+ public class CatalogInformation
+ {
+ ///
+ /// status of catalog
+ ///
+ public CatalogValidationStatus Status { get; set; }
+
+ ///
+ /// Hash Algorithm used to calculate the hashes of files in Catalog
+ ///
+ public string HashAlgorithm { get; set; }
+
+ ///
+ /// Dictionary mapping files relative paths to their hash values found from Catalog
+ ///
+ public Dictionary CatalogItems { get; set;}
+
+ ///
+ /// Dictionary mapping files relative paths to their hash values
+ ///
+ public Dictionary PathItems { get; set;}
+
+ ///
+ /// Signature for the catalog
+ ///
+ public Signature Signature { get; set; }
+ }
+
+ ///
+ /// Helper functions for Windows Catalog functionality
+ ///
+ internal static class CatalogHelper
+ {
+
+ // Catalog Version is (0X100 = 256) for Catalog Version 1
+ private static int catalogVersion1 = 256;
+
+ // Catalog Version is (0X200 = 512) for Catalog Version 2
+ private static int catalogVersion2 = 512;
+
+ // Hash Algorithms supported by Windows Catalog
+ private static string HashAlgorithmSHA1 = "SHA1";
+ private static string HashAlgorithmSHA256 = "SHA256";
+ private static PSCmdlet _cmdlet = null;
+
+ ///
+ /// Find out the Version of Catalog by reading its Meta data. We can have either version 1 or version 2 catalog
+ ///
+ /// Handle to open catalog file
+ /// Version of the catalog
+ private static int GetCatalogVersion(IntPtr catalogHandle)
+ {
+ int catalogVersion = -1;
+
+ IntPtr catalogData = NativeMethods.CryptCATStoreFromHandle(catalogHandle);
+ NativeMethods.CRYPTCATSTORE catalogInfo = ClrFacade.PtrToStructure(catalogData);
+
+ if (catalogInfo.dwPublicVersion == catalogVersion2)
+ {
+ catalogVersion = 2;
+ }
+ // One Windows 7 this API sent version information as decimal 1 not hex (0X100 = 256)
+ // so we are checking for that value as well. Reason we are not checking for version 2 above in
+ // this scenario because catalog verion 2 is not supported on win7.
+ else if ((catalogInfo.dwPublicVersion == catalogVersion1) || (catalogInfo.dwPublicVersion == 1))
+ {
+ catalogVersion = 1;
+ }
+ else
+ {
+ // catalog version we don't understand
+ Exception exception = new InvalidOperationException(StringUtil.Format(CatalogStrings.UnKnownCatalogVersion,
+ catalogVersion1.ToString("X"),
+ catalogVersion2.ToString("X")));
+
+ ErrorRecord errorRecord = new ErrorRecord(exception, "UnKnownCatalogVersion", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ }
+ return catalogVersion;
+ }
+
+ ///
+ /// HashAlgorithm used by the Catalog. It is based on the version of Catalog.
+ ///
+ /// Path of the output catalog file
+ /// Version of the catalog
+ private static string GetCatalogHashAlgorithm(int catalogVersion)
+ {
+ string hashAlgorithm = string.Empty;
+
+ if (catalogVersion == 1)
+ {
+ hashAlgorithm = HashAlgorithmSHA1;
+ }
+ else if (catalogVersion == 2)
+ {
+ hashAlgorithm = HashAlgorithmSHA256;
+ }
+ else
+ {
+ // version we don't understand
+ Exception exception = new InvalidOperationException(StringUtil.Format(CatalogStrings.UnKnownCatalogVersion,
+ "1.0",
+ "2.0"));
+
+ ErrorRecord errorRecord = new ErrorRecord(exception, "UnKnownCatalogVersion", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ }
+ return hashAlgorithm;
+ }
+
+ ///
+ /// Generate the Catalog Defintion File representing files and folders
+ ///
+ ///
+ /// Path of expected output .cdf file
+ ///
+ /// Path of the output catalog file
+ ///
+ /// Path of the catalog definiton file
+ ///
+ /// Version of catalog
+ ///
+ /// hash method used to generate hashes for the Catalog
+ /// HashSet for the relative Path for files in Catalog
+ internal static string GenerateCDFFile(Collection Path, string catalogFilePath, string cdfFilePath, int catalogVersion, string hashAlgorithm)
+ {
+ HashSet relativePaths = new HashSet();
+
+ string cdfHeaderContent = string.Empty;
+ string cdfFilesContent = string.Empty;
+ int catAttributeCount = 0;
+
+ // First create header and files section for the catalog then write in file
+ cdfHeaderContent += "[CatalogHeader]" + Environment.NewLine;
+ cdfHeaderContent += @"Name=" + catalogFilePath + Environment.NewLine;
+ cdfHeaderContent += "CatalogVersion=" + catalogVersion + Environment.NewLine;
+ cdfHeaderContent += "HashAlgorithms=" + hashAlgorithm + Environment.NewLine;
+ cdfFilesContent += "[CatalogFiles]" + Environment.NewLine;
+
+ foreach (string catalogFile in Path)
+ {
+ if (System.IO.Directory.Exists(catalogFile))
+ {
+ var directoryItems = Directory.EnumerateFiles(catalogFile, "*.*", SearchOption.AllDirectories);
+ foreach (string fileItem in directoryItems)
+ {
+ ProcessFileToBeAddedInCatalogDefinitionFile(new FileInfo(fileItem), new DirectoryInfo(catalogFile), ref relativePaths, ref cdfHeaderContent, ref cdfFilesContent, ref catAttributeCount);
+ }
+ }
+ else if (System.IO.File.Exists(catalogFile))
+ {
+ ProcessFileToBeAddedInCatalogDefinitionFile(new FileInfo(catalogFile), null, ref relativePaths, ref cdfHeaderContent, ref cdfFilesContent, ref catAttributeCount);
+ }
+ }
+
+ using (System.IO.StreamWriter fileWriter = new System.IO.StreamWriter(new FileStream(cdfFilePath, FileMode.Create)))
+ {
+ fileWriter.WriteLine(cdfHeaderContent);
+ fileWriter.WriteLine();
+ fileWriter.WriteLine(cdfFilesContent);
+ }
+ return cdfFilePath;
+ }
+
+ ///
+ /// Get file attribute (Relative path in our case) from catalog
+ ///
+ /// file to hash
+ /// directory information about file needed to calculate relative file path.
+ /// working set of relative paths of all files.
+ /// content to be added in CatalogHeader section of cdf File
+ /// content to be added in CatalogFiles section of cdf File
+ /// indictaing the current no of catalog header level attributes
+ /// void
+ internal static void ProcessFileToBeAddedInCatalogDefinitionFile(FileInfo fileToHash, DirectoryInfo dirInfo, ref HashSet relativePaths, ref string cdfHeaderContent, ref string cdfFilesContent, ref int catAttributeCount)
+ {
+ string relativePath = string.Empty;
+
+ if (dirInfo != null)
+ {
+ //Relative path of the file is the path inside the containing folder excluding folder Name
+ relativePath = fileToHash.FullName.Substring(dirInfo.FullName.Length).TrimStart('\\');
+ }
+ else
+ {
+ relativePath = fileToHash.Name;
+ }
+
+ if (!relativePaths.Contains(relativePath))
+ {
+ relativePaths.Add(relativePath);
+ if (fileToHash.Length != 0)
+ {
+ cdfFilesContent += "" + fileToHash.FullName + "=" + fileToHash.FullName + Environment.NewLine;
+ cdfFilesContent += "" + fileToHash.FullName + "ATTR1=0x10010001:FilePath:" + relativePath + Environment.NewLine;
+ }
+ else
+ {
+ // zero length files are added as catalog level attributes because they can not be hashed
+ cdfHeaderContent += "CATATTR" + (++catAttributeCount) + "=0x10010001:FilePath:" + relativePath + Environment.NewLine;
+ }
+ }
+ else
+ {
+ // If Files have same relative paths we can not distinguish them for
+ // Validation. So failing.
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFilesRelativePath, relativePath)), "FoundDuplicateFilesRelativePath", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ }
+ }
+ ///
+ /// Generate the Catalog file for Input Catalog Defintion File
+ ///
+ ///
+ /// Path to the Input .cdf file
+ internal static void GenerateCatalogFile(string cdfFilePath)
+ {
+ string pwszFilePath = cdfFilePath;
+ NativeMethods.CryptCATCDFOpenCallBack catOpenCallBack = new NativeMethods.CryptCATCDFOpenCallBack(ParseErrorCallback);
+
+ // Open CDF File
+ IntPtr resultCDF = NativeMethods.CryptCATCDFOpen(pwszFilePath, catOpenCallBack);
+
+ // navigate CDF header and files sections
+ if (resultCDF != IntPtr.Zero)
+ {
+ // First navigate all catalog level attributes entries first, they represent zero size files
+ IntPtr catalogAttr = IntPtr.Zero;
+ do
+ {
+ catalogAttr = NativeMethods.CryptCATCDFEnumCatAttributes(resultCDF, catalogAttr, catOpenCallBack);
+
+ if (catalogAttr != IntPtr.Zero)
+ {
+ string filePath = ProcessFilePathAttributeInCatalog(catalogAttr);
+ _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.AddFileToCatalog, filePath, filePath));
+ }
+ } while (catalogAttr != IntPtr.Zero);
+
+ // navigate all the files hash entries in the .cdf file
+ IntPtr memberInfo = IntPtr.Zero;
+ try
+ {
+ IntPtr memberFile = IntPtr.Zero;
+ NativeMethods.CryptCATCDFEnumMembersByCDFTagExErrorCallBack memberCallBack = new NativeMethods.CryptCATCDFEnumMembersByCDFTagExErrorCallBack(ParseErrorCallback);
+ string fileName = string.Empty;
+ do
+ {
+ memberFile = NativeMethods.CryptCATCDFEnumMembersByCDFTagEx(resultCDF, memberFile, memberCallBack, ref memberInfo, true, IntPtr.Zero);
+ fileName = Marshal.PtrToStringUni(memberFile);
+
+ if (!String.IsNullOrEmpty(fileName))
+ {
+
+ IntPtr memberAttr = IntPtr.Zero;
+ string fileRelativePath = String.Empty;
+ do
+ {
+ memberAttr = NativeMethods.CryptCATCDFEnumAttributesWithCDFTag(resultCDF, memberFile, memberInfo, memberAttr, memberCallBack);
+
+ if (memberAttr != IntPtr.Zero)
+ {
+ fileRelativePath = ProcessFilePathAttributeInCatalog(memberAttr);
+ if (!String.IsNullOrEmpty(fileRelativePath))
+ {
+ // Found the attribute we are looking for
+ // Filename we read from the above API has appended to its name as per CDF file tags convention
+ // Truncating that Information from the string.
+ string itemName = fileName.Substring(6);
+ _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.AddFileToCatalog, itemName, fileRelativePath));
+ break;
+ }
+ }
+ } while (memberAttr != IntPtr.Zero);
+ }
+ } while (fileName != null);
+ }
+ finally
+ {
+ NativeMethods.CryptCATCDFClose(resultCDF);
+ }
+ }
+ else
+ {
+ // If we are not able to open CDF file we can not continue generating catalog
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(CatalogStrings.UnableToOpenCatalogDefinitionFile), "UnableToOpenCatalogDefinitionFile", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ }
+ }
+
+ ///
+ /// To generate Catalog for the folder
+ ///
+ ///
+ /// Path to folder or File
+ /// Catalog File Path
+ /// Catalog File Path
+ /// Instance of cmdlet calling this method
+ /// true if able to generate .cat file or false
+ internal static FileInfo GenerateCatalog(PSCmdlet cmdlet, Collection Path, string catalogFilePath, int catalogVersion)
+ {
+ _cmdlet = cmdlet;
+ string hashAlgorithm = GetCatalogHashAlgorithm(catalogVersion);
+
+ if (!String.IsNullOrEmpty(hashAlgorithm))
+ {
+ // Generate Path for Catalog Defintion File
+ string cdfFilePath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());
+ cdfFilePath = cdfFilePath + ".cdf";
+ try
+ {
+ cdfFilePath = GenerateCDFFile(Path, catalogFilePath, cdfFilePath, catalogVersion, hashAlgorithm);
+
+ if (!File.Exists(cdfFilePath))
+ {
+ // If we are not able to generate catalog defintion file we can not continue generating catalog
+ // throw PSTraceSource.NewInvalidOperationException("catalog", CatalogStrings.CatalogDefintionFileNotGenerated);
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(CatalogStrings.CatalogDefintionFileNotGenerated), "CatalogDefintionFileNotGenerated", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ }
+
+ GenerateCatalogFile(cdfFilePath);
+ if (File.Exists(catalogFilePath))
+ {
+ return new FileInfo(catalogFilePath);
+ }
+ }
+ finally
+ {
+ File.Delete(cdfFilePath);
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Get file attribute (Relative path in our case) from catalog
+ ///
+ /// Pointer to current attribute of catalog member.
+ /// value of the attribute
+ internal static string ProcessFilePathAttributeInCatalog(IntPtr memberAttrInfo)
+ {
+ string relativePath = string.Empty;
+
+ NativeMethods.CRYPTCATATTRIBUTE currentMemberAttr = ClrFacade.PtrToStructure(memberAttrInfo);
+
+ // check if this is the attribute we are looking for
+ // catalog generated other way not using New-FileCatalog can have attributes we don't understand
+ if (currentMemberAttr.pwszReferenceTag.Equals("FilePath", StringComparison.OrdinalIgnoreCase))
+ {
+ // find the size for the current attribute value and then allocate buffer and copy from byte array
+ int attrValueSize = (int)currentMemberAttr.cbValue;
+ byte[] attrValue = new byte[attrValueSize];
+ Marshal.Copy(currentMemberAttr.pbValue, attrValue, 0, attrValueSize);
+ relativePath = System.Text.Encoding.Unicode.GetString(attrValue);
+ relativePath = relativePath.TrimEnd('\0');
+ }
+
+ return relativePath;
+ }
+
+ ///
+ /// Make a hash for the file
+ ///
+ ///
+ /// Path of the file
+ /// Used to calculate Hash
+ /// HashValue for the file
+ internal static string CalculateFileHash(string filePath, string hashAlgorithm)
+ {
+ string hashValue = string.Empty;
+ IntPtr catAdmin = IntPtr.Zero;
+
+ // To get handle to the hash algorithm to be used to calculate hashes
+ if (!NativeMethods.CryptCATAdminAcquireContext2(ref catAdmin, IntPtr.Zero, hashAlgorithm, IntPtr.Zero, 0))
+ {
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToAcquireHashAlgorithmContext, hashAlgorithm)), "UnableToAcquireHashAlgorithmContext", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ }
+
+ DWORD GENERIC_READ = 0x80000000;
+ DWORD OPEN_EXISTING = 3;
+ IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
+
+ // Open the file that is to be hashed for reading and get its handle
+ IntPtr fileHandle = NativeMethods.CreateFile(filePath, GENERIC_READ, 0, 0 , OPEN_EXISTING, 0, IntPtr.Zero);
+ if (fileHandle != INVALID_HANDLE_VALUE)
+ {
+ try
+ {
+ DWORD hashBufferSize = 0;
+ IntPtr hashBuffer = IntPtr.Zero;
+
+ // Call first time to get the size of expected buffer to hold new hash value
+ if (!NativeMethods.CryptCATAdminCalcHashFromFileHandle2(catAdmin, fileHandle, ref hashBufferSize, hashBuffer, 0))
+ {
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, filePath)), "UnableToCreateFileHash", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ }
+
+ int size = (int)hashBufferSize;
+ hashBuffer = Marshal.AllocHGlobal(size);
+ try
+ {
+ // Call second time to actually get the hash value
+ if (!NativeMethods.CryptCATAdminCalcHashFromFileHandle2(catAdmin, fileHandle, ref hashBufferSize, hashBuffer, 0))
+ {
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, filePath)), "UnableToCreateFileHash", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ }
+
+ byte[] hashBytes = new byte[size];
+ Marshal.Copy(hashBuffer, hashBytes, 0, size);
+ hashValue = BitConverter.ToString(hashBytes).Replace("-", "");
+
+ }
+ finally
+ {
+ if (hashBuffer != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(hashBuffer);
+ }
+ }
+ }
+ finally
+ {
+ NativeMethods.CryptCATAdminReleaseContext(catAdmin, 0);
+ NativeMethods.CloseHandle(fileHandle);
+ }
+ }
+ else
+ {
+ // If we are not able to open file that is to be hashed we can not continue with catalog validation
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToReadFileToHash, filePath)), "UnableToReadFileToHash", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ }
+ return hashValue;
+ }
+
+ ///
+ /// Make list of hashes for given Catalog File
+ ///
+ /// Path to the folder having catalog file
+ ///
+ /// The version of input catalog we read from catalog meta data after opening it.
+ /// Dictionary mapping files relative paths to HashValues
+ internal static Dictionary GetHashesFromCatalog(string catalogFilePath, WildcardPattern[] excludedPatterns,out int catalogVersion)
+ {
+ IntPtr resultCatalog = NativeMethods.CryptCATOpen(catalogFilePath, 0, IntPtr.Zero, 1, 0);
+ IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
+ Dictionary catalogHashes = new Dictionary(StringComparer.CurrentCultureIgnoreCase);
+ catalogVersion = 0;
+
+ if (resultCatalog != INVALID_HANDLE_VALUE)
+ {
+ try
+ {
+ IntPtr catAttrInfo = IntPtr.Zero;
+
+ // First traverse all catalog level attributes to get information about zero size file.
+ do
+ {
+ catAttrInfo = NativeMethods.CryptCATEnumerateCatAttr(resultCatalog, catAttrInfo);
+
+ // If we found attribute it is a file information retrieve its relative path
+ // and add it to catalog hash collection if its not in excluded files criteria
+ if (catAttrInfo != IntPtr.Zero)
+ {
+ string relativePath = ProcessFilePathAttributeInCatalog(catAttrInfo);
+ if (!String.IsNullOrEmpty(relativePath))
+ {
+ ProcessCatalogFile(relativePath, string.Empty, excludedPatterns, ref catalogHashes);
+ }
+ }
+ } while (catAttrInfo != IntPtr.Zero);
+
+ catalogVersion = GetCatalogVersion(resultCatalog);
+
+ IntPtr memberInfo = IntPtr.Zero;
+ // Next Navigate all members in Catalog files and get their relative paths and hashes
+ do
+ {
+ memberInfo = NativeMethods.CryptCATEnumerateMember(resultCatalog, memberInfo);
+ if (memberInfo != IntPtr.Zero)
+ {
+ NativeMethods.CRYPTCATMEMBER currentMember = ClrFacade.PtrToStructure(memberInfo);
+ NativeMethods.SIP_INDIRECT_DATA pIndirectData = ClrFacade.PtrToStructure(currentMember.pIndirectData);
+
+ // For Catalog version 2 CryptoAPI puts hashes of file attributes(relative path in our case) in Catalog as well
+ // We validate those along with file hashes so we are skipping duplicate entries
+ if (!((catalogVersion == 2) && (pIndirectData.DigestAlgorithm.pszObjId.Equals(new Oid("SHA1").Value, StringComparison.OrdinalIgnoreCase))))
+ {
+ string relativePath = String.Empty;
+ IntPtr memberAttrInfo = IntPtr.Zero;
+ do
+ {
+ memberAttrInfo = NativeMethods.CryptCATEnumerateAttr(resultCatalog, memberInfo, memberAttrInfo);
+
+ if (memberAttrInfo != IntPtr.Zero)
+ {
+ relativePath = ProcessFilePathAttributeInCatalog(memberAttrInfo);
+ if (!String.IsNullOrEmpty(relativePath))
+ {
+ break;
+ }
+ }
+ }
+ while (memberAttrInfo != IntPtr.Zero);
+
+ // If we did not find any Relative Path for the item in catalog we should quit
+ // This catalog must not be valid for our use as catalogs generated using New-FileCatalog
+ // always contains relative file Paths
+ if (String.IsNullOrEmpty(relativePath))
+ {
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToOpenCatalogFile, catalogFilePath)), "UnableToOpenCatalogFile", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ }
+
+ ProcessCatalogFile(relativePath, currentMember.pwszReferenceTag, excludedPatterns, ref catalogHashes);
+ }
+ }
+ } while (memberInfo != IntPtr.Zero);
+ }
+ finally
+ {
+ NativeMethods.CryptCATClose(resultCatalog);
+ }
+ }
+ else
+ {
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToOpenCatalogFile, catalogFilePath)), "UnableToOpenCatalogFile", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ }
+ return catalogHashes;
+ }
+
+ ///
+ /// Process file in path for its relative paths
+ ///
+ /// relative path of file found in catalog
+ /// hash of file found in catalog.
+ /// skip file from validation if it matches these patterns
+ /// collection of hashes of catalog
+ /// void
+ internal static void ProcessCatalogFile(string relativePath, string fileHash, WildcardPattern[] excludedPatterns, ref Dictionary catalogHashes)
+ {
+ // Found the attribute we are looking for
+ _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.FoundFileHashInCatlogItem, relativePath, fileHash));
+
+ // Only add the file for validation if it does not meet exclusion criteria
+ if (!CheckExcludedCriteria((new FileInfo(relativePath)).Name, excludedPatterns))
+ {
+ // Add relativePath mapping to hashvalue for each file
+ catalogHashes.Add(relativePath, fileHash);
+ }
+ else
+ {
+ // Verbose about skipping file from catalog
+ _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.SkipValidationOfCatalogFile, relativePath));
+ }
+ }
+ ///
+ /// Process file in path for its relative paths
+ ///
+ /// file to hash
+ /// directory information about file needed to calculate relative file path.
+ /// Used to calculate Hash
+ /// skip file if it matches these patterns
+ /// collection of hashes of files
+ /// void
+ internal static void ProcessPathFile(FileInfo fileToHash, DirectoryInfo dirInfo, string hashAlgorithm, WildcardPattern[] excludedPatterns, ref Dictionary fileHashes)
+ {
+ string relativePath = string.Empty;
+ string exclude = string.Empty;
+
+ if (dirInfo != null)
+ {
+ //Relative path of the file is the path inside the containing folder excluding folder Name
+ relativePath = fileToHash.FullName.Substring(dirInfo.FullName.Length).TrimStart('\\');
+ exclude = fileToHash.Name;
+ }
+ else
+ {
+ relativePath = fileToHash.Name;
+ exclude = relativePath;
+ }
+
+ if (!CheckExcludedCriteria(exclude, excludedPatterns))
+ {
+ string fileHash = string.Empty;
+
+ if(fileToHash.Length != 0)
+ {
+ fileHash = CalculateFileHash(fileToHash.FullName, hashAlgorithm);
+ }
+
+ if (!fileHashes.ContainsKey(relativePath))
+ {
+ fileHashes.Add(relativePath, fileHash);
+ _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.FoundFileInPath, relativePath, fileHash));
+ }
+ else
+ {
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFilesRelativePath, relativePath)), "FoundDuplicateFilesRelativePath", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ }
+ }
+ else
+ {
+ // Verbose about skipping file from path
+ _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.SkipValidationOfPathFile, relativePath));
+ }
+ }
+
+ ///
+ /// Generate the hashes of all the files in given folder
+ ///
+ ///
+ /// Path to folder or File
+ /// catalog file path it should be skipped when calculating the hashes
+ /// Used to calculate Hash
+ ///
+ /// Dictionary mapping file relative paths to hashes.
+ internal static Dictionary CalculateHashesFromPath(Collection folderPaths, string catalogFilePath, string hashAlgorithm, WildcardPattern[] excludedPatterns)
+ {
+ // Create a HashTable of file Hashes
+ Dictionary fileHashes = new Dictionary(StringComparer.CurrentCultureIgnoreCase);
+
+ foreach (string folderPath in folderPaths)
+ {
+ if (System.IO.Directory.Exists(folderPath))
+ {
+ var directoryItems = Directory.EnumerateFiles(folderPath, "*.*", SearchOption.AllDirectories);
+ foreach (string fileItem in directoryItems)
+ {
+ // if its the catalog file we are validating we will skip it
+ if (String.Equals(fileItem, catalogFilePath, StringComparison.OrdinalIgnoreCase))
+ continue;
+
+ ProcessPathFile(new FileInfo(fileItem), new DirectoryInfo(folderPath), hashAlgorithm, excludedPatterns, ref fileHashes);
+ }
+ }
+ else if (System.IO.File.Exists(folderPath))
+ {
+ ProcessPathFile(new FileInfo(folderPath), null , hashAlgorithm, excludedPatterns, ref fileHashes);
+ }
+ }
+ return fileHashes;
+ }
+
+ ///
+ /// Compare Dictionary objects
+ ///
+ ///
+ /// Hashes extracted from Catalog
+ ///
+ /// Hashes created from folders path
+ ///
+ /// True if both collections are same
+ internal static bool CompareDictionaries(Dictionary catalogItems, Dictionary pathItems)
+ {
+ bool Status = true;
+
+ List relativePathsFromFolder = pathItems.Keys.ToList();
+ List relativePathsFromCatalog = catalogItems.Keys.ToList();
+
+ // Find entires those are not in both list lists. These should be empty lists for success
+ // Hashes in Catalog should be exact similar to the ones from folder
+ List relativePathsNotInFolder = relativePathsFromFolder.Except(relativePathsFromCatalog, StringComparer.CurrentCultureIgnoreCase).ToList();
+ List relativePathsNotInCatalog = relativePathsFromCatalog.Except(relativePathsFromFolder, StringComparer.CurrentCultureIgnoreCase).ToList();
+
+ //Found extra hashes in Folder
+ if ((relativePathsNotInFolder.Count != 0) || (relativePathsNotInCatalog.Count != 0))
+ {
+ Status = false;
+ }
+
+ foreach (KeyValuePair item in catalogItems)
+ {
+ string catalogHashValue = (string)catalogItems[item.Key];
+ if (pathItems.ContainsKey(item.Key))
+ {
+ string folderHashValue = (string)pathItems[item.Key];
+ if (folderHashValue.Equals(catalogHashValue))
+ {
+ continue;
+ }
+ else
+ {
+ Status = false;
+ }
+ }
+ }
+ return Status;
+ }
+ ///
+ /// To Validate the Integrity of Catalog
+ ///
+ ///
+ /// Folder for which catalog is created
+ /// File Name of the Catalog
+ ///
+ /// Instance of cmdlet calling this method
+ /// Information about Catalog
+ internal static CatalogInformation ValidateCatalog(PSCmdlet cmdlet, Collection catalogFolders, String catalogFilePath, WildcardPattern[] excludedPatterns)
+ {
+ _cmdlet = cmdlet;
+ int catalogVersion = 0;
+ Dictionary catalogHashes = GetHashesFromCatalog(catalogFilePath, excludedPatterns, out catalogVersion);
+ string hashAlgorithm = GetCatalogHashAlgorithm(catalogVersion);
+
+ if (!String.IsNullOrEmpty(hashAlgorithm))
+ {
+ Dictionary fileHashes = CalculateHashesFromPath(catalogFolders, catalogFilePath, hashAlgorithm, excludedPatterns);
+ CatalogInformation catalog = new CatalogInformation();
+ catalog.CatalogItems = catalogHashes;
+ catalog.PathItems = fileHashes;
+ bool status = CompareDictionaries(catalogHashes, fileHashes);
+ if (status == true)
+ {
+ catalog.Status = CatalogValidationStatus.Valid;
+ }
+ else
+ {
+ catalog.Status = CatalogValidationStatus.ValidationFailed;
+ }
+ catalog.HashAlgorithm = hashAlgorithm;
+ catalog.Signature = SignatureHelper.GetSignature(catalogFilePath, null);
+ return catalog;
+ }
+ return null;
+ }
+
+ ///
+ /// Check if file meets the skip validation criteria
+ ///
+ ///
+ ///
+ /// True if match is found else false
+ internal static bool CheckExcludedCriteria(string filename, WildcardPattern[] excludedPatterns)
+ {
+ if (excludedPatterns != null)
+ {
+ foreach (WildcardPattern patternItem in excludedPatterns)
+ {
+ if (patternItem.IsMatch(filename))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ ///
+ /// Call back when error is thrown by catalog API's
+ ///
+ private static void ParseErrorCallback(DWORD dwErrorArea, DWORD dwLocalError, string pwszLine)
+ {
+ switch (dwErrorArea)
+ {
+ case NativeConstants.CRYPTCAT_E_AREA_HEADER: break;
+ case NativeConstants.CRYPTCAT_E_AREA_MEMBER: break;
+ case NativeConstants.CRYPTCAT_E_AREA_ATTRIBUTE: break;
+ default: break;
+ }
+ switch (dwLocalError)
+ {
+ case NativeConstants.CRYPTCAT_E_CDF_MEMBER_FILE_PATH:
+ {
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToFindFileNameOrPathForCatalogMember, pwszLine)), "UnableToFindFileNameOrPathForCatalogMember", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ break;
+ }
+ case NativeConstants.CRYPTCAT_E_CDF_MEMBER_INDIRECTDATA:
+ {
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, pwszLine)), "UnableToCreateFileHash", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ break;
+ }
+ case NativeConstants.CRYPTCAT_E_CDF_MEMBER_FILENOTFOUND:
+ {
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToFindFileToHash, pwszLine)), "UnableToFindFileToHash", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ break;
+ }
+ case NativeConstants.CRYPTCAT_E_CDF_BAD_GUID_CONV: break;
+ case NativeConstants.CRYPTCAT_E_CDF_ATTR_TYPECOMBO: break;
+ case NativeConstants.CRYPTCAT_E_CDF_ATTR_TOOFEWVALUES: break;
+ case NativeConstants.CRYPTCAT_E_CDF_UNSUPPORTED: break;
+ case NativeConstants.CRYPTCAT_E_CDF_DUPLICATE:
+ {
+ ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFileMemberInCatalog, pwszLine)), "FoundDuplicateFileMemberInCatalog", ErrorCategory.InvalidOperation, null);
+ _cmdlet.ThrowTerminatingError(errorRecord);
+ break;
+ }
+ case NativeConstants.CRYPTCAT_E_CDF_TAGNOTFOUND: break;
+ default: break;
+ }
+ }
+ }
+}
+
+#endif
diff --git a/test/map.json b/test/map.json
new file mode 100644
index 0000000000..9a1176fc13
--- /dev/null
+++ b/test/map.json
@@ -0,0 +1,4 @@
+{
+ "monad/tests/MultiMachine/Tests/Commands/Cmdlets/Microsoft.PowerShell.Management/Pester.Commands.Cmdlets.Management.TimeZone.Tests.ps1": "powershell/Modules/Microsoft.PowerShell.Management/TimeZone.Tests.ps1",
+ "monad/tests/MultiMachine/Tests/Commands/Cmdlets/Microsoft.PowerShell.Security/Pester.Commands.Cmdlets.CatalogTests.ps1": "powershell/Modules/Microsoft.PowerShell.Security/FileCatalog.Tests.ps1"
+}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Clear-EventLog.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Clear-EventLog.Tests.ps1
index 014b3126a7..9fff93ab30 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Management/Clear-EventLog.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Clear-EventLog.Tests.ps1
@@ -1,4 +1,4 @@
-if ($IsWindows) {
+if ($IsWindows -and !$IsCore) {
#check to see whether we're running as admin in Windows...
$windowsIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$windowsPrincipal = new-object 'Security.Principal.WindowsPrincipal' $windowsIdentity
@@ -28,4 +28,4 @@ if ($IsWindows) {
} catch {$_.FullyQualifiedErrorId | Should Be "Microsoft.PowerShell.Commands.ClearEventLogCommand"}
}
}
-}
\ No newline at end of file
+}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-EventLog.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-EventLog.Tests.ps1
index d10fcc5943..ebeb00f480 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-EventLog.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-EventLog.Tests.ps1
@@ -1,4 +1,4 @@
-if ($IsWindows) {
+if ($IsWindows -and !$IsCore) {
#check to see whether we're running as admin in Windows...
$windowsIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$windowsPrincipal = new-object 'Security.Principal.WindowsPrincipal' $windowsIdentity
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/New-EventLog.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/New-EventLog.Tests.ps1
index 8ef155616e..30955ffd06 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Management/New-EventLog.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Management/New-EventLog.Tests.ps1
@@ -1,4 +1,4 @@
-if ($IsWindows) {
+if ($IsWindows -and !$IsCore) {
#check to see whether we're running as admin in Windows...
$windowsIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$windowsPrincipal = new-object 'Security.Principal.WindowsPrincipal' $windowsIdentity
@@ -44,4 +44,4 @@
$result.EventID | Should be 4
}
}
-}
\ No newline at end of file
+}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Remove-EventLog.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Remove-EventLog.Tests.ps1
index 78a6a9ae9f..26787c206d 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Management/Remove-EventLog.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Remove-EventLog.Tests.ps1
@@ -1,4 +1,4 @@
-if ($IsWindows) {
+if ($IsWindows -and !$IsCore) {
#check to see whether we're running as admin in Windows...
$windowsIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$windowsPrincipal = new-object 'Security.Principal.WindowsPrincipal' $windowsIdentity
@@ -28,4 +28,4 @@
} catch {$_.FullyQualifiedErrorId | Should Be "System.InvalidOperationException,Microsoft.PowerShell.Commands.GetEventLogCommand"}
}
}
-}
\ No newline at end of file
+}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/TimeZone.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/TimeZone.Tests.ps1
new file mode 100644
index 0000000000..ae2b340e50
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Management/TimeZone.Tests.ps1
@@ -0,0 +1,227 @@
+# This is a Pester test suite to validate the cmdlets in the TimeZone module
+#
+# Copyright (c) Microsoft Corporation, 2016
+
+<#
+ --------------------------------------
+ Script Notes: opportunities for improving this test script
+ --------------------------------------
+ Localization
+ Many of the tests below looking-up timezones by Name do not support localization.
+ That is, the current tests use us english versions of StandardName and DaylighName for tests.
+
+ ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms725481.aspx
+ [snippet] Both StandardName and DaylightName are localized according to the current user default UI language.
+#>
+
+if ($IsWindows) {
+
+function Assert-ListsSame
+{
+ param([object[]] $expected, [object[]] $observed )
+ $compResult = Compare-Object $observed $expected | Select-Object -ExpandProperty InputObject
+ if ($compResult)
+ {
+ $observedList = ([string]::Join("|",$observed))
+ $expectedList = ([string]::Join("|",$expected))
+ $observedList | Should Be $expectedList
+ }
+}
+
+Describe "Get-Timezone test case no switches" -tags 'BVT' {
+ It "Call without ListAvailable switch returns current TimeZoneInfo" {
+ $observed = (Get-TimeZone).Id
+ $expected = ([System.TimeZoneInfo]::Local).Id
+ $observed -eq $expected | Should Be $true
+ }
+}
+
+Describe "Get-Timezone test cases" -tags 'Innerloop','RI' {
+
+ It "Call without ListAvailable switch returns an object of type TimeZoneInfo" {
+ $result = (Get-TimeZone).GetType().Name
+ $result | Should Be "TimeZoneInfo"
+ }
+
+ It "Call WITH ListAvailable switch returns ArrayList of TimeZoneInfo objects where the list is greater than 0 item" {
+ $list = Get-TimeZone -ListAvailable
+ $list.Count -gt 0 | Should Be $true
+
+ $list.GetType().Name | Should Be "Object[]"
+ $list[0].GetType().Name | Should Be "TimeZoneInfo"
+ }
+
+ It "Call with ListAvailable switch returns a list containing TimeZoneInfo.Local" {
+ $observedIdList = Get-TimeZone -ListAvailable | select -ExpandProperty Id
+ $oneExpectedId = ([System.TimeZoneInfo]::Local).Id
+ $observedIdList -contains $oneExpectedId | Should Be $true
+ }
+
+ It "Call with ListAvailable switch returns a list containing one returned by Get-TimeZone" {
+ $observedIdList = Get-TimeZone -ListAvailable | select -ExpandProperty Id
+ $oneExpectedId = (Get-TimeZone).Id
+ $observedIdList -contains $oneExpectedId | Should Be $true
+ }
+
+ It "Call Get-TimeZone using ID param and single item" {
+ (Get-TimeZone -Id "Cape Verde Standard Time").Id -eq "Cape Verde Standard Time" | Should Be $true
+ }
+
+ It "Call Get-TimeZone using ID param and multiple items" {
+ $idList = @("Cape Verde Standard Time","Morocco Standard Time","Azores Standard Time")
+ $result = (Get-TimeZone -Id $idList).Id
+ Assert-ListsSame $result $idList
+ }
+
+ It "Call Get-TimeZone using ID param and multiple items, where first and third are invalid ids - expect error" {
+ $null = Get-TimeZone -Id @("Cape Verde Standard","Morocco Standard Time","Azores Standard") `
+ -ErrorVariable errVar -ErrorAction SilentlyContinue
+ $errVar.Count -eq 2 | Should Be $true
+ $errVar[0].FullyQualifiedErrorID | Should Be "TimeZoneNotFound,Microsoft.PowerShell.Commands.GetTimeZoneCommand"
+ }
+
+ It "Call Get-TimeZone using ID param and multiple items, one is wild card but error action ignore works as expected" {
+ $result = Get-TimeZone -Id @("Cape Verde Standard Time","Morocco Standard Time","*","Azores Standard Time") `
+ -ErrorAction SilentlyContinue | % Id
+ $expectedIdList = @("Cape Verde Standard Time","Morocco Standard Time","Azores Standard Time")
+ Assert-ListsSame $expectedIdList $result
+ }
+
+ It "Call Get-TimeZone using Name param and singe item" {
+ $timezoneList = Get-TimeZone -ListAvailable
+ $timezoneName = $timezoneList[0].StandardName
+ $observed = Get-TimeZone -Name $timezoneName
+ $observed.StandardName -eq $timezoneName | Should Be $true
+ }
+
+ It "Call Get-TimeZone using Name param with wild card" {
+ $result = (Get-TimeZone -Name "Pacific*").Id
+ $expectedIdList = @("Pacific Standard Time (Mexico)","Pacific Standard Time","Pacific SA Standard Time")
+ Assert-ListsSame $expectedIdList $result
+ }
+
+ It "Verify that alias 'gtz' exists" {
+ (Get-Alias -Name "gtz").Name | Should Be "gtz"
+ }
+
+ It "Call Get-TimeZone Name parameter from pipeline by value " {
+ $result = ("Pacific*" | Get-TimeZone).Id
+ $expectedIdList = @("Pacific Standard Time (Mexico)","Pacific Standard Time","Pacific SA Standard Time")
+ Assert-ListsSame $expectedIdList $result
+ }
+
+ It "Call Get-TimeZone Id parameter from pipeline by ByPropertyName" {
+ $timezoneList = Get-TimeZone -ListAvailable
+ $timezone = $timezoneList[0]
+ $observed = $timezone | Get-TimeZone
+ $observed.StandardName -eq $timezone.StandardName | Should Be $true
+ }
+}
+
+Describe "Set-Timezone test case: call by single Id" -tags 'BVT' {
+ $originalTimeZoneId
+ BeforeAll {
+ $originalTimeZoneId = (Get-TimeZone).Id
+ }
+ AfterAll {
+ Set-TimeZone -ID $originalTimeZoneId
+ }
+
+ It "Call Set-TimeZone by Id" {
+ $origTimeZoneID = (Get-TimeZone).Id
+ $timezoneList = Get-TimeZone -ListAvailable
+ $testTimezone = $null
+ foreach($timezone in $timezoneList)
+ {
+ if ($timezone.Id -ne $origTimeZoneID)
+ {
+ $testTimezone = $timezone
+ break
+ }
+ }
+ Set-TimeZone -Id $testTimezone.Id
+ $observed = Get-TimeZone
+ $testTimezone.Id -eq $observed.Id | Should Be $true
+ }
+}
+
+Describe "Set-Timezone test cases" -tags 'Innerloop','RI' {
+ $originalTimeZoneId
+ BeforeAll {
+ $originalTimeZoneId = (Get-TimeZone).Id
+ }
+ AfterAll {
+ Set-TimeZone -ID $originalTimeZoneId
+ }
+
+ It "Call Set-TimeZone with invalid Id" {
+ $exception = $null
+ try { Set-TimeZone -Id "zzInvalidID" } catch { $exception = $_ }
+ $exception.FullyQualifiedErrorID | Should Be "TimeZoneNotFound,Microsoft.PowerShell.Commands.SetTimeZoneCommand"
+ }
+
+ It "Call Set-TimeZone by Name" {
+ $origTimeZoneName = (Get-TimeZone).StandardName
+ $timezoneList = Get-TimeZone -ListAvailable
+ $testTimezone = $null
+ foreach($timezone in $timezoneList)
+ {
+ if ($timezone.StandardName -ne $origTimeZoneName)
+ {
+ $testTimezone = $timezone
+ break
+ }
+ }
+ Set-TimeZone -Name $testTimezone.StandardName
+ $observed = Get-TimeZone
+ $testTimezone.StandardName -eq $observed.StandardName | Should Be $true
+ }
+
+ It "Call Set-TimeZone with invalid Name" {
+ $exception = $null
+ try { Set-TimeZone -Name "zzINVALID_Name" } catch { $exception = $_ }
+ $exception.FullyQualifiedErrorID | Should Be "TimeZoneNotFound,Microsoft.PowerShell.Commands.SetTimeZoneCommand"
+ }
+
+ It "Verify that alias 'stz' exists" {
+ (Get-Alias -Name "stz").Name | Should Be "stz"
+ }
+
+ It "Call Set-TimeZone from pipeline input object of type TimeZoneInfo" {
+ $origTimeZoneID = (Get-TimeZone).Id
+ $timezoneList = Get-TimeZone -ListAvailable
+ $testTimezone = $null
+ foreach($timezone in $timezoneList)
+ {
+ if ($timezone.Id -ne $origTimeZoneID)
+ {
+ $testTimezone = $timezone
+ break
+ }
+ }
+
+ $testTimezone | Set-TimeZone
+ $observed = Get-TimeZone
+ $observed.ID -eq $testTimezone.Id | Should Be $true
+ }
+
+ It "Call Set-TimeZone from pipeline input object of type TimeZoneInfo, verify supports whatif" {
+ $origTimeZoneID = (Get-TimeZone).Id
+ $timezoneList = Get-TimeZone -ListAvailable
+ $testTimezone = $null
+ foreach($timezone in $timezoneList)
+ {
+ if ($timezone.Id -ne $origTimeZoneID)
+ {
+ $testTimezone = $timezone
+ break
+ }
+ }
+
+ Set-TimeZone -Id $testTimezone.Id -WhatIf > $null
+ $observed = Get-TimeZone
+ $observed.Id -eq $origTimeZoneID | Should Be $true
+ }
+}
+
+}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/FileCatalog.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Security/FileCatalog.Tests.ps1
new file mode 100644
index 0000000000..ec08f7f902
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Security/FileCatalog.Tests.ps1
@@ -0,0 +1,381 @@
+# This is a Pester test suite to validate the New-FileCatalog & Test-FileCatalog cmdlets on PowerShell Core.
+#
+# Copyright (c) Microsoft Corporation, 2016
+#
+
+if ($IsWindows) {
+
+$script:catalogPath = ""
+
+Describe "Test suite for NewFileCatalogAndTestFileCatalogCmdlets" -Tags "Innerloop","BVT" {
+
+ #compare two hashtables
+ function CompareHashTables
+ {
+ param
+ (
+ $hashTable1,
+ $hashTable2
+ )
+
+ foreach ($key in $hashTable1.keys)
+ {
+ $keyValue1 = $hashTable1["$key"]
+ if($hashTable2.ContainsKey($key))
+ {
+ $keyValue2 = $hashTable2["$key"]
+ $keyValue1 | Should Be $keyValue2
+ }
+ else
+ {
+ throw "Failed to find the file $keyValue1 for $key in Hashtable"
+ }
+ }
+ }
+
+ BeforeAll {
+ $testDataPath = "$PSScriptRoot\TestData\CatalogTestData"
+ }
+
+ Context "NewAndTestCatalogTests PositiveTestCases when validation Succeeds" {
+
+ It "NewFileCatalogFolder" {
+
+ $sourcePath = Join-Path $testDataPath 'UserConfigProv\DSCResources\scriptdsc'
+ $catalogPath = "$testDataPath\NewFileCatalogFolder.cat"
+
+ try
+ {
+ $null = New-FileCatalog -Path $sourcePath -CatalogFilePath $catalogPath -CatalogVersion 1.0
+ $result = Test-FileCatalog -Path $sourcePath -CatalogFilePath $catalogPath -Detailed
+ }
+ finally
+ {
+ Remove-Item $catalogPath -Force -ErrorAction SilentlyContinue
+ }
+
+ # Validate result properties
+ $result.Status | Should Be "Valid"
+ $result.Signature.Status | Should Be "NotSigned"
+ $result.HashAlgorithm | Should Be "SHA1"
+ }
+
+ It "NewFileCatalogFolderWithSubFolders" {
+
+ $sourcePath = Join-Path $testDataPath 'UserConfigProv'
+ # use non existant Path for the directory when .cat file name is specified
+ $catalogPath = "$testDataPath\OutPutCatalog\NewFileCatalogFolderWithSubFolders.cat"
+
+ try
+ {
+ $null = New-FileCatalog -Path $sourcePath -CatalogFilePath $catalogPath
+ $result = Test-FileCatalog -Path $sourcePath -CatalogFilePath $catalogPath -Detailed
+ }
+ finally
+ {
+ Remove-Item "$sourcePath\OutPutCatalog" -Force -ErrorAction SilentlyContinue -Recurse
+ }
+
+ # Validate result properties
+ $result.Status | Should Be "Valid"
+ $result.Signature.Status | Should Be "NotSigned"
+ $result.HashAlgorithm | Should Be "SHA1"
+ }
+
+ It "NewFileCatalogWithSingleFile" {
+
+ $sourcePath = Join-Path $testDataPath '\CatalogTestFile1.mof'
+ # use existant Path for the directory when .cat file name is not specified
+ $catalogPath = $testDataPath
+ try
+ {
+ $null = New-FileCatalog -Path $sourcePath -CatalogFilePath $catalogPath
+ $result = Test-FileCatalog -Path $sourcePath -CatalogFilePath ($catalogPath + "\catalog.cat")
+ }
+ finally
+ {
+ Remove-Item "$catalogPath\catalog.cat" -Force -ErrorAction SilentlyContinue
+ }
+
+ # Validate result properties
+ $result | Should Be "Valid"
+ }
+
+ It "NewFileCatalogForFilesThatDoNotSupportEmbeddedSignatures" {
+
+ $expectedPathsAndHashes = @{ "TestImage.gif" = "B0E4B9F0BB21284AA0AF0D525C913420AD73DA6A" ;
+ "TestFileCatalog.txt" = "925834D22A8AEB8E0A5EAFABC739F8AFAAD3E490" }
+
+ # use non existant Path for the directory when .cat file name is not specified
+ $catalogPath = "$testDataPath\OutPutCatalog"
+
+ try
+ {
+ $null = New-FileCatalog -Path "$testDataPath\TestImage.gif","$testDataPath\TestFileCatalog.txt" -CatalogFilePath $catalogPath -CatalogVersion 1.0
+ $result = Test-FileCatalog -Path "$testDataPath\TestImage.gif","$testDataPath\TestFileCatalog.txt" -CatalogFilePath ($catalogPath + "\catalog.cat") -Detailed
+ }
+ finally
+ {
+ Remove-Item "$catalogPath" -Force -ErrorAction SilentlyContinue -Recurse
+ }
+
+ $result.Status | Should Be "Valid"
+ $result.CatalogItems.Count | Should Be 2
+ $result.PathItems.Count | Should Be 2
+ CompareHashTables $result.CatalogItems $result.PathItems
+ CompareHashTables $result.CatalogItems $expectedPathsAndHashes
+ }
+
+ It "NewFileCatalogWithMutipleFoldersAndFiles" -Skip:$true {
+
+ $expectedPathsAndHashes = @{
+ "UserConfigProv.psd1" = "748E5486814051DA3DFB79FE8964152727213248" ;
+ "DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.schema.mof" ="F7CAB050E32CF0C9B2AC2807C4F24D31EFCC8B61";
+ "dscresources\UserConfigProviderModVersion3\UserConfigProviderModVersion3.psm1" = "F9DD6B02C7BD0FB98A25BE0D41210B2A2333E139";
+ "DSCResources\scriptdsc\scriptDSC.schema.psm1"= "CDBAF85FEDE2E0CD09B1AEA0532010CEFCECBC12";
+ "DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1" = "7599777B85B60377B1F3E492C817190090A754A7"
+ "DSCResources\scriptdsc\scriptdsc.psd1"= "CDDC68AF9B863760A14031772DC9ADDAFD209D80";
+ "DSCResources\UserConfigProviderModVersion3\UserConfigProviderModVersion3.schema.mof" ="AFEB46104F506FC64CAB4B0B2A9C6C50622B487A";
+ "DSCResources\UserConfigProviderModVersion2\UserConfigProviderModVersion2.psm1"= "60CB9C8AEDA7A64127D34361ED4F30DEAFE37022";
+ "DSCResources\UserConfigProviderModVersion2\UserConfigProviderModVersion2.schema.mof" = "E33FBFEA28E9A8FBA793FBC3D8015BCC9A10944B";
+ "CatalogTestFile1.mof" = "083B0953D0D70FFF62710F0356FEB86BCE327FE7";
+ "CatalogTestFile2.xml" = "E73BB7A0DD9FAC6A8182F67B750D9CA3094490F1" }
+
+ $catalogPath = "$env:TEMP\NewFileCatalogWithMutipleFoldersAndFiles.cat"
+ $catalogDataPath = @("$testDataPath\UserConfigProv\","$testDataPath\CatalogTestFile1.mof","$testDataPath\CatalogTestFile2.xml")
+
+ try
+ {
+ $null =New-FileCatalog -Path $catalogDataPath -CatalogFilePath $catalogPath -CatalogVersion 1.0
+ $result = Test-FileCatalog -Path $catalogDataPath -CatalogFilePath $catalogPath -Detailed
+ }
+ finally
+ {
+ Remove-Item "$catalogPath" -Force -ErrorAction SilentlyContinue
+ }
+
+ $result.Status | Should Be "Valid"
+ $result.Signature.Status | Should Be "NotSigned"
+ $result.HashAlgorithm | Should Be "SHA1"
+ $result.CatalogItems.Count | Should Be 11
+ $result.PathItems.Count | Should Be 11
+
+ CompareHashTables $result.CatalogItems $result.PathItems
+ CompareHashTables $result.CatalogItems $expectedPathsAndHashes
+ }
+
+ It "NewFileCatalogVersion2WithMutipleFoldersAndFiles" -Skip:$true {
+
+ $expectedPathsAndHashes = @{
+ "UserConfigProv.psd1" = "9FFE4CA2873CD91CDC9D71362526446ECACDA64D26DEA768E6CE489B84D888E4" ;
+ "DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.schema.mof" ="517F625CB6C465928586F5C613F768B33C20F477DAF843C179071B8C74B992AA";
+ "DSCResources\UserConfigProviderModVersion3\UserConfigProviderModVersion3.psm1" = "0774A539E73B1A480E38CFFE2CF0B8AC46120A0B2E0377E0DE2630031BE83347";
+ "DSCResources\scriptdsc\scriptdsc.schema.psm1"= "7DE80DED0F96FA7D34CF34089A1B088E91CD7B1D80251949FC7C78A6308D51C3";
+ "DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1" = "EB0310C630EDFDFBDD1D993A636EC9B75BB1F04DF7E7FFE39CF6357679C852C7"
+ "DSCResources\scriptdsc\scriptdsc.psd1"= "AB8E8D0840D4854CDCDE25058872413AF417FC016BD77FD5EC677BBB7393532B";
+ "DSCResources\UserConfigProviderModVersion3\UserConfigProviderModVersion3.schema.mof" ="7163E607F067A3C4F91D3AFF3C466ECA47C0CF84B5F0DDA22B2C0E99929B5E21";
+ "DSCResources\UserConfigProviderModVersion2\UserConfigProviderModVersion2.psm1"= "6591FE02528D7FB66F00E09D7F1A025D5D5BAF30A49C5FF1EC562FAE39B38F43";
+ "DSCResources\UserConfigProviderModVersion2\UserConfigProviderModVersion2.schema.mof" = "679318201B012CC5936B29C095956B2131FAF828C0CCA4342A5914F721480FB9";
+ "CatalogTestFile1.mof" = "7C1885AE5F76F58DAA232A5E962875F90308C3CB8580400EE12F999B4E10F940";
+ "CatalogTestFile2.xml" = "00B7DA28CD285F796660D36B77B2EC6054F21A44D5B329EB6BC4EC7687D70B13";
+ "TestImage.gif" = "2D938D255D0D6D547747BD21447CF7295318D34D9B4105D04C1C27487D2FF402" }
+
+
+ $catalogPath = "$env:TEMP\NewFileCatalogVersion2WithMutipleFoldersAndFiles.cat"
+ $catalogDataPath = @("$testDataPath\UserConfigProv\","$testDataPath\CatalogTestFile1.mof","$testDataPath\CatalogTestFile2.xml", "$testDataPath\TestImage.gif")
+
+ try
+ {
+ $null = New-FileCatalog -Path $catalogDataPath -CatalogFilePath $catalogPath -CatalogVersion 2.0
+ $result = Test-FileCatalog -Path $catalogDataPath -CatalogFilePath $catalogPath -Detailed
+ }
+ finally
+ {
+ Remove-Item "$catalogPath" -Force -ErrorAction SilentlyContinue
+ }
+
+ $result.Status | Should Be "Valid"
+ $result.Signature.Status | Should Be "NotSigned"
+ $result.HashAlgorithm | Should Be "SHA256"
+ $result.CatalogItems.Count | Should Be 12
+ $result.PathItems.Count | Should Be 12
+ CompareHashTables $result.CatalogItems $result.PathItems
+ CompareHashTables $result.CatalogItems $expectedPathsAndHashes
+ }
+
+ It "NewFileCatalogFolderWhenCatlogFileIsCreatedInsideSameFolder" {
+
+ $catalogPath = "$env:TEMP\UserConfigProv\NewFileCatalogFolderWhenCatlogFileIsCreatedInsideSameFolder.cat"
+ try
+ {
+ copy-item $testDataPath\UserConfigProv $env:temp -Recurse -ErrorAction SilentlyContinue
+ $null = New-FileCatalog -Path $env:temp\UserConfigProv\ -CatalogFilePath $catalogPath -CatalogVersion 1.0
+ $result = Test-FileCatalog -Path $env:temp\UserConfigProv\ -CatalogFilePath $catalogPath
+ }
+ finally
+ {
+ Remove-Item "$catalogPath" -Force -ErrorAction SilentlyContinue
+ Remove-Item "$env:temp\UserConfigProv\" -Force -ErrorAction SilentlyContinue -Recurse
+ }
+
+ $result | Should Be "Valid"
+ }
+
+ It "NewFileCatalogWithUnicodeCharactersInFileNames" -Skip:$true {
+
+ $expectedPathsAndHashes = @{
+ "UserConfigProv.psd1" = "9FFE4CA2873CD91CDC9D71362526446ECACDA64D26DEA768E6CE489B84D888E4" ;
+ "DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.schema.mof" ="517F625CB6C465928586F5C613F768B33C20F477DAF843C179071B8C74B992AA";
+ "DSCResources\UserConfigProviderModVersion3\UserConfigProviderModVersion3.psm1" = "0774A539E73B1A480E38CFFE2CF0B8AC46120A0B2E0377E0DE2630031BE83347";
+ "DSCResources\scriptdsc\scriptdsc.schema.psm1"= "7DE80DED0F96FA7D34CF34089A1B088E91CD7B1D80251949FC7C78A6308D51C3";
+ "DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1" = "EB0310C630EDFDFBDD1D993A636EC9B75BB1F04DF7E7FFE39CF6357679C852C7"
+ "DSCResources\scriptdsc\scriptdsc.psd1"= "AB8E8D0840D4854CDCDE25058872413AF417FC016BD77FD5EC677BBB7393532B";
+ "DSCResources\UserConfigProviderModVersion3\UserConfigProviderModVersion3.schema.mof" ="7163E607F067A3C4F91D3AFF3C466ECA47C0CF84B5F0DDA22B2C0E99929B5E21";
+ "DSCResources\UserConfigProviderModVersion2\UserConfigProviderModVersion2.psm1"= "6591FE02528D7FB66F00E09D7F1A025D5D5BAF30A49C5FF1EC562FAE39B38F43";
+ "DSCResources\UserConfigProviderModVersion2\UserConfigProviderModVersion2.schema.mof" = "679318201B012CC5936B29C095956B2131FAF828C0CCA4342A5914F721480FB9";
+ "ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ.txt" = "EFD0AE8FF12C7387D51FFC03259B60E06DA012BF7D3B7B9D3480FAB2864846CE";
+ "ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ.txt" = "9FB57660EDD8DA898A9F1E7F5A36B8B760B4A21625F9968D87A32A55B3546BF9"}
+
+ # Create Test Files with unicode characters in names and content
+ $unicodeTempDir = Join-Path -Path $testDataPath -ChildPath "UnicodeTestDir"
+ $null = New-Item -ItemType Directory -Path $unicodeTempDir -Force
+
+ $null = New-Item -ItemType File -Path "$unicodeTempDir\ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ.txt" -Force -ErrorAction SilentlyContinue
+ $null = Add-Content -Path "$unicodeTempDir\ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ.txt" -Value "Testing unicode"
+ $null = Out-File -FilePath "$unicodeTempDir\ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ.txt" -Encoding unicode -InputObject "ɗ ɘ ə ɚ ɛ ɜ ɝ ɞ ɟ ɠ ɡ ɢ ɣ ɤ ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ ɭ ɮ ɯ ɰ ɱ ɲ ɳ ɴ ɵ ɶ ɷ ɸ ɹ ɺ ɻ ɼ ɽ ɾ ɿ ʀ ʁ ʂ ʃ ʄ ʅ" -Append
+ $null = New-Item -ItemType File -Path "$unicodeTempDir\ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ.txt" -Force -ErrorAction SilentlyContinue
+ $null = Out-File -FilePath "$unicodeTempDir\ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ.txt" -Encoding unicode -InputObject "ਅ ਆ ਇ ਈ ਉ ਊ ਏ ਐ ਓ ਔ ਕ ਖ ਗ ਘ ਙ ਚ ਛ ਜ ਝ ਞ ਟ ਠ ਡ ਢ ਣ ਤ ਥ ਦ ਧ ਨ ਪ ਫ ਬ ਭ ਮ ਯ ਰ ਲ ਲ਼ ਵ " -Append
+ $null = Out-File -FilePath "$unicodeTempDir\ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ.txt" -Encoding unicode -InputObject "அ ஆ இ ஈ உ ஊ எ ஏ ஐ ஒ ஓ ஔ க ங ச ஜ ஞ ட ண த ந ன ப ம ய ர ற ல ள ழ வ ஷ ஸ ஹ " -Append
+
+ $catalogPath = "$env:TEMP\క ఖ గ ఘ ఙ చ ఛ జ ఝ ఞ.cat"
+ $catalogDataPath = @("$testDataPath\UserConfigProv\", "$unicodeTempDir\ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ.txt" ,"$unicodeTempDir\ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ.txt")
+
+ try
+ {
+ $null = New-FileCatalog -Path $catalogDataPath -CatalogFilePath $catalogPath -CatalogVersion 2.0
+ $result = Test-FileCatalog -Path $catalogDataPath -CatalogFilePath $catalogPath -Detailed
+ }
+ finally
+ {
+ Remove-Item $unicodeTempDir -Recurse -Force -ErrorAction SilentlyContinue
+ Remove-Item "$catalogPath" -Force -ErrorAction SilentlyContinue
+ }
+
+ $result.Status | Should Be "Valid"
+ $result.Signature.Status | Should Be "NotSigned"
+ $result.HashAlgorithm | Should Be "SHA256"
+ $result.CatalogItems.Count | Should Be 11
+ $result.PathItems.Count | Should Be 11
+ CompareHashTables $result.CatalogItems $result.PathItems
+ CompareHashTables $result.CatalogItems $expectedPathsAndHashes
+ }
+ }
+
+ Context "NewAndTestCatalogTests NegativeTestCases when creation or validation Fails"{
+
+ AfterEach {
+ Remove-Item "$script:catalogPath" -Force -ErrorAction SilentlyContinue
+ Remove-Item "$env:temp\UserConfigProv" -Force -Recurse -ErrorAction SilentlyContinue
+ }
+
+ It "TestCatalogWhenNewFileAddedtoFolderBeforeValidation" {
+
+ $script:catalogPath = "$env:TEMP\TestCatalogWhenNewFileAddedtoFolderBeforeValidation.cat"
+ $null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 2.0
+ $null = copy-item $testDataPath\UserConfigProv $env:temp -Recurse -ErrorAction SilentlyContinue
+ $null = New-Item $env:temp\UserConfigProv\DSCResources\NewFile.txt -ItemType File
+ Add-Content $env:temp\UserConfigProv\DSCResources\NewFile.txt -Value "More Data" -force
+ $result = Test-FileCatalog -Path $env:temp\UserConfigProv -CatalogFilePath $script:catalogPath -Detailed
+
+ $result.Status | Should Be "ValidationFailed"
+ $result.CatalogItems.Count | Should Be 9
+ $result.PathItems.Count | Should Be 10
+ $result.CatalogItems.ContainsKey("DSCResources\NewFile.txt") | Should Be $false
+ $result.PathItems.ContainsKey("DSCResources\NewFile.txt") | Should Be $true
+
+ # By Skipping the new added file validation will pass
+ $result = Test-FileCatalog -Path $env:temp\UserConfigProv -CatalogFilePath $script:catalogPath -Detailed -FilesToSkip "NewFile.txt"
+ $result.Status | Should Be "Valid"
+ }
+
+ It "TestCatalogWhenNewFileDeletedFromFolderBeforeValidation" {
+
+ $script:catalogPath = "$env:TEMP\TestCatalogWhenNewFileDeletedFromFolderBeforeValidation.cat"
+ $null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 1.0
+ $null = copy-item $testDataPath\UserConfigProv $env:temp -Recurse -ErrorAction SilentlyContinue
+ del $env:temp\UserConfigProv\DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1 -force -ErrorAction SilentlyContinue
+ $result = Test-FileCatalog -Path $env:temp\UserConfigProv -CatalogFilePath $script:catalogPath -Detailed
+
+ $result.Status | Should Be "ValidationFailed"
+ $result.CatalogItems.Count | Should Be 9
+ $result.PathItems.Count | Should Be 8
+ $result.CatalogItems.ContainsKey("DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1") | Should Be $true
+ $result.PathItems.ContainsKey("DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1") | Should Be $false
+
+ # By Skipping the deleted file validation will pass
+ $result = Test-FileCatalog -Path $env:temp\UserConfigProv -CatalogFilePath $script:catalogPath -Detailed -FilesToSkip "UserConfigProviderModVersion1.psm1"
+ $result.Status | Should Be "Valid"
+ }
+
+ It "TestCatalogWhenFileContentModifiedBeforeValidation" {
+
+ $script:catalogPath = "$env:TEMP\TestCatalogWhenFileContentModifiedBeforeValidation.cat"
+ $null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 1.0
+ $null = copy-item $testDataPath\UserConfigProv $env:temp -Recurse -ErrorAction SilentlyContinue
+ Add-Content $env:temp\UserConfigProv\DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1 -Value "More Data" -Force
+ $result = Test-FileCatalog -Path $env:temp\UserConfigProv -CatalogFilePath $script:catalogPath -Detailed
+
+ $result.Status | Should Be "ValidationFailed"
+ $result.CatalogItems.Count | Should Be 9
+ $result.PathItems.Count | Should Be 9
+ $catalogHashValue = $result.CatalogItems["DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1"]
+ $pathHashValue = $result.PathItems["DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1"]
+ ($catalogHashValue -eq $pathHashValue) | Should Be $false
+
+ # By Skipping the file with modifed contents validation will pass
+ $result = Test-FileCatalog -Path $env:temp\UserConfigProv -CatalogFilePath $script:catalogPath -Detailed -FilesToSkip "UserConfigProviderModVersion1.psm1"
+ $result.Status | Should Be "Valid"
+ }
+ }
+
+ Context "TestCatalog Skip Validation Tests"{
+
+ AfterEach {
+ Remove-Item "$script:catalogPath" -Force -ErrorAction SilentlyContinue
+ }
+
+ It "TestCatalogSkipSingleFileDuringValidation" {
+
+ $script:catalogPath = "$env:TEMP\TestCatalogSkipSingleFileDuringValidation.cat"
+ $null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 2.0
+ $result = Test-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -FilesToSkip "scriptdsc.schema"
+ $result | Should Be "Valid"
+ }
+
+ It "TestCatalogSkipCertainFileTypeDuringValidation" {
+
+ $script:catalogPath = "$env:TEMP\TestCatalogSkipCertainFileTypeDuringValidation.cat"
+ $null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 2.0
+ $result = Test-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -FilesToSkip "*.mof"
+ $result | Should Be "Valid"
+ }
+
+ It "TestCatalogSkipWildCardPatternDuringValidation" {
+
+ $script:catalogPath = "$env:TEMP\TestCatalogSkipWildCardPatternDuringValidation.cat"
+ $null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 1.0
+ $result = Test-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -FilesToSkip "UserConfigProvider*.psm1"
+ $result | Should Be "Valid"
+ }
+
+ It "TestCatalogSkipMuitplePattensDuringValidation" {
+
+ $script:catalogPath = "$env:TEMP\TestCatalogSkipMuitplePattensDuringValidation.cat"
+ $null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 1.0
+ $result = Test-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -FilesToSkip "*.psd1","UserConfigProviderModVersion2.psm1","*ModVersion1.schema.mof"
+ $result | Should Be "Valid"
+ }
+ }
+}
+
+}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/CatalogTestFile1.mof b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/CatalogTestFile1.mof
new file mode 100644
index 0000000000..5ec9506131
Binary files /dev/null and b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/CatalogTestFile1.mof differ
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/CatalogTestFile2.xml b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/CatalogTestFile2.xml
new file mode 100644
index 0000000000..5fbfb31e50
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/CatalogTestFile2.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+ No
+ Linux
+ CentOS
+ Fre
+ 00000000-0000-0000-0000-000000000000
+ No
+ Yes
+
+ $ipAddress
+
+ Yes
+
+ 2001:4898:28:3:215:5dff:feb8:b9b3
+
+ Linux1
+ Workgroup
+ None
+ X64
+ en-US
+ Windows 8 Server
+ X64
+ Server DataCenter
+ None
+ Unknown
+ C:\
+ No
+
+
+
+ Basic
+
+
+
+
+
+
+ Basic
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/TestFileCatalog.txt b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/TestFileCatalog.txt
new file mode 100644
index 0000000000..5c341af14e
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/TestFileCatalog.txt
@@ -0,0 +1 @@
+Text File to test catalog
\ No newline at end of file
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/TestImage.gif b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/TestImage.gif
new file mode 100644
index 0000000000..cac0342888
Binary files /dev/null and b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/TestImage.gif differ
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion1/UserConfigProviderModVersion1.psm1 b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion1/UserConfigProviderModVersion1.psm1
new file mode 100644
index 0000000000..f53a084f3d
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion1/UserConfigProviderModVersion1.psm1
@@ -0,0 +1,61 @@
+
+
+# The Get-TargetResource cmdlet is used to fetch the desired state of the DSC managed node through a powershell script.
+# This cmdlet executes the user supplied script (i.e., the script is responsible for validating the desired state of the
+# DSC managed node). The result of the script execution is in the form of a hashtable containing all the inormation
+# gathered from the GetScript execution.
+function Get-TargetResource
+{
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]
+ $text
+ )
+
+ $result = @{
+ Text = "Hello from Get!";
+ }
+ $result;
+ }
+
+
+# The Set-TargetResource cmdlet is used to Set the desired state of the DSC managed node through a powershell script.
+# The method executes the user supplied script (i.e., the script is responsible for validating the desired state of the
+# DSC managed node). If the DSC managed node requires a restart either during or after the execution of the SetScript,
+# the SetScript notifies the PS Infrasturcure by setting the variable $DSCMachineStatus.IsRestartRequired to $true.
+function Set-TargetResource
+{
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]
+ $text
+ )
+ $path = "$env:SystemDrive\dscTestPath\hello1.txt"
+ New-Item -Path $path -Type File -force
+ Add-Content -Path $path -Value $text
+}
+
+
+# The Test-TargetResource cmdlet is used to validate the desired state of the DSC managed node through a powershell script.
+# The method executes the user supplied script (i.e., the script is responsible for validating the desired state of the
+# DSC managed node). The result of the script execution should be true if the DSC managed machine is in the desired state
+# or else false should be returned.
+function Test-TargetResource
+{
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]
+ $text
+ )
+ $false
+}
+
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion1/UserConfigProviderModVersion1.schema.mof b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion1/UserConfigProviderModVersion1.schema.mof
new file mode 100644
index 0000000000..dffe8e6438
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion1/UserConfigProviderModVersion1.schema.mof
@@ -0,0 +1,8 @@
+
+[ClassVersion("1.0.0")]
+class UserConfigProviderModVersion1 : OMI_BaseResource
+{
+ [key] string Text;
+};
+
+
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion2/UserConfigProviderModVersion2.psm1 b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion2/UserConfigProviderModVersion2.psm1
new file mode 100644
index 0000000000..4b5b6855f0
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion2/UserConfigProviderModVersion2.psm1
@@ -0,0 +1,62 @@
+
+
+# The Get-TargetResource cmdlet is used to fetch the desired state of the DSC managed node through a powershell script.
+# This cmdlet executes the user supplied script (i.e., the script is responsible for validating the desired state of the
+# DSC managed node). The result of the script execution is in the form of a hashtable containing all the inormation
+# gathered from the GetScript execution.
+function Get-TargetResource
+{
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]
+ $text
+ )
+
+ $result = @{
+ Text = "Hello from Get!";
+ }
+ $result;
+ }
+
+
+# The Set-TargetResource cmdlet is used to Set the desired state of the DSC managed node through a powershell script.
+# The method executes the user supplied script (i.e., the script is responsible for validating the desired state of the
+# DSC managed node). If the DSC managed node requires a restart either during or after the execution of the SetScript,
+# the SetScript notifies the PS Infrasturcure by setting the variable $DSCMachineStatus.IsRestartRequired to $true.
+function Set-TargetResource
+{
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]
+ $text
+ )
+
+ $path = "$env:SystemDrive\dscTestPath\hello2.txt"
+ New-Item -Path $path -Type File -force
+ Add-Content -Path $path -Value $text
+}
+
+
+# The Test-TargetResource cmdlet is used to validate the desired state of the DSC managed node through a powershell script.
+# The method executes the user supplied script (i.e., the script is responsible for validating the desired state of the
+# DSC managed node). The result of the script execution should be true if the DSC managed machine is in the desired state
+# or else false should be returned.
+function Test-TargetResource
+{
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]
+ $text
+ )
+ $false
+}
+
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion2/UserConfigProviderModVersion2.schema.mof b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion2/UserConfigProviderModVersion2.schema.mof
new file mode 100644
index 0000000000..936780b59e
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion2/UserConfigProviderModVersion2.schema.mof
@@ -0,0 +1,8 @@
+
+[ClassVersion("1.0.0")]
+class UserConfigProviderModVersion2 : OMI_BaseResource
+{
+ [key] string Text;
+};
+
+
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion3/UserConfigProviderModVersion3.psm1 b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion3/UserConfigProviderModVersion3.psm1
new file mode 100644
index 0000000000..5a596c4e0c
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion3/UserConfigProviderModVersion3.psm1
@@ -0,0 +1,62 @@
+
+
+# The Get-TargetResource cmdlet is used to fetch the desired state of the DSC managed node through a powershell script.
+# This cmdlet executes the user supplied script (i.e., the script is responsible for validating the desired state of the
+# DSC managed node). The result of the script execution is in the form of a hashtable containing all the inormation
+# gathered from the GetScript execution.
+function Get-TargetResource
+{
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]
+ $text
+ )
+
+ $result = @{
+ Text = "Hello from Get!";
+ }
+ $result;
+ }
+
+
+# The Set-TargetResource cmdlet is used to Set the desired state of the DSC managed node through a powershell script.
+# The method executes the user supplied script (i.e., the script is responsible for validating the desired state of the
+# DSC managed node). If the DSC managed node requires a restart either during or after the execution of the SetScript,
+# the SetScript notifies the PS Infrasturcure by setting the variable $DSCMachineStatus.IsRestartRequired to $true.
+function Set-TargetResource
+{
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]
+ $text
+ )
+
+ $path = "$env:SystemDrive\dscTestPath\hello3.txt"
+ New-Item -Path $path -Type File -force
+ Add-Content -Path $path -Value $text
+}
+
+
+# The Test-TargetResource cmdlet is used to validate the desired state of the DSC managed node through a powershell script.
+# The method executes the user supplied script (i.e., the script is responsible for validating the desired state of the
+# DSC managed node). The result of the script execution should be true if the DSC managed machine is in the desired state
+# or else false should be returned.
+function Test-TargetResource
+{
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [string]
+ $text
+ )
+ $false
+}
+
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion3/UserConfigProviderModVersion3.schema.mof b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion3/UserConfigProviderModVersion3.schema.mof
new file mode 100644
index 0000000000..6cd3c3d8be
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/UserConfigProviderModVersion3/UserConfigProviderModVersion3.schema.mof
@@ -0,0 +1,8 @@
+
+[ClassVersion("1.0.0")]
+class UserConfigProviderModVersion3 : OMI_BaseResource
+{
+ [key] string Text;
+};
+
+
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/scriptdsc/scriptdsc.psd1 b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/scriptdsc/scriptdsc.psd1
new file mode 100644
index 0000000000..7039825420
Binary files /dev/null and b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/scriptdsc/scriptdsc.psd1 differ
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/scriptdsc/scriptdsc.schema.psm1 b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/scriptdsc/scriptdsc.schema.psm1
new file mode 100644
index 0000000000..2c58ee1768
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/DSCResources/scriptdsc/scriptdsc.schema.psm1
@@ -0,0 +1,15 @@
+
+
+configuration scriptdsc
+{
+ param ($one, $two, $three)
+
+ Import-DscResource -ModuleName UserConfigProv
+ Log scriptDscLogResource
+ {
+ Message = "This is the message from the embedded log resource: one=$one two=$two three=$three"
+ }
+
+ }
+
+Export-ModuleMember -Function scriptdsc
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/UserConfigProv.psd1 b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/UserConfigProv.psd1
new file mode 100644
index 0000000000..2bbd83efc5
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Security/TestData/CatalogTestData/UserConfigProv/UserConfigProv.psd1
@@ -0,0 +1,12 @@
+@{
+ ModuleVersion = '3.0.0.1'
+ Author = 'fayzas'
+ CompanyName = 'Microsoft Corporation'
+ Copyright = '(c) 2013 Microsoft Corporation. All rights reserved.'
+ Description = 'Hello World!'
+ PowerShellVersion = '3.0'
+ CLRVersion = '4.0'
+ RequiredModules = @()
+ # NestedModules = @("UserConfigProviderModVersion.psm1")
+ # FunctionsToExport = @("Get-TargetResource", "Set-TargetResource", "Test-TargetResource")
+ }
\ No newline at end of file
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Compare-Object.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Compare-Object.Tests.ps1
index 6bde8fdd93..73ff7a5c79 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Compare-Object.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Compare-Object.Tests.ps1
@@ -1,6 +1,6 @@
Describe "Compare-Object" {
BeforeAll {
- $nl = [Environment]::NewLine
+ $nl = [Environment]::NewLine
$content1 = "line 1" + $nl + "line 2"
$content2 = "line 1" + $nl + "line 2.1"
@@ -12,10 +12,10 @@ Describe "Compare-Object" {
$file3 = Join-Path -Path $TestDrive -ChildPath "test3.txt"
$file4 = Join-Path -Path $TestDrive -ChildPath "test4.txt"
- New-Item $file1 -ItemType file -Value $content1 -Force
- New-Item $file2 -ItemType file -Value $content2 -Force
- New-Item $file3 -ItemType file -Value $content3 -Force
- New-Item $file4 -ItemType file -Value $content4 -Force
+ $null = New-Item $file1 -ItemType file -Value $content1 -Force
+ $null = New-Item $file2 -ItemType file -Value $content2 -Force
+ $null = New-Item $file3 -ItemType file -Value $content3 -Force
+ $null = New-Item $file4 -ItemType file -Value $content4 -Force
}
It "Should be able to compare the same object using the referenceObject and differenceObject switches" {
@@ -454,4 +454,4 @@ Describe "Compare-Object DRT basic functionality" -Tags DRT{
$result.InputObject | Should Be "refObject"
$result.SideIndicator | Should Be "<="
}
-}
\ No newline at end of file
+}