Uninstalling product with WinRM and C# - c#

I'm using WSManAutomation to remotely manage servers.
I need to install and uninstall applications on remote servers that have WinRM over HTTPS enabled. The connection is not a problem
So far the code below executes msiexec.exe in the remote host as I can see it in the list of processes but it does not execute the uninstall command.
public void UninstallProduct(string path, string target, string username = null, string password = null)
{
IWSMan wsman = new WSManClass();
IWSManConnectionOptions options = (IWSManConnectionOptions)wsman.CreateConnectionOptions();
if (options != null)
{
try
{
options.UserName = username;
options.Password = password;
int iFlags = (int)_WSManSessionFlags.WSManFlagCredUsernamePassword;
IWSManSession session = (IWSManSession)wsman.CreateSession(string.Format("https://{0}:5986/wsman", target), iFlags, options);
// IWSManSession session = (IWSManSession)wsman.CreateSession(string.Format("http://{0}/wsman", target), 0, options);
if (session != null)
{
try
{
string strResource = "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Process";
string strInputParameters =string.Format("<p:Create_INPUT xmlns:p=\"{0}\"><p:CommandLine>\"{1}\"</p:CommandLine></p:Create_INPUT>", "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Process",path);
var reply = session.Invoke("Create", strResource, strInputParameters);
Console.WriteLine(reply);
Console.WriteLine();
}
finally
{
Marshal.ReleaseComObject(session);
}
}
}
finally
{
Marshal.ReleaseComObject(options);
}
}
}
The call to the method above would be:
obj.UninstallProduct(#"C:\Windows\System32\msiexec.exe /x {E499AB77-9B27-416CB9B6F-4A171D02BB31} /passive", "hostname", #"hostname\Administrator", "password");
Do you know why the command does not get executed?
Should I use another way to uninstall a product?
Thanks in advance.

Finally I came across a way to do this using remote PowerShell
string command = string.Format("(Get-WmiObject -Class Win32_Product | Where-Object {$_.Name -match {0}}).Uninstall()", productName);
PSCredential credential = new PSCredential(username, securePassword);
string shellUri = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell";
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(true, target, 5986, "/wsman", shellUri, credential);
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline(command);
try
{
Collection<PSObject> results = pipeline.Invoke("Set-ExecutionPolicy Unrestricted -Scope Process");
}
finally
{
runspace.Close();
}
}

Related

How to run services of one pc from another pc

