c748652c34
commit 8cec8f150da7583b7af5efbe2853efee0179750c
7387 lines
306 KiB
C#
7387 lines
306 KiB
C#
/********************************************************************++
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
--********************************************************************/
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Management.Automation;
|
|
using System.Management.Automation.Internal;
|
|
using System.Net;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.Serialization;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using Microsoft.Win32;
|
|
using Microsoft.PowerShell.Commands.Internal;
|
|
using Microsoft.Management.Infrastructure;
|
|
using Microsoft.Management.Infrastructure.Options;
|
|
using System.Linq;
|
|
using Dbg = System.Management.Automation;
|
|
|
|
#if CORECLR
|
|
using Microsoft.PowerShell.CoreClr.Stubs;
|
|
#else
|
|
//TODO:CORECLR System.DirectoryServices is not available on CORE CLR
|
|
using System.DirectoryServices;
|
|
//TODO:CORECLR System.Security.Permission is not available on CORE CLR
|
|
using System.Security.Permissions;
|
|
using System.Management; // We are not porting the library to CoreCLR
|
|
using Microsoft.WSMan.Management;
|
|
#endif
|
|
|
|
// FxCop suppressions for resource strings:
|
|
[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope = "resource", Target = "ComputerResources.resources", MessageId = "unjoined")]
|
|
[module: SuppressMessage("Microsoft.Naming", "CA1701:ResourceStringCompoundWordsShouldBeCasedCorrectly", Scope = "resource", Target = "ComputerResources.resources", MessageId = "UpTime")]
|
|
|
|
namespace Microsoft.PowerShell.Commands
|
|
{
|
|
#region Test-Connection
|
|
|
|
/// <summary>
|
|
/// This cmdlet is used to test whether a particular host is reachable across an
|
|
/// IP network. It works by sending ICMP "echo request" packets to the target
|
|
/// host and listening for ICMP "echo response" replies. This cmdlet prints a
|
|
/// statistical summary when finished.
|
|
/// </summary>
|
|
[Cmdlet(VerbsDiagnostic.Test, "Connection", DefaultParameterSetName = RegularParameterSet,
|
|
HelpUri = "http://go.microsoft.com/fwlink/?LinkID=135266", RemotingCapability = RemotingCapability.OwnedByCommand)]
|
|
[OutputType(typeof(Boolean))]
|
|
[OutputType(@"System.Management.ManagementObject#root\cimv2\Win32_PingStatus")]
|
|
public class TestConnectionCommand : PSCmdlet
|
|
{
|
|
#region "Parameters"
|
|
|
|
private const string RegularParameterSet = "Default";
|
|
private const string QuietParameterSet = "Quiet";
|
|
private const string SourceParameterSet = "Source";
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = SourceParameterSet)]
|
|
[Parameter(ParameterSetName = RegularParameterSet)]
|
|
public SwitchParameter AsJob
|
|
{
|
|
get { return asjob; }
|
|
set { asjob = value; }
|
|
}
|
|
private SwitchParameter asjob = false;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "DcomAuthentication".
|
|
/// Specifies the authentication level to be used with WMI connection. Valid
|
|
/// values are:
|
|
///
|
|
/// Unchanged = -1,
|
|
/// Default = 0,
|
|
/// None = 1,
|
|
/// Connect = 2,
|
|
/// Call = 3,
|
|
/// Packet = 4,
|
|
/// PacketIntegrity = 5,
|
|
/// PacketPrivacy = 6.
|
|
/// </summary>
|
|
|
|
[Parameter]
|
|
[Alias("Authentication")]
|
|
public AuthenticationLevel DcomAuthentication
|
|
{
|
|
get { return _authentication; }
|
|
set
|
|
{
|
|
_authentication = value;
|
|
}
|
|
}
|
|
private AuthenticationLevel _authentication = AuthenticationLevel.Packet;
|
|
|
|
/// <summary>
|
|
/// The authentication options for CIM_WSMan connection
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateSet(
|
|
"Default",
|
|
"Basic",
|
|
"Negotiate", // can be used with and without credential (without -> PSRP mapped to NegotiateWithImplicitCredential)
|
|
"CredSSP",
|
|
"Digest",
|
|
"Kerberos")] // can be used with and without credential (not sure about implications)
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
|
|
public string WsmanAuthentication
|
|
{
|
|
get { return _wsmanAuthentication; }
|
|
set { _wsmanAuthentication = value; }
|
|
}
|
|
private string _wsmanAuthentication = "Default";
|
|
|
|
/// <summary>
|
|
/// Specify the protocol to use
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateSet(ComputerWMIHelper.DcomProtocol, ComputerWMIHelper.WsmanProtocol)]
|
|
public string Protocol
|
|
{
|
|
get { return _protocol; }
|
|
set
|
|
{
|
|
_protocol = value;
|
|
}
|
|
}
|
|
|
|
//CoreClr does not support DCOM protocol
|
|
// This change makes sure that the the command works seamlessly if user did not explicitly entered the protocol
|
|
#if CORECLR
|
|
private string _protocol = ComputerWMIHelper.WsmanProtocol;
|
|
#else
|
|
private string _protocol = ComputerWMIHelper.DcomProtocol;
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "BufferSize".
|
|
/// Buffer size sent with the this command. The default value is 32.
|
|
/// </summary>
|
|
[Parameter]
|
|
[Alias("Size", "Bytes", "BS")]
|
|
[ValidateRange((int)0, (int)65500)]
|
|
public Int32 BufferSize
|
|
{
|
|
get { return buffersize; }
|
|
set { buffersize = value; }
|
|
}
|
|
private Int32 buffersize = 32;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "ComputerName".
|
|
/// Value of the address requested. The form of the value can be either the
|
|
/// computer name ("wxyz1234"), IPv4 address ("192.168.177.124"), or IPv6
|
|
/// address ("2010:836B:4179::836B:4179").
|
|
/// </summary>
|
|
[Parameter(Mandatory = true,
|
|
Position = 0,
|
|
ValueFromPipelineByPropertyName = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
[Alias("CN", "IPAddress", "__SERVER", "Server", "Destination")]
|
|
public String[] ComputerName
|
|
{
|
|
get { return destination; }
|
|
set { destination = value; }
|
|
}
|
|
private String[] destination;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "Count".
|
|
/// Number of echo requests to send.
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateRange(1, UInt32.MaxValue)]
|
|
public Int32 Count
|
|
{
|
|
get { return count; }
|
|
set { count = value; }
|
|
}
|
|
private Int32 count = 4;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "Credential".
|
|
/// Specifies a user account that has permission to perform this action. Type a
|
|
/// user-name, such as "User01" or "Domain01\User01", or enter a PSCredential
|
|
/// object, such as one from the Get-Credential cmdlet
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = SourceParameterSet, Mandatory = false)]
|
|
[ValidateNotNullOrEmpty]
|
|
[Credential]
|
|
public PSCredential Credential
|
|
{
|
|
get { return credential; }
|
|
set { credential = value; }
|
|
}
|
|
private PSCredential credential;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "FromComputerName".
|
|
/// Specifies the Computer names where the ping request is originated from.
|
|
/// </summary>
|
|
[Parameter(Position = 1, ParameterSetName = SourceParameterSet, Mandatory = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
[Alias("FCN", "SRC")]
|
|
public String[] Source
|
|
{
|
|
get { return source; }
|
|
set { source = value; }
|
|
}
|
|
private String[] source = new string[] { "." };
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "Impersonation".
|
|
/// Specifies the impersonation level to use when calling the WMI method. Valid
|
|
/// values are:
|
|
///
|
|
/// Default = 0,
|
|
/// Anonymous = 1,
|
|
/// Identify = 2,
|
|
/// Impersonate = 3,
|
|
/// Delegate = 4.
|
|
/// </summary>
|
|
[Parameter]
|
|
public ImpersonationLevel Impersonation
|
|
{
|
|
get { return impersonation; }
|
|
set { impersonation = value; }
|
|
}
|
|
private ImpersonationLevel impersonation = ImpersonationLevel.Impersonate;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "ThrottleLimit".
|
|
/// The number of concurrent computers on which the command will be allowed to
|
|
/// execute
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = SourceParameterSet)]
|
|
[Parameter(ParameterSetName = RegularParameterSet)]
|
|
[ValidateRange(int.MinValue, (int)1000)]
|
|
public Int32 ThrottleLimit
|
|
{
|
|
get { return throttlelimit; }
|
|
set
|
|
{
|
|
throttlelimit = value;
|
|
if (throttlelimit <= 0)
|
|
throttlelimit = 32;
|
|
}
|
|
}
|
|
private Int32 throttlelimit = 32;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "TimeToLive".
|
|
/// Life span of the packet in seconds. The value is treated as an upper limit.
|
|
/// All routers must decrement this value by 1 (one). When this value becomes 0
|
|
/// (zero), the packet is dropped by the router. The default value is 80
|
|
/// seconds. The hops between routers rarely take this amount of time.
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateRange(1, (int)255)]
|
|
[Alias("TTL")]
|
|
public Int32 TimeToLive
|
|
{
|
|
get { return timetolive; }
|
|
set { timetolive = value; }
|
|
}
|
|
private Int32 timetolive = 80;
|
|
|
|
/// <summary>
|
|
/// delay parameter
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateRange(1, 60)]
|
|
public Int32 Delay
|
|
{
|
|
get { return delay; }
|
|
set { delay = value; }
|
|
}
|
|
private Int32 delay = 1;
|
|
|
|
/// <summary>
|
|
/// quiet parameter
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = QuietParameterSet)]
|
|
public SwitchParameter Quiet
|
|
{
|
|
get { return quiet; }
|
|
set { quiet = value; }
|
|
}
|
|
private bool quiet = false;
|
|
|
|
#endregion "parameters"
|
|
#region "Overrides"
|
|
|
|
#if !CORECLR
|
|
///// <summary>
|
|
///// To Store the output for each ping reply
|
|
///// </summary>
|
|
private ManagementObjectSearcher searcher;
|
|
|
|
#endif
|
|
private TransportProtocol _transportProtocol = TransportProtocol.DCOM;
|
|
private readonly CancellationTokenSource cancel = new CancellationTokenSource();
|
|
private Dictionary<string, bool> quietResults = new Dictionary<string, bool>();
|
|
/// <summary>
|
|
/// To begin processing Test-connection
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
base.BeginProcessing();
|
|
// Verify parameter set
|
|
|
|
bool haveProtocolParam = this.MyInvocation.BoundParameters.ContainsKey("Protocol");
|
|
bool haveWsmanAuthenticationParam = this.MyInvocation.BoundParameters.ContainsKey("WsmanAuthentication");
|
|
bool haveDcomAuthenticationParam = this.MyInvocation.BoundParameters.ContainsKey("DcomAuthentication");
|
|
bool haveDcomImpersonation = this.MyInvocation.BoundParameters.ContainsKey("Impersonation");
|
|
_transportProtocol = (this.Protocol.Equals(ComputerWMIHelper.WsmanProtocol, StringComparison.OrdinalIgnoreCase) || (haveWsmanAuthenticationParam && !haveProtocolParam)) ?
|
|
TransportProtocol.WSMan : TransportProtocol.DCOM;
|
|
|
|
if (haveWsmanAuthenticationParam && (haveDcomAuthenticationParam || haveDcomImpersonation))
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.StopCommandParamWSManAuthConflict, ComputerResources.StopCommandParamMessage);
|
|
ThrowTerminatingError(
|
|
new ErrorRecord(
|
|
new PSArgumentException(errMsg),
|
|
"InvalidParameter",
|
|
ErrorCategory.InvalidArgument,
|
|
this));
|
|
}
|
|
|
|
if ((_transportProtocol == TransportProtocol.DCOM) && haveWsmanAuthenticationParam)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.StopCommandWSManAuthProtcolConflict, ComputerResources.StopCommandParamMessage);
|
|
ThrowTerminatingError(
|
|
new ErrorRecord(
|
|
new PSArgumentException(errMsg),
|
|
"InvalidParameter",
|
|
ErrorCategory.InvalidArgument,
|
|
this));
|
|
}
|
|
|
|
if ((_transportProtocol == TransportProtocol.WSMan) && (haveDcomAuthenticationParam || haveDcomImpersonation))
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.StopCommandAuthProtcolConflict, ComputerResources.StopCommandParamMessage);
|
|
ThrowTerminatingError(
|
|
new ErrorRecord(
|
|
new PSArgumentException(errMsg),
|
|
"InvalidParameter",
|
|
ErrorCategory.InvalidArgument,
|
|
this));
|
|
}
|
|
|
|
#if CORECLR
|
|
if (this.MyInvocation.BoundParameters.ContainsKey("DcomAuthentication"))
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.InvalidParameterForCoreClr, "DcomAuthentication");
|
|
PSArgumentException ex = new PSArgumentException(errMsg, ComputerResources.InvalidParameterForCoreClr);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForCoreClr", ErrorCategory.InvalidArgument, null));
|
|
}
|
|
|
|
if (this.MyInvocation.BoundParameters.ContainsKey("Impersonation"))
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.InvalidParameterForCoreClr, "Impersonation");
|
|
PSArgumentException ex = new PSArgumentException(errMsg, ComputerResources.InvalidParameterForCoreClr);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForCoreClr", ErrorCategory.InvalidArgument, null));
|
|
}
|
|
|
|
if(this.Protocol.Equals(ComputerWMIHelper.DcomProtocol , StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
InvalidOperationException ex = new InvalidOperationException(ComputerResources.InvalidParameterDCOMNotSupported);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterDCOMNotSupported", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
#endif
|
|
|
|
|
|
//testing
|
|
}
|
|
/// <summary>
|
|
/// Process Record
|
|
/// </summary>
|
|
protected override void ProcessRecord()
|
|
{
|
|
|
|
switch (_transportProtocol)
|
|
{
|
|
#if !CORECLR
|
|
case TransportProtocol.DCOM:
|
|
processDCOMProtocolForTestConnection();
|
|
break;
|
|
#endif
|
|
|
|
case TransportProtocol.WSMan:
|
|
ProcessWSManProtocolForTestConnection();
|
|
break;
|
|
}
|
|
|
|
}
|
|
/// <summary>
|
|
/// to implement ^C
|
|
/// </summary>
|
|
protected override void StopProcessing()
|
|
{
|
|
#if !CORECLR
|
|
ManagementObjectSearcher stopSearcher = searcher;
|
|
if (stopSearcher != null)
|
|
{
|
|
try
|
|
{
|
|
stopSearcher.Dispose();
|
|
}
|
|
catch (ObjectDisposedException) { }
|
|
}
|
|
#endif
|
|
try
|
|
{
|
|
cancel.Cancel();
|
|
}
|
|
catch (ObjectDisposedException) { }
|
|
catch (AggregateException) { }
|
|
}
|
|
#endregion
|
|
|
|
#region "Private Methods "
|
|
private string QueryString(string[] machinenames, bool escaperequired, bool selectrequired)
|
|
{
|
|
StringBuilder FilterString = new StringBuilder();
|
|
if (selectrequired)
|
|
{
|
|
FilterString.Append("Select * from ");
|
|
FilterString.Append(ComputerWMIHelper.WMI_Class_PingStatus);
|
|
FilterString.Append(" where ");
|
|
}
|
|
FilterString.Append("((");
|
|
for (int i = 0; i <= machinenames.Length - 1; i++)
|
|
{
|
|
FilterString.Append("Address='");
|
|
string EscapeComp = machinenames[i].ToString();
|
|
if (EscapeComp.Equals(".", StringComparison.CurrentCultureIgnoreCase))
|
|
EscapeComp = "localhost";
|
|
if (escaperequired)
|
|
{
|
|
EscapeComp = EscapeComp.Replace("\\", "\\\\'").ToString();
|
|
EscapeComp = EscapeComp.Replace("'", "\\'").ToString();
|
|
}
|
|
FilterString.Append(EscapeComp.ToString());
|
|
FilterString.Append("'");
|
|
if (i < machinenames.Length - 1)
|
|
{
|
|
FilterString.Append(" Or ");
|
|
}
|
|
}
|
|
FilterString.Append(")");
|
|
FilterString.Append(" And ");
|
|
FilterString.Append("TimeToLive=");
|
|
FilterString.Append(timetolive);
|
|
FilterString.Append(" And ");
|
|
FilterString.Append("BufferSize=");
|
|
FilterString.Append(buffersize);
|
|
FilterString.Append(")");
|
|
return FilterString.ToString();
|
|
}
|
|
private void ProcessPingStatus(Object pingStatusObj)
|
|
{
|
|
Dbg.Diagnostics.Assert(pingStatusObj != null, "Caller should verify that pingStatus != null");
|
|
//Dbg.Diagnostics.Assert(pingStatusObj.ClassPath.ClassName.Equals("Win32_PingStatus"), "Caller should verify that pingStatus is a Win32_PingStatus object");
|
|
string destinationAddress = null;
|
|
UInt32 primaryAddressResolutionStatus;
|
|
UInt32 statusCode;
|
|
#if !CORECLR
|
|
if (_transportProtocol == TransportProtocol.DCOM)
|
|
{
|
|
ManagementBaseObject pingStatus = (ManagementBaseObject)pingStatusObj;
|
|
|
|
destinationAddress = (string)LanguagePrimitives.ConvertTo(
|
|
pingStatus.GetPropertyValue("Address"),
|
|
typeof(string),
|
|
CultureInfo.InvariantCulture);
|
|
|
|
primaryAddressResolutionStatus = (UInt32)LanguagePrimitives.ConvertTo(
|
|
pingStatus.GetPropertyValue("PrimaryAddressResolutionStatus"),
|
|
typeof(UInt32),
|
|
CultureInfo.InvariantCulture);
|
|
statusCode = (UInt32)LanguagePrimitives.ConvertTo(
|
|
pingStatus.GetPropertyValue("StatusCode"),
|
|
typeof(UInt32),
|
|
CultureInfo.InvariantCulture);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
CimInstance pingStatus = (CimInstance)pingStatusObj;
|
|
destinationAddress = (string)LanguagePrimitives.ConvertTo(
|
|
pingStatus.CimInstanceProperties["Address"].Value.ToString(),
|
|
typeof(string),
|
|
CultureInfo.InvariantCulture);
|
|
primaryAddressResolutionStatus = (UInt32)LanguagePrimitives.ConvertTo(
|
|
pingStatus.CimInstanceProperties["PrimaryAddressResolutionStatus"].Value,
|
|
typeof(UInt32),
|
|
CultureInfo.InvariantCulture);
|
|
statusCode = (UInt32)LanguagePrimitives.ConvertTo(
|
|
pingStatus.CimInstanceProperties["StatusCode"].Value,
|
|
typeof(UInt32),
|
|
CultureInfo.InvariantCulture);
|
|
|
|
#if !CORECLR
|
|
}
|
|
#endif
|
|
if (primaryAddressResolutionStatus != 0)
|
|
{
|
|
if (!quiet)
|
|
{
|
|
Win32Exception win32Exception = new Win32Exception(unchecked((int)primaryAddressResolutionStatus));
|
|
string message = StringUtil.Format(ComputerResources.NoPingResult, destinationAddress, win32Exception.Message);
|
|
Exception pingException = new System.Net.NetworkInformation.PingException(message, win32Exception);
|
|
ErrorRecord errorRecord = new ErrorRecord(pingException, "TestConnectionException", ErrorCategory.ResourceUnavailable, destinationAddress);
|
|
WriteError(errorRecord);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
if (statusCode != 0)
|
|
{
|
|
if (!quiet)
|
|
{
|
|
Win32Exception win32Exception = new Win32Exception(unchecked((int)statusCode));
|
|
string message = StringUtil.Format(ComputerResources.NoPingResult, destinationAddress, win32Exception.Message);
|
|
Exception pingException = new System.Net.NetworkInformation.PingException(message, win32Exception);
|
|
ErrorRecord errorRecord = new ErrorRecord(pingException, "TestConnectionException", ErrorCategory.ResourceUnavailable, destinationAddress);
|
|
WriteError(errorRecord);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.quietResults[destinationAddress] = true;
|
|
if (!quiet)
|
|
{
|
|
WriteObject(pingStatusObj);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#if !CORECLR
|
|
private void processDCOMProtocolForTestConnection()
|
|
{
|
|
ConnectionOptions options = ComputerWMIHelper.GetConnectionOptions(_authentication, this.Impersonation, this.Credential);
|
|
if (asjob)
|
|
{
|
|
string filter = QueryString(destination, true, false);
|
|
GetWmiObjectCommand WMICmd = new GetWmiObjectCommand();
|
|
WMICmd.Filter = filter.ToString();
|
|
WMICmd.Class = ComputerWMIHelper.WMI_Class_PingStatus;
|
|
WMICmd.ComputerName = source;
|
|
WMICmd.Authentication = _authentication;
|
|
WMICmd.Impersonation = Impersonation;
|
|
WMICmd.ThrottleLimit = throttlelimit;
|
|
PSWmiJob wmiJob = new PSWmiJob(WMICmd, source, throttlelimit, this.MyInvocation.MyCommand.Name, count);
|
|
this.JobRepository.Add(wmiJob);
|
|
WriteObject(wmiJob);
|
|
}
|
|
else
|
|
{
|
|
int sourceCount = 0;
|
|
foreach (string fromcomp in source)
|
|
{
|
|
try
|
|
{
|
|
sourceCount++;
|
|
EnumerationOptions enumOptions = new EnumerationOptions();
|
|
enumOptions.UseAmendedQualifiers = true;
|
|
enumOptions.DirectRead = true;
|
|
|
|
int destCount = 0;
|
|
foreach (var tocomp in destination)
|
|
{
|
|
destCount++;
|
|
string querystring = QueryString(new string[] { tocomp }, true, true);
|
|
ObjectQuery query = new ObjectQuery(querystring);
|
|
ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(fromcomp, ComputerWMIHelper.WMI_Path_CIM), options);
|
|
scope.Options.EnablePrivileges = true;
|
|
scope.Connect();
|
|
|
|
using (searcher = new ManagementObjectSearcher(scope, query, enumOptions))
|
|
{
|
|
|
|
for (int j = 0; j <= count - 1; j++)
|
|
{
|
|
|
|
using (ManagementObjectCollection mobj = searcher.Get())
|
|
{
|
|
int mobjCount = 0;
|
|
foreach (ManagementBaseObject obj in mobj)
|
|
{
|
|
using (obj)
|
|
{
|
|
mobjCount++;
|
|
|
|
ProcessPingStatus(obj);
|
|
|
|
// to delay the request, if case to avoid the delay for the last pingrequest
|
|
if (mobjCount < mobj.Count || j < count - 1 || sourceCount < Source.Length || destCount < destination.Length)
|
|
Thread.Sleep(delay * 1000);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
searcher = null;
|
|
}
|
|
catch (ManagementException e)
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "TestConnectionException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
continue;
|
|
}
|
|
catch (System.Runtime.InteropServices.COMException e)
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "TestConnectionException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (quiet)
|
|
{
|
|
foreach (string destinationAddress in this.destination)
|
|
{
|
|
bool destinationResult = false;
|
|
this.quietResults.TryGetValue(destinationAddress, out destinationResult);
|
|
WriteObject(destinationResult);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
private void ProcessWSManProtocolForTestConnection()
|
|
{
|
|
|
|
if (asjob)
|
|
{
|
|
// TODO: Need job for MI.Net WSMan protocol
|
|
// Early return of job object.
|
|
throw new PSNotSupportedException();
|
|
}
|
|
|
|
var operationOptions = new CimOperationOptions
|
|
{
|
|
Timeout = TimeSpan.FromMilliseconds(2000),
|
|
CancellationToken = cancel.Token
|
|
};
|
|
int destCount = 0;
|
|
int sourceCount = 0;
|
|
foreach (string sourceComp in source)
|
|
{
|
|
try
|
|
{
|
|
sourceCount++;
|
|
string sourceMachine;
|
|
if ((sourceComp.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (sourceComp.Equals(".", StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
sourceMachine = Dns.GetHostName();
|
|
}
|
|
else
|
|
{
|
|
sourceMachine = sourceComp;
|
|
}
|
|
foreach (var tocomp in destination)
|
|
{
|
|
destCount++;
|
|
string querystring = QueryString(new string[] { tocomp }, true, true);
|
|
|
|
using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(sourceComp, this.Credential, _wsmanAuthentication, cancel.Token, this))
|
|
{
|
|
for (int echoRequestCount = 0; echoRequestCount < count; echoRequestCount++)
|
|
{
|
|
|
|
IEnumerable<CimInstance> mCollection = cimSession.QueryInstances(
|
|
ComputerWMIHelper.CimOperatingSystemNamespace,
|
|
ComputerWMIHelper.CimQueryDialect,
|
|
querystring,
|
|
operationOptions);
|
|
int total = mCollection.ToList().Count;
|
|
int cimInsCount = 1;
|
|
foreach (CimInstance obj in mCollection)
|
|
{
|
|
ProcessPingStatus(obj);
|
|
cimInsCount++;
|
|
// to delay the request, if case to avoid the delay for the last pingrequest
|
|
if (cimInsCount < total || echoRequestCount < count - 1 || sourceCount < Source.Length || destCount < destination.Length)
|
|
Thread.Sleep(delay * 1000);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (CimException ex)
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(ex, "TestConnectionException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
continue;
|
|
}
|
|
catch (System.Runtime.InteropServices.COMException e)
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "TestConnectionException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (quiet)
|
|
{
|
|
foreach (string destinationAddress in this.destination)
|
|
{
|
|
bool destinationResult = false;
|
|
this.quietResults.TryGetValue(destinationAddress, out destinationResult);
|
|
WriteObject(destinationResult);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion "Private Methods "
|
|
}
|
|
#endregion Test-Connection
|
|
#if !CORECLR
|
|
|
|
#region Enable-ComputerRestore
|
|
|
|
/// <summary>
|
|
/// Cmdlet for Enable-ComputerRestore
|
|
/// </summary>
|
|
[Cmdlet(VerbsLifecycle.Enable, "ComputerRestore", SupportsShouldProcess = true, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=135209")]
|
|
public sealed class EnableComputerRestoreCommand : PSCmdlet, IDisposable
|
|
{
|
|
#region Parameters
|
|
/// <summary>
|
|
/// Specifies the Drive on which the system restore will be enabled.
|
|
/// The drive string should be of the form "C:\".
|
|
/// </summary>
|
|
[Parameter(Position = 0, Mandatory = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public string[] Drive
|
|
{
|
|
get { return _drive; }
|
|
set
|
|
{
|
|
_drive = value;
|
|
}
|
|
}
|
|
private string[] _drive;
|
|
|
|
#endregion Parameters
|
|
private const string ErrorBase = "ComputerResources";
|
|
private ManagementClass WMIClass;
|
|
#region "IDisposable Members"
|
|
|
|
/// <summary>
|
|
/// Dispose Method
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
this.Dispose(true);
|
|
// Use SupressFinalize in case a subclass
|
|
// of this type implements a finalizer.
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dispose Method.
|
|
/// </summary>
|
|
/// <param name="disposing"></param>
|
|
public void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
if (WMIClass != null)
|
|
{
|
|
WMIClass.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion "IDisposable Members"
|
|
|
|
#region Overrides
|
|
|
|
/// <summary>
|
|
/// To Enable the Restore Point of the drives
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
// system restore APIs are not supported on ARM platform
|
|
if (ComputerWMIHelper.SkipSystemRestoreOperationForARMPlatform(this))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ManagementScope scope = new ManagementScope(ComputerWMIHelper.WMI_Path_Default);
|
|
scope.Connect();
|
|
WMIClass = new ManagementClass(ComputerWMIHelper.WMI_Class_SystemRestore);
|
|
WMIClass.Scope = scope;
|
|
int retValue;
|
|
//get the system drive
|
|
string sysdrive = System.Environment.ExpandEnvironmentVariables("%SystemDrive%");
|
|
sysdrive = String.Concat(new string[] { sysdrive, "\\" });
|
|
|
|
|
|
if (ComputerWMIHelper.ContainsSystemDrive(_drive, sysdrive))
|
|
{
|
|
object[] input = { sysdrive };
|
|
try
|
|
{
|
|
|
|
retValue = Convert.ToInt32(WMIClass.InvokeMethod("Enable", input), System.Globalization.CultureInfo.CurrentCulture);
|
|
//if success (return value is 0 or if already enabled (error code is 1056 in XP and 0 in vista)
|
|
if ((retValue.Equals(0)) || (retValue.Equals(ComputerWMIHelper.ErrorCode_Service)))
|
|
{
|
|
string driveNew;
|
|
foreach (string drive in _drive) //for each input drive
|
|
{
|
|
if (!ShouldProcess(drive))
|
|
{
|
|
continue;
|
|
}
|
|
if (!drive.EndsWith("\\", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
|
|
driveNew = String.Concat(drive, "\\");
|
|
|
|
}
|
|
else
|
|
driveNew = drive;
|
|
if (!ComputerWMIHelper.IsValidDrive(driveNew))//if not valid drive,throw error
|
|
{
|
|
Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.InvalidDrive, drive));
|
|
WriteError(new ErrorRecord(Ex, "EnableComputerRestoreInvalidDrive", ErrorCategory.InvalidData, null));
|
|
continue;
|
|
}
|
|
//parameter for Enable method
|
|
//if the input drive is not sytem drive
|
|
if (!driveNew.Equals(sysdrive, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
object[] inputDrive = { driveNew };
|
|
retValue = Convert.ToInt32(WMIClass.InvokeMethod("Enable", inputDrive), System.Globalization.CultureInfo.CurrentCulture);
|
|
|
|
//if not enabled, retry again
|
|
if (retValue.Equals(ComputerWMIHelper.ErrorCode_Interface))
|
|
{
|
|
retValue = Convert.ToInt32(WMIClass.InvokeMethod("Enable", inputDrive), System.Globalization.CultureInfo.CurrentCulture);
|
|
}
|
|
}
|
|
//if not success and if it is not already enabled (error code is 1056 in XP)
|
|
// Error 1717 - The interface is unknown. Eventhough this comes sometimes . The Drive is getting enabled.
|
|
if (!(retValue.Equals(0)) && !(retValue.Equals(ComputerWMIHelper.ErrorCode_Service)) && !(retValue.Equals(ComputerWMIHelper.ErrorCode_Interface)))
|
|
{
|
|
Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.NotEnabled, drive));
|
|
WriteError(new ErrorRecord(Ex, "EnableComputerRestoreNotEnabled", ErrorCategory.InvalidOperation, null));
|
|
continue;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ArgumentException Ex = new ArgumentException(StringUtil.Format(ComputerResources.NotEnabled, sysdrive));
|
|
WriteError(new ErrorRecord(Ex, "EnableComputerRestoreNotEnabled", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
|
|
}
|
|
catch (ManagementException e)
|
|
{
|
|
if ((e.ErrorCode.Equals(ManagementStatus.NotFound)) || (e.ErrorCode.Equals(ManagementStatus.InvalidClass)))
|
|
{
|
|
ErrorRecord er = new ErrorRecord(new ArgumentException(StringUtil.Format(ComputerResources.NotSupported)), null, ErrorCategory.InvalidOperation, null);
|
|
WriteError(er);
|
|
}
|
|
else
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "GetWMIManagementException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
}
|
|
|
|
|
|
}
|
|
catch (COMException e)
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(e.Message))
|
|
{
|
|
|
|
Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.SystemRestoreServiceDisabled));
|
|
WriteError(new ErrorRecord(Ex, "ServiceDisabled", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
else
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "COMException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ArgumentException Ex = new ArgumentException(StringUtil.Format(ComputerResources.NoSystemDrive));
|
|
WriteError(new ErrorRecord(Ex, "EnableComputerNoSystemDrive", ErrorCategory.InvalidArgument, null));
|
|
}
|
|
|
|
|
|
}//end of BeginProcessing
|
|
|
|
/// <summary>
|
|
/// to implement ^C
|
|
/// </summary>
|
|
protected override void StopProcessing()
|
|
{
|
|
if (WMIClass != null)
|
|
{
|
|
WMIClass.Dispose();
|
|
}
|
|
}
|
|
|
|
#endregion Overrides
|
|
|
|
}//end of class
|
|
#endregion
|
|
|
|
#region Disable-ComputerRestore
|
|
|
|
/// <summary>
|
|
/// This cmdlet is to Disable Computer Restore points.
|
|
/// </summary>
|
|
[Cmdlet(VerbsLifecycle.Disable, "ComputerRestore", SupportsShouldProcess = true, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=135207")]
|
|
public sealed class DisableComputerRestoreCommand : PSCmdlet, IDisposable
|
|
{
|
|
#region Parameters
|
|
/// <summary>
|
|
/// Specifies the Drive on which the system restore will be enabled.
|
|
/// The drive string should be of the form "C:\".
|
|
/// </summary>
|
|
[Parameter(Position = 0, Mandatory = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public string[] Drive
|
|
{
|
|
get { return _drive; }
|
|
set
|
|
{
|
|
_drive = value;
|
|
}
|
|
}
|
|
private string[] _drive;
|
|
|
|
#endregion Parameters
|
|
|
|
private ManagementClass WMIClass;
|
|
private const string ErrorBase = "ComputerResources";
|
|
#region "IDisposable Members"
|
|
|
|
/// <summary>
|
|
/// Dispose Method
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
this.Dispose(true);
|
|
// Use SupressFinalize in case a subclass
|
|
// of this type implements a finalizer.
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dispose Method.
|
|
/// </summary>
|
|
/// <param name="disposing"></param>
|
|
public void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
if (WMIClass != null)
|
|
{
|
|
WMIClass.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion "IDisposable Members"
|
|
|
|
#region Overrides
|
|
|
|
/// <summary>
|
|
/// To Disable the Restore Point of the drives
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
// system restore APIs are not supported on ARM platform
|
|
if (ComputerWMIHelper.SkipSystemRestoreOperationForARMPlatform(this))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ManagementScope scope = new ManagementScope(ComputerWMIHelper.WMI_Path_Default);
|
|
scope.Connect();
|
|
WMIClass = new ManagementClass(ComputerWMIHelper.WMI_Class_SystemRestore);
|
|
WMIClass.Scope = scope;
|
|
string driveNew;
|
|
foreach (string drive in _drive)
|
|
{
|
|
if (!ShouldProcess(drive))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!drive.EndsWith("\\", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
|
|
driveNew = String.Concat(drive, "\\");
|
|
|
|
}
|
|
else
|
|
driveNew = drive;
|
|
|
|
|
|
if (!ComputerWMIHelper.IsValidDrive(driveNew))
|
|
{
|
|
ErrorRecord er = new ErrorRecord(new ArgumentException(StringUtil.Format(ComputerResources.NotValidDrive, drive)), null, ErrorCategory.InvalidData, null);
|
|
WriteError(er);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
object[] input = { driveNew };
|
|
int retValue = Convert.ToInt32(WMIClass.InvokeMethod("Disable", input), System.Globalization.CultureInfo.CurrentCulture);
|
|
// Error 1717 - The interface is unknown. Eventhough this comes sometimes . The Drive is getting disabled.
|
|
if (!(retValue.Equals(0)) && !(retValue.Equals(ComputerWMIHelper.ErrorCode_Interface)))
|
|
{
|
|
ErrorRecord er = new ErrorRecord(new ArgumentException(StringUtil.Format(ComputerResources.NotDisabled, drive)), null, ErrorCategory.InvalidOperation, null);
|
|
WriteError(er);
|
|
continue;
|
|
}
|
|
|
|
}
|
|
catch (ManagementException e)
|
|
{
|
|
if ((e.ErrorCode.Equals(ManagementStatus.NotFound)) || (e.ErrorCode.Equals(ManagementStatus.InvalidClass)))
|
|
{
|
|
ErrorRecord er = new ErrorRecord(new ArgumentException(StringUtil.Format(ComputerResources.NotSupported)), null, ErrorCategory.InvalidOperation, null);
|
|
WriteError(er);
|
|
}
|
|
else
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "GetWMIManagementException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
}
|
|
|
|
|
|
}
|
|
catch (COMException e)
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(e.Message))
|
|
{
|
|
|
|
Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.SystemRestoreServiceDisabled));
|
|
WriteError(new ErrorRecord(Ex, "ServiceDisabled", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
else
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "COMException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// to implement ^C
|
|
/// </summary>
|
|
protected override void StopProcessing()
|
|
{
|
|
if (WMIClass != null)
|
|
{
|
|
WMIClass.Dispose();
|
|
}
|
|
}
|
|
|
|
#endregion Overrides
|
|
|
|
|
|
|
|
}//end of class
|
|
#endregion Disable-ComputerRestore
|
|
|
|
#region Checkpoint-Computer
|
|
|
|
/// <summary>
|
|
/// Creates the Restore Point for the Local computer
|
|
/// </summary>
|
|
|
|
[Cmdlet(VerbsData.Checkpoint, "Computer", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=135197")]
|
|
public class CheckpointComputerCommand : PSCmdlet, IDisposable
|
|
{
|
|
#region Parameters
|
|
|
|
/// <summary>
|
|
/// The description to be displayed so the user can easily identify a restore point.
|
|
/// </summary>
|
|
[Parameter(Position = 0, Mandatory = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
public string Description
|
|
{
|
|
get { return _description; }
|
|
set
|
|
{
|
|
_description = value;
|
|
}
|
|
}
|
|
private string _description;
|
|
|
|
|
|
/// <summary>
|
|
/// The type of restore point.
|
|
/// </summary>
|
|
[Parameter(Position = 1)]
|
|
[Alias("RPT")]
|
|
[ValidateSetAttribute(new string[] { "APPLICATION_INSTALL", "APPLICATION_UNINSTALL", "DEVICE_DRIVER_INSTALL", "MODIFY_SETTINGS", "CANCELLED_OPERATION" })]
|
|
[ValidateNotNullOrEmpty]
|
|
public string RestorePointType
|
|
{
|
|
get { return _restorepointtype; }
|
|
set
|
|
{
|
|
// null check is not needed (because of ValidateNotNullOrEmpty),
|
|
// but we have to include it to silence OACR
|
|
if (value == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("value");
|
|
}
|
|
|
|
_restorepointtype = value;
|
|
if (_restorepointtype.Equals("APPLICATION_INSTALL", StringComparison.OrdinalIgnoreCase))
|
|
intRestorePoint = 0;
|
|
else if (_restorepointtype.Equals("APPLICATION_UNINSTALL", StringComparison.OrdinalIgnoreCase))
|
|
intRestorePoint = 1;
|
|
else if (_restorepointtype.Equals("DEVICE_DRIVER_INSTALL", StringComparison.OrdinalIgnoreCase))
|
|
intRestorePoint = 10;
|
|
else if (_restorepointtype.Equals("MODIFY_SETTINGS", StringComparison.OrdinalIgnoreCase))
|
|
intRestorePoint = 12;
|
|
else if (_restorepointtype.Equals("CANCELLED_OPERATION", StringComparison.OrdinalIgnoreCase))
|
|
intRestorePoint = 13;
|
|
}
|
|
}
|
|
private string _restorepointtype = "APPLICATION_INSTALL";
|
|
#endregion Parameters
|
|
|
|
#region private
|
|
|
|
private DateTime lastTimeProgressWasWritten = DateTime.UtcNow;
|
|
private int intRestorePoint = 0;
|
|
private int ret = int.MaxValue;
|
|
/// <summary>
|
|
/// Shared Exception. Used when exception thrown from the Restore point thread.
|
|
/// </summary>
|
|
private static Exception exceptionfromnewthread = null;
|
|
|
|
private void WriteProgress(string statusDescription, int? percentComplete)
|
|
{
|
|
ProgressRecordType recordType;
|
|
if (percentComplete.HasValue && percentComplete.Value == 100)
|
|
{
|
|
recordType = ProgressRecordType.Completed;
|
|
}
|
|
else
|
|
{
|
|
recordType = ProgressRecordType.Processing;
|
|
}
|
|
|
|
if (recordType == ProgressRecordType.Processing)
|
|
{
|
|
TimeSpan timeSinceProgressWasWrittenLast = DateTime.UtcNow - lastTimeProgressWasWritten;
|
|
if (timeSinceProgressWasWrittenLast < TimeSpan.FromMilliseconds(200))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
lastTimeProgressWasWritten = DateTime.UtcNow;
|
|
|
|
string activityDescription = StringUtil.Format(ComputerResources.ProgressActivity);
|
|
ProgressRecord progressRecord = new ProgressRecord(
|
|
1905347723, // unique id
|
|
activityDescription,
|
|
statusDescription);
|
|
|
|
if (percentComplete.HasValue)
|
|
{
|
|
progressRecord.PercentComplete = percentComplete.Value;
|
|
}
|
|
|
|
progressRecord.RecordType = recordType;
|
|
this.WriteProgress(progressRecord);
|
|
}
|
|
|
|
private void WriteProgress(DateTime starttime)
|
|
{
|
|
int percentageCompleted = ProgressRecord.GetPercentageComplete(starttime, TimeSpan.FromSeconds(90));
|
|
if (percentageCompleted < 100)
|
|
{
|
|
WriteProgress(StringUtil.Format(ComputerResources.ProgressStatusCreatingRestorePoint, percentageCompleted), percentageCompleted);
|
|
}
|
|
}
|
|
|
|
private DateTime startUtcTime, startLocalTime;
|
|
private void WriteProgress()
|
|
{
|
|
while (true)
|
|
{
|
|
WriteProgress(this.startUtcTime);
|
|
System.Threading.Thread.Sleep(1000);
|
|
if (exceptionfromnewthread == null)
|
|
{
|
|
if (ret == 0)
|
|
{
|
|
if (this.IsRestorePointCreated(_description, this.startLocalTime))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (ret != int.MaxValue)
|
|
{
|
|
// Invocation is complete with error
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Exception is thrown, breaking the loop
|
|
break;
|
|
}
|
|
}
|
|
|
|
WriteProgress(StringUtil.Format(ComputerResources.ProgressStatusCompleted), 100);
|
|
}
|
|
|
|
private void CreateRestorePoint()
|
|
{
|
|
ManagementClass WMIClass = null;
|
|
try
|
|
{
|
|
ManagementScope scope = new ManagementScope(ComputerWMIHelper.WMI_Path_Default);
|
|
scope.Connect();
|
|
WMIClass = new ManagementClass(ComputerWMIHelper.WMI_Class_SystemRestore);
|
|
WMIClass.Scope = scope;
|
|
|
|
//create restore point
|
|
ManagementBaseObject inParams = WMIClass.GetMethodParameters("CreateRestorePoint");
|
|
object[] param ={ _description, intRestorePoint, 100 }; // the event type will be always 100,Begin_System_Change
|
|
ret = Convert.ToInt32(WMIClass.InvokeMethod("CreateRestorePoint", param), System.Globalization.CultureInfo.CurrentCulture);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// We catch all exceptions because we don't want the exception to be thrown from a separate worker thread
|
|
exceptionfromnewthread = ex;
|
|
}
|
|
finally
|
|
{
|
|
if (WMIClass != null)
|
|
{
|
|
WMIClass.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region "IDisposable Members"
|
|
|
|
/// <summary>
|
|
/// Dispose Method
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
// Use SuppressFinalize in case a subclass
|
|
// of this type implements a finalizer.
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
#endregion "IDisposable Members"
|
|
|
|
/// <summary>
|
|
/// BeginProcessing method.
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
// system restore APIs are not supported on ARM platform
|
|
if (ComputerWMIHelper.SkipSystemRestoreOperationForARMPlatform(this))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Setting Exception from the new thread always to null
|
|
exceptionfromnewthread = null;
|
|
|
|
//on vista, CANCELLED_OPERATION restorepointtype does not work
|
|
if ((Environment.OSVersion.Version.Major >= 6) && (intRestorePoint == 13))
|
|
{
|
|
ErrorRecord er = new ErrorRecord(new InvalidOperationException(StringUtil.Format(ComputerResources.NotSupported)), null, ErrorCategory.InvalidOperation, null);
|
|
ThrowTerminatingError(er);
|
|
}
|
|
|
|
if (!CanCreateNewRestorePoint(DateTime.Now)) { return; }
|
|
|
|
this.startUtcTime = DateTime.UtcNow;
|
|
this.startLocalTime = DateTime.Now;
|
|
ThreadStart start = new ThreadStart(this.CreateRestorePoint);
|
|
Thread thread = new Thread(start);
|
|
thread.Start();
|
|
WriteProgress();
|
|
if (exceptionfromnewthread == null)
|
|
{
|
|
if (ret.Equals(1058))
|
|
{
|
|
Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.ServiceDisabled));
|
|
WriteError(new ErrorRecord(Ex, "CheckpointComputerServiceDisabled", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
else if (!(ret.Equals(0)) && !(ret.Equals(1058)))
|
|
{
|
|
Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.RestorePointNotCreated));
|
|
WriteError(new ErrorRecord(Ex, "CheckpointComputerPointNotCreated", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (exceptionfromnewthread is System.Runtime.InteropServices.COMException)
|
|
{
|
|
if (string.IsNullOrEmpty(exceptionfromnewthread.Message))
|
|
{
|
|
Exception e = new ArgumentException(StringUtil.Format(ComputerResources.SystemRestoreServiceDisabled));
|
|
WriteError(new ErrorRecord(e, "ServiceDisabled", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
else
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(exceptionfromnewthread, "COMException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
}
|
|
}
|
|
else if (exceptionfromnewthread is ManagementException)
|
|
{
|
|
if (((ManagementException)exceptionfromnewthread).ErrorCode.Equals(ManagementStatus.NotFound) || ((ManagementException)exceptionfromnewthread).ErrorCode.Equals(ManagementStatus.InvalidClass))
|
|
{
|
|
Exception e = new ArgumentException(StringUtil.Format(ComputerResources.NotSupported));
|
|
WriteError(new ErrorRecord(e, "CheckpointComputerNotSupported", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
else
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(exceptionfromnewthread, "GetWMIManagementException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// For other exception we caught from the worker thread, we throw it out here
|
|
throw exceptionfromnewthread;
|
|
}
|
|
}
|
|
|
|
}//End BeginProcessing()
|
|
|
|
private bool CanCreateNewRestorePoint(DateTime startTime)
|
|
{
|
|
const string srRegistryKeyPath = @"Software\Microsoft\Windows NT\CurrentVersion\SystemRestore";
|
|
const string srFrequencyKeyName = "SystemRestorePointCreationFrequency";
|
|
Version osVersion = Environment.OSVersion.Version;
|
|
int timeInterval = 1440; // Default value: 24 hours * 60 min/hr
|
|
bool canCreate = true;
|
|
|
|
// From Win8+, the default frequency of restore point creation is 24 hours, but the user
|
|
// can create the DWORD value SystemRestorePointCreationFrequency under the registry key
|
|
// HKLM\Software\Microsoft\Windows NT\CurrentVersion\SystemRestore, and the value of it
|
|
// can change the frequency of restore point creation. There are three cases here:
|
|
// 1. Such registry key doesn't exist. We use the default setting: 24 hours.
|
|
// 2. Registry key value is 0. No frequency limitation, new restore point can be created anytime.
|
|
// 3. Registry key value is integer N, the time interval is N minutes.
|
|
if ((osVersion.Major > 6) || (osVersion.Major == 6 && osVersion.Minor >= 2))
|
|
{
|
|
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(srRegistryKeyPath))
|
|
{
|
|
if (key != null)
|
|
{
|
|
object value = key.GetValue(srFrequencyKeyName);
|
|
if (value is int) { timeInterval = (int) value; }
|
|
}
|
|
}
|
|
|
|
var objectQuery = new ObjectQuery { QueryString = "select * from " + ComputerWMIHelper.WMI_Class_SystemRestore };
|
|
var scope = new ManagementScope(ComputerWMIHelper.WMI_Path_Default);
|
|
scope.Connect();
|
|
|
|
try
|
|
{
|
|
DateTime lastCreationTime = DateTime.MinValue;
|
|
using (var searcher = new ManagementObjectSearcher(scope, objectQuery))
|
|
{
|
|
foreach (ManagementObject obj in searcher.Get())
|
|
{
|
|
using (obj)
|
|
{
|
|
DateTime creationTime =
|
|
ManagementDateTimeConverter.ToDateTime(obj.Properties["CreationTime"].Value.ToString());
|
|
if (creationTime > lastCreationTime)
|
|
{
|
|
lastCreationTime = creationTime;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TimeSpan span = startTime.Subtract(lastCreationTime);
|
|
canCreate = (span.TotalMinutes >= timeInterval);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Fail to retrieve restore points
|
|
if (ex is ManagementException || ex is COMException)
|
|
{
|
|
// Something is wrong with System Restore (it may not be supported). We continue to let the
|
|
// call to "CreateRestorePoint" happen, and will handle the exception generated by that call.
|
|
canCreate = true;
|
|
}
|
|
else
|
|
{
|
|
// Not sure what happened, so we'd better terminate the execution and report the error.
|
|
string errorMsg = StringUtil.Format(ComputerResources.FailToRetrieveLastRestorePoint, ex.Message);
|
|
ThrowTerminatingError(
|
|
new ErrorRecord(new InvalidOperationException(errorMsg),
|
|
"FailToRetrieveLastRestorePoint",
|
|
ErrorCategory.InvalidOperation, null));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!canCreate)
|
|
{
|
|
// A new restore point cannot be created yet, so we write out warning message.
|
|
WriteWarning(StringUtil.Format(ComputerResources.CannotCreateRestorePointWarning, timeInterval));
|
|
}
|
|
|
|
return canCreate;
|
|
}
|
|
|
|
private bool IsRestorePointCreated(string description, DateTime starttime)
|
|
{
|
|
bool foundrestorepoint = false;
|
|
ManagementScope scope = new ManagementScope(ComputerWMIHelper.WMI_Path_Default);
|
|
scope.Connect();
|
|
|
|
ObjectQuery objquery = new ObjectQuery();
|
|
StringBuilder sb = new StringBuilder("select * from ");
|
|
sb.Append(ComputerWMIHelper.WMI_Class_SystemRestore);
|
|
sb.Append(" where description = '");
|
|
sb.Append(description.Replace("'", "\\'"));
|
|
sb.Append("'");
|
|
objquery.QueryString = sb.ToString();
|
|
|
|
try
|
|
{
|
|
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, objquery))
|
|
{
|
|
if (searcher.Get().Count > 0)
|
|
{
|
|
foreach (ManagementObject obj in searcher.Get())
|
|
{
|
|
using (obj)
|
|
{
|
|
// we are adding 1 second to creationTime to account for the fact that milliseconds
|
|
// are not reported in CreationTime property - it is possible to have
|
|
// startTime = 2009-07-20 9:35:18.123
|
|
// creationTime = 2009-07-20 9:35:18
|
|
// which would indicate creationTime < startTime
|
|
DateTime creationTime = ManagementDateTimeConverter.ToDateTime(obj.Properties["CreationTime"].Value.ToString());
|
|
if (creationTime.AddSeconds(1.0) >= starttime)
|
|
{
|
|
foundrestorepoint = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (ManagementException)
|
|
{
|
|
foundrestorepoint = true;
|
|
}
|
|
catch (COMException)
|
|
{
|
|
foundrestorepoint = true;
|
|
}
|
|
return foundrestorepoint;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Get-ComputerRestorePoint
|
|
|
|
/// <summary>
|
|
/// This cmdlet is to Get Computer Restore points.
|
|
/// </summary>
|
|
[Cmdlet(VerbsCommon.Get, "ComputerRestorePoint", DefaultParameterSetName = "ID", HelpUri = "http://go.microsoft.com/fwlink/?LinkID=135215")]
|
|
[OutputType(@"System.Management.ManagementObject#root\default\SystemRestore")]
|
|
public sealed class GetComputerRestorePointCommand : PSCmdlet, IDisposable
|
|
{
|
|
#region Parameters
|
|
|
|
/// <summary>
|
|
/// This cmdlet is to get Computer Restore points.
|
|
/// </summary>
|
|
[Parameter(Position = 0, ParameterSetName = "ID")]
|
|
[ValidateNotNullOrEmpty]
|
|
[ValidateRangeAttribute((int)1, int.MaxValue)]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public int[] RestorePoint
|
|
{
|
|
get { return _restorepoint; }
|
|
set
|
|
{
|
|
|
|
_restorepoint = value;
|
|
}
|
|
}
|
|
private int[] _restorepoint;
|
|
|
|
/// <summary>
|
|
/// This cmdlet is to get Computer Restore points.
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = "LastStatus", Mandatory = true)]
|
|
[ValidateNotNull]
|
|
public SwitchParameter LastStatus
|
|
{
|
|
get { return _laststatus; }
|
|
set { _laststatus = value; }
|
|
}
|
|
private SwitchParameter _laststatus;
|
|
|
|
#endregion
|
|
|
|
private ManagementClass WMIClass;
|
|
|
|
#region "IDisposable Members"
|
|
|
|
/// <summary>
|
|
/// Dispose Method
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
this.Dispose(true);
|
|
// Use SupressFinalize in case a subclass
|
|
// of this type implements a finalizer.
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dispose Method.
|
|
/// </summary>
|
|
/// <param name="disposing"></param>
|
|
public void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
if (WMIClass != null)
|
|
{
|
|
WMIClass.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion "IDisposable Members"
|
|
|
|
#region overrides
|
|
/// <summary>
|
|
/// Gets the list of Computer Restore point.
|
|
/// ID parameter id used to refer the sequence no. When given searched with particular
|
|
/// sequence no. and returns the restore point
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
// system restore APIs are not supported on ARM platform
|
|
if (ComputerWMIHelper.SkipSystemRestoreOperationForARMPlatform(this))
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ManagementScope scope = new ManagementScope(ComputerWMIHelper.WMI_Path_Default);
|
|
scope.Connect();
|
|
WMIClass = new ManagementClass(ComputerWMIHelper.WMI_Class_SystemRestore);
|
|
WMIClass.Scope = scope;
|
|
|
|
if (ParameterSetName.Equals("LastStatus"))
|
|
{
|
|
int restoreStatus = Convert.ToInt32(WMIClass.InvokeMethod("GetLastRestoreStatus", null), System.Globalization.CultureInfo.CurrentCulture);
|
|
|
|
if (restoreStatus.Equals(0))
|
|
WriteObject(ComputerResources.RestoreFailed);
|
|
else if (restoreStatus.Equals(1))
|
|
WriteObject(ComputerResources.RestoreSuceess);
|
|
else if (restoreStatus.Equals(2))
|
|
WriteObject(ComputerResources.RestoreInterrupted);
|
|
}
|
|
Dictionary<int, string> sequenceList = new Dictionary<int, string>();
|
|
if (ParameterSetName.Equals("ID"))
|
|
{
|
|
ObjectQuery objquery = new ObjectQuery();
|
|
// Dictionary<int,string> sequenceList = new Dictionary<int,string>();
|
|
if (_restorepoint == null)
|
|
{
|
|
objquery.QueryString = "select * from " + ComputerWMIHelper.WMI_Class_SystemRestore;
|
|
}
|
|
else
|
|
{
|
|
// sequenceList = new List<int>();
|
|
StringBuilder sb = new StringBuilder("select * from ");
|
|
sb.Append(ComputerWMIHelper.WMI_Class_SystemRestore);
|
|
sb.Append(" where SequenceNumber = ");
|
|
for (int i = 0; i <= _restorepoint.Length - 1; i++)
|
|
{
|
|
sb.Append(_restorepoint[i]);
|
|
if (i < _restorepoint.Length - 1)
|
|
sb.Append(" OR SequenceNumber = ");
|
|
if (!sequenceList.ContainsKey(_restorepoint[i]))
|
|
sequenceList.Add(_restorepoint[i], "true");
|
|
}
|
|
objquery.QueryString = sb.ToString();
|
|
}
|
|
|
|
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, objquery))
|
|
{
|
|
foreach (ManagementObject obj in searcher.Get())
|
|
{
|
|
using (obj)
|
|
{
|
|
WriteObject(obj);
|
|
if (_restorepoint != null)
|
|
{
|
|
|
|
int sequenceNo = Convert.ToInt32(obj.Properties["SequenceNumber"].Value, System.Globalization.CultureInfo.CurrentCulture);
|
|
sequenceList.Remove(sequenceNo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sequenceList != null)
|
|
{
|
|
if (sequenceList.Count > 0)
|
|
{
|
|
foreach (int id in sequenceList.Keys)
|
|
{
|
|
string message = StringUtil.Format(ComputerResources.NoResorePoint,id);
|
|
ArgumentException e = new ArgumentException(message);
|
|
ErrorRecord errorrecord = new ErrorRecord(e, "NoResorePoint", ErrorCategory.InvalidArgument, null);
|
|
WriteError(errorrecord);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (ManagementException e)
|
|
{
|
|
if ((e.ErrorCode.Equals(ManagementStatus.NotFound)) || (e.ErrorCode.Equals(ManagementStatus.InvalidClass)))
|
|
{
|
|
Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.NotSupported));
|
|
WriteError(new ErrorRecord(Ex, "GetComputerRestorePointNotSupported", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
else
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "GetWMIManagementException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
}
|
|
}
|
|
catch (COMException e)
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(e.Message))
|
|
{
|
|
|
|
Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.SystemRestoreServiceDisabled));
|
|
WriteError(new ErrorRecord(Ex, "ServiceDisabled", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
else
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "COMException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// to implement ^C
|
|
/// </summary>
|
|
protected override void StopProcessing()
|
|
{
|
|
if (WMIClass != null)
|
|
{
|
|
WMIClass.Dispose();
|
|
}
|
|
}
|
|
|
|
#endregion overrides
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endif
|
|
|
|
#region Restart-Computer
|
|
|
|
/// <summary>
|
|
/// This exception is thrown when the timeout expires before a computer finishes restarting
|
|
/// </summary>
|
|
[Serializable]
|
|
public sealed class RestartComputerTimeoutException : RuntimeException
|
|
{
|
|
/// <summary>
|
|
/// Name of the computer that is restarting
|
|
/// </summary>
|
|
public string ComputerName { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The timeout value specified by the user. It indicates the seconds to wait before timeout.
|
|
/// </summary>
|
|
public int Timeout { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Construct a RestartComputerTimeoutException.
|
|
/// </summary>
|
|
/// <param name="computerName"></param>
|
|
/// <param name="timeout"></param>
|
|
/// <param name="message"></param>
|
|
/// <param name="errorId"></param>
|
|
internal RestartComputerTimeoutException(string computerName, int timeout, string message, string errorId)
|
|
: base(message)
|
|
{
|
|
SetErrorId(errorId);
|
|
SetErrorCategory(ErrorCategory.OperationTimeout);
|
|
ComputerName = computerName;
|
|
Timeout = timeout;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct a RestartComputerTimeoutException
|
|
/// </summary>
|
|
public RestartComputerTimeoutException() : base() {}
|
|
|
|
/// <summary>
|
|
/// Constructs a RestartComputerTimeoutException
|
|
/// </summary>
|
|
///
|
|
/// <param name="message">
|
|
/// The message used in the exception.
|
|
/// </param>
|
|
public RestartComputerTimeoutException(string message) : base(message) {}
|
|
|
|
/// <summary>
|
|
/// Constructs a RestartComputerTimeoutException
|
|
/// </summary>
|
|
///
|
|
/// <param name="message">
|
|
/// The message used in the exception.
|
|
/// </param>
|
|
///
|
|
/// <param name="innerException">
|
|
/// An exception that led to this exception.
|
|
/// </param>
|
|
public RestartComputerTimeoutException(string message, Exception innerException) : base(message, innerException) {}
|
|
|
|
#region Serialization
|
|
/// <summary>
|
|
/// Serialization constructor for class RestartComputerTimeoutException
|
|
/// </summary>
|
|
///
|
|
/// <param name="info">
|
|
/// serialization information
|
|
/// </param>
|
|
///
|
|
/// <param name="context">
|
|
/// streaming context
|
|
/// </param>
|
|
private RestartComputerTimeoutException(SerializationInfo info, StreamingContext context)
|
|
: base(info, context)
|
|
{
|
|
if (info == null)
|
|
{
|
|
throw new PSArgumentNullException("info");
|
|
}
|
|
|
|
ComputerName = info.GetString("ComputerName");
|
|
Timeout = info.GetInt32("Timeout");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes the RestartComputerTimeoutException.
|
|
/// </summary>
|
|
///
|
|
/// <param name="info">
|
|
/// serialization information
|
|
/// </param>
|
|
///
|
|
/// <param name="context">
|
|
/// streaming context
|
|
/// </param>
|
|
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
|
|
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
|
{
|
|
if (info == null)
|
|
{
|
|
throw new PSArgumentNullException("info");
|
|
}
|
|
|
|
base.GetObjectData(info, context);
|
|
info.AddValue("ComputerName", ComputerName);
|
|
info.AddValue("Timeout", Timeout);
|
|
}
|
|
#endregion Serialization
|
|
}
|
|
|
|
/// <summary>
|
|
/// Defines the services that Restart-Computer can wait on
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")]
|
|
public enum WaitForServiceTypes
|
|
{
|
|
/// <summary>
|
|
/// Wait for the WMI service to be ready
|
|
/// </summary>
|
|
Wmi = 0x0,
|
|
|
|
/// <summary>
|
|
/// Wait for the WinRM service to be ready
|
|
/// </summary>
|
|
WinRM = 0x1,
|
|
|
|
/// <summary>
|
|
/// Wait for the PowerShell to be ready
|
|
/// </summary>
|
|
PowerShell = 0x2,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restarts the computer
|
|
/// </summary>
|
|
[Cmdlet(VerbsLifecycle.Restart, "Computer", SupportsShouldProcess = true, DefaultParameterSetName = DefaultParameterSet,
|
|
HelpUri = "http://go.microsoft.com/fwlink/?LinkID=135253", RemotingCapability = RemotingCapability.OwnedByCommand)]
|
|
public class RestartComputerCommand : PSCmdlet, IDisposable
|
|
{
|
|
#region "Parameters and PrivateData"
|
|
|
|
private const string DefaultParameterSet = "DefaultSet";
|
|
private const string AsJobParameterSet = "AsJobSet";
|
|
|
|
/// <summary>
|
|
/// Used to start a command remotely as a Job. The Job results are collected
|
|
/// and stored in the global cache on the client machine.
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = AsJobParameterSet)]
|
|
public SwitchParameter AsJob
|
|
{
|
|
get { return _asjob; }
|
|
set { _asjob = value; }
|
|
}
|
|
private SwitchParameter _asjob = false;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "Authentication".
|
|
/// Specifies the authentication level to be used with WMI connection. Valid
|
|
/// values are:
|
|
///
|
|
/// Unchanged = -1,
|
|
/// Default = 0,
|
|
/// None = 1,
|
|
/// Connect = 2,
|
|
/// Call = 3,
|
|
/// Packet = 4,
|
|
/// PacketIntegrity = 5,
|
|
/// PacketPrivacy = 6.
|
|
/// </summary>
|
|
[Parameter]
|
|
[Alias("Authentication")]
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
|
|
public AuthenticationLevel DcomAuthentication
|
|
{
|
|
get { return _dcomAuthentication; }
|
|
set
|
|
{
|
|
_dcomAuthentication = value;
|
|
_isDcomAuthenticationSpecified = true;
|
|
}
|
|
}
|
|
private AuthenticationLevel _dcomAuthentication = AuthenticationLevel.Packet;
|
|
private bool _isDcomAuthenticationSpecified = false;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "Impersonation".
|
|
/// Specifies the impersonation level to use when calling the WMI method. Valid
|
|
/// values are:
|
|
///
|
|
/// Default = 0,
|
|
/// Anonymous = 1,
|
|
/// Identify = 2,
|
|
/// Impersonate = 3,
|
|
/// Delegate = 4.
|
|
/// </summary>
|
|
[Parameter]
|
|
public ImpersonationLevel Impersonation
|
|
{
|
|
get { return _impersonation; }
|
|
set
|
|
{
|
|
_impersonation = value;
|
|
_isImpersonationSpecified = true;
|
|
|
|
}
|
|
}
|
|
private ImpersonationLevel _impersonation = ImpersonationLevel.Impersonate;
|
|
private bool _isImpersonationSpecified = false;
|
|
|
|
/// <summary>
|
|
/// The authentication options for CIM_WSMan connection
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = DefaultParameterSet)]
|
|
[ValidateSet(
|
|
"Default",
|
|
"Basic",
|
|
"Negotiate", // can be used with and without credential (without -> PSRP mapped to NegotiateWithImplicitCredential)
|
|
"CredSSP",
|
|
"Digest",
|
|
"Kerberos")] // can be used with and without credential (not sure about implications)
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
|
|
public string WsmanAuthentication { get; set; }
|
|
|
|
/// <summary>
|
|
/// Specify the protocol to use
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = DefaultParameterSet)]
|
|
[ValidateSet(ComputerWMIHelper.DcomProtocol, ComputerWMIHelper.WsmanProtocol)]
|
|
public string Protocol
|
|
{
|
|
get { return _protocol; }
|
|
set
|
|
{
|
|
_protocol = value;
|
|
_isProtocolSpecified = true;
|
|
}
|
|
}
|
|
|
|
//CoreClr does not support DCOM protocol
|
|
// This change makes sure that the the command works seamlessly if user did not explicitly entered the protocol
|
|
#if CORECLR
|
|
private string _protocol = ComputerWMIHelper.WsmanProtocol;
|
|
#else
|
|
private string _protocol = ComputerWMIHelper.DcomProtocol;
|
|
#endif
|
|
private bool _isProtocolSpecified = false;
|
|
|
|
/// <summary>
|
|
/// Specifies the computer (s)Name on which this command is executed.
|
|
/// When this parameter is omitted, this cmdlet restarts the local computer.
|
|
/// Type the NETBIOS name, IP address, or fully-qualified domain name of one
|
|
/// or more computers in a comma-separated list. To specify the local computer, type the computername or "localhost".
|
|
/// </summary>
|
|
[Parameter(Position = 0, ValueFromPipeline = true,
|
|
ValueFromPipelineByPropertyName = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
[Alias("CN", "__SERVER", "Server", "IPAddress")]
|
|
public String[] ComputerName
|
|
{
|
|
get { return _computername; }
|
|
set { _computername = value; }
|
|
}
|
|
private String[] _computername = new string[] { "." };
|
|
private List<string> _validatedComputerNames = new List<string>();
|
|
private readonly List<string> _waitOnComputers = new List<string>();
|
|
private readonly HashSet<string> _uniqueComputerNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "Credential".
|
|
/// Specifies a user account that has permission to perform this action. Type a
|
|
/// user-name, such as "User01" or "Domain01\User01", or enter a PSCredential
|
|
/// object, such as one from the Get-Credential cmdlet
|
|
/// </summary>
|
|
[Parameter(Position = 1)]
|
|
[ValidateNotNullOrEmpty]
|
|
[Credential]
|
|
public PSCredential Credential { get; set; }
|
|
|
|
/// <summary>
|
|
/// Using Force in conjunction with Reboot on a
|
|
/// remote computer immediately reboots the remote computer.
|
|
/// </summary>
|
|
[Parameter]
|
|
[Alias("f")]
|
|
public SwitchParameter Force { get; set; }
|
|
|
|
/// <summary>
|
|
/// Allows the user of the cmdlet to specify a throttling value
|
|
/// for throttling the number of remote operations that can
|
|
/// be executed simultaneously.
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = AsJobParameterSet)]
|
|
[ValidateRange(int.MinValue, (int)1000)]
|
|
public Int32 ThrottleLimit
|
|
{
|
|
get { return _throttlelimit; }
|
|
set
|
|
{
|
|
_throttlelimit = value;
|
|
if (_throttlelimit <= 0)
|
|
_throttlelimit = 32;
|
|
}
|
|
}
|
|
private Int32 _throttlelimit = 32;
|
|
|
|
/// <summary>
|
|
/// Specify the Wait parameter. Prompt will be blocked is the Timeout is not 0
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = DefaultParameterSet)]
|
|
public SwitchParameter Wait { get; set; }
|
|
|
|
/// <summary>
|
|
/// Specify the Timeout parameter.
|
|
/// Negative value indicates wait infinitely.
|
|
/// Positive value indicates the seconds to wait before timeout.
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = DefaultParameterSet)]
|
|
[Alias("TimeoutSec")]
|
|
[ValidateRange(-1, int.MaxValue)]
|
|
public int Timeout
|
|
{
|
|
get { return _timeout; }
|
|
set
|
|
{
|
|
_timeout = value;
|
|
_timeoutSpecified = true;
|
|
}
|
|
}
|
|
private int _timeout = -1;
|
|
private bool _timeoutSpecified = false;
|
|
|
|
/// <summary>
|
|
/// Specify the For parameter.
|
|
/// Wait for the specific service before unblocking the prompt.
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = DefaultParameterSet)]
|
|
public WaitForServiceTypes For
|
|
{
|
|
get { return _waitFor; }
|
|
set
|
|
{
|
|
_waitFor = value;
|
|
_waitForSpecified = true;
|
|
}
|
|
}
|
|
private WaitForServiceTypes _waitFor = WaitForServiceTypes.PowerShell;
|
|
private bool _waitForSpecified = false;
|
|
|
|
/// <summary>
|
|
/// Specify the Delay parameter.
|
|
/// The specific time interval (in second) to wait between network pings or service queries.
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = DefaultParameterSet)]
|
|
[ValidateRange(1, Int16.MaxValue)]
|
|
public Int16 Delay
|
|
{
|
|
get { return (Int16)_delay; }
|
|
set
|
|
{
|
|
_delay = value;
|
|
_delaySpecified = true;
|
|
}
|
|
}
|
|
private int _delay = 5;
|
|
private bool _delaySpecified = false;
|
|
|
|
/// <summary>
|
|
/// Script to test if the PowerShell is ready
|
|
/// </summary>
|
|
private const string TestPowershellScript = @"
|
|
$array = @($input)
|
|
$result = @{}
|
|
foreach ($computerName in $array[1])
|
|
{
|
|
$ret = $null
|
|
if ($array[0] -eq $null)
|
|
{
|
|
$ret = Invoke-Command -ComputerName $computerName {$true} -SessionOption (New-PSSessionOption -NoMachineProfile) -ErrorAction SilentlyContinue
|
|
}
|
|
else
|
|
{
|
|
$ret = Invoke-Command -ComputerName $computerName {$true} -SessionOption (New-PSSessionOption -NoMachineProfile) -ErrorAction SilentlyContinue -Credential $array[0]
|
|
}
|
|
|
|
if ($ret -eq $true)
|
|
{
|
|
$result[$computerName] = $true
|
|
}
|
|
else
|
|
{
|
|
$result[$computerName] = $false
|
|
}
|
|
}
|
|
$result
|
|
";
|
|
|
|
/// <summary>
|
|
/// The indicator to use when show progress
|
|
/// </summary>
|
|
private string[] _indicator = {"|", "/", "-", "\\"};
|
|
|
|
/// <summary>
|
|
/// The activity id
|
|
/// </summary>
|
|
private int _activityId;
|
|
|
|
/// <summary>
|
|
/// After call 'Shutdown' on the target computer, wait a few
|
|
/// seconds for the restart to begin.
|
|
/// </summary>
|
|
private const int SecondsToWaitForRestartToBegin = 25;
|
|
|
|
/// <summary>
|
|
/// Actual time out in seconds
|
|
/// </summary>
|
|
private int _timeoutInMilliseconds;
|
|
|
|
/// <summary>
|
|
/// Indicate to exit
|
|
/// </summary>
|
|
private bool _exit, _timeUp;
|
|
private readonly CancellationTokenSource _cancel = new CancellationTokenSource();
|
|
|
|
/// <summary>
|
|
/// A waithandler to wait on. Current thread will wait on it during the delay interval.
|
|
/// </summary>
|
|
private readonly ManualResetEventSlim _waitHandler = new ManualResetEventSlim(false);
|
|
private readonly Dictionary<string, ComputerInfo> _computerInfos = new Dictionary<string, ComputerInfo>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
// CLR 4.0 Port note - use https://msdn.microsoft.com/en-us/library/system.net.networkinformation.ipglobalproperties.hostname(v=vs.110).aspx
|
|
private readonly string _shortLocalMachineName = Dns.GetHostName();
|
|
|
|
// And for this, use PsUtils.GetHostname()
|
|
private readonly string _fullLocalMachineName = Dns.GetHostEntryAsync("").Result.HostName;
|
|
|
|
private int _percent;
|
|
private string _status;
|
|
private string _activity;
|
|
private Timer _timer;
|
|
private System.Management.Automation.PowerShell _powershell;
|
|
|
|
private const string StageVerification = "VerifyStage";
|
|
private const string WmiConnectionTest = "WMI";
|
|
private const string WinrmConnectionTest = "WinRM";
|
|
private const string PowerShellConnectionTest = "PowerShell";
|
|
|
|
#endregion "parameters and PrivateData"
|
|
|
|
#region "IDisposable Members"
|
|
|
|
/// <summary>
|
|
/// Dispose Method
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
this.Dispose(true);
|
|
// Use SupressFinalize in case a subclass
|
|
// of this type implements a finalizer.
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dispose Method.
|
|
/// </summary>
|
|
/// <param name="disposing"></param>
|
|
public void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
if (_timer != null)
|
|
{
|
|
_timer.Dispose();
|
|
}
|
|
|
|
_waitHandler.Dispose();
|
|
_cancel.Dispose();
|
|
if (_powershell != null)
|
|
{
|
|
_powershell.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion "IDisposable Members"
|
|
|
|
#region "Private Methods"
|
|
|
|
/// <summary>
|
|
/// Validate parameters for 'DefaultSet'
|
|
/// 1. When the Wait is specified, the computername cannot contain the local machine
|
|
/// 2. If the local machine is present, make sure it is at the end of the list (so the remote ones get restarted before the local machine reboot).
|
|
/// </summary>
|
|
private void ValidateComputerNames()
|
|
{
|
|
bool containLocalhost = false;
|
|
_validatedComputerNames.Clear();
|
|
|
|
foreach (string name in _computername)
|
|
{
|
|
ErrorRecord error = null;
|
|
string targetComputerName = ComputerWMIHelper.ValidateComputerName(name, _shortLocalMachineName, _fullLocalMachineName, ref error);
|
|
if (targetComputerName == null)
|
|
{
|
|
if (error != null)
|
|
{
|
|
WriteError(error);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (targetComputerName.Equals(ComputerWMIHelper.localhostStr, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
containLocalhost = true;
|
|
}
|
|
else if (!_uniqueComputerNames.Contains(targetComputerName))
|
|
{
|
|
_validatedComputerNames.Add(targetComputerName);
|
|
_uniqueComputerNames.Add(targetComputerName);
|
|
}
|
|
}
|
|
|
|
if (Wait && containLocalhost)
|
|
{
|
|
// The local machine will be ignored, and an error will be emitted.
|
|
InvalidOperationException ex = new InvalidOperationException(ComputerResources.CannotWaitLocalComputer);
|
|
WriteError(new ErrorRecord(ex, "CannotWaitLocalComputer", ErrorCategory.InvalidOperation, null));
|
|
containLocalhost = false;
|
|
}
|
|
|
|
// Add the localhost to the end of the list, so we will restart remote machines
|
|
// before we restart the local one.
|
|
if (containLocalhost)
|
|
{
|
|
_validatedComputerNames.Add(ComputerWMIHelper.localhostStr);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write out progress
|
|
/// </summary>
|
|
/// <param name="activity"></param>
|
|
/// <param name="status"></param>
|
|
/// <param name="percent"></param>
|
|
/// <param name="progressRecordType"></param>
|
|
private void WriteProgress(string activity, string status, int percent, ProgressRecordType progressRecordType)
|
|
{
|
|
ProgressRecord progress = new ProgressRecord(_activityId, activity, status);
|
|
progress.PercentComplete = percent;
|
|
progress.RecordType = progressRecordType;
|
|
WriteProgress(progress);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculate the progress percentage
|
|
/// </summary>
|
|
/// <param name="currentStage"></param>
|
|
/// <returns></returns>
|
|
private int CalculateProgressPercentage(string currentStage)
|
|
{
|
|
switch (currentStage)
|
|
{
|
|
case StageVerification:
|
|
return _waitFor.Equals(WaitForServiceTypes.Wmi) || _waitFor.Equals(WaitForServiceTypes.WinRM)
|
|
? 33
|
|
: 20;
|
|
case WmiConnectionTest:
|
|
return _waitFor.Equals(WaitForServiceTypes.Wmi) ? 66 : 40;
|
|
case WinrmConnectionTest:
|
|
return _waitFor.Equals(WaitForServiceTypes.WinRM) ? 66 : 60;
|
|
case PowerShellConnectionTest:
|
|
return 80;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
Dbg.Diagnostics.Assert(false, "CalculateProgressPercentage should never hit the default case");
|
|
return 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event handler for the timer
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
private void OnTimedEvent(object s)
|
|
{
|
|
_exit = _timeUp = true;
|
|
_cancel.Cancel();
|
|
_waitHandler.Set();
|
|
|
|
if (_powershell != null)
|
|
{
|
|
_powershell.Stop();
|
|
_powershell.Dispose();
|
|
}
|
|
}
|
|
|
|
private class ComputerInfo
|
|
{
|
|
internal string LastBootUpTime;
|
|
internal bool RebootComplete;
|
|
}
|
|
|
|
#if !CORECLR
|
|
private List<string> TestRestartStageUsingDcom(IEnumerable<string> computerNames, List<string> nextTestList, CancellationToken token, ConnectionOptions options)
|
|
{
|
|
var restartStageTestList = new List<string>();
|
|
var query = new ObjectQuery("Select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem);
|
|
var enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true };
|
|
|
|
foreach (var computer in computerNames)
|
|
{
|
|
try
|
|
{
|
|
if (token.IsCancellationRequested) { break; }
|
|
var scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options);
|
|
using (var searcher = new ManagementObjectSearcher(scope, query, enumOptions))
|
|
{
|
|
if (token.IsCancellationRequested) { break; }
|
|
|
|
using (var mCollection = searcher.Get())
|
|
{
|
|
if (mCollection.Count > 0)
|
|
{
|
|
foreach (ManagementBaseObject os in mCollection)
|
|
{
|
|
using (os)
|
|
{
|
|
string newLastBootUpTime = os.Properties["LastBootUpTime"].Value.ToString();
|
|
string oldLastBootUpTime = _computerInfos[computer].LastBootUpTime;
|
|
|
|
if (string.Compare(newLastBootUpTime, oldLastBootUpTime, StringComparison.OrdinalIgnoreCase) != 0)
|
|
{
|
|
_computerInfos[computer].RebootComplete = true;
|
|
nextTestList.Add(computer);
|
|
}
|
|
else
|
|
{
|
|
restartStageTestList.Add(computer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
restartStageTestList.Add(computer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (ManagementException)
|
|
{
|
|
restartStageTestList.Add(computer);
|
|
}
|
|
catch (COMException)
|
|
{
|
|
restartStageTestList.Add(computer);
|
|
}
|
|
catch (UnauthorizedAccessException)
|
|
{
|
|
restartStageTestList.Add(computer);
|
|
}
|
|
}
|
|
|
|
return restartStageTestList;
|
|
}
|
|
#endif
|
|
|
|
private List<string> TestRestartStageUsingWsman(IEnumerable<string> computerNames, List<string> nextTestList, CancellationToken token)
|
|
{
|
|
var restartStageTestList = new List<string>();
|
|
var operationOptions = new CimOperationOptions
|
|
{
|
|
Timeout = TimeSpan.FromMilliseconds(2000),
|
|
CancellationToken = token
|
|
};
|
|
foreach (var computer in computerNames)
|
|
{
|
|
try
|
|
{
|
|
if (token.IsCancellationRequested) { break; }
|
|
using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(computer, Credential, WsmanAuthentication, token, this))
|
|
{
|
|
bool itemRetrieved = false;
|
|
IEnumerable<CimInstance> mCollection = cimSession.QueryInstances(
|
|
ComputerWMIHelper.CimOperatingSystemNamespace,
|
|
ComputerWMIHelper.CimQueryDialect,
|
|
"Select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem,
|
|
operationOptions);
|
|
foreach (CimInstance os in mCollection)
|
|
{
|
|
itemRetrieved = true;
|
|
string newLastBootUpTime = os.CimInstanceProperties["LastBootUpTime"].Value.ToString();
|
|
string oldLastBootUpTime = _computerInfos[computer].LastBootUpTime;
|
|
|
|
if (string.Compare(newLastBootUpTime, oldLastBootUpTime, StringComparison.OrdinalIgnoreCase) != 0)
|
|
{
|
|
_computerInfos[computer].RebootComplete = true;
|
|
nextTestList.Add(computer);
|
|
}
|
|
else
|
|
{
|
|
restartStageTestList.Add(computer);
|
|
}
|
|
}
|
|
|
|
if (!itemRetrieved)
|
|
{
|
|
restartStageTestList.Add(computer);
|
|
}
|
|
}
|
|
}
|
|
catch (CimException)
|
|
{
|
|
restartStageTestList.Add(computer);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
CommandProcessorBase.CheckForSevereException(ex);
|
|
restartStageTestList.Add(computer);
|
|
}
|
|
|
|
}
|
|
|
|
return restartStageTestList;
|
|
}
|
|
|
|
#if !CORECLR
|
|
private List<string> SetUpComputerInfoUsingDcom(IEnumerable<string> computerNames, ConnectionOptions options)
|
|
{
|
|
var validComputerNameList = new List<string>();
|
|
var query = new ObjectQuery("Select * From " + ComputerWMIHelper.WMI_Class_OperatingSystem);
|
|
var enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true };
|
|
|
|
foreach (var computer in computerNames)
|
|
{
|
|
try
|
|
{
|
|
var scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options);
|
|
using (var searcher = new ManagementObjectSearcher(scope, query, enumOptions))
|
|
{
|
|
using (var mCollection = searcher.Get())
|
|
{
|
|
if (mCollection.Count > 0)
|
|
{
|
|
foreach (ManagementBaseObject os in mCollection)
|
|
{
|
|
using (os)
|
|
{
|
|
if (!_computerInfos.ContainsKey(computer))
|
|
{
|
|
var info = new ComputerInfo
|
|
{
|
|
LastBootUpTime = os.Properties["LastBootUpTime"].Value.ToString(),
|
|
RebootComplete = false
|
|
};
|
|
_computerInfos.Add(computer, info);
|
|
validComputerNameList.Add(computer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ComputerResources.CannotGetOperatingSystemObject);
|
|
var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped",
|
|
ErrorCategory.OperationStopped, computer);
|
|
this.WriteError(error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (ManagementException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ex.Message);
|
|
var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped",
|
|
ErrorCategory.OperationStopped, computer);
|
|
this.WriteError(error);
|
|
}
|
|
catch (COMException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ex.Message);
|
|
var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped",
|
|
ErrorCategory.OperationStopped, computer);
|
|
this.WriteError(error);
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ex.Message);
|
|
var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped",
|
|
ErrorCategory.OperationStopped, computer);
|
|
this.WriteError(error);
|
|
}
|
|
}
|
|
|
|
return validComputerNameList;
|
|
}
|
|
#endif
|
|
|
|
private List<string> SetUpComputerInfoUsingWsman(IEnumerable<string> computerNames, CancellationToken token)
|
|
{
|
|
var validComputerNameList = new List<string>();
|
|
var operationOptions = new CimOperationOptions
|
|
{
|
|
Timeout = TimeSpan.FromMilliseconds(2000),
|
|
CancellationToken = token
|
|
};
|
|
foreach (var computer in computerNames)
|
|
{
|
|
try
|
|
{
|
|
using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(computer, Credential, WsmanAuthentication, token, this))
|
|
{
|
|
bool itemRetrieved = false;
|
|
IEnumerable<CimInstance> mCollection = cimSession.QueryInstances(
|
|
ComputerWMIHelper.CimOperatingSystemNamespace,
|
|
ComputerWMIHelper.CimQueryDialect,
|
|
"Select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem,
|
|
operationOptions);
|
|
foreach (CimInstance os in mCollection)
|
|
{
|
|
itemRetrieved = true;
|
|
if (!_computerInfos.ContainsKey(computer))
|
|
{
|
|
var info = new ComputerInfo
|
|
{
|
|
LastBootUpTime = os.CimInstanceProperties["LastBootUpTime"].Value.ToString(),
|
|
RebootComplete = false
|
|
};
|
|
_computerInfos.Add(computer, info);
|
|
validComputerNameList.Add(computer);
|
|
}
|
|
}
|
|
|
|
if (!itemRetrieved)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ComputerResources.CannotGetOperatingSystemObject);
|
|
var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped",
|
|
ErrorCategory.OperationStopped, computer);
|
|
this.WriteError(error);
|
|
}
|
|
}
|
|
}
|
|
catch (CimException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ex.Message);
|
|
var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped",
|
|
ErrorCategory.OperationStopped, computer);
|
|
this.WriteError(error);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
CommandProcessorBase.CheckForSevereException(ex);
|
|
string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ex.Message);
|
|
var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped",
|
|
ErrorCategory.OperationStopped, computer);
|
|
this.WriteError(error);
|
|
}
|
|
}
|
|
|
|
return validComputerNameList;
|
|
}
|
|
|
|
private void WriteOutTimeoutError(IEnumerable<string> computerNames)
|
|
{
|
|
const string errorId = "RestartComputerTimeout";
|
|
foreach (string computer in computerNames)
|
|
{
|
|
string errorMsg = StringUtil.Format(ComputerResources.RestartcomputerFailed, computer, ComputerResources.TimeoutError);
|
|
var exception = new RestartComputerTimeoutException(computer, Timeout, errorMsg, errorId);
|
|
var error = new ErrorRecord(exception, errorId, ErrorCategory.OperationTimeout, computer);
|
|
WriteError(error);
|
|
}
|
|
}
|
|
|
|
#endregion "Private Methods"
|
|
|
|
#region "Internal Methods"
|
|
|
|
internal static List<string> TestWmiConnectionUsingWsman(List<string> computerNames, List<string> nextTestList, CancellationToken token, PSCredential credential, string wsmanAuthentication, PSCmdlet cmdlet)
|
|
{
|
|
// Check if the WMI service "Winmgmt" is started
|
|
const string wmiServiceQuery = "Select * from " + ComputerWMIHelper.WMI_Class_Service + " Where name = 'Winmgmt'";
|
|
var wmiTestList = new List<string>();
|
|
var operationOptions = new CimOperationOptions
|
|
{
|
|
Timeout = TimeSpan.FromMilliseconds(2000),
|
|
CancellationToken = token
|
|
};
|
|
foreach (var computer in computerNames)
|
|
{
|
|
try
|
|
{
|
|
if (token.IsCancellationRequested) { break; }
|
|
using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(computer, credential, wsmanAuthentication, token, cmdlet))
|
|
{
|
|
bool itemRetrieved = false;
|
|
IEnumerable<CimInstance> mCollection = cimSession.QueryInstances(
|
|
ComputerWMIHelper.CimOperatingSystemNamespace,
|
|
ComputerWMIHelper.CimQueryDialect,
|
|
wmiServiceQuery,
|
|
operationOptions);
|
|
foreach (CimInstance service in mCollection)
|
|
{
|
|
itemRetrieved = true;
|
|
if (LanguagePrimitives.IsTrue(service.CimInstanceProperties["Started"].Value))
|
|
{
|
|
nextTestList.Add(computer);
|
|
}
|
|
else
|
|
{
|
|
wmiTestList.Add(computer);
|
|
}
|
|
}
|
|
|
|
if (!itemRetrieved)
|
|
{
|
|
wmiTestList.Add(computer);
|
|
}
|
|
}
|
|
}
|
|
catch (CimException)
|
|
{
|
|
wmiTestList.Add(computer);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
CommandProcessorBase.CheckForSevereException(ex);
|
|
wmiTestList.Add(computer);
|
|
}
|
|
}
|
|
|
|
return wmiTestList;
|
|
}
|
|
|
|
#if !CORECLR
|
|
/// <summary>
|
|
/// Test WinRM connectivity for the restarting machine
|
|
/// </summary>
|
|
/// <param name="computerNames"></param>
|
|
/// <param name="nextTestList"></param>
|
|
/// <param name="token"></param>
|
|
/// <returns></returns>
|
|
internal static List<string> TestWinrmConnection(List<string> computerNames, List<string> nextTestList, CancellationToken token)
|
|
{
|
|
List<string> winrmTestList = new List<string>();
|
|
IWSManEx wsmanObject = (IWSManEx)new WSManClass();
|
|
int sessionFlags = (int)WSManSessionFlags.WSManFlagUseNoAuthentication | (int)WSManSessionFlags.WSManFlagUtf8;
|
|
IWSManConnectionOptionsEx2 connObject = (IWSManConnectionOptionsEx2)wsmanObject.CreateConnectionOptions();
|
|
|
|
foreach (string computerName in computerNames)
|
|
{
|
|
try
|
|
{
|
|
if (token.IsCancellationRequested) { break; }
|
|
IWSManSession sessionObj = (IWSManSession)wsmanObject.CreateSession(computerName, sessionFlags, connObject);
|
|
if (token.IsCancellationRequested) { break; }
|
|
sessionObj.Timeout = 1500;
|
|
sessionObj.Identify(0);
|
|
nextTestList.Add(computerName);
|
|
}
|
|
catch (COMException)
|
|
{
|
|
winrmTestList.Add(computerName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
CommandProcessorBase.CheckForSevereException(ex);
|
|
winrmTestList.Add(computerName);
|
|
}
|
|
}
|
|
return winrmTestList;
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Test the PowerShell state for the restarting computer
|
|
/// </summary>
|
|
/// <param name="computerNames"></param>
|
|
/// <param name="nextTestList"></param>
|
|
/// <param name="powershell"></param>
|
|
/// <param name="credential"></param>
|
|
/// <returns></returns>
|
|
internal static List<string> TestPowerShell(List<string> computerNames, List<string> nextTestList, System.Management.Automation.PowerShell powershell, PSCredential credential)
|
|
{
|
|
List<string> psList = new List<string>();
|
|
|
|
try
|
|
{
|
|
Collection<PSObject> psObjectCollection = powershell.Invoke(new object[] { credential, computerNames.ToArray() });
|
|
if (psObjectCollection == null)
|
|
{
|
|
Dbg.Diagnostics.Assert(false, "This should never happen. Invoke should never return null.");
|
|
}
|
|
|
|
// If ^C or timeout happens when we are in powershell.Invoke(), the psObjectCollection might be empty
|
|
if (psObjectCollection.Count == 0)
|
|
{
|
|
return computerNames;
|
|
}
|
|
|
|
object result = PSObject.Base(psObjectCollection[0]);
|
|
Hashtable data = result as Hashtable;
|
|
|
|
Dbg.Diagnostics.Assert(data != null, "data should never be null");
|
|
Dbg.Diagnostics.Assert(data.Count == computerNames.Count, "data should contain results for all computers in computerNames");
|
|
|
|
foreach (string computer in computerNames)
|
|
{
|
|
if (LanguagePrimitives.IsTrue(data[computer]))
|
|
{
|
|
nextTestList.Add(computer);
|
|
}
|
|
else
|
|
{
|
|
psList.Add(computer);
|
|
}
|
|
}
|
|
}
|
|
catch (PipelineStoppedException)
|
|
{
|
|
// powershell.Stop() is invoked becaue timeout expires, or Ctrl+C is pressed
|
|
}
|
|
catch (ObjectDisposedException)
|
|
{
|
|
// powershell.dispose() is invoked because timeout expires, or Ctrl+C is pressed
|
|
}
|
|
|
|
return psList;
|
|
}
|
|
|
|
#if !CORECLR
|
|
/// <summary>
|
|
/// Restart one computer
|
|
/// </summary>
|
|
/// <param name="cmdlet"></param>
|
|
/// <param name="isLocalhost"></param>
|
|
/// <param name="computerName"></param>
|
|
/// <param name="flags"></param>
|
|
/// <param name="options"></param>
|
|
/// <returns>
|
|
/// True if the restart was successful
|
|
/// False otherwise
|
|
/// </returns>
|
|
internal static bool RestartOneComputerUsingDcom(PSCmdlet cmdlet, bool isLocalhost, string computerName, object[] flags, ConnectionOptions options)
|
|
{
|
|
bool isSuccess = false;
|
|
PlatformInvokes.TOKEN_PRIVILEGE currentPrivilegeState = new PlatformInvokes.TOKEN_PRIVILEGE();
|
|
|
|
try
|
|
{
|
|
if (!(isLocalhost && PlatformInvokes.EnableTokenPrivilege(ComputerWMIHelper.SE_SHUTDOWN_NAME, ref currentPrivilegeState)) &&
|
|
!(!isLocalhost && PlatformInvokes.EnableTokenPrivilege(ComputerWMIHelper.SE_REMOTE_SHUTDOWN_NAME, ref currentPrivilegeState)))
|
|
{
|
|
string message =
|
|
StringUtil.Format(ComputerResources.PrivilegeNotEnabled, computerName,
|
|
isLocalhost ? ComputerWMIHelper.SE_SHUTDOWN_NAME : ComputerWMIHelper.SE_REMOTE_SHUTDOWN_NAME);
|
|
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(message), "PrivilegeNotEnabled", ErrorCategory.InvalidOperation, null);
|
|
cmdlet.WriteError(errorRecord);
|
|
return false;
|
|
}
|
|
|
|
ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(isLocalhost ? "localhost" : computerName, ComputerWMIHelper.WMI_Path_CIM), options);
|
|
EnumerationOptions enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true };
|
|
ObjectQuery query = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem);
|
|
using (var searcher = new ManagementObjectSearcher(scope, query, enumOptions))
|
|
{
|
|
foreach (ManagementObject operatingSystem in searcher.Get())
|
|
{
|
|
using (operatingSystem)
|
|
{
|
|
object result = operatingSystem.InvokeMethod("Win32shutdown", flags);
|
|
int retVal = Convert.ToInt32(result.ToString(), CultureInfo.CurrentCulture);
|
|
if (retVal != 0)
|
|
{
|
|
var ex = new Win32Exception(retVal);
|
|
string errMsg = StringUtil.Format(ComputerResources.RestartcomputerFailed, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(
|
|
new InvalidOperationException(errMsg), "RestartcomputerFailed", ErrorCategory.OperationStopped, computerName);
|
|
cmdlet.WriteError(error);
|
|
}
|
|
else
|
|
{
|
|
isSuccess = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (ManagementException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.RestartcomputerFailed, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartcomputerFailed",
|
|
ErrorCategory.OperationStopped, computerName);
|
|
cmdlet.WriteError(error);
|
|
}
|
|
catch (COMException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.RestartcomputerFailed, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartcomputerFailed",
|
|
ErrorCategory.OperationStopped, computerName);
|
|
cmdlet.WriteError(error);
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.RestartcomputerFailed, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartcomputerFailed",
|
|
ErrorCategory.OperationStopped, computerName);
|
|
cmdlet.WriteError(error);
|
|
}
|
|
finally
|
|
{
|
|
// Restore the previous privilege state if something unexpected happened
|
|
PlatformInvokes.RestoreTokenPrivilege(
|
|
isLocalhost ? ComputerWMIHelper.SE_SHUTDOWN_NAME : ComputerWMIHelper.SE_REMOTE_SHUTDOWN_NAME, ref currentPrivilegeState);
|
|
}
|
|
|
|
return isSuccess;
|
|
}
|
|
#endif
|
|
|
|
#endregion "Internal Methods"
|
|
|
|
#region "Overrides"
|
|
|
|
/// <summary>
|
|
/// BeginProcessing method.
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
if (ParameterSetName.Equals(DefaultParameterSet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (WsmanAuthentication != null && (_isDcomAuthenticationSpecified || _isImpersonationSpecified))
|
|
{
|
|
string errorMsg = StringUtil.Format(ComputerResources.ParameterConfliction,
|
|
ComputerResources.ParameterUsage);
|
|
InvalidOperationException ex = new InvalidOperationException(errorMsg);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "ParameterConfliction", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
|
|
bool usingDcom = Protocol.Equals(ComputerWMIHelper.DcomProtocol, StringComparison.OrdinalIgnoreCase);
|
|
bool usingWsman = Protocol.Equals(ComputerWMIHelper.WsmanProtocol, StringComparison.OrdinalIgnoreCase);
|
|
|
|
if (_isProtocolSpecified && usingDcom && WsmanAuthentication != null)
|
|
{
|
|
string errorMsg = StringUtil.Format(ComputerResources.InvalidParameterForDCOM,
|
|
ComputerResources.ParameterUsage);
|
|
InvalidOperationException ex = new InvalidOperationException(errorMsg);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForDCOM", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
|
|
if (_isProtocolSpecified && usingWsman && (_isDcomAuthenticationSpecified || _isImpersonationSpecified))
|
|
{
|
|
string errorMsg = StringUtil.Format(ComputerResources.InvalidParameterForWSMan,
|
|
ComputerResources.ParameterUsage);
|
|
InvalidOperationException ex = new InvalidOperationException(errorMsg);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForWSMan", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
|
|
if (!_isProtocolSpecified && WsmanAuthentication != null)
|
|
{
|
|
// Change the protocol to be WSMan if the WsmanAuthentication is specified
|
|
Protocol = ComputerWMIHelper.WsmanProtocol;
|
|
}
|
|
}
|
|
|
|
#if CORECLR
|
|
if (this.MyInvocation.BoundParameters.ContainsKey("DcomAuthentication"))
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.InvalidParameterForCoreClr, "DcomAuthentication");
|
|
PSArgumentException ex = new PSArgumentException(errMsg, ComputerResources.InvalidParameterForCoreClr);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForCoreClr", ErrorCategory.InvalidArgument, null));
|
|
}
|
|
|
|
if (this.MyInvocation.BoundParameters.ContainsKey("Impersonation"))
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.InvalidParameterForCoreClr, "Impersonation");
|
|
PSArgumentException ex = new PSArgumentException(errMsg, ComputerResources.InvalidParameterForCoreClr);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForCoreClr", ErrorCategory.InvalidArgument, null));
|
|
}
|
|
|
|
// DCOM Authentication is not supported for CoreCLR. Throw an error
|
|
// and request that the user specify WSMan Authentication.
|
|
if (_isDcomAuthenticationSpecified ||
|
|
Protocol.Equals(ComputerWMIHelper.DcomProtocol, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
InvalidOperationException ex = new InvalidOperationException(ComputerResources.InvalidParameterDCOMNotSupported);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterDCOMNotSupported", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
|
|
// TODO:CORECLR This should be re-visited if we decide to add double hop remoting to CoreCLR (outgoing connections)
|
|
if (ParameterSetName.Equals(AsJobParameterSet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
InvalidOperationException ex = new InvalidOperationException(ComputerResources.InvalidParameterSetAsJob);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterSetAsJob", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
#endif
|
|
|
|
// Timeout, For, Delay, Progress cannot be present if Wait is not present
|
|
if ((_timeoutSpecified || _waitForSpecified || _delaySpecified) && !Wait)
|
|
{
|
|
InvalidOperationException ex = new InvalidOperationException(ComputerResources.RestartComputerInvalidParameter);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "RestartComputerInvalidParameter", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
|
|
if (Wait)
|
|
{
|
|
_activityId = (new Random()).Next();
|
|
if (_timeout == -1 || _timeout >= int.MaxValue / 1000)
|
|
{
|
|
_timeoutInMilliseconds = int.MaxValue;
|
|
}
|
|
else
|
|
{
|
|
_timeoutInMilliseconds = _timeout*1000;
|
|
}
|
|
|
|
// We don't support combined service types for now
|
|
switch (_waitFor)
|
|
{
|
|
case WaitForServiceTypes.Wmi:
|
|
case WaitForServiceTypes.WinRM:
|
|
break;
|
|
case WaitForServiceTypes.PowerShell:
|
|
_powershell = System.Management.Automation.PowerShell.Create();
|
|
_powershell.AddScript(TestPowershellScript);
|
|
break;
|
|
default:
|
|
InvalidOperationException ex = new InvalidOperationException(ComputerResources.NoSupportForCombinedServiceType);
|
|
ErrorRecord error = new ErrorRecord(ex, "NoSupportForCombinedServiceType", ErrorCategory.InvalidOperation, (int)_waitFor);
|
|
ThrowTerminatingError(error);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// ProcessRecord method.
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
|
|
protected override void ProcessRecord()
|
|
{
|
|
// Validate parameters
|
|
ValidateComputerNames();
|
|
|
|
object[] flags = new object[] { 2, 0 };
|
|
if (Force) flags[0] = 6;
|
|
|
|
#if CORECLR
|
|
if (ParameterSetName.Equals(DefaultParameterSet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
#else // TODO:CORECLR Revisit if or when jobs are supported
|
|
if (ParameterSetName.Equals(AsJobParameterSet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string[] names = _validatedComputerNames.ToArray();
|
|
string strComputers = ComputerWMIHelper.GetMachineNames(names);
|
|
if (!ShouldProcess(strComputers))
|
|
return;
|
|
|
|
InvokeWmiMethod WmiInvokeCmd = new InvokeWmiMethod();
|
|
WmiInvokeCmd.Path = ComputerWMIHelper.WMI_Class_OperatingSystem + "=@";
|
|
WmiInvokeCmd.ComputerName = names;
|
|
WmiInvokeCmd.Authentication = DcomAuthentication;
|
|
WmiInvokeCmd.Impersonation = Impersonation;
|
|
WmiInvokeCmd.Credential = Credential;
|
|
WmiInvokeCmd.ThrottleLimit = ThrottleLimit;
|
|
WmiInvokeCmd.Name = "Win32Shutdown";
|
|
WmiInvokeCmd.EnableAllPrivileges = SwitchParameter.Present;
|
|
WmiInvokeCmd.ArgumentList = flags;
|
|
PSWmiJob wmiJob = new PSWmiJob(WmiInvokeCmd, names, ThrottleLimit, Job.GetCommandTextFromInvocationInfo(this.MyInvocation));
|
|
this.JobRepository.Add(wmiJob);
|
|
WriteObject(wmiJob);
|
|
}
|
|
else if (ParameterSetName.Equals(DefaultParameterSet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// CoreCLR does not support DCOM, so there is no point checking
|
|
// it here. It was already validated in BeginProcessing().
|
|
|
|
bool dcomInUse = Protocol.Equals(ComputerWMIHelper.DcomProtocol, StringComparison.OrdinalIgnoreCase);
|
|
ConnectionOptions options = ComputerWMIHelper.GetConnectionOptions(this.DcomAuthentication, this.Impersonation, this.Credential);
|
|
#endif
|
|
if (Wait && _timeout != 0)
|
|
{
|
|
_validatedComputerNames =
|
|
#if !CORECLR
|
|
dcomInUse ? SetUpComputerInfoUsingDcom(_validatedComputerNames, options) :
|
|
#endif
|
|
SetUpComputerInfoUsingWsman(_validatedComputerNames, _cancel.Token);
|
|
}
|
|
|
|
foreach (string computer in _validatedComputerNames)
|
|
{
|
|
bool isLocal = false;
|
|
string compname;
|
|
|
|
if (computer.Equals("localhost", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
compname = _shortLocalMachineName;
|
|
isLocal = true;
|
|
|
|
#if !CORECLR
|
|
if (dcomInUse)
|
|
{
|
|
// The local machine will always at the end of the list. If the current target
|
|
// computer is the local machine, it's safe to set Username and Password to null.
|
|
options.Username = null;
|
|
options.SecurePassword = null;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
compname = computer;
|
|
}
|
|
|
|
// Generate target and action strings
|
|
string action =
|
|
StringUtil.Format(
|
|
ComputerResources.RestartComputerAction,
|
|
isLocal ? ComputerResources.LocalShutdownPrivilege : ComputerResources.RemoteShutdownPrivilege);
|
|
string target =
|
|
isLocal ? StringUtil.Format(ComputerResources.DoubleComputerName, "localhost", compname) : compname;
|
|
|
|
if (!ShouldProcess(target, action))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool isSuccess =
|
|
#if !CORECLR
|
|
dcomInUse ? RestartOneComputerUsingDcom(this, isLocal, compname, flags, options) :
|
|
#endif
|
|
ComputerWMIHelper.InvokeWin32ShutdownUsingWsman(this, isLocal, compname, flags, Credential, WsmanAuthentication, ComputerResources.RestartcomputerFailed, "RestartcomputerFailed", _cancel.Token);
|
|
|
|
if (isSuccess && Wait && _timeout != 0)
|
|
{
|
|
_waitOnComputers.Add(computer);
|
|
}
|
|
|
|
}//end foreach
|
|
|
|
if (_waitOnComputers.Count > 0)
|
|
{
|
|
var restartStageTestList = new List<string>(_waitOnComputers);
|
|
var wmiTestList = new List<string>();
|
|
var winrmTestList = new List<string>();
|
|
var psTestList = new List<string>();
|
|
var allDoneList = new List<string>();
|
|
|
|
bool isForWmi = _waitFor.Equals(WaitForServiceTypes.Wmi);
|
|
bool isForWinRm = _waitFor.Equals(WaitForServiceTypes.WinRM);
|
|
bool isForPowershell = _waitFor.Equals(WaitForServiceTypes.PowerShell);
|
|
|
|
int indicatorIndex = 0;
|
|
int machineCompleteRestart = 0;
|
|
int actualDelay = SecondsToWaitForRestartToBegin;
|
|
bool first = true;
|
|
bool waitComplete = false;
|
|
|
|
_percent = 0;
|
|
_status = ComputerResources.WaitForRestartToBegin;
|
|
_activity = _waitOnComputers.Count == 1 ?
|
|
StringUtil.Format(ComputerResources.RestartSingleComputerActivity, _waitOnComputers[0]) :
|
|
ComputerResources.RestartMultipleComputersActivity;
|
|
|
|
_timer = new Timer(OnTimedEvent, null, _timeoutInMilliseconds, System.Threading.Timeout.Infinite);
|
|
|
|
while (true)
|
|
{
|
|
int loopCount = actualDelay * 4; // (delay * 1000)/250ms
|
|
while (loopCount > 0)
|
|
{
|
|
WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing);
|
|
|
|
loopCount--;
|
|
_waitHandler.Wait(250);
|
|
if (_exit) { break; }
|
|
}
|
|
|
|
if (first)
|
|
{
|
|
actualDelay = _delay;
|
|
first = false;
|
|
|
|
if (_waitOnComputers.Count > 1)
|
|
{
|
|
_status = StringUtil.Format(ComputerResources.WaitForMultipleComputers, machineCompleteRestart, _waitOnComputers.Count);
|
|
WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing);
|
|
}
|
|
}
|
|
|
|
do
|
|
{
|
|
// Test restart stage.
|
|
// We check if the target machine has already rebooted by querying the LastBootUpTime from the Win32_OperatingSystem object.
|
|
// So after this step, we are sure that both the Network and the WMI or WinRM service have already come up.
|
|
if (_exit) { break; }
|
|
if (restartStageTestList.Count > 0)
|
|
{
|
|
if (_waitOnComputers.Count == 1)
|
|
{
|
|
_status = ComputerResources.VerifyRebootStage;
|
|
_percent = CalculateProgressPercentage(StageVerification);
|
|
WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing);
|
|
}
|
|
List<string> nextTestList = (isForWmi || isForPowershell) ? wmiTestList : winrmTestList;
|
|
restartStageTestList =
|
|
#if !CORECLR
|
|
dcomInUse ? TestRestartStageUsingDcom(restartStageTestList, nextTestList, _cancel.Token, options) :
|
|
#endif
|
|
TestRestartStageUsingWsman(restartStageTestList, nextTestList, _cancel.Token);
|
|
}
|
|
|
|
// Test WMI service
|
|
if (_exit) { break; }
|
|
if (wmiTestList.Count > 0)
|
|
{
|
|
#if !CORECLR
|
|
if (dcomInUse)
|
|
{
|
|
// CIM-DCOM is in use. In this case, restart stage checking is done by using WMIv1,
|
|
// so the WMI service on the target machine is already up at this point.
|
|
winrmTestList.AddRange(wmiTestList);
|
|
wmiTestList.Clear();
|
|
|
|
if (_waitOnComputers.Count == 1)
|
|
{
|
|
// This is to simulate the test for WMI service
|
|
_status = ComputerResources.WaitForWMI;
|
|
_percent = CalculateProgressPercentage(WmiConnectionTest);
|
|
|
|
loopCount = actualDelay * 4; // (delay * 1000)/250ms
|
|
while (loopCount > 0)
|
|
{
|
|
WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing);
|
|
|
|
loopCount--;
|
|
_waitHandler.Wait(250);
|
|
if (_exit) { break; }
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
// This statement block executes for both CLRs.
|
|
// In the "full" CLR, it serves as the else case.
|
|
{
|
|
if (_waitOnComputers.Count == 1)
|
|
{
|
|
_status = ComputerResources.WaitForWMI;
|
|
_percent = CalculateProgressPercentage(WmiConnectionTest);
|
|
WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing);
|
|
}
|
|
wmiTestList = TestWmiConnectionUsingWsman(wmiTestList, winrmTestList, _cancel.Token, Credential, WsmanAuthentication, this);
|
|
}
|
|
}
|
|
if (isForWmi) { break; }
|
|
|
|
// Test WinRM service
|
|
if (_exit) { break; }
|
|
if (winrmTestList.Count > 0)
|
|
{
|
|
#if !CORECLR
|
|
if (dcomInUse)
|
|
{
|
|
if (_waitOnComputers.Count == 1)
|
|
{
|
|
_status = ComputerResources.WaitForWinRM;
|
|
_percent = CalculateProgressPercentage(WinrmConnectionTest);
|
|
WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing);
|
|
}
|
|
winrmTestList = TestWinrmConnection(winrmTestList, psTestList, _cancel.Token);
|
|
}
|
|
else
|
|
#endif
|
|
// This statement block executes for both CLRs.
|
|
// In the "full" CLR, it serves as the else case.
|
|
{
|
|
// CIM-WSMan in use. In this case, restart stage checking is done by using WMIv2,
|
|
// so the WinRM service on the target machine is already up at this point.
|
|
psTestList.AddRange(winrmTestList);
|
|
winrmTestList.Clear();
|
|
|
|
if (_waitOnComputers.Count == 1)
|
|
{
|
|
// This is to simulate the test for WinRM service
|
|
_status = ComputerResources.WaitForWinRM;
|
|
_percent = CalculateProgressPercentage(WinrmConnectionTest);
|
|
|
|
loopCount = actualDelay * 4; // (delay * 1000)/250ms
|
|
while (loopCount > 0)
|
|
{
|
|
WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing);
|
|
|
|
loopCount--;
|
|
_waitHandler.Wait(250);
|
|
if (_exit) { break; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (isForWinRm) { break; }
|
|
|
|
// Test PowerShell
|
|
if (_exit) { break; }
|
|
if (psTestList.Count > 0)
|
|
{
|
|
if (_waitOnComputers.Count == 1)
|
|
{
|
|
_status = ComputerResources.WaitForPowerShell;
|
|
_percent = CalculateProgressPercentage(PowerShellConnectionTest);
|
|
WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing);
|
|
}
|
|
psTestList = TestPowerShell(psTestList, allDoneList, _powershell, this.Credential);
|
|
}
|
|
|
|
} while (false);
|
|
|
|
// if time is up or Ctrl+c is typed, break out
|
|
if (_exit) { break; }
|
|
|
|
// Check if the restart completes
|
|
switch (_waitFor)
|
|
{
|
|
case WaitForServiceTypes.Wmi:
|
|
waitComplete = (winrmTestList.Count == _waitOnComputers.Count);
|
|
machineCompleteRestart = winrmTestList.Count;
|
|
break;
|
|
case WaitForServiceTypes.WinRM:
|
|
waitComplete = (psTestList.Count == _waitOnComputers.Count);
|
|
machineCompleteRestart = psTestList.Count;
|
|
break;
|
|
case WaitForServiceTypes.PowerShell:
|
|
waitComplete = (allDoneList.Count == _waitOnComputers.Count);
|
|
machineCompleteRestart = allDoneList.Count;
|
|
break;
|
|
}
|
|
|
|
// Wait is done or time is up
|
|
if (waitComplete || _exit)
|
|
{
|
|
if (waitComplete)
|
|
{
|
|
_status = ComputerResources.RestartComplete;
|
|
WriteProgress(_indicator[indicatorIndex % 4] + _activity, _status, 100, ProgressRecordType.Completed);
|
|
_timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (_waitOnComputers.Count > 1)
|
|
{
|
|
_status = StringUtil.Format(ComputerResources.WaitForMultipleComputers, machineCompleteRestart, _waitOnComputers.Count);
|
|
_percent = machineCompleteRestart * 100 / _waitOnComputers.Count;
|
|
}
|
|
}// end while(true)
|
|
|
|
if (_timeUp)
|
|
{
|
|
// The timeout expires. Write out timeout error messages for the computers that haven't finished restarting
|
|
do
|
|
{
|
|
if (restartStageTestList.Count > 0) { WriteOutTimeoutError(restartStageTestList); }
|
|
if (wmiTestList.Count > 0) { WriteOutTimeoutError(wmiTestList); }
|
|
// Wait for WMI. All computers that finished restarting are put in "winrmTestList"
|
|
if (isForWmi) { break; }
|
|
|
|
// Wait for WinRM. All computers that finished restarting are put in "psTestList"
|
|
if (winrmTestList.Count > 0) { WriteOutTimeoutError(winrmTestList); }
|
|
if (isForWinRm) { break; }
|
|
|
|
if (psTestList.Count > 0) { WriteOutTimeoutError(psTestList); }
|
|
// Wait for PowerShell. All computers that finished restarting are put in "allDoneList"
|
|
} while (false);
|
|
}
|
|
}// end if(waitOnComputer.Count > 0)
|
|
}//end DefaultParameter
|
|
}//End Processrecord
|
|
|
|
/// <summary>
|
|
/// to implement ^C
|
|
/// </summary>
|
|
protected override void StopProcessing()
|
|
{
|
|
_exit = true;
|
|
_cancel.Cancel();
|
|
_waitHandler.Set();
|
|
|
|
if (_timer != null)
|
|
{
|
|
_timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
|
|
}
|
|
|
|
if (_powershell != null)
|
|
{
|
|
_powershell.Stop();
|
|
_powershell.Dispose();
|
|
}
|
|
}
|
|
|
|
|
|
#endregion "Overrides"
|
|
|
|
}
|
|
|
|
#endregion Restart-Computer
|
|
|
|
#region Stop-Computer
|
|
|
|
/// <summary>
|
|
/// cmdlet to stop computer
|
|
/// </summary>
|
|
[Cmdlet(VerbsLifecycle.Stop, "Computer", SupportsShouldProcess = true,
|
|
HelpUri = "http://go.microsoft.com/fwlink/?LinkID=135263", RemotingCapability = RemotingCapability.SupportedByCommand)]
|
|
public sealed class StopComputerCommand : PSCmdlet, IDisposable
|
|
{
|
|
#region Private Members
|
|
|
|
#if !CORECLR
|
|
private ManagementObjectSearcher _searcher;
|
|
#endif
|
|
private readonly CancellationTokenSource _cancel = new CancellationTokenSource();
|
|
private TransportProtocol _transportProtocol = TransportProtocol.DCOM;
|
|
|
|
#endregion
|
|
|
|
#region "Parameters"
|
|
|
|
/// <summary>
|
|
/// parameter
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter AsJob
|
|
{
|
|
get { return _asjob; }
|
|
set
|
|
{
|
|
_asjob = value;
|
|
}
|
|
}
|
|
private SwitchParameter _asjob = false;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "DcomAuthentication".
|
|
/// Specifies the authentication level to be used with WMI connection. Valid
|
|
/// values are:
|
|
///
|
|
/// Unchanged = -1,
|
|
/// Default = 0,
|
|
/// None = 1,
|
|
/// Connect = 2,
|
|
/// Call = 3,
|
|
/// Packet = 4,
|
|
/// PacketIntegrity = 5,
|
|
/// PacketPrivacy = 6.
|
|
/// </summary>
|
|
[Parameter]
|
|
[Alias("Authentication")]
|
|
public AuthenticationLevel DcomAuthentication
|
|
{
|
|
get { return _authentication; }
|
|
set
|
|
{
|
|
_authentication = value;
|
|
}
|
|
}
|
|
private AuthenticationLevel _authentication = AuthenticationLevel.Packet;
|
|
|
|
/// <summary>
|
|
/// The authentication options for CIM_WSMan connection
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateSet(
|
|
"Default",
|
|
"Basic",
|
|
"Negotiate", // can be used with and without credential (without -> PSRP mapped to NegotiateWithImplicitCredential)
|
|
"CredSSP",
|
|
"Digest",
|
|
"Kerberos")] // can be used with and without credential (not sure about implications)
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
|
|
public string WsmanAuthentication
|
|
{
|
|
get { return _wsmanAuthentication; }
|
|
set { _wsmanAuthentication = value; }
|
|
}
|
|
private string _wsmanAuthentication = "Default";
|
|
|
|
/// <summary>
|
|
/// Specify the protocol to use
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateSet(ComputerWMIHelper.DcomProtocol, ComputerWMIHelper.WsmanProtocol)]
|
|
public string Protocol
|
|
{
|
|
get { return _protocol; }
|
|
set
|
|
{
|
|
_protocol = value;
|
|
}
|
|
}
|
|
|
|
//CoreClr does not support DCOM protocol
|
|
// This change makes sure that the the command works seamlessly if user did not explicitly entered the protocol
|
|
#if CORECLR
|
|
private string _protocol = ComputerWMIHelper.WsmanProtocol;
|
|
#else
|
|
private string _protocol = ComputerWMIHelper.DcomProtocol;
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "ComputerName".
|
|
/// Value of the address requested. The form of the value can be either the
|
|
/// computer name ("wxyz1234"), IPv4 address ("192.168.177.124"), or IPv6
|
|
/// address ("2010:836B:4179::836B:4179").
|
|
/// </summary>
|
|
[Parameter(Position = 0, ValueFromPipelineByPropertyName = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
[Alias("CN", "__SERVER", "Server", "IPAddress")]
|
|
public String[] ComputerName
|
|
{
|
|
get { return _computername; }
|
|
set
|
|
{
|
|
_computername = value;
|
|
}
|
|
}
|
|
private String[] _computername = new string[] { "." };
|
|
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "Credential".
|
|
/// Specifies a user account that has permission to perform this action. Type a
|
|
/// user-name, such as "User01" or "Domain01\User01", or enter a PSCredential
|
|
/// object, such as one from the Get-Credential cmdlet
|
|
/// </summary>
|
|
[Parameter(Position = 1)]
|
|
[ValidateNotNullOrEmpty]
|
|
[Credential]
|
|
public PSCredential Credential
|
|
{
|
|
get { return _credential; }
|
|
set
|
|
{
|
|
_credential = value;
|
|
}
|
|
}
|
|
private PSCredential _credential;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "Impersonation".
|
|
/// Specifies the impersonation level to use when calling the WMI method. Valid
|
|
/// values are:
|
|
///
|
|
/// Default = 0,
|
|
/// Anonymous = 1,
|
|
/// Identify = 2,
|
|
/// Impersonate = 3,
|
|
/// Delegate = 4.
|
|
/// </summary>
|
|
[Parameter]
|
|
public ImpersonationLevel Impersonation
|
|
{
|
|
get { return _impersonation; }
|
|
set
|
|
{
|
|
_impersonation = value;
|
|
}
|
|
}
|
|
private ImpersonationLevel _impersonation = ImpersonationLevel.Impersonate;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "ThrottleLimit".
|
|
/// The number of concurrent computers on which the command will be allowed to
|
|
/// execute
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateRange(int.MinValue, (int)1000)]
|
|
public Int32 ThrottleLimit
|
|
{
|
|
get { return _throttlelimit; }
|
|
set
|
|
{
|
|
_throttlelimit = value;
|
|
if (_throttlelimit <= 0)
|
|
_throttlelimit = 32;
|
|
}
|
|
}
|
|
private Int32 _throttlelimit = 32;
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "ThrottleLimit".
|
|
/// The number of concurrent computers on which the command will be allowed to
|
|
/// execute
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter Force
|
|
{
|
|
get { return _force; }
|
|
set
|
|
{
|
|
_force = value;
|
|
}
|
|
}
|
|
private SwitchParameter _force = false;
|
|
|
|
#endregion "parameters"
|
|
|
|
#region "IDisposable Members"
|
|
|
|
/// <summary>
|
|
/// Dispose Method
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
_cancel.Dispose();
|
|
}
|
|
catch (ObjectDisposedException) { }
|
|
}
|
|
|
|
#endregion "IDisposable Members"
|
|
|
|
#region "Overrides"
|
|
|
|
/// <summary>
|
|
/// BeginProcessing
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
base.BeginProcessing();
|
|
|
|
// Verify parameter set
|
|
bool haveProtocolParam = this.MyInvocation.BoundParameters.ContainsKey("Protocol");
|
|
bool haveWsmanAuthenticationParam = this.MyInvocation.BoundParameters.ContainsKey("WsmanAuthentication");
|
|
bool haveDcomAuthenticationParam = this.MyInvocation.BoundParameters.ContainsKey("DcomAuthentication");
|
|
bool haveDcomImpersonation = this.MyInvocation.BoundParameters.ContainsKey("Impersonation");
|
|
_transportProtocol = (this.Protocol.Equals(ComputerWMIHelper.WsmanProtocol, StringComparison.OrdinalIgnoreCase) || (haveWsmanAuthenticationParam && !haveProtocolParam)) ?
|
|
TransportProtocol.WSMan : TransportProtocol.DCOM;
|
|
|
|
if (haveWsmanAuthenticationParam && (haveDcomAuthenticationParam || haveDcomImpersonation))
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.StopCommandParamWSManAuthConflict, ComputerResources.StopCommandParamMessage);
|
|
ThrowTerminatingError(
|
|
new ErrorRecord(
|
|
new PSArgumentException(errMsg),
|
|
"InvalidParameter",
|
|
ErrorCategory.InvalidArgument,
|
|
this));
|
|
}
|
|
|
|
if ((_transportProtocol == TransportProtocol.DCOM) && haveWsmanAuthenticationParam)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.StopCommandWSManAuthProtcolConflict, ComputerResources.StopCommandParamMessage);
|
|
ThrowTerminatingError(
|
|
new ErrorRecord(
|
|
new PSArgumentException(errMsg),
|
|
"InvalidParameter",
|
|
ErrorCategory.InvalidArgument,
|
|
this));
|
|
}
|
|
|
|
if ((_transportProtocol == TransportProtocol.WSMan) && (haveDcomAuthenticationParam || haveDcomImpersonation))
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.StopCommandAuthProtcolConflict, ComputerResources.StopCommandParamMessage);
|
|
ThrowTerminatingError(
|
|
new ErrorRecord(
|
|
new PSArgumentException(errMsg),
|
|
"InvalidParameter",
|
|
ErrorCategory.InvalidArgument,
|
|
this));
|
|
}
|
|
|
|
#if CORECLR
|
|
if (this.MyInvocation.BoundParameters.ContainsKey("DcomAuthentication"))
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.InvalidParameterForCoreClr, "DcomAuthentication");
|
|
PSArgumentException ex = new PSArgumentException(errMsg, ComputerResources.InvalidParameterForCoreClr);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForCoreClr", ErrorCategory.InvalidArgument, null));
|
|
}
|
|
|
|
if (this.MyInvocation.BoundParameters.ContainsKey("Impersonation"))
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.InvalidParameterForCoreClr, "Impersonation");
|
|
PSArgumentException ex = new PSArgumentException(errMsg, ComputerResources.InvalidParameterForCoreClr);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForCoreClr", ErrorCategory.InvalidArgument, null));
|
|
}
|
|
|
|
if(this.Protocol.Equals(ComputerWMIHelper.DcomProtocol , StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
InvalidOperationException ex = new InvalidOperationException(ComputerResources.InvalidParameterDCOMNotSupported);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterDCOMNotSupported", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// ProcessRecord
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
|
|
protected override void ProcessRecord()
|
|
{
|
|
object[] flags = new object[] { 1, 0 };
|
|
if (_force.IsPresent)
|
|
flags[0] = 5;
|
|
|
|
switch (_transportProtocol)
|
|
{
|
|
#if !CORECLR
|
|
case TransportProtocol.DCOM:
|
|
ProcessDCOMProtocol(flags);
|
|
break;
|
|
#endif
|
|
|
|
case TransportProtocol.WSMan:
|
|
ProcessWSManProtocol(flags);
|
|
break;
|
|
}
|
|
}//End Processrecord
|
|
|
|
/// <summary>
|
|
/// to implement ^C
|
|
/// </summary>
|
|
protected override void StopProcessing()
|
|
{
|
|
#if !CORECLR
|
|
ManagementObjectSearcher searcher = _searcher;
|
|
if (searcher != null)
|
|
{
|
|
try
|
|
{
|
|
searcher.Dispose();
|
|
}
|
|
catch (ObjectDisposedException) { }
|
|
}
|
|
#endif
|
|
|
|
try
|
|
{
|
|
_cancel.Cancel();
|
|
}
|
|
catch (ObjectDisposedException) { }
|
|
catch (AggregateException) { }
|
|
}
|
|
|
|
#endregion "Overrides"
|
|
|
|
#region Private Methods
|
|
|
|
#if !CORECLR
|
|
private void ProcessDCOMProtocol(object[] flags)
|
|
{
|
|
if (_asjob.IsPresent)
|
|
{
|
|
string strComputers = ComputerWMIHelper.GetMachineNames(ComputerName);
|
|
if (!ShouldProcess(strComputers))
|
|
return;
|
|
InvokeWmiMethod WmiInvokeCmd = new InvokeWmiMethod();
|
|
WmiInvokeCmd.Path = ComputerWMIHelper.WMI_Class_OperatingSystem + "=@";
|
|
WmiInvokeCmd.ComputerName = _computername;
|
|
WmiInvokeCmd.Authentication = _authentication;
|
|
WmiInvokeCmd.Impersonation = _impersonation;
|
|
WmiInvokeCmd.Credential = _credential;
|
|
WmiInvokeCmd.ThrottleLimit = _throttlelimit;
|
|
WmiInvokeCmd.Name = "Win32Shutdown";
|
|
WmiInvokeCmd.EnableAllPrivileges = SwitchParameter.Present;
|
|
WmiInvokeCmd.ArgumentList = flags;
|
|
PSWmiJob wmiJob = new PSWmiJob(WmiInvokeCmd, _computername, _throttlelimit, Job.GetCommandTextFromInvocationInfo(this.MyInvocation));
|
|
this.JobRepository.Add(wmiJob);
|
|
WriteObject(wmiJob);
|
|
}
|
|
else
|
|
{
|
|
string compname = string.Empty;
|
|
string strLocal = string.Empty;
|
|
|
|
foreach (string computer in _computername)
|
|
{
|
|
if ((computer.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (computer.Equals(".", StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
compname = Dns.GetHostName();
|
|
strLocal = "localhost";
|
|
}
|
|
else
|
|
{
|
|
compname = computer;
|
|
}
|
|
|
|
if (!ShouldProcess(StringUtil.Format(ComputerResources.DoubleComputerName, strLocal, compname)))
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
ConnectionOptions options = ComputerWMIHelper.GetConnectionOptions(_authentication, this.Impersonation, this.Credential);
|
|
ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options);
|
|
EnumerationOptions enumOptions = new EnumerationOptions();
|
|
enumOptions.UseAmendedQualifiers = true;
|
|
enumOptions.DirectRead = true;
|
|
ObjectQuery query = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem);
|
|
using (_searcher = new ManagementObjectSearcher(scope, query, enumOptions))
|
|
{
|
|
foreach (ManagementObject obj in _searcher.Get())
|
|
{
|
|
using (obj)
|
|
{
|
|
object result = obj.InvokeMethod("Win32shutdown", flags);
|
|
int retVal = Convert.ToInt32(result.ToString(), CultureInfo.CurrentCulture);
|
|
if (retVal != 0)
|
|
{
|
|
ComputerWMIHelper.WriteNonTerminatingError(retVal, this, compname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_searcher = null;
|
|
}
|
|
catch (ManagementException e)
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "StopComputerException", ErrorCategory.InvalidOperation, compname);
|
|
WriteError(errorRecord);
|
|
continue;
|
|
}
|
|
catch (System.Runtime.InteropServices.COMException e)
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "StopComputerException", ErrorCategory.InvalidOperation, compname);
|
|
WriteError(errorRecord);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
private void ProcessWSManProtocol(object[] flags)
|
|
{
|
|
if (_asjob.IsPresent)
|
|
{
|
|
// TODO: Need job for MI.Net WSMan protocol
|
|
// Early return of job object.
|
|
throw new PSNotSupportedException();
|
|
}
|
|
|
|
foreach (string computer in _computername)
|
|
{
|
|
string compname = string.Empty;
|
|
string strLocal = string.Empty;
|
|
bool isLocalHost = false;
|
|
|
|
if (_cancel.Token.IsCancellationRequested) { break; }
|
|
|
|
if ((computer.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (computer.Equals(".", StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
compname = Dns.GetHostName();
|
|
strLocal = "localhost";
|
|
isLocalHost = true;
|
|
}
|
|
else
|
|
{
|
|
compname = computer;
|
|
}
|
|
|
|
if (!ShouldProcess(StringUtil.Format(ComputerResources.DoubleComputerName, strLocal, compname)))
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
ComputerWMIHelper.InvokeWin32ShutdownUsingWsman(
|
|
this,
|
|
isLocalHost,
|
|
compname,
|
|
flags,
|
|
Credential,
|
|
WsmanAuthentication,
|
|
ComputerResources.StopcomputerFailed,
|
|
"StopComputerException",
|
|
_cancel.Token);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
|
|
#if !CORECLR // TODO:CORECLR Enable once moved to MI .Net
|
|
|
|
#region Restore-Computer
|
|
|
|
/// <summary>
|
|
/// This cmdlet is to Restore Computer
|
|
/// </summary>
|
|
[Cmdlet(VerbsData.Restore, "Computer", SupportsShouldProcess = true, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=135254")]
|
|
public sealed class RestoreComputerCommand : PSCmdlet, IDisposable
|
|
{
|
|
#region Parameters
|
|
/// <summary>
|
|
/// Restorepoint parameter
|
|
/// </summary>
|
|
|
|
[Parameter(Position = 0, Mandatory = true)]
|
|
[ValidateNotNull]
|
|
[ValidateRangeAttribute((int)1, int.MaxValue)]
|
|
[Alias("SequenceNumber", "SN", "RP")]
|
|
public int RestorePoint
|
|
{
|
|
get { return _restorepoint; }
|
|
set
|
|
{
|
|
_restorepoint = value;
|
|
}
|
|
}
|
|
private int _restorepoint;
|
|
|
|
#endregion
|
|
|
|
private ManagementClass WMIClass;
|
|
|
|
#region "IDisposable Members"
|
|
|
|
/// <summary>
|
|
/// Dispose Method
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
this.Dispose(true);
|
|
// Use SupressFinalize in case a subclass
|
|
// of this type implements a finalizer.
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dispose Method.
|
|
/// </summary>
|
|
/// <param name="disposing"></param>
|
|
public void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
if (WMIClass != null)
|
|
{
|
|
WMIClass.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion "IDisposable Members"
|
|
|
|
#region overrides
|
|
/// <summary>
|
|
/// Restores the computer with
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
// system restore APIs are not supported on ARM platform
|
|
if (ComputerWMIHelper.SkipSystemRestoreOperationForARMPlatform(this))
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
|
|
ConnectionOptions conn = ComputerWMIHelper.GetConnectionOptions(AuthenticationLevel.Packet, ImpersonationLevel.Impersonate, null);
|
|
ManagementPath mPath = new ManagementPath();
|
|
mPath.Path = ComputerWMIHelper.WMI_Path_Default;
|
|
ManagementScope scope = new ManagementScope(mPath, conn);
|
|
scope.Connect();
|
|
WMIClass = new ManagementClass(ComputerWMIHelper.WMI_Class_SystemRestore);
|
|
WMIClass.Scope = scope;
|
|
|
|
//query to get the list of restore points
|
|
ObjectQuery oquery = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_SystemRestore + " where SequenceNumber = " + _restorepoint);
|
|
using (ManagementObjectSearcher R_results = new ManagementObjectSearcher(scope, oquery))
|
|
{
|
|
//check if the entered restore point is a valid one
|
|
if (R_results.Get().Count == 0)
|
|
{
|
|
string message = StringUtil.Format(ComputerResources.InvalidRestorePoint, _restorepoint);
|
|
ArgumentException e = new ArgumentException(message);
|
|
ErrorRecord errorrecord = new ErrorRecord(e, "InvalidRestorePoint", ErrorCategory.InvalidArgument, null);
|
|
WriteError(errorrecord);
|
|
return;
|
|
}
|
|
}
|
|
//confrm with the user before restoring
|
|
string computerName = Environment.MachineName;
|
|
if (!ShouldProcess(computerName))
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//add the restorepoint parameter and invoke th emethod
|
|
Object[] arr = new Object[] { _restorepoint };
|
|
WMIClass.InvokeMethod("Restore", arr);
|
|
//Restore requires a Reboot and while reboot only the restore actually happens
|
|
//code to restart computer
|
|
mPath.Path = ComputerWMIHelper.WMI_Path_CIM;
|
|
scope.Path = mPath;
|
|
ManagementClass OsWMIClass = new ManagementClass(ComputerWMIHelper.WMI_Class_OperatingSystem);
|
|
OsWMIClass.Scope = scope;
|
|
ObjectQuery objQuery = new ObjectQuery("Select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem);
|
|
using (ManagementObjectSearcher results = new ManagementObjectSearcher(scope, objQuery))
|
|
{
|
|
foreach (ManagementObject mobj in results.Get())
|
|
{
|
|
using (mobj)
|
|
{
|
|
string[] param = { "" };
|
|
mobj.InvokeMethod("Reboot", param);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
catch (ManagementException e)
|
|
{
|
|
if (e.ErrorCode.ToString().Equals("NotFound") || e.ErrorCode.ToString().Equals("InvalidClass"))
|
|
{
|
|
Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.NotSupported));
|
|
WriteError(new ErrorRecord(Ex, "RestoreComputerNotSupported", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
else
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "GetWMIManagementException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
}
|
|
|
|
}
|
|
catch (COMException e)
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(e.Message))
|
|
{
|
|
|
|
Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.SystemRestoreServiceDisabled));
|
|
WriteError(new ErrorRecord(Ex, "ServiceDisabled", ErrorCategory.InvalidOperation, null));
|
|
}
|
|
else
|
|
{
|
|
ErrorRecord errorRecord = new ErrorRecord(e, "COMException", ErrorCategory.InvalidOperation, null);
|
|
WriteError(errorRecord);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// to implement ^C
|
|
/// </summary>
|
|
protected override void StopProcessing()
|
|
{
|
|
if (WMIClass != null)
|
|
{
|
|
WMIClass.Dispose();
|
|
}
|
|
}
|
|
|
|
#endregion overrides
|
|
|
|
}
|
|
#endregion
|
|
|
|
#region Add-Computer
|
|
|
|
/// <summary>
|
|
/// Options for joining a computer to a domain
|
|
/// </summary>
|
|
[Flags]
|
|
public enum JoinOptions
|
|
{
|
|
/// <summary>
|
|
/// Create account on the domain
|
|
/// </summary>
|
|
AccountCreate = 0x2,
|
|
|
|
/// <summary>
|
|
/// Join operation is part of an upgrade
|
|
/// </summary>
|
|
Win9XUpgrade = 0x10,
|
|
|
|
/// <summary>
|
|
/// Perform an unsecure join
|
|
/// </summary>
|
|
UnsecuredJoin = 0x40,
|
|
|
|
/// <summary>
|
|
/// Indicate that the password passed to the join operation is the local machine account password, not a user password.
|
|
/// It's valid only for unsecure join
|
|
/// </summary>
|
|
PasswordPass = 0x80,
|
|
|
|
/// <summary>
|
|
/// Writing SPN and DNSHostName attributes on the computer object should be deferred until the rename operation that
|
|
/// follows the join operation
|
|
/// </summary>
|
|
DeferSPNSet = 0x100,
|
|
|
|
/// <summary>
|
|
/// Join the target machine with a new name queried from the registry. This options is used if the rename has been called prior
|
|
/// to rebooting the machine
|
|
/// </summary>
|
|
JoinWithNewName = 0x400,
|
|
|
|
/// <summary>
|
|
/// Use a readonly domain controller
|
|
/// </summary>
|
|
JoinReadOnly = 0x800,
|
|
|
|
/// <summary>
|
|
/// Invoke during insatll
|
|
/// </summary>
|
|
InstallInvoke = 0x40000
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the specified computer(s) to the Domain or Work Group. If the account
|
|
/// does not already exist on the domain, it also creates one (see notes for
|
|
/// implementation details).
|
|
/// If the computer is already joined to a domain, it can be moved to a new
|
|
/// domain (see notes for implementation details).
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.PowerShell", "PS1004AcceptForceParameterWhenCallingShouldContinue")]
|
|
[Cmdlet(VerbsCommon.Add, "Computer", SupportsShouldProcess = true, DefaultParameterSetName = DomainParameterSet,
|
|
HelpUri = "http://go.microsoft.com/fwlink/?LinkID=135194", RemotingCapability = RemotingCapability.SupportedByCommand)]
|
|
[OutputType(typeof(ComputerChangeInfo))]
|
|
public class AddComputerCommand : PSCmdlet
|
|
{
|
|
#region parameter
|
|
|
|
private const string DomainParameterSet = "Domain";
|
|
private const string WorkgroupParameterSet = "Workgroup";
|
|
|
|
/// <summary>
|
|
/// Target computer names
|
|
/// </summary>
|
|
[Parameter(ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public string[] ComputerName
|
|
{
|
|
get { return _computerName; }
|
|
set { _computerName = value; }
|
|
}
|
|
private string[] _computerName ={ "localhost" };
|
|
|
|
/// <summary>
|
|
/// The local admin credential to the target computer
|
|
/// </summary>
|
|
[Parameter]
|
|
[Credential]
|
|
[ValidateNotNullOrEmpty]
|
|
public PSCredential LocalCredential
|
|
{
|
|
get { return _localCredential; }
|
|
set { _localCredential = value; }
|
|
}
|
|
private PSCredential _localCredential;
|
|
|
|
/// <summary>
|
|
/// The domain credential used to unjoin a domain
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = DomainParameterSet)]
|
|
[Credential]
|
|
[ValidateNotNullOrEmpty]
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
|
|
public PSCredential UnjoinDomainCredential
|
|
{
|
|
get { return _unjoinDomainCredential; }
|
|
set { _unjoinDomainCredential = value; }
|
|
}
|
|
private PSCredential _unjoinDomainCredential;
|
|
|
|
/// <summary>
|
|
/// The domain credential.
|
|
/// In DomainParameterSet, it is for the domain to join to.
|
|
/// In WorkgroupParameterSet, it is for the doamin to disjoin from.
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = DomainParameterSet, Mandatory = true)]
|
|
[Parameter(ParameterSetName = WorkgroupParameterSet)]
|
|
[Alias("DomainCredential")]
|
|
[Credential]
|
|
[ValidateNotNullOrEmpty]
|
|
public PSCredential Credential
|
|
{
|
|
get { return _domainCredential; }
|
|
set { _domainCredential = value; }
|
|
}
|
|
private PSCredential _domainCredential;
|
|
|
|
/// <summary>
|
|
/// Name of the domain to join
|
|
/// </summary>
|
|
[Parameter(Position = 0, Mandatory = true, ParameterSetName = DomainParameterSet)]
|
|
[Alias("DN", "Domain")]
|
|
[ValidateNotNullOrEmpty]
|
|
public String DomainName
|
|
{
|
|
get { return _domainName; }
|
|
set { _domainName = value; }
|
|
}
|
|
private String _domainName;
|
|
|
|
/// <summary>
|
|
/// The organization unit (OU). It's the path on the AD under which the new account will
|
|
/// be created
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = DomainParameterSet)]
|
|
[Alias("OU")]
|
|
[ValidateNotNullOrEmpty]
|
|
public string OUPath
|
|
{
|
|
get { return _ouPath; }
|
|
set { _ouPath = value; }
|
|
}
|
|
private string _ouPath;
|
|
|
|
/// <summary>
|
|
/// The name of a domain controller that performs the add.
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = DomainParameterSet)]
|
|
[Alias("DC")]
|
|
[ValidateNotNullOrEmpty]
|
|
public string Server
|
|
{
|
|
get { return _server; }
|
|
set { _server = value; }
|
|
}
|
|
private string _server;
|
|
|
|
/// <summary>
|
|
/// Perform an unsecure join.
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
|
|
[Parameter(ParameterSetName = DomainParameterSet)]
|
|
public SwitchParameter Unsecure
|
|
{
|
|
get { return _unsecure; }
|
|
set { _unsecure = value; }
|
|
}
|
|
private SwitchParameter _unsecure;
|
|
|
|
/// <summary>
|
|
/// Additional options for the "join domain" operation
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = DomainParameterSet)]
|
|
public JoinOptions Options
|
|
{
|
|
get { return _joinOptions; }
|
|
set { _joinOptions = value; }
|
|
}
|
|
private JoinOptions _joinOptions = JoinOptions.AccountCreate;
|
|
|
|
/// <summary>
|
|
/// Name of the workgroup to join in.
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "WorkGroup")]
|
|
[Parameter(Position = 0, Mandatory = true, ParameterSetName = WorkgroupParameterSet)]
|
|
[Alias("WGN")]
|
|
[ValidateNotNullOrEmpty]
|
|
public string WorkgroupName
|
|
{
|
|
get { return _workgroupName; }
|
|
set { _workgroupName = value; }
|
|
}
|
|
private string _workgroupName;
|
|
|
|
/// <summary>
|
|
/// Restart the target computer
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter Restart
|
|
{
|
|
get { return _restart; }
|
|
set { _restart = value; }
|
|
}
|
|
private SwitchParameter _restart = false;
|
|
|
|
/// <summary>
|
|
/// Emit the output.
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter PassThru
|
|
{
|
|
get { return _passThru; }
|
|
set { _passThru = value; }
|
|
}
|
|
private SwitchParameter _passThru;
|
|
|
|
/// <summary>
|
|
/// New names for the target computers
|
|
/// </summary>
|
|
[Parameter(ValueFromPipelineByPropertyName = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
public string NewName
|
|
{
|
|
get { return _newName; }
|
|
set { _newName = value; }
|
|
}
|
|
private string _newName;
|
|
|
|
/// <summary>
|
|
/// To suppress ShouldContinue
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter Force
|
|
{
|
|
get { return _force; }
|
|
set { _force = value; }
|
|
}
|
|
private bool _force;
|
|
|
|
private int _joinDomainflags = 1;
|
|
private bool _containsLocalHost = false;
|
|
private string _newNameForLocalHost = null;
|
|
|
|
private readonly string _shortLocalMachineName = Dns.GetHostName();
|
|
private readonly string _fullLocalMachineName = Dns.GetHostEntry("").HostName;
|
|
|
|
#endregion parameter
|
|
|
|
#region private
|
|
|
|
/// <summary>
|
|
/// Unjoin the computer from its current domain
|
|
/// <remarks>
|
|
/// In the DomainParameterSet, the UnjoinDomainCredential is our first choice to unjoin a domain.
|
|
/// But if the UnjoinDomainCredential is not specified, the DomainCredential will be our second
|
|
/// choice. This is to keep the backward compatibility. In Win7, we can do:
|
|
/// Add-Computer -DomianName domain1 -Credential $credForDomain1AndDomain2
|
|
/// to switch the local machine that is currently in domain2 to domain1.
|
|
///
|
|
/// Since DomainCredential has an alias "Credential", the same command should still work for the
|
|
/// new Add-Computer cmdlet.
|
|
///
|
|
/// In the WorkgroupParameterSet, the UnjoinDomainCredential is the only choice.
|
|
/// </remarks>
|
|
/// </summary>
|
|
/// <param name="computerSystem"></param>
|
|
/// <param name="computerName"></param>
|
|
/// <param name="curDomainName"></param>
|
|
/// <param name="dUserName"></param>
|
|
/// <param name="dPassword"></param>
|
|
/// <returns></returns>
|
|
private int UnjoinDomain(ManagementObject computerSystem, string computerName, string curDomainName, string dUserName, string dPassword)
|
|
{
|
|
ManagementBaseObject unjoinDomainParameter = computerSystem.GetMethodParameters("UnjoinDomainOrWorkgroup");
|
|
unjoinDomainParameter.SetPropertyValue("UserName", dUserName);
|
|
unjoinDomainParameter.SetPropertyValue("Password", dPassword);
|
|
unjoinDomainParameter.SetPropertyValue("FUnjoinOptions", 4); // default option, disable the active directory
|
|
|
|
ManagementBaseObject result = computerSystem.InvokeMethod("UnjoinDomainOrWorkgroup", unjoinDomainParameter, null);
|
|
Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Unjoin method is invoked");
|
|
int returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture);
|
|
if (returnCode != 0)
|
|
{
|
|
var ex = new Win32Exception(returnCode);
|
|
WriteErrorHelper(ComputerResources.FailToUnjoinDomain, "FailToUnjoinDomain", computerName,
|
|
ErrorCategory.OperationStopped, false, computerName, curDomainName, ex.Message);
|
|
}
|
|
return returnCode;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Join a domain from a workgroup
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// If a computer is already in a domain, we first unjoin it from its current domain, and
|
|
/// then do the join operation to the new domain. So when this method is invoked, we are
|
|
/// currently in a workgroup
|
|
/// </remarks>
|
|
/// <param name="computerSystem"></param>
|
|
/// <param name="computerName"></param>
|
|
/// <param name="oldDomainName"></param>
|
|
/// <param name="curWorkgroupName"></param>
|
|
/// <returns></returns>
|
|
private int JoinDomain(ManagementObject computerSystem, string computerName, string oldDomainName, string curWorkgroupName)
|
|
{
|
|
string joinDomainUserName = Credential != null ? Credential.UserName : null;
|
|
string joinDomainPassword = Credential != null ? Utils.GetStringFromSecureString(Credential.Password) : null;
|
|
|
|
ManagementBaseObject joinDomainParameter = computerSystem.GetMethodParameters("JoinDomainOrWorkgroup");
|
|
joinDomainParameter.SetPropertyValue("Name", _domainName);
|
|
joinDomainParameter.SetPropertyValue("UserName", joinDomainUserName);
|
|
joinDomainParameter.SetPropertyValue("Password", joinDomainPassword);
|
|
joinDomainParameter.SetPropertyValue("AccountOU", _ouPath);
|
|
joinDomainParameter.SetPropertyValue("FJoinOptions", _joinDomainflags);
|
|
|
|
ManagementBaseObject result = computerSystem.InvokeMethod("JoinDomainOrWorkgroup", joinDomainParameter, null);
|
|
Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Join method is invoked");
|
|
int returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture);
|
|
if (returnCode != 0)
|
|
{
|
|
var ex = new Win32Exception(returnCode);
|
|
string errMsg;
|
|
string errorId;
|
|
if (oldDomainName != null)
|
|
{
|
|
errMsg = StringUtil.Format(ComputerResources.FailToJoinNewDomainAfterUnjoinOldDomain,
|
|
computerName, oldDomainName, _domainName, ex.Message);
|
|
errorId = "FailToJoinNewDomainAfterUnjoinOldDomain";
|
|
}
|
|
else
|
|
{
|
|
errMsg = StringUtil.Format(ComputerResources.FailToJoinDomainFromWorkgroup, computerName,
|
|
_domainName, curWorkgroupName, ex.Message);
|
|
errorId = "FailToJoinDomainFromWorkgroup";
|
|
}
|
|
|
|
WriteErrorHelper(errMsg, errorId, computerName, ErrorCategory.OperationStopped, false);
|
|
}
|
|
return returnCode;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Join in a new workgroup from the current workgroup
|
|
/// </summary>
|
|
/// <param name="computerSystem"></param>
|
|
/// <param name="computerName"></param>
|
|
/// <param name="oldDomainName"></param>
|
|
/// <returns></returns>
|
|
private int JoinWorkgroup(ManagementObject computerSystem, string computerName, string oldDomainName)
|
|
{
|
|
ManagementBaseObject joinWorkgroupParam = computerSystem.GetMethodParameters("JoinDomainOrWorkgroup");
|
|
joinWorkgroupParam.SetPropertyValue("Name", _workgroupName);
|
|
joinWorkgroupParam.SetPropertyValue("UserName", null);
|
|
joinWorkgroupParam.SetPropertyValue("Password", null);
|
|
joinWorkgroupParam.SetPropertyValue("FJoinOptions", 0); // join a workgroup
|
|
|
|
ManagementBaseObject result = computerSystem.InvokeMethod("JoinDomainOrWorkgroup", joinWorkgroupParam, null);
|
|
Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Join method is invoked");
|
|
int returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture);
|
|
if (returnCode != 0)
|
|
{
|
|
var ex = new Win32Exception(returnCode);
|
|
string errMsg;
|
|
if (oldDomainName != null)
|
|
{
|
|
errMsg =
|
|
StringUtil.Format(ComputerResources.FailToSwitchFromDomainToWorkgroup, computerName,
|
|
oldDomainName, _workgroupName, ex.Message);
|
|
}
|
|
else
|
|
{
|
|
errMsg = StringUtil.Format(ComputerResources.FailToJoinWorkGroup, computerName, _workgroupName,
|
|
ex.Message);
|
|
}
|
|
|
|
WriteErrorHelper(errMsg, "FailToJoinWorkGroup", computerName, ErrorCategory.OperationStopped, false);
|
|
}
|
|
return returnCode;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rename the computer in workgroup
|
|
/// </summary>
|
|
/// <param name="computerSystem"></param>
|
|
/// <param name="computerName"></param>
|
|
/// <param name="newName"></param>
|
|
/// <returns></returns>
|
|
private int RenameComputer(ManagementObject computerSystem, string computerName, string newName)
|
|
{
|
|
string domainUserName = null;
|
|
string domainPassword = null;
|
|
|
|
if (_domainName != null && Credential != null)
|
|
{
|
|
// The rename operation happens after the computer is joined to the new domain, so we should provide
|
|
// the domain user name and password to the rename operation
|
|
domainUserName = Credential.UserName;
|
|
domainPassword = Utils.GetStringFromSecureString(Credential.Password);
|
|
}
|
|
|
|
ManagementBaseObject renameParameter = computerSystem.GetMethodParameters("Rename");
|
|
renameParameter.SetPropertyValue("Name", newName);
|
|
renameParameter.SetPropertyValue("UserName", domainUserName);
|
|
renameParameter.SetPropertyValue("Password", domainPassword);
|
|
|
|
ManagementBaseObject result = computerSystem.InvokeMethod("Rename", renameParameter, null);
|
|
Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Rename method is invoked");
|
|
int returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture);
|
|
if (returnCode != 0)
|
|
{
|
|
var ex = new Win32Exception(returnCode);
|
|
string errMsg;
|
|
string errorId;
|
|
if (_workgroupName != null)
|
|
{
|
|
errMsg = StringUtil.Format(ComputerResources.FailToRenameAfterJoinWorkgroup, computerName,
|
|
_workgroupName, newName, ex.Message);
|
|
errorId = "FailToRenameAfterJoinWorkgroup";
|
|
}
|
|
else
|
|
{
|
|
errMsg = StringUtil.Format(ComputerResources.FailToRenameAfterJoinDomain, computerName, _domainName,
|
|
newName, ex.Message);
|
|
errorId = "FailToRenameAfterJoinDomain";
|
|
}
|
|
|
|
WriteErrorHelper(errMsg, errorId, computerName, ErrorCategory.OperationStopped, false);
|
|
}
|
|
return returnCode;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to write out non-terminating errors
|
|
/// </summary>
|
|
/// <param name="resourceString"></param>
|
|
/// <param name="errorId"></param>
|
|
/// <param name="targetObj"></param>
|
|
/// <param name="category"></param>
|
|
/// <param name="terminating"></param>
|
|
/// <param name="args"></param>
|
|
private void WriteErrorHelper(string resourceString, string errorId, object targetObj, ErrorCategory category, bool terminating, params object[] args)
|
|
{
|
|
string errMsg;
|
|
if (null == args || 0 == args.Length)
|
|
{
|
|
// Don't format in case the string contains literal curly braces
|
|
errMsg = resourceString;
|
|
}
|
|
else
|
|
{
|
|
errMsg = StringUtil.Format(resourceString, args);
|
|
}
|
|
|
|
if (String.IsNullOrEmpty(errMsg))
|
|
{
|
|
Dbg.Diagnostics.Assert(false, "Could not load text for error record '" + errorId + "'");
|
|
}
|
|
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), errorId,
|
|
category, targetObj);
|
|
if (terminating)
|
|
{
|
|
ThrowTerminatingError(error);
|
|
}
|
|
else
|
|
{
|
|
WriteError(error);
|
|
}
|
|
}
|
|
|
|
private void DoAddComputerAction(string computer, string newName, bool isLocalhost, ConnectionOptions options, EnumerationOptions enumOptions, ObjectQuery computerSystemQuery)
|
|
{
|
|
int returnCode = 0;
|
|
bool success = false;
|
|
string computerName = isLocalhost ? _shortLocalMachineName : computer;
|
|
|
|
if (ParameterSetName == DomainParameterSet)
|
|
{
|
|
string action = StringUtil.Format(ComputerResources.AddComputerActionDomain, _domainName);
|
|
if (!ShouldProcess(computerName, action))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string action = StringUtil.Format(ComputerResources.AddComputerActionWorkgroup, _workgroupName);
|
|
if (!ShouldProcess(computerName, action))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check the length of the new name
|
|
if (newName != null && newName.Length > ComputerWMIHelper.NetBIOSNameMaxLength)
|
|
{
|
|
string truncatedName = newName.Substring(0, ComputerWMIHelper.NetBIOSNameMaxLength);
|
|
string query = StringUtil.Format(ComputerResources.TruncateNetBIOSName, truncatedName);
|
|
string caption = ComputerResources.TruncateNetBIOSNameCaption;
|
|
if (!Force && !ShouldContinue(query, caption))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If LocalCred is given, use the local credential for WMI connection. Otherwise, use
|
|
// the current caller's context (Username = null, Password = null)
|
|
if (LocalCredential != null)
|
|
{
|
|
options.SecurePassword = LocalCredential.Password;
|
|
options.Username = ComputerWMIHelper.GetLocalAdminUserName(computerName, LocalCredential);
|
|
}
|
|
|
|
// The local machine will always be processed in the very end. If the
|
|
// current target computer is the local machine, it's the last one to
|
|
// be processed, so we can safely set the Username and Password to be
|
|
// null. We cannot use a user credential when connecting to the local
|
|
// machine.
|
|
if (isLocalhost)
|
|
{
|
|
options.Username = null;
|
|
options.SecurePassword = null;
|
|
}
|
|
|
|
ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options);
|
|
|
|
try
|
|
{
|
|
using (var searcher = new ManagementObjectSearcher(scope, computerSystemQuery, enumOptions))
|
|
{
|
|
foreach (ManagementObject computerSystem in searcher.Get())
|
|
{
|
|
using (computerSystem)
|
|
{
|
|
// If we are not using the new computer name, check the length of the target machine name
|
|
string hostName = (string)computerSystem["DNSHostName"];
|
|
if (newName == null && hostName.Length > ComputerWMIHelper.NetBIOSNameMaxLength)
|
|
{
|
|
string truncatedName = hostName.Substring(0, ComputerWMIHelper.NetBIOSNameMaxLength);
|
|
string query = StringUtil.Format(ComputerResources.TruncateNetBIOSName, truncatedName);
|
|
string caption = ComputerResources.TruncateNetBIOSNameCaption;
|
|
if (!Force && !ShouldContinue(query, caption))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (newName != null && hostName.Equals(newName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
WriteErrorHelper(
|
|
ComputerResources.NewNameIsOldName,
|
|
"NewNameIsOldName",
|
|
newName,
|
|
ErrorCategory.InvalidArgument,
|
|
false,
|
|
computerName, newName);
|
|
continue;
|
|
}
|
|
|
|
if (ParameterSetName == DomainParameterSet)
|
|
{
|
|
if ((bool)computerSystem["PartOfDomain"])
|
|
{
|
|
string curDomainName = (string)LanguagePrimitives.ConvertTo(computerSystem["Domain"], typeof(string), CultureInfo.InvariantCulture);
|
|
string shortDomainName = "";
|
|
if (curDomainName.Contains("."))
|
|
{
|
|
int dotIndex = curDomainName.IndexOf(".", StringComparison.OrdinalIgnoreCase);
|
|
shortDomainName = curDomainName.Substring(0, dotIndex);
|
|
}
|
|
|
|
// If the target computer is already in the specified domain, throw an error
|
|
if (curDomainName.Equals(_domainName, StringComparison.OrdinalIgnoreCase) || shortDomainName.Equals(_domainName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
WriteErrorHelper(ComputerResources.AddComputerToSameDomain,
|
|
"AddComputerToSameDomain", computerName,
|
|
ErrorCategory.InvalidOperation, false, computerName, _domainName);
|
|
continue;
|
|
}
|
|
|
|
// Switch between domains
|
|
// If the UnjoinDomainCredential is not specified, we assume the DomainCredential can be used for both removing
|
|
// the computer from its current domain, and adding the computer to the new domain. This behavior is supported on
|
|
// Win7, we don't want to break it.
|
|
PSCredential credTobeUsed = UnjoinDomainCredential ?? Credential;
|
|
string unjoinDomainUserName = credTobeUsed != null ? credTobeUsed.UserName : null;
|
|
string unjoinDomainPassword = credTobeUsed != null ? Utils.GetStringFromSecureString(credTobeUsed.Password) : null;
|
|
|
|
// Leave the current domain
|
|
returnCode = UnjoinDomain(computerSystem, computerName, curDomainName, unjoinDomainUserName, unjoinDomainPassword);
|
|
if (returnCode == 0)
|
|
{
|
|
// Successfully unjoin the old domain, join the computer to the new domain
|
|
returnCode = JoinDomain(computerSystem, computerName, curDomainName, null);
|
|
|
|
if (returnCode == 0 && newName != null)
|
|
{
|
|
// Rename the computer in the new domain
|
|
returnCode = RenameComputer(computerSystem, computerName, newName);
|
|
}
|
|
}
|
|
|
|
success = returnCode == 0;
|
|
}
|
|
else
|
|
{
|
|
// Add a workgroup computer to domain
|
|
string curWorkgroupName = (string)LanguagePrimitives.ConvertTo(computerSystem["Domain"], typeof(string), CultureInfo.InvariantCulture);
|
|
returnCode = JoinDomain(computerSystem, computerName, null, curWorkgroupName);
|
|
|
|
if (returnCode == 0 && newName != null)
|
|
{
|
|
returnCode = RenameComputer(computerSystem, computerName, newName);
|
|
}
|
|
|
|
success = returnCode == 0;
|
|
}
|
|
}
|
|
else // WorkgroupParameterSet
|
|
{
|
|
if ((bool)computerSystem["PartOfDomain"])
|
|
{
|
|
// Remind the user to have local admin credential only if the computer is domain joined
|
|
string shouldContinueMsg = ComputerResources.RemoveComputerConfirm;
|
|
if (!Force && !ShouldContinue(shouldContinueMsg, null /* null = default caption */ ))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Leave the current domain
|
|
string curDomainName = (string)LanguagePrimitives.ConvertTo(computerSystem["Domain"], typeof(string), CultureInfo.InvariantCulture);
|
|
string dUserName = Credential != null ? Credential.UserName : null;
|
|
string dPassword = Credential != null ? Utils.GetStringFromSecureString(Credential.Password) : null;
|
|
|
|
returnCode = UnjoinDomain(computerSystem, computerName, curDomainName, dUserName, dPassword);
|
|
if (returnCode == 0)
|
|
{
|
|
// Join the specified workgroup
|
|
returnCode = JoinWorkgroup(computerSystem, computerName, curDomainName);
|
|
if (returnCode == 0 && newName != null)
|
|
{
|
|
// Rename the computer
|
|
returnCode = RenameComputer(computerSystem, computerName, newName);
|
|
}
|
|
}
|
|
|
|
success = returnCode == 0;
|
|
}
|
|
else // in workgroup
|
|
{
|
|
string curWorkgroup = (string)LanguagePrimitives.ConvertTo(computerSystem["Domain"], typeof(string), CultureInfo.InvariantCulture);
|
|
if (curWorkgroup.Equals(_workgroupName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
WriteErrorHelper(ComputerResources.AddComputerToSameWorkgroup,
|
|
"AddComputerToSameWorkgroup", computerName,
|
|
ErrorCategory.InvalidOperation, false, computerName, _workgroupName);
|
|
continue;
|
|
}
|
|
|
|
// Join to another workgroup
|
|
returnCode = JoinWorkgroup(computerSystem, computerName, null);
|
|
if (returnCode == 0 && newName != null)
|
|
{
|
|
returnCode = RenameComputer(computerSystem, computerName, newName);
|
|
}
|
|
|
|
success = returnCode == 0;
|
|
}
|
|
}// end of else -- WorkgroupParameterSet
|
|
|
|
if (_passThru)
|
|
{
|
|
WriteObject(ComputerWMIHelper.GetComputerStatusObject(returnCode, computerName));
|
|
}
|
|
}
|
|
}// end of foreach
|
|
|
|
// If successful and the Restart parameter is specified, restart the computer
|
|
if (success && _restart)
|
|
{
|
|
object[] flags = new object[] { 6, 0 };
|
|
RestartComputerCommand.RestartOneComputerUsingDcom(this, isLocalhost, computerName, flags, options);
|
|
}
|
|
|
|
// If successful and the Restart parameter is not specified, write out warning
|
|
if (success && !_restart)
|
|
{
|
|
WriteWarning(StringUtil.Format(ComputerResources.RestartNeeded, null, computerName));
|
|
}
|
|
}
|
|
} // end of try
|
|
catch (ManagementException ex)
|
|
{
|
|
WriteErrorHelper(ComputerResources.FailToConnectToComputer, "AddComputerException", computerName,
|
|
ErrorCategory.OperationStopped, false, computerName, ex.Message);
|
|
}
|
|
catch (COMException ex)
|
|
{
|
|
WriteErrorHelper(ComputerResources.FailToConnectToComputer, "AddComputerException", computerName,
|
|
ErrorCategory.OperationStopped, false, computerName, ex.Message);
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
WriteErrorHelper(ComputerResources.FailToConnectToComputer, "AddComputerException", computerName,
|
|
ErrorCategory.OperationStopped, false, computerName, ex.Message);
|
|
}
|
|
}
|
|
|
|
private string ValidateComputerName(string computer, bool validateNewName)
|
|
{
|
|
ErrorRecord error = null;
|
|
string targetComputer = ComputerWMIHelper.ValidateComputerName(computer, _shortLocalMachineName, _fullLocalMachineName, ref error);
|
|
if (targetComputer == null)
|
|
{
|
|
if (error != null)
|
|
{
|
|
WriteError(error);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
bool isLocalhost = targetComputer.Equals(ComputerWMIHelper.localhostStr, StringComparison.OrdinalIgnoreCase);
|
|
|
|
if (validateNewName && _newName != null)
|
|
{
|
|
if (!ComputerWMIHelper.IsComputerNameValid(_newName))
|
|
{
|
|
WriteErrorHelper(
|
|
ComputerResources.InvalidNewName,
|
|
"InvalidNewName",
|
|
_newName,
|
|
ErrorCategory.InvalidArgument,
|
|
false,
|
|
isLocalhost ? _shortLocalMachineName : targetComputer, _newName);
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return targetComputer;
|
|
}
|
|
|
|
#endregion private
|
|
|
|
#region override
|
|
|
|
/// <summary>
|
|
/// BeginProcessing method
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
if (ParameterSetName == DomainParameterSet)
|
|
{
|
|
if ((_joinOptions & JoinOptions.PasswordPass) != 0 && (_joinOptions & JoinOptions.UnsecuredJoin) == 0)
|
|
{
|
|
WriteErrorHelper(ComputerResources.InvalidJoinOptions, "InvalidJoinOptions", _joinOptions,
|
|
ErrorCategory.InvalidArgument, true, JoinOptions.PasswordPass.ToString(),
|
|
JoinOptions.UnsecuredJoin.ToString());
|
|
}
|
|
|
|
if ((_joinOptions & JoinOptions.AccountCreate) != 0)
|
|
{
|
|
_joinDomainflags |= (int) JoinOptions.AccountCreate;
|
|
}
|
|
if ((_joinOptions & JoinOptions.Win9XUpgrade) != 0)
|
|
{
|
|
_joinDomainflags |= (int) JoinOptions.Win9XUpgrade;
|
|
}
|
|
if ((_joinOptions & JoinOptions.UnsecuredJoin) != 0)
|
|
{
|
|
_joinDomainflags |= (int) JoinOptions.UnsecuredJoin;
|
|
}
|
|
if ((_joinOptions & JoinOptions.PasswordPass) != 0)
|
|
{
|
|
_joinDomainflags |= (int) JoinOptions.PasswordPass;
|
|
}
|
|
if ((_joinOptions & JoinOptions.DeferSPNSet) != 0)
|
|
{
|
|
_joinDomainflags |= (int)JoinOptions.DeferSPNSet;
|
|
}
|
|
if ((_joinOptions & JoinOptions.JoinWithNewName) != 0)
|
|
{
|
|
_joinDomainflags |= (int) JoinOptions.JoinWithNewName;
|
|
}
|
|
if ((_joinOptions & JoinOptions.JoinReadOnly) != 0)
|
|
{
|
|
_joinDomainflags |= (int)JoinOptions.JoinReadOnly;
|
|
}
|
|
if ((_joinOptions & JoinOptions.InstallInvoke) != 0)
|
|
{
|
|
_joinDomainflags |= (int) JoinOptions.InstallInvoke;
|
|
}
|
|
|
|
if (_unsecure)
|
|
{
|
|
_joinDomainflags |= (int)(JoinOptions.UnsecuredJoin | JoinOptions.PasswordPass);
|
|
}
|
|
|
|
if (_server != null)
|
|
{
|
|
// It's the name of a domain controller. We need to check if the specified domain controller actually exists
|
|
try
|
|
{
|
|
Dns.GetHostEntry(_server);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
CommandsCommon.CheckForSevereException(this, ex);
|
|
WriteErrorHelper(ComputerResources.CannotResolveServerName, "AddressResolutionException",
|
|
_server, ErrorCategory.InvalidArgument, true, _server);
|
|
}
|
|
_domainName = _domainName + "\\" + _server;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// ProcessRecord method
|
|
/// </summary>
|
|
protected override void ProcessRecord()
|
|
{
|
|
if (_newName != null && _computerName.Length != 1)
|
|
{
|
|
WriteErrorHelper(ComputerResources.CannotRenameMultipleComputers, "CannotRenameMultipleComputers",
|
|
_newName, ErrorCategory.InvalidArgument, false);
|
|
return;
|
|
}
|
|
|
|
// If LocalCred is not provided, we use the current caller's context
|
|
ConnectionOptions options = new ConnectionOptions
|
|
{
|
|
Authentication = AuthenticationLevel.PacketPrivacy,
|
|
Impersonation = ImpersonationLevel.Impersonate,
|
|
EnablePrivileges = true
|
|
};
|
|
|
|
EnumerationOptions enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true };
|
|
ObjectQuery computerSystemQuery = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem);
|
|
|
|
int oldJoinDomainFlags = _joinDomainflags;
|
|
if (_newName != null && ParameterSetName == DomainParameterSet)
|
|
{
|
|
// We rename the computer after it's joined to the target domain, so writing SPN and DNSHostName attributes
|
|
// on the computer object should be deferred until the rename operation that follows the join operation
|
|
_joinDomainflags |= (int)JoinOptions.DeferSPNSet;
|
|
}
|
|
|
|
try
|
|
{
|
|
foreach (string computer in _computerName)
|
|
{
|
|
string targetComputer = ValidateComputerName(computer, _newName != null);
|
|
if (targetComputer == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool isLocalhost = targetComputer.Equals("localhost", StringComparison.OrdinalIgnoreCase);
|
|
if (isLocalhost)
|
|
{
|
|
if (!_containsLocalHost)
|
|
_containsLocalHost = true;
|
|
_newNameForLocalHost = _newName;
|
|
|
|
continue;
|
|
}
|
|
|
|
DoAddComputerAction(targetComputer, _newName, false, options, enumOptions, computerSystemQuery);
|
|
}// end of foreach
|
|
}
|
|
finally
|
|
{
|
|
// Reverting the domainflags to previous status if DeferSPNSet is added to the domain flags.
|
|
if (_newName != null && ParameterSetName == DomainParameterSet)
|
|
{
|
|
_joinDomainflags = oldJoinDomainFlags;
|
|
}
|
|
}
|
|
|
|
}// end of ProcessRecord
|
|
|
|
/// <summary>
|
|
/// EndProcessing method
|
|
/// </summary>
|
|
protected override void EndProcessing()
|
|
{
|
|
if (!_containsLocalHost) return;
|
|
|
|
// If LocalCred is not provided, we use the current caller's context
|
|
ConnectionOptions options = new ConnectionOptions
|
|
{
|
|
Authentication = AuthenticationLevel.PacketPrivacy,
|
|
Impersonation = ImpersonationLevel.Impersonate,
|
|
EnablePrivileges = true
|
|
};
|
|
|
|
EnumerationOptions enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true };
|
|
ObjectQuery computerSystemQuery = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem);
|
|
|
|
DoAddComputerAction("localhost", _newNameForLocalHost, true, options, enumOptions, computerSystemQuery);
|
|
}
|
|
|
|
#endregion override
|
|
|
|
}//End Class
|
|
|
|
#endregion Add-Computer
|
|
|
|
#region RemoveComputer
|
|
|
|
/// <summary>
|
|
/// Removes the Specified Computer(s) from the relevant Domain or Work Group
|
|
/// </summary>
|
|
|
|
[Cmdlet(VerbsCommon.Remove, "Computer", SupportsShouldProcess = true, DefaultParameterSetName = LocalParameterSet,
|
|
HelpUri = "http://go.microsoft.com/fwlink/?LinkID=135246", RemotingCapability = RemotingCapability.SupportedByCommand)]
|
|
[OutputType(typeof(ComputerChangeInfo))]
|
|
public class RemoveComputerCommand : PSCmdlet
|
|
{
|
|
#region "Parameters and Private Data"
|
|
|
|
private const string LocalParameterSet = "Local";
|
|
private const string RemoteParameterSet = "Remote";
|
|
|
|
/// <summary>
|
|
/// The domain credential is used for authenticating to the domain controller.
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = RemoteParameterSet, Mandatory = true)]
|
|
[Parameter(Position = 0, ParameterSetName = LocalParameterSet)]
|
|
[Alias("Credential")]
|
|
[ValidateNotNullOrEmpty]
|
|
[Credential]
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
|
|
public PSCredential UnjoinDomainCredential
|
|
{
|
|
get { return _unjoinDomainCredential; }
|
|
set { _unjoinDomainCredential = value; }
|
|
}
|
|
private PSCredential _unjoinDomainCredential;
|
|
|
|
/// <summary>
|
|
/// The local admin credential for authenticating to the target computer
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = RemoteParameterSet)]
|
|
[ValidateNotNullOrEmpty]
|
|
[Credential]
|
|
public PSCredential LocalCredential
|
|
{
|
|
get { return _localCredential; }
|
|
set { _localCredential = value; }
|
|
}
|
|
private PSCredential _localCredential;
|
|
|
|
/// <summary>
|
|
/// Restart parameter
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter Restart
|
|
{
|
|
get { return _restart; }
|
|
set { _restart = value; }
|
|
}
|
|
private SwitchParameter _restart = false;
|
|
|
|
/// <summary>
|
|
/// The target computer names to remove from the domain
|
|
/// </summary>
|
|
[Parameter(ParameterSetName = RemoteParameterSet, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
|
|
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
|
|
public string[] ComputerName
|
|
{
|
|
get { return _computerName; }
|
|
set { _computerName = value; }
|
|
}
|
|
private string[] _computerName = { "localhost" };
|
|
|
|
/// <summary>
|
|
/// Force parameter (to suppress the shouldprocess and shouldcontinue)
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter Force
|
|
{
|
|
get { return _force; }
|
|
set { _force = value; }
|
|
}
|
|
private bool _force;
|
|
|
|
/// <summary>
|
|
/// Only emit if passthru is specified. One bool/string pair for each
|
|
/// computer that was joined. Bool = success/failure. String = ComputerName.
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter PassThru
|
|
{
|
|
get { return _passThru; }
|
|
set { _passThru = value; }
|
|
}
|
|
private SwitchParameter _passThru;
|
|
|
|
/// <summary>
|
|
/// Specify the workgroup name to join in if the target machine is removed from
|
|
/// the domain
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateNotNullOrEmpty]
|
|
public string WorkgroupName
|
|
{
|
|
get { return _workGroup; }
|
|
set { _workGroup = value; }
|
|
}
|
|
private string _workGroup = "WORKGROUP";
|
|
|
|
private bool _containsLocalHost = false;
|
|
private readonly string _shortLocalMachineName = Dns.GetHostName();
|
|
private readonly string _fullLocalMachineName = Dns.GetHostEntry("").HostName;
|
|
|
|
#endregion "Parameters and Private Data"
|
|
|
|
#region "Private Methods"
|
|
|
|
private void DoRemoveComputerAction(string computer, bool isLocalhost, ConnectionOptions options, EnumerationOptions enumOptions, ObjectQuery computerSystemQuery)
|
|
{
|
|
bool successful = false;
|
|
string computerName = isLocalhost ? _shortLocalMachineName : computer;
|
|
|
|
if (!ShouldProcess(computerName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If LocalCred is given, use the local credential for WMI connection
|
|
if (LocalCredential != null)
|
|
{
|
|
options.SecurePassword = LocalCredential.Password;
|
|
options.Username = ComputerWMIHelper.GetLocalAdminUserName(computerName, LocalCredential);
|
|
}
|
|
|
|
// The local machine will always be processed in the very end. If the
|
|
// current target computer is the local machine, it's the last one to
|
|
// be processed, so we can safely set the Username and Password to be
|
|
// null. We cannot use a user credential when connecting to the local
|
|
// machine.
|
|
if (isLocalhost)
|
|
{
|
|
options.Username = null;
|
|
options.SecurePassword = null;
|
|
}
|
|
|
|
ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options);
|
|
try
|
|
{
|
|
using (var searcher = new ManagementObjectSearcher(scope, computerSystemQuery, enumOptions))
|
|
{
|
|
foreach (ManagementObject computerSystem in searcher.Get())
|
|
{
|
|
using (computerSystem)
|
|
{
|
|
if (!(bool)computerSystem["PartOfDomain"])
|
|
{
|
|
// Not in a domain, throw out non-terminating error
|
|
string errMsg = StringUtil.Format(ComputerResources.ComputerNotInDomain, computerName);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "ComputerNotInDomain", ErrorCategory.InvalidOperation, computerName);
|
|
WriteError(error);
|
|
continue;
|
|
}
|
|
|
|
// Remind the user to have local admin credential only if the computer is domain joined
|
|
string shouldContinueMsg = ComputerResources.RemoveComputerConfirm;
|
|
if (!Force && !ShouldContinue(shouldContinueMsg, null /* null = default caption */ ))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int returnCode = 0;
|
|
string curDomainName = (string)LanguagePrimitives.ConvertTo(computerSystem["Domain"], typeof(string), CultureInfo.InvariantCulture);
|
|
string dUserName = UnjoinDomainCredential != null ? UnjoinDomainCredential.UserName : null;
|
|
string dPassword = UnjoinDomainCredential != null ? Utils.GetStringFromSecureString(UnjoinDomainCredential.Password) : null;
|
|
|
|
ManagementBaseObject unjoinParameter = computerSystem.GetMethodParameters("UnjoinDomainOrWorkgroup");
|
|
unjoinParameter.SetPropertyValue("UserName", dUserName);
|
|
unjoinParameter.SetPropertyValue("Password", dPassword);
|
|
unjoinParameter.SetPropertyValue("FUnjoinOptions", 4); // default option, disable the active directory account
|
|
|
|
ManagementBaseObject result = computerSystem.InvokeMethod("UnjoinDomainOrWorkgroup", unjoinParameter, null);
|
|
Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Unjoin method is invoked");
|
|
returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture);
|
|
|
|
// Error code 1355 - The specified domain either does not exist or could not be contacted.
|
|
// This might happen when the old domain is gone or unreachable.
|
|
// Error code 53 - The network path was not found.
|
|
// This might happen when the network is not available.
|
|
if ((returnCode == 1355 || returnCode == 53) && Force)
|
|
{
|
|
// When -Force is specified, we unjoin the domain without disable the AD account
|
|
unjoinParameter.SetPropertyValue("FUnjoinOptions", 0);
|
|
result = computerSystem.InvokeMethod("UnjoinDomainOrWorkgroup", unjoinParameter, null);
|
|
Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Unjoin method is invoked");
|
|
returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture);
|
|
}
|
|
|
|
if (returnCode != 0)
|
|
{
|
|
var ex = new Win32Exception(returnCode);
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToUnjoinDomain, computerName, curDomainName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToUnjoinDomain", ErrorCategory.OperationStopped, computerName);
|
|
WriteError(error);
|
|
}
|
|
else
|
|
{
|
|
// Join into the specified workgroup if it's given
|
|
successful = true;
|
|
if (_workGroup != null)
|
|
{
|
|
ManagementBaseObject joinParameter = computerSystem.GetMethodParameters("JoinDomainOrWorkgroup");
|
|
joinParameter.SetPropertyValue("Name", _workGroup);
|
|
joinParameter.SetPropertyValue("Password", null);
|
|
joinParameter.SetPropertyValue("UserName", null);
|
|
joinParameter.SetPropertyValue("FJoinOptions", 0); // Join in a workgroup
|
|
|
|
result = computerSystem.InvokeMethod("JoinDomainOrWorkgroup", joinParameter, null);
|
|
Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Join method is invoked");
|
|
returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture);
|
|
if (returnCode != 0)
|
|
{
|
|
var ex = new Win32Exception(returnCode);
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToSwitchFromDomainToWorkgroup, computerName, curDomainName, _workGroup, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToJoinWorkGroup", ErrorCategory.OperationStopped, computerName);
|
|
WriteError(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_passThru)
|
|
{
|
|
WriteObject(ComputerWMIHelper.GetComputerStatusObject(returnCode, computerName));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If successful and the Restart parameter is specified, restart the computer
|
|
if (successful && _restart)
|
|
{
|
|
object[] flags = new object[] { 6, 0 };
|
|
RestartComputerCommand.RestartOneComputerUsingDcom(this, isLocalhost, computerName, flags, options);
|
|
}
|
|
|
|
// If successful and the Restart parameter is not specified, write out warning
|
|
if (successful && !_restart)
|
|
{
|
|
WriteWarning(StringUtil.Format(ComputerResources.RestartNeeded, null, computerName));
|
|
}
|
|
}
|
|
catch (ManagementException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RemoveComputerException", ErrorCategory.OperationStopped, computerName);
|
|
WriteError(error);
|
|
}
|
|
catch (COMException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RemoveComputerException", ErrorCategory.OperationStopped, computerName);
|
|
WriteError(error);
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RemoveComputerException", ErrorCategory.OperationStopped, computerName);
|
|
WriteError(error);
|
|
}
|
|
}
|
|
|
|
private string ValidateComputerName(string computer)
|
|
{
|
|
ErrorRecord error = null;
|
|
string targetComputer = ComputerWMIHelper.ValidateComputerName(computer, _shortLocalMachineName, _fullLocalMachineName, ref error);
|
|
if (targetComputer == null)
|
|
{
|
|
if (error != null)
|
|
{
|
|
WriteError(error);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
return targetComputer;
|
|
}
|
|
|
|
#endregion "Private Methods"
|
|
|
|
#region "Override Methods"
|
|
|
|
/// <summary>
|
|
/// ProcessRecord method.
|
|
/// </summary>
|
|
protected override void ProcessRecord()
|
|
{
|
|
// If both LocalCred and DomainCred are not provided, we use the default options
|
|
ConnectionOptions options = new ConnectionOptions
|
|
{
|
|
Authentication = AuthenticationLevel.PacketPrivacy,
|
|
Impersonation = ImpersonationLevel.Impersonate,
|
|
EnablePrivileges = true
|
|
};
|
|
|
|
// For Remove-Computer, usually the domain credential is also the local admin credential
|
|
// for the target computer. So use the local credential if given, otherwise use the domain
|
|
// credential to connect to the target machine.
|
|
// If the LocalCred is not given but the DomainCred is available, use the DomainCred for WMI connection.
|
|
if (LocalCredential == null && UnjoinDomainCredential != null)
|
|
{
|
|
options.SecurePassword = UnjoinDomainCredential.Password;
|
|
options.Username = UnjoinDomainCredential.UserName;
|
|
}
|
|
|
|
EnumerationOptions enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true };
|
|
ObjectQuery computerSystemQuery = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem);
|
|
|
|
foreach (string computer in _computerName)
|
|
{
|
|
string targetComputer = ValidateComputerName(computer);
|
|
if (targetComputer == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool isLocalhost = targetComputer.Equals("localhost", StringComparison.OrdinalIgnoreCase);
|
|
if (isLocalhost)
|
|
{
|
|
if (!_containsLocalHost)
|
|
_containsLocalHost = true;
|
|
|
|
continue;
|
|
}
|
|
|
|
DoRemoveComputerAction(computer, false, options, enumOptions, computerSystemQuery);
|
|
}
|
|
}//End ProcessRecord()
|
|
|
|
/// <summary>
|
|
/// EndProcessing method: deal with the local computer in the end
|
|
/// </summary>
|
|
protected override void EndProcessing()
|
|
{
|
|
if (!_containsLocalHost) return;
|
|
|
|
// If both LocalCred and DomainCred are not provided, we use the default options
|
|
ConnectionOptions options = new ConnectionOptions
|
|
{
|
|
Authentication = AuthenticationLevel.PacketPrivacy,
|
|
Impersonation = ImpersonationLevel.Impersonate,
|
|
EnablePrivileges = true
|
|
};
|
|
|
|
// For Remove-Computer, usually the domain credential is also the local admin credential
|
|
// for the target computer. So use the local credential if given, otherwise use the domain
|
|
// credential to connect to the target machine.
|
|
// If the LocalCred is not given but the DomainCred is available, use the DomainCred for WMI connection.
|
|
if (LocalCredential == null && UnjoinDomainCredential != null)
|
|
{
|
|
options.SecurePassword = UnjoinDomainCredential.Password;
|
|
options.Username = UnjoinDomainCredential.UserName;
|
|
}
|
|
|
|
EnumerationOptions enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true };
|
|
ObjectQuery computerSystemQuery = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem);
|
|
|
|
DoRemoveComputerAction("localhost", true, options, enumOptions, computerSystemQuery);
|
|
}
|
|
|
|
#endregion "Override Methods"
|
|
|
|
}//End Class
|
|
|
|
#endregion Remove-Computer
|
|
|
|
#endif
|
|
|
|
#region Rename-Computer
|
|
|
|
/// <summary>
|
|
/// Renames a domain computer and its corresponding domain account or a
|
|
/// workgroup computer. Use this command to rename domain workstations and local
|
|
/// machines only. It cannot be used to rename Domain Controllers.
|
|
/// </summary>
|
|
|
|
[Cmdlet(VerbsCommon.Rename, "Computer", SupportsShouldProcess = true,
|
|
HelpUri = "http://go.microsoft.com/fwlink/?LinkID=219990", RemotingCapability = RemotingCapability.SupportedByCommand)]
|
|
public class RenameComputerCommand : PSCmdlet
|
|
{
|
|
#region Private Members
|
|
|
|
private bool _containsLocalHost = false;
|
|
private string _newNameForLocalHost = null;
|
|
|
|
private TransportProtocol _transportProtocol = TransportProtocol.DCOM;
|
|
|
|
private readonly string _shortLocalMachineName = Dns.GetHostName();
|
|
private readonly string _fullLocalMachineName = Dns.GetHostEntryAsync("").Result.HostName;
|
|
|
|
#endregion
|
|
|
|
#region Parameters
|
|
|
|
/// <summary>
|
|
/// Target computers to rename
|
|
/// </summary>
|
|
[Parameter(ValueFromPipelineByPropertyName = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
public string ComputerName
|
|
{
|
|
get { return _computerName; }
|
|
set { _computerName = value; }
|
|
}
|
|
private string _computerName = "localhost";
|
|
|
|
/// <summary>
|
|
/// Emit the output.
|
|
/// </summary>
|
|
//[Alias("Restart")]
|
|
[Parameter]
|
|
public SwitchParameter PassThru
|
|
{
|
|
get { return _passThru; }
|
|
set
|
|
{
|
|
_passThru = value;
|
|
}
|
|
}
|
|
private SwitchParameter _passThru;
|
|
|
|
/// <summary>
|
|
/// The domain credential of the domain the target computer joined
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateNotNullOrEmpty]
|
|
[Credential]
|
|
public PSCredential DomainCredential
|
|
{
|
|
get { return _domainCredential; }
|
|
set
|
|
{
|
|
_domainCredential = value;
|
|
}
|
|
}
|
|
private PSCredential _domainCredential;
|
|
|
|
/// <summary>
|
|
/// The administrator credential of the target computer
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateNotNullOrEmpty]
|
|
[Credential]
|
|
public PSCredential LocalCredential
|
|
{
|
|
get { return _localCredential; }
|
|
set { _localCredential = value; }
|
|
}
|
|
private PSCredential _localCredential;
|
|
|
|
/// <summary>
|
|
/// New names for the target computers
|
|
/// </summary>
|
|
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true)]
|
|
[ValidateNotNullOrEmpty]
|
|
public string NewName
|
|
{
|
|
get { return _newComputerName; }
|
|
set
|
|
{
|
|
_newComputerName = value;
|
|
}
|
|
}
|
|
private string _newComputerName;
|
|
|
|
/// <summary>
|
|
/// Suppress the ShouldContinue
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter Force
|
|
{
|
|
get { return _force; }
|
|
set { _force = value; }
|
|
}
|
|
private bool _force;
|
|
|
|
/// <summary>
|
|
/// To restart the target computer after rename it
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter Restart
|
|
{
|
|
get { return _restart; }
|
|
set { _restart = value; }
|
|
}
|
|
private bool _restart;
|
|
|
|
/// <summary>
|
|
/// The authentication options for CIM_WSMan connection
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateSet(
|
|
"Default",
|
|
"Basic",
|
|
"Negotiate", // can be used with and without credential (without -> PSRP mapped to NegotiateWithImplicitCredential)
|
|
"CredSSP",
|
|
"Digest",
|
|
"Kerberos")] // can be used with and without credential (not sure about implications)
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
|
|
public string WsmanAuthentication
|
|
{
|
|
get { return _wsmanAuthentication; }
|
|
set { _wsmanAuthentication = value; }
|
|
}
|
|
private string _wsmanAuthentication = "Default";
|
|
|
|
/// <summary>
|
|
/// Specify the protocol to use
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateSet(ComputerWMIHelper.DcomProtocol, ComputerWMIHelper.WsmanProtocol)]
|
|
public string Protocol
|
|
{
|
|
get { return _protocol; }
|
|
set
|
|
{
|
|
_protocol = value;
|
|
}
|
|
}
|
|
|
|
//CoreClr does not support DCOM protocol
|
|
// This change makes sure that the the command works seamlessly if user did not explicitly entered the protocol
|
|
#if CORECLR
|
|
private string _protocol = ComputerWMIHelper.WsmanProtocol;
|
|
#else
|
|
private string _protocol = ComputerWMIHelper.DcomProtocol;
|
|
#endif
|
|
|
|
#endregion
|
|
|
|
#region "Private Methods"
|
|
|
|
/// <summary>
|
|
/// Check to see if the target computer is the local machine
|
|
/// </summary>
|
|
private string ValidateComputerName()
|
|
{
|
|
// Validate target name.
|
|
ErrorRecord targetError = null;
|
|
string targetComputer = ComputerWMIHelper.ValidateComputerName(_computerName, _shortLocalMachineName, _fullLocalMachineName, ref targetError);
|
|
if (targetComputer == null)
|
|
{
|
|
if (targetError != null)
|
|
{
|
|
WriteError(targetError);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Validate *new* name. Validate the format of the new name. Check if the old name is the same as the
|
|
// new name later.
|
|
if (!ComputerWMIHelper.IsComputerNameValid(_newComputerName))
|
|
{
|
|
bool isLocalhost = targetComputer.Equals(ComputerWMIHelper.localhostStr, StringComparison.OrdinalIgnoreCase);
|
|
string errMsg = StringUtil.Format(ComputerResources.InvalidNewName, isLocalhost ? _shortLocalMachineName : targetComputer, _newComputerName);
|
|
ErrorRecord error = new ErrorRecord(
|
|
new InvalidOperationException(errMsg), "InvalidNewName",
|
|
ErrorCategory.InvalidArgument, _newComputerName);
|
|
WriteError(error);
|
|
return null;
|
|
}
|
|
|
|
return targetComputer;
|
|
}
|
|
|
|
private void DoRenameComputerAction(string computer, string newName, bool isLocalhost)
|
|
{
|
|
string computerName = isLocalhost ? _shortLocalMachineName : computer;
|
|
|
|
if (!ShouldProcess(computerName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Check the length of the new name
|
|
if (newName != null && newName.Length > ComputerWMIHelper.NetBIOSNameMaxLength)
|
|
{
|
|
string truncatedName = newName.Substring(0, ComputerWMIHelper.NetBIOSNameMaxLength);
|
|
string query = StringUtil.Format(ComputerResources.TruncateNetBIOSName, truncatedName);
|
|
string caption = ComputerResources.TruncateNetBIOSNameCaption;
|
|
if (!Force && !ShouldContinue(query, caption))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (_transportProtocol)
|
|
{
|
|
case TransportProtocol.WSMan:
|
|
DoRenameComputerWsman(computer, computerName, newName, isLocalhost);
|
|
break;
|
|
#if !CORECLR
|
|
case TransportProtocol.DCOM:
|
|
DoRenameComputerDcom(computer, computerName, newName, isLocalhost);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
private void DoRenameComputerWsman(string computer, string computerName, string newName, bool isLocalhost)
|
|
{
|
|
bool successful = false;
|
|
PSCredential credToUse = isLocalhost ? null : (LocalCredential ?? DomainCredential);
|
|
|
|
try
|
|
{
|
|
using (CancellationTokenSource cancelTokenSource = new CancellationTokenSource())
|
|
using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(computer, credToUse, _wsmanAuthentication, cancelTokenSource.Token, this))
|
|
{
|
|
var operationOptions = new CimOperationOptions
|
|
{
|
|
Timeout = TimeSpan.FromMilliseconds(10000),
|
|
CancellationToken = cancelTokenSource.Token,
|
|
//This prefix works against all versions of the WinRM server stack, both win8 and win7
|
|
ResourceUriPrefix = new Uri(ComputerWMIHelper.CimUriPrefix)
|
|
};
|
|
|
|
IEnumerable<CimInstance> mCollection = cimSession.QueryInstances(
|
|
ComputerWMIHelper.CimOperatingSystemNamespace,
|
|
ComputerWMIHelper.CimQueryDialect,
|
|
"Select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem,
|
|
operationOptions);
|
|
|
|
foreach (CimInstance cimInstance in mCollection)
|
|
{
|
|
var oldName = cimInstance.CimInstanceProperties["DNSHostName"].Value.ToString();
|
|
if (oldName.Equals(newName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.NewNameIsOldName, computerName, newName);
|
|
ErrorRecord error = new ErrorRecord(
|
|
new InvalidOperationException(errMsg), "NewNameIsOldName",
|
|
ErrorCategory.InvalidArgument, newName);
|
|
WriteError(error);
|
|
|
|
continue;
|
|
}
|
|
|
|
// If the target computer is in a domain, always use the DomainCred. If the DomainCred is not given,
|
|
// use null for UserName and Password, so the context of the caller will be used.
|
|
// If the target computer is not in a domain, just use null for the UserName and Password
|
|
string dUserName = null;
|
|
string dPassword = null;
|
|
if (((bool) cimInstance.CimInstanceProperties["PartOfDomain"].Value) && (DomainCredential != null))
|
|
{
|
|
dUserName = DomainCredential.UserName;
|
|
dPassword = Utils.GetStringFromSecureString(DomainCredential.Password);
|
|
}
|
|
|
|
var methodParameters = new CimMethodParametersCollection();
|
|
methodParameters.Add(CimMethodParameter.Create(
|
|
"Name",
|
|
newName,
|
|
Microsoft.Management.Infrastructure.CimType.String,
|
|
CimFlags.None));
|
|
|
|
methodParameters.Add(CimMethodParameter.Create(
|
|
"UserName",
|
|
dUserName,
|
|
Microsoft.Management.Infrastructure.CimType.String,
|
|
(dUserName == null) ? CimFlags.NullValue : CimFlags.None));
|
|
|
|
methodParameters.Add(
|
|
CimMethodParameter.Create(
|
|
"Password",
|
|
dPassword,
|
|
Microsoft.Management.Infrastructure.CimType.String,
|
|
(dPassword == null) ? CimFlags.NullValue : CimFlags.None));
|
|
|
|
CimMethodResult result = cimSession.InvokeMethod(
|
|
ComputerWMIHelper.CimOperatingSystemNamespace,
|
|
cimInstance,
|
|
"Rename",
|
|
methodParameters,
|
|
operationOptions);
|
|
|
|
int retVal = Convert.ToInt32(result.ReturnValue.Value, CultureInfo.CurrentCulture);
|
|
if (retVal != 0)
|
|
{
|
|
var ex = new Win32Exception(retVal);
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToRename, computerName, newName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToRenameComputer", ErrorCategory.OperationStopped, computerName);
|
|
WriteError(error);
|
|
}
|
|
else
|
|
{
|
|
successful = true;
|
|
}
|
|
|
|
if (_passThru)
|
|
{
|
|
WriteObject(ComputerWMIHelper.GetRenameComputerStatusObject(retVal, newName, computerName));
|
|
}
|
|
|
|
if (successful)
|
|
{
|
|
if (_restart)
|
|
{
|
|
// If successful and the Restart parameter is specified, restart the computer
|
|
object[] flags = new object[] { 6, 0 };
|
|
ComputerWMIHelper.InvokeWin32ShutdownUsingWsman(
|
|
this,
|
|
isLocalhost,
|
|
computerName,
|
|
flags,
|
|
credToUse,
|
|
_wsmanAuthentication,
|
|
ComputerResources.RestartcomputerFailed,
|
|
"RestartcomputerFailed",
|
|
cancelTokenSource.Token);
|
|
}
|
|
else
|
|
{
|
|
WriteWarning(StringUtil.Format(ComputerResources.RestartNeeded, null, computerName));
|
|
}
|
|
}
|
|
} // end foreach
|
|
} // end using
|
|
}
|
|
catch (CimException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RenameComputerException",
|
|
ErrorCategory.OperationStopped, computerName);
|
|
WriteError(error);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
CommandProcessorBase.CheckForSevereException(ex);
|
|
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RenameComputerException",
|
|
ErrorCategory.OperationStopped, computerName);
|
|
WriteError(error);
|
|
}
|
|
}
|
|
|
|
#if !CORECLR
|
|
private void DoRenameComputerDcom(string computer, string computerName, string newName, bool isLocalhost)
|
|
{
|
|
EnumerationOptions enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true };
|
|
ObjectQuery computerSystemQuery = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem);
|
|
|
|
// If both LocalCred and DomainCred are not provided, we use the default options
|
|
ConnectionOptions options = new ConnectionOptions
|
|
{
|
|
Authentication = AuthenticationLevel.PacketPrivacy,
|
|
Impersonation = ImpersonationLevel.Impersonate,
|
|
EnablePrivileges = true
|
|
};
|
|
|
|
if (isLocalhost)
|
|
{
|
|
options.Username = null;
|
|
options.SecurePassword = null;
|
|
}
|
|
// If the LocalCred is not given but the DomainCred is available, use the DomainCred for WMI connection.
|
|
// If LocalCred is given, use the local credential for WMI connection
|
|
else if (LocalCredential != null)
|
|
{
|
|
options.SecurePassword = LocalCredential.Password;
|
|
options.Username = ComputerWMIHelper.GetLocalAdminUserName(computerName, LocalCredential);
|
|
}
|
|
else if (DomainCredential != null)
|
|
{
|
|
options.SecurePassword = DomainCredential.Password;
|
|
options.Username = DomainCredential.UserName;
|
|
}
|
|
|
|
bool successful = false;
|
|
ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options);
|
|
try
|
|
{
|
|
using (var searcher = new ManagementObjectSearcher(scope, computerSystemQuery, enumOptions))
|
|
{
|
|
foreach (ManagementObject computerSystem in searcher.Get())
|
|
{
|
|
using (computerSystem)
|
|
{
|
|
string oldName = (string)computerSystem["DNSHostName"];
|
|
if (oldName.Equals(newName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.NewNameIsOldName, computerName, newName);
|
|
ErrorRecord error = new ErrorRecord(
|
|
new InvalidOperationException(errMsg), "NewNameIsOldName",
|
|
ErrorCategory.InvalidArgument, newName);
|
|
WriteError(error);
|
|
continue;
|
|
}
|
|
|
|
string dUserName = null;
|
|
string dPassword = null;
|
|
if ((bool)computerSystem["PartOfDomain"])
|
|
{
|
|
// If the target computer is in a domain, always use the DomainCred. If the DomainCred is not given,
|
|
// use null for UserName and Password, so the context of the caller will be used.
|
|
dUserName = DomainCredential != null ? DomainCredential.UserName : null;
|
|
dPassword = DomainCredential != null ? Utils.GetStringFromSecureString(DomainCredential.Password) : null;
|
|
}
|
|
// If the target computer is not in a domain, just use null for the UserName and Password
|
|
|
|
ManagementBaseObject renameParameter = computerSystem.GetMethodParameters("Rename");
|
|
renameParameter.SetPropertyValue("Name", newName);
|
|
renameParameter.SetPropertyValue("UserName", dUserName);
|
|
renameParameter.SetPropertyValue("Password", dPassword);
|
|
|
|
ManagementBaseObject result = computerSystem.InvokeMethod("Rename", renameParameter, null);
|
|
Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Rename method is invoked");
|
|
int retVal = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture);
|
|
if (retVal != 0)
|
|
{
|
|
var ex = new Win32Exception(retVal);
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToRename, computerName, newName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToRenameComputer", ErrorCategory.OperationStopped, computerName);
|
|
WriteError(error);
|
|
}
|
|
else
|
|
{
|
|
successful = true;
|
|
}
|
|
|
|
if (_passThru)
|
|
{
|
|
WriteObject(ComputerWMIHelper.GetRenameComputerStatusObject(retVal, newName, computerName));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If successful and the Restart parameter is specified, restart the computer
|
|
if (successful && _restart)
|
|
{
|
|
object[] flags = new object[] { 6, 0 };
|
|
RestartComputerCommand.RestartOneComputerUsingDcom(this, isLocalhost, computerName, flags, options);
|
|
}
|
|
|
|
// If successful and the Restart parameter is not specified, write out warning
|
|
if (successful && !_restart)
|
|
{
|
|
WriteWarning(StringUtil.Format(ComputerResources.RestartNeeded, null, computerName));
|
|
}
|
|
}
|
|
catch (ManagementException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RenameComputerException",
|
|
ErrorCategory.OperationStopped, computerName);
|
|
WriteError(error);
|
|
}
|
|
catch (COMException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RenameComputerException",
|
|
ErrorCategory.OperationStopped, computerName);
|
|
WriteError(error);
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RenameComputerException",
|
|
ErrorCategory.OperationStopped, computerName);
|
|
WriteError(error);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#endregion "Private Methods"
|
|
|
|
#region "Override Methods"
|
|
|
|
/// <summary>
|
|
/// Begin Processing
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
base.BeginProcessing();
|
|
|
|
bool haveWsmanAuthenticationParam = this.MyInvocation.BoundParameters.ContainsKey("WsmanAuthentication");
|
|
bool haveProtocolParam = this.MyInvocation.BoundParameters.ContainsKey("Protocol");
|
|
_transportProtocol = (this.Protocol.Equals(ComputerWMIHelper.WsmanProtocol, StringComparison.OrdinalIgnoreCase) || (haveWsmanAuthenticationParam && !haveProtocolParam)) ?
|
|
TransportProtocol.WSMan : TransportProtocol.DCOM;
|
|
|
|
// Verify parameter set
|
|
if ((_transportProtocol == TransportProtocol.DCOM) && haveWsmanAuthenticationParam)
|
|
{
|
|
ThrowTerminatingError(
|
|
new ErrorRecord(
|
|
new PSArgumentException(ComputerResources.RenameCommandWsmanAuthParamConflict),
|
|
"InvalidParameter",
|
|
ErrorCategory.InvalidArgument,
|
|
this));
|
|
}
|
|
|
|
#if CORECLR
|
|
// DCOM Authentication is not supported for CoreCLR. Throw an error
|
|
// and request that the user specify WSMan Authentication.
|
|
if (_transportProtocol == TransportProtocol.DCOM)
|
|
{
|
|
PSArgumentException ex = new PSArgumentException(ComputerResources.InvalidParameterDCOMNotSupported);
|
|
ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterDCOMNotSupported", ErrorCategory.InvalidArgument, null));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// ProcessRecord method.
|
|
/// </summary>
|
|
protected override void ProcessRecord()
|
|
{
|
|
string targetComputer = ValidateComputerName();
|
|
if (targetComputer == null) return;
|
|
|
|
bool isLocalhost = targetComputer.Equals("localhost", StringComparison.OrdinalIgnoreCase);
|
|
if (isLocalhost)
|
|
{
|
|
if (!_containsLocalHost)
|
|
_containsLocalHost = true;
|
|
_newNameForLocalHost = _newComputerName;
|
|
|
|
return;
|
|
}
|
|
|
|
DoRenameComputerAction(targetComputer, _newComputerName, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// EndProcessing method
|
|
/// </summary>
|
|
protected override void EndProcessing()
|
|
{
|
|
if (!_containsLocalHost) return;
|
|
|
|
DoRenameComputerAction("localhost", _newNameForLocalHost, true);
|
|
}
|
|
|
|
#endregion "Override Methods"
|
|
|
|
}
|
|
|
|
#endregion Rename-Computer
|
|
|
|
#if !CORECLR
|
|
|
|
#region Test-ComputerSecureChannel
|
|
|
|
|
|
/// <summary>
|
|
/// This cmdlet queries the status of trust relationships and will remove and
|
|
/// rebuild the trust if specified.
|
|
/// </summary>
|
|
|
|
[Cmdlet(VerbsDiagnostic.Test, "ComputerSecureChannel", SupportsShouldProcess = true, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=137749")]
|
|
[OutputType(typeof(Boolean))]
|
|
public class TestComputerSecureChannelCommand : PSCmdlet
|
|
{
|
|
|
|
#region Parameters
|
|
|
|
/// <summary>
|
|
/// Repair the secure channel between the local machine with the domain, if it's broken
|
|
/// </summary>
|
|
[Parameter]
|
|
public SwitchParameter Repair { get; set; }
|
|
|
|
/// <summary>
|
|
/// The trusted domain controller to operate "Repair" on.
|
|
/// </summary>
|
|
[Parameter, ValidateNotNullOrEmpty]
|
|
public string Server { get; set; }
|
|
|
|
/// <summary>
|
|
/// The domain credential for authenticating to the domain the local machine joined
|
|
/// </summary>
|
|
[Parameter, ValidateNotNullOrEmpty, Credential]
|
|
public PSCredential Credential { get; set; }
|
|
|
|
private const uint NETLOGON_CONTROL_REDISCOVER = 5;
|
|
private const uint NETLOGON_CONTROL_TC_QUERY = 6;
|
|
private const uint NETLOGON_INFO_2 = 2;
|
|
|
|
#endregion Parameters
|
|
|
|
#region "Private Methods"
|
|
|
|
[SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = "Return results are used in debug asserts.")]
|
|
private bool VerifySecureChannel(string domain, string localMachineName)
|
|
{
|
|
IntPtr queryInfo = IntPtr.Zero;
|
|
IntPtr domainPtr = Marshal.StringToCoTaskMemAuto(domain);
|
|
bool scInGoodState = false;
|
|
|
|
try
|
|
{
|
|
int errorCode = SAMAPI.I_NetLogonControl2(null, NETLOGON_CONTROL_TC_QUERY, NETLOGON_INFO_2, ref domainPtr, out queryInfo);
|
|
|
|
if (errorCode != 0)
|
|
{
|
|
var ex = new Win32Exception(errorCode);
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToTestSecureChannel, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToTestSecureChannel",
|
|
ErrorCategory.OperationStopped, localMachineName);
|
|
ThrowTerminatingError(error);
|
|
}
|
|
|
|
var infoData = (SAMAPI.NetLogonInfo2)Marshal.PtrToStructure(queryInfo, typeof(SAMAPI.NetLogonInfo2));
|
|
scInGoodState = infoData.PdcConnectionStatus == 0;
|
|
}
|
|
finally
|
|
{
|
|
if (domainPtr != IntPtr.Zero)
|
|
{
|
|
Marshal.FreeCoTaskMem(domainPtr);
|
|
}
|
|
if (queryInfo != IntPtr.Zero)
|
|
{
|
|
int freeResult = SAMAPI.NetApiBufferFree(queryInfo);
|
|
Dbg.Diagnostics.Assert(freeResult == 0, "NetApiBufferFree returned non-zero value");
|
|
}
|
|
}
|
|
|
|
return scInGoodState;
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = "Return results are used in debug asserts.")]
|
|
private bool ResetSecureChannel(string domain)
|
|
{
|
|
IntPtr queryInfo = IntPtr.Zero;
|
|
IntPtr domainPtr = Marshal.StringToCoTaskMemAuto(domain);
|
|
bool scInGoodState = false;
|
|
|
|
try
|
|
{
|
|
int errorCode = SAMAPI.I_NetLogonControl2(null, NETLOGON_CONTROL_REDISCOVER, NETLOGON_INFO_2, ref domainPtr, out queryInfo);
|
|
if (errorCode == 0)
|
|
{
|
|
var infoData = (SAMAPI.NetLogonInfo2)Marshal.PtrToStructure(queryInfo, typeof(SAMAPI.NetLogonInfo2));
|
|
scInGoodState = infoData.TrustedDcName != null;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (domainPtr != IntPtr.Zero)
|
|
{
|
|
Marshal.FreeCoTaskMem(domainPtr);
|
|
}
|
|
if (queryInfo != IntPtr.Zero)
|
|
{
|
|
int freeResult = SAMAPI.NetApiBufferFree(queryInfo);
|
|
Dbg.Diagnostics.Assert(freeResult == 0, "NetApiBufferFree returned non-zero value");
|
|
}
|
|
}
|
|
|
|
return scInGoodState;
|
|
}
|
|
|
|
#endregion "Private Methods"
|
|
|
|
#region "Override Methods"
|
|
|
|
/// <summary>
|
|
/// BeginProcessing method
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
if (Server != null)
|
|
{
|
|
if (Server.Length == 1 && Server[0] == '.')
|
|
{
|
|
Server = "localhost";
|
|
}
|
|
|
|
try
|
|
{
|
|
string resolveFullName = Dns.GetHostEntry(Server).HostName;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
CommandProcessorBase.CheckForSevereException(exception);
|
|
|
|
string errMsg = StringUtil.Format(ComputerResources.CannotResolveComputerName, Server, exception.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "AddressResolutionException", ErrorCategory.InvalidArgument, Server);
|
|
ThrowTerminatingError(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// ProcessRecord method.
|
|
/// Suppress the message about NetApiBufferFree. The retuned results are
|
|
/// actually used, but only in checked builds
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults")]
|
|
protected override void ProcessRecord()
|
|
{
|
|
string localMachineName = Dns.GetHostName();
|
|
string domain = null;
|
|
Exception exception = null;
|
|
|
|
if (!ShouldProcess(localMachineName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ManagementObject computerSystemInstance = new ManagementObject("Win32_ComputerSystem.Name=\"" + localMachineName + "\"");
|
|
if (!(bool)computerSystemInstance["PartOfDomain"])
|
|
{
|
|
string errMsg = ComputerResources.TestComputerNotInDomain;
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "ComputerNotInDomain",
|
|
ErrorCategory.InvalidOperation, localMachineName);
|
|
ThrowTerminatingError(error);
|
|
}
|
|
domain = (string)LanguagePrimitives.ConvertTo(computerSystemInstance["Domain"], typeof(string), CultureInfo.InvariantCulture);
|
|
}
|
|
catch (ManagementException ex)
|
|
{
|
|
exception = ex;
|
|
}
|
|
catch (COMException ex)
|
|
{
|
|
exception = ex;
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
exception = ex;
|
|
}
|
|
if (exception != null)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToGetDomainInformation, exception.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToGetDomainInformation",
|
|
ErrorCategory.OperationStopped, localMachineName);
|
|
ThrowTerminatingError(error);
|
|
}
|
|
|
|
Dbg.Diagnostics.Assert(domain != null, "domain should not be null at this point");
|
|
bool scInGoodState = false;
|
|
string verboseMsg = null;
|
|
if (Repair)
|
|
{
|
|
ResetComputerMachinePasswordCommand.
|
|
ResetMachineAccountPassword(domain, localMachineName, Server, Credential, this);
|
|
|
|
scInGoodState = ResetSecureChannel(domain);
|
|
verboseMsg = scInGoodState
|
|
? StringUtil.Format(ComputerResources.RepairSecureChannelSucceed, domain)
|
|
: StringUtil.Format(ComputerResources.RepairSecureChannelFail, domain);
|
|
}
|
|
else
|
|
{
|
|
scInGoodState = VerifySecureChannel(domain, localMachineName);
|
|
verboseMsg = scInGoodState
|
|
? StringUtil.Format(ComputerResources.SecureChannelAlive, domain)
|
|
: StringUtil.Format(ComputerResources.SecureChannelBroken, domain);
|
|
}
|
|
|
|
WriteObject(scInGoodState);
|
|
WriteVerbose(verboseMsg);
|
|
}
|
|
|
|
#endregion "Override Methods"
|
|
|
|
}//End Class
|
|
|
|
|
|
#endregion
|
|
|
|
#region Reset-ComputerMachinePassword
|
|
/// <summary>
|
|
/// Resets the computer machine password used to authenticate with DCs.
|
|
/// </summary>
|
|
|
|
[Cmdlet("Reset", "ComputerMachinePassword",
|
|
SupportsShouldProcess = true, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=135252")]
|
|
public class ResetComputerMachinePasswordCommand : PSCmdlet
|
|
{
|
|
#region "Parameter and PrivateData"
|
|
|
|
/// <summary>
|
|
/// The following is the definition of the input parameter "Server".
|
|
/// Specifies the name of the domain controller to use for setting the machine
|
|
/// account password.
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateNotNullOrEmpty]
|
|
public string Server { get; set; }
|
|
|
|
/// <summary>
|
|
/// The domain credential for authenticating to the domain the local machine joined
|
|
/// </summary>
|
|
[Parameter]
|
|
[ValidateNotNullOrEmpty]
|
|
[Credential]
|
|
public PSCredential Credential { get; set; }
|
|
|
|
private const uint STATUS_ACCESS_DENIED = 0xc0000022;
|
|
private const uint STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034;
|
|
|
|
private const uint SECRET_SET_VALUE = 0x00000001;
|
|
private const uint SECRET_QUERY_VALUE = 0x00000002;
|
|
|
|
// This number comes from the GenerateRandomPassword implementation of NetDom.exe.
|
|
// There is a reason behind this number.
|
|
private const int PasswordLength = 120;
|
|
private const string SecretKey = "$MACHINE.ACC";
|
|
|
|
#endregion "Parameter and PrivateData"
|
|
|
|
#region "Private Methods"
|
|
|
|
/// <summary>
|
|
/// Throw out terminating error for LSA function invocations
|
|
/// </summary>
|
|
/// <param name="ret"></param>
|
|
/// <param name="cmdlet"></param>
|
|
private static void ThrowOutLsaError(uint ret, PSCmdlet cmdlet)
|
|
{
|
|
var ex = new Win32Exception(SAMAPI.LsaNtStatusToWinError((int)ret));
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToResetPasswordOnLocalMachine, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToResetPasswordOnLocalMachine",
|
|
ErrorCategory.OperationStopped, Dns.GetHostName());
|
|
cmdlet.ThrowTerminatingError(error);
|
|
}
|
|
|
|
#endregion "Private Methods"
|
|
|
|
#region "Internal Methods"
|
|
|
|
/// <summary>
|
|
/// Reset machine account password
|
|
/// </summary>
|
|
/// <param name="domain"></param>
|
|
/// <param name="localMachineName"></param>
|
|
/// <param name="server"></param>
|
|
/// <param name="credential"></param>
|
|
/// <param name="cmdlet"></param>
|
|
[SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = "Return results are used in debug asserts.")]
|
|
internal static void ResetMachineAccountPassword(string domain, string localMachineName, string server, PSCredential credential, PSCmdlet cmdlet)
|
|
{
|
|
// Get domain directory entry and reset the password on the machine account of the local machine
|
|
string newPassword = null;
|
|
string domainOrServerName = server ?? domain;
|
|
|
|
try
|
|
{
|
|
string dUserName = credential != null ? credential.UserName : null;
|
|
string dPassword = credential != null ? Utils.GetStringFromSecureString(credential.Password) : null;
|
|
|
|
using (var domainEntry = new DirectoryEntry(
|
|
"LDAP://" + domainOrServerName,
|
|
dUserName,
|
|
dPassword,
|
|
AuthenticationTypes.Secure))
|
|
{
|
|
using (var searcher = new DirectorySearcher(domainEntry))
|
|
{
|
|
searcher.Filter = "(&(objectClass=computer)(|(cn=" + localMachineName + ")(dn=" + localMachineName + ")))";
|
|
SearchResult result = searcher.FindOne();
|
|
|
|
if (result == null)
|
|
{
|
|
string format = server != null
|
|
? ComputerResources.CannotFindMachineAccountFromServer
|
|
: ComputerResources.CannotFindMachineAccountFromDomain;
|
|
string errMsg = StringUtil.Format(format, domainOrServerName);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "CannotFindMachineAccount",
|
|
ErrorCategory.OperationStopped, localMachineName);
|
|
cmdlet.ThrowTerminatingError(error);
|
|
}
|
|
else
|
|
{
|
|
// Generate a random password of length 120, and reset the password on the machine account
|
|
using (var targetEntry = result.GetDirectoryEntry())
|
|
{
|
|
newPassword = ComputerWMIHelper.GetRandomPassword(PasswordLength);
|
|
targetEntry.Invoke("SetPassword", new object[] { newPassword });
|
|
targetEntry.Properties["LockOutTime"].Value = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (DirectoryServicesCOMException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToResetPasswordOnDomain, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToResetPasswordOnDomain",
|
|
ErrorCategory.OperationStopped, localMachineName);
|
|
cmdlet.ThrowTerminatingError(error);
|
|
}
|
|
catch (TargetInvocationException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToResetPasswordOnDomain, ex.InnerException.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToResetPasswordOnDomain",
|
|
ErrorCategory.OperationStopped, localMachineName);
|
|
cmdlet.ThrowTerminatingError(error);
|
|
}
|
|
catch (COMException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToResetPasswordOnDomain, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToResetPasswordOnDomain",
|
|
ErrorCategory.OperationStopped, localMachineName);
|
|
cmdlet.ThrowTerminatingError(error);
|
|
}
|
|
|
|
// Set the same password to the local machine
|
|
Dbg.Diagnostics.Assert(newPassword != null, "the newPassword should not be null at this point");
|
|
|
|
// A direct translation of function NetpManageMachineSecret2 in //depot/winmain/ds/netapi/netjoin/joinutl.c
|
|
// Initialize the LSA_OBJECT_ATTRIBUTES
|
|
var lsaAttr = new SAMAPI.LSA_OBJECT_ATTRIBUTES();
|
|
lsaAttr.RootDirectory = IntPtr.Zero;
|
|
lsaAttr.ObjectName = IntPtr.Zero;
|
|
lsaAttr.Attributes = 0;
|
|
lsaAttr.SecurityDescriptor = IntPtr.Zero;
|
|
lsaAttr.SecurityQualityOfService = IntPtr.Zero;
|
|
lsaAttr.Length = Marshal.SizeOf(typeof(SAMAPI.LSA_OBJECT_ATTRIBUTES));
|
|
|
|
// Initialize the policy handle and secret handle
|
|
IntPtr policyHandle = IntPtr.Zero;
|
|
IntPtr secretHandle = IntPtr.Zero;
|
|
|
|
// Initialize variables for LsaQuerySecret call
|
|
IntPtr currentPassword = IntPtr.Zero;
|
|
|
|
// Declare the key, newData and currentData
|
|
var key = new SAMAPI.LSA_UNICODE_STRING { Buffer = IntPtr.Zero };
|
|
var newData = new SAMAPI.LSA_UNICODE_STRING { Buffer = IntPtr.Zero };
|
|
|
|
// Initialize the systemName for the localhost
|
|
var localhost = new SAMAPI.LSA_UNICODE_STRING();
|
|
localhost.Buffer = IntPtr.Zero;
|
|
localhost.Length = 0;
|
|
localhost.MaximumLength = 0;
|
|
|
|
try
|
|
{
|
|
// Open the LSA policy
|
|
uint ret = SAMAPI.LsaOpenPolicy(ref localhost, ref lsaAttr, (int)SAMAPI.LSA_ACCESS.AllAccess, out policyHandle);
|
|
if (ret == STATUS_ACCESS_DENIED)
|
|
{
|
|
string errMsg = ComputerResources.NeedAdminPrivilegeToResetPassword;
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "UnauthorizedAccessException",
|
|
ErrorCategory.InvalidOperation, localMachineName);
|
|
cmdlet.ThrowTerminatingError(error);
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
ThrowOutLsaError(ret, cmdlet);
|
|
}
|
|
|
|
// Initialize sercret key, new secret
|
|
SAMAPI.InitLsaString(SecretKey, ref key);
|
|
SAMAPI.InitLsaString(newPassword, ref newData);
|
|
bool secretCreated = false;
|
|
|
|
// Open the secret. If the secret is not found, create the secret
|
|
ret = SAMAPI.LsaOpenSecret(policyHandle, ref key, SECRET_SET_VALUE | SECRET_QUERY_VALUE, out secretHandle);
|
|
if (ret == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
ret = SAMAPI.LsaCreateSecret(policyHandle, ref key, SECRET_SET_VALUE, out secretHandle);
|
|
secretCreated = true;
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
ThrowOutLsaError(ret, cmdlet);
|
|
}
|
|
|
|
SAMAPI.LSA_UNICODE_STRING currentData;
|
|
// Get the current password
|
|
if (secretCreated)
|
|
{
|
|
// Use the new password as the current one
|
|
currentData = newData;
|
|
}
|
|
else
|
|
{
|
|
// Query for the current password
|
|
ret = SAMAPI.LsaQuerySecret(secretHandle, out currentPassword, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
|
|
if (ret != 0)
|
|
{
|
|
ThrowOutLsaError(ret, cmdlet);
|
|
}
|
|
|
|
currentData = (SAMAPI.LSA_UNICODE_STRING)Marshal.PtrToStructure(currentPassword, typeof(SAMAPI.LSA_UNICODE_STRING));
|
|
}
|
|
|
|
ret = SAMAPI.LsaSetSecret(secretHandle, ref newData, ref currentData);
|
|
if (ret != 0)
|
|
{
|
|
ThrowOutLsaError(ret, cmdlet);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
// Release pointers
|
|
if (currentPassword != IntPtr.Zero)
|
|
{
|
|
int releaseResult = SAMAPI.LsaFreeMemory(currentPassword);
|
|
Dbg.Diagnostics.Assert(releaseResult == 0, "LsaFreeMemory returned non-zero value");
|
|
}
|
|
|
|
// Release handles
|
|
if (policyHandle != IntPtr.Zero)
|
|
{
|
|
int releaseResult = SAMAPI.LsaClose(policyHandle);
|
|
Dbg.Diagnostics.Assert(releaseResult == 0, "LsaClose returned non-zero value");
|
|
}
|
|
|
|
if (secretHandle != IntPtr.Zero)
|
|
{
|
|
int releaseResult = SAMAPI.LsaClose(secretHandle);
|
|
Dbg.Diagnostics.Assert(releaseResult == 0, "LsaClose returned non-zero value");
|
|
}
|
|
|
|
// Release LSA_UNICODE_STRING
|
|
SAMAPI.FreeLsaString(ref key);
|
|
SAMAPI.FreeLsaString(ref newData);
|
|
}
|
|
}
|
|
|
|
#endregion "Internal Methods"
|
|
|
|
#region "Override Methods"
|
|
|
|
/// <summary>
|
|
/// BeginProcessing method.
|
|
/// </summary>
|
|
protected override void BeginProcessing()
|
|
{
|
|
if (Server != null)
|
|
{
|
|
if (Server.Length == 1 && Server[0] == '.')
|
|
{
|
|
Server = "localhost";
|
|
}
|
|
|
|
try
|
|
{
|
|
string resolveFullName = Dns.GetHostEntry(Server).HostName;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
CommandProcessorBase.CheckForSevereException(exception);
|
|
|
|
string errMsg = StringUtil.Format(ComputerResources.CannotResolveComputerName, Server, exception.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "AddressResolutionException", ErrorCategory.InvalidArgument, Server);
|
|
ThrowTerminatingError(error);
|
|
}
|
|
}
|
|
}//End BeginProcessing()
|
|
|
|
/// <summary>
|
|
/// ProcessRecord method
|
|
/// Suppress the message about LsaFreeMemory and LsaClose. The retuned results are
|
|
/// actually used, but only in checked builds
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults")]
|
|
protected override void ProcessRecord()
|
|
{
|
|
// Not to use Environment.MachineName to avoid the injection attack
|
|
string localMachineName = Dns.GetHostName();
|
|
string domainName = null;
|
|
Exception exception = null;
|
|
|
|
if (!ShouldProcess(localMachineName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ManagementObject computerSystemInstance = new ManagementObject("Win32_ComputerSystem.Name=\"" + localMachineName + "\"");
|
|
if (!(bool)computerSystemInstance["PartOfDomain"])
|
|
{
|
|
string errMsg = ComputerResources.ResetComputerNotInDomain;
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "ComputerNotInDomain",
|
|
ErrorCategory.InvalidOperation, localMachineName);
|
|
ThrowTerminatingError(error);
|
|
}
|
|
domainName = (string)LanguagePrimitives.ConvertTo(computerSystemInstance["Domain"], typeof(string), CultureInfo.InvariantCulture);
|
|
}
|
|
catch (ManagementException ex)
|
|
{
|
|
exception = ex;
|
|
}
|
|
catch (COMException ex)
|
|
{
|
|
exception = ex;
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
exception = ex;
|
|
}
|
|
if (exception != null)
|
|
{
|
|
string errMsg = StringUtil.Format(ComputerResources.FailToGetDomainInformation, exception.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToGetDomainInformation",
|
|
ErrorCategory.OperationStopped, localMachineName);
|
|
ThrowTerminatingError(error);
|
|
}
|
|
|
|
// Get domain directory entry and reset the password on the machine account of the local machine
|
|
Dbg.Diagnostics.Assert(domainName != null, "domainOrServerName should not be null at this point");
|
|
ResetMachineAccountPassword(domainName, localMachineName, Server, Credential, this);
|
|
}
|
|
|
|
#endregion "Override Methods"
|
|
|
|
}//End Class
|
|
|
|
#endregion Reset-ComputerMachinePassword
|
|
|
|
#region SAMCmdletsHelper
|
|
|
|
/// <summary>
|
|
/// the static class for calling the the NetJoinDomain function.
|
|
/// </summary>
|
|
static internal class SAMAPI
|
|
{
|
|
/// <summary>
|
|
/// Structure for the LSA unicode string
|
|
/// </summary>
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
internal struct LSA_UNICODE_STRING
|
|
{
|
|
internal ushort Length;
|
|
internal ushort MaximumLength;
|
|
[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
|
|
internal IntPtr Buffer;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Structure for the LSA object attributes
|
|
/// </summary>
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
internal struct LSA_OBJECT_ATTRIBUTES
|
|
{
|
|
internal int Length;
|
|
internal IntPtr RootDirectory;
|
|
internal IntPtr ObjectName;
|
|
internal int Attributes;
|
|
internal IntPtr SecurityDescriptor;
|
|
internal IntPtr SecurityQualityOfService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The LSA access mask
|
|
/// </summary>
|
|
internal enum LSA_ACCESS
|
|
{
|
|
Read = 0x20006,
|
|
AllAccess = 0x00F0FFF,
|
|
Execute = 0X20801,
|
|
Write = 0X207F8
|
|
}
|
|
|
|
/// <summary>
|
|
/// LsaOpenPolicy function
|
|
/// </summary>
|
|
/// <param name="systemName"></param>
|
|
/// <param name="objectAttributes"></param>
|
|
/// <param name="desiredAccess"></param>
|
|
/// <param name="policyHandle"></param>
|
|
/// <returns></returns>
|
|
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern uint LsaOpenPolicy(
|
|
ref LSA_UNICODE_STRING systemName,
|
|
ref LSA_OBJECT_ATTRIBUTES objectAttributes,
|
|
uint desiredAccess,
|
|
out IntPtr policyHandle);
|
|
|
|
/// <summary>
|
|
/// LsaOpenSecret function
|
|
/// </summary>
|
|
/// <param name="policyHandle"></param>
|
|
/// <param name="secretName"></param>
|
|
/// <param name="accessMask"></param>
|
|
/// <param name="secretHandle"></param>
|
|
/// <returns></returns>
|
|
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern uint LsaOpenSecret(
|
|
IntPtr policyHandle,
|
|
ref LSA_UNICODE_STRING secretName,
|
|
uint accessMask,
|
|
out IntPtr secretHandle);
|
|
|
|
/// <summary>
|
|
/// LsaCreateSecret function
|
|
/// </summary>
|
|
/// <param name="policyHandle"></param>
|
|
/// <param name="secretName"></param>
|
|
/// <param name="desiredAccess"></param>
|
|
/// <param name="secretHandle"></param>
|
|
/// <returns></returns>
|
|
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern uint LsaCreateSecret(
|
|
IntPtr policyHandle,
|
|
ref LSA_UNICODE_STRING secretName,
|
|
uint desiredAccess,
|
|
out IntPtr secretHandle);
|
|
|
|
/// <summary>
|
|
/// LsaQuerySecret function
|
|
/// </summary>
|
|
/// <param name="secretHandle"></param>
|
|
/// <param name="currentValue"></param>
|
|
/// <param name="currentValueSetTime"></param>
|
|
/// <param name="oldValue"></param>
|
|
/// <param name="oldValueSetTime"></param>
|
|
/// <returns></returns>
|
|
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern uint LsaQuerySecret(
|
|
IntPtr secretHandle,
|
|
out IntPtr currentValue,
|
|
IntPtr currentValueSetTime,
|
|
IntPtr oldValue,
|
|
IntPtr oldValueSetTime);
|
|
|
|
/// <summary>
|
|
/// LsaSetSecret function
|
|
/// </summary>
|
|
/// <param name="secretHandle"></param>
|
|
/// <param name="currentValue"></param>
|
|
/// <param name="oldValue"></param>
|
|
/// <returns></returns>
|
|
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern uint LsaSetSecret(
|
|
IntPtr secretHandle,
|
|
ref LSA_UNICODE_STRING currentValue,
|
|
ref LSA_UNICODE_STRING oldValue);
|
|
|
|
/// <summary>
|
|
/// LsaNtStatusToWinError function
|
|
/// </summary>
|
|
/// <param name="ntStatus"></param>
|
|
/// <returns></returns>
|
|
[DllImport("advapi32")]
|
|
internal static extern int LsaNtStatusToWinError(int ntStatus);
|
|
|
|
/// <summary>
|
|
/// LsaClose function
|
|
/// </summary>
|
|
/// <param name="policyHandle"></param>
|
|
/// <returns></returns>
|
|
[DllImport("advapi32")]
|
|
internal static extern int LsaClose(IntPtr policyHandle);
|
|
|
|
/// <summary>
|
|
/// LsaFreeMemory function
|
|
/// </summary>
|
|
/// <param name="buffer"></param>
|
|
/// <returns></returns>
|
|
[DllImport("advapi32")]
|
|
internal static extern int LsaFreeMemory(IntPtr buffer);
|
|
|
|
/// <summary>
|
|
/// Initialize a LSA_UNICODE_STRING
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
/// <param name="lus"></param>
|
|
/// <returns></returns>
|
|
internal static void InitLsaString(string s, ref LSA_UNICODE_STRING lus)
|
|
{
|
|
// Unicode strings max 32KB. The max value for MaximumLength should be ushort.MaxValue-1
|
|
// because UnicodeEncoding.CharSize is 2. So the length of the string s should not be larger
|
|
// than (ushort.MaxValue - 1)/UnicodeEncoding.CharSize - 1, which is 0x7ffe (32766)
|
|
ushort maxLength = (ushort.MaxValue - 1)/UnicodeEncoding.CharSize - 1;
|
|
if (s.Length > maxLength)
|
|
throw new ArgumentException("String too long");
|
|
lus.Buffer = Marshal.StringToHGlobalUni(s);
|
|
lus.Length = (ushort)(s.Length * UnicodeEncoding.CharSize);
|
|
lus.MaximumLength = (ushort)((s.Length + 1) * UnicodeEncoding.CharSize);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Free the LSA_UNICODE_STRING
|
|
/// </summary>
|
|
/// <param name="s"></param>
|
|
internal static void FreeLsaString(ref LSA_UNICODE_STRING s)
|
|
{
|
|
if (s.Buffer == IntPtr.Zero) return;
|
|
|
|
Marshal.FreeHGlobal(s.Buffer);
|
|
s.Buffer = IntPtr.Zero;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The NETLOGON_INFO_2 struct used for function I_NetLogonControl2
|
|
/// </summary>
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
|
internal struct NetLogonInfo2
|
|
{
|
|
internal uint Flags;
|
|
/// <summary>
|
|
/// Secure channel status with the primary domain controller
|
|
/// </summary>
|
|
internal uint PdcConnectionStatus;
|
|
/// <summary>
|
|
/// Name of the trusted domain controller
|
|
/// </summary>
|
|
internal string TrustedDcName;
|
|
/// <summary>
|
|
/// Secure channel status with the specified trusted domain controller
|
|
/// </summary>
|
|
internal uint TdcConnectionStatus;
|
|
}
|
|
|
|
/// <summary>
|
|
/// To Reset a passowrd for a computer in domain.
|
|
/// </summary>
|
|
[DllImport("netapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
internal static extern int I_NetLogonControl2(
|
|
[In] string lpServerName,
|
|
uint lpFunctionCode,
|
|
uint lpQueryLevel,
|
|
ref IntPtr lpInputData,
|
|
out IntPtr queryInformation);
|
|
|
|
|
|
[DllImport("Netapi32.dll", SetLastError = true)]
|
|
internal static extern int NetApiBufferFree(IntPtr Buffer);
|
|
|
|
internal const int WorkrGroupMachine = 2692;
|
|
internal const int MaxMachineNameLength = 15;
|
|
}
|
|
|
|
# endregion
|
|
|
|
#endif
|
|
|
|
#region "Public API"
|
|
/// <summary>
|
|
/// The object returned by SAM Computer cmdlets representing the status of the target machine.
|
|
/// </summary>
|
|
public sealed class ComputerChangeInfo
|
|
{
|
|
private const string MatchFormat = "{0}:{1}";
|
|
|
|
/// <summary>
|
|
/// The HasSuceeded which shows the operation was success or not
|
|
/// </summary>
|
|
public bool HasSucceeded
|
|
{
|
|
get
|
|
{
|
|
return hasSucceeded;
|
|
}
|
|
set
|
|
{
|
|
hasSucceeded = value;
|
|
}
|
|
}
|
|
private bool hasSucceeded;
|
|
|
|
/// <summary>
|
|
/// The ComputerName on which the operation is done.
|
|
/// </summary>
|
|
public string ComputerName
|
|
{
|
|
get
|
|
{
|
|
return computername;
|
|
}
|
|
set
|
|
{
|
|
computername = value;
|
|
}
|
|
}
|
|
private string computername;
|
|
|
|
/// <summary>
|
|
/// Returns the string representation of this object.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override string ToString()
|
|
{
|
|
return FormatLine(this.HasSucceeded.ToString(), this.computername);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Formats a line for use in ToString.
|
|
/// </summary>
|
|
/// <param name="HasSucceeded"></param>
|
|
/// <param name="computername"></param>
|
|
/// <returns></returns>
|
|
private string FormatLine(string HasSucceeded, string computername)
|
|
{
|
|
return StringUtil.Format(MatchFormat, HasSucceeded, computername);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The object returned by Rename-Computer cmdlet representing the status of the target machine.
|
|
/// </summary>
|
|
public sealed class RenameComputerChangeInfo
|
|
{
|
|
private const string MatchFormat = "{0}:{1}:{2}";
|
|
|
|
/// <summary>
|
|
/// The status which shows the operation was success or failure.
|
|
/// </summary>
|
|
public bool HasSucceeded
|
|
{
|
|
get
|
|
{
|
|
return hasSucceeded;
|
|
}
|
|
set
|
|
{
|
|
hasSucceeded = value;
|
|
}
|
|
}
|
|
private bool hasSucceeded;
|
|
|
|
/// <summary>
|
|
/// The NewComputerName which represents the target machine.
|
|
/// </summary>
|
|
public string NewComputerName
|
|
{
|
|
get
|
|
{
|
|
return newcomputername;
|
|
}
|
|
set
|
|
{
|
|
newcomputername = value;
|
|
}
|
|
}
|
|
private string newcomputername;
|
|
|
|
/// <summary>
|
|
/// The OldComputerName which represented the target machine.
|
|
/// </summary>
|
|
public string OldComputerName
|
|
{
|
|
get
|
|
{
|
|
return oldcomputername;
|
|
}
|
|
set
|
|
{
|
|
oldcomputername = value;
|
|
}
|
|
}
|
|
private string oldcomputername;
|
|
|
|
/// <summary>
|
|
/// Returns the string representation of this object.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override string ToString()
|
|
{
|
|
return FormatLine(this.HasSucceeded.ToString(), this.newcomputername, this.oldcomputername);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Formats a line for use in ToString.
|
|
/// </summary>
|
|
/// <param name="HasSucceeded"></param>
|
|
/// <param name="newcomputername"></param>
|
|
/// <param name="oldcomputername"></param>
|
|
/// <returns></returns>
|
|
private string FormatLine(string HasSucceeded, string newcomputername, string oldcomputername)
|
|
{
|
|
return StringUtil.Format(MatchFormat, HasSucceeded, newcomputername, oldcomputername);
|
|
}
|
|
}
|
|
#endregion "Public API"
|
|
|
|
#region Helper
|
|
/// <summary>
|
|
/// Helper Class used by Stop-Computer,Restart-Computer and Test-Connection
|
|
/// Also Contain constants used by System Restore related Cmdlets.
|
|
/// </summary>
|
|
static internal class ComputerWMIHelper
|
|
{
|
|
/// <summary>
|
|
/// The maximum length of a valid NetBIOS name
|
|
/// </summary>
|
|
internal const int NetBIOSNameMaxLength = 15;
|
|
|
|
/// <summary>
|
|
/// System Restore Class used by Cmdlets
|
|
/// </summary>
|
|
internal const string WMI_Class_SystemRestore = "SystemRestore";
|
|
|
|
/// <summary>
|
|
/// OperatingSystem WMI class used by Cmdlets
|
|
/// </summary>
|
|
internal const string WMI_Class_OperatingSystem = "Win32_OperatingSystem";
|
|
|
|
/// <summary>
|
|
/// Service WMI class used by Cmdlets
|
|
/// </summary>
|
|
internal const string WMI_Class_Service = "Win32_Service";
|
|
|
|
/// <summary>
|
|
/// Win32_ComputerSystem WMI class used by Cmdlets
|
|
/// </summary>
|
|
internal const string WMI_Class_ComputerSystem = "Win32_ComputerSystem";
|
|
|
|
/// <summary>
|
|
/// Ping Class used by Cmdlet.
|
|
/// </summary>
|
|
internal const string WMI_Class_PingStatus = "Win32_PingStatus";
|
|
|
|
/// <summary>
|
|
/// CIMV2 path
|
|
/// </summary>
|
|
internal const string WMI_Path_CIM = "\\root\\cimv2";
|
|
|
|
/// <summary>
|
|
/// Default path
|
|
/// </summary>
|
|
internal const string WMI_Path_Default = "\\root\\default";
|
|
|
|
/// <summary>
|
|
/// The error says The interface is unknown.
|
|
/// </summary>
|
|
internal const int ErrorCode_Interface = 1717;
|
|
|
|
/// <summary>
|
|
/// This error says An instance of the service is already running.
|
|
/// </summary>
|
|
internal const int ErrorCode_Service = 1056;
|
|
|
|
/// <summary>
|
|
/// The name of the privilege to shutdown a local system
|
|
/// </summary>
|
|
internal const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
|
|
|
|
/// <summary>
|
|
/// The name of the privilege to shutdown a remote system
|
|
/// </summary>
|
|
internal const string SE_REMOTE_SHUTDOWN_NAME = "SeRemoteShutdownPrivilege";
|
|
|
|
/// <summary>
|
|
/// DCOM protocol
|
|
/// </summary>
|
|
internal const string DcomProtocol = "DCOM";
|
|
|
|
/// <summary>
|
|
/// WSMan protocol
|
|
/// </summary>
|
|
internal const string WsmanProtocol = "WSMan";
|
|
|
|
/// <summary>
|
|
/// CimUriPrefix
|
|
/// </summary>
|
|
internal const string CimUriPrefix = "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2";
|
|
|
|
/// <summary>
|
|
/// CimOperatingSystemNamespace
|
|
/// </summary>
|
|
internal const string CimOperatingSystemNamespace = "root/cimv2";
|
|
|
|
/// <summary>
|
|
/// CimOperatingSystemShutdownMethod
|
|
/// </summary>
|
|
internal const string CimOperatingSystemShutdownMethod = "Win32shutdown";
|
|
|
|
/// <summary>
|
|
/// CimQueryDialect
|
|
/// </summary>
|
|
internal const string CimQueryDialect = "WQL";
|
|
|
|
/// <summary>
|
|
/// Local host name
|
|
/// </summary>
|
|
internal const string localhostStr = "localhost";
|
|
|
|
|
|
/// <summary>
|
|
/// Get the local admin user name from a local NetworkCredential
|
|
/// </summary>
|
|
/// <param name="computerName"></param>
|
|
/// <param name="psLocalCredential"></param>
|
|
/// <returns></returns>
|
|
internal static string GetLocalAdminUserName(string computerName, PSCredential psLocalCredential)
|
|
{
|
|
string localUserName = null;
|
|
|
|
// The format of local admin username should be "ComputerName\AdminName"
|
|
if (psLocalCredential.UserName.Contains("\\"))
|
|
{
|
|
localUserName = psLocalCredential.UserName;
|
|
}
|
|
else
|
|
{
|
|
int dotIndex = computerName.IndexOf(".", StringComparison.OrdinalIgnoreCase);
|
|
if (dotIndex == -1)
|
|
{
|
|
localUserName = computerName + "\\" + psLocalCredential.UserName;
|
|
}
|
|
else
|
|
{
|
|
localUserName = computerName.Substring(0, dotIndex) + "\\" + psLocalCredential.UserName;
|
|
}
|
|
}
|
|
|
|
return localUserName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate a random password
|
|
/// </summary>
|
|
/// <param name="passwordLength"></param>
|
|
/// <returns></returns>
|
|
internal static string GetRandomPassword(int passwordLength)
|
|
{
|
|
const int charMin = 32, charMax = 122;
|
|
const int allowedCharsCount = charMax - charMin + 1;
|
|
byte[] randomBytes = new byte[passwordLength];
|
|
char[] chars = new char[passwordLength];
|
|
|
|
RandomNumberGenerator rng = RandomNumberGenerator.Create();
|
|
rng.GetBytes(randomBytes);
|
|
|
|
for (int i = 0; i < passwordLength; i++)
|
|
{
|
|
chars[i] = (char)(randomBytes[i] % allowedCharsCount + charMin);
|
|
}
|
|
|
|
return new string(chars);
|
|
}
|
|
|
|
#if !CORECLR // TODO:CORECLR Remove once ported to MI .Net
|
|
|
|
/// <summary>
|
|
/// Get the Connection Options
|
|
/// </summary>
|
|
/// <param name="Authentication"></param>
|
|
/// <param name="Impersonation"></param>
|
|
/// <param name="Credential"></param>
|
|
/// <returns></returns>
|
|
internal static ConnectionOptions GetConnectionOptions(AuthenticationLevel Authentication, ImpersonationLevel Impersonation, PSCredential Credential)
|
|
{
|
|
ConnectionOptions options = new ConnectionOptions();
|
|
options.Authentication = Authentication;
|
|
options.EnablePrivileges = true;
|
|
options.Impersonation = Impersonation;
|
|
if (Credential != null)
|
|
{
|
|
options.Username = Credential.UserName;
|
|
options.SecurePassword = Credential.Password;
|
|
}
|
|
return options;
|
|
}
|
|
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Gets the Scope
|
|
///
|
|
/// </summary>
|
|
/// <param name="computer"></param>
|
|
/// <param name="namespaceParameter"></param>
|
|
/// <returns></returns>
|
|
internal static string GetScopeString(string computer, string namespaceParameter)
|
|
{
|
|
StringBuilder returnValue = new StringBuilder("\\\\");
|
|
if (computer.Equals("::1", StringComparison.CurrentCultureIgnoreCase) || computer.Equals("[::1]", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
returnValue.Append("localhost");
|
|
}
|
|
else
|
|
{
|
|
returnValue.Append(computer);
|
|
}
|
|
returnValue.Append(namespaceParameter);
|
|
return returnValue.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if it is a valid drive on the system.
|
|
/// </summary>
|
|
/// <param name="drive"></param>
|
|
/// <returns></returns>
|
|
internal static bool IsValidDrive(string drive)
|
|
{
|
|
DriveInfo[] drives = DriveInfo.GetDrives();
|
|
foreach (DriveInfo logicalDrive in drives)
|
|
{
|
|
if (logicalDrive.DriveType.Equals(DriveType.Fixed))
|
|
{
|
|
if (drive.ToString().Equals(logicalDrive.Name.ToString(), System.StringComparison.OrdinalIgnoreCase))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Chacks whether string[] contains System Drive.
|
|
/// </summary>
|
|
/// <param name="drives"></param>
|
|
/// <param name="sysdrive"></param>
|
|
/// <returns></returns>
|
|
internal static bool ContainsSystemDrive(string[] drives, string sysdrive)
|
|
{
|
|
string driveApp;
|
|
foreach (string drive in drives)
|
|
{
|
|
if (!drive.EndsWith("\\", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
|
|
driveApp = String.Concat(drive, "\\");
|
|
|
|
}
|
|
else
|
|
driveApp = drive;
|
|
if (driveApp.Equals(sysdrive, StringComparison.CurrentCultureIgnoreCase))
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the given computernames in a string
|
|
/// </summary>
|
|
/// <param name="computerNames"></param>
|
|
internal static string GetMachineNames(string[] computerNames)
|
|
{
|
|
string separator = ",";
|
|
RegistryKey regKey = Registry.CurrentUser.OpenSubKey(@"Control Panel\International");
|
|
if (regKey != null)
|
|
{
|
|
object sListValue = regKey.GetValue("sList");
|
|
if (sListValue != null)
|
|
{
|
|
separator = sListValue.ToString();
|
|
}
|
|
}
|
|
|
|
string compname = string.Empty;
|
|
StringBuilder strComputers = new StringBuilder();
|
|
int i = 0;
|
|
foreach (string computer in computerNames)
|
|
{
|
|
if (i > 0)
|
|
{
|
|
strComputers.Append(separator);
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
|
|
if ((computer.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (computer.Equals(".", StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
compname = Dns.GetHostName();
|
|
}
|
|
else
|
|
{
|
|
compname = computer;
|
|
}
|
|
|
|
strComputers.Append(compname);
|
|
}
|
|
|
|
return strComputers.ToString();
|
|
}
|
|
|
|
internal static ComputerChangeInfo GetComputerStatusObject(int errorcode, string computername)
|
|
{
|
|
ComputerChangeInfo computerchangeinfo = new ComputerChangeInfo();
|
|
computerchangeinfo.ComputerName = computername;
|
|
if (errorcode != 0)
|
|
{
|
|
computerchangeinfo.HasSucceeded = false;
|
|
}
|
|
else
|
|
{
|
|
computerchangeinfo.HasSucceeded = true;
|
|
}
|
|
return computerchangeinfo;
|
|
}
|
|
|
|
internal static RenameComputerChangeInfo GetRenameComputerStatusObject(int errorcode, string newcomputername, string oldcomputername)
|
|
{
|
|
RenameComputerChangeInfo renamecomputerchangeinfo = new RenameComputerChangeInfo();
|
|
renamecomputerchangeinfo.OldComputerName = oldcomputername;
|
|
renamecomputerchangeinfo.NewComputerName = newcomputername;
|
|
if (errorcode != 0)
|
|
{
|
|
renamecomputerchangeinfo.HasSucceeded = false;
|
|
}
|
|
else
|
|
{
|
|
renamecomputerchangeinfo.HasSucceeded = true;
|
|
}
|
|
return renamecomputerchangeinfo;
|
|
}
|
|
|
|
|
|
internal static void WriteNonTerminatingError(int errorcode, PSCmdlet cmdlet, string computername)
|
|
{
|
|
Win32Exception ex = new Win32Exception(errorcode);
|
|
string additionalmessage = String.Empty;
|
|
if (ex.NativeErrorCode.Equals(0x00000035))
|
|
{
|
|
additionalmessage = StringUtil.Format(ComputerResources.NetworkPathNotFound, computername);
|
|
}
|
|
string message = StringUtil.Format(ComputerResources.OperationFailed, ex.Message, computername, additionalmessage);
|
|
ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, computername);
|
|
cmdlet.WriteError(er);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check whether the new computer name is valid
|
|
/// </summary>
|
|
/// <param name="computerName"></param>
|
|
/// <returns></returns>
|
|
internal static bool IsComputerNameValid(string computerName)
|
|
{
|
|
bool allDigits = true;
|
|
|
|
if (computerName.Length >= 64)
|
|
return false;
|
|
|
|
foreach (char t in computerName)
|
|
{
|
|
if (t >= 'A' && t <= 'Z' ||
|
|
t >= 'a' && t <= 'z')
|
|
{
|
|
allDigits = false;
|
|
continue;
|
|
}
|
|
else if (t >= '0' && t <= '9')
|
|
{
|
|
continue;
|
|
}
|
|
else if (t == '-')
|
|
{
|
|
allDigits = false;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return !allDigits;
|
|
}
|
|
|
|
/// <summary>
|
|
/// System Restore APIs are not supported on the ARM platform. Skip the system restore operation is necessary.
|
|
/// </summary>
|
|
/// <param name="cmdlet"></param>
|
|
/// <returns></returns>
|
|
internal static bool SkipSystemRestoreOperationForARMPlatform(PSCmdlet cmdlet)
|
|
{
|
|
bool retValue = false;
|
|
if (PsUtils.IsRunningOnProcessorArchitectureARM())
|
|
{
|
|
var ex = new InvalidOperationException(ComputerResources.SystemRestoreNotSupported);
|
|
var er = new ErrorRecord(ex, "SystemRestoreNotSupported", ErrorCategory.InvalidOperation, null);
|
|
cmdlet.WriteError(er);
|
|
retValue = true;
|
|
}
|
|
return retValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invokes the Win32Shutdown command on provided target computer using WSMan
|
|
/// over a CIMSession. The flags parameter determines the type of shutdown operation
|
|
/// such as shutdown, reboot, force etc.
|
|
/// </summary>
|
|
/// <param name="cmdlet">Cmdlet host for reporting errors</param>
|
|
/// <param name="isLocalhost">True if local host computer</param>
|
|
/// <param name="computerName">Target computer</param>
|
|
/// <param name="flags">Win32Shutdown flags</param>
|
|
/// <param name="credential">Optional credential</param>
|
|
/// <param name="authentication">Optional authentication</param>
|
|
/// <param name="formatErrorMessage">Error message format string that takes two parameters</param>
|
|
/// <param name="ErrorFQEID">Fully qualified error Id</param>
|
|
/// <param name="cancelToken">Cancel token</param>
|
|
/// <returns>True on success</returns>
|
|
internal static bool InvokeWin32ShutdownUsingWsman(
|
|
PSCmdlet cmdlet,
|
|
bool isLocalhost,
|
|
string computerName,
|
|
object[] flags,
|
|
PSCredential credential,
|
|
string authentication,
|
|
string formatErrorMessage,
|
|
string ErrorFQEID,
|
|
CancellationToken cancelToken)
|
|
{
|
|
Dbg.Diagnostics.Assert(flags.Length == 2, "Caller need to verify the flags passed in");
|
|
|
|
bool isSuccess = false;
|
|
string targetMachine = isLocalhost ? "localhost" : computerName;
|
|
string authInUse = isLocalhost ? null : authentication;
|
|
PSCredential credInUse = isLocalhost ? null : credential;
|
|
var currentPrivilegeState = new PlatformInvokes.TOKEN_PRIVILEGE();
|
|
var operationOptions = new CimOperationOptions
|
|
{
|
|
Timeout = TimeSpan.FromMilliseconds(10000),
|
|
CancellationToken = cancelToken,
|
|
//This prefix works against all versions of the WinRM server stack, both win8 and win7
|
|
ResourceUriPrefix = new Uri(ComputerWMIHelper.CimUriPrefix)
|
|
};
|
|
|
|
try
|
|
{
|
|
if (!(isLocalhost && PlatformInvokes.EnableTokenPrivilege(ComputerWMIHelper.SE_SHUTDOWN_NAME, ref currentPrivilegeState)) &&
|
|
!(!isLocalhost && PlatformInvokes.EnableTokenPrivilege(ComputerWMIHelper.SE_REMOTE_SHUTDOWN_NAME, ref currentPrivilegeState)))
|
|
{
|
|
string message =
|
|
StringUtil.Format(ComputerResources.PrivilegeNotEnabled, computerName,
|
|
isLocalhost ? ComputerWMIHelper.SE_SHUTDOWN_NAME : ComputerWMIHelper.SE_REMOTE_SHUTDOWN_NAME);
|
|
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(message), "PrivilegeNotEnabled", ErrorCategory.InvalidOperation, null);
|
|
cmdlet.WriteError(errorRecord);
|
|
return false;
|
|
}
|
|
|
|
using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(targetMachine, credInUse, authInUse, cancelToken, cmdlet))
|
|
{
|
|
var methodParameters = new CimMethodParametersCollection();
|
|
methodParameters.Add(CimMethodParameter.Create(
|
|
"Flags",
|
|
flags[0],
|
|
Microsoft.Management.Infrastructure.CimType.SInt32,
|
|
CimFlags.None));
|
|
|
|
methodParameters.Add(CimMethodParameter.Create(
|
|
"Reserved",
|
|
flags[1],
|
|
Microsoft.Management.Infrastructure.CimType.SInt32,
|
|
CimFlags.None));
|
|
|
|
CimMethodResult result = cimSession.InvokeMethod(
|
|
ComputerWMIHelper.CimOperatingSystemNamespace,
|
|
ComputerWMIHelper.WMI_Class_OperatingSystem,
|
|
ComputerWMIHelper.CimOperatingSystemShutdownMethod,
|
|
methodParameters,
|
|
operationOptions);
|
|
|
|
int retVal = Convert.ToInt32(result.ReturnValue.Value, CultureInfo.CurrentCulture);
|
|
if (retVal != 0)
|
|
{
|
|
var ex = new Win32Exception(retVal);
|
|
string errMsg = StringUtil.Format(formatErrorMessage, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(
|
|
new InvalidOperationException(errMsg), ErrorFQEID, ErrorCategory.OperationStopped, computerName);
|
|
cmdlet.WriteError(error);
|
|
}
|
|
else
|
|
{
|
|
isSuccess = true;
|
|
}
|
|
}
|
|
}
|
|
catch (CimException ex)
|
|
{
|
|
string errMsg = StringUtil.Format(formatErrorMessage, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), ErrorFQEID,
|
|
ErrorCategory.OperationStopped, computerName);
|
|
cmdlet.WriteError(error);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
CommandProcessorBase.CheckForSevereException(ex);
|
|
string errMsg = StringUtil.Format(formatErrorMessage, computerName, ex.Message);
|
|
ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), ErrorFQEID,
|
|
ErrorCategory.OperationStopped, computerName);
|
|
cmdlet.WriteError(error);
|
|
}
|
|
finally
|
|
{
|
|
// Restore the previous privilege state if something unexpected happened
|
|
PlatformInvokes.RestoreTokenPrivilege(
|
|
isLocalhost ? ComputerWMIHelper.SE_SHUTDOWN_NAME : ComputerWMIHelper.SE_REMOTE_SHUTDOWN_NAME, ref currentPrivilegeState);
|
|
}
|
|
|
|
return isSuccess;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns valid computer name or null on failure.
|
|
/// </summary>
|
|
/// <param name="nameToCheck">Computer name to validate</param>
|
|
/// <param name="shortLocalMachineName"></param>
|
|
/// <param name="fullLocalMachineName"></param>
|
|
/// <param name="error"></param>
|
|
/// <returns>Valid computer name</returns>
|
|
internal static string ValidateComputerName(
|
|
string nameToCheck,
|
|
string shortLocalMachineName,
|
|
string fullLocalMachineName,
|
|
ref ErrorRecord error)
|
|
{
|
|
string validatedComputerName = null;
|
|
|
|
if (nameToCheck.Equals(".", StringComparison.OrdinalIgnoreCase) ||
|
|
nameToCheck.Equals(localhostStr, StringComparison.OrdinalIgnoreCase) ||
|
|
nameToCheck.Equals(shortLocalMachineName, StringComparison.OrdinalIgnoreCase) ||
|
|
nameToCheck.Equals(fullLocalMachineName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
validatedComputerName = localhostStr;
|
|
}
|
|
else
|
|
{
|
|
bool isIPAddress = false;
|
|
try
|
|
{
|
|
IPAddress unused;
|
|
isIPAddress = IPAddress.TryParse(nameToCheck, out unused);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
CommandProcessorBase.CheckForSevereException(e);
|
|
}
|
|
|
|
try
|
|
{
|
|
string fqcn = Dns.GetHostEntryAsync(nameToCheck).Result.HostName;
|
|
if (fqcn.Equals(shortLocalMachineName, StringComparison.OrdinalIgnoreCase) ||
|
|
fqcn.Equals(fullLocalMachineName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// The IPv4 or IPv6 of the local machine is specified
|
|
validatedComputerName = localhostStr;
|
|
}
|
|
else
|
|
{
|
|
validatedComputerName = nameToCheck;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
CommandProcessorBase.CheckForSevereException(e);
|
|
|
|
// If GetHostEntry() throw exception, then the target should not be the local machine
|
|
if (!isIPAddress)
|
|
{
|
|
// Return error if the computer name is not an IP address. Dns.GetHostEntry() may not work on IP addresses.
|
|
string errMsg = StringUtil.Format(ComputerResources.CannotResolveComputerName, nameToCheck, e.Message);
|
|
error = new ErrorRecord(
|
|
new InvalidOperationException(errMsg), "AddressResolutionException",
|
|
ErrorCategory.InvalidArgument, nameToCheck);
|
|
|
|
return null;
|
|
}
|
|
validatedComputerName = nameToCheck;
|
|
}
|
|
}
|
|
|
|
return validatedComputerName;
|
|
}
|
|
}
|
|
#endregion Helper
|
|
|
|
#region Internal Enums
|
|
|
|
internal enum TransportProtocol
|
|
{
|
|
DCOM = 1,
|
|
WSMan = 2
|
|
}
|
|
|
|
#endregion
|
|
|
|
}//End namespace
|