I am trying to create and assign application pool to a website in IIS6 and IIS7 using c#. I managed to do this for IIS7 using Microsoft.Web.Administration; but since its not supported in IIS6 i want to accomplish this using System.DirectoryServices;. I have managed to create the application pool but the assigning application pool does not work.
Here is what I have so far...
private static string DefaultAppPool = "DefaultAppPool";
public static void SetApplicationPoolIIS6(string websiteName)
{
string metabasePath = "IIS://localhost/W3SVC";
string appPoolName = GetSavedApplicationPoolName(websiteName);
if (string.IsNullOrEmpty(appPoolName) || appPoolName.Equals(DefaultAppPool))
appPoolName = websiteName + "AppPool";
bool isAppPoolCreated = IsAppPoolCreatedIIS6(websiteName, appPoolName);
if (!isAppPoolCreated)
CreateApplicationPoolIIS6(appPoolName, metabasePath);
DirectoryEntry w3svc = new DirectoryEntry(metabasePath);
w3svc.RefreshCache();
foreach (DirectoryEntry item in w3svc.Children)
{
string siteName = item.Properties["ServerComment"].Value == null ? "" : item.Properties["ServerComment"].Value.ToString();
if (string.IsNullOrEmpty(siteName))
continue;
if (siteName.Equals(websiteName))
{
item.Invoke("Stop", null);
item.Properties["AppPoolId"].Value = appPoolName;
item.CommitChanges();
item.Invoke("Start", null);
break;
}
}
w3svc.CommitChanges();
DirectoryEntry w3svc2 = new DirectoryEntry(metabasePath);
w3svc2.RefreshCache();
SaveWebSiteSettingsIIS6(websiteName, appPoolName);
}
Any idea why the below lines don't assign the created app pool ?
item.Properties["AppPoolId"].Value = appPoolName;
item.CommitChanges();
Any help is much appreciated. Thank you.
Related
Is it possible to get the logon as account using the ServiceController class? I am using the following code to get the service display names and the service status on a remote machine. I do not see a property indicating what account the service is running under. If not then is there another class that I can use to find out what account a service is running under?
ServiceController[] services = ServiceController.GetServices("MyRemotePC");
foreach (ServiceController service in services)
{
Console.WriteLine(
"The {0} service is currently {1}.",
service.DisplayName,
service. Status
);
}
For each service the following first checks to see if the service is running.
If so, it gets the service's processId and uses ManagementObjectSearch to retrieve the corresponding process object. From there it calls GetOwner(out string user, out string domain) from the underlying Win32_Process object, and outputs the result if the call was successful.
The code below worked locally, however I don't have the access to test this against a remote computer. Even locally I had to run the application as an administrator. for GetOwner to not return an error result of 2 (Access Denied).
var services = ServiceController.GetServices("MyRemotePC");
var getOptions = new ObjectGetOptions(null, TimeSpan.MaxValue, true);
var scope = new ManagementScope(#"\\MyRemotePC\root\cimv2");
foreach (ServiceController service in services)
{
Console.WriteLine($"The {service.DisplayName} service is currently {service.Status}.");
if (service.Status != ServiceControllerStatus.Stopped)
{
var svcObj = new ManagementObject(scope, new ManagementPath($"Win32_Service.Name='{service.ServiceName}'"), getOptions);
var processId = (uint)svcObj["ProcessID"];
var searcher = new ManagementObjectSearcher(scope, new SelectQuery($"SELECT * FROM Win32_Process WHERE ProcessID = '{processId}'"));
var processObj = searcher.Get().Cast<ManagementObject>().First();
var props = processObj.Properties.Cast<PropertyData>().ToDictionary(x => x.Name, x => x.Value);
string[] outArgs = new string[] { string.Empty, string.Empty };
var returnVal = (UInt32)processObj.InvokeMethod("GetOwner", outArgs);
if (returnVal == 0)
{
var userName = outArgs[1] + "\\" + outArgs[0];
Console.WriteLine(userName);
}
}
}
Good Day All,
I am having an issue with ManagementObjectSearcher. I am trying to query the exact value that i want but cannot find any reference to the precise syntax requirements and I continually receive an error when trying to finish out the code to be what I need it to be.
the specific portion of code that is presenting the issue is when I check for the drives Encryption state(I know for a fact that my disk is not encrypted on this machine, which is why that is the only value i have if'd currently). Any assistance in getting this code to pull the correct value would be greatly appreciated.
I've tried both the "=" method and the "LIKE" method with no change in output.
using Microsoft.Win32;
using System;
using System.Drawing;
using System.IO;
using System.Management;
using System.Windows.Forms;
public Form1()
{
InitializeComponent();
// Check for OS Version
string OSVer = Convert.ToString(Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "ProductName", null));
OSDialog.Text = OSVer;
// Check Architecture
if (Directory.Exists("C:\\Program Files (x86)"))
{
ArchitectureDialog.Text = "64 Bit";
}
else
{
ArchitectureDialog.Text = "32 Bit";
}
// Check Encryption
ManagementObjectSearcher Collect = new ManagementObjectSearcher("SELECT ProtectionStatus FROM Win32_EncryptableVolume WHERE DriveLetter = 'C:'");
string Encryption = Collect.ToString();
if (Encryption == "0")
{
EncryptionDialog.Text = "Disk is not Encrypted";
EncryptionDialog.ForeColor = Color.Green;
}
}
private void Cancel_Click(object sender, EventArgs e)
{
Close();
}
Getting BitLocker information from WMI requires elevated permissions. Your code has to be running as an admin and you have to ask for elevated privileges. So, I don't use ManagementObjectSearcher to obtain BitLocker info. Instead, I do something similar to the following (modified to your scenario - but not tested as shown):
ManagementObject GetBitLockerManager( string driveLetter )
{
var path = new ManagementPath( );
path.Server = string.Empty;
path.NamespacePath = #"\ROOT\CIMV2\Security\MicrosoftVolumeEncryption";
path.ClassName = "Win32_EncryptableVolume";
var options = new ConnectionOptions( );
options.Impersonation = ImpersonationLevel.Impersonate;
options.EnablePrivileges = true;
options.Authentication = AuthenticationLevel.PacketPrivacy;
var scope = new ManagementScope( path, options );
var mgmt = new ManagementClass( scope, path, new ObjectGetOptions( ) );
mgmt.Get( );
return mgmt
.GetInstances( )
.Cast<ManagementObject>( )
.FirstOrDefault
( vol =>
string.Compare
(
vol[ "DriveLetter" ] as string,
driveLetter,
true
) == 0
);
}
OK so I figured it out, thank you for all of the assistance provided. Code is below.
ManagementObjectSearcher Encryption = new ManagementObjectSearcher(#"root\cimv2\Security\MicrosoftVolumeEncryption", "SELECT * FROM Win32_EncryptableVolume");
foreach (ManagementObject QueryObj in Encryption.Get())
{
string EncryptionStatus = QueryObj.GetPropertyValue("ProtectionStatus").ToString();
if (EncryptionStatus == "0")
{
EncryptionDialog.Text = "Unencrypted";
}
else if (EncryptionStatus == "1")
{
EncryptionDialog.Text = "Encrypted - SysPrep will not complete";
}
else if (EncryptionStatus == "2")
{
EncryptionDialog.Text = "Cannot Determine Encryption";
}
}
We have been able to create a web site. We did this using the information in this link:
https://msdn.microsoft.com/en-us/library/ms525598.aspx
However, we would like to use a port number other that port 80. How do we do this?
We are using IIS 6
If you're using IIS 7, there is a new managed API called Microsoft.Web.Administration
An example from the above blog post:
ServerManager iisManager = new ServerManager();
iisManager.Sites.Add("NewSite", "http", "*:8080:", "d:\\MySite");
iisManager.CommitChanges();
If you're using IIS 6 and want to do this, it's more complex unfortunately.
You will have to create a web service on every server, a web service that handles the creation of a website because direct user impersonation over the network won't work properly (If I recall this correctly).
You will have to use Interop Services and do something similar to this (This example uses two objects, server and site, which are instances of custom classes that store a server's and site's configuration):
string metabasePath = "IIS://" + server.ComputerName + "/W3SVC";
DirectoryEntry w3svc = new DirectoryEntry(metabasePath, server.Username, server.Password);
string serverBindings = ":80:" + site.HostName;
string homeDirectory = server.WWWRootPath + "\\" + site.FolderName;
object[] newSite = new object[] { site.Name, new object[] { serverBindings }, homeDirectory };
object websiteId = (object)w3svc.Invoke("CreateNewSite", newSite);
// Returns the Website ID from the Metabase
int id = (int)websiteId;
See more here
Heres the solution.
Blog article : How to add new website in IIS 7
On Button click :
try
{
ServerManager serverMgr = new ServerManager();
string strWebsitename = txtwebsitename.Text; // abc
string strApplicationPool = "DefaultAppPool"; // set your deafultpool :4.0 in IIS
string strhostname = txthostname.Text; //abc.com
string stripaddress = txtipaddress.Text;// ip address
string bindinginfo = stripaddress + ":80:" + strhostname;
//check if website name already exists in IIS
Boolean bWebsite = IsWebsiteExists(strWebsitename);
if (!bWebsite)
{
Site mySite = serverMgr.Sites.Add(strWebsitename.ToString(), "http", bindinginfo, "C:\\inetpub\\wwwroot\\yourWebsite");
mySite.ApplicationDefaults.ApplicationPoolName = strApplicationPool;
mySite.TraceFailedRequestsLogging.Enabled = true;
mySite.TraceFailedRequestsLogging.Directory = "C:\\inetpub\\customfolder\\site";
serverMgr.CommitChanges();
lblmsg.Text = "New website " + strWebsitename + " added sucessfully";
}
else
{
lblmsg.Text = "Name should be unique, " + strWebsitename + " is already exists. ";
}
}
catch (Exception ae)
{
Response.Redirect(ae.Message);
}
Looping over sites whether name already exists
public bool IsWebsiteExists(string strWebsitename)
{
Boolean flagset = false;
SiteCollection sitecollection = serverMgr.Sites;
foreach (Site site in sitecollection)
{
if (site.Name == strWebsitename.ToString())
{
flagset = true;
break;
}
else
{
flagset = false;
}
}
return flagset;
}
Try the following Code to Know the unUsed PortNo
DirectoryEntry root = new DirectoryEntry("IIS://localhost/W3SVC");
// Find unused ID PortNo for new web site
bool found_valid_port_no = false;
int random_port_no = 1;
do
{
bool regenerate_port_no = false;
System.Random random_generator = new Random();
random_port_no = random_generator.Next(9000,15000);
foreach (DirectoryEntry e in root.Children)
{
if (e.SchemaClassName == "IIsWebServer")
{
int site_id = Convert.ToInt32(e.Name);
//For each detected ID find the port Number
DirectoryEntry vRoot = new DirectoryEntry("IIS://localhost/W3SVC/" + site_id);
PropertyValueCollection pvcServerBindings = vRoot.Properties["serverbindings"];
String bindings = pvcServerBindings.Value.ToString().Replace(":", "");
int port_no = Convert.ToInt32(bindings);
if (port_no == random_port_no)
{
regenerate_port_no = true;
break;
}
}
}
found_valid_port_no = !regenerate_port_no;
} while (!found_valid_port_no);
int newportId = random_port_no;
I have gone though all answer here and also tested. Here is the most clean smarter version of answer for this question. However this still cant work on IIS 6.0. so IIS 8.0 or above is required.
string domainName = "";
string appPoolName = "";
string webFiles = "C:\\Users\\John\\Desktop\\New Folder";
if (IsWebsiteExists(domainName) == false)
{
ServerManager iisManager = new ServerManager();
iisManager.Sites.Add(domainName, "http", "*:8080:", webFiles);
iisManager.ApplicationDefaults.ApplicationPoolName = appPoolName;
iisManager.CommitChanges();
}
else
{
Console.WriteLine("Name Exists already");
}
public static bool IsWebsiteExists(string strWebsitename)
{
ServerManager serverMgr = new ServerManager();
Boolean flagset = false;
SiteCollection sitecollection = serverMgr.Sites;
flagset = sitecollection.Any(x => x.Name == strWebsitename);
return flagset;
}
This simplified method will create a site with default binding settings, and also create the application pool if needed:
public void addIISApplication(string siteName, string physicalPath, int port, string appPoolName)
{
using (var serverMgr = new ServerManager())
{
var sitecollection = serverMgr.Sites;
if (!sitecollection.Any(x => x.Name.ToLower() == siteName.ToLower()))
{
var appPools = serverMgr.ApplicationPools;
if (!appPools.Any(x => x.Name.ToLower() == appPoolName.ToLower()))
{
serverMgr.ApplicationPools.Add(appPoolName);
}
var mySite = serverMgr.Sites.Add(siteName, physicalPath, port);
mySite.ApplicationDefaults.ApplicationPoolName = appPoolName;
serverMgr.CommitChanges();
}
}
}
In properties of site select "Web Site" tab and specify TCP Port.
In studio to debug purpose specify http://localhost:<port>/<site> at tab Web for "Use Local IIS Web Server"
I have a virtual directory name. For this virtual directory i have to find out the application pool associated. Once i get the application pool i have to find out all the virtual directories on this application pool..
I am using this code to find out the application pool associated with virtual
directory
string AppPoolName = string.Empty;
ServerManager manager = new ServerManager();
foreach (Site site in manager.Sites)
{
foreach (Application app in site.Applications)
{
string path = app.Path;
path = path.Replace("/", " ");
path = path.Trim();
if (path.ToLower() == VDName.ToLower())
{
AppPoolName = app.ApplicationPoolName;
break;
}
}
}
using (var serverManager = new ServerManager())
{
var apps = (from site in serverManager.Sites
from app in site.Applications
where app.ApplicationPoolName.Equals("DefaultAppPool")
select app);
}
I think we have to rerun the function for application pool to get the name for applications associated.
ServerManager manager = new ServerManager();
foreach (Site site in manager.Sites)
{
foreach (Application app in site.Applications)
{
if (app.ApplicationPoolName.ToString() == AppPoolName)
{
string appname = app.Path;
}
}
}
Or a new line no looping approach:
Environment.GetEnvironmentVariable("APP_POOL_ID", EnvironmentVariableTarget.Process);
I need to know how I can detect the current application pool I am running under, so I can do a Recycle on it programmatically.
Does anyone know how to do this for IIS6?
My current code for recycling the app-pool is:
/// <summary>
/// Recycle an application pool
/// </summary>
/// <param name="IIsApplicationPool"></param>
public static void RecycleAppPool(string IIsApplicationPool) {
ManagementScope scope = new ManagementScope(#"\\localhost\root\MicrosoftIISv2");
scope.Connect();
ManagementObject appPool = new ManagementObject(scope, new ManagementPath("IIsApplicationPool.Name='W3SVC/AppPools/" + IIsApplicationPool + "'"), null);
appPool.InvokeMethod("Recycle", null, null);
}
And after searching I found the answer myself:
public string GetAppPoolName() {
string AppPath = Context.Request.ServerVariables["APPL_MD_PATH"];
AppPath = AppPath.Replace("/LM/", "IIS://localhost/");
DirectoryEntry root = new DirectoryEntry(AppPath);
if ((root == null)) {
return " no object got";
}
string AppPoolId = (string)root.Properties["AppPoolId"].Value;
return AppPoolId;
}
Hmm. They need a way to let me set my own answer as THE answer.
I found this one as well and it worked for me. Note you might need to include a reference for using System.DirectoryServices;
private static string GetCurrentApplicationPoolId()
{
string virtualDirPath = AppDomain.CurrentDomain.FriendlyName;
virtualDirPath = virtualDirPath.Substring(4);
int index = virtualDirPath.Length + 1;
index = virtualDirPath.LastIndexOf("-", index - 1, index - 1);
index = virtualDirPath.LastIndexOf("-", index - 1, index - 1);
virtualDirPath = "IIS://localhost/" + virtualDirPath.Remove(index);
DirectoryEntry virtualDirEntry = new DirectoryEntry(virtualDirPath);
return virtualDirEntry.Properties["AppPoolId"].Value.ToString();
}