Azure: ¿Qué es? ¿Cómo? y ¿Por donde comenzar?

Windows Azure Big

Azure va a dar mucho que hablar así, que espero estar por aquí algo más de lo habitual.

Ya en un post anterior comenté algún tema curioso sobre Azure, en este caso en concreto me gustaría dejar un resumen a grandes rasgos de manera que sirva de orientación para el comienzo con esta nueva gran “area de desarrollo”, principalmente para aquellos que aún no conocen poco o nada sobre Azure.

¿Qué es Azure? y, concretamente, ¿Qué es Windows Azure Platform?

Antes de dar ninguna explicación, me gustaría hacer mención sobre como equiparar a Azure con otros temas históricos de nuestra vida y de los que actualmente nos estamos beneficiando. Este simile aunque ya utilizado en más de una presentación creo que aclara bastante:   “La electricidad, el gas, etc, los tenemos al alcance de nuestra mano con un servicio. ¿Por qué no hacer lo mismo con la infraestructura de ordenadores y pagar por un servicio prestado? De esta manera cada “uno” se dedica única y exclusivamente a su negocio”.   Con el tiempo, hay una menor inversión y se produce un alto ROI.

Windows Azure es un entorno de “Cloud Computing” basado en Internet, es decir, un Sistema Operativo basado en Internet.  Una plataforma para el desarrollo de aplicaciones .net en la “nube”, todo ello apoyado en data centers repartidos por todo el mundo.

Una imagen muy buena que define o da respuesta a esta pregunta es la siguiente. ¡ Creo que hay poco más que contar para una breve respuesta!

Microsoft Platform

Esta plataforma esta formada por:

  • Windows Azure: Computación y capacidad de almacenamiento en la nube. Basicamente un OS.
  • App Fabric: Bus de datos y Control de acceso. Básicamente un ESB (Enterprise Service Bus).
  • SQL Azure: SQL Server para la nube.

Todo ello con algunas restricciones y algunas diferencias que ya intentaré ir contando poco a poco.

Adicionalmente, cuando comencemos a estudiar y a intentar aprender Azure, encontraremos muchos acronimos que es conveniente tener presente desde el principio y que yo creo que pueden acortar dicho tiempo:

  • IaaS: Infraestructure as a Service. Elasticidad y facilidad  para subir máquinas a la nube; maquinas virtuales (WM Ware), Amazon, …
  • PaaS: Platform as a Services. Plataforma para la ejecución de software/servicios a medida; Windows Azure, Google App Engine, …
  • SaaS: Software as a Services. Productos acabados. Windows Live, Bing, Sharepoint, Twitter, faceboos, Gmail, …
  • Windows Azure Appliance: Contenedores de máquinas de Azure administrados por la nube, y que van a encontrarse físicamente en casa del cliente, (éste, los quiere cerca, poque “no se fía”).
  • On-premise: Sistema Software “No-Cloud”.

Azure se identifica claramentecon algunos adjetivos; Elasticidad, flexibilidad y “On-Demand” (En el Cloud, se paga por lo que se usa).  Pincipalmente lo definiría como “Self-Service”.

Una vez  entendido que es Windows Azure Platform, he aquí un gráfico identificatio (sacado de uno de los correspondientes White Papers), aunque por el momento no entraremos en detalle:

image

Conviene conocer también que existe una API “REST” (tecnología basada en HTTP y URI), que es utilizada por Azure y que, aunque aparentemente es trasparente, su uso es posible de forma explicita incluso para otros lenguajes para el uso con componentes de la plataforma azure:

WindowsAzureStorageServices

Adicionalmente y de cara a la seguirdad tendremos que entender algunos terminos que aunque ya exsten hace tiempo, se le comenzará a dar más uso en y con la nube;

  • WIF (Windows Identity Foundation); WS_Trust y WS-Federation.
  • Claims: Es “Algo” que identifica a un usuario; el UserId, el Pwd, la edad, un conjunto de estas, etc.
  • STS (Security Token Service); Issue, Renew, Validate, Cancel
  • ADSF 2.0 (Active Directory Federation Services)
  • Y mucho más…

A partir de ahora no tiene por que ir todo a la nube, cada cosa irá en su sitio, exisiten aplicaciones idóneas para la nube mientras que otras no:

Idóneas:

  • Requieren escalabilidad; interet
  • Estacionales; Loterías, rebajas, etc
  • Con picos; Ofertas, vuelos, etc
  • De crecimiento exponencial generado por los usuarios; facebook, flickr, etc
  • etc…

