IIS7 Web サイト共有ホスティングのためのプロビジョニング用サンプルコード


前回のブログに書いた通り、ホスティング事業者様向けのイベントに向けて、エンドユーザーに IIS7 の Web サイトを管理させるためのプロビジョニング用を作成しています。

セットアップ用のコンテンツはまだ書いていませんが、コードができたのでご紹介しましょう。

このコードは以下の処理を行うためのものです。

1. IIS マネージャーユーザーアカウントを作成
2. Web サイト用の物理フォルダを作成
3. Web (HTTP) サイトを作成
4. 作成した Web サイトに FTP をバインド
5. 作成した Web サイトの [IIS マネージャーのアクセス許可] に作成したIIS マネージャーユーザーアカウントを登録

この一連の処理が完了すると、エンドユーザーは、IIS マネージャーを使用して、指定した自分の Web サイトに、指定した IIS マネージャーユーザーアカウント情報でログインし、Web サイトの管理を行えるようになります。

またコンテンツの更新などは FTP を使用して行えます。

もちろん、サーバー側でしっかり設定しておけば、ユーザーに任意で ASP.NET や、PHP のアプリケーションを実行させることが可能です。

これらの処理を完全に行わせるのは、プログラミングのコードだけではなくて、サーバーの設定も必要ですが、今回は前述の 5 つの機能を実行するサンプルコードのみをご紹介します。

なお、サンプルはクラスライブラリのものですので、そのままでは実行できませんので、適宜アプリケーションに組み込み、管理者アカウントで実行してみてください。

