Archivo de la categoría: Microsoft

Windows Azure Web sites #wapucolombia

Esta es la  presentación de Web sites del WAPU en Colombia, esta presentación es parte del
Azure tranning Kit. Este es el kit de entrenamiento de  Azure el cual es muy recomnedable para  aprender a utilizar la Nube.

Links relacionados

Windows Azure Media Services: Publicar videos para IOS y Windows Phone #azure

Introducción

Para el evento llamado Windows Azure Partner Academy, presenté como utilizar Windows Azure media Services de manera programática utilizando el SDK. La idea de la demostración es poder automatizar el flujo de ingestión, codificación y publicación de contenido en Media Services. En este post, se explica el código utilizado en esa demostración.
El propósito del ejemplo es tomar un video desde el disco local, subirlo a windows Azure Media Services, codificarlo primero en H264 y luego en HLS para que pueda ser visto por los sistemas IOS como es un Iphone, Ipad, etc. Todo esto de manera automática.

Pre requisitos

Para poder implementar un cliente que se conecte a Media Services y utilice las API para la ingestión, codificación, empaquetamiento y publicación del contenido se requieren los componentes y configuración en tú equipo.

  1. Un servicio Media Services creado
  2. Las llaves de acceso al servicio
  3. NET Framework 4.5 or .NET Framework 4.
  4. Visual Studio 2012 or Visual Studio 2010 SP1
  5. Windows Azure SDK for .NET
  6. Windows Azure Media Services SDK for .NET
  7. WCF Data Services 5.0 for OData V3 libraries

Paso a paso del proyecto

Paso 1: Crear el proyecto de consola y configuración

Para comenzar, se crea un proyecto de consola del tipo C# en Visual Studio. Para que este programa pueda acceder a Media Services se debe utilizar el Windows Azure media Services SDK for .NET. Este se puede obtener de manera muy simple utilizando NugetPackage como se ve en la siguiente figura.

Con esta referencia ya podemos hacer código que se comunique con Media Services.

Paso 2: Obtener Contexto en Media Services

Ahora, debemos obtener las Manage Keys del servicio. Para ello vamos al portal, entramos al servicio y las obtenemos utilizando el botón del menú de la barra inferior, como se muestra en la siguiente figura.

La idea es que utilicemos el Account Name y Primary Media Service Access Key para obtener el contexto del servicio y así poder comenzar a trabajar con media Services.


        static CloudMediaContext ObtenerContexto()
        {
            return new CloudMediaContext(_accountName, _accountKey);
        }
  

Para subir el archivo, vamos a utilizar el método Subir1ArchivoCrear que recibe como argumentos la opción de creación del Asset y el nombre del archivo que contiene el video. Las opciones de creación definen por ejemplo si el Asset estará cifrado en el storage. Para efectos de nuestro ejemplo usamos la opción por None. El método lo que hace es crear un nombre para identificar el Asset en Media Services, crearlo con ese nombre vacío y subir el video a media services.

static IAsset Subir1ArchivoCrear(AssetCreationOptions assetCreationOptions, string singleFilePath)
        {
            //1. Crear el nomrbe del Asset en Media Services
            var assetName = "TMP_UploadSingleFile_" + DateTime.UtcNow.ToString();
            var asset = CreateEmptyAsset(assetName, assetCreationOptions);
            var fileName = Path.GetFileName(singleFilePath);
            //2. Crear un Asset vacio
            var assetFile = asset.AssetFiles.Create(fileName);
            _PrefijoNombreAsset = assetFile.Name;
            Console.WriteLine("Creado el Archivo {0}", assetFile.Name);
            //3. Crear la Politica
            var accessPolicy = _contexto.AccessPolicies.Create(assetName, TimeSpan.FromDays(30),
                                                                AccessPermissions.Write | AccessPermissions.List);
            //4. Crear el Locator
            var locator = _contexto.Locators.CreateLocator(LocatorType.Sas, asset, accessPolicy);
            Console.WriteLine("Upload {0}", assetFile.Name);
            //5. Se crea un blob transfer para subir el video a Media Services
            var blobTransferClient = new BlobTransferClient();
            blobTransferClient.NumberOfConcurrentTransfers = 20;
            blobTransferClient.ParallelTransferThreadCount = 20;
            //6. se define el evento que muestra el avance del upload
            blobTransferClient.TransferProgressChanged += blobTransferClient_TransferProgressChanged;
            //7. Se genera un task list asincrono para subir el archivo
            var uploadTasks = new List<Task>();
            uploadTasks.Add(assetFile.UploadAsync(singleFilePath, blobTransferClient, locator, CancellationToken.None));
            //8. Se espera que termine de subir
            Task.WaitAll(uploadTasks.ToArray());
            Console.WriteLine("Done uploading of {0}", assetFile.Name);
            locator.Delete();
            accessPolicy.Delete();
            return asset;
        }

Paso 3: Crear el Job de codificación H264

El primer trabajo de codificación es al formato H264. Los Jobs son trabajos de media Services que están compuestos por tareas, pudiendo un Job contener varias tareas. En este caso, por simpleza del ejemplo, vamos a crear un Job con solo una tarea.
En la tarea se especifica el tipo de codificación que se utilizará y la codificación del Asset de salida. La codificación utilizada es estándar y se especifica como «H264 Smooth Streaming 720p».
Luego de ejecutar el JOB, en vez de utilizar progressJobTask.Wait() que solo muestra cuando el JOB termina, se utiliza código para mostrar avance en porcentaje, como en el portal. Por último, se retorna la referencia al JOB una vez finalizado.

 static IJob CrearTrabajoCod_H264Smooth(IAsset asset)
        {
            //1. Crear un Nuevo JOB
            IJob job = _contexto.Jobs.Create("Primer JOB de encode: H264 Smooth Streaming 720p");
            // Get a media processor reference, and pass to it the name of the
            // processor to use for the specific task.
            IMediaProcessor processor = GetLatestMediaProcessorByName("Windows Azure Media Encoder");
            //2. Se crea una tarea con los detalles de la codificación.
            //en este caso  "H264 Adaptive Bitrate MP4 Set 720p"
            ITask task = job.Tasks.AddNew("task: H264 Smooth Streaming 720p",processor,
                "H264 Smooth Streaming 720p", TaskOptions.ProtectedConfiguration);
            //3. Especifica el Asset a codificar
            task.InputAssets.Add(asset);
            //4. Crea y especifica el Asset de salida, sin cifrado
            string nombreContenido = _PrefijoNombreAsset + "_H264";
            task.OutputAssets.AddNew(nombreContenido,AssetCreationOptions.None);
            //5. El manejador de evento que muestra el avance del job
            job.StateChanged += new EventHandler<JobStateChangedEventArgs>(StateChanged);
            //6. Se ejecuta el JOB
            job.Submit();
            //7. Validar el estado del Job
            Task progressJobTask = job.GetExecutionProgressTask(CancellationToken.None);
           //8. en vez de utilizar  progressJobTask.Wait(); que solo muestra cuando el JOB termina
            //se utiliza el siguiente codigo para mostrar avance en porcentaje, como en el portal
            double avance = 0;
            while ((job.State != JobState.Finished) && (job.State != JobState.Canceled) && (job.State != JobState.Error))
            {
                if (job.State==JobState.Processing)
                {
                    if (avance != (job.Tasks[0].Progress / 100))
                    {
                        avance = job.Tasks[0].Progress / 100;
                        Console.WriteLine("Percent complete: {0}", avance.ToString("#0.##%"));
                    }
                }
                Thread.Sleep(TimeSpan.FromSeconds(5));
            }
            // 9. se regresa el la referencia al JOB
            job = GetJob(job.Id);
            return job;
        }