No idóneas:

  • Lineales, sin perspectiva de crecer.
  • Cotrol absoluto del entorno.
  • Alta sensibilidad en los datos; datos financieros.

Adicionalmente agradecer anuestro compañero de Geeks Ibon Landa, su recapitulación sobre el inicio con Windows Azure Platform.

Hasta aquí y en pocas palabras, un resumén o recapitulación y punto de partida hacia este gran y maravilloso mundo. En los siguientes post directo al grano con VS 2010 y Windows Azure Cloud Services.

Saludos desde nube
Juanlu

Azure está llegando, ¡Y de que manera!

Windows Azure Big

Muy buenas a todos,

Windows Azure está ya aquí rondando como ya sabéis y parece ser que no va a parar. Aunque aún estoy un poco “espeso” en la parte técnica a pesar de haber hecho algun que otro ejemplo y ser capaz de captar su potencia, por el momento os dejo algunos vídeos que os pueden dar una idea de lo que se mueve por “ahí fuera” alrededor de Azure, merece la pena echarles un vistazo: Videos sobre Windows Azure.

Algunas novedades:

Y, por otro frente tambien se aproxima “Windows Intune

Azufresaludos
Juanlu

¿Por qué editar manualmente un .msi de 64bits una vez generado?

SetupProject

Muy buenas o no tan buenas si es que acabas de llegar de las vacaciones ! 🙁

En esta ocasión y continuando con el tema de alguno de mis posts anteriores sobre los Setups (.msí) quiero comentar un pequeño truco para generar .msi en 64bits de forma automática.

Si habéis trabajado en la generación de “.msi” de 64bits y haciendo uso de las “Custom Actions”, habéis podido observar que una vez generado este, si se intenta instalar en una máquina de 64bits (a pesar de haber sido generado en 64bits), se obtiene el siguiente error:

Setup_Install_Error

¿Por que ocurre esto si realmente hemos generado un .msi de 64bits y lo estamos instalando en una maquina de 64?

Para dar solución a este tema tendremos que hacer uso de la herramienta “Orca” (o de alguna otra similar), ya postee sobre ella en mis inicios (“ORCA” – Windows Installer Table Editor) y mira por donde hoy, vuelvo a hacerle mención, ¡algo bueno tendrá!. Pues bien, esta herramienta nos va a permitir modificar uno de los valores de nuestro .msi con objeto de evitar este error debibo, parece ser a un “bug”, y que llevan ya tiempo sin querer solucionar. Para ello:

  • Siguiendo los pasos de mi anterior post “Compilando en 32 o en 64 bits”, bastará con que compilemos el proyecto “Setup1” para generar el fichero “Setup1.msi”.
  • Desde el “Orca” abrir “Setup1.msi” y buscar (Ctrl+F) “InstallUtil”.
  • Una vez localizado dicho valor, lo modificamos para que pase a tener este otro “C:WindowsMicrosoft.NETFramework64v2.0.50727InstallUtilLib.dll”.  Como estaremos compilando en una máquina de 32bits no existirá esta ruta, no hay problema, la creamos manualmente y copiamos en ella la dll (InstallUtillLib.dll) que podemos conseguir de cualquier máquina que tenga instalado el framework de 64bits. Esto es necesario porque el Orca valida la existencia del fichero. “No lo he probado, pero me atrevería a segurar que podemos engañar al Orca creando un fichero de texto en blando y renombrandolo con este nombre sin necesidad de tener que tener dicha dll”).

Orca_InstallUtil

  • Ahora el .msi puede ser ejecutado sin errores en una máquina de 64bits.

 

