mirror of
https://github.com/PowerShell/PowerShell.git
synced 2024-11-26 19:34:22 +08:00
Add the -ConfigurationFile
command line parameter to pwsh
to support local session configuration (#17447)
This commit is contained in:
parent
97e8e0c43a
commit
a8d55851e0
@ -227,6 +227,7 @@ namespace Microsoft.PowerShell
|
||||
Version = 0x00800000, // -Version | -v
|
||||
WindowStyle = 0x01000000, // -WindowStyle | -w
|
||||
WorkingDirectory = 0x02000000, // -WorkingDirectory | -wd
|
||||
ConfigurationFile = 0x04000000, // -ConfigurationFile
|
||||
// Enum values for specified ExecutionPolicy
|
||||
EPUnrestricted = 0x0000000100000000, // ExecutionPolicy unrestricted
|
||||
EPRemoteSigned = 0x0000000200000000, // ExecutionPolicy remote signed
|
||||
@ -370,6 +371,15 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
}
|
||||
|
||||
internal string? ConfigurationFile
|
||||
{
|
||||
get
|
||||
{
|
||||
AssertArgumentsParsed();
|
||||
return _configurationFile;
|
||||
}
|
||||
}
|
||||
|
||||
internal string? ConfigurationName
|
||||
{
|
||||
get
|
||||
@ -915,6 +925,19 @@ namespace Microsoft.PowerShell
|
||||
_noInteractive = false;
|
||||
ParametersUsed |= ParameterBitmap.Interactive;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "configurationfile", "configurationfile"))
|
||||
{
|
||||
++i;
|
||||
if (i >= args.Length)
|
||||
{
|
||||
SetCommandLineError(
|
||||
CommandLineParameterParserStrings.MissingConfigurationFileArgument);
|
||||
break;
|
||||
}
|
||||
|
||||
_configurationFile = args[i];
|
||||
ParametersUsed |= ParameterBitmap.ConfigurationFile;
|
||||
}
|
||||
else if (MatchSwitch(switchKey, "configurationname", "config"))
|
||||
{
|
||||
++i;
|
||||
@ -1474,6 +1497,7 @@ namespace Microsoft.PowerShell
|
||||
private bool _namedPipeServerMode;
|
||||
private bool _sshServerMode;
|
||||
private bool _showVersion;
|
||||
private string? _configurationFile;
|
||||
private string? _configurationName;
|
||||
private string? _error;
|
||||
private bool _showHelp;
|
||||
|
@ -73,6 +73,9 @@ namespace Microsoft.PowerShell
|
||||
/// <param name="helpText">
|
||||
/// Help text for minishell. This is displayed on 'minishell -?'.
|
||||
/// </param>
|
||||
/// <param name="issProvidedExternally">
|
||||
/// True when an external caller provides an InitialSessionState object, which can conflict with '-ConfigurationFile' argument.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The exit code for the shell.
|
||||
///
|
||||
@ -99,7 +102,10 @@ namespace Microsoft.PowerShell
|
||||
/// Anyone checking the exit code of the shell or monitor can mask off the high word to determine the exit code passed
|
||||
/// by the script that the shell last executed.
|
||||
/// </returns>
|
||||
internal static int Start(string bannerText, string helpText)
|
||||
internal static int Start(
|
||||
string bannerText,
|
||||
string helpText,
|
||||
bool issProvidedExternally)
|
||||
{
|
||||
#if DEBUG
|
||||
if (Environment.GetEnvironmentVariable("POWERSHELL_DEBUG_STARTUP") != null)
|
||||
@ -111,6 +117,12 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check for external InitialSessionState configuration conflict with '-ConfigurationFile' argument.
|
||||
if (issProvidedExternally && !string.IsNullOrEmpty(s_cpp.ConfigurationFile))
|
||||
{
|
||||
throw new ConsoleHostStartupException(ConsoleHostStrings.ShellCannotBeStartedWithConfigConflict);
|
||||
}
|
||||
|
||||
// put PSHOME in front of PATH so that calling `powershell` within `powershell` always starts the same running version
|
||||
string path = Environment.GetEnvironmentVariable("PATH");
|
||||
string pshome = Utils.DefaultPowerShellAppBase + Path.PathSeparator;
|
||||
@ -188,7 +200,6 @@ namespace Microsoft.PowerShell
|
||||
#if !UNIX
|
||||
TaskbarJumpList.CreateRunAsAdministratorJumpList();
|
||||
#endif
|
||||
|
||||
// First check for and handle PowerShell running in a server mode.
|
||||
if (s_cpp.ServerMode)
|
||||
{
|
||||
@ -197,7 +208,9 @@ namespace Microsoft.PowerShell
|
||||
StdIOProcessMediator.Run(
|
||||
initialCommand: s_cpp.InitialCommand,
|
||||
workingDirectory: s_cpp.WorkingDirectory,
|
||||
configurationName: null);
|
||||
configurationName: null,
|
||||
configurationFile: s_cpp.ConfigurationFile,
|
||||
combineErrOutStream: false);
|
||||
exitCode = 0;
|
||||
}
|
||||
else if (s_cpp.SSHServerMode)
|
||||
@ -207,7 +220,9 @@ namespace Microsoft.PowerShell
|
||||
StdIOProcessMediator.Run(
|
||||
initialCommand: s_cpp.InitialCommand,
|
||||
workingDirectory: null,
|
||||
configurationName: null);
|
||||
configurationName: null,
|
||||
configurationFile: s_cpp.ConfigurationFile,
|
||||
combineErrOutStream: true);
|
||||
exitCode = 0;
|
||||
}
|
||||
else if (s_cpp.NamedPipeServerMode)
|
||||
@ -301,8 +316,8 @@ namespace Microsoft.PowerShell
|
||||
PowerShellConfig.Instance.SetSystemConfigFilePath(s_cpp.SettingsFile);
|
||||
}
|
||||
|
||||
// Check registry setting for a Group Policy ConfigurationName entry and
|
||||
// use it to override anything set by the user.
|
||||
// Check registry setting for a Group Policy ConfigurationName entry,
|
||||
// and use it to override anything set by the user on the command line.
|
||||
// It depends on setting file so 'SetSystemConfigFilePath()' should be called before.
|
||||
s_cpp.ConfigurationName = CommandLineParameterParser.GetConfigurationNameFromGroupPolicy();
|
||||
}
|
||||
@ -1489,7 +1504,7 @@ namespace Microsoft.PowerShell
|
||||
|
||||
// NTRAID#Windows Out Of Band Releases-915506-2005/09/09
|
||||
// Removed HandleUnexpectedExceptions infrastructure
|
||||
exitCode = DoRunspaceLoop(cpp.InitialCommand, cpp.SkipProfiles, cpp.Args, cpp.StaMode, cpp.ConfigurationName);
|
||||
exitCode = DoRunspaceLoop(cpp.InitialCommand, cpp.SkipProfiles, cpp.Args, cpp.StaMode, cpp.ConfigurationName, cpp.ConfigurationFile);
|
||||
}
|
||||
while (false);
|
||||
|
||||
@ -1502,13 +1517,19 @@ namespace Microsoft.PowerShell
|
||||
/// <returns>
|
||||
/// The process exit code to be returned by Main.
|
||||
/// </returns>
|
||||
private uint DoRunspaceLoop(string initialCommand, bool skipProfiles, Collection<CommandParameter> initialCommandArgs, bool staMode, string configurationName)
|
||||
private uint DoRunspaceLoop(
|
||||
string initialCommand,
|
||||
bool skipProfiles,
|
||||
Collection<CommandParameter> initialCommandArgs,
|
||||
bool staMode,
|
||||
string configurationName,
|
||||
string configurationFilePath)
|
||||
{
|
||||
ExitCode = ExitCodeSuccess;
|
||||
|
||||
while (!ShouldEndSession)
|
||||
{
|
||||
RunspaceCreationEventArgs args = new RunspaceCreationEventArgs(initialCommand, skipProfiles, staMode, configurationName, initialCommandArgs);
|
||||
RunspaceCreationEventArgs args = new RunspaceCreationEventArgs(initialCommand, skipProfiles, staMode, configurationName, configurationFilePath, initialCommandArgs);
|
||||
CreateRunspace(args);
|
||||
|
||||
if (ExitCode == ExitCodeInitFailure) { break; }
|
||||
@ -1584,14 +1605,12 @@ namespace Microsoft.PowerShell
|
||||
return e;
|
||||
}
|
||||
|
||||
private void CreateRunspace(object runspaceCreationArgs)
|
||||
private void CreateRunspace(RunspaceCreationEventArgs runspaceCreationArgs)
|
||||
{
|
||||
RunspaceCreationEventArgs args = null;
|
||||
try
|
||||
{
|
||||
args = runspaceCreationArgs as RunspaceCreationEventArgs;
|
||||
Dbg.Assert(args != null, "Event Arguments to CreateRunspace should not be null");
|
||||
DoCreateRunspace(args.InitialCommand, args.SkipProfiles, args.StaMode, args.ConfigurationName, args.InitialCommandArgs);
|
||||
Dbg.Assert(runspaceCreationArgs != null, "Arguments to CreateRunspace should not be null.");
|
||||
DoCreateRunspace(runspaceCreationArgs);
|
||||
}
|
||||
catch (ConsoleHostStartupException startupException)
|
||||
{
|
||||
@ -1603,7 +1622,7 @@ namespace Microsoft.PowerShell
|
||||
/// <summary>
|
||||
/// Check if a screen reviewer utility is running.
|
||||
/// When a screen reader is running, we don't auto-load the PSReadLine module at startup,
|
||||
/// since PSReadLine is not accessibility-firendly enough as of today.
|
||||
/// since PSReadLine is not accessibility-friendly enough as of today.
|
||||
/// </summary>
|
||||
private bool IsScreenReaderActive()
|
||||
{
|
||||
@ -1646,12 +1665,33 @@ namespace Microsoft.PowerShell
|
||||
/// Opens and Initializes the Host's sole Runspace. Processes the startup scripts and runs any command passed on the
|
||||
/// command line.
|
||||
/// </summary>
|
||||
private void DoCreateRunspace(string initialCommand, bool skipProfiles, bool staMode, string configurationName, Collection<CommandParameter> initialCommandArgs)
|
||||
/// <param name="args">Runspace creation event arguments.</param>
|
||||
private void DoCreateRunspace(RunspaceCreationEventArgs args)
|
||||
{
|
||||
Dbg.Assert(_runspaceRef == null, "runspace should be null");
|
||||
Dbg.Assert(_runspaceRef == null, "_runspaceRef field should be null");
|
||||
Dbg.Assert(DefaultInitialSessionState != null, "DefaultInitialSessionState should not be null");
|
||||
s_runspaceInitTracer.WriteLine("Calling RunspaceFactory.CreateRunspace");
|
||||
|
||||
// Use session configuration file if provided.
|
||||
bool customConfigurationProvided = false;
|
||||
if (!string.IsNullOrEmpty(args.ConfigurationFilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Replace DefaultInitialSessionState with the initial state configuration defined by the file.
|
||||
DefaultInitialSessionState = InitialSessionState.CreateFromSessionConfigurationFile(
|
||||
path: args.ConfigurationFilePath,
|
||||
roleVerifier: null,
|
||||
validateFile: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ConsoleHostStartupException(ConsoleHostStrings.ShellCannotBeStarted, ex);
|
||||
}
|
||||
|
||||
customConfigurationProvided = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Runspace consoleRunspace = null;
|
||||
@ -1667,7 +1707,7 @@ namespace Microsoft.PowerShell
|
||||
// powershell -command "Update-Module PSReadline"
|
||||
// This should work just fine as long as no other instances of PowerShell are running.
|
||||
ReadOnlyCollection<ModuleSpecification> defaultImportModulesList = null;
|
||||
if (LoadPSReadline())
|
||||
if (!customConfigurationProvided && LoadPSReadline())
|
||||
{
|
||||
if (IsScreenReaderActive())
|
||||
{
|
||||
@ -1682,7 +1722,7 @@ namespace Microsoft.PowerShell
|
||||
consoleRunspace = RunspaceFactory.CreateRunspace(this, DefaultInitialSessionState);
|
||||
try
|
||||
{
|
||||
OpenConsoleRunspace(consoleRunspace, staMode);
|
||||
OpenConsoleRunspace(consoleRunspace, args.StaMode);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@ -1702,7 +1742,7 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
|
||||
consoleRunspace = RunspaceFactory.CreateRunspace(this, DefaultInitialSessionState);
|
||||
OpenConsoleRunspace(consoleRunspace, staMode);
|
||||
OpenConsoleRunspace(consoleRunspace, args.StaMode);
|
||||
}
|
||||
|
||||
Runspace.PrimaryRunspace = consoleRunspace;
|
||||
@ -1734,7 +1774,7 @@ namespace Microsoft.PowerShell
|
||||
_readyForInputTimeInMS = (DateTime.Now - Process.GetCurrentProcess().StartTime).TotalMilliseconds;
|
||||
#endif
|
||||
|
||||
DoRunspaceInitialization(skipProfiles, initialCommand, configurationName, initialCommandArgs);
|
||||
DoRunspaceInitialization(args);
|
||||
}
|
||||
|
||||
private static void OpenConsoleRunspace(Runspace runspace, bool staMode)
|
||||
@ -1751,7 +1791,7 @@ namespace Microsoft.PowerShell
|
||||
runspace.Open();
|
||||
}
|
||||
|
||||
private void DoRunspaceInitialization(bool skipProfiles, string initialCommand, string configurationName, Collection<CommandParameter> initialCommandArgs)
|
||||
private void DoRunspaceInitialization(RunspaceCreationEventArgs args)
|
||||
{
|
||||
if (_runspaceRef.Runspace.Debugger != null)
|
||||
{
|
||||
@ -1786,13 +1826,13 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(configurationName))
|
||||
if (!string.IsNullOrEmpty(args.ConfigurationName))
|
||||
{
|
||||
// If an endpoint configuration is specified then create a loop-back remote runspace targeting
|
||||
// the endpoint and push onto runspace ref stack. Ignore profile and configuration scripts.
|
||||
try
|
||||
{
|
||||
RemoteRunspace remoteRunspace = HostUtilities.CreateConfiguredRunspace(configurationName, this);
|
||||
RemoteRunspace remoteRunspace = HostUtilities.CreateConfiguredRunspace(args.ConfigurationName, this);
|
||||
remoteRunspace.ShouldCloseOnPop = true;
|
||||
PushRunspace(remoteRunspace);
|
||||
|
||||
@ -1827,7 +1867,7 @@ namespace Microsoft.PowerShell
|
||||
currentUserProfile,
|
||||
currentUserHostSpecificProfile));
|
||||
|
||||
if (!skipProfiles)
|
||||
if (!args.SkipProfiles)
|
||||
{
|
||||
// Run the profiles.
|
||||
// Profiles are run in the following order:
|
||||
@ -1887,11 +1927,11 @@ namespace Microsoft.PowerShell
|
||||
|
||||
tempPipeline.Commands.Add(c);
|
||||
|
||||
if (initialCommandArgs != null)
|
||||
if (args.InitialCommandArgs != null)
|
||||
{
|
||||
// add the args passed to the command.
|
||||
|
||||
foreach (CommandParameter p in initialCommandArgs)
|
||||
foreach (CommandParameter p in args.InitialCommandArgs)
|
||||
{
|
||||
c.Parameters.Add(p);
|
||||
}
|
||||
@ -1948,19 +1988,19 @@ namespace Microsoft.PowerShell
|
||||
ReportException(e1, exec);
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(initialCommand))
|
||||
else if (!string.IsNullOrEmpty(args.InitialCommand))
|
||||
{
|
||||
// Run the command passed on the command line
|
||||
|
||||
s_tracer.WriteLine("running initial command");
|
||||
|
||||
Pipeline tempPipeline = exec.CreatePipeline(initialCommand, true);
|
||||
Pipeline tempPipeline = exec.CreatePipeline(args.InitialCommand, true);
|
||||
|
||||
if (initialCommandArgs != null)
|
||||
if (args.InitialCommandArgs != null)
|
||||
{
|
||||
// add the args passed to the command.
|
||||
|
||||
foreach (CommandParameter p in initialCommandArgs)
|
||||
foreach (CommandParameter p in args.InitialCommandArgs)
|
||||
{
|
||||
tempPipeline.Commands[0].Parameters.Add(p);
|
||||
}
|
||||
@ -1976,7 +2016,7 @@ namespace Microsoft.PowerShell
|
||||
ParseError[] errors;
|
||||
|
||||
// Detect if they're using input. If so, read from it.
|
||||
Ast parsedInput = Parser.ParseInput(initialCommand, out tokens, out errors);
|
||||
Ast parsedInput = Parser.ParseInput(args.InitialCommand, out tokens, out errors);
|
||||
if (AstSearcher.IsUsingDollarInput(parsedInput))
|
||||
{
|
||||
executionOptions |= Executor.ExecutionOptions.ReadInputObjects;
|
||||
@ -3021,12 +3061,14 @@ namespace Microsoft.PowerShell
|
||||
bool skipProfiles,
|
||||
bool staMode,
|
||||
string configurationName,
|
||||
string configurationFilePath,
|
||||
Collection<CommandParameter> initialCommandArgs)
|
||||
{
|
||||
InitialCommand = initialCommand;
|
||||
SkipProfiles = skipProfiles;
|
||||
StaMode = staMode;
|
||||
ConfigurationName = configurationName;
|
||||
ConfigurationFilePath = configurationFilePath;
|
||||
InitialCommandArgs = initialCommandArgs;
|
||||
}
|
||||
|
||||
@ -3038,6 +3080,8 @@ namespace Microsoft.PowerShell
|
||||
|
||||
internal string ConfigurationName { get; set; }
|
||||
|
||||
internal string ConfigurationFilePath { get; set; }
|
||||
|
||||
internal Collection<CommandParameter> InitialCommandArgs { get; set; }
|
||||
}
|
||||
} // namespace
|
||||
|
@ -21,7 +21,12 @@ namespace Microsoft.PowerShell
|
||||
/// <returns>An integer value which should be used as exit code for the process.</returns>
|
||||
public static int Start(string? bannerText, string? helpText, string[] args)
|
||||
{
|
||||
return Start(InitialSessionState.CreateDefault2(), bannerText, helpText, args);
|
||||
return StartImpl(
|
||||
initialSessionState: InitialSessionState.CreateDefault2(),
|
||||
bannerText,
|
||||
helpText,
|
||||
args,
|
||||
issProvided: false);
|
||||
}
|
||||
|
||||
/// <summary>Entry point in to ConsoleShell. Used to create a custom Powershell console application.</summary>
|
||||
@ -31,6 +36,31 @@ namespace Microsoft.PowerShell
|
||||
/// <param name="args">Commandline parameters specified by user.</param>
|
||||
/// <returns>An integer value which should be used as exit code for the process.</returns>
|
||||
public static int Start(InitialSessionState initialSessionState, string? bannerText, string? helpText, string[] args)
|
||||
{
|
||||
return StartImpl(
|
||||
initialSessionState,
|
||||
bannerText,
|
||||
helpText,
|
||||
args,
|
||||
issProvided: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of entry point to ConsoleShell.
|
||||
/// Used to create a custom Powershell console application.
|
||||
/// </summary>
|
||||
/// <param name="initialSessionState">InitialSessionState to be used by the ConsoleHost.</param>
|
||||
/// <param name="bannerText">Banner text to be displayed by ConsoleHost.</param>
|
||||
/// <param name="helpText">Help text for the shell.</param>
|
||||
/// <param name="args">Commandline parameters specified by user.</param>
|
||||
/// <param name="issProvided">True when the InitialSessionState object is provided by caller.</param>
|
||||
/// <returns>An integer value which should be used as exit code for the process.</returns>
|
||||
private static int StartImpl(
|
||||
InitialSessionState initialSessionState,
|
||||
string? bannerText,
|
||||
string? helpText,
|
||||
string[] args,
|
||||
bool issProvided)
|
||||
{
|
||||
if (initialSessionState == null)
|
||||
{
|
||||
@ -45,7 +75,7 @@ namespace Microsoft.PowerShell
|
||||
ConsoleHost.ParseCommandLine(args);
|
||||
ConsoleHost.DefaultInitialSessionState = initialSessionState;
|
||||
|
||||
return ConsoleHost.Start(bannerText, helpText);
|
||||
return ConsoleHost.Start(bannerText, helpText, issProvided);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,10 @@ namespace Microsoft.PowerShell
|
||||
|
||||
ConsoleHost.DefaultInitialSessionState = InitialSessionState.CreateDefault2();
|
||||
|
||||
exitCode = ConsoleHost.Start(banner, ManagedEntranceStrings.UsageHelp);
|
||||
exitCode = ConsoleHost.Start(
|
||||
bannerText: banner,
|
||||
helpText: ManagedEntranceStrings.UsageHelp,
|
||||
issProvidedExternally: false);
|
||||
}
|
||||
catch (HostException e)
|
||||
{
|
||||
|
@ -187,7 +187,10 @@ Valid formats are:
|
||||
<value>Cannot process the command because -STA and -MTA are both specified. Specify either -STA or -MTA.</value>
|
||||
</data>
|
||||
<data name="MissingConfigurationNameArgument" xml:space="preserve">
|
||||
<value>Cannot process the command because -Configuration requires an argument that is a remote endpoint configuration name. Specify this argument and try again.</value>
|
||||
<value>Cannot process the command because -ConfigurationName requires an argument that is a remote endpoint configuration name. Specify this argument and try again.</value>
|
||||
</data>
|
||||
<data name="MissingConfigurationFileArgument" xml:space="preserve">
|
||||
<value>Cannot process the command because -ConfigurationFile requires an argument that is a session configuration (.pssc) file path. Specify this argument and try again.</value>
|
||||
</data>
|
||||
<data name="MissingCustomPipeNameArgument" xml:space="preserve">
|
||||
<value>Cannot process the command because -CustomPipeName requires an argument that is a name of the pipe you want to use. Specify this argument and try again.</value>
|
||||
|
@ -135,6 +135,9 @@
|
||||
<data name="ShellCannotBeStarted" xml:space="preserve">
|
||||
<value>The shell cannot be started. A failure occurred during initialization:</value>
|
||||
</data>
|
||||
<data name="ShellCannotBeStartedWithConfigConflict" xml:space="preserve">
|
||||
<value>The shell cannot be started. An InitialSessionState object has been provided along with a -ConfigurationFile argument. Both configuration directives cannot be used at the same time.</value>
|
||||
</data>
|
||||
<data name="UnhandledExceptionShutdownMessage" xml:space="preserve">
|
||||
<value>An error has occurred that was not properly handled. Additional information is shown below. The PowerShell process will exit.</value>
|
||||
</data>
|
||||
|
@ -288,6 +288,14 @@ All parameters are case-insensitive.</value>
|
||||
|
||||
Example: "pwsh -ConfigurationName AdminRoles"
|
||||
|
||||
-ConfigurationFile
|
||||
|
||||
Specifies a session configuration (.pssc) file path. The configuration
|
||||
contained in the configuration file will be applied to the PowerShell
|
||||
session.
|
||||
|
||||
Example: "pwsh -ConfigurationFile "C:\ProgramData\PowerShell\MyConfig.pssc"
|
||||
|
||||
-CustomPipeName
|
||||
|
||||
Specifies the name to use for an additional IPC server (named pipe) used
|
||||
|
@ -1312,7 +1312,7 @@ namespace System.Management.Automation.Runspaces
|
||||
/// Creates an initial session state from a PSSC configuration file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the PSSC session configuration file.</param>
|
||||
/// <returns></returns>
|
||||
/// <returns>InitialSessionState object.</returns>
|
||||
public static InitialSessionState CreateFromSessionConfigurationFile(string path)
|
||||
{
|
||||
return CreateFromSessionConfigurationFile(path, null);
|
||||
@ -1327,10 +1327,48 @@ namespace System.Management.Automation.Runspaces
|
||||
/// target session. If you have a WindowsPrincipal for a user, for example, create a Function that
|
||||
/// checks windowsPrincipal.IsInRole().
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public static InitialSessionState CreateFromSessionConfigurationFile(string path, Func<string, bool> roleVerifier)
|
||||
/// <returns>InitialSessionState object.</returns>
|
||||
public static InitialSessionState CreateFromSessionConfigurationFile(
|
||||
string path,
|
||||
Func<string, bool> roleVerifier)
|
||||
{
|
||||
Remoting.DISCPowerShellConfiguration discConfiguration = new Remoting.DISCPowerShellConfiguration(path, roleVerifier);
|
||||
return CreateFromSessionConfigurationFile(path, roleVerifier, validateFile: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an initial session state from a PSSC configuration file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the PSSC session configuration file.</param>
|
||||
/// <param name="roleVerifier">
|
||||
/// The verifier that PowerShell should call to determine if groups in the Role entry apply to the
|
||||
/// target session. If you have a WindowsPrincipal for a user, for example, create a Function that
|
||||
/// checks windowsPrincipal.IsInRole().
|
||||
/// </param>
|
||||
/// <param name="validateFile">Validates the file contents for supported SessionState options.</param>
|
||||
/// <returns>InitialSessionState object.</returns>
|
||||
public static InitialSessionState CreateFromSessionConfigurationFile(
|
||||
string path,
|
||||
Func<string, bool> roleVerifier,
|
||||
bool validateFile)
|
||||
{
|
||||
if (path is null)
|
||||
{
|
||||
throw new PSArgumentNullException(nameof(path));
|
||||
}
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
throw new PSInvalidOperationException(
|
||||
StringUtil.Format(ConsoleInfoErrorStrings.ConfigurationFileDoesNotExist, path));
|
||||
}
|
||||
|
||||
if (!path.EndsWith(".pssc", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new PSInvalidOperationException(
|
||||
StringUtil.Format(ConsoleInfoErrorStrings.NotConfigurationFile, path));
|
||||
}
|
||||
|
||||
Remoting.DISCPowerShellConfiguration discConfiguration = new Remoting.DISCPowerShellConfiguration(path, roleVerifier, validateFile);
|
||||
return discConfiguration.GetInitialSessionState(null);
|
||||
}
|
||||
|
||||
@ -5241,7 +5279,6 @@ end {
|
||||
{ "Enable-PSSessionConfiguration", new SessionStateCmdletEntry("Enable-PSSessionConfiguration", typeof(EnablePSSessionConfigurationCommand), helpFile) },
|
||||
{ "Get-PSSessionCapability", new SessionStateCmdletEntry("Get-PSSessionCapability", typeof(GetPSSessionCapabilityCommand), helpFile) },
|
||||
{ "Get-PSSessionConfiguration", new SessionStateCmdletEntry("Get-PSSessionConfiguration", typeof(GetPSSessionConfigurationCommand), helpFile) },
|
||||
{ "New-PSSessionConfigurationFile", new SessionStateCmdletEntry("New-PSSessionConfigurationFile", typeof(NewPSSessionConfigurationFileCommand), helpFile) },
|
||||
{ "Receive-PSSession", new SessionStateCmdletEntry("Receive-PSSession", typeof(ReceivePSSessionCommand), helpFile) },
|
||||
{ "Register-PSSessionConfiguration", new SessionStateCmdletEntry("Register-PSSessionConfiguration", typeof(RegisterPSSessionConfigurationCommand), helpFile) },
|
||||
{ "Unregister-PSSessionConfiguration", new SessionStateCmdletEntry("Unregister-PSSessionConfiguration", typeof(UnregisterPSSessionConfigurationCommand), helpFile) },
|
||||
@ -5273,6 +5310,7 @@ end {
|
||||
{ "New-ModuleManifest", new SessionStateCmdletEntry("New-ModuleManifest", typeof(NewModuleManifestCommand), helpFile) },
|
||||
{ "New-PSRoleCapabilityFile", new SessionStateCmdletEntry("New-PSRoleCapabilityFile", typeof(NewPSRoleCapabilityFileCommand), helpFile) },
|
||||
{ "New-PSSession", new SessionStateCmdletEntry("New-PSSession", typeof(NewPSSessionCommand), helpFile) },
|
||||
{ "New-PSSessionConfigurationFile", new SessionStateCmdletEntry("New-PSSessionConfigurationFile", typeof(NewPSSessionConfigurationFileCommand), helpFile) },
|
||||
{ "New-PSSessionOption", new SessionStateCmdletEntry("New-PSSessionOption", typeof(NewPSSessionOptionCommand), helpFile) },
|
||||
{ "New-PSTransportOption", new SessionStateCmdletEntry("New-PSTransportOption", typeof(NewPSTransportOptionCommand), helpFile) },
|
||||
{ "Out-Default", new SessionStateCmdletEntry("Out-Default", typeof(OutDefaultCommand), helpFile) },
|
||||
|
@ -15,7 +15,6 @@ using System.Text;
|
||||
|
||||
namespace Microsoft.PowerShell.Commands
|
||||
{
|
||||
#if !UNIX
|
||||
/// <summary>
|
||||
/// New-PSSessionConfigurationFile command implementation
|
||||
///
|
||||
@ -1126,7 +1125,6 @@ namespace Microsoft.PowerShell.Commands
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// New-PSRoleCapabilityFile command implementation
|
||||
|
@ -22,6 +22,8 @@ using Dbg = System.Management.Automation.Diagnostics;
|
||||
|
||||
namespace System.Management.Automation.Remoting
|
||||
{
|
||||
#region WSMan endpoint configuration
|
||||
|
||||
/// <summary>
|
||||
/// This struct is used to represent contents from configuration xml. The
|
||||
/// XML is passed to plugins by WSMan API.
|
||||
@ -56,6 +58,8 @@ namespace System.Management.Automation.Remoting
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
internal string StartupScript;
|
||||
// this field is used only by an Out-Of-Process (IPC) server process
|
||||
internal string InitializationScriptForOutOfProcessRunspace;
|
||||
@ -71,6 +75,10 @@ namespace System.Management.Automation.Remoting
|
||||
internal PSSessionConfigurationData SessionConfigurationData;
|
||||
internal string ConfigFilePath;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Using optionName and optionValue updates the current object.
|
||||
/// </summary>
|
||||
@ -324,6 +332,8 @@ namespace System.Management.Automation.Remoting
|
||||
throw PSTraceSource.NewArgumentException("typeToLoad", RemotingErrorIdStrings.UnableToLoadType,
|
||||
EndPointConfigurationTypeName, ConfigurationDataFromXML.INITPARAMETERSTOKEN);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -450,7 +460,8 @@ namespace System.Management.Automation.Remoting
|
||||
...
|
||||
</InitializationParameters>
|
||||
*/
|
||||
internal static ConfigurationDataFromXML LoadEndPointConfiguration(string shellId,
|
||||
internal static ConfigurationDataFromXML LoadEndPointConfiguration(
|
||||
string shellId,
|
||||
string initializationParameters)
|
||||
{
|
||||
ConfigurationDataFromXML configData = null;
|
||||
@ -798,6 +809,8 @@ namespace System.Management.Automation.Remoting
|
||||
/// </summary>
|
||||
internal sealed class DefaultRemotePowerShellConfiguration : PSSessionConfiguration
|
||||
{
|
||||
#region Method overrides
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="senderInfo"></param>
|
||||
@ -852,9 +865,15 @@ namespace System.Management.Automation.Remoting
|
||||
|
||||
return sessionState;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Declarative Initial Session Configuration
|
||||
#endregion
|
||||
|
||||
#region Declarative InitialSession Configuration
|
||||
|
||||
#region Supporting types
|
||||
|
||||
/// <summary>
|
||||
/// Specifies type of initial session state to use. Valid values are Empty and Default.
|
||||
@ -898,6 +917,10 @@ namespace System.Management.Automation.Remoting
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ConfigFileConstants
|
||||
|
||||
/// <summary>
|
||||
/// Configuration file constants.
|
||||
/// </summary>
|
||||
@ -1355,6 +1378,8 @@ namespace System.Management.Automation.Remoting
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DISC Utilities
|
||||
|
||||
/// <summary>
|
||||
@ -1681,6 +1706,8 @@ namespace System.Management.Automation.Remoting
|
||||
|
||||
#endregion
|
||||
|
||||
#region DISCPowerShellConfiguration
|
||||
|
||||
/// <summary>
|
||||
/// Creates an initial session state based on the configuration language for PSSC files.
|
||||
/// </summary>
|
||||
@ -1706,7 +1733,11 @@ namespace System.Management.Automation.Remoting
|
||||
/// target session. If you have a WindowsPrincipal for a user, for example, create a Function that
|
||||
/// checks windowsPrincipal.IsInRole().
|
||||
/// </param>
|
||||
internal DISCPowerShellConfiguration(string configFile, Func<string, bool> roleVerifier)
|
||||
/// <param name="validateFile">Validate file for supported configuration options.</param>
|
||||
internal DISCPowerShellConfiguration(
|
||||
string configFile,
|
||||
Func<string, bool> roleVerifier,
|
||||
bool validateFile = false)
|
||||
{
|
||||
_configFile = configFile;
|
||||
if (roleVerifier == null)
|
||||
@ -1726,6 +1757,12 @@ namespace System.Management.Automation.Remoting
|
||||
configFile, out scriptName);
|
||||
|
||||
_configHash = DISCUtils.LoadConfigFile(Runspace.DefaultRunspace.ExecutionContext, script);
|
||||
|
||||
if (validateFile)
|
||||
{
|
||||
DISCFileValidation.ValidateContents(_configHash);
|
||||
}
|
||||
|
||||
MergeRoleRulesIntoConfigHash(roleVerifier);
|
||||
MergeRoleCapabilitiesIntoConfigHash();
|
||||
|
||||
@ -2890,4 +2927,110 @@ namespace System.Management.Automation.Remoting
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region DISCFileValidation
|
||||
|
||||
internal static class DISCFileValidation
|
||||
{
|
||||
// Set of supported configuration options for a PowerShell InitialSessionState.
|
||||
#if UNIX
|
||||
private static readonly HashSet<string> SupportedConfigOptions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"AliasDefinitions",
|
||||
"AssembliesToLoad",
|
||||
"Author",
|
||||
"CompanyName",
|
||||
"Copyright",
|
||||
"Description",
|
||||
"EnvironmentVariables",
|
||||
"FormatsToProcess",
|
||||
"FunctionDefinitions",
|
||||
"GUID",
|
||||
"LanguageMode",
|
||||
"ModulesToImport",
|
||||
"MountUserDrive",
|
||||
"SchemaVersion",
|
||||
"ScriptsToProcess",
|
||||
"SessionType",
|
||||
"TranscriptDirectory",
|
||||
"TypesToProcess",
|
||||
"UserDriveMaximumSize",
|
||||
"VisibleAliases",
|
||||
"VisibleCmdlets",
|
||||
"VariableDefinitions",
|
||||
"VisibleExternalCommands",
|
||||
"VisibleFunctions",
|
||||
"VisibleProviders"
|
||||
};
|
||||
#else
|
||||
private static readonly HashSet<string> SupportedConfigOptions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"AliasDefinitions",
|
||||
"AssembliesToLoad",
|
||||
"Author",
|
||||
"CompanyName",
|
||||
"Copyright",
|
||||
"Description",
|
||||
"EnvironmentVariables",
|
||||
"ExecutionPolicy",
|
||||
"FormatsToProcess",
|
||||
"FunctionDefinitions",
|
||||
"GUID",
|
||||
"LanguageMode",
|
||||
"ModulesToImport",
|
||||
"MountUserDrive",
|
||||
"SchemaVersion",
|
||||
"ScriptsToProcess",
|
||||
"SessionType",
|
||||
"TranscriptDirectory",
|
||||
"TypesToProcess",
|
||||
"UserDriveMaximumSize",
|
||||
"VisibleAliases",
|
||||
"VisibleCmdlets",
|
||||
"VariableDefinitions",
|
||||
"VisibleExternalCommands",
|
||||
"VisibleFunctions",
|
||||
"VisibleProviders"
|
||||
};
|
||||
#endif
|
||||
|
||||
// These are configuration options for WSMan (WinRM) endpoint configurations, that
|
||||
// appearand in .pssc files, but are not part of PowerShell InitialSessionState.
|
||||
private static readonly HashSet<string> UnsupportedConfigOptions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"GroupManagedServiceAccount",
|
||||
"PowerShellVersion",
|
||||
"RequiredGroups",
|
||||
"RoleDefinitions",
|
||||
"RunAsVirtualAccount",
|
||||
"RunAsVirtualAccountGroups"
|
||||
};
|
||||
|
||||
internal static void ValidateContents(Hashtable configHash)
|
||||
{
|
||||
foreach (var key in configHash.Keys)
|
||||
{
|
||||
if (key is not string keyName)
|
||||
{
|
||||
throw new PSInvalidOperationException(RemotingErrorIdStrings.DISCInvalidConfigKeyType);
|
||||
}
|
||||
|
||||
if (UnsupportedConfigOptions.Contains(keyName))
|
||||
{
|
||||
throw new PSInvalidOperationException(
|
||||
StringUtil.Format(RemotingErrorIdStrings.DISCUnsupportedConfigName, keyName));
|
||||
}
|
||||
|
||||
if (!SupportedConfigOptions.Contains(keyName))
|
||||
{
|
||||
throw new PSInvalidOperationException(
|
||||
StringUtil.Format(RemotingErrorIdStrings.DISCUnknownConfigName, keyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@ -1980,10 +1980,10 @@ namespace System.Management.Automation.Remoting.Client
|
||||
break;
|
||||
}
|
||||
|
||||
if (data.StartsWith(System.Management.Automation.Remoting.Server.NamedPipeErrorTextWriter.ErrorPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
if (data.StartsWith(System.Management.Automation.Remoting.Server.FormattedErrorTextWriter.ErrorPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Error message from the server.
|
||||
string errorData = data.Substring(System.Management.Automation.Remoting.Server.NamedPipeErrorTextWriter.ErrorPrefix.Length);
|
||||
string errorData = data.Substring(System.Management.Automation.Remoting.Server.FormattedErrorTextWriter.ErrorPrefix.Length);
|
||||
HandleErrorDataReceived(errorData);
|
||||
}
|
||||
else
|
||||
@ -2592,6 +2592,12 @@ namespace System.Management.Automation.Remoting.Server
|
||||
_stdOutWriter = outWriter;
|
||||
_stdErrWriter = errWriter;
|
||||
_cmdTransportManagers = new Dictionary<Guid, OutOfProcessServerTransportManager>();
|
||||
|
||||
this.WSManTransportErrorOccured += (object sender, TransportErrorOccuredEventArgs e) =>
|
||||
{
|
||||
string msg = e.Exception.TransportMessage ?? e.Exception.InnerException?.Message ?? string.Empty;
|
||||
_stdErrWriter.WriteLine(StringUtil.Format(RemotingErrorIdStrings.RemoteTransportError, msg));
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -306,10 +306,16 @@ namespace System.Management.Automation.Remoting
|
||||
PSOpcode.Connect, PSTask.None,
|
||||
PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic,
|
||||
requestDetails.ToString(), senderInfo.UserInfo.Identity.Name, requestDetails.resourceUri);
|
||||
ServerRemoteSession remoteShellSession = ServerRemoteSession.CreateServerRemoteSession(senderInfo,
|
||||
requestDetails.resourceUri,
|
||||
extraInfo,
|
||||
serverTransportMgr);
|
||||
|
||||
ServerRemoteSession remoteShellSession = ServerRemoteSession.CreateServerRemoteSession(
|
||||
senderInfo: senderInfo,
|
||||
configurationProviderId: requestDetails.resourceUri,
|
||||
initializationParameters: extraInfo,
|
||||
transportManager: serverTransportMgr,
|
||||
initialCommand: null, // Not used by WinRM endpoint.
|
||||
configurationName: null, // Not used by WinRM endpoint, which has its own configuration.
|
||||
configurationFile: null, // Same.
|
||||
initialLocation: null); // Same.
|
||||
|
||||
if (remoteShellSession == null)
|
||||
{
|
||||
|
@ -300,6 +300,7 @@ namespace System.Management.Automation.Remoting.Server
|
||||
|
||||
protected OutOfProcessServerSessionTransportManager CreateSessionTransportManager(
|
||||
string configurationName,
|
||||
string configurationFile,
|
||||
PSRemotingCryptoHelperServer cryptoHelper,
|
||||
string workingDirectory)
|
||||
{
|
||||
@ -317,14 +318,20 @@ namespace System.Management.Automation.Remoting.Server
|
||||
senderInfo = new PSSenderInfo(userPrincipal, "http://localhost");
|
||||
#endif
|
||||
|
||||
OutOfProcessServerSessionTransportManager tm = new OutOfProcessServerSessionTransportManager(originalStdOut, originalStdErr, cryptoHelper);
|
||||
var tm = new OutOfProcessServerSessionTransportManager(
|
||||
originalStdOut,
|
||||
originalStdErr,
|
||||
cryptoHelper);
|
||||
|
||||
ServerRemoteSession.CreateServerRemoteSession(
|
||||
senderInfo,
|
||||
_initialCommand,
|
||||
tm,
|
||||
configurationName,
|
||||
workingDirectory);
|
||||
senderInfo: senderInfo,
|
||||
configurationProviderId: "Microsoft.PowerShell",
|
||||
initializationParameters: string.Empty,
|
||||
transportManager: tm,
|
||||
initialCommand: _initialCommand,
|
||||
configurationName: configurationName,
|
||||
configurationFile: configurationFile,
|
||||
initialLocation: workingDirectory);
|
||||
|
||||
return tm;
|
||||
}
|
||||
@ -333,11 +340,16 @@ namespace System.Management.Automation.Remoting.Server
|
||||
string initialCommand,
|
||||
PSRemotingCryptoHelperServer cryptoHelper,
|
||||
string workingDirectory,
|
||||
string configurationName)
|
||||
string configurationName,
|
||||
string configurationFile)
|
||||
{
|
||||
_initialCommand = initialCommand;
|
||||
|
||||
sessionTM = CreateSessionTransportManager(configurationName, cryptoHelper, workingDirectory);
|
||||
sessionTM = CreateSessionTransportManager(
|
||||
configurationName: configurationName,
|
||||
configurationFile: configurationFile,
|
||||
cryptoHelper: cryptoHelper,
|
||||
workingDirectory: workingDirectory);
|
||||
|
||||
try
|
||||
{
|
||||
@ -348,7 +360,11 @@ namespace System.Management.Automation.Remoting.Server
|
||||
{
|
||||
if (sessionTM == null)
|
||||
{
|
||||
sessionTM = CreateSessionTransportManager(configurationName, cryptoHelper, workingDirectory);
|
||||
sessionTM = CreateSessionTransportManager(
|
||||
configurationName: configurationName,
|
||||
configurationFile: configurationFile,
|
||||
cryptoHelper: cryptoHelper,
|
||||
workingDirectory: workingDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
@ -432,7 +448,8 @@ namespace System.Management.Automation.Remoting.Server
|
||||
/// It will replace StdIn,StdOut and StdErr stream with TextWriter.Null. This is
|
||||
/// to make sure these streams are totally used by our Mediator.
|
||||
/// </summary>
|
||||
private StdIOProcessMediator() : base(true)
|
||||
/// <param name="combineErrOutStream">Redirects remoting errors to the Out stream.</param>
|
||||
private StdIOProcessMediator(bool combineErrOutStream) : base(exitProcessOnError: true)
|
||||
{
|
||||
// Create input stream reader from Console standard input stream.
|
||||
// We don't use the provided Console.In TextReader because it can have
|
||||
@ -441,16 +458,23 @@ namespace System.Management.Automation.Remoting.Server
|
||||
// stream encoding. This way the stream encoding is determined by the
|
||||
// stream BOM as needed.
|
||||
originalStdIn = new StreamReader(Console.OpenStandardInput(), true);
|
||||
Console.SetIn(TextReader.Null);
|
||||
|
||||
// replacing StdOut with Null so that no other app messes with the
|
||||
// original stream
|
||||
// Remoting errors can optionally be written to stdErr or stdOut with
|
||||
// special formatting.
|
||||
originalStdOut = new OutOfProcessTextWriter(Console.Out);
|
||||
Console.SetOut(TextWriter.Null);
|
||||
if (combineErrOutStream)
|
||||
{
|
||||
originalStdErr = new FormattedErrorTextWriter(Console.Out);
|
||||
}
|
||||
else
|
||||
{
|
||||
originalStdErr = new OutOfProcessTextWriter(Console.Error);
|
||||
}
|
||||
|
||||
// replacing StdErr with Null so that no other app messes with the
|
||||
// original stream
|
||||
originalStdErr = new OutOfProcessTextWriter(Console.Error);
|
||||
// Replacing StdIn, StdOut, StdErr with Null so that no other app messes with the
|
||||
// original streams.
|
||||
Console.SetIn(TextReader.Null);
|
||||
Console.SetOut(TextWriter.Null);
|
||||
Console.SetError(TextWriter.Null);
|
||||
}
|
||||
|
||||
@ -464,10 +488,14 @@ namespace System.Management.Automation.Remoting.Server
|
||||
/// <param name="initialCommand">Specifies the initialization script.</param>
|
||||
/// <param name="workingDirectory">Specifies the initial working directory. The working directory is set before the initial command.</param>
|
||||
/// <param name="configurationName">Specifies an optional configuration name that configures the endpoint session.</param>
|
||||
/// <param name="configurationFile">Specifies an optional path to a configuration (.pssc) file for the session.</param>
|
||||
/// <param name="combineErrOutStream">Specifies the option to write remoting errors to stdOut stream, with special formatting.</param>
|
||||
internal static void Run(
|
||||
string initialCommand,
|
||||
string workingDirectory,
|
||||
string configurationName)
|
||||
string configurationName,
|
||||
string configurationFile,
|
||||
bool combineErrOutStream)
|
||||
{
|
||||
lock (SyncObject)
|
||||
{
|
||||
@ -477,14 +505,15 @@ namespace System.Management.Automation.Remoting.Server
|
||||
return;
|
||||
}
|
||||
|
||||
s_singletonInstance = new StdIOProcessMediator();
|
||||
s_singletonInstance = new StdIOProcessMediator(combineErrOutStream);
|
||||
}
|
||||
|
||||
s_singletonInstance.Start(
|
||||
initialCommand: initialCommand,
|
||||
cryptoHelper: new PSRemotingCryptoHelperServer(),
|
||||
workingDirectory: workingDirectory,
|
||||
configurationName: configurationName);
|
||||
configurationName: configurationName,
|
||||
configurationFile: configurationFile);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -526,7 +555,7 @@ namespace System.Management.Automation.Remoting.Server
|
||||
// Create transport reader/writers from named pipe.
|
||||
originalStdIn = namedPipeServer.TextReader;
|
||||
originalStdOut = new OutOfProcessTextWriter(namedPipeServer.TextWriter);
|
||||
originalStdErr = new NamedPipeErrorTextWriter(namedPipeServer.TextWriter);
|
||||
originalStdErr = new FormattedErrorTextWriter(namedPipeServer.TextWriter);
|
||||
|
||||
#if !UNIX
|
||||
// Flow impersonation as needed.
|
||||
@ -557,17 +586,18 @@ namespace System.Management.Automation.Remoting.Server
|
||||
initialCommand: initialCommand,
|
||||
cryptoHelper: new PSRemotingCryptoHelperServer(),
|
||||
workingDirectory: null,
|
||||
configurationName: namedPipeServer.ConfigurationName);
|
||||
configurationName: namedPipeServer.ConfigurationName,
|
||||
configurationFile: null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal sealed class NamedPipeErrorTextWriter : OutOfProcessTextWriter
|
||||
internal sealed class FormattedErrorTextWriter : OutOfProcessTextWriter
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
internal NamedPipeErrorTextWriter(
|
||||
internal FormattedErrorTextWriter(
|
||||
TextWriter textWriter) : base(textWriter)
|
||||
{ }
|
||||
|
||||
@ -575,6 +605,8 @@ namespace System.Management.Automation.Remoting.Server
|
||||
|
||||
#region Base class overrides
|
||||
|
||||
// Write error data to stream with 'ErrorPrefix' prefix that will
|
||||
// be interpreted by the client.
|
||||
public override void WriteLine(string data)
|
||||
{
|
||||
string dataToWrite = (data != null) ? ErrorPrefix + data : null;
|
||||
@ -632,7 +664,8 @@ namespace System.Management.Automation.Remoting.Server
|
||||
initialCommand: initialCommand,
|
||||
cryptoHelper: new PSRemotingCryptoHelperServer(),
|
||||
workingDirectory: null,
|
||||
configurationName: configurationName);
|
||||
configurationName: configurationName,
|
||||
configurationFile: null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -89,6 +89,10 @@ namespace System.Management.Automation.Remoting
|
||||
// Creates a pushed remote runspace session created with this configuration name.
|
||||
private string _configurationName;
|
||||
|
||||
// Specifies an optional .pssc configuration file path for out-of-proc session use.
|
||||
// The .pssc file is used to configure the runspace for the endpoint session.
|
||||
private string _configurationFile;
|
||||
|
||||
// Specifies an initial location of the powershell session.
|
||||
private string _initialLocation;
|
||||
|
||||
@ -173,7 +177,9 @@ namespace System.Management.Automation.Remoting
|
||||
/// xml.
|
||||
/// </param>
|
||||
/// <param name="transportManager"></param>
|
||||
/// <param name="initialCommand">Optional initial command used for OutOfProc sessions.</param>
|
||||
/// <param name="configurationName">Optional configuration endpoint name for OutOfProc sessions.</param>
|
||||
/// <param name="configurationFile">Optional configuration file (.pssc) path for OutOfProc sessions.</param>
|
||||
/// <param name="initialLocation">Optional configuration initial location of the powershell session.</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
@ -192,8 +198,10 @@ namespace System.Management.Automation.Remoting
|
||||
string configurationProviderId,
|
||||
string initializationParameters,
|
||||
AbstractServerSessionTransportManager transportManager,
|
||||
string configurationName = null,
|
||||
string initialLocation = null)
|
||||
string initialCommand,
|
||||
string configurationName,
|
||||
string configurationFile,
|
||||
string initialLocation)
|
||||
{
|
||||
Dbg.Assert(
|
||||
(senderInfo != null) && (senderInfo.UserInfo != null),
|
||||
@ -215,7 +223,9 @@ namespace System.Management.Automation.Remoting
|
||||
initializationParameters,
|
||||
transportManager)
|
||||
{
|
||||
_initScriptForOutOfProcRS = initialCommand,
|
||||
_configurationName = configurationName,
|
||||
_configurationFile = configurationFile,
|
||||
_initialLocation = initialLocation
|
||||
};
|
||||
|
||||
@ -226,33 +236,6 @@ namespace System.Management.Automation.Remoting
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by OutOfProcessServerMediator to create a remote session.
|
||||
/// </summary>
|
||||
/// <param name="senderInfo"></param>
|
||||
/// <param name="initializationScriptForOutOfProcessRunspace"></param>
|
||||
/// <param name="transportManager"></param>
|
||||
/// <param name="configurationName"></param>
|
||||
/// <param name="initialLocation"></param>
|
||||
/// <returns></returns>
|
||||
internal static ServerRemoteSession CreateServerRemoteSession(
|
||||
PSSenderInfo senderInfo,
|
||||
string initializationScriptForOutOfProcessRunspace,
|
||||
AbstractServerSessionTransportManager transportManager,
|
||||
string configurationName,
|
||||
string initialLocation)
|
||||
{
|
||||
ServerRemoteSession result = CreateServerRemoteSession(
|
||||
senderInfo,
|
||||
"Microsoft.PowerShell",
|
||||
string.Empty,
|
||||
transportManager,
|
||||
configurationName: configurationName,
|
||||
initialLocation: initialLocation);
|
||||
result._initScriptForOutOfProcRS = initializationScriptForOutOfProcessRunspace;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides
|
||||
@ -754,8 +737,7 @@ namespace System.Management.Automation.Remoting
|
||||
// Get Initial Session State from custom session config suppliers
|
||||
// like Exchange.
|
||||
ConfigurationDataFromXML configurationData =
|
||||
PSSessionConfiguration.LoadEndPointConfiguration(_configProviderId,
|
||||
_initParameters);
|
||||
PSSessionConfiguration.LoadEndPointConfiguration(_configProviderId, _initParameters);
|
||||
// used by Out-Of-Proc (IPC) runspace.
|
||||
configurationData.InitializationScriptForOutOfProcessRunspace = _initScriptForOutOfProcRS;
|
||||
// start with data from configuration XML and then override with data
|
||||
@ -763,8 +745,6 @@ namespace System.Management.Automation.Remoting
|
||||
_maxRecvdObjectSize = configurationData.MaxReceivedObjectSizeMB;
|
||||
_maxRecvdDataSizeCommand = configurationData.MaxReceivedCommandSizeMB;
|
||||
|
||||
DISCPowerShellConfiguration discProvider = null;
|
||||
|
||||
if (string.IsNullOrEmpty(configurationData.ConfigFilePath))
|
||||
{
|
||||
_sessionConfigProvider = configurationData.CreateEndPointConfigurationInstance();
|
||||
@ -772,11 +752,8 @@ namespace System.Management.Automation.Remoting
|
||||
else
|
||||
{
|
||||
System.Security.Principal.WindowsPrincipal windowsPrincipal = new System.Security.Principal.WindowsPrincipal(_senderInfo.UserInfo.WindowsIdentity);
|
||||
|
||||
Func<string, bool> validator = (role) => windowsPrincipal.IsInRole(role);
|
||||
|
||||
discProvider = new DISCPowerShellConfiguration(configurationData.ConfigFilePath, validator);
|
||||
_sessionConfigProvider = discProvider;
|
||||
_sessionConfigProvider = new DISCPowerShellConfiguration(configurationData.ConfigFilePath, validator);
|
||||
}
|
||||
|
||||
// exchange of ApplicationArguments and ApplicationPrivateData is be done as early as possible
|
||||
@ -787,6 +764,7 @@ namespace System.Management.Automation.Remoting
|
||||
|
||||
if (configurationData.SessionConfigurationData != null)
|
||||
{
|
||||
// Use the provided WinRM endpoint runspace configuration information.
|
||||
try
|
||||
{
|
||||
rsSessionStateToUse =
|
||||
@ -797,8 +775,21 @@ namespace System.Management.Automation.Remoting
|
||||
rsSessionStateToUse = _sessionConfigProvider.GetInitialSessionState(_senderInfo);
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(_configurationFile))
|
||||
{
|
||||
// Use the optional _configurationFile parameter to create the endpoint runspace configuration.
|
||||
// This parameter is only used by Out-Of-Proc transports (not WinRM transports).
|
||||
var discConfiguration = new Remoting.DISCPowerShellConfiguration(
|
||||
configFile: _configurationFile,
|
||||
roleVerifier: null,
|
||||
validateFile: true);
|
||||
rsSessionStateToUse = discConfiguration.GetInitialSessionState(_senderInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a runspace configuration based on the provided PSSessionConfiguration provider.
|
||||
// This can be either a 'default' configuration, or third party configuration PSSessionConfiguration provider object.
|
||||
// So far, only Exchange provides a custom PSSessionConfiguration provider implementation.
|
||||
rsSessionStateToUse = _sessionConfigProvider.GetInitialSessionState(_senderInfo);
|
||||
}
|
||||
|
||||
|
@ -234,4 +234,10 @@
|
||||
<data name="ExportConsoleCannotDeleteFile" xml:space="preserve">
|
||||
<value>The Save operation failed. Cannot remove the file {0}.</value>
|
||||
</data>
|
||||
<data name="ConfigurationFileDoesNotExist" xml:space="preserve">
|
||||
<value>The provided configuration file '{0}' does not exist.</value>
|
||||
</data>
|
||||
<data name="NotConfigurationFile" xml:space="preserve">
|
||||
<value>The provided configuration file '{0}' must have a .pssc file extension.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
@ -1705,4 +1705,13 @@ SSH client process terminated before connection could be established.</value>
|
||||
<data name="InvalidPSSessionArgument" xml:space="preserve">
|
||||
<value>The Runspace argument to Create must be a non-null RemoteRunspace object.</value>
|
||||
</data>
|
||||
<data name="DISCInvalidConfigKeyType" xml:space="preserve">
|
||||
<value>The session configuration hash table contains an invalid key type. Keys should be string types.</value>
|
||||
</data>
|
||||
<data name="DISCUnsupportedConfigName" xml:space="preserve">
|
||||
<value>The session configuration file contains an unsupported configuration option: {0}. This is a remoting endpoint configuration option, that does not apply to PowerShell session state.</value>
|
||||
</data>
|
||||
<data name="DISCUnknownConfigName" xml:space="preserve">
|
||||
<value>The session configuration file contains an unknown configuration option: {0}.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
@ -230,6 +230,25 @@ Describe "SSHRemoting Basic Tests" -tags CI {
|
||||
VerifySession $script:sessions[1]
|
||||
Write-Verbose -Verbose "It Complete"
|
||||
}
|
||||
|
||||
It "Verifies the 'pwshconfig' configured endpoint." {
|
||||
Write-Verbose -Verbose "It Starting: Verifies the 'pwshconfig' configured endpoint."
|
||||
$script:session = TryNewPSSession -HostName localhost -Subsystem 'pwshconfig'
|
||||
$script:session | Should -Not -BeNullOrEmpty
|
||||
# Configured session should be in ConstrainedLanguage mode.
|
||||
$sessionLangMode = Invoke-Command -Session $script:session -ScriptBlock { "$($ExecutionContext.SessionState.LanguageMode)" }
|
||||
$sessionLangMode | Should -BeExactly "ConstrainedLanguage"
|
||||
Write-Verbose -Verbose "It Complete"
|
||||
}
|
||||
|
||||
<#
|
||||
It "Verifes that 'pwshbroken' throws expected error for missing config file." {
|
||||
Write-Verbose -Verbose "It Starting: Verifes that 'pwshbroken' throws expected error for missing config file."
|
||||
{ $script:session = TryNewPSSession -HostName localhost -Subsystem 'pwshbroken' } | Should -Throw
|
||||
$script:session = $null
|
||||
Write-Verbose -Verbose "It Complete"
|
||||
}
|
||||
#>
|
||||
}
|
||||
|
||||
function TryCreateRunspace
|
||||
@ -270,7 +289,7 @@ Describe "SSHRemoting Basic Tests" -tags CI {
|
||||
}
|
||||
}
|
||||
|
||||
if ($null -eq $rs)
|
||||
if (($null -eq $rs) -or !($rs -is [runspace]))
|
||||
{
|
||||
$message = "Runspace open unable to connect to SSH remoting endpoint after two attempts. Error: $($connectionError.Message)"
|
||||
throw [System.Management.Automation.PSInvalidOperationException]::new($message)
|
||||
@ -319,7 +338,7 @@ Describe "SSHRemoting Basic Tests" -tags CI {
|
||||
|
||||
AfterEach {
|
||||
Write-Verbose -Verbose "Starting Runspace close AfterEach"
|
||||
if ($script:rs -ne $null) { $script:rs.Dispose() }
|
||||
if (($script:rs -ne $null) -and ($script:rs -is [runspace])) { $script:rs.Dispose() }
|
||||
Write-Verbose -Verbose "AfterEach complete"
|
||||
}
|
||||
|
||||
|
@ -183,6 +183,19 @@ namespace PowerShell.Hosting.SDK.Tests
|
||||
Assert.Equal(42, ret);
|
||||
}
|
||||
|
||||
/* Test disabled because CommandLineParser is static and can only be intialized once (above in TestConsoleShellScenario)
|
||||
/// <summary>
|
||||
/// ConsoleShell cannot start with both InitialSessionState and -ConfigurationFile argument configurations specified.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public static void TestConsoleShellConfigConflictError()
|
||||
{
|
||||
var iss = System.Management.Automation.Runspaces.InitialSessionState.CreateDefault2();
|
||||
int ret = ConsoleShell.Start(iss, "BannerText", string.Empty, new string[] { @"-ConfigurationFile ""noneSuch""" });
|
||||
Assert.Equal(70, ret); // ExitCodeInitFailure.
|
||||
}
|
||||
*/
|
||||
|
||||
[Fact]
|
||||
public static void TestBuiltInModules()
|
||||
{
|
||||
|
@ -375,7 +375,7 @@ Describe "Verify approved aliases list" -Tags "CI" {
|
||||
"Cmdlet", "New-PSDrive", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "Low"
|
||||
"Cmdlet", "New-PSRoleCapabilityFile", "", $( $CoreWindows -or $CoreUnix), "", "", "None"
|
||||
"Cmdlet", "New-PSSession", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None"
|
||||
"Cmdlet", "New-PSSessionConfigurationFile", "", $($FullCLR -or $CoreWindows ), "", "", "None"
|
||||
"Cmdlet", "New-PSSessionConfigurationFile", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None"
|
||||
"Cmdlet", "New-PSSessionOption", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None"
|
||||
"Cmdlet", "New-PSTransportOption", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None"
|
||||
"Cmdlet", "New-Service", "", $($FullCLR -or $CoreWindows ), "", "", "Medium"
|
||||
|
@ -556,7 +556,28 @@ function Install-SSHRemotingOnLinux
|
||||
Write-Verbose -Verbose "Running Enable-SSHRemoting ..."
|
||||
Write-Verbose -Verbose "PSScriptRoot: $PSScriptRoot"
|
||||
$modulePath = "${PSScriptRoot}\..\Microsoft.PowerShell.RemotingTools\Microsoft.PowerShell.RemotingTools.psd1"
|
||||
$cmdLine = "Import-Module ${modulePath}; Enable-SSHRemoting -SSHDConfigFilePath /etc/ssh/sshd_config -PowerShellFilePath $PowerShellPath -Force"
|
||||
$sshdFilePath = '/etc/ssh/sshd_config'
|
||||
|
||||
# First create a default 'powershell' named endpoint.
|
||||
$cmdLine = "Import-Module ${modulePath}; Enable-SSHRemoting -SSHDConfigFilePath $sshdFilePath -PowerShellFilePath $PowerShellPath -Force"
|
||||
Write-Verbose -Verbose "CmdLine: $cmdLine"
|
||||
sudo pwsh -c $cmdLine
|
||||
|
||||
# Next create a 'pwshconfig' named configured endpoint.
|
||||
# Configuration file:
|
||||
$configFilePath = Join-Path -Path "$env:HOME" -ChildPath 'PSTestConfig.pssc'
|
||||
'@{
|
||||
GUID = "4d667b90-25f8-47d5-9c90-619b27954748"
|
||||
Author = "Microsoft"
|
||||
Description = "Test local PowerShell session configuration"
|
||||
LanguageMode = "ConstrainedLanguage"
|
||||
}' | Out-File -FilePath $configFilePath
|
||||
$cmdLine = "Import-Module ${modulePath}; Enable-SSHRemoting -SSHDConfigFilePath $sshdFilePath -PowerShellFilePath $PowerShellPath -ConfigFilePath $configFilePath -SubsystemName 'pwshconfig' -Force"
|
||||
Write-Verbose -Verbose "CmdLine: $cmdLine"
|
||||
sudo pwsh -c $cmdLine
|
||||
|
||||
# Finally create a 'pwshbroken' named configured endpoint.
|
||||
$cmdLine = "Import-Module ${modulePath}; Enable-SSHRemoting -SSHDConfigFilePath $sshdFilePath -PowerShellFilePath $PowerShellPath -ConfigFilePath '$HOME/NoSuch.pssc' -SubsystemName 'pwshbroken' -Force"
|
||||
Write-Verbose -Verbose "CmdLine: $cmdLine"
|
||||
sudo pwsh -c $cmdLine
|
||||
|
||||
|
@ -85,14 +85,17 @@ class SSHRemotingConfig
|
||||
[PlatformInfo] $platformInfo
|
||||
[SSHSubSystemEntry[]] $psSubSystemEntries = @()
|
||||
[string] $configFilePath
|
||||
[string] $subsystemName
|
||||
$configComponents = @()
|
||||
|
||||
SSHRemotingConfig(
|
||||
[PlatformInfo] $platInfo,
|
||||
[string] $configFilePath)
|
||||
[string] $configFilePath,
|
||||
[string] $subsystemName)
|
||||
{
|
||||
$this.platformInfo = $platInfo
|
||||
$this.configFilePath = $configFilePath
|
||||
$this.subsystemName = $subsystemName
|
||||
$this.ParseSSHRemotingConfig()
|
||||
}
|
||||
|
||||
@ -121,7 +124,7 @@ class SSHRemotingConfig
|
||||
$components = $this.SplitConfigLine($line)
|
||||
$this.configComponents += @{ Line = $line; Components = $components }
|
||||
|
||||
if (($components[0] -eq "Subsystem") -and ($components[1] -eq "powershell"))
|
||||
if (($components[0] -eq "Subsystem") -and ($components[1] -eq $this.subsystemName))
|
||||
{
|
||||
$entry = [SSHSubSystemEntry]::New()
|
||||
$entry.subSystemLine = $line
|
||||
@ -143,7 +146,9 @@ function UpdateConfiguration
|
||||
{
|
||||
param (
|
||||
[SSHRemotingConfig] $config,
|
||||
[string] $PowerShellPath
|
||||
[string] $PowerShellPath,
|
||||
[string] $SubsystemName,
|
||||
[string] $ConfigFilePath
|
||||
)
|
||||
|
||||
#
|
||||
@ -152,15 +157,19 @@ function UpdateConfiguration
|
||||
|
||||
# Subsystem
|
||||
[System.Collections.Generic.List[string]] $newContents = [System.Collections.Generic.List[string]]::new()
|
||||
$psSubSystemEntry = "Subsystem powershell {0} {1} {2} {3}" -f $powerShellPath, "-SSHS", "-NoProfile", "-NoLogo"
|
||||
$subSystemAdded = $false
|
||||
$psSubSystemEntry = "Subsystem {0} {1} -SSHS -NoProfile -NoLogo" -f $SubsystemName, $powerShellPath
|
||||
if (![string]::IsNullOrEmpty($ConfigFilePath))
|
||||
{
|
||||
$psSubSystemEntry += " -ConfigurationFile {0}" -f $ConfigFilePath
|
||||
}
|
||||
|
||||
$subSystemAdded = $false
|
||||
foreach ($lineItem in $config.configComponents)
|
||||
{
|
||||
$line = $lineItem.Line
|
||||
$components = $lineItem.Components
|
||||
|
||||
if ($components[0] -eq "SubSystem")
|
||||
if ($components[0] -eq "Subsystem")
|
||||
{
|
||||
if (! $subSystemAdded)
|
||||
{
|
||||
@ -169,7 +178,7 @@ function UpdateConfiguration
|
||||
$subSystemAdded = $true
|
||||
}
|
||||
|
||||
if ($components[1] -eq "powershell")
|
||||
if ($components[1] -eq $SubsystemName)
|
||||
{
|
||||
# Remove all existing powershell subsystem entries
|
||||
continue
|
||||
@ -324,6 +333,10 @@ function Enable-SSHRemoting
|
||||
|
||||
[string] $PowerShellFilePath,
|
||||
|
||||
[string] $SubsystemName = "powershell",
|
||||
|
||||
[string] $ConfigFilePath,
|
||||
|
||||
[switch] $Force
|
||||
)
|
||||
|
||||
@ -475,7 +488,7 @@ function Enable-SSHRemoting
|
||||
WriteLine "$SSHDConfigFilePath" -AppendLines 1
|
||||
|
||||
# Get the SSHD configurtion
|
||||
$sshdConfig = [SSHRemotingConfig]::new($platformInfo, $SSHDConfigFilePath)
|
||||
$sshdConfig = [SSHRemotingConfig]::new($platformInfo, $SSHDConfigFilePath, $SubsystemName)
|
||||
|
||||
if ($sshdConfig.psSubSystemEntries.Count -gt 0)
|
||||
{
|
||||
@ -499,7 +512,7 @@ function Enable-SSHRemoting
|
||||
{
|
||||
WriteLine "Updating configuration file ..." -PrependLines 1 -AppendLines 1
|
||||
|
||||
UpdateConfiguration $sshdConfig $PowerShellToUse
|
||||
UpdateConfiguration $sshdConfig $PowerShellToUse $SubsystemName $ConfigFilePath
|
||||
|
||||
WriteLine "The configuration file has been updated:" -PrependLines 1
|
||||
WriteLine $sshdConfig.configFilePath -AppendLines 1
|
||||
|
@ -24,6 +24,7 @@ namespace PSTests.Parallel
|
||||
Assert.False(cpp.AbortStartup);
|
||||
Assert.Empty(cpp.Args);
|
||||
Assert.Null(cpp.ConfigurationName);
|
||||
Assert.Null(cpp.ConfigurationFile);
|
||||
Assert.Null(cpp.CustomPipeName);
|
||||
Assert.Null(cpp.ErrorMessage);
|
||||
Assert.Null(cpp.ExecutionPolicy);
|
||||
@ -437,6 +438,38 @@ namespace PSTests.Parallel
|
||||
Assert.Null(cpp.ErrorMessage);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("-configurationfile")]
|
||||
public static void TestParameter_ConfigurationFile_No_Name(params string[] commandLine)
|
||||
{
|
||||
var cpp = new CommandLineParameterParser();
|
||||
|
||||
cpp.Parse(commandLine);
|
||||
|
||||
Assert.True(cpp.AbortStartup);
|
||||
Assert.True(cpp.NoExit);
|
||||
Assert.False(cpp.ShowShortHelp);
|
||||
Assert.False(cpp.ShowBanner);
|
||||
Assert.Equal((uint)ConsoleHost.ExitCodeBadCommandLineParameter, cpp.ExitCode);
|
||||
Assert.Equal(CommandLineParameterParserStrings.MissingConfigurationFileArgument, cpp.ErrorMessage);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("-configurationfile", "qwerty")]
|
||||
public static void TestParameter_ConfigurationFile_With_Name(params string[] commandLine)
|
||||
{
|
||||
var cpp = new CommandLineParameterParser();
|
||||
|
||||
cpp.Parse(commandLine);
|
||||
|
||||
Assert.False(cpp.AbortStartup);
|
||||
Assert.True(cpp.NoExit);
|
||||
Assert.False(cpp.ShowShortHelp);
|
||||
Assert.True(cpp.ShowBanner);
|
||||
Assert.Equal("qwerty", cpp.ConfigurationFile);
|
||||
Assert.Null(cpp.ErrorMessage);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("-custompipename")]
|
||||
[InlineData("-cus")]
|
||||
|
Loading…
Reference in New Issue
Block a user