//
// 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);
}
}
}