Adicionalmente y con objeto de hacer esta labor de manera totalmente automática, seguiremos los siguientes pasos:

  • Generamos una pequeña aplicación de consola y añadimos una referencia COM “Microsoft Windows Installer Object Library”.
  • Añadimos a nuestra clase “program.cs” entre otro, el siguiente código:

            …Type classType = Type.GetTypeFromProgID(“WindowsInstaller.Installer“);
            Object installerClassObject = Activator.CreateInstance(classType);
            WindowsInstaller.Installer i = (WindowsInstaller.Installer)installerClassObject;

           
            WindowsInstaller.Database db = i.OpenDatabase(MSIFileSpec, WindowsInstaller.MsiOpenDatabaseMode.msiOpenDatabaseModeTransact);
            Console.WriteLine(“Running SQL Query for InstallUtil in the Binary MSI table“);

            // NOTE: The ` is correct in the SQL statement below – it is not ” or ‘
            WindowsInstaller.View v = db.OpenView(“SELECT `Name`,`Data` FROM `Binary` where `Binary`.`Name` = ‘InstallUtil’“);
            v.Execute(null);
            WindowsInstaller.Record Record = v.Fetch();
            if (Record != null)
            {
                Console.WriteLine(“Updating the Binary Data for InstallUtil“);
                Record.SetStream(2, InstallUtil64BitFileSpec);
                v.Modify(WindowsInstaller.MsiViewModify.msiViewModifyUpdate, Record);               
                ChangesMade = true;
            }
            else
            {
                Console.WriteLine(“Error : InstallUtil not found in the Binary MSI Table“);
            }
            v.Close();
            if (ChangesMade)
            {
                Console.WriteLine(“Commiting the changes to the database“);
                db.Commit();
            }….

Nota: Como se puede ver, lo que finalmente va a hacer nuestra aplicación de consola es exactamente lo mismo que hemos comentado anteriormente con nuestro amiga “Orca”.

  • Dotamos a la aplicación de consola del tratamiendo de un parámetro de entrada (ruta del “.msi” a editar).
  • Y, finalmente, solo nos queda tener ubicada esta aplicación en un sitio común para que todos nuestros proyecto de Setup la puedan utilizar:
    • Editamos la propiedad “PostBuildEvent” de cada uno de nuestros poryectos de setup y damos el valor “<AplicacionConsola> ruta_msi”.
    • El .msi generado ahora, ya puede instalarse directamente en una máquina de 64bits sin tener que hacer uso de Orca.

Nota: Si se genera un .msi de 64bits desde un entorno de desarrollo de 64bits esto no llega a ocurrir.

Os dejo aquí (“elGuerre.InstallUtilEditor.zip”) la aplicación de consola para que podáis utilizarla directamente.

Descarga “Orca” desde aquí.

Saludos
Juanlu

Forzando el “Merge” cuando hay problemas

Buenas,

He aquí, una anécdota del día a día:

Hoy estaba intentando hacer un “merge”, ¡la verdad, nada complicado, pero, cuanto más fácil parece, más “enrevesado” e incrompresible resulta! 😀

Después de no sé cuantos intentos, y de seguir diferentes caminos para intentar conseguirlo, nada de nada, los cambios se resisten.

Problema: Existen muchos cambios a llevar a cabo en el merge pero no se hace efectivo ninguno. Realizas comparaciones de ficheros y son distintos, pero nada, que el “merge” no reconoce los cambios.

Solución: Desde la línea de comandos de Visual Studio, ejecutar la siguiente instrucción: 

tf merge /baseless “LocalPath-1” “LocalPath-2” /recursive

Y, después de la resolución de algún conflicto, “zasssss…” problema resuelto.

Referencia: “http://msdn.microsoft.com/en-us/library/bb668976.aspx

Saludos @3Cantos

Juanlu

“Enterprise Library 5.0” Lista para ser descargada

image 

Muy buenas a tod@s,

Ya está lista la Release de lla Entlib 5.0 para ser descargada.

  • Soporte para .Net Framework 4.0 e Integración en Visual Studio 2010
  • Integración con WPF
  • Consolas de configuración  de todos los tipos
    • Para 32 y 64 bits
    • Para 32 y 64 bits con el Framework 4.0
  • Y mucho más………….

Todo, desde aquí (EntLib 5.0).

Saludos y a disfrutarla, 😉
Juanlu

Compilando en 32 o en 64 bits

 

Últimamente me he encontrado con dudas y preguntas que se plantean algunos compañeros sobre la compilación en 64bits.  En ocasiones, me he encontrado cosas un poco “liososas” al respecto, pero,  sinceramente, despues de hacer un pequeño ejemplo para chequearlo, veo que no parece tan complicado, o al menos eso creo yo.

Veamos ese ejemplo “chorras” pero que una vez más nos da la solución a la duda

  • Crear una solución Visual Studio “Basic Deploy x32 x64”
  • Añadir un proyecto de tipo Consola
  • Añadir otro proyecto de tipo “Setup” según se muestra en la siguiente figura:

image

  • Ahora nuestra solución quedará de la siguiente manera:

image

  • Lo siguiente es crear una nueva plataforma de compilación “x64” tal y como sigue:
    • Hacer click sobre la solución y seleccionar “Configuration Manager…

          image

  •  
    • A continuación podemos eligir entre copiar la configuración de una plataforma ya existente o vacía (“<Empty>”). La ventaja de utilizar una existente (x86, que siempre está present), tenemos la ventaja de que las opciones de compilación para “Debug” y “Release” están listas. 

          image

  • Ahora ya está todo listo, ajustamos la configuración; “Debug” y “Release” si no lo hemos hecho en el paso anterior, es decir en caso de haber creado la plataforma a partir de una vacía. Para ello tendremos que seleccionar en cada proyecto y para cada uno de los tipos de configuración si queremos marcar, “DEBUG”, “TRACE”, etc… Entre otras cosas, es posible que nos interese cambiar el “Output path”, sino, por defecto la compilación en 32 bits se realizará en “bindebug” y  la de 64 en “binx64debug”.
  • Si ejecutamos el proyecto en 32 bits y observamos el c podemos observar como las Dlls utilizadas; Sytem.Core, System.Data, etc son las de 32 bits.

image

  • Sin embargo, si ejecutamos la aplicación en 64bits y volvermos a observar el “Process Exprorer”, vemos como las DLLs utilizadas son las de 64 bits.

image

Llegado este momento el proyecto compila y se ejecuta en 32 y 64 bits. Es en momento de ver como se comporta el proyecto de SETUP:

Entre las propieades del proyecto de setup tenemos la propiedad “TargetPlatform” que tendremos que cambiar según la compilación que queramos hacer; para 32 o para 64 bits.

image

Hasta aquí, el caso fácil y prácticamente todo automático, pero, siempre existen algunas excepciones o “complicaciones” a tener en cuenta:

  • ¿Que pasa si tengo que trabajar con directorios específicos de 64bits; “ProgramFiles64Folder”, “CommonFiles64Folder” o, para el caso de 32 bits “ProgramFilesFolder”, “CommonFilesfolder”, etc.? En este caso sólo se podrá trabajar con una plataforma específica, y es posible que tengamos que crear dos proyectos de Setup, uno para 32 y otro para 64.
  • Pero, ¿Y si tenemos mucha lógica en el setup; nuevos formularios, custom actions, etc? La idea es intentar evitar dependencias de cada plataforma. Desde mi punto de vista lo mejor es lanzar el .MSI desde un .BAT o .VBS pasando el parámetro “TARGETDIR”.  En cuanto a otros directorios o depencias,  lo mejor es trabajar con “custom actions” creando la lógica adecuada, pero de esta manera siempre un único Setup y sólo cambiando la propiead “TargetPlatform”. Para más detalle sobre todo esto, hechad un vistazo a mi anterior post; “Como hacer un setup en 15 min”.
  • ¿Que ocurre si nuestro proyecto de 64 bits trabaja con DLLs concretas de terceros?¿Que ocurre con las referencias? Una buena idea puede ser; crear un nuevo proyecto vacío (sin ninguna clase) en el mismo directorio del proyecto de 32 bits, de manera que ambos proyectos lo compartan todo.  Es decir, tendríamos en un mismo directorio dos ficheros “.csproj”. Bastará incluir las referencias por separado en cada uno de los proyectos y en algún caso, alguna directiva de compilación. En este caso nos tocará crear dos proyectos de Setup. ¡intentemos unificar toda la logica!.

Estos úiltimos puntos dependerán de la complejidad que tenga nuestro Setup.  La cuestión es, complicarse cuanto menos, mejor.    El principio KISS, siempre presente.

Saludos
Juanlu

Como hacer un setup personalizado en 15min con Visual Studio .NET

Muy buenas de nuevo, (y nunca mejor dicho lo de “nuevo”).

Llevo ya un tiempo sin aparecer por aquí, y la verdad, es que desde entonces no han dejado de aparecer novedades.  Me siento como un niño pequeño que quiere jugar con sus nuevos juguetitos y, de alguna manera “no le dejan”, en fin….. Ya os contaré algunas cosas poco a poco.

En un intento de retomar mi aparición por aqui, os muestro a continuación y un pequeño artículo sobre la generación de Setup personalizados que escribí hace poco. A ver si os sirve:

Hace ya un tiempo quería comentar la facilidad de .net para la generación de setup’s personalizados y sobre todo como encaminarlos a la distribución vía SCCM (el antiguo SMS) o incluso, como sustitución a la generación de diferentes ficheros de configuración para nuestros “famosos” y diferentes entornos; desarrollo, integración y producción.

  • ¿Por qué copiar de acá para allá diferentes ficheros de configuración que cambian de un día para otro?
  • ¿No es mejor disponer de un mismo fichero de configuración y no compilar nada para el paso entre entornos?

