Compartir a través de


Ejemplo de aprovisionamiento en C#

por Walter Oliver

Información general

Con el paso de los años, la creación de un sitio web propio por parte de los usuarios web avanzados ha pasado a ser un proceso sencillo. Una vez que un usuario final ha registrado su nombre de dominio, hay varios tipos de proveedor de servicios de hosting entre los que se puede elegir. Existen proveedores de servicios de hosting web disponibles en todo el mundo para satisfacer las demandas de los clientes finales. Pero independientemente del proveedor de servicios de hosting elegido, el escenario de registro y creación de un sitio web es similar en todos los casos.

Inicialmente, es necesario establecer una nueva cuenta de usuario para el usuario. Una vez configurada la cuenta, el usuario final decide qué características y opciones debe incorporar el sitio: por ejemplo, espacio necesario en disco, capacidad de FTP, creación de directorios virtuales, si se necesitan o no bases de datos, etc. Los proveedores de servicios de hosting crean paneles de control o aplicaciones de panel que permiten a los usuarios finales crear y administrar estas características.

Hay varias maneras de implementar estas funcionalidades en el panel de control. En esta sección, veremos cómo implementar el aspecto de aprovisionamiento de estas características mediante código administrado. Un esquema de las características es el siguiente:

Aprovisionamiento de una nueva cuenta de usuario

El propietario del sitio que administra y mantiene el sitio necesita una nueva cuenta de usuario. La cuenta puede ser una cuenta de Active Directory® o una cuenta de usuario local. Los fragmentos de código siguientes muestran la creación de una cuenta local. Tenga en cuenta que se debe especificar el espacio de nombres 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;
}

Crear almacenamiento de contenido

Un sitio web requiere una ubicación en los sistemas de archivos a los que los usuarios pueden cargar contenido para el sitio. La clase de directorio de Microsoft® .NET proporciona la interfaz de programación de aplicaciones (API) para crear directorios en el sistema de archivos.

Directory.CreateDirectory(parentPath + "\\" + directoryName);

El almacenamiento de contenido requiere permisos específicos configurados para que los usuarios puedan administrar su propio contenido. En el fragmento de código siguiente se muestra cómo establecer permisos de directorio mediante código administrado en 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;
}

En el fragmento de código siguiente se muestra cómo establecer la cuota de disco mediante código administrado si la cuota de disco está restringida. Para usar la administración de cuotas de disco, debe agregar una referencia al componente de administración de cuotas de disco de Windows®; reside en 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;
}

Crear un grupo de aplicaciones

Un grupo de aplicaciones define la configuración de un proceso de trabajo que hospeda una o varias aplicaciones de Internet Information Services 7 (IIS 7), que llevan a cabo su procesamiento de solicitudes. El grupo de aplicaciones es una unidad de aislamiento de procesos, ya que todo el procesamiento de solicitudes para una aplicación se ejecuta dentro de los procesos de trabajo del grupo de aplicaciones.

Un grupo de aplicaciones también proporciona aislamiento, que a su vez proporciona seguridad. Cada grupo de aplicaciones se puede ejecutar con una identidad única y puede usar una lista de control de acceso (ACL) para evitar que las aplicaciones de otros grupos accedan a sus recursos. En el fragmento de código siguiente se muestra la creación de un grupo de aplicaciones, la configuración de la identidad y la configuración de las propiedades.

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;
 }

Crear un sitio

Un sitio es el contenedor lógico de nivel superior que especifica cómo se reciben y procesan las solicitudes HTTP. Un sitio define un grupo de enlaces que determinan cómo escucha el sitio las solicitudes entrantes, y un sitio contiene las definiciones de aplicaciones o directorios virtuales que particionan el espacio de nombres de la dirección URL del sitio para estructurar el contenido de la aplicación.

En el fragmento de código siguiente se muestra cómo crear un sitio. Los espacios de nombres Microsoft.Web.Administration son necesarios al implementar el objeto ServerManager. Tenga en cuenta que se accede a la colección de aplicaciones, la colección de sitios y los objetos de enlace a través del objeto 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;
}

Crear un enlace

Un enlace es una combinación del nombre del protocolo y la información de enlace específica del protocolo. Aunque IIS 7 admite enlaces de varios protocolos (como Windows® Communication Foundation [WCF] SOAP-TCP y FTP), el código siguiente solo usa la ruta de acceso HTTP; un enlace HTTP define eficazmente un punto de conexión HTTP que escucha en una dirección IP de interfaz específica (o todas las interfaces), un número de puerto específico o un encabezado de host HTTP específico (o todos los encabezados de host). De este modo, puede configurar muchos sitios en el servidor que escuchan en diferentes direcciones IP, puertos diferentes o en la misma dirección IP o puerto, pero con distintos encabezados de host.

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);
    }
}

Creación de una aplicación raíz

Una aplicación es un contenedor lógico para la funcionalidad del sitio web, que permite dividir el espacio de nombres de la dirección URL del sitio en partes independientes y controlar el comportamiento en tiempo de ejecución de cada elemento individualmente.

Por ejemplo, puede configurar cada aplicación para que resida en un grupo de aplicaciones independiente. Esto aísla una aplicación del resto, al colocarla en un proceso independiente y, opcionalmente, hacer que ese proceso se ejecute con una identidad de Windows diferente para poner la aplicación en un espacio aislado.

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);
    }
}

Crear un directorio virtual

Un directorio virtual asigna una parte del espacio de nombres de la dirección URL de la aplicación a una ubicación física en el disco. Cuando una solicitud se enruta a la aplicación, usa el mismo algoritmo para buscar el directorio virtual con la ruta de acceso virtual más larga que coincida con el resto de la ruta de acceso absoluta de la solicitud después de la ruta de acceso de la aplicación.

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);
    }
}

Crear un sitio FTP

Un sitio FTP permite a los clientes cargar contenido en su sitio web y proporciona la capacidad de mover archivos a través de Internet. El cliente puede administrar el contenido y el acceso. La creación de un sitio FTP es similar a la de un sitio web: se crea un grupo de aplicaciones y un sitio con una aplicación raíz y un directorio virtual y, a continuación, se aplica un enlace 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;
}