다음을 통해 공유


C의 프로비저닝 샘플#

작성자 : Walter Oliver

개요

수년에 걸쳐 고급 웹 사용자가 자신의 웹 사이트를 만드는 것이 간단해졌습니다. 최종 사용자가 도메인 이름을 등록하면 선택할 수 있는 다양한 유형의 호스트가 있습니다. 웹 호스트는 최종 고객의 요구를 충족하기 위해 전 세계에서 사용할 수 있습니다. 그러나 어떤 호스트를 선택하든 웹 사이트를 등록하고 만드는 시나리오는 모든 경우에 유사합니다.

처음에는 사용자에 대해 새 사용자 계정을 설정해야 합니다. 계정이 설정되면 최종 사용자는 사이트에서 통합해야 하는 기능과 옵션(instance, 디스크 공간 양, FTP 기능, 가상 디렉터리 만들기, 데이터베이스 필요 여부 등)을 결정합니다. 호스트는 최종 사용자가 이러한 기능을 만들고 관리할 수 있도록 제어판 또는 dashboard 애플리케이션을 빌드합니다.

이러한 기능을 제어판에 구현할 수 있는 방법에는 여러 가지가 있습니다. 이 섹션에서는 관리 코드를 통해 이러한 기능의 프로비저닝 측면을 구현하는 것을 살펴봅합니다. 기능의 개요는 다음과 같습니다.

새 사용자 계정 프로비전

사이트를 관리하고 유지 관리하는 사이트 소유자에게 새 사용자 계정이 필요합니다. 계정은 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;
}

Content Storage 만들기

웹 사이트에는 사용자가 사이트의 콘텐츠를 업로드할 수 있는 파일 시스템의 위치가 필요합니다. Microsoft® .NET Directory 클래스는 파일 시스템에 디렉터리를 만드는 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;
}

애플리케이션 풀 만들기

애플리케이션 풀은 요청 처리를 수행하는 하나 이상의 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);
    }
}

루트 애플리케이션 만들기

애플리케이션은 사이트 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 사이트를 사용하면 고객이 웹 사이트에 콘텐츠를 업로드하고 인터넷을 통해 파일을 이동할 수 있습니다. 고객은 콘텐츠와 액세스를 모두 관리할 수 있습니다. FTP 사이트 만들기는 웹 사이트와 비슷합니다. 애플리케이션 풀과 사이트는 루트 애플리케이션 및 가상 디렉터리를 사용하여 만든 다음 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;
}