Vamos a seguir detalladamente los pasos adecuados a partir de un sencillo ejemplo que consiste en un formulario Windows que utiliza un texto fijo recuperado del fichero de configuración y que podría ser cualquiera de nuestros proyectos actuales

1. Añadimos a la solución existente un proyecto de tipo “Setup and Deployment” con el nombre <NombreProyecto>Setup:

clip_image002

2. Añadimos un nuevo proyecto de tipo “Class library” y denominarlo por ejemplo, <NombreProyecto>CustomInstall:

clip_image004

3. Añadimos una nueva clase “CustomParameters.cs” o renombrar la clase “Class1.cs” generada por defecto con la siguiente información:

using System.Collections;
using System.Configuration.Install;

namespace DeployEn15MinCustomInstall
{
    public class CustomParameters
    {               
        private static System.Collections.IDictionary state = null;
        public static string PREFIJO_SALUDO = "PrefijoSaludo";
        
        private string _prefijoSaludo;        

        /// <summary>
        /// Constructor para los instalables sin parametros de entrada
        /// </summary>
        /// <param name="savedState"></param>
        public CustomParameters(IDictionary savedState)
        {
            state = savedState;
        }

        public string PrefijoSaludo
        {
            get { return _prefijoSaludo; }
            set { _prefijoSaludo = value; }
        }

        /// <summary>
        /// Recupera los parametros pasados al msi.
        /// </summary>
        /// <param name="installContext"></param>
        public void LoadContext(InstallContext installContext)
        {
            this.PrefijoSaludo = installContext.Parameters[PREFIJO_SALUDO];          
        }
    }
}

Donde; la constante “PREFIJO_SALUDO”, el atributo y la propiedad “PrefijoSaludo” serán sustituidos por cuantos parámetros sean necesarios pasar al ”.msi” para la instalación.

4. Añadimos una nueva clase “ConfigManager” con objeto de manejar la sección de “AppSettings” del fichero de configuración de la aplicación y poder añadir así nuevos valores a las key’s ya existentes.

public class ConfigManager
    {
        #region Attributos

        private string _ConfigPath;
        private Configuration _Config;
        private AppSettingsSection _AppSettings;

        #endregion

        /// <summary>
        /// Inicializa una nueva instancia de la clase<see cref="ConfigUpdater"/>.
        /// </summary>
        /// <param name="configPath">The config path.</param>
        public ConfigManager(string configPath)
        {
            _ConfigPath = (configPath == null ? String.Empty : configPath);

            try
            {
                _Config = ConfigurationManager.OpenExeConfiguration(_ConfigPath);
                _AppSettings = _Config.AppSettings;
            }
            catch (Exception ex)
            {
                throw new ApplicationException(
                    String.Format("Error adding new configuration parameters into: '{0}'.{1}{2}",
                    _ConfigPath, ex.Message, ex.StackTrace));
            }
        }

        /// <summary>
        /// Añade una nueva clave (keyName) a la sección "appSettings"
        /// del fichero de configuración con un valor (value).
        /// </summary>
        /// <param name="keyName">Nombre de la clave a añadir.</param>
        /// <param name="value">Valor a asignar a la nueva clave.</param>
        public void AddParam(string keyName, string value)
        {
            if (_AppSettings.Settings[keyName] == null)
                _AppSettings.Settings.Add(keyName, value);
        }

        /// <summary>
        /// Elimina del fichero de configuración una clave (keyName) de la sección "appSettings"
        /// del fichero de configuración.
        /// </summary>
        /// <param name="keyName">Nombre de la clave a eliminar..</param>
        public void RemoveParam(string keyName)
        {
            _AppSettings.Settings.Remove(keyName);
        }

        /// <summary>
        /// Modifica o añade una clave (keyName) de la sección "appSettings".
        /// </summary>
        /// <param name="keyName">Nombre de la clare a añadir o modificar.</param>
        /// <param name="value">Valor a asignar a la clave</param>
        public void SaveParam(string keyName, string value)
        {
            if (_AppSettings.Settings[keyName] != null)
                _AppSettings.Settings[keyName].Value = value;
            else
                AddParam(keyName, value);
        }

        /// <summary>
        /// Guarda el fichero de configuración con los cambios realizados
        /// en la sección "appSetting".
        /// </summary>
        public void Save()
        {
            if (!_AppSettings.ElementInformation.IsLocked)
                _Config.Save();
            else
                throw new ApplicationException("Section was locked, could not update");
        }
    }