Una vez codificado el video tendremos 2 Assets en media Services. El video original y el codificado a H264. Ya no necesitamos el original, por lo cual lo borramos utilizando el siguiente método.

static void BorrarAsset(IAsset asset)
{
    // delete the asset
    asset.Delete();
    // Verify asset deletion
     if (GetAsset(asset.Id) == null)
         Console.WriteLine("Deleted the Asset");
}

Paso 4: Crear el Job de codificación HLS

Como el objetivo es poder mostrar el video en IOs también, entonces debemos codificarlo en HLS para que así los Iphone, Ipad, etc puedan verlo. Para ellos vamos a crear un JOB, con una tarea que haga la codificación. Ahora, para especificar la codificación, a diferencia a lo hecho en el paso anterior con el formato H264, vamos a especificar la codificación con el siguiente archivo XML.

<?xml version="1.0" encoding="utf-8" ?>
<taskDefinition xmlns="http://schemas.microsoft.com/iis/media/v4/TM/TaskDefinition#">
  <name>Smooth Streams to Apple HTTP Live Streams</name>
  <id>A72D7A5D-3022-45f2-89B4-1DDC5457C111</id>
  <description xml:lang="en">Converts on-demand Smooth Streams encoded with H.264 (AVC) video and AAC-LC audio codecs to Apple HTTP Live Streams (MPEG-2 TS) and creates an Apple HTTP Live Streaming playlist (.m3u8) file for the converted presentation.</description>
  <inputDirectory></inputDirectory>
  <outputFolder>TS_Out</outputFolder>
  <properties namespace="http://schemas.microsoft.com/iis/media/AppleHTTP#" prefix="hls">
    <property name="maxbitrate" required="true" value="1600000" helpText="The maximum bit rate, in bits per second (bps), to be converted to MPEG-2 TS. On-demand Smooth Streams at or below this value are converted to MPEG-2 TS segments. Smooth Streams above this value are not converted. Most Apple devices can play media encoded at bit rates up to 1,600 Kbps."/>
    <property name="manifest" required="false" value="" helpText="The file name to use for the converted Apple HTTP Live Streaming playlist file (a file with an .m3u8 file name extension). If no value is specified, the following default value is used: &lt;ISM_file_name&gt;-m3u8-aapl.m3u8"/>
    <property name="segment" required="false" value="10" helpText="The duration of each MPEG-2 TS segment, in seconds. 10 seconds is the Apple-recommended setting for most Apple mobile digital devices."/>
    <property name="log"  required="false" value="" helpText="The file name to use for a log file (with a .log file name extension) that records the conversion activity. If you specify a log file name, the file is stored in the task output folder." />
    <property name="encrypt"  required="false" value="false" helpText="Enables encryption of MPEG-2 TS segments by using the Advanced Encryption Standard (AES) with a 128-bit key (AES-128)." />
    <property name="pid"  required="false" value="" helpText="The program ID of the MPEG-2 TS presentation. Different encodings of MPEG-2 TS streams in the same presentation use the same program ID so that clients can easily switch between bit rates." />
    <property name="codecs"  required="false" value="false" helpText="Enables codec format identifiers, as defined by RFC 4281, to be included in the Apple HTTP Live Streaming playlist (.m3u8) file." />
    <property name="backwardcompatible"  required="false" value="false" helpText="Enables playback of the MPEG-2 TS presentation on devices that use the Apple iOS 3.0 mobile operating system." />
    <property name="allowcaching"  required="false" value="true" helpText="Enables the MPEG-2 TS segments to be cached on Apple devices for later playback." />
    <property name="passphrase"  required="false" value="" helpText="A passphrase that is used to generate the content key identifier." />
    <property name="key"  required="false" value="" helpText="The hexadecimal representation of the 16-octet content key value that is used for encryption." />
    <property name="keyuri"  required="false" value="" helpText="An alternate URI to be used by clients for downloading the key file. If no value is specified, it is assumed that the Live Smooth Streaming publishing point provides the key file." />
    <property name="overwrite"  required="false" value="true" helpText="Enables existing files in the output folder to be overwritten if converted output files have identical file names." />
  </properties>
  <taskCode>
    <type>Microsoft.Web.Media.TransformManager.SmoothToHLS.SmoothToHLSTask, Microsoft.Web.Media.TransformManager.SmoothToHLS, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</type>
  </taskCode>
</taskDefinition>

Este archivo le indica al encoder de media Services que va a recibir un video H264 (AVC) video y AAC-LC audio y que lo convierta a Apple HTTP Live Streams (MPEG-2 TS).
El método CreateSmoothToHlsJob utiliza el archivo de configuración para crear la tarea de codificación en base a esa configuración.

public static IJob CreateSmoothToHlsJob(string configFilePath, IAsset asset)
{
    // 1. Usamos la configuración de codificacón desde un Archivo XML
    string configuration = File.ReadAllText(Path.GetFullPath(configFilePath
        + @"\MediaPackager_SmoothToHLS.xml"));

    //2. Valida que el Asset contenga un solo archivo ISM
    var ismAssetFiles = asset.AssetFiles.ToList().
                Where(f => f.Name.EndsWith(".ism", StringComparison.OrdinalIgnoreCase)).ToArray();
    if (ismAssetFiles.Count() != 1)
        throw new ArgumentException("The asset should have only one, .ism file");
    ismAssetFiles.First().IsPrimary = true;
    ismAssetFiles.First().Update();
    // 3. Creal el JOB
    IJob job = _contexto.Jobs.Create("My Smooth Streams to Apple HLS job");
    IMediaProcessor processor = GetLatestMediaProcessorByName("Windows Azure Media Packager");
    // 4. Crea la tarea con los detalles de codificación del archivo XML
    ITask task = job.Tasks.AddNew("My Smooth to HLS Task",
        processor,
        configuration,TaskOptions.ProtectedConfiguration);
    // 5. Define el Asset de entrada
    task.InputAssets.Add(asset);
    // 6. Crea el Asset de Salida con el sufijo HLS.
    string nombreContenido = _PrefijoNombreAsset + "_HLS";
    task.OutputAssets.AddNew(nombreContenido, AssetCreationOptions.None);
    //7. define el manejador del evento
    job.StateChanged += new EventHandler<JobStateChangedEventArgs>(StateChanged);
    //8. Lanza el JOB
    job.Submit();
    //9. Revisa el estado de ejecución del JOB
    Task progressJobTask = job.GetExecutionProgressTask(CancellationToken.None);
    //10. en vez de utilizar  progressJobTask.Wait(); que solo muestra cuando el JOB termina
    //se utiliza el siguiente codigo para mostrar avance en porcentaje, como en el portal
    double avance = 0;
    while ((job.State != JobState.Finished) && (job.State != JobState.Canceled) && (job.State != JobState.Error))
    {

        if (job.State == JobState.Processing)
        {
            if (avance != (job.Tasks[0].Progress / 100))
            {
                avance = job.Tasks[0].Progress / 100;
                Console.WriteLine("Percent complete: {0}", avance.ToString("#0.##%"));
            }
        }

        Thread.Sleep(TimeSpan.FromSeconds(5));
    }
    //11. regresa la referencia al JOB terminado
    return job;
}