I have a code that tries to access the services of another computer.
try
{
var serviceName = "MyService";
var ip = "10.10.11.16";
var username = "SomeUser";
var password = "APassword";
var connectoptions = new ConnectionOptions();
connectoptions.Impersonation = ImpersonationLevel.Impersonate;
connectoptions.Authentication = AuthenticationLevel.Packet;
connectoptions.EnablePrivileges = true;
connectoptions.Username = username;
connectoptions.Password = password;
var scope = new ManagementScope("\\\\10.10.11.16\\root\\cimv2");
scope.Options = connectoptions;
var query = new SelectQuery("select * from Win32_Service where name = '" + serviceName + "'");
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
{
var collection = searcher.Get();
foreach (ManagementObject service in collection.OfType<ManagementObject>())
{
if (service["started"].Equals(true))
{
service.InvokeMethod("StopService", null);
BtnStartStop.Content = "Stop";
LblService.Content = serviceName;
LblServiceStatus.Content = "Stopped";
}
else
{
service.InvokeMethod("StartService", null);
BtnStartStop.Content = "Stop";
LblService.Content = serviceName;
LblServiceStatus.Content = "Running";
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Will this work on Server and client only? Won't this work on regular pc to another regular pc? Each time I run this when I get to the part of:
var collection = searcher.Get();
I get an error of
Access is denied. (Exception from HRESULT: 0x80070005
(E_ACCESSDENIED))
Do you have an idea on to make this work? Thank you.
STEPS DONE SO FAR
Followed the instruction on
https://learn.microsoft.com/en-us/windows/win32/wmisdk/connecting-to-wmi-remotely-starting-with-vista
typed in the cmd with admin privilege
netsh advfirewall firewall set rule group="windows management instrumentation (wmi)" new enable=yes
I even turned off the firewall just to be sure.
edited the registry of the pc I am connecting to this:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WBEM\CIMOM\AllowAnonymousCallback
Data type
REG\_DWORD
As for the antivirus, the pc I am connecting to does not have any anti virus.
I still get the same error.
As #colosso pointed out, you are receiving that error message because you do not have permission on the remote host to connect to the WMI service.
You should follow the instructions here to ensure the remote host is configured to allow your connection.

How to retrieve results of Poweshell script back in C# objects?

I want to execute a PowerShell script through C#. My script will create a .csv file at a location specified. The below code creates a file at the location specified, but I want the code to return an object which has all the content/data the file has. Is that possible?
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))
{
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
Command scriptCommand = new Command(#"C:\powershell.ps1");
Collection<CommandParameter> commandParameters = new Collection<CommandParameter>();
pipeline.Commands.Add(scriptCommand);
Collection<PSObject> psObjects;
psObjects = pipeline.Invoke();
}
Can you rather make use of Powershell APIs?
For example:
PowerShell psinstance = PowerShell.Create();
psinstance.AddScript(scriptPath);
var results = psinstance.Invoke();
More details can be found here:
https://blogs.msdn.microsoft.com/kebab/2014/04/28/executing-powershell-scripts-from-c/
You can return file content from powershell with this command and then write powershell output to the PSDataCollection.
private async Task<IEnumerable<string>> Process(string command)
{
var output = new List<string>();
using (var powerShell = System.Management.Automation.PowerShell.Create())
{
powerShell.AddScript(command);
var outputCollection = new PSDataCollection<PSObject>();
await Task.Factory.FromAsync(
powerShell.BeginInvoke<PSObject, PSObject>(null, outputCollection),
result =>
{
OnDeployEnd?.Invoke(this, EventArgs.Empty);
foreach (var data in outputCollection)
{
output.Add(data.ToString());
}
}
);
if (powerShell.HadErrors)
{
var errorsReport = powerShell.Streams.Error.GetErrorsReport();
throw new Exception(errorsReport);
}
}
return output;
}

How to run powershell script using c# in SharePoint solution

i am getting below error while i am trying to run power-shell script in my SharePoint solution
The type initializer for
'System.Management.Automation.SessionStateScope' threw an exception.
just a reference
private void RunPowershellScript(string scriptFile, List<string> parameters)
{
PowerShell OPowerShell = null;
Runspace OSPRunSpace = null;
try
{
RunspaceConfiguration OSPRSConfiguration = RunspaceConfiguration.Create();
PSSnapInException OExSnapIn = null;
//Add a snap in for SharePoint. This will include all the power shell commands for SharePoint
PSSnapInInfo OSnapInInfo = OSPRSConfiguration.AddPSSnapIn("Microsoft.SharePoint.PowerShell", out OExSnapIn);
OSPRunSpace = RunspaceFactory.CreateRunspace(OSPRSConfiguration);
OPowerShell = PowerShell.Create();
OPowerShell.Runspace = OSPRunSpace;
Command Cmd1 = new Command("backup-spsite");
Cmd1.Parameters.Add("identity", "Your Site Coll URL");
Cmd1.Parameters.Add("path", "Back up file path");
OPowerShell.Commands.AddCommand(Cmd1 );
OSPRunSpace.Open();
OPowerShell.Invoke();
OSPRunSpace.Close();
}
catch (Exception Ex)
{
//Handle exception
}
finally
{
if (OSPRunSpace != null)
{
OSPRunSpace.Dispose();
OSPRunSpace = null;
}
if (OPowerShell != null)
{
OPowerShell.Dispose();
OPowerShell = null;
}
}

How to get Exchange 2010 database size

I'm trying to get smallest Exchange database in my Exchange 2010 server using remote session.
I successfully connect to my exchange server and get database with properties. Some of them with value, but Properties DatabaseSize with Null value.
Did some body be able to get database size value?
Part of my code below:
static void Main(string[] args)
{
string exchangePowershellRPSURI = "http://my.domain/powershell?serializationLevel=Full";
PSCredential credentials = (PSCredential)null;
//Provides the connection information that is needed to connect to a remote runspace
// Prepare the connection
WSManConnectionInfo connInfo = new WSManConnectionInfo((new Uri(exchangePowershellRPSURI)),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange", credentials);
connInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
connInfo.SkipCACheck = true;
connInfo.SkipCNCheck = true;
connInfo.SkipRevocationCheck = true;
// Create the runspace where the command will be executed
Runspace runspace = RunspaceFactory.CreateRunspace(connInfo);
// Add the command to the runspace's pipeline
runspace.Open();
//Represents the base functionality of a pipeline that can be used to invoke commands
Pipeline pipeline = runspace.CreatePipeline();
Command getMDB = new Command("Get-MailboxDatabase");
getMDB.Parameters.Add("Identity", "*");
getMDB.Parameters.Add("Status", null);
pipeline.Commands.Add(getMDB);
Collection<PSObject> select = pipeline.Invoke();
if (select.Count > 0)
{
foreach(PSObject obj in select)
{
var db = obj.Properties["DatabaseSize"].Value;
string name = obj.Properties["Name"].Value.ToString();
Console.WriteLine("Database Name: {0} Size: {1}", name, db);
}
}
else
{
Console.WriteLine("Failed to create email account");
}
runspace.Dispose();
Console.ReadLine();
}
I have found an solution
In the getMDB.Parameters.Add need for Status parameter to change value from "null" to "true"
getMDB.Parameters.Add("Status", null);

Modify the code to perform operation on remote servers

I wrote the following code to change the user account and password associated with a Windows Service. How can I modify this code to be able to perform the same operation on a remote system?
static void Main(string[] args)
{
string serviceName = "DummyService";
string username = ".\\Service_Test2";
string password = "Password1";
ServiceController sc = new ServiceController(serviceName);
Console.WriteLine(sc.Status.ToString());
if (sc.Status == ServiceControllerStatus.Running)
{
sc.Stop();
}
Thread.Sleep(2000);
sc.Refresh();
Console.WriteLine(sc.Status.ToString());
string objPath = string.Format("Win32_Service.Name='{0}'", serviceName);
using (ManagementObject service = new ManagementObject(new ManagementPath(objPath)))
{
object[] wmiParams = new object[11];
wmiParams[6] = username;
wmiParams[7] = password;
service.InvokeMethod("Change", wmiParams);
}
Thread.Sleep(2000);
Console.WriteLine(sc.Status.ToString());
if (sc.Status == ServiceControllerStatus.Stopped)
{
sc.Start();
}
Thread.Sleep(2000);
sc.Refresh();
Console.WriteLine(sc.Status.ToString());
}
Use ServiceController constructor overload that allows target machine-name to be specified
Modify WMI object path to include the target server.
new ManagementPath(
"\\\\ComputerName\\root" +
"\\cimv2:Win32_Service.Name='{0}'");
Make sure your user/password have sufficient rights on target machine, change those if not.

Categories

Resources