5. Añadimos al proyecto “CustomIntall” un nuevo ítem de tipo “Installer Class” y lo denominamos por ejemplo “CustomActions.cs”.

clip_image006

El contenido de este fichero será:

using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Reflection;

namespace DeployEn15MinCustomInstall
{
    [RunInstaller(true)]
    public partial class CustomActions : Installer
    {
        private const string PAREMETER_NOT_SPECIFIED = "El parámetro de instalación '{0}' no ha sido especificado. La instalación será cancelada.";

        private CustomParameters _parameters = null;        

        // Añadir al array todos los parámetros requeridos durante la instalación.
        // Dejarlo vacío sino hay parámetros requeridos.
        private ArrayList requiredParameters = new ArrayList { CustomParameters.PREFIJO_SALUDO };

        public CustomActions()
        {
            InitializeComponent();
        }


        public override void Install(System.Collections.IDictionary stateSaver)
        {
#if DEBUG
            Debugger.Launch();
#endif

            base.Install(stateSaver);

            // Si la instalación es ejectuada previo paso de parámetros.
            if (this.Context != null && this.Context.Parameters.Count > 0)
            {
                _parameters = new CustomParameters(stateSaver);
                _parameters.LoadContext(this.Context);

                ConfigureFileConfig();
            }
        }

        private void ConfigureFileConfig()
        {
            string assemblyPath = this.Context.Parameters["assemblyPath"];
            string fileName = Path.GetFileNameWithoutExtension(assemblyPath).Replace("CustomInstall", ".exe");

            string exeConfigPath = Path.Combine(Directory.GetParent(assemblyPath).FullName, fileName);

            ConfigManager config = new ConfigManager(exeConfigPath);

            this.UpdateAppSettings(config);

            // Si todo es correcto se realiza el commit del fichero de configuración.
            config.Save();

            config = null;
        }

        private void UpdateAppSettings(ConfigManager config)
        {
            PropertyInfo[] props = _parameters.GetType().GetProperties();

            foreach (PropertyInfo prop in props)
            {
                string propValue = prop.GetValue(_parameters, null) as string;
                if (!String.IsNullOrEmpty(propValue))
                {
                    config.SaveParam(prop.Name, propValue);
                }
                else
                {
                    // Parámetro requerido no especificado
                    if (requiredParameters.Contains(prop.Name))
                    {
                        throw new InstallException(
                            String.Format(PAREMETER_NOT_SPECIFIED, prop.Name));
                    }
                }
            }
        }

    }
}
Las siguientes instrucciones 

Las siguientes instrucciones van a permitir realizar una parada en tiempo de depuración con objeto de facilitar dicha labor. Esta instrucción tendrá más sentido cuando el ejecutable no se inicie directamente desde el Visual Studio.

#if DEBUG
            Debugger.Launch();
#endif

6. Añadimos al proyecto de Setup dos items del tipo, “Primary Output”;

  • “DeployEn15Min”(Ejecutable Windows Form) y,
  • “DeployEn15MinCustomInstall”(Class Library que contiene el “Installer class”) .

clip_image008

7. Finalizado el paso anterior, nuestra solución tendrá la siguiente apariencia además de proyecto de Test correspondiente (como en cualquier proyecto debemos tener siempre):

clip_image010

8. A continuación seleccionamos el proyecto “DeployEn15MinSetup” y hacemos click en el icono de “Custom Actions”.

9. Sobre la carpeta “Install” de las Custom Actions, hamemos click derecho y seleccionamos del “Application Folder” el “Primary output” referente al “CustomInstall”.

clip_image012

10. Generamos un fichero de comandos “.bat” con la siguiente instrucción y lo ejecutamos en la misma carpeta en donde se encuentre el Setup:

msiexec /l* “DeployEn15Min.log” /i DeployEn15MinSetup.msi TARGETDIR=”C:Test” allUsers=[ALLUSERS] PREFIJOSALUDO=”Deploy en 15 minutos v1.1″

Ahora, el ejecutable recibe los parámetros pasados en el comando. Los que se muestran durante la instalación son parámetros estándares, sin embargo, si después de la instalación comprobamos el fichero de configuración resultante, veremos como el valor de la key “PrefijoSaludo” del “appSettings”, ha cambiado por el nuevo valor.