Paso 5: Publicar los videos ya codificados

Para esto se debe obtener la referencia al manifiesto del Asset, el archivo de extensión ISM. Al publicar debemos especificar la política de uso de lo que estamos publicando, en este caso le vamos a decir que se publica solo lectura por 30 días. Inmediatamente después debemos crear un origen donde localizar nuestro manifiesto y asignarle una URL. Esta es la que utilizaran los clientes para acceder al contenido. Ahora, en el caso de ser HLS debemos agregar «(format=m3u8-aapl)» a la URL.

public static ILocator GetStreamingOriginLocator(string targetAssetID, MediaContentType contentType)
{
    // 1. Obtener la referencia al Asset que se quiere publicar
    IAsset assetToStream = GetAsset(targetAssetID);
    //2. Obtener la referencia del manifiesto
    var theManifest =
                        from f in assetToStream.AssetFiles
                        where f.Name.EndsWith(".ism")
                        select f;

    // 3. crear una referencia al manifiesto
    IAssetFile manifestFile = theManifest.First();
    //3. Crear la politica de acceso de 30 dias solo lectura.
    IAccessPolicy policy = _contexto.AccessPolicies.Create("Streaming policy",
        TimeSpan.FromDays(30),
        AccessPermissions.Read);
    //4.Crear un Locator
    ILocator originLocator = _contexto.Locators.CreateLocator(LocatorType.OnDemandOrigin, assetToStream,
        policy,
        DateTime.UtcNow.AddMinutes(-5));
    Console.WriteLine("Streaming asset base path on origin: ");
    Console.WriteLine(originLocator.Path);
    Console.WriteLine();
    //5. Crear una URL completa para el manifiesto
    string urlForClientStreaming = originLocator.Path + manifestFile.Name + "/manifest";
    //5.1 En el caso de ser HLS se debe agregar al final de la URL  "(format=m3u8-aapl)"
    if (contentType == MediaContentType.HLS)
        urlForClientStreaming = String.Format("{0}{1}", urlForClientStreaming, "(format=m3u8-aapl)");

    Console.WriteLine("URL to manifest for client streaming: ");
    Console.WriteLine(urlForClientStreaming);
    Console.WriteLine();
    Console.WriteLine("Origin locator Id: " + originLocator.Id);
    Console.WriteLine("Access policy Id: " + policy.Id);
    Console.WriteLine("Streaming asset Id: " + assetToStream.Id);
    // 6. regresa el origen
    return originLocator;
}

Paso 6: Juntar todo en una demostración consistente

Ahora que vimos cada uno de los métodos principales que necesitamos para hacer la ingestión, codificación y publicación del contenido vamos a ver el cómo se articulan todos estos métodos en una secuencia coherente. El siguiente código lleva a cabo la secuencia completa.

static void Demo1()
{
    _accountName = "xxxxxx";
    _accountKey = "xxxxxx=";
    _contexto = ObtenerContexto();
    //Video
    string fileName = @"K:\video\interview2.wmv";
    Console.WriteLine("\n 1.Subir el Archivo\n");
    IAsset VideoOriginal = Subir1ArchivoCrear(AssetCreationOptions.None, fileName);
    //Crear el JOB para H264
    Console.WriteLine("\n 2.Crear el JOB para H264\n");
    IJob myJob = CrearTrabajoCod_H264Smooth(VideoOriginal);
    // Paso 4: Borrar el Video Origial
    Console.WriteLine("\n 3.Borrar el Video Origial\n");
    BorrarAsset(VideoOriginal);
    // Paso 4: Crear el JOB para HLS
    Console.WriteLine("\n  4.Crear el JOB para HLS\n");
    IJob myHLSjob = CreateSmoothToHlsJob(_configFile, myJob.Tasks[0].OutputAssets[0]);
    //Paso 5: Publicar H264
    Console.WriteLine("\n 5.Publicar H264\n");
    GetStreamingOriginLocator(myJob.Tasks[0].OutputAssets[0].Id, MediaContentType.SmoothStreaming);
    //Publicar HLS
    Console.WriteLine("\n 6.Publicar HLS\n");
    GetStreamingOriginLocator(myHLSjob.Tasks[0].OutputAssets[0].Id, MediaContentType.HLS);
    //Lista de Videos
    ListAssets();
    //Salir
    Console.WriteLine("\nEspera click para borrar todo.....[s/n]");
    string siono=Console.ReadLine();
    if (siono == "s")
    {
        BorrarTodo();
    }
    Console.WriteLine("\nFin.....");
    Console.ReadLine();
}

Al ejecutar el proceso obtenemos las siguientes salidas.

En el paso 1 podemos ver como se sube el archivo original al contenido de Media Services.

Paso1

En el paso 2, podemos ver el avance del primer JOB(H.264)

Paso2

En el paso 3, se muestra cuando se borra el archivo original.

paso3

En el paso 4, vemos el avance en la codificación HLS

paso4

por último, podemos ver la publicación de ambos videos en Media Services.

paso5y6

EL código fuente del ejemplo desarrollado pueden descargarlo desde aquí.

Conclusión

Este es un simple ejemplo de cómo podemos utilizar Windows Azure Media Services de manera programática para poder llevar a cabo un proceso de publicación de videos para dos plataformas distintas de manera automática.

Links relacionados

ITCamps Azure Santiago #azure

Durante abril voy a apoyar a Alex en la ejecución de los ITCAMP, actividades de entrenamiento técnico para profesionales de IT con foco en el uso de Windows Azure.

Esta es la invitación al evento.

——————————————————————————————————————————————

itcamps[1]

La nube, la alternativa para extender nuestro Datacenter.

