Remote MSI install using c# and WMI
Hi,
I having a problem whilst trying to install an application on remote machines using WMI. The code im using seems right, but it just isn't working. Can anybody,please, help point me in the right direction? Or maybe post a link/script i can have a look at?
Just to note the strings..
_DOMAIN
_USERNAME
_PASSWORD
_MSIPATH
_ARGS
_MACHINE
are all set correctly before they are passed to the method...
Here is the code I'm working on...The thing is it doesn't throw any Exceptions at all, but it dosen't work either.So i am at a bit of a loss as how to fix it...
Any help at all would be greatly appreciated.
CODE:
-----public void installMsi(string _DOMAIN, string _USERNAME, etc, etc)
{
ManagementScope ms = null;
// Connection options
ConnectionOptions co = new ConnectionOptions();
co.Impersonation = ImpersonationLevel.Impersonate;
co.Authentication = AuthenticationLevel.PacketPrivacy;
co.Authority = "ntlmdomain:" + _DOMAIN;
// local machine
if (_MACHINE.ToUpper() == Environment.MachineName.ToUpper())
{
ms = new ManagementScope(@"\ROOT\CIMV2", co);
}
// remote machine
else
{
co.Username = _USERNAME;
co.Password = _PASSWORD;
ms = new ManagementScope(@"\\" + _MACHINE + "\root\cimv2", co);
ms.Options.Impersonation = ImpersonationLevel.Impersonate;
}
try
{
ms.Connect();
ManagementPath mp = new ManagementPath("Win32_Product");
ObjectGetOptions ogo = new ObjectGetOptions();
ManagementClass mc = new ManagementClass(ms, mp, ogo);
ManagementBaseObject inParams = mc.GetMethodParameters("Install");
inParams["PackageLocation"] = _MSIPATH;
inParams["Options"] = _ARGS;
inParams["AllUsers"] = true;
ManagementBaseObject retVal = mc.InvokeMethod("Install", inParams, null);
}catch (Exception ex)
{MessageBox.Show("Exception: " + ex.Message);
}
catch (ManagementException err)
{
MessageBox.Show("WMI Error: " + err.Message);
}
catch (System.UnauthorizedAccessException unauthorizedErr)
{
MessageBox.Show("Connection error: " + unauthorizedErr.Message);
}}
//END
Thanks.
Fraser
Answers
Hi Marek,
Wow, thanks so much for your response, it has helped me a great deal.
With regard to the remote installation of MSI packages, I eventually write a method which allowed me to use MSI packages hosted on a third machine.
Computer A: machine I’m working on
Computer B: machine I’m want to install apps on
Computer C: machine hosting the MSI packages.
The additional steps were to setup Kerberos delegation in Active Directory.
I.E:
1: Enabled delegation in Active Directory on the domain controller.
2: The account on B marked as Trusted (Kerberos).
3: The account on A not marked as sensitive.
I decided to post the code in case anyone else was looking for a similar answer. The code is a bit sloppy but it did what I wanted. Hope it may help someone.
public void RemoteMSI(string machine, string msi, string commandline, string username, string password, string domain)
{
try
{
ConnectionOptions connection =
new ConnectionOptions();
connection.Authority = "kerberos:" + domain + @"\" + machine;
connection.Username = username;
connection.Password = password;
connection.Impersonation = ImpersonationLevel.Delegate;
connection.Authentication = AuthenticationLevel.PacketPrivacy;
//define the WMI root name space
ManagementScope scope =
new ManagementScope(@"\\" + machine + "." + domain + @"\root\CIMV2", connection);
//define path for the WMI class
ManagementPath p =
new ManagementPath("Win32_Product");
//define new instance
ManagementClass classInstance = new ManagementClass(scope, p, null);
// Obtain in-parameters for the method
ManagementBaseObject inParams = classInstance.GetMethodParameters("Install");
// Add the input parameters.
inParams["AllUsers"] = true; //to install for all users
inParams["Options"] = commandline; //paramters must be in the format “property=setting“
inParams["PackageLocation"] = msi; //source file must be on the remote machine
// Execute the method and obtain the return values.
ManagementBaseObject outParams = classInstance.InvokeMethod("Install", inParams, null);
// List outParams
string retVal = outParams["ReturnValue"].ToString();
string msg = null;
switch (retVal)
{
case "0":
msg = "The installation completed successfully.";
break;
case "2":
msg = "The system cannot find the specified file. \n\r\n\r" + msi;
break;
case "3":
msg = "The system cannot find the path specified. \n\r\n\r" + msi;
break;
case "1619":
msg = "This installation package \n\r\n\r " + msi + "\n\r\n\rcould not be opened, please verify that it is accessible.";
break;
case "1620":
msg = "This installation package \n\r\n\r " + msi + "\n\r\n\rcould not be opened, please verify that it is a valid MSI package.";
break;
default:
msg = "Please see... \n\r\n\r http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/error_codes.asp \n\r\n\rError code: " + retVal;
break;
}
// Display outParams
MessageBox.Show(msg, "Installation report");
}
catch (ManagementException me)
{
MessageBox.Show(me.Message, "Management Exception");
}
catch (COMException ioe)
{
MessageBox.Show(ioe.Message, "COM Exception");
}
}
Hi Udooz,
I would suggest that the two lines in you code:
co.Impersonation = ImpersonationLevel.Impersonate;
co.Authentication = AuthenticationLevel.Default;
need to read:
co.Impersonation = ImpersonationLevel.Delegate;
co.Authentication = AuthenticationLevel.PacketPrivacy;
Also you need to enable delegation on the computer accounts.
Have a look at the following:
A: Machine running your application.
B: Machine to install software on to.
C: Machine hosting the required MSI packages
+----+ +----+ +----+
| | | | | |
| A: | ==== 1st hop ===> | B: | ==== 2nd hop ===> | C: |
| | (A connects to B) | | (B connects to C) | |
+----+ +----+ +----+
1: Enable delegation in Active Directory on the domain controller.
2: The account on B must be marked as Trusted (Kerberos).
3: The account on A must not be marked as sensitive.
4: A, B, and the domain controller must be members of the same domain.
Working Code:
// set these values to your requirements
_DOMAIN = "your.domain"; // your domain name
_MACHINE = "target.machine"; // machine to install on
_USERNAME ="username"; // username of account that exists on A and B
_PASSWORD = "password"; // password for _USERNAME account
_MSI = "\\path\to\package.msi"; // UNC path to the MSI package
_ARGS = "property=value"; // commandline arguments for installer
//
ConnectionOptions co =
new ConnectionOptions();
co.Authority = "kerberos:" + _DOMAIN + @"\" + _MACHINE;
co.Username = _USERNAME;
co.Password = _PASSWORD;
//to impersonate current logged user
co.Impersonation = ImpersonationLevel.Delegate;
//PRC_C_IMP_LEVEL_DELEGATE (c++)
co.Authentication = AuthenticationLevel.PacketPrivacy;
co.EnablePrivileges = true;
//define the WMI root name space
ManagementScope ms =
new ManagementScope(@"\\" + _MACHINE + "." + _DOMAIN + @"\root\CIMV2", co);
//define path for the WMI class
ManagementPath mp =
new ManagementPath("Win32_Product");
//define new instance
ManagementClass mc =
new ManagementClass(ms, mp, null);
// Obtain input parameters for the method
ManagementBaseObject inParams = mc.GetMethodParameters("Install");
// Add the input parameters.
inParams["AllUsers"] = true; //to install for all users
inParams["PackageLocation"] = _MSI; //msi source file
inParams["Options"] = _ARGS; //'property=setting'
// Execute the method and obtain the return values.
ManagementBaseObject outParams =
mc.InvokeMethod("Install", inParams, null);
All Replies
Hi, this part of code should help you.
Code depends on two things:
- connection to local/remote computer.
- computer is in domain or workgroup.
Local:
///
<summary> /// Binds working namespace to local computer's root\cimv2 namespace. /// </summary> public void ConnectLocalComputer(){
m_WorkingNamespace =
new ManagementScope();m_WorkingNamespace.Path =
new ManagementPath(@"\\.\root\cimv2"); try{
m_WorkingNamespace.Connect();
}
catch (COMException comException){
this.m_WorkingNamespace = null; throw new ArgumentException("Server does not exists or access denied. \nConfigure firewall options.", comException);}
catch (UnauthorizedAccessException authException){
this.m_WorkingNamespace = null; throw new ArgumentException("Access denied or timeout expired. \nCheck if you are in domain Administrators group.", authException);}
}
Remote:
///
<summary> /// Binds working namespace to remote computer's root\cimv2 namespace. /// </summary> /// <param name="computerName">Name or ip address of remote computer.</param> /// <param name="domainName">Remote computer's domain. If computer is not in domain pass null value.</param> /// <param name="userName">Windows user name, domain account in the Administrators group.</param> /// <param name="password">Username's password.</param> /// <param name="connectionTimeout">Connection timeout.</param> public void ConnectRemoteComputer( string computerName, string domainName, string userName, string password, TimeSpan connectionTimeout ){
ConnectionOptions connectionConfiguration = new ConnectionOptions();connectionConfiguration.Impersonation =
ImpersonationLevel.Impersonate;connectionConfiguration.Authentication =
AuthenticationLevel.Default; if (domainName == null || domainName == string.Empty){
connectionConfiguration.Username = computerName +
"\\" + userName;connectionConfiguration.Authority =
null;}
else{
connectionConfiguration.Username = userName;
connectionConfiguration.Authority =
"NTLMdomain:" + domainName;}
connectionConfiguration.Password = password;
connectionConfiguration.Timeout = connectionTimeout;
connectionConfiguration.EnablePrivileges =
true;m_WorkingNamespace =
new ManagementScope();m_WorkingNamespace.Path =
new ManagementPath(@"\\" + computerName + @"\root\cimv2");m_WorkingNamespace.Options = connectionConfiguration;
try{
m_WorkingNamespace.Connect();
}
catch( COMException comException ){
this.m_WorkingNamespace = null; throw new ArgumentException("Server does not exists or access denied. \nCheck host name and configure firewall options.", comException);}
catch( UnauthorizedAccessException authException ){
this.m_WorkingNamespace = null; throw new ArgumentException("Access denied or timeout expired. \nCheck if username, password and domain are correct and if user is a member of domain Administrators group.", authException);}
}
Remember that you will connect remotely only if remote user is in Administrators group and your firewall has remote admnistration enabled.
Now installing:
///
<summary> /// Installs new msi package on bound computer /// </summary> /// <param name="msiFilePath">Path (on bound computer) to msi package. /// There can be only path to local, phisical drive!</param> /// <param name="installOptions">Additional command line options for installation if format property=setting.</param> /// <param name="allUsers">Indicates whether packege is installed for all users.</param> public void InstallProduct(string msiFilePath, string installOptions, bool allUsers){
if (!this.IsConnected) throw new InvalidOperationException("Object is not bound to WMI namespace. Use one of Connect methods before this operation."); ManagementClass productClass = new ManagementClass(this.m_WorkingNamespace, new ManagementPath("Win32_Product"), new ObjectGetOptions()); try{
object[] parameters = { msiFilePath, installOptions, allUsers }; UInt32 returnValue = (UInt32) productClass.InvokeMethod("Install", parameters); if (returnValue > 0) throw new Exception("Installation failed. Error code = " + returnValue);}
catch (ManagementException exc){
throw new Exception("Installation failed. RPC Server Fault Error.", exc);}
}
I hope it helps :)
Cheers,
Marek
Hi Marek,
Wow, thanks so much for your response, it has helped me a great deal.
With regard to the remote installation of MSI packages, I eventually write a method which allowed me to use MSI packages hosted on a third machine.
Computer A: machine I’m working on
Computer B: machine I’m want to install apps on
Computer C: machine hosting the MSI packages.
The additional steps were to setup Kerberos delegation in Active Directory.
I.E:
1: Enabled delegation in Active Directory on the domain controller.
2: The account on B marked as Trusted (Kerberos).
3: The account on A not marked as sensitive.
I decided to post the code in case anyone else was looking for a similar answer. The code is a bit sloppy but it did what I wanted. Hope it may help someone.
public void RemoteMSI(string machine, string msi, string commandline, string username, string password, string domain)
{
try
{
ConnectionOptions connection =
new ConnectionOptions();
connection.Authority = "kerberos:" + domain + @"\" + machine;
connection.Username = username;
connection.Password = password;
connection.Impersonation = ImpersonationLevel.Delegate;
connection.Authentication = AuthenticationLevel.PacketPrivacy;
//define the WMI root name space
ManagementScope scope =
new ManagementScope(@"\\" + machine + "." + domain + @"\root\CIMV2", connection);
//define path for the WMI class
ManagementPath p =
new ManagementPath("Win32_Product");
//define new instance
ManagementClass classInstance = new ManagementClass(scope, p, null);
// Obtain in-parameters for the method
ManagementBaseObject inParams = classInstance.GetMethodParameters("Install");
// Add the input parameters.
inParams["AllUsers"] = true; //to install for all users
inParams["Options"] = commandline; //paramters must be in the format “property=setting“
inParams["PackageLocation"] = msi; //source file must be on the remote machine
// Execute the method and obtain the return values.
ManagementBaseObject outParams = classInstance.InvokeMethod("Install", inParams, null);
// List outParams
string retVal = outParams["ReturnValue"].ToString();
string msg = null;
switch (retVal)
{
case "0":
msg = "The installation completed successfully.";
break;
case "2":
msg = "The system cannot find the specified file. \n\r\n\r" + msi;
break;
case "3":
msg = "The system cannot find the path specified. \n\r\n\r" + msi;
break;
case "1619":
msg = "This installation package \n\r\n\r " + msi + "\n\r\n\rcould not be opened, please verify that it is accessible.";
break;
case "1620":
msg = "This installation package \n\r\n\r " + msi + "\n\r\n\rcould not be opened, please verify that it is a valid MSI package.";
break;
default:
msg = "Please see... \n\r\n\r http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/error_codes.asp \n\r\n\rError code: " + retVal;
break;
}
// Display outParams
MessageBox.Show(msg, "Installation report");
}
catch (ManagementException me)
{
MessageBox.Show(me.Message, "Management Exception");
}
catch (COMException ioe)
{
MessageBox.Show(ioe.Message, "COM Exception");
}
}
Hi fraser_foo,
I've used the same code in .NET 2.0, but i'm facing installer file acess perfmission error # 1619.
Please give me a solution for this.
Regards,
Udooz
Hi Udooz,
Well Error # 1619 is ERROR_INSTALL_PACKAGE_OPEN_FAILED if you check on:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/error_codes.asp
You can see that the decription for this is:
This installation package could not be opened. Verify that the package exists and is accessible, or contact the application vendor to verify that this is a valid Windows Installer package.
So at a guess the MSI isn't accessable to the target machine...Are your paths correct?
Also, when you say you used the same code, what do you mean by that? The code I posted above?
I wrote that code to work in a particular application i was developing. It may not be what you need at all...
lIf you post your code i could propbably give some more help...
F.
Hi Fraser_Foo,
Consider the following case:
Machine A: The application is running.
Machine B: where the MSI file (eg: TestAppSetup.MSI) is placed.
Machine C: target machine on which i need to install TestAppSetup.MSI
S.No
Scenario
Test Result
Return Value
1
Running application in A.
MSI file in B. [Accessing this in UNC file path format]
Target machine is A itself with local credential.
PASS
0
2
Running application in A.
MSI file in B.[Accessing this in UNC file path format]
Target machine is C with admin credential.
FAIL
1619
3
Running application in A.
MSI file in C. [Accessing this in UNC file path format]
Target machine is C with admin credential.
PASS
0
But my application exactly needs the scenario #2 only.
See the sample code:
string
machineCName; //In this case, "machineC"
if (!Directory.Exists(installerPath)) //installerPath eg. \\machineB\builds
{
return "The source installer path " + installerPath + " is invalid";
}machineCFullPath = "\\\\" + machineCName + @"\root\cimv2";
ManagementScope ms = null;
ConnectionOptions co = new ConnectionOptions();
co.Impersonation = ImpersonationLevel.Impersonate;
co.Authentication = AuthenticationLevel.Default;
co.Authority = "kerberos:" + domainName + "\\" + machineCName;co.Username = mUserName;
co.Password = mPassword;
// New
co.EnablePrivileges = true;
ms = new ManagementScope(machineCFullPath, co);
try
{
ms.Connect();
}
catch (System.Runtime.InteropServices.COMException comException)
{
return "Server does not exists or access denied. Check host name and configure firewall options. Details: " + comException.Message
+ Environment.NewLine + comException.StackTrace;
}
catch (UnauthorizedAccessException authException)
{
return "Access denied or timeout expired. Check if username, password and domain are correct and if user is a member of domain Administrators group. Details: " +
authException.Message + Environment.NewLine + authException.StackTrace;
}
catch (Exception ex)
{
return "Unexpected error occurred. Details: " + ex.Message + Environment.NewLine + ex.StackTrace;
}
try
{
string[] msiFiles = Directory.GetFiles(installerPath, "*.msi");
int intRetVal = 0;
string lastMSIFile = "";
for (int i = 0; i < msiFiles.Length; i++)
{
lastMSIFile = msiFiles
;ManagementPath mp = new ManagementPath("Win32_Product");
ObjectGetOptions ogo = new ObjectGetOptions();
ManagementClass mc = new ManagementClass(ms, mp, ogo);
ManagementBaseObject inParams = mc.GetMethodParameters("Install");
inParams["PackageLocation"] = msiFiles
;if (mArgument != null && mArgument.Trim() != "")
inParams["Options"] = mArgument;
inParams["AllUsers"] = true;
ManagementBaseObject retVal = mc.InvokeMethod("Install", inParams, null);
intRetVal = Convert.ToInt32(retVal["ReturnValue"]);
if (intRetVal != 0)
break;
}
if (intRetVal == 1619)
return "Unable to open installation package " + lastMSIFile + ". Check the file permission.";
else if (intRetVal == 1620)
return "Unable to open installation package " + lastMSIFile + ". Verify that given is valid MSI.";
else if (intRetVal == 2)
return "The system cannot find the MSI file.";
else if (intRetVal == 3)
return "The system cannot find installer path.";
else if (intRetVal == 1259)
return "The MSI " + lastMSIFile + " is incompatible with current operating system installed on this machine.";
else if (intRetVal == 1601)
return "Unable to access MSI service in this machine.";
else if (intRetVal == 1618)
return "An installer already running on the machine.";
else if (intRetVal != 0)
return "Unknown error code " + intRetVal + " returned.";
return "";
}
catch (Exception ex)
{
return "Error while performing installation. Details: " + ex.Message + " " + ex.StackTrace;
}
Please send reply asap. Thanks
Regards,
Udooz
Hi Udooz,
I would suggest that the two lines in you code:
co.Impersonation = ImpersonationLevel.Impersonate;
co.Authentication = AuthenticationLevel.Default;
need to read:
co.Impersonation = ImpersonationLevel.Delegate;
co.Authentication = AuthenticationLevel.PacketPrivacy;
Also you need to enable delegation on the computer accounts.
Have a look at the following:
A: Machine running your application.
B: Machine to install software on to.
C: Machine hosting the required MSI packages
+----+ +----+ +----+
| | | | | |
| A: | ==== 1st hop ===> | B: | ==== 2nd hop ===> | C: |
| | (A connects to B) | | (B connects to C) | |
+----+ +----+ +----+
1: Enable delegation in Active Directory on the domain controller.
2: The account on B must be marked as Trusted (Kerberos).
3: The account on A must not be marked as sensitive.
4: A, B, and the domain controller must be members of the same domain.
Working Code:
// set these values to your requirements
_DOMAIN = "your.domain"; // your domain name
_MACHINE = "target.machine"; // machine to install on
_USERNAME ="username"; // username of account that exists on A and B
_PASSWORD = "password"; // password for _USERNAME account
_MSI = "\\path\to\package.msi"; // UNC path to the MSI package
_ARGS = "property=value"; // commandline arguments for installer
//
ConnectionOptions co =
new ConnectionOptions();
co.Authority = "kerberos:" + _DOMAIN + @"\" + _MACHINE;
co.Username = _USERNAME;
co.Password = _PASSWORD;
//to impersonate current logged user
co.Impersonation = ImpersonationLevel.Delegate;
//PRC_C_IMP_LEVEL_DELEGATE (c++)
co.Authentication = AuthenticationLevel.PacketPrivacy;
co.EnablePrivileges = true;
//define the WMI root name space
ManagementScope ms =
new ManagementScope(@"\\" + _MACHINE + "." + _DOMAIN + @"\root\CIMV2", co);
//define path for the WMI class
ManagementPath mp =
new ManagementPath("Win32_Product");
//define new instance
ManagementClass mc =
new ManagementClass(ms, mp, null);
// Obtain input parameters for the method
ManagementBaseObject inParams = mc.GetMethodParameters("Install");
// Add the input parameters.
inParams["AllUsers"] = true; //to install for all users
inParams["PackageLocation"] = _MSI; //msi source file
inParams["Options"] = _ARGS; //'property=setting'
// Execute the method and obtain the return values.
ManagementBaseObject outParams =
mc.InvokeMethod("Install", inParams, null);
Hi fraser_foo
I've done the same, but still, i can't resolved.
If i'm using NTLMADMIN. getting the "Not Found" error at the following line:
ManagementBaseObject
inParams = objMgmtClass.GetMethodParameters("Install");Exception Detail:
Message: "Not found "
Source: "System.Management"
StackTrace: at System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode)\r\n at System.Management.ManagementObject.Initialize(Boolean getObject)\r\n at System.Management.ManagementObject.get_ClassPath()\r\n at System.Management.ManagementObject.GetMethodParameters(String methodName, ManagementBaseObject& inParameters, IWbemClassObjectFreeThreaded& inParametersClass, IWbemClassObjectFreeThreaded& outParametersClass)\r\n at System.Management.ManagementObject.GetMethodParameters(String methodName)\r\n at iSOFT.iDeployer.App.Deploy.DeployMSI(String serverIP, String installerPath) in D:\\Projects\\PackDeploy\\iDeployerSolution\\iDeployer\\Deploy.cs:line 160"If I'm using Kerberos, getting the "A security package specific error occurred. (Exception from HRESULT: 0x80070721)" error at the same:
Exception Detail:
Source: "mscorlib"
StackTrace: " at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)\r\n at System.Management.ManagementObject.Initialize(Boolean getObject)\r\n at System.Management.ManagementObject.get_ClassPath()\r\n at System.Management.ManagementObject.GetMethodParameters(String methodName, ManagementBaseObject& inParameters, IWbemClassObjectFreeThreaded& inParametersClass, IWbemClassObjectFreeThreaded& outParametersClass)\r\n at System.Management.ManagementObject.GetMethodParameters(String methodName)\r\n at iSOFT.iDeployer.App.Deploy.DeployMSI(String serverIP, String installerPath) in D:\\Projects\\PackDeploy\\iDeployerSolution\\iDeployer\\Deploy.cs:line 160" string
Please help me.Regards,
Udooz
Hi Udooz,
You can't do this 2-hop-method via NTLMADMIN, you must use Kerberos delegation. The error your getting is because the client asks for delegation, but the created security context does not support delegation.
Do you have a firewall runnig on the server? on the target machine? If so try turning these off... If it then works, you need to configure the firewall to stop it blocking the requests.
Also, are the machines all at least XP? or is the target 2000?
F.
Hi, fraser_foo,
I am using your code, but always got an exception of "The RPC server is unavailable". In my situation, I want to deploy a msi package to computer B remotely. B is the target machine, A is the machine running the application code. Also, the msi package is in a shared directory in machine A. A is windows 2003 server, B is Windows XP SP2. I am using a domain administrator account. Any ideas about the exception?
Thanks
John
The error message: RPC Server is Unavailable
This issue can occur for any of the following reasons:
• The RPC service may not be started. • You are unable to resolve a DNS or NetBIOS name. • An RPC channel cannot be established. You could try:
1) Make sure the Remote Procedure Call (RPC) service is running on both machines (net start rpcss)
2) Make sure the Remote Procedure Call Locator (rpclocator) service is running on both machines (net start rpclocator)
3) Make sure the DCOM Server Process Launcher (DcomLaunch) service is running on both machines (net start dcomlaunch)
4) Make sure any firewall / antivirus software isn't blocking the RPC.
Regards,
Fraser
Thank you very much for your response. I checked all items you mentioned above and found no problem. But I still got "RPC server is unavailable" error. I read your previous posted message carefully and found you mentioned in one of the messages the following:
The additional steps were to setup Kerberos delegation in Active Directory.
I.E:
1: Enabled delegation in Active Directory on the domain controller.
2: The account on B marked as Trusted (Kerberos).
3: The account on A not marked as sensitive.
What's the detailed steps to do it (I am using domain administrator's account)?
Thanks
John
Hi Fraser,
I've did all those things.
After that, it returns error number 1619 or 2, even i've provided all permissions to access the installer file path.
Regards,
Udooz
Hi, Marek,
I am using your code to install a MSI package on remote machines (I am using the code you posted exactly and the MSI package is already on the remote machine). It works fine if the remote machine is a windows xp sp2. But It fails if remote machine is windows 2003 server sp1 and it throws an ManagementException. Do you have any idea about this?
Thanks
John
Hi John,
Check that whether WMI Installer Provider is installed on your server or not? By default, WMI Windows Installer Provider is not installed in Win2003. You have to install by
select Start > Control Panel > Add or Remove Programs.
In Add or Remove Programs window, press Add or Remove Windows Components button.
In Windows Components Wizard window, select Management and Monitoring Tools from the Components list. Press Details.. button.
In Management and Monitoring Tools window, select WMI Windows Installer Provider in the Subcomponents of Management and Monitoring Tools list.
Press OK.
And one more thing, did u check the following scenario?
Place the MSI file on one machine and install it on some other remote machine through WMI. If yes, please give me the details.
Regards,
Udooz
Hi, Udooz,
That is exactly why it failed on windows 2003. After I installed WMI installer provider, It worked fine.
Also, I tried to install application on a remote machine using msi package on a another machine, I never succeeded. I read all the posts in this thread carefully, but I still cannot figure out the reason of failure. Now I am using a workaround: remotely copy MSI file to the target machine by using "WNetAddConnection2", and then "CopyFile".
Thanks
John
Hi John,
Can you please give sample code for the abve?
Thanks
Udooz
- I am using your code to do Remote Installation. But i am getting the following error.
Access denied. (Exception from HRESULT: 0x80070005 (E_ACCESSEDDENIED)
at this LocationManagementBaseObject inParams = mc.GetMethodParameters("Install");
any idea why i am getting this error.
- I did like the same thing but during remote installation first i copied my msi file to remote system in which i have to install.Then i install that pack there and after installation i remove my msi file from remote system. This process working fine.I was also getting priviously that error.
Befor running ur program check these configration:
1. If reading logs from another computer on the network, make sure that the user and password you have supplied for each feed correspond to an administrator account on the target computer. That account MUST have a non-blank password.
2. Check that DCOM is enabled on both the host and the target PC. Check the following registry value on both computers:
Key: HKLM\Software\Microsoft\OLE, value: EnableDCOM, should be set to 'Y'
3. Check that WMI is installed. WMI is present by default in all flavors of Windows 2000 and later operating systems, but must be installed manually on NT4 systems.
To check for the presence of WMI, type "wbemtest" into the Run box (Start Menu). If the WMI Tester application starts up, then WMI is present, if not, it must be installed. Consult the Troubleshooting section of the Help for details.
4. Ensure that WMI permissions have been set correctly. Please consult the Troubleshooting section of the Help for details.
5. On a Windows XP Pro computer, make sure that remote logons are not being coerced to the GUEST account (aka "ForceGuest", which is enabled by default computers that are not attached to a domain). To do this, open the Local Security Policy editor (e.g. by typing 'secpol.msc' into the Run box, without quotes). Expand the "Local Policies" node and select "Security Options". Now scroll down to the setting titled "Network access: Sharing and security model for local accounts". If this is set to "Guest only", change it to "Classic" and restart your computer.
6. Also on an XP computer running SP2, configure the firewall to allow remote administration. To do this, open a command prompt and type: netsh firewall set service RemoteAdmin
7. If you have other internal firewalls on your network, you may have to configure them to allow WMI messages. Again, you'll find advice on how to do this in the troubleshooting section of the Help.
Even if you are not knowingly running any firewall software, bear in mind that big-name antivirus solutions such as those produced by McAfee and Symantec often contain their own firewall functionality. If such software is not properly configured to allow WMI traffic, then this may be the cause of the problem.
8. Make sure that no remote access or WMI-related services have been disabled. On an XP machine, the following services should be running (or at least allowed to start on demand):
COM+ Event System
Remote Access Auto Connection Manager
Remote Access Connection Manager
Remote Procedure Call (RPC)
Remote Procedure Call (RPC) Locator
Remote Registry
Server
Windows Management Instrumentation
Windows Management Instrumentation Driver Extensions
WMI Performance Adapter
Workstationthanks
tekchand
Hi Tekchand
These are so many steps to just do this. Dont you think if i have to do Remote Installation on 100 Computers this is more time consuming then to just copy the Installation file on that computer and Run it manually.
In Windoes 2003 Win32_Product class in not isntalled by default. I was testing on that PC for one week when finally i got this Information. I just want to know whats the differance between NTLMADMIN and Kerberos.
Here is what my solution looks like using the code given in this post. It uses NTLMADMIN
1) Map the Remote Machine(A)'s drive to Local Machine (B)
2) Copy the Installation file from Local Machine(B) to Remote Machine(A)
3) Connect to Remote Machine Using NTLMADMIN authentication to Install the product: Everything using WMI and C#
4) Delete the Installation File from Remote Machine.
5) UnMapp the network Drive.
I want to achive the Remote Installation ability without mapping the drive. Is there way to Install the msi file from UNC path..(share folder). I know that for that i have to use kerberos authenticaton, but all the solution posted in this thread some how does not work as they should be for this type of authentication.
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Management;
using System.IO;
using System.Runtime.InteropServices;
namespace RemoteInstall
{
public class RemoteInstall
{
ManagementScope m_WorkingNamespace;
private string _status;
/*
* Install Properties
*/
public string Status
{
get { return _status; }
set
{
_status = value;
}
}
public delegate void StatusUpdate(string message);
// Define an Event based on the above Delegate
public event StatusUpdate statusUpdate;
protected void OnStatusUpdate(string message)
{
if (statusUpdate != null)
{
statusUpdate(message);
}
}
public int StartRemoteInstall(String RemoteMachineName, String domainName, String username, String password, String localmsiPath, String installOptions)
{
NetworkDrive NetDrive = new NetworkDrive();
try
{
if(RemoteMachineName.ToUpper().Equals(Environment.MachineName.ToUpper()))
{
Status = "Installing on Local machine with Current User's Credentials";
OnStatusUpdate(Status);
int retVal = LocalInstall(localmsiPath,installOptions,true);
return retVal;
}
else
{
Status = "Mapping a remote machine drive to local machine";
OnStatusUpdate(Status);
NetDrive.Force = true;
NetDrive.ShareName = @"\\" + RemoteMachineName + @"\c$";
String nextdriveletter = NextDriveLetter();
NetDrive.LocalDrive = nextdriveletter;
NetDrive.MapDrive(username, password);
Status = "Copying installer on to Remote machine.";
OnStatusUpdate(Status);
String networkdrive = nextdriveletter + "\\" + Path.GetFileName(localmsiPath);
System.IO.File.Copy(localmsiPath, networkdrive, true);
Status = "Connecting to Remote Machine.";
OnStatusUpdate(Status);
TimeSpan connectionTimeout = new TimeSpan(0, 3, 0);
ConnectRemoteComputer(RemoteMachineName, domainName, username, password, connectionTimeout);
Status = "Installing Product on Remote Machine.";
OnStatusUpdate(Status);
String remotemsiPath = "C:\\" + Path.GetFileName(localmsiPath);
int retVal = InstallProduct(remotemsiPath, installOptions, true);
System.Threading.Thread.Sleep(10000);
Status = "Removing Installer from Remote Machine.";
OnStatusUpdate(Status);
System.IO.File.Delete(networkdrive);
Status = "Unmapping a remote machine drive from local machine.";
OnStatusUpdate(Status);
NetDrive.UnMapDrive();
return retVal;
}
}
catch (Exception ex)
{
if (!RemoteMachineName.ToUpper().Equals(Environment.MachineName.ToUpper()))
{
NetDrive.UnMapDrive();
}
Status = ex.Message;
return 1;
}
}
public void ConnectLocalComputer()
{
m_WorkingNamespace = new ManagementScope();
m_WorkingNamespace.Path = new ManagementPath(@"\\.\root\cimv2");
try
{
m_WorkingNamespace.Connect();
}
catch (COMException comException)
{
this.m_WorkingNamespace = null;
throw new ArgumentException("Server does not exists or access denied. \nConfigure firewall options.", comException);
}
catch (UnauthorizedAccessException authException)
{
this.m_WorkingNamespace = null;
throw new ArgumentException("Access denied or timeout expired. \nCheck if you are in domain Administrators group.", authException);
}
}
public int LocalInstall(string msiFilePath, string installOptions, bool allUsers)
{
ConnectionOptions connection = new ConnectionOptions();
connection.Impersonation = ImpersonationLevel.Impersonate;
connection.Authentication = AuthenticationLevel.PacketPrivacy;
connection.EnablePrivileges = true;
//define the WMI root name space
ManagementScope scope =
new ManagementScope(@"\\" + "." + @"\root\CIMV2", connection);
//define path for the WMI class
ManagementPath p = new ManagementPath("Win32_Product");
//define new instance
ManagementClass classInstance = new ManagementClass(scope, p, null);
ManagementBaseObject inParams = null;
try
{
inParams = classInstance.GetMethodParameters("Install");
}
catch (Exception exex)
{
throw new Exception(exex.Message);
}
inParams["AllUsers"] = allUsers; //to install for all users
inParams["Options"] = installOptions; //paramters must be in the format “property=setting“
inParams["PackageLocation"] = msiFilePath; //source file must be on the remote machine
// Execute the method and obtain the return values.
ManagementBaseObject outParams = classInstance.InvokeMethod("Install", inParams, null);
// List outParams
UInt32 retVal = (UInt32)outParams["ReturnValue"];
return (int)retVal;
}
public void ConnectRemoteComputer(string computerName, string domainName, string userName, string password,TimeSpan connectionTimeout)
{
ConnectionOptions connectionConfiguration = new ConnectionOptions();
connectionConfiguration.Impersonation = ImpersonationLevel.Impersonate;
connectionConfiguration.Authentication = AuthenticationLevel.Default;
if (domainName == null || domainName == string.Empty)
{
connectionConfiguration.Username = computerName + "\\" + userName;
connectionConfiguration.Authority = null;
}
else
{
connectionConfiguration.Username = userName;
connectionConfiguration.Authority = "NTLMdomain:" + domainName;
}
connectionConfiguration.Password = password;
connectionConfiguration.Timeout = connectionTimeout;
connectionConfiguration.EnablePrivileges = true;
m_WorkingNamespace = new ManagementScope();
m_WorkingNamespace.Path = new ManagementPath(@"\\" + computerName + @"\root\cimv2");
m_WorkingNamespace.Options = connectionConfiguration;
try
{
m_WorkingNamespace.Connect();
}
catch (COMException comException)
{
this.m_WorkingNamespace = null;
throw new ArgumentException("Server does not exists or access denied. \nCheck host name and configure firewall options.", comException);
}
catch (UnauthorizedAccessException authException)
{
this.m_WorkingNamespace = null;
throw new ArgumentException("Access denied or timeout expired. \nCheck if username, password and domain are correct and if user is a member of domain Administrators group.", authException);
}
}
public int InstallProduct(string msiFilePath, string installOptions, bool allUsers)
{
if (!this.m_WorkingNamespace.IsConnected)
throw new InvalidOperationException("Object is not bound to WMI namespace. Use one of Connect methods before this operation.");
ManagementClass productClass = new ManagementClass(this.m_WorkingNamespace,
new ManagementPath("Win32_Product"), new ObjectGetOptions());
try
{
object[] parameters = { msiFilePath, installOptions, allUsers };
UInt32 returnValue = (UInt32)productClass.InvokeMethod("Install", parameters);
return (int)returnValue;
}
catch (ManagementException exc)
{
throw new Exception("Installation failed. RPC Server Fault Error.", exc);
}
}
public int CreateProcess(string command,String workingDir)
{
if (!this.m_WorkingNamespace.IsConnected)
throw new InvalidOperationException("Object is not bound to WMI namespace. Use one of Connect methods before this operation.");
ManagementClass productClass = new ManagementClass(this.m_WorkingNamespace,
new ManagementPath("Win32_Process"), new ObjectGetOptions());
ManagementBaseObject inParams = null;
try
{
inParams = productClass.GetMethodParameters("Create");
}
catch (Exception exex)
{
throw new Exception(exex.Message);
}
// Add the input parameters.
inParams["CommandLine"] = command; //execute this command
inParams["CurrentDirectory"] = workingDir; //working directory for new process
// Execute the method and obtain the return values.
ManagementBaseObject outParams = productClass.InvokeMethod("Create", inParams, null);
// List outParams
string retVal = outParams["ReturnValue"].ToString();
int retValInt = Convert.ToInt32(retVal);
return retValInt;
}
public String ErrorCodeInstall(int errorcode)
{
string msg = null;
switch (errorcode.ToString())
{
case "0":
msg = "The installation completed successfully.";
break;
case "1":
msg = Status;
break;
case "2":
msg = "The system cannot find the specified file.";
break;
case "3":
msg = "The system cannot find the path specified.";
break;
case "1619":
msg = "This installation package could not be opened, please verify that it is accessible.";
break;
case "1620":
msg = "This installation package could not be opened, please verify that it is a valid MSI package.";
break;
default:
msg = "Please see... \n\r\n\r http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/error_codes.asp \n\r\n\rError code: " + errorcode.ToString();
break;
}
return msg;
}
public string NextDriveLetter()
{
DriveInfo[] allDrives = DriveInfo.GetDrives();
for (int i = 68; i <= 90; i++)
{
String nextletter = Convert.ToChar(i).ToString() + ":\\";
int temp = 0;
foreach (DriveInfo de in allDrives)
{
if (de.Name.Equals(nextletter))
temp = 1;
}
if (temp == 0)
return nextletter.Substring(0, 2);
}
return null;
}
}
}.
- Hi,
is there someone, who finds the solution with UNC path to MSI package?
I think that copy msi to remote computer isn't the best way... - What about the cenario below:
Run application and share MSI from Machine A and install on Machine B.
ie: A <--> B
Would I still have to use Kerberos?
I am able to install on Machine B if the MSI is already on Machine B using NTLMdomain authentication. However, when I try to install the MSI from Machine A (via UNC path), I get the 1619 error (The RPC server is unavailable).
Thanks in advance. - OK, I was able to get the MSI deployment working using delegation for the second hop.
However, I still fell this method is limited as you can only deploy to machines on the same domain.
Other than copying the MSI file to the remote machine first, are there any options of deploying a MSI to machines on another domain? - Hi bigcoops,
Can you please post your code how did you get it working on Same Domain whithout copying the installer on to the remove machine? - I used the exact same code that fraser_foo provided in this thread. You must setup delegation in Active Directory as he outlined.
I ended up not using this method for deployment as it is difficult to have working in different environments, and prone to failing, so I don't have the exact code I used. - Hi bigcoops,
In that case my code works perfectly fine. You dont have to do any thing on your machine and remote machine.
It can also deploy something on another domain. As long as you hv administrator rights on that remote machine. Can anyone provide some code examples of how I can remotely install a *.exe installation file instread of a *.msi file?
Thanks!
AlejandroDear RonakPPatel and bigcoops,
There are 2 methods you can install using UNC path:
1) Use Impersonate=delegate
http://www.microsoft.com/technet/scriptcenter/resources/wmifaq.mspx
You need domain to do delegation.
2) Run NET USE on machine B to Machine C (using WMI's Win32_Process Method Create)
You don't need domain but need the login
*********************
Dear Alejando,You can install *.exe using Win32_Process but bear in mind that the exe must able to install silently.
- Inetkid, thanks for your response.I discovered this thread while looking into WMIC... unfortunately, I have the same difficulty (cannot remotely install a WMI compatible MSI unless the files are hosted on that machine). You mentioned the net use command, but I'm concerned that the net use commans are the same that create network drives. I guess my question is:How can net use be applied in such a way that wmic or wmi installation calls may be executed on the remote machine - which accesses the .msi across a network?Thanks for any info you can toss my way!