サンプル(C#) : IishostingHepler クラスの定義

using System;
using Microsoft.Web.Administration;
using System.Security.Cryptography;
using System.Text;

namespace IisHostingHepler
{
    public class Provisoning
    {

#region Webサイト関連の処理

        //Web サイト用のフォルダを作成する
        public Exception CreatePhysicalPath(string physicalPath)
        {
            try
            {
                System.IO.DirectoryInfo dirInf = new System.IO.DirectoryInfo(physicalPath);
                if (!dirInf.Exists)
                {
                    dirInf.Create();
                }
                return null;
            }
            catch (Exception ex)
            {
                return ex;
            }
        }

        //Web サイトの作成(アプリケーションプールはDefaultAppPool が設定される)
        public Exception CreateWebSite(string siteName, string physicalPath, string ipAddress, string portNumber, string hostName)
        {
            Exception returnExp = null;
            string bindingInformation = (ipAddress == "") ? "*:" + portNumber + ":" + hostName : ipAddress + ":" + portNumber + ":" + hostName;

            try
            {
                ServerManager serverManager = new ServerManager();

                Site mySite = serverManager.Sites.Add(siteName, "http", bindingInformation, physicalPath);

                mySite.ServerAutoStart = true;
                serverManager.CommitChanges();
            }
            catch (Exception exp)
            {
                returnExp = exp;
            }
            return returnExp;
        }

        //指定された名前の Web サイトがすでにあるか確認する
        public bool ExistWebSite(string siteName)
        {
            ServerManager serverManager = new ServerManager();
            foreach (Site site in serverManager.Sites)
            {
                if (String.Equals(site.Name, siteName, StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }
            }
            return false;
        }

        //既存の Web サイトに FTP バインドを追加
        public Exception AddFtpBind(string siteName, string ipAddress, string portNumber, string hostName, string accountName, string permType,
            string serverCertHash, string sslCtrlPolicy, string sslDataPolicy)
        {
            Exception returnExp = null;
            string bindingInformation = (ipAddress == "") ? "*:" + portNumber + ":" + hostName : ipAddress + ":" + portNumber + ":" + hostName;

            try
            {
                using (ServerManager serverManager = new ServerManager())
                {
                    Site site = serverManager.Sites[siteName];

                    // FTP のバインド情報を追加
                    site.Bindings.Add(bindingInformation, @"ftp");

                    ConfigurationElement ftpServerElement = site.GetChildElement("ftpServer");

                    ConfigurationElement securityElement = ftpServerElement.GetChildElement("security");

                    //SSL の設定
                    ConfigurationElement sslElement = securityElement.GetChildElement("ssl");

                    //証明書のハッシュが設定されていない場合は、とりあえずなにもしない
                    if (serverCertHash != "") sslElement["serverCertHash"] = serverCertHash;
                    sslElement["controlChannelPolicy"] = sslCtrlPolicy;
                    sslElement["dataChannelPolicy"] = sslDataPolicy;

                    //基本認証の設定
                    ConfigurationElement authenticationElement = securityElement.GetChildElement("authentication");
                    ConfigurationElement basicAuthenticationElement = authenticationElement.GetChildElement("basicAuthentication");
                    basicAuthenticationElement["enabled"] = true;

                    //IIS 管理ユーザー用 カス���ム認証プロバイダーの設定
                    ConfigurationElement customAuthenticationElement = authenticationElement.GetChildElement("customAuthentication");
                    ConfigurationElement customAuthenticationPoviderElement = customAuthenticationElement.ChildElements["providers"];
                    ConfigurationElementCollection providers = customAuthenticationPoviderElement.GetCollection();
                    ConfigurationElement addElement = providers.CreateElement("add");
                    addElement["name"] = "IisManagerAuth";
                    addElement["enabled"] = "true";
                    providers.Add(addElement);

                    // Add Authorization Rules
                    Configuration appHost = serverManager.GetApplicationHostConfiguration();
                    ConfigurationSection authorization = appHost.GetSection("system.ftpServer/security/authorization", site.Name);
                    ConfigurationElementCollection authorizationRules = authorization.GetCollection();
                    ConfigurationElement authElement = authorizationRules.CreateElement();
                    authElement["accessType"] = "Allow";
                    authElement["users"] = accountName;
                    authElement["permissions"] = permType;
                    authorizationRules.Add(authElement);

                    serverManager.CommitChanges();
                }
            }
            catch (Exception exp)
            {
                returnExp = exp;
            }
            return returnExp;
        }

#endregion

#region 管理者アカウントの処理

        //サイトの管理用アカウントを生成する
        public Exception CreateAdminAccount(string userName, string passWord)
        {
            Exception returnExp = null;
            try
            {
                using (ServerManager serverManager = new ServerManager())
                {
                    Configuration config = serverManager.GetAdministrationConfiguration();

                    ConfigurationSection authenticationSection = config.GetSection("system.webServer/management/authentication");
                    ConfigurationElementCollection credentialsCollection = authenticationSection.GetCollection("credentials");
                    ConfigurationElement addElement = credentialsCollection.CreateElement("add");
                    addElement["name"] = userName;
                    addElement["password"] = GetHashPwssword(passWord);
                    //addElement["enabled"] = true;
                    credentialsCollection.Add(addElement);
                    serverManager.CommitChanges();
                }
            }
            catch (Exception exp)
            {
                returnExp = exp;
            }
            return returnExp;
        }

        //管理用アカウントがすでに存在するか確認する
        public bool ExistAdminAccount(string userName)
        {
            using (ServerManager serverManager = new ServerManager())
            {
                Configuration config = serverManager.GetAdministrationConfiguration();
                ConfigurationSection authenticationSection = config.GetSection("system.webServer/management/authentication");
                ConfigurationElementCollection credentialsCollection = authenticationSection.GetCollection("credentials");
                foreach (ConfigurationElement conf in authenticationSection.GetCollection("credentials"))
                {
                    if (conf.Attributes["name"].Value.ToString() == userName)
                    {
                        return true;
                    }
                }
                return false;
            }
        }

        //管理アカウントで Web サイトにアクセス可能とする
        public Exception AllowLogon(string siteName, string adminName)
        {
            Exception returnExp = null;

            try
            {
                siteName = @"/" + siteName;
                using (ServerManager serverManager = new ServerManager())
                {
                    Configuration config = serverManager.GetAdministrationConfiguration();
                    ConfigurationSection authorizationSection = config.GetSection("system.webServer/management/authorization");
                    ConfigurationElementCollection authorizationRulesCollection = authorizationSection.GetCollection("authorizationRules");

                    ConfigurationElement scopeElement = FindElement(authorizationRulesCollection, "scope", siteName);
                    if (scopeElement == null)
                    {
                        scopeElement = authorizationRulesCollection.CreateElement("scope");
                        scopeElement["path"] = siteName;
                        authorizationRulesCollection.Add(scopeElement);
                    }
                   
                    ConfigurationElementCollection scopeCollection = scopeElement.GetCollection();

                    //すでにアカウントが登録されていたら処理を抜ける
                    if(ExistAccount(scopeCollection,adminName)) return returnExp;

                    ConfigurationElement addElement = scopeCollection.CreateElement("add");
                    addElement["name"] = adminName;
                    scopeCollection.Add(addElement);
                    serverManager.CommitChanges();
                }
            }
            catch (Exception exp)
            {
                returnExp = exp;
            }

            return returnExp;
        }

        //すでにアカウントが登録されているかどうかチェック
        private bool ExistAccount(ConfigurationElementCollection scopeCollection, string AccountName)
        {
            foreach (ConfigurationElement confElement in scopeCollection)
            {
                if (confElement.Attributes["Name"].Value.ToString() == AccountName) return true;
            }
            return false;
        }

        //<scope> 要素が Web サイト の <authorizationRules> 要素に既に追加されているかどうかを確認するコード。※新規作成の際には必要ないはずであるが念のため
        private static ConfigurationElement FindElement(ConfigurationElementCollection collection, string elementTagName, string keyValues)
        {
            foreach (ConfigurationElement element in collection)
            {
                if (String.Equals(element.ElementTagName, elementTagName, StringComparison.OrdinalIgnoreCase))
                {
                    if (string.Equals(element.Attributes["path"].Value.ToString(), keyValues, StringComparison.OrdinalIgnoreCase))
                    {
                        return element;
                    }
                }
            }
            return null;
        }

        //パスワードを SHA256 でハッシュする
        public string GetHashPwssword(string passWord)
        {
            SHA256 sha = SHA256.Create();
            byte[] hashBytes = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(passWord));

            byte f1 = 0xf0;
            byte f2 = 0x0f;
            string hexString = "";

            foreach (byte b in hashBytes)
            {
                int first4 = (b & f1) >> 4;
                int second4 = (b & f2);
                hexString = hexString + ((first4 > 9) ? (char)(‘A’ + (first4 – 10)) : (char)(‘0’ + first4));
                hexString = hexString + ((second4 > 9) ? ((char)(‘A’ + (second4 – 10))) : (char)(‘0’ + second4));
            }
                return hexString;  
            }
    }

#endregion

}

サンプル(C#):アプリケーション側の呼び出し例

//Provisoning クラスのインスタンスを生成
Provisoning Prov = new Provisoning();
Exception exp = null;

            //管理用ユーザーアカウントを作成
            exp = Prov.CreateAdminAccount(userInfo.userName, userInfo.password);
            if (exp != null) return exp.Message;

            //Web サイト用の物理パスを作成
            string newPhysicalPath = webSiteInfo.physicalPath + "\\" + webSiteInfo.siteName;
            exp = Prov.CreatePhysicalPath(newPhysicalPath);
            if (exp != null) return exp.Message;

            //Web サイトを作成
            exp = Prov.CreateWebSite(webSiteInfo.siteName, newPhysicalPath, webSiteInfo.ipAddress, webSiteInfo.portNumber, webSiteInfo.hostName);
            if (exp != null) return exp.Message;

            //Web サイトに FTP をバインド
            exp = Prov.AddFtpBind(webSiteInfo.siteName, ftpSiteInfo.ipAddress, ftpSiteInfo.portNumber, ftpSiteInfo.hostName, userInfo.userName, "Read,Write", "", "SslAllow", "SslAllow");
            if (exp != null) return exp.Message;

            //Web サイトに管理ユーザーアカウントを登録
            exp = Prov.AllowLogon(webSiteInfo.siteName, userInfo.userName);
            if (exp != null) return exp.Message;

管理者アカウントでの実行については、Web アプリケーションの場合は、WCF あるいは ASP.NET Web サービスとして作成し、アプリケーションを管理者権限で動作させるのが簡単です。また、その際には IIS 側の設定で、localhost (127.0.0.1) からの接続にのみ実行するようにしておけばセキュリティが保たれます。

Windows フォームアプリケーションに組み込んで検証を行う場合は、アプリケーションの実行時に管理者アカウントを使用するだけで結構です。

なお、このコードは、Windows 7, Windows Vista のクライアント OS 用の IIS7.x でも実行できますが、これらクライアント OS に付属する IIS マネージャーでは、[管理サービス] に関する設定を確認する機能が搭載されていないので、コードの実行結果を確認するには、administration.config ファイル、applicationHost.config ファイルの内容を直接確認する必要があります。

クライアント OS の IIS7.x において、これらの設定を確認/更新する機能は Microsoft.Web.Administration、Microsoft.Web.Management を使用して作成が可能と思われるので、ニーズがあるようであれば、IIS7.x のアドインとして作成してみようかな、なんて思っています。

また、作成中のプロビジョニングページの実際のサンプルプロジェクトは、後日、配置/検証の手順書と合わせてこのブログで公開予定ですので、興味のある方はお楽しみに。

――― ・ ――― ・ ――― ・ ――― ・ ――― ・ ――― ・ ――― ・ ――― ・ ―――

おしらせ

Windows Server を操る IT Pro の熱い戦い INSTALL MANIAX の第三弾が "Hyper-V 祭り" と題して年末年始に開催されることになりました。


http://www.thinkit.co.jp/maniax/3/index.html

本コンテストは、IIS へのオープンソース・アプリケーションのインストール数を競うという非常にシンプルな協議内容で、参加者にはサーバーマシンと Windos Server OS が無償提供されます。

なんと今回は決勝戦が海外で行われるという噂もちらほら….。(あくまでも噂です)

"ぅおおっ、インストールならまかせんしゃい!!" という、腕に覚えのあるそこのアナタ、ふるってご応募してみませんか?

 

Real Time Analytics

Clicky

Comments (0)

Skip to main content