clip_image014

Es en este punto, donde cobra fuerza la instrucción, “Debugger.Launch();” antes comentada.

Para conseguir la modificación de todos nuestros parámetros de configuración durante la instalación, tendremos que añadir valores a la propiedad “CustomActionData” del Custom Action “Install” tal y como se indica en la siguiente figura para nuestro parámetro “PrefijoSaludo”:

clip_image016

Se añadirán tantos valores como parámetros sean susceptibles de recuperar en tiempo de instalación.

Los “custom actions”; “Commit”, “Rollback” y “Uninstall”, serán implementados de la misma manera siempre y cuando en nuestra clase DeployEn15MinCustomInstall.CustomInstall.cs sobrescribamos los métodos correspondientes. Adicionalmente podrán sobrescribirse otros métodos que ocurren antes o después de estas acciones, con lo que tendremos mayor control sobre cualquier “cosa” que queramos realizar durante y después de la instalación.

El comando “msiexec” recibe un parámetro /l* que permite indicar un fichero de log en el que se registrarán todos pasos generados durante la instalación. Su objetivo, detectar cualquier posible error durante dicho proceso.

Nota: Con objeto de mejorar la instalación de prerrequisitos, actualizaciones, parches etc. de nuestra aplicación, podemos utilizar un “.vbs” (Visual Basic Script) en lugar de un “.bat”, permitiendo de esta manera mayor control; acceso al registro de Windows, etc. El objetivo de esto es permitir a los administradores de sistemas las modificaciones adecuadas según las necesidades concretas de la distribución. Un ejemplo claro podría ser, comprobar una clave del registro de Windows para verificar la existencia de otro programa o prerrequisito, de una versión anterior, etc.

 Un saludo desde mi “nueva casa”

Juanlu, El Guerre

Asp.Net Projects in Visual Studio 2008 & IIS7

  AND 

 

Hace unos días, tuve la necesidad de formatear (a Windows Vista, aunque para Windows Server 2008 el caso hubiese sido el mismo) de nuevo el equipo, ¡cosa casi habitual!.  Después de otros varios días trabajando con el, y tras haber instalado Visual Studio 2008 entre otras cosas, :-D, me disponía a crear un proyecto ASP.NET con objeto de realizar unas pruebas, pero de repente,  me encuentro con “esto”, no es que sea un error, pero si me parece curioso, o ¡al menos es la primera vez que lo veo!.

Pues bien, si intentas publicar un proyecto ASP.NET en un IIS7 (local en mi caso) en donde NO se encuentre instalada la caractarística “IIS6 Metabase and II6 Configuration Compatibility” de II6, resulta imposible publicar, curioso como digo, pero, si estoy con II7,¿no debería evitar esta compatibilidad y trabajar directamente con IIS7?, 😀

 

Aquí os dejo los pasos que seguí:

  1. Crear un proyecto ASP.NET
  2. Publicar el proyecto en local (Publish Web Site)
  3. Elegir la publicación en “Local IIS”
  4. Zassss…. ¡no se puede! :-$

 

A continuación continué con las indicaciones de la instalación de esta  característica, marcando sólo la casita “IIS Metabase and II6 configuration compatibility” y tras un reinicio y una actualización de Window Vista…. ¡pues sí, finalmente funcionó!

 

Tampoco soy el único con este caso o similar; Visual Studio 2008 and IIS 7.

Para más detalle, aquí os dejo un enlace bastante interesante sobre temas de deploy con II7: Using Visual Studio 2008 with IIS 7.0

 

Buuueno, poco para aprender, pero algo más a tener en cuenta, 😉

 

Saludos @home
Juanlu

Exchange, directo al grano con OwaAuth

Durante la semana pasada he estado realizando una pequeña POC para el acceso a OWA (Outloook Web Access) via OwaAuth.dll y creo que, en cierto modo, estoy en la necesidad de que no pase al olvido, (si Juan, por si acaso, jejeje…).

¿Que es eso de OwaAuth.dll?

Se trata de la ISAPI de Exchange para el Logon en Owa. Esta es quizás una gran desconocida para muchos y aunque pasa desapercibida puede ser que en alguna ocasión sea necesario tener que hacer uso de ella para un propósito “un tanto distinto” :-D.

 

Owa necesita un “sessionid” y un “cadata” para poder acceder y obtener información de cada una de sus páginas, pues bien, owaauth.dll es la encargada de obtener estos dos datos en una primera petición http de tipo POST, y, a partir de aquí, haciendo uso de estos en las peticiones sucesivas cada página web de Owa retornará el código HTML sin ningún tipo de preámbulo.

Paso a paso:

  • Invocar a owaauth.dll mediante una petición http (POST) a través de la url: “https://MACHINE_NAME/owa/auth/owaauth.dll” y con una serie de parámetros específicos; destination, flags, forcedownlevel, trusted, username, password, submitCreds, isUtf8. Para la obtención de estos parámetros he utilizado el siguiente método:
public string GetPostData(Uri uri)
{
// Setting data to create a request body.
Dictionary<string, string> props = new Dictionary<string, string>();
props.Add(
"destination",
uri.Scheme
+ Uri.SchemeDelimiter + HttpUtility.UrlEncode(uri.Host + "/exchange/" + _user + "/"));
props.Add(
"flags", "4"); /* 0 */
props.Add(
"forcedownlevel", "0");
props.Add(
"trusted", "4"); /* 0 */
props.Add(
"username", HttpUtility.UrlEncode(String.Format(@"{0}{1}", _domain, _user)));
props.Add(
"password", HttpUtility.UrlEncode(_password));
props.Add(
"submitCreds", "Log+On");
props.Add(
"isUtf8", "1");

ASCIIEncoding encoding
= new ASCIIEncoding();

string postData = String.Empty;
foreach (string key in props.Keys)
{
string pValue = null;
props.TryGetValue(key,
out pValue);
postData
+= string.Format("{0}={1}&", key, pValue);
}
if (props.Keys.Count > 0)
postData
= postData.Substring(0, postData.Length - 1);

return postData;
}
  • Realizada la petición HTTP según el siguiente método, se obtienen los datos buscados; “sessionid” y “cadata”. Estos son recuperados en el objeto “CookieCollection“.
public CookieCollection Authenticate(string url)
{
string postData = GetPostData(new Uri(url));
// Get Response - Cookie
var response = HttpManager.SendRequest(
url, postData,
null,
RequestMethod.POST, _user, _password, _domain,
"application/x-www-form-urlencoded");

if (response.Cookies.Count < 2)
return null;

return response.Cookies;
}
  • Ahora ya se pueden realizar peticiones a cualquiera de las páginas de Owa, eso sí, de tipo GET y teniendo en cuenta que deben ir acompañadas de “sessionid” y “cadata”.
  • La respuesta a cada petición contiene el código HTML de cada una de las páginas solicitadas.

 

El método “SendRequest” es el encargado de realizar:

  1. La primera petición de tipo “POST” y contentType “application/x-www-form-urlencoded” y,
  2. Cada petición de tipo “GET” y contentType “text/xml”.
public static HttpWebResponse SendRequest(string url, string postData,
CookieCollection cookieCol, RequestMethod requestMethod,
string user, string pwd, string domain, string contentType)
{
ServicePointManager.ServerCertificateValidationCallback
= delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
return true;
};

Uri uri
= new Uri(url);

var request
= (HttpWebRequest)HttpWebRequest.Create(uri);

request.Method
= requestMethod.ToString();
request.CookieContainer
= new CookieContainer();
request.ContentType
= contentType;
request.AllowAutoRedirect
= false;
// request.ServicePoint.Expect100Continue = false;
request.KeepAlive = true;
request.UserAgent
= "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1;" +
".NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)";
request.Accept
= "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,"
+ "application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*";

byte[] body = null;
if (requestMethod == RequestMethod.POST)
{
// BODY
body = Encoding.ASCII.GetBytes(postData);
request.ContentLength
= body.Length;
}

// Cookies
if (cookieCol != null)
{
foreach (Cookie c in cookieCol)
// request.Headers.Add(c.Name, c.Value);
request.CookieContainer.Add(c);
}

// Security
CredentialCache credentialCache = new System.Net.CredentialCache();
credentialCache.Add(uri,
"Basic", /* Basic */
new System.Net.NetworkCredential(user, pwd, domain)
);

request.Credentials
= credentialCache;

if (requestMethod == RequestMethod.POST)
{
// Response
var stream = request.GetRequestStream();
stream.Write(body,
0, body.Length);

stream.Close();
}

return (HttpWebResponse)request.GetResponse();
}

Esto ha sido todo por esta vez, espero como siempre,  haber dado un pasito más.

Saludos @SundayHome
Juanlu