// // Copyright (C) Microsoft. All rights reserved. // using System; using System.Activities; using System.Activities.Validation; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime; using System.Activities.Statements; using System.Management.Automation; using System.Management.Automation.Runspaces; using System.ComponentModel; using Microsoft.PowerShell.Activities; using System.Management.Automation.Tracing; using System.Globalization; namespace Microsoft.PowerShell.Management.Activities { /// /// RestartActivityContext /// [Serializable] public class RestartActivityContext { /// /// Indicates whether a self restart is needed. /// [NonSerialized] internal bool NeedsRestart = false; } /// /// RestartComputerActivity /// public sealed class RestartComputer : PSActivity { /// /// Gets the display name of the command invoked by this activity. /// public RestartComputer() { this.DisplayName = "Restart-Computer"; } private static Tracer _structuredTracer = new Tracer(); /// /// If true, them the workflow will not checkpoint and resume after /// the computer is restarted. /// [ParameterSpecificCategory] [DefaultValue(null)] public static bool DisableSelfRestart { get; set; } /// /// Gets the fully qualified name of the command invoked by this activity. /// public override string PSCommandName { get { return "Microsoft.PowerShell.Management\\Restart-Computer"; } } // Arguments /// /// Provides access to the Authentication parameter. /// [ParameterSpecificCategory] [DefaultValue(null)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public InArgument DcomAuthentication { get; set; } /// /// Provides access to the Impersonation parameter. /// [ParameterSpecificCategory] [DefaultValue(null)] public InArgument Impersonation { get; set; } /// /// Provides access to the WsmanAuthentication parameter. /// [ParameterSpecificCategory] [DefaultValue(null)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public InArgument WsmanAuthentication { get; set; } /// /// Provides access to the Protocol parameter. /// [ParameterSpecificCategory] [DefaultValue(null)] public InArgument Protocol { get; set; } /// /// The computer name to invoke this activity on. /// [ConnectivityCategory] [DefaultValue(null)] public InArgument PSComputerName { get; set; } /// /// Defines the credential to use in the remote connection. /// [ConnectivityCategory] [DefaultValue(null)] public InArgument PSCredential { get; set; } /// /// Provides access to the Force parameter. /// [ParameterSpecificCategory] [DefaultValue(null)] public InArgument Force { get; set; } /// /// Provides access to the ThrottleLimit parameter. /// [ParameterSpecificCategory] [DefaultValue(null)] public InArgument ThrottleLimit { get; set; } /// /// Provides access to the Wait parameter. /// [ParameterSpecificCategory] [DefaultValue(null)] public InArgument Wait { get; set; } /// /// Provides access to the Timeout parameter. /// [ParameterSpecificCategory] [DefaultValue(null)] public InArgument Timeout { get; set; } /// /// Provides access to the For parameter. /// [ParameterSpecificCategory] [DefaultValue(null)] public InArgument For { get; set; } /// /// Provides access to the Delay parameter. /// [ParameterSpecificCategory] [DefaultValue(null)] public InArgument Delay { get; set; } private Variable restartActivityContext = new Variable("RestartActivityContext"); private readonly PSPersist persistActivity = new PSPersist(); /// /// Returns a configured instance of System.Management.Automation.PowerShell, pre-populated with the command to run. /// /// The NativeActivityContext for the currently running activity. /// A populated instance of System.Management.Automation.PowerShell /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) { System.Management.Automation.PowerShell invoker = global::System.Management.Automation.PowerShell.Create(); System.Management.Automation.PowerShell targetCommand = invoker.AddCommand(PSCommandName); // Get the host default values we should use if no explicit parameter was provided. var hostExtension = context.GetExtension(); Dictionary parameterDefaults = new Dictionary(StringComparer.OrdinalIgnoreCase); if (hostExtension != null) { Dictionary incomingArguments = hostExtension.Parameters; foreach (KeyValuePair parameterDefault in incomingArguments) { parameterDefaults[parameterDefault.Key] = parameterDefault.Value; } } if (DcomAuthentication.Expression != null) { targetCommand.AddParameter("DcomAuthentication", DcomAuthentication.Get(context)); } if (Impersonation.Expression != null) { targetCommand.AddParameter("Impersonation", Impersonation.Get(context)); } // See if the DCOM protocol is to be used bool usingDcom = false; if (Protocol.Expression != null) { string protocol = Protocol.Get(context); targetCommand.AddParameter("Protocol", protocol); if (string.Equals(protocol, "DCOM", StringComparison.OrdinalIgnoreCase)) { usingDcom = true; } } // Get the WSMan authentication mechanism to use. If no expression was specified, // and DCOM is not being used, get the default from the host. if (WsmanAuthentication.Expression != null) { targetCommand.AddParameter("WsmanAuthentication", WsmanAuthentication.Get(context)); } else { if (!usingDcom) { if (parameterDefaults.ContainsKey("PSAuthentication")) { string authString = parameterDefaults["PSAuthentication"].ToString(); // Note: the underlying cmdlet does support NegotiateWithImplicitCredential so it is expected // that passing this in as-is will result in an (appropriate) error being emitted in that case. targetCommand.AddParameter("WsmanAuthentication", authString); } } } // Map PSComputerName to the underlying cmdlet name only if the computername is not empty string[] computerName = GetPSComputerName(context); if ((computerName != null) && (computerName.Length != 0)) { targetCommand.AddParameter("ComputerName", computerName); } // Map PSCredential to credential. If no expression was provided, then use the default. if (PSCredential.Expression != null) { targetCommand.AddParameter("Credential", PSCredential.Get(context)); } else { if (parameterDefaults.ContainsKey("PSCredential")) { targetCommand.AddParameter("Credential", parameterDefaults["PSCredential"]); } } if (Force.Expression != null) { targetCommand.AddParameter("Force", Force.Get(context)); } if (ThrottleLimit.Expression != null) { targetCommand.AddParameter("ThrottleLimit", ThrottleLimit.Get(context)); } // Ignore the -Wait parameter in the self-restart case. if (!IsSelfRestart(context)) { if (Wait.Expression != null) { targetCommand.AddParameter("Wait", Wait.Get(context)); } if (For.Expression != null) { targetCommand.AddParameter("For", For.Get(context)); } if (Delay.Expression != null) { targetCommand.AddParameter("Delay", Delay.Get(context)); } } if (Timeout.Expression != null) { targetCommand.AddParameter("Timeout", Timeout.Get(context)); } return new ActivityImplementationContext() { PowerShellInstance = invoker }; } /// /// Execute this command for this activity. /// /// protected override void Execute(NativeActivityContext context) { if (!IsSelfRestart(context)) { if (_structuredTracer.IsEnabled) { string[] computerNames = new string[] { }; computerNames = PSComputerName.Get(context); _structuredTracer.DebugMessage("Executing activity '" + this.DisplayName + "', restarting managed nodes: [" + string.Join(", ", computerNames, ", ") + "]"); } base.Execute(context); } else { _structuredTracer.DebugMessage("Executing activity '" + this.DisplayName + "',doing self-restart" ); PersistAndRestart(context); } } /// /// Test to see if we're restarting the machine we're running on. /// /// /// true if we are restarting the local machine. private bool IsSelfRestart(NativeActivityContext context) { string[] computerName = GetPSComputerName(context); if (computerName == null || computerName.Length == 0) return true; // NOTE: this is known to be problematic as the number of ways of saying "me" in the various // protocols is potentially unbounded. This should be reviewed periodically. foreach (string s in computerName) { string cn = s.Trim(); if (string.Equals(cn, "localhost", StringComparison.OrdinalIgnoreCase) || string.Equals(cn, "127.0.0.1", StringComparison.OrdinalIgnoreCase) || string.Equals(cn, "0:0:0:0:0:0:0:1", StringComparison.OrdinalIgnoreCase) || string.Equals(cn, "::1", StringComparison.OrdinalIgnoreCase) || string.Equals(cn, ".", StringComparison.OrdinalIgnoreCase) || string.Equals(cn, Environment.MachineName, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } /// /// Gets the PSComputerName argument from the context /// /// /// private string[] GetPSComputerName(NativeActivityContext context) { if (PSComputerName.Expression != null) { return PSComputerName.Get(context); } // Retrieve our host overrides var hostValues = context.GetExtension(); if (hostValues != null) { Dictionary incomingArguments = hostValues.Parameters; if (incomingArguments.ContainsKey("PSComputerName")) { return incomingArguments["PSComputerName"] as string[]; } } return null; } /// /// CacheMetadata /// /// protected override void CacheMetadata(NativeActivityMetadata metadata) { base.CacheMetadata(metadata); metadata.AddImplementationChild(this.persistActivity); metadata.AddImplementationVariable(restartActivityContext); } private void PersistAndRestart(NativeActivityContext executionContext) { if (DisableSelfRestart) { _structuredTracer.DebugMessage("Executing activity '" + this.DisplayName + "', resume of workflow after restart was disabled."); return; } _structuredTracer.DebugMessage("Executing activity '" + this.DisplayName + "' scheduling persistence for workflow resumption after restart."); RestartActivityContext c = new RestartActivityContext(); c.NeedsRestart = true; restartActivityContext.Set(executionContext, c); executionContext.ScheduleActivity(persistActivity, SelfRestart); } private void SelfRestart(NativeActivityContext context, ActivityInstance target) { RestartActivityContext restartContext = restartActivityContext.Get(context); if (!restartContext.NeedsRestart) { _structuredTracer.DebugMessage("Executing activity '" + this.DisplayName + "', local machine (" + Environment.MachineName + ") has been restarted."); return; } _structuredTracer.DebugMessage("Executing activity '" + this.DisplayName + "', local machine (" + Environment.MachineName + ") is self-restarting"); base.Execute(context); } } }