C# のプロビジョニング サンプル
作成者: Walter Oliver
概要
上級 Web ユーザーが独自の Web サイトを作成することが、年々簡単になってきました。 エンド ユーザーがドメイン名を登録したら、さまざまな種類のホストから選択できます。 Web ホストは、エンド カスタマーの要求を満たすために世界中で利用できます。 ただし、どのホストが選択されるかに関係なく、Web サイトのサインアップと作成のシナリオは、すべてのケースで似ています。
最初に、ユーザーに対して新しいユーザー アカウントを確立する必要があります。 アカウントが設定されると、エンド ユーザーは、サイトに組み込む必要がある機能とオプションを決定します。たとえば、ディスク領域の量、FTP 機能、仮想ディレクトリの作成、データベースが必要かどうかなどです。ホストは、エンド ユーザーがこれらの機能を作成して管理できるようにするコントロール パネルまたはダッシュボード アプリケーションを構築します。
コントロール パネルにこれらの機能を実装するには、いくつかの方法があります。 このセクションでは、マネージド コードを使用したこれらの機能のプロビジョニングの側面を実装する方法について説明します。 機能の概要は次のとおりです。
新しいユーザー アカウントをプロビジョンする
サイトを管理して保守するサイト所有者には、新しいユーザー アカウントが必要です。 このアカウントは、Active Directory® アカウントまたはローカル ユーザー アカウントにすることができます。 次のコード フラグメントは、ローカル アカウントの作成を示しています。 System.DirectoryServices 名前空間を指定する必要があることに注意してください。
public static bool CreateLocalUserAccount(string userName, string password)
{
try
{
if (string.IsNullOrEmpty(userName))
throw new ArgumentNullException("userName", "Invalid User Name.");
if (string.IsNullOrEmpty(password))
throw new ArgumentNullException("password", "Invalid Password.");
DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" +
Environment.MachineName + ",computer");
bool userFound = false;
try
{
if (directoryEntry.Children.Find(userName, "user") != null)
userFound = true;
}
catch
{
userFound = false;
}
if (!userFound)
{
DirectoryEntry newUser = directoryEntry.Children.Add(userName, "user");
newUser.Invoke("SetPassword", new object[] { password });
newUser.Invoke("Put", new object[] { "Description", "Application Pool User Account" });
newUser.CommitChanges();
newUser.Close();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
コンテンツ ストレージを作成する
Web サイトには、ユーザーがサイトのコンテンツをアップロードできるファイル システム上の場所が必要です。 Microsoft® .NET ディレクトリ クラスには、ファイル システム上にディレクトリを作成するためのアプリケーション プログラミング インターフェイス (API) が用意されています。
Directory.CreateDirectory(parentPath + "\\" + directoryName);
コンテンツ ストレージには、ユーザーが自分のコンテンツを管理できるように構成された特定のアクセス許可が必要です。 次のコード フラグメントは、C# でマネージド コードを使用してディレクトリのアクセス許可を設定する方法を示しています。
public static bool AddDirectorySecurity(string directoryPath, string userAccount, FileSystemRights rights, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType controlType)
{
try
{
// Create a new DirectoryInfo object.
DirectoryInfo dInfo = new DirectoryInfo(directoryPath);
// Get a DirectorySecurity object that represents the
// current security settings.
DirectorySecurity dSecurity = dInfo.GetAccessControl();
// Add the FileSystemAccessRule to the security settings.
dSecurity.AddAccessRule(new FileSystemAccessRule(userAccount, rights, inheritanceFlags, propagationFlags, controlType));
// Set the new access settings.
dInfo.SetAccessControl(dSecurity);
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
次のコード フラグメントは、ディスク クォータが制限されている場合にマネージド コードを使用してディスク クォータを設定する方法を示しています。 ディスク クォータ管理を使用するには、Windows® ディスク クォータ管理コンポーネントへの参照を追加する必要があります。このコンポーネントは Windows\system32\dskquota.dll の下にあります。
public static bool AddUserDiskQuota(string userName, double quota, double quotaThreshold, string diskVolume)
{
try
{
DiskQuotaControlClass diskQuotaCtrl = GetDiskQuotaControl(diskVolume);
diskQuotaCtrl.UserNameResolution = UserNameResolutionConstants.dqResolveNone;
DIDiskQuotaUser diskUser = diskQuotaCtrl.AddUser(userName);
diskUser.QuotaLimit = quota;
diskUser.QuotaThreshold = quotaThreshold;
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
アプリケーション プールを作成する
アプリケーション プールは、要求処理を実行する 1 つ以上のインターネット インフォメーション サービス 7 (IIS 7) アプリケーションをホストする、ワーカー プロセスの設定を定義します。 アプリケーションに対するすべての要求処理がアプリケーション プールのワーカー プロセス内で実行されるため、アプリケーション プールはプロセス分離の単位です。
また、アプリケーション プールでは分離も提供され、その結果として分離によりセキュリティが提供されます。 各アプリケーション プールは、一意の ID で実行でき、アクセス制御リスト (ACL) を使用して、他のプール内のアプリケーションがそのリソースにアクセスできないようにすることができます。 次のコード フラグメントは、アプリケーション プールの作成、ID の設定、およびプロパティの設定を示しています。
public static bool CreateApplicationPool(string applicationPoolName, ProcessModelIdentityType identityType, string applicationPoolIdentity, string password, string managedRuntimeVersion, bool autoStart, bool enable32BitAppOnWin64,ManagedPipelineMode managedPipelineMode, long queueLength, TimeSpan idleTimeout, long periodicRestartPrivateMemory, TimeSpan periodicRestartTime)
{
try
{
if (identityType == ProcessModelIdentityType.SpecificUser)
{
if (string.IsNullOrEmpty(applicationPoolName))
throw new ArgumentNullException("applicationPoolName", "CreateApplicationPool: applicationPoolName is null or empty.");
if (string.IsNullOrEmpty(applicationPoolIdentity))
throw new ArgumentNullException("applicationPoolIdentity", "CreateApplicationPool: applicationPoolIdentity is null or empty.");
if (string.IsNullOrEmpty(password))
throw new ArgumentNullException("password", "CreateApplicationPool: password is null or empty.");
}
using (ServerManager mgr = new ServerManager())
{
ApplicationPool newAppPool = mgr.ApplicationPools.Add(applicationPoolName);
if (identityType == ProcessModelIdentityType.SpecificUser)
{
newAppPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
newAppPool.ProcessModel.UserName = applicationPoolIdentity;
newAppPool.ProcessModel.Password = password;
}
else
{
newAppPool.ProcessModel.IdentityType = identityType;
}
if (!string.IsNullOrEmpty(managedRuntimeVersion))
newAppPool.ManagedRuntimeVersion = managedRuntimeVersion;
newAppPool.AutoStart = autoStart;
newAppPool.Enable32BitAppOnWin64 = enable32BitAppOnWin64;
newAppPool.ManagedPipelineMode = managedPipelineMode;
if (queueLength > 0)
newAppPool.QueueLength = queueLength;
if (idleTimeout != TimeSpan.MinValue)
newAppPool.ProcessModel.IdleTimeout = idleTimeout;
if (periodicRestartPrivateMemory > 0)
newAppPool.Recycling.PeriodicRestart.PrivateMemory = periodicRestartPrivateMemory;
if (periodicRestartTime != TimeSpan.MinValue)
newAppPool.Recycling.PeriodicRestart.Time = periodicRestartTime;
mgr.CommitChanges();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
サイトを作成する
サイトは、HTTP 要求の受信と処理の方法を指定する最上位の論理コンテナーです。 サイトは、サイトが着信要求をリッスンする方法を決定するバインドのグループを定義します。また、サイトには、アプリケーション コンテンツを構成するためにサイトの URL 名前空間を分割するアプリケーションまたは仮想ディレクトリの定義が含まれています。
次のコード フラグメントは、新しいサイトの作成方法を示しています。 ServerManager オブジェクトを実装する場合は、Microsoft.Web.Administration 名前空間が必要です。 アプリケーション コレクション、サイト コレクション、バインド オブジェクトはすべて、ServerManager オブジェクトを介してアクセスされることに注意してください。
public static bool CreateWebSite(string siteName)
{
try
{
if (string.IsNullOrEmpty(siteName))
{
throw new ArgumentNullException("siteName", "CreateWebSite: siteName is null or empty.");
}
//get the server manager instance
using (ServerManager mgr = new ServerManager())
{
Site newSite = mgr.Sites.CreateElement();
//get site id
newSite.Id = GenerateNewSiteID(mgr, siteName);
newSite.SetAttributeValue("name", siteName);
mgr.Sites.Add(newSite);
mgr.CommitChanges();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
バインディングを作成する
バインディングは、プロトコル名とプロトコル固有のバインディング情報の組み合わせです。 IIS 7 ではマルチプロトコル バインディング (Windows® Communication Foundation [WCF] SOAP-TCP や FTP など) がサポートされていますが、後に続くコードでは HTTP パスのみを使用します。HTTP バインディングは、特定のインターフェイス IP アドレス (またはすべてのインターフェイス)、特定のポート番号、または特定の HTTP ホスト ヘッダー (またはすべてのホスト ヘッダー) でリッスンする HTTP エンドポイントを実質的に定義します。 この方法で、異なる IP アドレス、異なるポートでリッスンするサイト、または同じ IP アドレスまたはポートで、ただしホスト ヘッダーが異なるサイトを多数、サーバー上で構成できます。
public static bool AddSiteBinding(string siteName, string ipAddress, string tcpPort, string hostHeader, string protocol)
{
try
{
if (string.IsNullOrEmpty(siteName))
{
throw new ArgumentNullException("siteName", "AddSiteBinding: siteName is null or empty.");
}
//get the server manager instance
using (ServerManager mgr = new ServerManager())
{
SiteCollection sites = mgr.Sites;
Site site = mgr.Sites[siteName];
if (site != null)
{
string bind = ipAddress + ":" + tcpPort + ":" + hostHeader;
//check the binding exists or not
foreach (Binding b in site.Bindings)
{
if (b.Protocol == protocol && b.BindingInformation == bind)
{
throw new Exception("A binding with the same ip, port and host header already exists.");
}
}
Binding newBinding = site.Bindings.CreateElement();
newBinding.Protocol = protocol;
newBinding.BindingInformation = bind;
site.Bindings.Add(newBinding);
mgr.CommitChanges();
return true;
}
else
throw new Exception("Site: " + siteName + " does not exist.");
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
ルート アプリケーションを作成する
アプリケーションは Web サイト機能の論理コンテナーであり、サイト URL 名前空間を別々の部分に分割し、各部分のランタイムの動作を個別に制御できるようにします。
たとえば、別々のアプリケーション プールに存在するように各アプリケーションを構成できます。 これにより、アプリケーションを別々のプロセスに配置し、必要に応じてそのプロセスを別の Windows ID で実行させてサンドボックス化することで、アプリケーションが他のアプリケーションから分離されます。
public static bool AddApplication(string siteName, string applicationPath, string applicationPool, string virtualDirectoryPath, string physicalPath, string userName, string password)
{
try
{
if (string.IsNullOrEmpty(siteName))
throw new ArgumentNullException("siteName", "AddApplication: siteName is null or empty.");
if (string.IsNullOrEmpty(applicationPath))
throw new ArgumentNullException("applicationPath", "AddApplication: application path is null or empty.");
if (string.IsNullOrEmpty(physicalPath))
throw new ArgumentNullException("PhysicalPath", "AddApplication: Invalid physical path.");
if (string.IsNullOrEmpty(applicationPool))
throw new ArgumentNullException("ApplicationPool", "AddApplication: application pool namespace is Nullable or empty.");
using (ServerManager mgr = new ServerManager())
{
ApplicationPool appPool = mgr.ApplicationPools[applicationPool];
if (appPool == null)
throw new Exception("Application Pool: " + applicationPool + " does not exist.");
Site site = mgr.Sites[siteName];
if (site != null)
{
Application app = site.Applications[applicationPath];
if (app != null)
throw new Exception("Application: " + applicationPath + " already exists.");
else
{
app = site.Applications.CreateElement();
app.Path = applicationPath;
app.ApplicationPoolName = applicationPool;
VirtualDirectory vDir = app.VirtualDirectories.CreateElement();
vDir.Path = virtualDirectoryPath;
vDir.PhysicalPath = physicalPath;
if (!string.IsNullOrEmpty(userName))
{
if (string.IsNullOrEmpty(password))
throw new Exception("Invalid Virtual Directory User Account Password.");
else
{
vDir.UserName = userName;
vDir.Password = password;
}
}
app.VirtualDirectories.Add(vDir);
}
site.Applications.Add(app);
mgr.CommitChanges();
return true;
}
else
throw new Exception("Site: " + siteName + " does not exist.");
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
仮想ディレクトリを作成する
仮想ディレクトリは、アプリケーション URL 名前空間の一部をディスク上の物理的な場所にマップします。 要求がアプリケーションにルーティングされると、同じアルゴリズムを使用して、アプリケーション パス後の要求の絶対パスの残りと一致する最も長い仮想パスを持つ仮想ディレクトリを検出します。
public static bool AddVirtualDirectory(string siteName, string application, string virtualDirectoryPath, string physicalPath, string userName, string password)
{
try
{
if (string.IsNullOrEmpty(siteName))
throw new ArgumentNullException("siteName", "AddVirtualDirectory: siteName is null or empty.");
if (string.IsNullOrEmpty(application))
throw new ArgumentNullException("application", "AddVirtualDirectory: application is null or empty.");
if (string.IsNullOrEmpty(virtualDirectoryPath))
throw new ArgumentNullException("virtualDirectoryPath", "AddVirtualDirectory: virtualDirectoryPath is null or empty.");
if (string.IsNullOrEmpty(physicalPath))
throw new ArgumentNullException("physicalPath", "AddVirtualDirectory: physicalPath is null or empty.");
using (ServerManager mgr = new ServerManager())
{
Site site = mgr.Sites[siteName];
if (site != null)
{
Application app = site.Applications[application];
if (app != null)
{
VirtualDirectory vDir = app.VirtualDirectories[virtualDirectoryPath];
if (vDir != null)
{
throw new Exception("Virtual Directory: " + virtualDirectoryPath + " already exists.");
}
else
{
vDir = app.VirtualDirectories.CreateElement();
vDir.Path = virtualDirectoryPath;
vDir.PhysicalPath = physicalPath;
if (!string.IsNullOrEmpty(userName))
{
if (string.IsNullOrEmpty(password))
throw new Exception("Invalid Virtual Directory User Account Password.");
else
{
vDir.UserName = userName;
vDir.Password = password;
}
}
app.VirtualDirectories.Add(vDir);
}
mgr.CommitChanges();
return true;
}
else
throw new Exception("Application: " + application + " does not exist.");
}
else
throw new Exception("Site: " + siteName + " does not exist.");
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
FTP サイトを作成する
FTP サイトを使用すると、お客様は Web サイトにコンテンツをアップロードでき、インターネット経由でファイルを移動できます。 お客様はコンテンツとアクセスの両方を管理できます。 FTP サイトの作成は、Web サイトの作成と似ています。アプリケーション プールとサイトはルート アプリケーションと仮想ディレクトリと一緒に作成されてから、FTP バインドが適用されます。
public static bool CreateFtpSite(string applicationPoolName,string siteName, string domainName, string userName, string password,string contentPath, string ipAddress, string tcpPort, string hostHeader)
{
try
{
//provision the application pool
using (ServerManager mgr = new ServerManager())
{
ApplicationPool appPool = mgr.ApplicationPools[applicationPoolName];
//per IIS7 team recommendation, we always create a new application pool
//create new application pool
if (appPool == null)
{
appPool = mgr.ApplicationPools.Add(applicationPoolName);
//set the application pool attribute
appPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
appPool.ProcessModel.UserName = domainName + "\\" + userName;
appPool.ProcessModel.Password = password;
}
//if the appPool is null, we throw an exception. The appPool should be created or already exists.
if (appPool == null)
throw new Exception("Invalid Application Pool.");
//if the site already exists, throw an exception
if (mgr.Sites[siteName] != null)
throw new Exception("Site already exists.");
//create site
Site newSite = mgr.Sites.CreateElement();
newSite.Id = GenerateNewSiteID(mgr, siteName);
newSite.SetAttributeValue("name", siteName);
newSite.ServerAutoStart = true;
mgr.Sites.Add(newSite);
//create the default application for the site
Application newApp = newSite.Applications.CreateElement();
newApp.SetAttributeValue("path", "/"); //set to default root path
newApp.SetAttributeValue("applicationPool", applicationPoolName);
newSite.Applications.Add(newApp);
//create the default virtual directory
VirtualDirectory newVirtualDirectory = newApp.VirtualDirectories.CreateElement();
newVirtualDirectory.SetAttributeValue("path", "/");
newVirtualDirectory.SetAttributeValue("physicalPath", contentPath);
newApp.VirtualDirectories.Add(newVirtualDirectory);
//add the bindings
Binding binding = newSite.Bindings.CreateElement();
binding.SetAttributeValue("protocol", "ftp");
binding.SetAttributeValue("bindingInformation", ipAddress + ":" + tcpPort + ":" + hostHeader);
newSite.Bindings.Add(binding);
//commit the changes
mgr.CommitChanges();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}