Te invitamos a participar de nuestros próximos laboratorios de 5 horas basados en escenarios prácticos aplicables a cualquier tipo de empresa. Esta vez utilizaremos la nube de Microsoft, en su visión de Infraestructura como Servicios para crear máquinas virtuales que complementaran nuestro Datacenter.

En estas sesiones aprenderá como desplegar un SharePoint en la nube de Microsoft, una réplica de su Active Directory y la publicación de una aplicación Web. Todo esto mediante el panel de administración de Windows Azure o mediante PowerShell.

Estos laboratorios se repiten en 4 sesiones, regístrate en la que prefieras escogiendo de las siguientes alternativas:

Registro

——————————————————————————————————————————————

inks relacionados

Windows Azure Media Services #WAPUCOLOMBIA

Esta es una presentación introductoria a Windows Azure Media Services que presenté en el WAPU de  Bogotá.

El ejemplo de código que automatiza la ingestión, codificación, empaquetamiento y publicación lo voy a publicar la próxima semana, con una explicación de cómo funciona.

Links relacionados

windows Azure IaaS & vNet

Esta es la presentación que expuse en el WAPU de Colombia. Es un replay de la presentación que hicimos en el Open Source Summit de Buenos Aires la semana pasada.
En esta presentación se explican los conceptos de IaaS, vNet y Paas. Lo mejor es que se muestran los escenarios híbridos.

Otros links relacionados

Windows Azure Open Platform Services Summit – Buenos Aires

Conozca más sobre cómo Windows Azure ha ayudado a las empresas a realizar un alto valor en la nube. Windows Azure soporta múltiples plataformas de desarrollo, lenguajes y herramientas, ofreciendo a los desarrolladores diversas opciones más allá de .NET. Regístrese ahora para atender a este único evento, en el cual aprenderá como tomar ventaja de una plataforma abierta, flexible y robusta para el desarrollo de sus aplicaciones.

Objetivos del Windows Azure Open Platform Summit

Conducido por expertos en Windows Azure OSS de la corporación, los asistentes aprenderán y entenderán temas como los siguientes:

  • Cómo se puede aprovechar OSS (Open Source Software) para desarrollar y escalar aplicaciones en Windows Azure
  • Cómo IaaS (Infraestructura-como-servicio) provee máquinas virtuales (VMs) y redes virtuales (VPNs) para aplicaciones basadas en Linux y Windows.
  • Experimente el ciclo de desarrollo con herramientas como Eclipse, y usando SDKs para Node.js, Python, y Java.
  • Aprenda acerca de las diferentes ofertas de datos de Windows Azure, tanto para SQL como NoSQL, y con tecnologías tales como MySQL y MongoDB

Agenda

Detalles del Evento

A quien está dirigido el evento?

Dirigido a tomadores de decisiones en su organización –CTOs, Arquitectos, Lideres de equipos de Ingeniería o Desarrollo y/o si está incorporando tecnologías de OSS como parte de la solución de su empresa.
Pre-requisito

Ver el siguiente video introductorio de Windows Azure – http://www.meetwindowsazure.com/Conversations#ScottGuthrieMeet

Cómo respaldar Azure SQL Database usando un Worker Role

Trabajando con una empresa que tiene un ERP de nicho, quieren mover su operación a Azure. Dentro de los temas que importantes a tener en cuenta apareció el tema del cumplimiento de normativas que les obligan a tener respaldos y manejarlos siguiendo una política.

En ese sentido, el resapldo en Azure SQL Database es diferente a lo que ofrece SQL Server. Puede verse un detalle de esto en http://msdn.microsoft.com/en-us/library/jj650016.aspx. En Azure SQL Database podemos realizar un respaldo lógico de la siguiente manera.

El Worker Role hace una copia de la base de datos en Azure Sql Database, esta se utilizará para hacer el export. No se hace directamente de la base de datos original porque el export no garantiza consistencia ya que va exportando los datos tabla a tabla y si se hace un cambio mientras el proceso está corriendo podría quedar inconsistente. La Copia si asegura consistencia. Luego de copiar la db sobre este duplicado se hace el export. Una vez terminado el proceso entonces se borra la copia de la db. El proceso se muestra en el siguiente diagrama de secuencia.

Escenario

En este artículo vamos a desarrollar un ejemplo de un servicio que realiza el export de una base de datos siguiendo el proceso antes descrito.

Las ventajas de desarrollarlo de esta forma es que podemos dejarlo corriendo de manera programada y así asegurarnos de cumplir con la política. Además, que el Export se hace en la nube por lo cual no hay costos de bajar la data a nuestro datacenter u otro lugar.

Prerrequisitos

Los prerrequisitos para poder desarrollar esta solución son:

  1. Tener instalado Windows Azure SDK for .NET
  2. Tener los instaladores de los componentes del Microsoft Data-tier Application Framework. Se pueden obtener desde
    1. http://www.microsoft.com/en-us/download/details.aspx?id=24000
    2. http://www.microsoft.com/en-us/download/details.aspx?id=35756
  3. Tener creada una base de datos en Azure Sql Database

Pasos para construir el proyecto

Los pasos a seguir para construir el proyecto de ejemplo son los siguientes

  1. Crear el proyecto Cloud
  2. Agregar Local Storage
  3. Agregar cadena de conexión con nuestro storage
  4. Configurar las variables necesarias para el export
  5. Agregar las referencias a utilizar para CloudDirve
  6. Crear una clase Helper para manejar CloudDrive
  7. Probar el funcionamiento del disco CloudDrive
  8. Agregar la referencia necesaria para Data-tier Application Framework (DAC)
  9. Crear una clase Helper para manejar DAC
  10. Crear método para copiar la DB
  11. Crear el método para borrar la copia de la DB
  12. Crear los manejadores de eventos y configuración de la traza
  13. Invocar el proceso de copia, Export y borrado en el método Run()
  14. Probar el proceso completo en el Emulador
  15. Configurar la instalación de DAC Framework
  16. Probar el Deploy en Azure

Paso 1: Crear el proyecto Cloud

En visual studio 2012 se crea un proyecto cloud, con un Worker Role. La siguiente figura ilustra el proyecto a crear.

Paso 2: Agregar Local Storage

Los CloudDrive son discos duros virtuales que se pueden montar en las máquinas de los Cloud Services. En nuestro caso vamos utilizar un CloudDrive para guardar ahí los archivos export que sacaremos desde la base de datos. CloudDrive es un concepto importante, y pueden leer un buen documento en el siguiente link

Para poder utilizar CloudDrive debemos agregar Local Storage a la definición del servicio, esto lo hacemos en la configuración del Worker Role como se muestra en la siguiente imagen.

Paso 3: Agregar cadena de conexión con nuestro storage

El CloudDrive se alancena en un Blob Storage. Para poder conectarnos a ese Blob Storage necesitamos una cade de conexiones, que declaramos en la configuración del servicio como aparece en la siguiente figura. Yo estoy configurando un Storage en la nube, pero se puede utilizar el de desarrollo de igual manera mientas se desarrolla.

