I am trying to change IIS App Pool Identity (user) remotely using C# and getting an error
System.Runtime.InteropServices.COMException (0x800706BA): The RPC server is unavailable.
I am able to do it properly if I allow all RPC dynamic port (in the range of 49152 to 65535) from firewall for all services on a remote machine.
I just want to know the exact service or process name used by the remote system to complete the process so that I can allow the ports for that service only.
public static bool ChangeAppPoolUser(string ip, string machineName, string username, string password, string applicationPoolName)
{
try
{
var metabasePath = "IIS://" + ip + "/W3SVC/AppPools";
// Get list of appPools at specified metabasePath location
using (DirectoryEntry appPools = new DirectoryEntry(metabasePath, username, password))
{
if(appPools==null)
{
Helper.PrepareDebugLog("appPools is null");
}
Helper.PrepareDebugLog("metabasePath:" + metabasePath + " username:" + username + " password:" + password);
// From the list of appPools, Search and get the appPool
using (DirectoryEntry AppPool = appPools.Children.Find(applicationPoolName, "IIsApplicationPool"))
{
Helper.PrepareDebugLog("in");
if (AppPool != null)
{
AppPool.InvokeSet("AppPoolIdentityType", new Object[] { 3 });
// Configure username for the AppPool with above specified username
AppPool.InvokeSet("WAMUserName", new Object[] { Environment.UserDomainName + "\\" + Environment.UserName });
// Configure password for the AppPool with above specified password
AppPool.InvokeSet("WAMUserPass", new Object[] { CommonProgramVariables.localPassword });
// Write above settings to IIS metabase
AppPool.Invoke("SetInfo", null);
// Commit the above configuration changes that are written to metabase
AppPool.CommitChanges();
return true;
}
}
}
}
catch (Exception e)
{
Helper.PrepareLogWithTimstamp("EXCEPTION WHILE CHNAGE USER: Parameter USED machineName:" + machineName + " username:" + username + " password:" + password + " applicationPoolName:" + applicationPoolName + " LocalPassword:" + CommonProgramVariables.localPassword + " Local User:" + Environment.UserDomainName + "\\" + Environment.UserName);
Helper.PrepareLog("EXCEPTION:", e);
}
return false;
}
Expected: AppPool User should be changed for remote machine AppPool.
Actual result:
System.Runtime.InteropServices.COMException (0x800706BA): The RPC server is unavailable.
The error The RPC server is unavailable. (Exception from HRESULT: 0x800706BA) can occur if RPC / WMI connections are blocked on the target machine due to Firewall restrictions or you entered incorrect hostname / IP address of the target machine.
To resolve this error you could follow the below steps:
1)Open Control Panel, click Security and then click Windows Firewall.
2)Click Change Settings and then click the Exceptions tab.
3)In the Exceptions window, select the check box for Windows Management Instrumentation (WMI) to enable WMI traffic through the firewall.
Related
I'm trying to create a function for an application to automatically copy over specific files during the final stage of a job. The only issue here is that the copy location is a protected share drive and uses a separate login instead of being validated by the active directory so I've got to do a bit of a workaround to get File.Copy() working. My workaround has been to call the built in net.exe utility and passing a command line argument that points to the share drive to open up a connection to the drive then using File.Copy() to get the file where it needs to go then deleting the connection. The issue at hand is that this works great on my computer but when anyone else on the team runs the same program a "Logon failure: unknown user name or bad password" is thrown. I'm at a bit of loss as to why this would happen since the username and password are static and not being changed and everyone on the team has the same network permissions I do. Here is the WPF/C# code I'm using to do this:
try
{
string mrdfDropPath = #"dropPathHere";
string MRDFPath = #"storePath\test.xml";
string command = #"use " + mrdfDropPath + #" /user:CORP\Username Password";
Process.Start("net.exe", command);
File.Copy(MRDFPath, mrdfDropPath + "test.xml");
string command2 = #"use " + mrdfDropPath + #" /delete";
Process.Start("net.exe", command2);
StreamWriter writer = new StreamWriter(#"logPath\log.txt", true);
writer.WriteLine(mrdfDropPath + "test.xml" + "," + File.GetLastWriteTime(MRDFPath).ToString());
writer.Close();
}
catch (Exception e)
{
StreamWriter writer = new StreamWriter(#"logPath\log.txt", true);
writer.WriteLine(e.Message);
writer.Close();
}
Like I said this works as expected during debug and when I run the application, but for anyone else it is throwing the error.
There is something terribly wrong below but i just cannot figure out what.
Although the website is created like a charm, the Application pool that should be associated with it, is not created at all.
public string Create(string sitename)
{
try
{
using (ServerManager serverMgr = new ServerManager())
{
string strhostname = sitename + "." + domain;
string bindinginfo = ":80:" + strhostname;
if (!IsWebsiteExists(serverMgr.Sites, strhostname))
{
Site mySite = serverMgr.Sites.Add(strhostname, "http", bindinginfo, "C:\\admin\\" + domain);
ApplicationPool newPool = serverMgr.ApplicationPools.Add(strhostname);
newPool.ManagedRuntimeVersion = "v4.0";
newPool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
serverMgr.CommitChanges();
return "Website " + strhostname + " added sucessfully";
}
else
{
return "Name should be unique, " + strhostname + " already exists.";
}
}
}
catch (Exception ex)
{
return ex.Message;
}
}
What am i doing wrong here?
What's happening here is that when you create your site it automatically gets assigned to the DefaultAppPool.
What you need to do is replace your site's root Application (/) and point it at the application pool you just created.
The easiest way to do this is to first clear your new site's Application collection, then add a new root application that points to your application pool.
Taking your code snippet I changed it to the following:
Site mySite = serverMgr.Sites.Add(strhostname, "http", bindinginfo, "C:\\admin\\" + domain);
// Clear Applications collection
mySite.Applications.Clear();
ApplicationPool newPool = serverMgr.ApplicationPools.Add(strhostname);
newPool.ManagedRuntimeVersion = "v4.0";
newPool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
// Create new root app and specify new application pool
Application app = mySite.Applications.Add("/", "C:\\admin\\" + domain);
app.ApplicationPoolName = strhostname;
serverMgr.CommitChanges();
I wouldnt expect the App Pool name to have punctuation in it. Adding the domain as part of the app pool name is a little unusual - perhaps thats the source. The basic method is discussed here, along with the appcmd syntax to make the same thing happen on the command line - try creating your app pool on the cmd line to see if your parameters are acceptable.
Create an application pool that uses .NET 4.0
I have a console app written in C# that uses MS Fax (FAXCOMEXLib) to send faxes. If I run the application manually or from a command prompt it works as expected. If I schedule the application with Task Scheduler or try to run from a service with a timer, it fails when calling the ConnectedSubmit2 on the FaxDocument object. The application runs as expected, gets the data, creates the pdf, connects to Fax Service, fills the FaxDocument properties, but bombs on ConnectedSubmit2. It feels like a security issue. The windows account the TaskScheduler runs under belongs to the administrator group.
This same application has worked on another Server 2008 (not R2) computer without issue with Task Scheduler.
The server in question is running Microsoft Server 2008 R2.
Recap: The application will work if run manually, fails if run from another process like Task Scheduler.
Any suggestions would be most appreciated. Thank you.
C# Code:
FAXCOMEXLib.FaxServer faxServer = new FAXCOMEXLib.FaxServer();
FAXCOMEXLib.FaxDocument faxDocument = new FAXCOMEXLib.FaxDocument();
ArrayList al = new ArrayList();
al.Add(orderPdfFilePath);
if (facesheetPdfFilePath != "")
al.Add(facesheetPdfFilePath);
if (write) Console.WriteLine("Preparing to Connect to Fax Server...");
sbLog.Append("Preparing to Connect to Fax Server...\r\n");
faxServer.Connect("");
if (write) Console.WriteLine("Connected.");
sbLog.Append("Connected.\r\n");
// Add Sender Information to outgoing fax
faxDocument.Sender.Name = dr2["FacilityName"].ToString();
faxDocument.Sender.Department = dr2["TSID"].ToString();
faxDocument.Sender.TSID = Truncate(dr2["TSID"].ToString(), 20);
faxDocument.Recipients.Add(dr2["FaxNumber"].ToString(), dr2["Pharmacy"].ToString());
faxDocument.Bodies = al.ToArray(typeof(string));
faxDocument.Subject = order;
if (write) Console.WriteLine("Attempting submit to fax server...");
sbLog.Append("Attempting submit to fax server...\r\n");
// attempt send...
try
{
object o;
faxDocument.ConnectedSubmit2(faxServer, out o);
if (write) Console.WriteLine("Fax sent successfully " + DateTime.Now.ToString());
sbLog.Append("Fax sent successfully " + DateTime.Now.ToString() + ".\r\n");
}
catch (Exception ex)
{
if (write) Console.WriteLine("SEND FAILED! " + order + " " + DateTime.Now.ToString() + " " + ex.Message);
sbLog.Append("SEND FAILED! " + order + " " + DateTime.Now.ToString() + ".\r\n" + ex.Message + "\r\n" + ex.InnerException + "\r\n");
error = true;
}
Errors in Event Log:
System.Runtime.InteropServices.COMException (0x80070102): Operation failed.
at FAXCOMEXLib.FaxDocumentClass.ConnectedSubmit2(IFaxServer pFaxServer, Object& pvFaxOutgoingJobIDs)
System.UnauthorizedAccessException: Access denied. at FAXCOMEXLib.FaxDocumentClass.ConnectedSubmit2(IFaxServer pFaxServer, Object& pvFaxOutgoingJobIDs) at ElementsTransmission.Program.Main(String[] args)
See
http://blogs.msdn.com/b/dougste/archive/2011/08/30/system-runtime-interopservices-comexception-0x80070102-operation-failed-trying-to-send-a-fax-from-and-asp-net-application.aspx
Bill
We just started up a new webserver and i'm running into "class not registered" when creating a new application pool. I'm using the code below but I have no idea how to distinguish what is not registered. Any thoughts would be awesome.
Thanks.
string path = "IIS://" + server + "/W3SVC";
string app_pools_path = path + "/AppPools";
/error below.
var app_pools = new DirectoryEntry(app_pools_path);
foreach (DirectoryEntry app_pool in app_pools.Children)
{
//do work
}
Error "Class no registered" error code:2147221164
ON the server open the server manager
add new features ==> Web Server (IIS) ==> Management Tools ==> IIS 6 Management Compatibility then check IIS6 Metabase Compatibility. use your original connection string / path
string path = "IIS://" + server + "/W3SVC";
string app_pools_path = path + "/AppPools";
try this please :
private void StopAppPool(string app_Pool , string server)
{
try
{
ConnectionOptions co = new ConnectionOptions();
co.Username = "DomainName\\UserName";
co.Password = "UserPassword";
string appPool = "W3SVC/AppPools/" + app_Pool;
co.Impersonation = ImpersonationLevel.Impersonate;
co.Authentication = AuthenticationLevel.PacketPrivacy;
string objPath = "IISApplicationPool.Name='" + appPool + "'";
ManagementScope scope = new ManagementScope(#"\\" + server + #"\root\MicrosoftIISv2", co);
using (ManagementObject mc = new ManagementObject(objPath))
{
mc.Scope = scope;
mc.InvokeMethod("Stop", null, null);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
Console.WriteLine(e.InnerException);
Console.WriteLine(e.Data);
}
//Console.ReadLine();
}
You should avoid using DirectoryEntry to manipulate IIS 7 and above. That's the old API based on IIS ADSI interfaces,
http://msdn.microsoft.com/en-us/library/ms524896(v=vs.90).aspx
IIS 6 Compatibilities might help you out though,
http://blogs.msdn.com/b/narahari/archive/2009/05/13/when-using-directoryservices-to-access-iis-schema-iis6-management-compatibility-pack-needs-to-be-installed-system-runtime-interopservices-comexception-0x80005000.aspx
The best solution (which is also strong typed and more convenient for C# developers) is Microsoft.Web.Administration,
http://www.iis.net/learn/manage/scripting/how-to-use-microsoftwebadministration
I'm trying to write simple application in C# which will allow me to backup, zip and send over ftp my SQL Server database.
One problem I have encountered is that I'm not able to create the backup file (.bak) if I try to do it in different location than "C:\Program Files\Microsoft SQL Server\MSSQL.3\MSSQL\Backup" or "C:\Program Files\Microsoft SQL Server\MSSQL.3\MSSQL\Data" folder. I understand that this is a premission problem. Could someone point me to the resources or write here a short snippet how to programmatically add such a permission to any folder on my system.
Regards
Kris
i assume you are running your programm as a scheduled task ... did you give writing permissions to the target folder for the executing user of the task??
edit:
with permissions you can have 2 scenarios:
windows authenification
mixed authentification
if you are using windows authentification, the read and write permissions of the windows user are taken. otherwise the permissions for the sql server service account.
and this behaviour makes sense to me and maybe hits the nail in your scenario!
edit 2:
i don't want to encourage you to do so ... some admins may hate you when you mess up their acl's
but this may do the trick
btw: Magnus Johansson already gave you a "try-this" link
no matter for which method you go - be sure to hand in the correct user (as descriped above!)
(for full history)
...
side-note:
i know this is not the exact answer to your question, but i would recommend you smo to generate backups ...
like
using Microsoft.SqlServer.Management.Smo;
var bdi = new BackupDeviceItem(/* your path inlcuding desired file */);
var backup = new Backup
{
Database = /* name of the database */,
Initialize = true
};
backup.Devices.Add(bdi);
var server = new Server(this.SqlServer);
try
{
backup.SqlBackup(server);
}
catch (Exception ex)
{
// * log or sth
}
you only have to care for the .dll's. take assemblies for the desired server version (some params/properties vary through different server versions)
more info here
Ok Guys, Magnus and dittodhole! Thanks a lot for your help. I have combined Magnus'es link to the article on setting up permisions on the folder together with some more research and finally I've got it :).
So reassuming, I'm using Smo, and to create a folder with proper permissions I have to look for the group instead of win32_Users. Here you go a short snippet if someone finds this post he can find it usefull:
string tempPath = Directory.CreateDirectory("C:\\path_to_your_folder").FullName;
//set permissions
SelectQuery sQuery = new SelectQuery("Win32_Group",
"Domain='" +
System.Environment.UserDomainName.ToString() +
"'");
try
{
DirectoryInfo myDirectoryInfo = new DirectoryInfo("C:\\path_to_your_folder");
DirectorySecurity myDirectorySecurity = myDirectoryInfo.GetAccessControl();
ManagementObjectSearcher mSearcher = new ManagementObjectSearcher(sQuery);
foreach (ManagementObject mObject in mSearcher.Get())
{
string User = System.Environment.UserDomainName + "\\" + mObject["Name"];
if(User.StartsWith("your-machine-name\\SQL"))
{
myDirectorySecurity.
AddAccessRule(new FileSystemAccessRule(User,
FileSystemRights.FullControl,
AccessControlType.Allow));
}
}
myDirectoryInfo.SetAccessControl(myDirectorySecurity);
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
Again thanks everyone for your help! Stackoverflow rocks!
Here is a procedure is use for back up in C#.Hope it helps
public void BackupDatabase (string BackUpLocation, string BackUpFileName, string
DatabaseName, string ServerName )
{
DatabaseName = "[" + DatabaseName + "]";
string fileUNQ = DateTime.Now.Day.ToString() + "_" + DateTime.Now.Month.ToString() + "_" + DateTime.Now.Year.ToString() +"_"+ DateTime.Now.Hour.ToString()+ DateTime.Now .Minute .ToString () + "_" + DateTime .Now .Second .ToString () ;
BackUpFileName = BackUpFileName + fileUNQ + ".bak";
string SQLBackUp = #"BACKUP DATABASE " + DatabaseName + " TO DISK = N'" + BackUpLocation + #"\" + BackUpFileName + #"'";
string svr = "Server=" + ServerName + ";Database=master;Integrated Security=True";
SqlConnection cnBk = new SqlConnection(svr);
SqlCommand cmdBkUp = new SqlCommand(SQLBackUp, cnBk);
try
{
cnBk.Open();
cmdBkUp.ExecuteNonQuery();
Label1.Text = "Done";
Label2.Text = SQLBackUp + " ######## Server name " + ServerName + " Database " + DatabaseName + " successfully backed up to " + BackUpLocation + #"\" + BackUpFileName + "\n Back Up Date : " + DateTime.Now.ToString();
}
catch (Exception ex)
{
Label1.Text = ex.ToString();
Label2.Text = SQLBackUp + " ######## Server name " + ServerName + " Database " + DatabaseName + " successfully backed up to " + BackUpLocation + #"\" + BackUpFileName + "\n Back Up Date : " + DateTime.Now.ToString();
}
finally
{
if (cnBk.State == ConnectionState.Open)
{
cnBk .Close();
}
}
}
Take a look at this article.
Remember to set the permissions for the account that the SQL Server instance is running with.
Although this may not answer your immediate question, I'd advice you to look into SQL Server Integration Services (SSIS). This looks like the exact thing SSIS was created for, and in the 2008 version there's the possibility to use C# code if needed, should the standard components not do what you need (earlier versions used VB.NET).
MSDN SSIS Info Link 1
SSIS 2005 Tutorial Link 2
Take a look at it.