MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections;6using System.Collections.Generic;7using System.Diagnostics.CodeAnalysis;8using System.IO;9using System.Management.Automation;10using System.Management.Automation.Internal;11using System.Management.Automation.Remoting;12using System.Management.Automation.Runspaces;13using System.Management.Automation.Security;14 15namespace Microsoft.PowerShell.Commands16{17 /// <summary>18 /// This cmdlet start invocation of jobs in background.19 /// </summary>20 [Cmdlet(VerbsLifecycle.Start, "Job", DefaultParameterSetName = StartJobCommand.ComputerNameParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096796")]21 [OutputType(typeof(PSRemotingJob))]22 public class StartJobCommand : PSExecutionCmdlet, IDisposable23 {24 #region Private members25 26 private static readonly string s_startJobType = "BackgroundJob";27 28 #endregion29 30 #region Parameters31 32 private const string DefinitionNameParameterSet = "DefinitionName";33 34 /// <summary>35 /// JobDefinition Name.36 /// </summary>37 [Parameter(Position = 0, Mandatory = true,38 ParameterSetName = StartJobCommand.DefinitionNameParameterSet)]39 [ValidateTrustedData]40 [ValidateNotNullOrEmpty]41 public string DefinitionName42 {43 get { return _definitionName; }44 45 set { _definitionName = value; }46 }47 48 private string _definitionName;49 50 /// <summary>51 /// JobDefinition file path.52 /// </summary>53 [Parameter(Position = 1,54 ParameterSetName = StartJobCommand.DefinitionNameParameterSet)]55 [ValidateNotNullOrEmpty]56 public string DefinitionPath57 {58 get { return _definitionPath; }59 60 set { _definitionPath = value; }61 }62 63 private string _definitionPath;64 65 /// <summary>66 /// Job SourceAdapter type for this job definition.67 /// </summary>68 [Parameter(Position = 2,69 ParameterSetName = StartJobCommand.DefinitionNameParameterSet)]70 [ValidateNotNullOrEmpty]71 [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]72 public string Type73 {74 get { return _definitionType; }75 76 set { _definitionType = value; }77 }78 79 private string _definitionType;80 81 /// <summary>82 /// Friendly name for this job object.83 /// </summary>84 [Parameter(ValueFromPipelineByPropertyName = true,85 ParameterSetName = StartJobCommand.FilePathComputerNameParameterSet)]86 [Parameter(ValueFromPipelineByPropertyName = true,87 ParameterSetName = StartJobCommand.ComputerNameParameterSet)]88 [Parameter(ValueFromPipelineByPropertyName = true,89 ParameterSetName = StartJobCommand.LiteralFilePathComputerNameParameterSet)]90 public virtual string Name91 {92 get93 {94 return _name;95 }96 97 set98 {99 if (!string.IsNullOrEmpty(value))100 {101 _name = value;102 }103 }104 }105 106 private string _name;107 108 /// <summary>109 /// Command to execute specified as a string. This can be a single110 /// cmdlet, an expression or anything that can be internally111 /// converted into a ScriptBlock.112 /// </summary>113 /// <remarks>This is used in the in process case with a114 /// "ValueFromPipelineProperty" enabled in order to maintain115 /// compatibility with v1.0</remarks>116 [Parameter(Position = 0,117 Mandatory = true,118 ParameterSetName = StartJobCommand.ComputerNameParameterSet)]119 [ValidateTrustedData]120 [Alias("Command")]121 public override ScriptBlock ScriptBlock122 {123 get124 {125 return base.ScriptBlock;126 }127 128 set129 {130 base.ScriptBlock = value;131 }132 }133 134 #region Suppress PSRemotingBaseCmdlet parameters135 136 // suppress all the parameters from PSRemotingBaseCmdlet137 // which should not be part of Start-PSJob138 139 /// <summary>140 /// Overriding to suppress this parameter.141 /// </summary>142 public override PSSession[] Session143 {144 get145 {146 return null;147 }148 }149 150 /// <summary>151 /// Overriding to suppress this parameter.152 /// </summary>153 public override string[] ComputerName154 {155 get156 {157 return null;158 }159 }160 161 /// <summary>162 /// Not used for OutOfProc jobs. Suppressing this parameter.163 /// </summary>164 public override SwitchParameter EnableNetworkAccess165 {166 get { return false; }167 }168 169 /// <summary>170 /// Suppress SSHTransport.171 /// </summary>172 public override SwitchParameter SSHTransport173 {174 get { return false; }175 }176 177 /// <summary>178 /// Suppress SSHConnection.179 /// </summary>180 public override Hashtable[] SSHConnection181 {182 get { return null; }183 }184 185 /// <summary>186 /// Suppress UserName.187 /// </summary>188 public override string UserName189 {190 get { return null; }191 }192 193 /// <summary>194 /// Suppress KeyFilePath.195 /// </summary>196 public override string KeyFilePath197 {198 get { return null; }199 }200 201 /// <summary>202 /// Suppress HostName.203 /// </summary>204 public override string[] HostName205 {206 get { return null; }207 }208 209 /// <summary>210 /// Suppress Subsystem.211 /// </summary>212 public override string Subsystem213 {214 get { return null; }215 }216 217 #endregion218 219 /// <summary>220 /// Credential to use for this job.221 /// </summary>222 [Parameter(ParameterSetName = StartJobCommand.FilePathComputerNameParameterSet)]223 [Parameter(ParameterSetName = StartJobCommand.ComputerNameParameterSet)]224 [Parameter(ParameterSetName = StartJobCommand.LiteralFilePathComputerNameParameterSet)]225 [Credential]226 public override PSCredential Credential227 {228 get229 {230 return base.Credential;231 }232 233 set234 {235 base.Credential = value;236 }237 }238 239 /// <summary>240 /// Overriding to suppress this parameter.241 /// </summary>242 public override int Port243 {244 get245 {246 return 0;247 }248 }249 250 /// <summary>251 /// Overriding to suppress this parameter.252 /// </summary>253 public override SwitchParameter UseSSL254 {255 get256 {257 return false;258 }259 }260 261 /// <summary>262 /// Overriding to suppress this parameter.263 /// </summary>264 public override string ConfigurationName265 {266 get267 {268 return base.ConfigurationName;269 }270 271 set272 {273 base.ConfigurationName = value;274 }275 }276 277 /// <summary>278 /// Overriding to suppress this parameter.279 /// </summary>280 public override Int32 ThrottleLimit281 {282 get283 {284 return 0;285 }286 }287 288 /// <summary>289 /// Overriding to suppress this parameter.290 /// </summary>291 public override string ApplicationName292 {293 get294 {295 return null;296 }297 }298 299 /// <summary>300 /// Overriding to suppress this parameter.301 /// </summary>302 public override Uri[] ConnectionUri303 {304 get305 {306 return null;307 }308 }309 310 /// <summary>311 /// Filepath to execute as a script.312 /// </summary>313 [Parameter(314 Position = 0,315 Mandatory = true,316 ParameterSetName = StartJobCommand.FilePathComputerNameParameterSet)]317 [ValidateTrustedData]318 public override string FilePath319 {320 get321 {322 return base.FilePath;323 }324 325 set326 {327 base.FilePath = value;328 }329 }330 331 /// <summary>332 /// Literal Filepath to execute as a script.333 /// </summary>334 [Parameter(335 Mandatory = true,336 ParameterSetName = StartJobCommand.LiteralFilePathComputerNameParameterSet)]337 [ValidateTrustedData]338 [Alias("PSPath", "LP")]339 public string LiteralPath340 {341 get342 {343 return base.FilePath;344 }345 346 set347 {348 base.FilePath = value;349 base.IsLiteralPath = true;350 }351 }352 353 /// <summary>354 /// Use basic authentication to authenticate the user.355 /// </summary>356 [Parameter(ParameterSetName = StartJobCommand.FilePathComputerNameParameterSet)]357 [Parameter(ParameterSetName = StartJobCommand.ComputerNameParameterSet)]358 [Parameter(ParameterSetName = StartJobCommand.LiteralFilePathComputerNameParameterSet)]359 public override AuthenticationMechanism Authentication360 {361 get362 {363 return base.Authentication;364 }365 366 set367 {368 base.Authentication = value;369 }370 }371 372 /// <summary>373 /// Overriding to suppress this parameter.374 /// </summary>375 public override string CertificateThumbprint376 {377 get378 {379 return base.CertificateThumbprint;380 }381 382 set383 {384 base.CertificateThumbprint = value;385 }386 }387 388 /// <summary>389 /// Overriding to suppress this parameter.390 /// </summary>391 public override SwitchParameter AllowRedirection392 {393 get394 {395 return false;396 }397 }398 399 /// <summary>400 /// Overriding to suppress this parameter.401 /// </summary>402 public override Guid[] VMId403 {404 get405 {406 return null;407 }408 }409 410 /// <summary>411 /// Overriding to suppress this parameter.412 /// </summary>413 public override string[] VMName414 {415 get416 {417 return null;418 }419 }420 421 /// <summary>422 /// Overriding to suppress this parameter.423 /// </summary>424 public override string[] ContainerId425 {426 get427 {428 return null;429 }430 }431 432 /// <summary>433 /// Overriding to suppress this parameter.434 /// </summary>435 public override SwitchParameter RunAsAdministrator436 {437 get438 {439 return false;440 }441 }442 443 /// <summary>444 /// Extended Session Options for controlling the session creation. Use445 /// "New-WSManSessionOption" cmdlet to supply value for this parameter.446 /// </summary>447 /// <remarks>448 /// This is not declared as a Parameter for Start-PSJob as this is not449 /// used for background jobs.450 /// </remarks>451 public override PSSessionOption SessionOption452 {453 get454 {455 return base.SessionOption;456 }457 458 set459 {460 base.SessionOption = value;461 }462 }463 464 /// <summary>465 /// Script that is used to initialize the background job.466 /// </summary>467 [Parameter(Position = 1,468 ParameterSetName = StartJobCommand.FilePathComputerNameParameterSet)]469 [Parameter(Position = 1,470 ParameterSetName = StartJobCommand.ComputerNameParameterSet)]471 [Parameter(Position = 1,472 ParameterSetName = StartJobCommand.LiteralFilePathComputerNameParameterSet)]473 [ValidateTrustedData]474 public virtual ScriptBlock InitializationScript475 {476 get { return _initScript; }477 478 set { _initScript = value; }479 }480 481 private ScriptBlock _initScript;482 483 /// <summary>484 /// Gets or sets an initial working directory for the powershell background job.485 /// </summary>486 [Parameter]487 [ValidateNotNullOrWhiteSpace]488 public string WorkingDirectory { get; set; }489 490 /// <summary>491 /// Launches the background job as a 32-bit process. This can be used on492 /// 64-bit systems to launch a 32-bit wow process for the background job.493 /// </summary>494 [Parameter(ParameterSetName = StartJobCommand.FilePathComputerNameParameterSet)]495 [Parameter(ParameterSetName = StartJobCommand.ComputerNameParameterSet)]496 [Parameter(ParameterSetName = StartJobCommand.LiteralFilePathComputerNameParameterSet)]497 public virtual SwitchParameter RunAs32 { get; set; }498 499 /// <summary>500 /// Powershell Version to execute the background job.501 /// </summary>502 [Parameter(ParameterSetName = StartJobCommand.FilePathComputerNameParameterSet)]503 [Parameter(ParameterSetName = StartJobCommand.ComputerNameParameterSet)]504 [Parameter(ParameterSetName = StartJobCommand.LiteralFilePathComputerNameParameterSet)]505 [ValidateNotNullOrEmpty]506 public virtual Version PSVersion507 {508 get509 {510 return _psVersion;511 }512 513 set514 {515 // PSVersion value can only be 5.1 for Start-Job.516 if (!(value.Major == 5 && value.Minor == 1))517 {518 throw new ArgumentException(519 StringUtil.Format(RemotingErrorIdStrings.PSVersionParameterOutOfRange, value, "PSVersion"));520 }521 522 _psVersion = value;523 }524 }525 526 private Version _psVersion;527 528 /// <summary>529 /// InputObject.530 /// </summary>531 [Parameter(ValueFromPipeline = true,532 ParameterSetName = StartJobCommand.FilePathComputerNameParameterSet)]533 [Parameter(ValueFromPipeline = true,534 ParameterSetName = StartJobCommand.ComputerNameParameterSet)]535 [Parameter(ValueFromPipeline = true,536 ParameterSetName = StartJobCommand.LiteralFilePathComputerNameParameterSet)]537 [ValidateTrustedData]538 public override PSObject InputObject539 {540 get { return base.InputObject; }541 542 set { base.InputObject = value; }543 }544 545 /// <summary>546 /// ArgumentList.547 /// </summary>548 [Parameter(ParameterSetName = StartJobCommand.FilePathComputerNameParameterSet)]549 [Parameter(ParameterSetName = StartJobCommand.ComputerNameParameterSet)]550 [Parameter(ParameterSetName = StartJobCommand.LiteralFilePathComputerNameParameterSet)]551 [ValidateTrustedData]552 [Alias("Args")]553 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]554 public override object[] ArgumentList555 {556 get { return base.ArgumentList; }557 558 set { base.ArgumentList = value; }559 }560 561 #endregion Parameters562 563 #region Overrides564 565 /// <summary>566 /// 1. Set the throttling limit and reset operations complete567 /// 2. Create helper objects568 /// 3. For async case, write the async result object down the569 /// pipeline.570 /// </summary>571 protected override void BeginProcessing()572 {573 if (!File.Exists(PowerShellProcessInstance.PwshExePath))574 {575 // The pwsh executable file is not found under $PSHOME.576 // This means that PowerShell is currently being hosted in another application,577 // and 'Start-Job' is not supported by design in that scenario.578 string message = StringUtil.Format(579 RemotingErrorIdStrings.IPCPwshExecutableNotFound,580 PowerShellProcessInstance.PwshExePath);581 582 var errorRecord = new ErrorRecord(583 new PSNotSupportedException(message),584 "IPCPwshExecutableNotFound",585 ErrorCategory.NotInstalled,586 PowerShellProcessInstance.PwshExePath);587 588 ThrowTerminatingError(errorRecord);589 }590 591 if (RunAs32.IsPresent && Environment.Is64BitProcess)592 {593 // We cannot start a 32-bit 'pwsh' process from a 64-bit 'pwsh' installation.594 string message = RemotingErrorIdStrings.RunAs32NotSupported;595 var errorRecord = new ErrorRecord(596 new PSNotSupportedException(message),597 "RunAs32NotSupported",598 ErrorCategory.InvalidOperation,599 targetObject: null);600 601 ThrowTerminatingError(errorRecord);602 }603 604 if (WorkingDirectory != null && !InvokeProvider.Item.IsContainer(WorkingDirectory))605 {606 string message = StringUtil.Format(RemotingErrorIdStrings.StartJobWorkingDirectoryNotFound, WorkingDirectory);607 var errorRecord = new ErrorRecord(608 new DirectoryNotFoundException(message),609 "DirectoryNotFoundException",610 ErrorCategory.InvalidOperation,611 targetObject: null);612 613 ThrowTerminatingError(errorRecord);614 }615 616 if (WorkingDirectory == null)617 {618 try619 {620 WorkingDirectory = SessionState.Internal.CurrentLocation.Path;621 }622 catch (PSInvalidOperationException)623 {624 }625 }626 627 CommandDiscovery.AutoloadModulesWithJobSourceAdapters(this.Context, this.CommandOrigin);628 629 if (ParameterSetName == DefinitionNameParameterSet)630 {631 return;632 }633 634 // since jobs no more depend on WinRM635 // we will have to skip the check for the same636 SkipWinRMCheck = true;637 638 base.BeginProcessing();639 }640 641 /// <summary>642 /// Create a throttle operation using NewProcessConnectionInfo643 /// ie., Out-Of-Process runspace.644 /// </summary>645 protected override void CreateHelpersForSpecifiedComputerNames()646 {647 // If we're in ConstrainedLanguage mode and the system is in lockdown mode,648 // ensure that they haven't specified a ScriptBlock or InitScript - as649 // we can't protect that boundary.650 if ((Context.LanguageMode == PSLanguageMode.ConstrainedLanguage) &&651 (SystemPolicy.GetSystemLockdownPolicy() != SystemEnforcementMode.Enforce) &&652 ((ScriptBlock != null) || (InitializationScript != null)))653 {654 ThrowTerminatingError(655 new ErrorRecord(656 new PSNotSupportedException(RemotingErrorIdStrings.CannotStartJobInconsistentLanguageMode),657 "CannotStartJobInconsistentLanguageMode",658 ErrorCategory.PermissionDenied,659 Context.LanguageMode));660 }661 662 NewProcessConnectionInfo connectionInfo = new NewProcessConnectionInfo(this.Credential);663 connectionInfo.InitializationScript = _initScript;664 connectionInfo.AuthenticationMechanism = this.Authentication;665 connectionInfo.PSVersion = this.PSVersion;666 connectionInfo.WorkingDirectory = this.WorkingDirectory;667 668 RemoteRunspace remoteRunspace = (RemoteRunspace)RunspaceFactory.CreateRunspace(connectionInfo, this.Host,669 Utils.GetTypeTableFromExecutionContextTLS());670 671 remoteRunspace.Events.ReceivedEvents.PSEventReceived += OnRunspacePSEventReceived;672 673 Pipeline pipeline = CreatePipeline(remoteRunspace);674 675 IThrottleOperation operation =676 new ExecutionCmdletHelperComputerName(remoteRunspace, pipeline);677 678 Operations.Add(operation);679 }680 /// <summary>681 /// The expression will be executed in the remote computer if a682 /// remote runspace parameter or computer name is specified. If683 /// none other than command parameter is specified, then it684 /// just executes the command locally without creating a new685 /// remote runspace object.686 /// </summary>687 protected override void ProcessRecord()688 {689 if (ParameterSetName == DefinitionNameParameterSet)690 {691 // Get the Job2 object from the Job Manager for this definition name and start the job.692 string resolvedPath = null;693 if (!string.IsNullOrEmpty(_definitionPath))694 {695 ProviderInfo provider = null;696 System.Collections.ObjectModel.Collection<string> paths =697 this.Context.SessionState.Path.GetResolvedProviderPathFromPSPath(_definitionPath, out provider);698 699 // Only file system paths are allowed.700 if (!provider.NameEquals(this.Context.ProviderNames.FileSystem))701 {702 string message = StringUtil.Format(RemotingErrorIdStrings.StartJobDefinitionPathInvalidNotFSProvider,703 _definitionName,704 _definitionPath,705 provider.FullName);706 WriteError(new ErrorRecord(new RuntimeException(message), "StartJobFromDefinitionNamePathInvalidNotFileSystemProvider",707 ErrorCategory.InvalidArgument, null));708 709 return;710 }711 712 // Only a single file path is allowed.713 if (paths.Count != 1)714 {715 string message = StringUtil.Format(RemotingErrorIdStrings.StartJobDefinitionPathInvalidNotSingle,716 _definitionName,717 _definitionPath);718 WriteError(new ErrorRecord(new RuntimeException(message), "StartJobFromDefinitionNamePathInvalidNotSingle",719 ErrorCategory.InvalidArgument, null));720 721 return;722 }723 724 resolvedPath = paths[0];725 }726 727 List<Job2> jobs = JobManager.GetJobToStart(_definitionName, resolvedPath, _definitionType, this, false);728 729 if (jobs.Count == 0)730 {731 string message = (_definitionType != null) ?732 StringUtil.Format(RemotingErrorIdStrings.StartJobDefinitionNotFound2, _definitionType, _definitionName) :733 StringUtil.Format(RemotingErrorIdStrings.StartJobDefinitionNotFound1, _definitionName);734 735 WriteError(new ErrorRecord(new RuntimeException(message), "StartJobFromDefinitionNameNotFound",736 ErrorCategory.ObjectNotFound, null));737 738 return;739 }740 741 if (jobs.Count > 1)742 {743 string message = StringUtil.Format(RemotingErrorIdStrings.StartJobManyDefNameMatches, _definitionName);744 WriteError(new ErrorRecord(new RuntimeException(message), "StartJobFromDefinitionNameMoreThanOneMatch",745 ErrorCategory.InvalidResult, null));746 747 return;748 }749 750 // Start job.751 Job2 job = jobs[0];752 job.StartJob();753 754 // Write job object to host.755 WriteObject(job);756 757 return;758 }759 760 if (_firstProcessRecord)761 {762 _firstProcessRecord = false;763 764 PSRemotingJob job = new PSRemotingJob(ResolvedComputerNames, Operations,765 ScriptBlock.ToString(), ThrottleLimit, _name);766 767 job.PSJobTypeName = s_startJobType;768 769 this.JobRepository.Add(job);770 WriteObject(job);771 }772 773 // inject input774 if (InputObject != AutomationNull.Value)775 {776 foreach (IThrottleOperation operation in Operations)777 {778 ExecutionCmdletHelper helper = (ExecutionCmdletHelper)operation;779 helper.Pipeline.Input.Write(InputObject);780 }781 }782 }783 784 private bool _firstProcessRecord = true;785 786 /// <summary>787 /// InvokeAsync would have been called in ProcessRecord. Wait here788 /// for all the results to become available.789 /// </summary>790 protected override void EndProcessing()791 {792 // close the input stream on all the pipelines793 CloseAllInputStreams();794 }795 796 #endregion Overrides797 798 #region IDisposable Overrides799 800 /// <summary>801 /// Dispose the cmdlet.802 /// </summary>803 public void Dispose()804 {805 Dispose(true);806 GC.SuppressFinalize(this);807 }808 809 /// <summary>810 /// Internal dispose method which does the actual disposing.811 /// </summary>812 /// <param name="disposing">Whether called from dispose or finalize.</param>813 private void Dispose(bool disposing)814 {815 if (disposing)816 {817 CloseAllInputStreams();818 }819 }820 821 #endregion IDisposable Overrides822 }823}824 