Paso 4: Configurar las variables necesarias para el export

Las variables siguientes deben estar definidas para realizar el export:

  1. miStoragevhd: cadena de conexión con el storage donde se almacena el CloudDrive. Es muy importante que el Storage que se use en Azure esté en el mismo Datacenter que el Worker Role. Mejor si están en el mismo grupo de afinidad. Otro punto importante es que se debe utilizar en esta conexión HTTP no HTTPS. Por ejemplo:

    DefaultEndpointsProtocol=http;AccountName=jpgarciaexportdb;AccountKey=xxxxxxxxxx==

  2. NombreBaseDatos: nombre de la base de datos a exportar
  3. CadenaConexionDb: cadena de conexión para la base de datos, reemplazando el nombre de la db por {0}, ejemplo

    Server=tcp:abc.database.windows.net,1433;Database={0};User ID=SQLDATASERVER@e4rvhdsl4i;Password=P@ssw0rd;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;

La siguiente figura muestra las variables de configuración

Paso 5: Agregar las referencias a utilizar para CloudDrive

Para poder utilizar CloudDriver necesitamos agregar referencias a ODataLib 5.0.2 y Storage Client. Para agregar ODataLib en la versión 5.0.2 podemos utilizar Package Manager Console con la siguiente línea de comandos

PM> Install-Package Microsoft.Data.OData -Version 5.0.2

Ahora, debemos instalar las librearías de Windows Azure Store, utilizando para ellos NuGet Packages como se muestra en la siguiente pantalla.

Para poder acceder a al storage debemos agregar la referencia Microsoft.WindowsAzure.StorageClient.dll.

Por ultimo agregamos la referencia a Microsoft.WindowsAzure.CloudDrive.dll , para poder utilizar CloudDrive.

Paso 6: Crear una clase Helper para manejar CloudDrive

Para simplificar el desarrollo vamos a crear una clase que nos ayude a manejar todos los aspectos relacionados a CloudDirve. Esa clase se llamará CloudDriveHelp.

Esta clase tiene dos métodos, el primero para iniciar el disco. Para ello se debe definir cuál Storage se almacena en disco virtual. En este método se monta el disco virtual y se obtiene la letra del drive. Esta letra se almacena en la propiedad azureDrivePath de la clase CloudDriveHelp. myCloudDrive también es una propiedad de la clase CloudDriveHelp porque es necesario que su alcance sea general para así poder utilizarla para desmontar el dirve al finalizar el ciclo de vida del Worker Role.

Este método se llama solo una vez al iniciar el Worker Role y lo que hace es montar (literalmente) el disco virtual (VHD) en la máquina virtual que corre nuestro Worker Role.

public void IniciarDisco()
{
    CloudStorageAccount storageAccount;
    //1. Storage a Utilizar
    if (RoleEnvironment.IsEmulated)
    {
        storageAccount = CloudStorageAccount.DevelopmentStorageAccount;
    }
    else
    {
        storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("miStoragevhd"));
    }
    //2. Referencia al disco de cache local
    LocalResource localCache = RoleEnvironment.GetLocalResource("midisco");
    //3. Inicializa el disco de cache
    CloudDrive.InitializeCache(localCache.RootPathlocalCache.MaximumSizeInMegabytes);
    //4. Validar que exista Drives 
    CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
    blobClient.GetContainerReference("drives").CreateIfNotExist();
    //5. Crear el cloudDrive 
    myCloudDrive =
        storageAccount.CreateCloudDrive(blobClient.GetContainerReference("drives").
            GetPageBlobReference("myexportdrive.vhd").Uri.ToString());
    try
    {
        // 6. Crearlo si no existe, ojo que este drive es persistente
        myCloudDrive.CreateIfNotExist(localCache.MaximumSizeInMegabytes);
    }
    catch (CloudDriveException ex)
    {
        error = ex.Message;

    }
    //6. Aqui se obtiene la letra del Drive
    azureDrivePath = myCloudDrive.Mount(25DriveMountOptions.Force);
}

El método IniciarDisco() se llama desde el método OnStrat() del Worker Role, al iniciar el role se inicia el disco.

public override bool OnStart()
 {
    ConfigurarTraza();
    // Set the maximum number of concurrent connections 
    ServicePointManager.DefaultConnectionLimit = 12;
    //1. Crea el objeto myDrive
    myDrive = new CloudDriveHelp();
    //2. Inicia el Drive y lo monta
    myDrive.IniciarDisco();
    return base.OnStart();
 }

El método FinalizarDisco() de la clase CloudDriveHelp simplemente desmonta el drive de la VM donde se está ejecutando el role.

public override bool OnStart()
 {
    ConfigurarTraza();
    // Set the maximum number of concurrent connections 
    ServicePointManager.DefaultConnectionLimit = 12;
    //1. Crea el objeto myDrive
    myDrive = new CloudDriveHelp();
    //2. Inicia el Drive y lo monta
    myDrive.IniciarDisco();
    return base.OnStart();
 }

Este método se llama cuando el Worker Role se detiene, es decir desde el método OnStop() como muestra el siguiente código

public override void OnStop()
        {
            myDrive.FinalizarDisco();
        }

Paso 7: Probar el funcionamiento del disco CloudDrive

Para validar que nuestro disco este funcionado correctamente vamos a ejecutar el proyecto en modo debug e inspeccionar el disco. Esto lo haremos en el emulador, para simplificar. Una vez ejecutado el proyecto y que el Worker Role ya se inicie vamos al Storage Emulator y utilizando el menú abrimos la carpeta donde se almacenan los Azure Drive.

Ahora, vamos a la carpeta Drives y vemos que se ha creado nuestro disco myexportdrive.vhd como se muestra en la siguiente imagen.

Paso 8: Agregar la referencia necesaria para Data-tier Application Framework (DAC)

Ahora vamos a utilizar DAC para acceder a Azure Sql Database y hacer un export de una base de datos. Para ello debemos agregar la referencia a Microsoft.SqlServer.Dac.dll. Esta DLL es parte de los prerrequisitos que aparecen al inicio del artículo.

Paso 9: Crear una clase Helper para manejar DAC

Para simplificar el uso de DAC vamos a utilizar la clase DACHelp. El método principal que implementamos es Exportar() que realiza un export de la base de datos a un archivo. La idea es que ese archivo se almacene en el CloudDrive.

