Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
PSConfiguration.cs742 linesDownload Raw Back to engine
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.Generic;6using System.IO;7using System.Management.Automation.Internal;8using System.Text;9using System.Threading;10 11using Newtonsoft.Json;12using Newtonsoft.Json.Linq;13 14namespace System.Management.Automation.Configuration15{16    /// <summary>17    /// The scope of the configuration file.18    /// </summary>19    public enum ConfigScope20    {21        /// <summary>22        /// AllUsers configuration applies to all users.23        /// </summary>24        AllUsers = 0,25 26        /// <summary>27        /// CurrentUser configuration applies to the current user.28        /// </summary>29        CurrentUser = 130    }31 32    /// <summary>33    /// Reads from and writes to the JSON configuration files.34    /// The config values were originally stored in the Windows registry.35    /// </summary>36    /// <remarks>37    /// The config file access APIs are designed to avoid hitting the disk as much as possible.38    /// - For the first read request targeting a config file, the config data is read from the file and then cached as a 'JObject' instance;39    ///     * the first read request happens very early during the startup of 'pwsh'.40    /// - For the subsequent read requests targeting the same config file, they will then work with that 'JObject' instance;41    /// - For the write request targeting a config file, the cached config data corresponding to that config file will be refreshed after the write operation is successfully done.42    ///43    /// To summarize the expected behavior:44    /// Once a 'pwsh' process starts up -45    ///   1. any changes made to the config file from outside this 'pwsh' process is not guaranteed to be seen by it (most likely won't be seen).46    ///   2. any changes to the config file by this 'pwsh' process via the config file access APIs will be seen by it, if it chooses to read those changes afterwards.47    /// </remarks>48    internal sealed class PowerShellConfig49    {50        private const string ConfigFileName = "powershell.config.json";51        private const string ExecutionPolicyDefaultShellKey = "Microsoft.PowerShell:ExecutionPolicy";52        private const string DisableImplicitWinCompatKey = "DisableImplicitWinCompat";53        private const string WindowsPowerShellCompatibilityModuleDenyListKey = "WindowsPowerShellCompatibilityModuleDenyList";54        private const string WindowsPowerShellCompatibilityNoClobberModuleListKey = "WindowsPowerShellCompatibilityNoClobberModuleList";55 56        // Provide a singleton57        internal static readonly PowerShellConfig Instance = new PowerShellConfig();58 59        // The json file containing system-wide configuration settings.60        // When passed as a pwsh command-line option, overrides the system wide configuration file.61        private string systemWideConfigFile;62        private string systemWideConfigDirectory;63 64        // The json file containing the per-user configuration settings.65        private readonly string perUserConfigFile;66        private readonly string perUserConfigDirectory;67 68        // Note: JObject and JsonSerializer are thread safe.69        // Root Json objects corresponding to the configuration file for 'AllUsers' and 'CurrentUser' respectively.70        // They are used as a cache to avoid hitting the disk for every read operation.71        private readonly JObject[] configRoots;72        private readonly JObject emptyConfig;73        private readonly JsonSerializer serializer;74 75        /// <summary>76        /// Lock used to enable multiple concurrent readers and singular write locks within a single process.77        /// TODO: This solution only works for IO from a single process.78        ///       A more robust solution is needed to enable ReaderWriterLockSlim behavior between processes.79        /// </summary>80        private readonly ReaderWriterLockSlim fileLock;81 82        private PowerShellConfig()83        {84            // Sets the system-wide configuration file.85            systemWideConfigDirectory = Utils.DefaultPowerShellAppBase;86            systemWideConfigFile = Path.Combine(systemWideConfigDirectory, ConfigFileName);87 88            // Sets the per-user configuration directory89            // Note: This directory may or may not exist depending upon the execution scenario.90            // Writes will attempt to create the directory if it does not already exist.91            perUserConfigDirectory = Platform.ConfigDirectory;92            if (!string.IsNullOrEmpty(perUserConfigDirectory))93            {94                perUserConfigFile = Path.Combine(perUserConfigDirectory, ConfigFileName);95            }96 97            emptyConfig = new JObject();98            configRoots = new JObject[2];99            serializer = JsonSerializer.Create(new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.None, MaxDepth = 10 });100 101            fileLock = new ReaderWriterLockSlim();102        }103 104        private string GetConfigFilePath(ConfigScope scope)105        {106            return (scope == ConfigScope.CurrentUser) ? perUserConfigFile : systemWideConfigFile;107        }108 109        /// <summary>110        /// Sets the system wide configuration file path.111        /// </summary>112        /// <param name="value">A fully qualified path to the system wide configuration file.</param>113        /// <exception cref="FileNotFoundException"><paramref name="value"/> is a null reference or the associated file does not exist.</exception>114        /// <remarks>115        /// This method is for use when processing the -SettingsFile configuration setting and should not be used for any other purpose.116        /// </remarks>117        internal void SetSystemConfigFilePath(string value)118        {119            if (!string.IsNullOrEmpty(value) && !File.Exists(value))120            {121                throw new FileNotFoundException(value);122            }123 124            FileInfo info = new FileInfo(value);125            systemWideConfigFile = info.FullName;126            systemWideConfigDirectory = info.Directory.FullName;127        }128 129        /// <summary>130        /// Existing Key = HKLM:\System\CurrentControlSet\Control\Session Manager\Environment131        /// Proposed value = %ProgramFiles%\PowerShell\Modules by default132        /// Note: There is no setter because this value is immutable.133        /// </summary>134        /// <param name="scope">Whether this is a system-wide or per-user setting.</param>135        /// <returns>Value if found, null otherwise. The behavior matches ModuleIntrinsics.GetExpandedEnvironmentVariable().</returns>136        internal string GetModulePath(ConfigScope scope)137        {138            string modulePath = ReadValueFromFile<string>(scope, Constants.PSModulePathEnvVar);139            if (!string.IsNullOrEmpty(modulePath))140            {141                modulePath = Environment.ExpandEnvironmentVariables(modulePath);142            }143 144            return modulePath;145        }146 147        /// <summary>148        /// Existing Key = HKCU and HKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell149        /// Proposed value = Existing default execution policy if not already specified150        ///151        /// Schema:152        /// {153        ///     "shell-ID-string:ExecutionPolicy" : "execution policy string"154        /// }155        ///156        /// TODO: In a single config file, it might be better to nest this. It is unnecessary complexity until a need arises for more nested values.157        /// </summary>158        /// <param name="scope">Whether this is a system-wide or per-user setting.</param>159        /// <param name="shellId">The shell associated with this policy. Typically, it is "Microsoft.PowerShell".</param>160        /// <returns>The execution policy if found. Null otherwise.</returns>161        internal string GetExecutionPolicy(ConfigScope scope, string shellId)162        {163            string key = GetExecutionPolicySettingKey(shellId);164            string execPolicy = ReadValueFromFile<string>(scope, key);165            return string.IsNullOrEmpty(execPolicy) ? null : execPolicy;166        }167 168        internal void RemoveExecutionPolicy(ConfigScope scope, string shellId)169        {170            string key = GetExecutionPolicySettingKey(shellId);171            RemoveValueFromFile<string>(scope, key);172        }173 174        internal void SetExecutionPolicy(ConfigScope scope, string shellId, string executionPolicy)175        {176            string key = GetExecutionPolicySettingKey(shellId);177            WriteValueToFile<string>(scope, key, executionPolicy);178        }179 180        private static string GetExecutionPolicySettingKey(string shellId)181        {182            return string.Equals(shellId, Utils.DefaultPowerShellShellID, StringComparison.Ordinal)183                ? ExecutionPolicyDefaultShellKey184                : string.Concat(shellId, ":", "ExecutionPolicy");185        }186 187        /// <summary>188        /// Get the names of experimental features enabled in the config file.189        /// </summary>190        internal string[] GetExperimentalFeatures()191        {192            string[] features = ReadValueFromFile(ConfigScope.CurrentUser, "ExperimentalFeatures", Array.Empty<string>());193 194            if (features.Length == 0)195            {196                features = ReadValueFromFile(ConfigScope.AllUsers, "ExperimentalFeatures", Array.Empty<string>());197            }198 199            return features;200        }201 202        /// <summary>203        /// Set the enabled list of experimental features in the config file.204        /// </summary>205        /// <param name="scope">The ConfigScope of the configuration file to update.</param>206        /// <param name="featureName">The name of the experimental feature to change in the configuration.</param>207        /// <param name="setEnabled">If true, add to configuration; otherwise, remove from configuration.</param>208        internal void SetExperimentalFeatures(ConfigScope scope, string featureName, bool setEnabled)209        {210            var features = new List<string>(GetExperimentalFeatures());211            bool containsFeature = features.Contains(featureName);212            if (setEnabled && !containsFeature)213            {214                features.Add(featureName);215                WriteValueToFile<string[]>(scope, "ExperimentalFeatures", features.ToArray());216            }217            else if (!setEnabled && containsFeature)218            {219                features.Remove(featureName);220                WriteValueToFile<string[]>(scope, "ExperimentalFeatures", features.ToArray());221            }222        }223 224        internal bool IsImplicitWinCompatEnabled()225        {226            bool settingValue = ReadValueFromFile<bool?>(ConfigScope.CurrentUser, DisableImplicitWinCompatKey)227                ?? ReadValueFromFile<bool?>(ConfigScope.AllUsers, DisableImplicitWinCompatKey)228                ?? false;229 230            return !settingValue;231        }232 233        internal string[] GetWindowsPowerShellCompatibilityModuleDenyList()234        {235            return ReadValueFromFile<string[]>(ConfigScope.CurrentUser, WindowsPowerShellCompatibilityModuleDenyListKey)236                ?? ReadValueFromFile<string[]>(ConfigScope.AllUsers, WindowsPowerShellCompatibilityModuleDenyListKey);237        }238 239        internal string[] GetWindowsPowerShellCompatibilityNoClobberModuleList()240        {241            return ReadValueFromFile<string[]>(ConfigScope.CurrentUser, WindowsPowerShellCompatibilityNoClobberModuleListKey)242                ?? ReadValueFromFile<string[]>(ConfigScope.AllUsers, WindowsPowerShellCompatibilityNoClobberModuleListKey);243        }244 245        /// <summary>246        /// Corresponding settings of the original Group Policies.247        /// </summary>248        internal PowerShellPolicies GetPowerShellPolicies(ConfigScope scope)249        {250            return ReadValueFromFile<PowerShellPolicies>(scope, nameof(PowerShellPolicies));251        }252 253#if UNIX254        /// <summary>255        /// Gets the identity name to use for writing to syslog.256        /// </summary>257        /// <returns>258        /// The string identity to use for writing to syslog. The default value is 'powershell'.259        /// </returns>260        internal string GetSysLogIdentity()261        {262            string identity = ReadValueFromFile<string>(ConfigScope.AllUsers, "LogIdentity");263 264            if (string.IsNullOrEmpty(identity) ||265                identity.Equals(LogDefaultValue, StringComparison.OrdinalIgnoreCase))266            {267                identity = "powershell";268            }269 270            return identity;271        }272 273        /// <summary>274        /// Gets the log level filter.275        /// </summary>276        /// <returns>277        /// One of the PSLevel values indicating the level to log. The default value is PSLevel.Informational.278        /// </returns>279        internal PSLevel GetLogLevel()280        {281            string levelName = ReadValueFromFile<string>(ConfigScope.AllUsers, "LogLevel");282            PSLevel level;283 284            if (string.IsNullOrEmpty(levelName) ||285                levelName.Equals(LogDefaultValue, StringComparison.OrdinalIgnoreCase) ||286                !Enum.TryParse<PSLevel>(levelName, true, out level))287            {288                level = PSLevel.Informational;289            }290 291            return level;292        }293 294        /// <summary>295        /// The supported separator characters for listing channels and keywords in configuration.296        /// </summary>297        private static readonly char[] s_valueSeparators = new char[] {' ', ',', '|'};298 299        /// <summary>300        /// Provides a string name to indicate the default for a configuration setting.301        /// </summary>302        private const string LogDefaultValue = "default";303 304        /// <summary>305        /// Gets the bitmask of the PSChannel values to log.306        /// </summary>307        /// <returns>308        /// A bitmask of PSChannel.Operational and/or PSChannel.Analytic. The default value is PSChannel.Operational.309        /// </returns>310        internal PSChannel GetLogChannels()311        {312            string values = ReadValueFromFile<string>(ConfigScope.AllUsers, "LogChannels");313 314            PSChannel result = 0;315            if (!string.IsNullOrEmpty(values))316            {317                string[] names = values.Split(s_valueSeparators, StringSplitOptions.RemoveEmptyEntries);318 319                foreach (string name in names)320                {321                    if (name.Equals(LogDefaultValue, StringComparison.OrdinalIgnoreCase))322                    {323                        result = 0;324                        break;325                    }326 327                    PSChannel value;328                    if (Enum.TryParse<PSChannel>(name, true, out value))329                    {330                        result |= value;331                    }332                }333            }334 335            if (result == 0)336            {337                result = System.Management.Automation.Tracing.PSSysLogProvider.DefaultChannels;338            }339 340            return result;341        }342 343        /// <summary>344        /// Gets the bitmask of keywords to log.345        /// </summary>346        /// <returns>347        /// A bitmask of PSKeyword values. The default value is all keywords other than UseAlwaysAnalytic.348        /// </returns>349        internal PSKeyword GetLogKeywords()350        {351            string values = ReadValueFromFile<string>(ConfigScope.AllUsers, "LogKeywords");352 353            PSKeyword result = 0;354            if (!string.IsNullOrEmpty(values))355            {356                string[] names = values.Split(s_valueSeparators, StringSplitOptions.RemoveEmptyEntries);357 358                foreach (string name in names)359                {360                    if (name.Equals(LogDefaultValue, StringComparison.OrdinalIgnoreCase))361                    {362                        result = 0;363                        break;364                    }365 366                    PSKeyword value;367                    if (Enum.TryParse<PSKeyword>(name, true, out value))368                    {369                        result |= value;370                    }371                }372            }373 374            if (result == 0)375            {376                result = System.Management.Automation.Tracing.PSSysLogProvider.DefaultKeywords;377            }378 379            return result;380        }381#endif // UNIX382 383        /// <summary>384        /// Read a value from the configuration file.385        /// </summary>386        /// <typeparam name="T">The type of the value</typeparam>387        /// <param name="scope">The ConfigScope of the configuration file to update.</param>388        /// <param name="key">The string key of the value.</param>389        /// <param name="defaultValue">The default value to return if the key is not present.</param>390        private T ReadValueFromFile<T>(ConfigScope scope, string key, T defaultValue = default)391        {392            string fileName = GetConfigFilePath(scope);393            if (string.IsNullOrEmpty(fileName))394            {395                return defaultValue;396            }397 398            JObject configData = configRoots[(int)scope];399 400            if (configData == null)401            {402                if (File.Exists(fileName))403                {404                    try405                    {406                        // Open file for reading, but allow multiple readers407                        fileLock.EnterReadLock();408 409                        using var stream = OpenFileStreamWithRetry(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);410                        using var jsonReader = new JsonTextReader(new StreamReader(stream));411 412                        configData = serializer.Deserialize<JObject>(jsonReader) ?? emptyConfig;413                    }414                    catch (Exception exc)415                    {416                        throw PSTraceSource.NewInvalidOperationException(exc, PSConfigurationStrings.CanNotConfigurationFile, args: fileName);417                    }418                    finally419                    {420                        fileLock.ExitReadLock();421                    }422                }423                else424                {425                    configData = emptyConfig;426                }427 428                // Set the configuration cache.429                JObject originalValue = Interlocked.CompareExchange(ref configRoots[(int)scope], configData, null);430                if (originalValue != null)431                {432                    configData = originalValue;433                }434            }435 436            if (configData != emptyConfig && configData.TryGetValue(key, StringComparison.OrdinalIgnoreCase, out JToken jToken))437            {438                return jToken.ToObject<T>(serializer) ?? defaultValue;439            }440 441            return defaultValue;442        }443 444        private static FileStream OpenFileStreamWithRetry(string fullPath, FileMode mode, FileAccess access, FileShare share)445        {446            const int MaxTries = 5;447            for (int numTries = 0; numTries < MaxTries; numTries++)448            {449                try450                {451                    return new FileStream(fullPath, mode, access, share);452                }453                catch (IOException)454                {455                    if (numTries == (MaxTries - 1))456                    {457                        throw;458                    }459 460                    Thread.Sleep(50);461                }462            }463 464            throw new IOException(nameof(OpenFileStreamWithRetry));465        }466 467        /// <summary>468        /// Update a value in the configuration file.469        /// </summary>470        /// <typeparam name="T">The type of the value</typeparam>471        /// <param name="scope">The ConfigScope of the configuration file to update.</param>472        /// <param name="key">The string key of the value.</param>473        /// <param name="value">The value to set.</param>474        /// <param name="addValue">Whether the key-value pair should be added to or removed from the file.</param>475        private void UpdateValueInFile<T>(ConfigScope scope, string key, T value, bool addValue)476        {477            try478            {479                string fileName = GetConfigFilePath(scope);480                fileLock.EnterWriteLock();481 482                // Since multiple properties can be in a single file, replacement is required instead of overwrite if a file already exists.483                // Handling the read and write operations within a single FileStream prevents other processes from reading or writing the file while484                // the update is in progress. It also locks out readers during write operations.485 486                JObject jsonObject = null;487                using FileStream fs = OpenFileStreamWithRetry(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);488 489                // UTF8, BOM detection, and bufferSize are the same as the basic stream constructor.490                // The most important parameter here is the last one, which keeps underlying stream open after StreamReader is disposed491                // so that it can be reused for the subsequent write operation.492                using (StreamReader streamRdr = new StreamReader(fs, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true))493                using (JsonTextReader jsonReader = new JsonTextReader(streamRdr))494                {495                    // Safely determines whether there is content to read from the file496                    bool isReadSuccess = jsonReader.Read();497                    if (isReadSuccess)498                    {499                        // Read the stream into a root JObject for manipulation500                        jsonObject = serializer.Deserialize<JObject>(jsonReader);501                        JProperty propertyToModify = jsonObject.Property(key);502 503                        if (propertyToModify == null)504                        {505                            // The property doesn't exist, so add it506                            if (addValue)507                            {508                                jsonObject.Add(new JProperty(key, value));509                            }510                            // else the property doesn't exist so there is nothing to remove511                        }512                        else513                        {514                            // The property exists515                            if (addValue)516                            {517                                propertyToModify.Replace(new JProperty(key, value));518                            }519                            else520                            {521                                propertyToModify.Remove();522                            }523                        }524                    }525                    else526                    {527                        // The file doesn't already exist and we want to write to it or it exists with no content.528                        // A new file will be created that contains only this value.529                        // If the file doesn't exist and a we don't want to write to it, no action is needed.530                        if (addValue)531                        {532                            jsonObject = new JObject(new JProperty(key, value));533                        }534                        else535                        {536                            return;537                        }538                    }539                }540 541                // Reset the stream position to the beginning so that the542                // changes to the file can be written to disk543                fs.Seek(0, SeekOrigin.Begin);544 545                // Update the file with new content546                using (StreamWriter streamWriter = new StreamWriter(fs))547                using (JsonTextWriter jsonWriter = new JsonTextWriter(streamWriter))548                {549                    // The entire document exists within the root JObject.550                    // I just need to write that object to produce the document.551                    jsonObject.WriteTo(jsonWriter);552 553                    // This trims the file if the file shrank. If the file grew,554                    // it is a no-op. The purpose is to trim extraneous characters555                    // from the file stream when the resultant JObject is smaller556                    // than the input JObject.557                    fs.SetLength(fs.Position);558                }559 560                // Refresh the configuration cache.561                Interlocked.Exchange(ref configRoots[(int)scope], jsonObject);562            }563            finally564            {565                fileLock.ExitWriteLock();566            }567        }568 569        /// <summary>570        /// TODO: Should this return success, fail, or throw?571        /// </summary>572        /// <typeparam name="T">The type of value to write.</typeparam>573        /// <param name="scope">The ConfigScope of the file to update.</param>574        /// <param name="key">The string key of the value.</param>575        /// <param name="value">The value to write.</param>576        private void WriteValueToFile<T>(ConfigScope scope, string key, T value)577        {578            if (scope == ConfigScope.CurrentUser && !Directory.Exists(perUserConfigDirectory))579            {580                Directory.CreateDirectory(perUserConfigDirectory);581            }582 583            UpdateValueInFile<T>(scope, key, value, true);584        }585 586        /// <summary>587        /// TODO: Should this return success, fail, or throw?588        /// </summary>589        /// <typeparam name="T">The type of value to remove.</typeparam>590        /// <param name="scope">The ConfigScope of the file to update.</param>591        /// <param name="key">The string key of the value.</param>592        private void RemoveValueFromFile<T>(ConfigScope scope, string key)593        {594            string fileName = GetConfigFilePath(scope);595            // Optimization: If the file doesn't exist, there is nothing to remove596            if (File.Exists(fileName))597            {598                UpdateValueInFile<T>(scope, key, default(T), false);599            }600        }601    }602 603    #region GroupPolicy Configs604 605    /// <summary>606    /// The GroupPolicy related settings used in PowerShell are as follows in Registry:607    ///  - Software\Policies\Microsoft\PowerShellCore -- { EnableScripts (0 or 1); ExecutionPolicy (string) }608    ///      SubKeys                  Name-Value-Pairs609    ///      - ScriptBlockLogging     { EnableScriptBlockLogging (0 or 1); EnableScriptBlockInvocationLogging (0 or 1) }610    ///      - ModuleLogging          { EnableModuleLogging (0 or 1); ModuleNames (string[]) }611    ///      - Transcription          { EnableTranscripting (0 or 1); OutputDirectory (string); EnableInvocationHeader (0 or 1) }612    ///      - UpdatableHelp          { DefaultSourcePath (string) }613    ///      - ConsoleSessionConfiguration { EnableConsoleSessionConfiguration (0 or 1); ConsoleSessionConfigurationName (string) }614    ///  - Software\Policies\Microsoft\Windows\EventLog615    ///     SubKeys                   Name-Value-Pairs616    ///      - ProtectedEventLogging  { EnableProtectedEventLogging (0 or 1); EncryptionCertificate (string[]) }617    ///618    /// The JSON representation is in sync with the 'PowerShellPolicies' type. Here is an example:619    /// {620    ///   "PowerShellPolicies": {621    ///     "ScriptExecution": {622    ///       "ExecutionPolicy": "RemoteSigned"623    ///     },624    ///     "ScriptBlockLogging": {625    ///       "EnableScriptBlockInvocationLogging": true,626    ///       "EnableScriptBlockLogging": false627    ///     },628    ///     "ProtectedEventLogging": {629    ///       "EnableProtectedEventLogging": false,630    ///       "EncryptionCertificate": [631    ///         "Joe"632    ///       ]633    ///     },634    ///     "Transcription": {635    ///       "EnableTranscripting": true,636    ///       "EnableInvocationHeader": true,637    ///       "OutputDirectory": "c:\\tmp"638    ///     },639    ///     "UpdatableHelp": {640    ///       "DefaultSourcePath": "f:\\temp"641    ///     },642    ///     "ConsoleSessionConfiguration": {643    ///       "EnableConsoleSessionConfiguration": true,644    ///       "ConsoleSessionConfigurationName": "name"645    ///     }646    ///   }647    /// }648    /// </summary>649    internal sealed class PowerShellPolicies650    {651        public ScriptExecution ScriptExecution { get; set; }652 653        public ScriptBlockLogging ScriptBlockLogging { get; set; }654 655        public ModuleLogging ModuleLogging { get; set; }656 657        public ProtectedEventLogging ProtectedEventLogging { get; set; }658 659        public Transcription Transcription { get; set; }660 661        public UpdatableHelp UpdatableHelp { get; set; }662 663        public ConsoleSessionConfiguration ConsoleSessionConfiguration { get; set; }664    }665 666    internal abstract class PolicyBase { }667 668    /// <summary>669    /// Setting about ScriptExecution.670    /// </summary>671    internal sealed class ScriptExecution : PolicyBase672    {673        public string ExecutionPolicy { get; set; }674 675        public bool? EnableScripts { get; set; }676    }677 678    /// <summary>679    /// Setting about ScriptBlockLogging.680    /// </summary>681    internal sealed class ScriptBlockLogging : PolicyBase682    {683        public bool? EnableScriptBlockInvocationLogging { get; set; }684 685        public bool? EnableScriptBlockLogging { get; set; }686    }687 688    /// <summary>689    /// Setting about ModuleLogging.690    /// </summary>691    internal sealed class ModuleLogging : PolicyBase692    {693        public bool? EnableModuleLogging { get; set; }694 695        public string[] ModuleNames { get; set; }696    }697 698    /// <summary>699    /// Setting about Transcription.700    /// </summary>701    internal sealed class Transcription : PolicyBase702    {703        public bool? EnableTranscripting { get; set; }704 705        public bool? EnableInvocationHeader { get; set; }706 707        public string OutputDirectory { get; set; }708    }709 710    /// <summary>711    /// Setting about UpdatableHelp.712    /// </summary>713    internal sealed class UpdatableHelp : PolicyBase714    {715        public bool? EnableUpdateHelpDefaultSourcePath { get; set; }716 717        public string DefaultSourcePath { get; set; }718    }719 720    /// <summary>721    /// Setting about ConsoleSessionConfiguration.722    /// </summary>723    internal sealed class ConsoleSessionConfiguration : PolicyBase724    {725        public bool? EnableConsoleSessionConfiguration { get; set; }726 727        public string ConsoleSessionConfigurationName { get; set; }728    }729 730    /// <summary>731    /// Setting about ProtectedEventLogging.732    /// </summary>733    internal sealed class ProtectedEventLogging : PolicyBase734    {735        public bool? EnableProtectedEventLogging { get; set; }736 737        public string[] EncryptionCertificate { get; set; }738    }739 740    #endregion741}742