I'm trying to create application pool using the ServerManager class. This is my code:
using (ServerManager serverManager = new ServerManager()) {
if (!serverManager.ApplicationPools.Any(p => p.Name == poolName)) {
ApplicationPool newPool = serverManager.ApplicationPools.Add(poolName);
newPool.ManagedRuntimeVersion = "v4.0";
newPool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
newPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
newPool.ProcessModel.UserName = user;
newPool.ProcessModel.Password = pass;
serverManager.CommitChanges();
}
}
The application pool gets created, but no identity is set for it - the identity column in the application pools table in IIS Manager is blank.
What am i doing wrong?
User is in the form of domain\username, and the credentials passed are correct (another part of code checks for those).
Take a look to this code. It is working for me:
public static ApplicationPool GetOrCreateApplicationPool(ServerManager serverManager, string applicationPoolName, string userName, SecureString password)
{
if (!serverManager.ApplicationPools.Any(p => p.Name.Equals(applicationPoolName)))
{
ApplicationPool appPool = serverManager.ApplicationPools.CreateElement();
appPool.Name = applicationPoolName;
appPool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
appPool.ManagedRuntimeVersion = "v4.0";
if(password != null)
{
if (!(string.IsNullOrWhiteSpace(userName) || string.IsNullOrWhiteSpace(password.ToPlainTextString())))
{
appPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
appPool.ProcessModel.UserName = userName;
appPool.ProcessModel.Password = password.ToPlainTextString();
}
}
serverManager.ApplicationPools.Add(appPool);
serverManager.CommitChanges();
}
return serverManager.ApplicationPools.First(p => p.Name.Equals(applicationPoolName));
}
Of course there are some additional methods such extensions that might cause this piece of code is not able to run but the idea is there and this is currently running on my application.
Related
I'm trying to create an application pool before install and set that application pool for the installed application post install in a visual studio web setup project
Trying to filter out the applications under a site so that the newly created application pool applies only to a specific application with the name "websetup9090" in this case. Tried a few methods to compare the name of the applications but none of them worked
or
If there is any way to run the custom action to create the application pool before it reaches the Installation address UI page it would be even better as there is an option to select the application pool for the user via dropdown
The code below is called via custom action in the web setup project
protected override void OnBeforeInstall(IDictionary stateSaver)
{
ServerManager iisManager = new ServerManager();
bool chkPool = false;
chkPool = iisManager.ApplicationPools.Any(t => t.Name == "Pool99");
if (chkPool == false)
{
var myPool = iisManager.ApplicationPools.Add("Pool99");
myPool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
myPool.ManagedRuntimeVersion = ".NET CLR version 4.0.30319";
myPool.Enable32BitAppOnWin64 = true;
iisManager.CommitChanges();
}
}
protected override void OnAfterInstall(IDictionary stateSaver)
{
ServerManager iisManager = new ServerManager();
var sites = iisManager.Sites;
foreach (Site site in sites)
{
if (site.Name == "Default Web Site")
{
Site mySite = site;
mySite.Stop();
var myPools = iisManager.ApplicationPools;
foreach (ApplicationPool pool in myPools)
{
if (pool.Name == "Pool99")
{
ApplicationPool myPool = pool;
foreach (var item in mySite.Applications)
{
if (item.Path.ToLower().IndexOf("websetup9090") > 0)
{
item.ApplicationPoolName = myPool.Name;
}
}
iisManager.CommitChanges();
myPool.Recycle();
}
}
iisManager.CommitChanges();
mySite.Start();
}
}
}
I resolved the issue with attaching application pool to the newly created application by moving the code in onAfterInstall to Commit method
I am developing a C# application to automate process of deploying website to the server.The website is hosted in a web farm in WINDOWS SERVER 2012 R2. So the problem here is I am trying to take server offline or bring it online by means of some programming interface. but I couldn't find anything related inside Microsoft docs. How do I get the job done?
UPDATE:
As suggested by Timur I did as following, but it didn't work.
ServiceController p = new ServiceController("W3SVC","SERVER_IP");
p.Start();
p.WaitForStatus(ServiceControllerStatus.Running);
This is the sample that generated by configuration manager. It take server offline/online by change the Enabled property of server item in web farm collection.
using System;
using System.Text;
using Microsoft.Web.Administration;
internal static class Sample
{
private static void Main()
{
using (ServerManager serverManager = new ServerManager())
{
Configuration config = serverManager.GetApplicationHostConfiguration();
ConfigurationSection webFarmsSection = config.GetSection("webFarms");
ConfigurationElementCollection webFarmsCollection = webFarmsSection.GetCollection();
ConfigurationElement webFarmElement = FindElement(webFarmsCollection, "webFarm", "name", #"123213");
if (webFarmElement == null) throw new InvalidOperationException("Element not found!");
ConfigurationElementCollection webFarmCollection = webFarmElement.GetCollection();
ConfigurationElement serverElement = FindElement(webFarmCollection, "server", "address", #"11.1.1.1");
if (serverElement == null) throw new InvalidOperationException("Element not found!");
serverElement["enabled"] = false;
serverManager.CommitChanges();
}
}
private static ConfigurationElement FindElement(ConfigurationElementCollection collection, string elementTagName, params string[] keyValues)
{
foreach (ConfigurationElement element in collection)
{
if (String.Equals(element.ElementTagName, elementTagName, StringComparison.OrdinalIgnoreCase))
{
bool matches = true;
for (int i = 0; i < keyValues.Length; i += 2)
{
object o = element.GetAttributeValue(keyValues[i]);
string value = null;
if (o != null)
{
value = o.ToString();
}
if (!String.Equals(value, keyValues[i + 1], StringComparison.OrdinalIgnoreCase))
{
matches = false;
break;
}
}
if (matches)
{
return element;
}
}
}
return null;
}
}
IIS is a Windows service. Therefore the easiest way to start/stop it will be to do something along the lines of this SO answer.
You'll be looking for service name, which likely depends on your version.
UPD see an artist's impression on how your management tool might look like
var hostNames = new List<string> { "appServer1", "webServer1", "webServer2" };
foreach (var host in hostNames)
{
var svc = new ServiceController("W3SVC", host);
svc.Stop();
svc.WaitForStatus(ServiceControllerStatus.Stopped);
Thread.Sleep(10000);// or your custom logic
svc.Start();
svc.WaitForStatus(ServiceControllerStatus.Running);
}
bear in mind, you'll need to run this as a user with sufficient privileges to successfully change service state: as in you need to run this as Admin.
You've got at least two options to do it:
Run your IDE as admin
Update your application manifest as described in this answer
UPD2 apparently you can interface with WFF controller like so
I have a Winform Client that use Windows Active Directory to get the current Windows account name.
Is there any way to know if this solution will work with the new Windows Server 2016 Active Directory without setting it up?
Client code
public string GetCurrentActiveDirectoryAccountName()
{
var windowsName = WindowsIdentity.GetCurrent().Name;
var index = windowsName.LastIndexOf("\\");
if (index > 0)
windowsName = windowsName.Substring(index + 1);
return windowsName;
}
public void AuthenticateActiveDirectoryAccount(string username, string password)
{
//Hidden code to setup variables
if (ADUserName.Length > 0)
context = new PrincipalContext(ContextType.Domain, ADServer, ADUserName, ADUserPassword);
else
context = new PrincipalContext(ContextType.Domain, ADServer);
using (context)
{
if (!context.ValidateCredentials(account, password))
//Hidden code to throw exception
}
}
public string CheckActiveDirectoryAccount(string account)
{
///Hidden code to setup variables
if (ADUserName.Length > 0)
context = new PrincipalContext(ContextType.Domain, ADServer, null, ADUserName, ADUserPassword);
else
context = new PrincipalContext(ContextType.Domain, ADServer);
using (context)
{
if ((user = UserPrincipal.FindByIdentity(context, account)) == null)
{
if (account.Contains("\\"))
{
userPrincipalNameList = user.UserPrincipalName.Split('\\').ToList();
if (userPrincipalNameList.Count > 0)
user = UserPrincipal.FindByIdentity(context, userPrincipalNameList[0]);
}
}
if (user != null)
{
using (user)
{
userAccount = user.SamAccountName;
return userAccount.ToLower();
}
}
}
return string.Empty;
}
Microsoft is historically pretty careful with being backwards-compatible. This is why you can still run DOS programs in Windows 10.
With AD, they don't usually remove features. They only add them. Take a look at the this article to see what's new in AD for Server 2016: https://learn.microsoft.com/en-us/windows-server/identity/whats-new-active-directory-domain-services
I would expect all of that to work with AD running on Server 2016.
I hade to setup a test with Microsoft Windows Server 2016 as as expected my AD integration works just as fine.
I can successfully create an Application Pool and an Application plus link them together.
What I am failing to do however is set the applications Windows Authentication to true and Anonymous Authentication to false.
I patched a hodge podge of examples into one to make this work but I keep getting the following error.
This configuration section cannot be used at this path. This happens
when the section is locked at a parent level. Locking is either by
default (overrideModeDefault="Deny"), or set explicitly by a location
tag with overrideMode="Deny" or the legacy allowOverride="false".
Now barring the obvious that overrideMode needs to likely equal Allow. How do I accomplish this?
public static bool CreateApplication(String websiteName, String applicationName, String appDIR,String appPoolName)
{
try
{
ServerManager iisManager = new ServerManager();
if (!applicationName.Contains("/"))
applicationName = "/" + applicationName;
var app = iisManager.Sites[websiteName].Applications.Add(applicationName, appDIR);
app.ApplicationPoolName = appPoolName;
var config = app.GetWebConfiguration();
var anonsection = config.GetSection("system.webServer/security/authentication/anonymousAuthentication", iisManager.Sites[websiteName].Name + applicationName);
//This is where it fails
anonsection["enabled"] = false;
var winsection = config.GetSection("system.webServer/security/authentication/windowsAuthentication", iisManager.Sites[websiteName].Name + applicationName);
winsection["enabled"] = true;
iisManager.CommitChanges();
return true;
}
catch
{
return false;
}
}
Use the following commands from an admin command prompt
%windir%\system32\inetsrv\appcmd unlock config -section:system.webServer/security/authentication/windowsAuthentication
%windir%\system32\inetsrv\appcmd unlock config -section:system.webServer/security/authentication/anonymousAuthentication
This will unlock those config sections.
I'm trying to automate IIS application creation and for this I'm using Microsoft.Web.Administration library. Here is part of code I'm using:
IISHelper.cs
public static void CreateApplicationPool(string applicationPoolName)
{
using (ServerManager serverManager = new ServerManager())
{
if (serverManager.ApplicationPools[applicationPoolName] != null)
return;
ApplicationPool newPool = serverManager.ApplicationPools.Add(applicationPoolName);
newPool.ManagedRuntimeVersion = "v4.0";
serverManager.CommitChanges();
}
}
public static void CreateSite(string siteName, string path)
{
using (ServerManager serverManager = new ServerManager())
{
var sites = serverManager.Sites;
if (sites[siteName] == null)
{
sites.Add(siteName, "http", "*:80:", path);
serverManager.CommitChanges();
}
}
}
public static void CreateApplication(string siteName, string applicationName, string path)
{
using (ServerManager serverManager = new ServerManager())
{
var site = GetSite(serverManager, siteName);
var applications = site.Applications;
if (applications["/" + applicationName] == null)
{
applications.Add("/" + applicationName, path);
serverManager.CommitChanges();
}
}
}
public static void CreateVirtualDirectory(string siteName, string applicationName, string virtualDirectoryName, string path)
{
using (ServerManager serverManager = new ServerManager())
{
var application = GetApplication(serverManager, siteName, applicationName);
application.VirtualDirectories.Add("/" + virtualDirectoryName, path);
serverManager.CommitChanges();
}
}
public static void SetApplicationApplicationPool(string siteName, string applicationPoolName)
{
using (ServerManager serverManager = new ServerManager())
{
var site = GetSite(serverManager, siteName);
if (site != null)
{
foreach (Application app in site.Applications)
{
app.ApplicationPoolName = applicationPoolName;
}
}
serverManager.CommitChanges();
}
}
And here is main code which calls methods from above mentioned IISHelper class
string siteName = "TestSite";
string applicationName = "TestApp";
string virtualDirectoryName = "TestVirtualDirectory";
string applicationPoolName = "TestAppPool";
string physicalPath = #"C:\inetpub\mynewsite";
IISHelper.CreateApplicationPool(applicationPoolName);
IISHelper.CreateSite(siteName, physicalPath);
IISHelper.CreateApplication(siteName, applicationName, physicalPath);
IISHelper.CreateVirtualDirectory(siteName, applicationName, virtualDirectoryName, physicalPath);
IISHelper.SetApplicationApplicationPool(siteName, applicationPoolName);
After execution of this code I'm getting successfully created application with site and appropriate application pool under the IIS, but when I'm selecting "View Application" in the menu of newly created application pool I'm seeing there two application, but I'm sure there must be only one application. I'm working on this to avoid two application creation but can't find a way. Here is a picture illustrating that:
So here is my question: Why after the above mentioned code execution two applications are created and how to modify the code to avoid that issue?
Every site must include a "Root Application" which is what you are seeing as the second app. Basically when you request your site above as 'http://example.com/' (or whatever host name you set in the bindings) you will be requesting from that root app, in addition you are creating /TestApp which will be requested when calling 'http://example.com/TestApp'. When you create the Site and you provide the physical path it internally creates a root app "/" for it.
To see this you can open %windir%\system32\inetsrv\applicationHost.config.
So technically what you are seeing is correct, but you might not actually want a new app? and only need the root app? Do you really need /TestApp to be in the URL?