El argumento Mensajes y Progreso son referencia a manejadores de eventos, que reciben información del progreso y mensajes de la tarea de exportación.

 public bool Exportar(string sourceDatabaseNamestring targetConnectionStringstring fullNombreArchivo, 
        EventHandler<DacMessageEventArgs> MensajesEventHandler<DacProgressEventArgs> Progreso)
    {
        try
        {
            DacServices svc;
            //1. Crear el servicio DAC con la cadena de conexión
            svc = new DacServices(targetConnectionString);
            if (Mensajes != null)
            {
                //a. asignar los métodos de manejo de eventos de mensajes y Progreso
                svc.Message += new EventHandler<DacMessageEventArgs>(Mensajes);
                svc.ProgressChanged += new EventHandler<DacProgressEventArgs>(Progreso);
            }
            //2. Ejecutar el Export de la base de datos a un archivo
            svc.ExportBacpac(fullNombreArchivosourceDatabaseName);

        }
        catch (DacServicesException )
        {

            throw;
        }
        //TODO: dar mejor info del proceso
        return true;
    }

Paso 10: Crear método para copiar la DB

Para poder copiar la base de datos vamos a utilizar una instrucción SQL que nos permite crear una base de datos como una copia de una existente. Este comando se debe ejecutar en la base de datos MASTER. El comando SQL a ejecutar es:

CREATE DATABASE {nombreCopia} AS COPY OF {nombreDB}

Una vez ejecutado el comando de copia debemos esperar que la copia esté lista antes de continuar con el proceso de export. Para saber el estado de la copia ejecutamos el comando SQL

select SD.state_desc AS [Current State] from sys.databases SD

LEFT JOIN sys.dm_database_copies DDC ON DDC.database_id = SD.database_id

WHERE SD.name = ‘nombreCopia’

La idea es repetir esta consulta hasta obtener como respuesta «ONLINE» que significa que la copia terminó y podemos utilizarla para exportar el esquema y los datos. El tiempo que se espera, medido e segundos, se almacena en la variable segundos. El valor se lee desde el archivo de configuración app.config, de la llave RespaldarCada[Minutos]. Lo coloqué ahí para poder cambiarlo libremente sin tener que hacer update de la configuración del ROL con propósitos de pruebas. Las llaves que se agregarón al app.config se muestran a continuación.

<appSettings>
    <add key="switchvalue ="1"/>
    <add key="transferTimevalue ="5"/>
    <add key="snapshotPoolingSegundosvalue ="30"/>
    <add key="RespaldarCada[Minutos]value ="60"/>
  </appSettings>

El método Copiardb() es el siguiente.

public string  CopiarDb(string sourceDatabaseNamestring MasterConnectionString)
{
    string  dbCopyName;
    bool copySw = true;
    string currentState;
    string comando;
    Random random = new Random();
    int randomNumber = random.Next(0100);
    try
    {
        //1. Nombre de la DB copia
        dbCopyName = string.Format("{0}_Copy_{1}"sourceDatabaseNamerandomNumber.ToString());

        using (SqlConnection con = new SqlConnection(MasterConnectionString))
        {
            //2. Comando crear base de datos como copia de otra
            comando = string.Format("CREATE DATABASE {0} AS COPY OF {1}",dbCopyNamesourceDatabaseName);
            con.Open();
            using (SqlCommand command = new SqlCommand(comandocon))
            {
                //4.Ejecutar el comando de copia
                command.ExecuteNonQuery();
            }
            Trace.WriteLine("Copiando base de datos:" + dbCopyName"Snapshot");
            do
            {
                //3. comando para consultar el estado de la copia, ya que toma tiempo
                comando = 
                    string.Format(
                        "select SD.state_desc AS [Current State] from sys.databases SD LEFT JOIN sys.dm_database_copies DDC " + 
                        " ON DDC.database_id = SD.database_id WHERE SD.name = '{0}'"dbCopyName);
                using (SqlCommand command = new SqlCommand(comandocon))
                {
                    //4. ejecutar la consulta del estado de la copia
                    currentState = command.ExecuteScalar().ToString();
                    Trace.WriteLine(currentState + ": " + dbCopyName"Snapshot");
                }
                if (currentState == "ONLINE")
                {
                    //La copia esta lista
                    copySw = false;
                }
                if (currentState == "SUSPECT")
                {
                    //la copia fallo
                    throw new Exception("The 'CREATE DATABASE AS COPY OF' operation failed");
                }
                //Esperar antes de volver a preguntar por el estado de la copia
                int segundos = int.Parse(System.Configuration.ConfigurationManager.AppSettings.Get("snapshotPoolingSegundos"));
                System.Threading.Thread.Sleep(segundos*1000);
            } while (copySw);
        }

    }
    catch (Exception X)
    {
        Trace.WriteLine("Error: " + X.Message,"Snapshot");
        throw;
    }
    return dbCopyName;
} 

Paso 11: Crear el método para borrar la copia de la DB

Borrar una base de datos es simple utlizando una sentencia SQL estando conectado a la base de datos Master. Sólo se ejecuta

DROP DATABASE [NombreBaseDatos]

El método implementado es el siguiente

public void BorrarDB(string dbNamestring MasterConnectionString)
    {
        try
        {
            using (SqlConnection con = new SqlConnection(MasterConnectionString))
            {
                string comando = string.Format("DROP DATABASE {0}"dbName);
                con.Open();
                using (SqlCommand command = new SqlCommand(comandocon))
                {
                    command.ExecuteNonQuery();
                }
            }
        }
        catch (Exception x)
        {
            Trace.WriteLine(x.Message,"Borrar DB error:");
        }
    }

Paso 12: Crear los manejadores de eventos y configuración de la traza

Para poder recibir y mostrar el estado de avance, así como los mensajes del proceso de Export vamos a crear dos métodos que escriben esos detalles en la traza. Los métodos son los siguientes y se crean en el Worker Role.

public void receiveDacServiceMessageEvent(object senderDacMessageEventArgs e)
{
    Trace.WriteLine(string.Format("Message Type:{0} Prefix:{1} Number:{2} Message:{3}", 
        e.Message.MessageTypee.Message.Prefixe.Message.Numbere.Message.Message));
}
public void receiveDacServiceProgessEvent(object senderDacProgressEventArgs e)
{
    Trace.WriteLine(string.Format("Progress Event:{0} Progrss Status:{1}"e.Messagee.Status));
}

Cuando trabajamos en el emulador, toda la traza aparece en la consola del emulador. Ahora, cuando estamos en Azure y queremos ver la traza debemos configurar dónde vamos a almacenar la información de la traza. En este caso utilizaremos una tabla en el store. Para ello debemos agregar lo siguiente en el app.config

<system.diagnostics>
        <trace>
            <listeners>
                <add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                    name="AzureDiagnostics">
                    <filter type="" />
                </add>
            </listeners>
        </trace>
    </system.diagnostics>

Luego en el método OnStrat() llamar al método ConfigurarTraza() que se hace cargo de configurar la conexión a la tabla y el tiempo de transferencia de los datos. El método es el siguiente.

private void ConfigurarTraza()
{
    string wadConnectionString = "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString";
    CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
                                            RoleEnvironment.GetConfigurationSettingValue(wadConnectionString));
    RoleInstanceDiagnosticManager roleInstanceDiagnosticManager = 
                                        storageAccount.CreateRoleInstanceDiagnosticManager(
                                                RoleEnvironment.DeploymentIdRoleEnvironment.CurrentRoleInstance.Role.Name, 
                                                RoleEnvironment.CurrentRoleInstance.Id);
    DiagnosticMonitorConfiguration config = roleInstanceDiagnosticManager.GetCurrentConfiguration();
    if (config == null)
    {
        config = DiagnosticMonitor.GetDefaultInitialConfiguration();
    }
    //Configura cada cuanto tiempo se envía la información a la tabla
    double transferTime = double.Parse(System.Configuration.ConfigurationManager.AppSettings.Get("transferTime"));
    config.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(transferTime);
    config.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;
    roleInstanceDiagnosticManager.SetCurrentConfiguration(config);
}

La configuración que utiliza para conectarse a la tabla se define en la configuración del Web Role.

Paso 13: Invocar el proceso de copia, Export y borrado en el método Run()

El método Run() del Worker Role implementa el proceso de copia, exportación y borrado de la base de datos. Esto se realiza casa cierto tiempo definido en minutos en la variable de configuración «RespaldarCada[Minutos]«. El proceso es controlado por la variable de configuración «switch» del app.config. La idea es que si esa llave tiene el valor 0 no se realiza el proceso. Esto está pensando en poder detener los export sin necesidad de bajar el Worker Role.

Al iniciar el proceso se captura la hora de inicio para poder calcular el tiempo que toma el proceso. Luego se obtiene desde la configuración del role el nombre de la base de datos, la cadena de conexión a la db a exportar y a la Master. Con estos datos comienza el proceso con el primer paso que es copiar la base de datos. Para ello se invoca el método privado CopiarDb() que retorna el nombre de la copia.

private string CopiarDB(string NombreBaseDatosstring CadenaConexionDbMaster)
{
    string respuesta = null;
    //1. Crer el Helper
    DACHelp myDAC = new DACHelp();
    try
    {
        DateTime tiempoInicio = System.DateTime.Now;
        respuesta = myDAC.CopiarDb(NombreBaseDatosCadenaConexionDbMaster);
        Trace.WriteLine("Tiempo Copia: " + DateTime.Now.Subtract(tiempoInicio).ToString(), "Copia DB");
    }
    catch (Exception x)
    {
        Trace.WriteLine("************************************************************");
        Trace.WriteLine(x.Message"Error Copia DB:");
        Trace.WriteLine("************************************************************");
    }

    return respuesta;
}

Si la copia es exitosa, esto lo sabe porque CopiarDB() entrega el nombre de la base de datos copiada, entonces se llama el método privado exportar() que también utiliza DACHelp. Este es el encargado de formar el nombre del archivo donde se guarda el export, agregándole al nombre la fecha y hora en que se hizo. Un punto importante de notar en el nombre del archivo es como se obtiene la ruta del mismo utilizando myDrive.azureDrivePath. Cuando se monta un disco se le asigna una letra, pero no se asegura que siempre sea la misma por lo cual es importante recuperar la letra asignada para confeccionar el nombre completo del archivo.

También, al invocar el Exportar indica cuales son los manejadores de eventos que reciben la información del proceso y sus mensajes.

private void exportar(string NombreBaseDatosstring targetConnectionStringstring idRespaldo)
{
    //1. Crer el Helper
    DACHelp myDAC = new DACHelp();
    //2. Arma el nombre del archivo para el BACPAC
    string fullNombreArchivo = string.Format("{0}{1}_{2}.BACPAC "myDrive.azureDrivePathNombreBaseDatosidRespaldo);
    try
    {
        //3. Ejecuta el Exportar del Helper
        myDAC.Exportar(NombreBaseDatostargetConnectionStringfullNombreArchivothis.receiveDacServiceMessageEventthis.receiveDacServiceProgessEvent);
    }
    catch (Exception x)
    {
        Trace.WriteLine("************************************************************");
        Trace.WriteLine(x.Message,"Error Copiar DB:");
        Trace.WriteLine("************************************************************");
    }  
}

Una vez terminado el export, se manda a la traza el tiempo que se demoró el proceso y se listan los respaldos que se encuentran en el cloudDrive.

Por último, se llama al método privado BorrarTemp() para que borre la copia de la DB. Este también es un método simple porque utiliza DACHelp.

private void BorrarDBTemp(string nombreDbTmpstring CadenaConexionDbMaster)
{
    //1. Crer el Helper
    DACHelp myDAC = new DACHelp();
    //2. Arma el nombre del archivo para el BACPAC
    try
    {
        //3. Ejecuta el Exportar del Helper
        myDAC.BorrarDB(nombreDbTmpCadenaConexionDbMaster);
    }
    catch (Exception x)
    {
        Trace.WriteLine("************************************************************");
        Trace.WriteLine(x.Message"Error Borrar DB TMP:");
        Trace.WriteLine("************************************************************");
    }
}

Por último el método Run() del Worker Role espera el tiempo definido en minutos en la variable de configuración RespaldarCada[Minutos] para iniciar nuevamente el proceso completo.

Paso 14: Probar el proceso completo en el Emulador

Ahora estamos listos para probar el proyecto apuntando a una DB en Azure y utilizando el Storage del emulador. Si se ejecuta el proyecto, en la consola del emulador se puede ver el progreso.

Como primer paso aparece el mensaje de copiando la base de datos AdventureWorks2012 a AdventureWorks2012_Copy_94. 94 es un número que se genera aleatoriamente para evitar que se repitan los nombres de la bases de datos de copia. EL mensaje en la consola es:

Snapshot: COPYING: AdventureWorks2012_Copy_94

Cuando termina la copia de la base de datos aparece el mensaje:

Copia DB: Tiempo Copia: 00:05:37.8043059

Ahora comienza el proceso de exportación. En la consola se pueden ver información del proceso y los mensajes de DAC. Por ejemplo el evento cuando completa el export de la tabla : [Sales].[CurrencyRate] es el siguiente:

Progress Event:Processing table: [Sales].[CurrencyRate] Progrss Status:Completed

Por otra parte, un ejemplo de un mensaje donde muestra la cantidad de registros exportados de la tabla [Sales].[PersonCreditCard] sería:

Message Type:Message Prefix:SQL Number:73152 Message:Table [Sales].[PersonCreditCard] has exported 19118 rows.

Yo estoy utilizando la base de datos AdventureWorks2012 que descargue desde el siguiente link.

http://msftdbprodsamples.codeplex.com/releases/view/55330

Las propiedades de la base de datos que me muestra el portal de administración se muestran en la Siguiente imagen.

El tamaño de la base de datos es de 141.51 MB y en mi prueba tomó 5 minutos con 37 segundo las copia de la base de datos. La exportación del esquema y los datos tomó 9 minutos con treinta y dos segundos. Cuando el proceso termina nos entrega la siguiente información:

Progress Event:Exporting data from database Progrss Status:Completed

Information: Fin Respaldo

Information: Tiempo Respaldo: 00:09:32.6295793

info respaldo:: The directory a:\ contains the following files:

info respaldo:: The size of AdventureWorks2012_2013-01-10_07-15-02-PM.BACPAC is 17681362 bytes.

Information: proximo respaldo:3600000

Ahora, revisando el disco virtual en el Storage Emulator podemos ver que el archivo pesa 17 MB.

Paso 14: Configurar la instalación de DAC Framework

Una vez probado en el ambiente local podemos preparar el proyecto para el Deploy en Azure. Cuando se ejecuta en Azure el Worker Role, es necesario instalar los componentes de Data-Tier Application Framework de la versión 2008 R2 y de la versión 2012. Estos podemos encontrarlos respectivamente en los siguientes links:

http://www.microsoft.com/en-us/download/details.aspx?id=35756

http://www.microsoft.com/en-us/download/details.aspx?id=24000

Los DAC en la versión 2012 requiere instalar los MSI de x86 y x64 para correr en los sistemas operativos de 64 bits.

Para instalar estos componentes vamos a agregar los archivos MSI al proyecto para poder instalarlos antes de ejecutar el ROL. La siguiente imagen muestra los archivos ya incorporados al proyecto.

Una vez agregados, vamos a sus propiedades y configuramos Copy to Output Directory con el valor Copy always. Esto es necesario para que cuando se hace deploy en la VM del Worker Role, se copien los instaladores.

Ahora agregamos un archivo llamado intallDAC.cmd al proyecto. Este archivo para asegurarnos que se ejecuta demos grabarlo con la codificación Western European (Windows) codepage 1252. Esto se hace en la opción Adavanced Save Options. En este cmd también vamos a sus propiedades y configuramos Copy to Output Directory con el valor Copy always.

El archivo CMD lo primero que hace es discriminar si se está corriendo en el emulador o no. Si se corre en el emulador no instala los MSI. Esto porque en mi maquina ya tengo todo instalado y no es necesario.

REM   Check if this task is running on the compute emulator.
IF "%ComputeEmulatorRunning%" == "true" (
    REM   This task is running on the compute emulator. Perform tasks that must be run only in the compute emulator.
) ELSE (
		ECHO This task is running on the cloud. Perform tasks that must be run only in the cloud.

	 IF EXIST "MSI\DAC2012x64\log_DACFramework.txt" (

		ECHO Ya se instalaron los MSI. 

	) 	ELSE 	(
		ECHO \DAC2012x64
		MSI\DAC2012x64\DACFramework.msi  /qn /l* MSI\DAC2012x64\log_DACFramework.txt
		MSI\DAC2012x64\SQLSysClrTypes.msi  /qn /l* MSI\DAC2012x64\log_SQLSysClrTypes.txt
		MSI\DAC2012x64\SqlDom.msi  /qn /l* MSI\DAC2012x64\log_SqlDom.txt

		ECHO \DAC2012x86
		MSI\DAC2012x86\DACFramework.msi  /qn /l* MSI\DAC2012x86\log_DACFramework.txt
		MSI\DAC2012x86\SQLSysClrTypes.msi  /qn /l* MSI\DAC2012x86\log_SQLSysClrTypes.txt
		MSI\DAC2012x86\SqlDom.msi  /qn /l* MSI\DAC2012x86\log_SqlDom.txt

		ECHO \DAC2008R2
		MSI\DAC2008R2\DACFramework.msi  /qn /l* MSI\DAC2008R2\log_DACFramework.txt
		MSI\DAC2008R2\SQLSysClrTypes.msi  /qn /l* MSI\DAC2008R2\log_SQLSysClrTypes.txt
		MSI\DAC2008R2\TSqlLanguageService.msi  /qn /l* MSI\DAC2008R2\log_TSqlLanguageService.txt
		MSI\DAC2008R2\SharedManagementObjects.msi  /qn /forcerestart /l*  MSI\DAC2008R2\log_SharedManagementObjects.txt
	)
)

Ahora, si está corriendo en Azure valida que los componentes no hayan instalado antes. Para eso lo que hace es ver si existe el archivo log_DACFramework.txt que se crea al instalar utilizando este CMD. Esta validación es necesaria porque al instalar el último componente se reinicia la vm del Worker Role. Sin esta validación, la VM entraría en el loop infinito de reinicios. El archivo CMD es el siguiente.Por último, en el archive ServiceDefinition.csdef debemos agregar la tarea de inicio para que se ejecuten las instalaciones de los archivos MSI y la variable de control ComputeEmulatorRunning para poder saber si estamos en el emulador o en Azure. Esto se hace agregando el siguiente TAG al archivo.

<Startup>
      <Task commandLine="installDAC.cmdexecutionContext="elevatedtaskType="background">
        <Environment>
          <Variable name="ComputeEmulatorRunning">
            <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
          </Variable>
        </Environment>
      </Task>
    </Startup>

Más información de cómo definir tareas de inicio para un role pueden verlo en How to Define Startup Tasks for a Role

Paso 15: Probar el Deploy en Azure

Ahora hacemos deploy a Windows azure. Para poder revisar el archivo en el CloudDrive, activamos en el wizard de publicación la opción de Remote Desktop como muestra la siguiente imagen.

Una vez hecho el deploy, podemos revisar que tengamos nuestro Azure CloudDrive.

Se puede revisar en el disco de la aplicación, que se encuentran los archivos de LOG de instalación, como se de en la siguiente imagen.

Como estamos en Azure y no en el emulador, para poder ver el avance nos conectamos a la tabla de Traza para poder ver el avance. La cadena de conexión es la que se configuró en las propiedades del role. Podemos ver la traza de inicio del respaldo y que se comenzó a hacer la copia de la base de datos con el nombre AdventureWorks2012_Copy_43.

Cuando termina la copia de la base de datos, aparece el registro que nos indica que la copia tomo 16 minutos.

Copia DB: Tiempo Copia: 00:16:05.5937425

Por último podemos ver el término del proceso, el cual tomó 19 minutos y generó un archivo de 17MB.

Como validación final, vamos al disco F que en este caso es nuestro CloudDrive y vemos el archivo BACPAC creado de 17 MB, es decir ya tenemos el respaldo de nuestra base de datos.

Al cierre

Este ejemplo implementa el proceso de copia, exportación y borrado de la copia de una base de datos de Azure en un CloudDrive. El uso de CLoudDrive es opcional, ya que se podría haber implementado el guardar los archivos directamente en el Blob Storage sin usar un disco virtual.

El código es de ejemplo y no debe ser utilizado en producción, puede ser descargado desde aquí.

Otros artículos relacionados con Azure: