MSDyn365 y Azure DevOps ALM

Introducción

Lo desarrolladores de X++ hemos estado trabajando sin control de versiones la mayor parte de nuestras carreras. Teníamos MorphX VCS para AX 2009 y la opción de usar TFVC en AX 2009 y AX 2012, pero no era obligatorio. De hecho, y siempre desde mi experiencia personal, la mayoría de los proyectos no usaban ningún control de versiones más allá de los comentarios en el código. No digo que todos fueran así, pero en 10 años sólo vi un proyecto de AX 2009 que lo usara.

Si le contásemos esto a un desarrollador de otra tecnología pensaría que estamos locos y que probablemente seamos estúpidos por no usar un control de código. ¿Quién se arriesgaría a perder su trabajo por culpa de un error tonto? Porque a eso nos arriesgábamos. ¿Conocéis a alguien que perdiera el trabajo de un día por darle al botón equivocado? ¡Yo sí!

El AOT antes de la llegada del control de versiones
The AOT before source control, by cazapelusas.com

Uno de los mayores cambios que nos han llegado con Finance and Operations es el uso obligatorio de un sistema de control de versiones.

Lo que sigue a continuación es producto de varios posts escritos en ariste.info. He reescrito el contenido para adaptarlo a los cambios y nuevas funcionalidades que hemos recibido en Azure DevOps para Microsoft Dynamics 365 FnO y he intentado reordenarlo para ofrecer una lectura más lógica.

También intentaré mantener este documento actualizado si algo cambia, pero no lo puedo garantizar. Si encontráis un error me podéis contactar en adria (arroba) ariste (punto) info.

Espero que esta guía ayude a todos los desarrolladores. Necesitamos una comunidad de desarrollo fuerte y que haga un uso correcto de estas herramientas para poder llevar a cabo mejores implantaciones de nuestros proyectos de Dynamics 365.

Contenido ocultar
4 Azure hosted build para Dynamics 365 Finance & SCM

Dynamics 365 for Finance & Operations y Azure DevOps

Azure DevOps

Azure DevOps será el servicio que usaremos para el control de código. Microsoft Dynamics 365 for Finance and Operations soporta TFVC por defecto como sistema de control de versiones.

Pero Azure DevOps no sólo ofrece una herramienta de control de código. Por supuesto que seremos los desarrolladores los que más nos beneficiaremos de usarlo, pero desde la gestión del proyecto al equipo funcional y clientes pueden sacarle partido. La sincronización del BPM y la creaciñon de tareas, planificación del equipo, control de código y builds y releases automatizadas son algunas de las herramientas que ofrece. Todos estos cambios exigen algo de aprendizaje por nuestra parte, pero a corto plazo todo esto nos ayudará llevar mejor las implantaciones.

Como he dicho, el equipo técnico es el más afectado por esto, pero también el mas beneficiado…

Primeros pasos

Para usar todas las características descritas en esta guía necesitamos crear un proyecto de Azure DevOps y conectarlo a LCS. Este será el primer paso y es obligatorio, así que vamos a ver como lo hacemos.

Crear una organización en Azure DevOps

Puede que tengas que hacer esto o puede que no. Si tú o tu cliente ya tenéis una cuenta la podéis usar y crear el nuevo proyecto en ella. Si no es así nos dirigimos a https://dev.azure.com y creamos una nueva organización:

Azure DevOps sign up
Crear la cuenta en Azure DevOps

Después de crearla tenemos que crear un nuevo proyecto con estas opciones:

Create Azure DevOps project
Crear un proyecto de Azure DevOps

Le damos al botón “Create project” y listo. Ahora vamos a conectar este proyecto de Azure DevOps con nuestro proyecto de LCS.

Cuando un cliente compra una suscripción de Finance and Operations el tipo de proyecto de LCS es “Implementation project” y se crea automáticamente. El cliente te tiene que invitar a su proyecto. Si trabajas en un ISV puedes usar un proyecto de tipo “Migrate, create solutions, and learn”.

En cualquiera de los casos, necesitas ir a «Project settings» y seleccionar la pestaña “Visual Studio Team Services”. Vamos abajo del todo y deberías ver dos campos. Rellenamos el campo con la URL de nuestro DevOps sin la parte del proyecto. Si tu URL es del tipo https://dev.azure.com/YOUR_ORG URL tienes que poner https://YOUR_ORG.visualstudio.com:

Azure DevOps setup on LCS
Azure DevOps en LCS

Para obtener el “Personal access token” volvemos a Azure DevOps, hacemos clic en el icono de usuario y seleccionamos “Personal access tokens”:

MSDyn365 y Azure DevOps ALM 1

Añadimos uno nuevo, le ponemos una fecha de fin y le damos acceso completo. Finalmente pulsamos el botón “Create” y aparecerá un nuevo diálogo con nuestro token, lo copiamos y lo pegamos en LCS.

Azure DevOps personal token
Azure DevOps personal token

Volvemos a LCS y, una vez hemos pegado el toke, pulsamos en el botón “Continue”. En el siguiente paso seleccionamos nuestro proyecto, pulsamos “Continue” y para acabar en “Save” en el útlimo paso.

Si tenéis algún problema podéis echarle un vistazo a los docs, donde está todo perfectamente documentado.

El servidor de build

Una vez tenemos LCS enlazado con Azure DevOps tenemos que despligar la máquina de build. Esto será el corazón de nuestros procesos de CI/CD.

Aunque la máquina virtual de build tiene la misma topología que una de desarrollo, no es una máquina de desarrollo y nunca debería usarse como tal. ¡No desarrolléis en ella! Tiene Visual Studio, la carpeta AosService con todos los paquetes y un SQL Server con una AxDB, como una máquina de desarrollo, pero ese no es su propósito.

No usaremos nada de eso directamente. El «corazón» de la máquina de build es el agente de build, una aplicación que usa Azure DevOps para ejecutar las tareas de las definiciones de build en Azure DevOps.

También podemos usar agentes hospedados en Azure. Estos nos permiten ejecutar builds sin una máquina virtual. Lo veremos más adelante.

La VM de build

Esta VM está normalmente en la suscripción de Microsoft, pero se puede usar otra máquina virtual de las hospedadas en la nube como VM de build.

Cuando se despliega esta VM pasan dos cosas: se crea la estructura de carpetas básica en nuestro árbol de control de código y se crea la definición de build por defecto.

MSDyn365 y Azure DevOps ALM 2
MSDyn365 y Azure DevOps ALM 3

Visual Studio

Ya tenemos lo básico para empezar a trabajar. Entramos a nuestra máquina de desarrollo y abrimos Visual Studio. Primero tenemos que mapear la carpeta Main contra la carpeta de los paquetes de nuestra máquina. Abrimos el team explorer y seleccionamos “Connect to a Project…”:

MSDyn365 y Azure DevOps ALM 4

Nos pedirá las credenciales y después nos mostrará todos los proyectos que tenemos. Seleccionamos el que hemos creado antes y hacemos clic en “Connect”:

MSDyn365 y Azure DevOps ALM 5

Ahora abrimos el “Source Control Explorer”, seleccionamos la carpeta Main y hacemos clic en el texto “Not mapped”:

MSDyn365 y Azure DevOps ALM 6

Mapeamos la carpeta main contra K:\AosService\PackagesLocalDirectory en nuestra unidad de servicio (que podría ser la unidad C si estáis usando una VM local en vez de una en Azure):

MSDyn365 y Azure DevOps ALM 7

Lo que acabamos de hacer es decirle a Visual Studio que lo que está en nuestra carpeta Main de Azure DevOps irá en la carpeta local K:\AosService\PackagesLocalDirectory de nuestra VM de desarrollo.

Con esto ya podemos mapear las máquinas de desarrollo y empezar a trabajar. La carpeta Main que veis en la imagen es una carpeta normal, pero la podremos convertir en una rama en caso de que lo necesitemos.

The Main folder we have in our source control tree is a regular folder, but we can convert it into a branch if we need it.

MSDyn365 y Azure DevOps ALM 8
MSDyn365 y Azure DevOps ALM 9

En la imagen de arriba podemos ver que el icono de Main cambia cuando se convierte en una rama. Las ramas (branches en inglés) nos ofrecen funcionalidades que no están disponibles en las carpetas. Lo podemos ver en el menú contextual:

Menú contextual carpeta
Menú contextual carpeta
Menú contextual rama
Menú contextual rama

 

 

 

 

Por ejemplo en las ramas podemos ver la jerarquía de las distintas ramas del proyecto (En este caso que sólo trabajamos con dos ramas no parece muy útil :P).

Jerarquía de las ramas

También son distintas las ventanas de propiedades de ambas. Las de una carpeta:

MSDyn365 y Azure DevOps ALM 10

Y las propiedades de una rama, donde podemos ver las relaciones y las ramas que se han creado a partir de ella:

Propiedades de la rama

Todo esto son detallitos, pero quizás lo que mas nos interese de convertir Main en una rama es que nos permitirá ver dónde se ha mergeado qué, como veremos en un próximo post 😛

Un consejo

La carpeta de Projects es buena idea ponerla en la raíz del proyecto de DevOps (al mismo nivel que BuildProcessTemplates y Trunk). Si no se cambia y acabáis trabajando con una rama de dev y la de Main, los check-in de las soluciones y proyectos de Visual Studio se seguirán haciendo en Main (porque la carpeta de proyectos estará ahí). Os ahorrará microinfartos cuando veáis la lista de changesets en el correo de la build de Main 🙂

Los que llevamos años trabajando con AX nos habíamos acostumbrado a no usar un control de versiones. MSDyn365FO nos ha llevado a un terreno sin explorar, con lo que no es raro que cada equipo haya decidido trabajar de una forma u otra según las experiencias que se hayan ido encontrando. Evidentemente, hay un componente del interés de los miembros de estos equipos para investigar un poco por su cuenta sobre la gestión de código, ramas y metodologías. Muchas veces a base de experimentación y prueba-error, y con las prisas de algunos proyectos esto sale mal, o muy mal. Y aquí he echado de menos un poco de guidance por parte de Microsoft (que igual la hay y me lo he perdido).

A pesar de esta quejita el camino y el aprendizaje ha sido, y creo que lo que viene también será, bastante divertido 😉

Estrategias de branching

Vaya por delante que no soy, ni mucho menos, un experto en gestión del código ni Azure DevOps. Todo lo que viene a continuación es fruto, como comentaba antes, de la experiencia (y las meteduras de pata) de casi 3 años trabajando con MSDyn365Ops. En este artículo de la documentación sobre estrategias de branching hay más información sobre branching y links a artículos del equipo de DevOps. Y en la Biblioteca de herramientas y guías de los DevOps Rangers hay incluso muchísimo más!

Main – Release

Esta es de las más simples, trabajaremos en la rama Main, todos los desarrolladores. Seguiremos el trabajo ahí hasta el arranque.

Cuando vayamos a arrancar haremos un branch de Main y crearemos Release. Esta nueva rama será la que usaremos para promocionar el código a producción. El desarrollo de nuevas características y bugs se hará en Main.

Cuando terminamos un desarrollo, o arreglamos un bug, haremos un merge del changeset (o changesets) a la rama Release. Sí, haremos cherry picking, sé que tiene mala reputación pero dependemos de que los clientes validen los cambios…

En nuestros proyectos solemos tener por lo menos 2 entornos Tier 2+. Usamos uno para testing y validación. En éste entorno desplegamos el DP creado con la rama Main. En el otro Tier 2+ los usuarios hacen sus pruebas y es el que usamos para promocionar el código a prod. Este segundo entorno es el que se actualizará con el código de Release.

Dev – Main – Release

Esto es algo que hemos estado haciendo últimamente para intentar emular las ramas de desarrollo tipo Git. Estamos usando una rama Dev para cada desarrollador. Trabajamos cada uno en nuestra rama, hacemos todos los check-ins que queremos mientras desarrollamos y, cuando está terminado, lo mergeamos todo en un único changeset a Main. Después hacemos Forward Reverse Integrate de Main a nuestra rama Dev para traer todos los desarrollos de los demás programadores.

Sí, esto implica un poco más de merges en la parte de Dev-Main. Pero la idea es tener una lista limpia de changesets únicos en nuestra rama Main para cada desarrollo. ¿Por qué? Porque… cherry picking…

Trabajaremos con Dev y Main hasta el arranque, entonces haremos branch de Main y crearemos la rama Release. Actualizaremos los entornos Tier 2+ de la misma manera que con la estrategia Main ‘ Release.

Como he dicho la idea es tener una lista limpia de changesets para mover los desarrollos de Main a Release y resolver todos los conflictos de merging en las ramas Dev. Cada desarrollador es responsable de su rama y de resolver los conflictos ahí.

Hemos estado trabajando unos meses de esta forma y los resultados son positivos y no estamos teniendo problemas con la gestión. En el futuro lo probaremos con Git pero esto lo va a explicar Juanan!

Azure Pipelines

Builds

Ya hemos visto que la definición de build por defecto viene con todos los pasos activos. Podemos deshabilitar (o borrar) los que no vayamos a usar. Por ejemplo, los pasos de testing se pueden borrar si no hacemos unit testing. También podemos crear nuevas definiciones de build de cero, pero es mucho más fácil duplicar la estándar y modificarla.

Con la versión 8.1 de MSDyn365FO han desaparecido los hotfixes de código X++, todos son binarios. Esto lo que implica es que en la carpeta Metadata de nuestras ramas ya no van a aparecer los modelos del estándar, solo los nuestros.

Integración contínua

La integración contínua (CI por sus siglas en inglés) es el proceso de automatizar una build y testing del código cada vez que una persona del equipo manda cambios al control de versiones. (fuente)

¿Debería tu proyecto/equipo usar CI? Sí, sí, y sí! Es una de las claves de tener un proceso automatizado de build.

Así es como sería una build de CI que simplemente compile nuestro código:

Definicion build continua

Esta build no hace nada más, solo compila. Así a priori no parece muy útil pero si activamos la opción de integración continua:

DevOps continuous integration

Después de cada check-in de cada desarrollador se lanzará una build que compilará todo el código y fallará si hay algún error. ¿Claro que no debería fallar no? Porque todos compilamos los proyectos antes de hacer el check-in, ¿verdad?

tysonjaja

Pues por eso y porque las prisas son malas y a veces tenemos que vivir con ellas, esta build puede ser bastante útil. Sobretodo cuando lo tengamos configurado para la rama Main y nos «chive» los errores que pueden aparecer después de un merge con conflictos. Y cuando hay que pasar algo urgente a producción y tenemos poco margen nos interesa poder generar el paquete lo antes posible. Usando esta estrategia conseguimos que generar un DP con nuestros paquetes tardara 9 minutos en vez de 1h15m generando todo.

Gated check-ins

Con este tipo de check-in el código se compila ANTES de que el check-in se haga efectivo. Si falla la build el changeset no se hace efectivo hasta que se corrijan los errores y se vuelva a hacer el check-in.

A priori esta opción parece ideal para los check-in de merges de una rama de desarrollo a Main. Los problemas que me he encontrado con esta opción son varios:

  • Si haces múltiples merge y check-ins de un mismo desarrollo y el primero falla no se mergea, pero si el segundo compila correctamente sí.
  • Problemas con las notificaciones de errores y código pendiente al fallar el check-in
  • En merges con más de un check-in se encolan muchas builds (y por defecto solo tenemos un build agent disponible…)

Seguro que esto tiene solución, pero no he sabido encontrarla. Y de todas formas la opción de integración contínua que comentaba antes nos ha funcionado perfectamente para validar que la rama compila sin errores. Como digo todo esto ha sido fruto de la investigación del equipo y prueba-error.

Configurar las nuevas tareas de Azure DevOps para generar el paquete y versiones de modelos

Tarea Update Model Version

Esta es sencillita, simplemente hay que añadirla a tu definición de build debajo de la tarea actual, deshabilitas la original y listo. Si tienes algún filtro, excluyendo modelos por ej., necesitaras crear el filtro en el campo Descriptor Search Pattern usando la sintaxis de patrones de Azure DevOps.

Tarea Create Deployable Package

Esta tarea va a sustituir la Generate packages actual. Para configurarla correctamente necesitamos hacer un par de cambios a los valores que trae por defecto:

X++ Tools Path
MSDyn365 y Azure DevOps ALM 11

Esto es el directorio físico de tu VM de build donde está la carpeta bin. La carpeta AosService normalmente está en la unidad K en las VMs desplegadas en la suscripción del cliente. Probablemente esto cambie cuando pasemos a un modelo sin VMs para hacer las builds.

La ruta a la unidad se puede cambiar por $(ServiceDrive), quedando una ruta como $(ServiceDrive)\AOSService\PackagesLocalDirectory\bin.

Location of the X++ binaries to package
MSDyn365 y Azure DevOps ALM 12

La tarea viene con este campo rellenado con $(Build.BinariesDirectory) por defecto, pero esto no nos ha funcionado para nuestras builds, quizás esa variable no esta en el archivo proj. Sólo hay que cambiarlo por $(Agent.BuildDirectory)\Bin y el DP se generará sin problemas.

Filename and path for the deployable package
MSDyn365 y Azure DevOps ALM 13

La ruta en la imagen debería cambiarse por $(Build.ArtifactStagingDirectory)\Packages\AXDeployableRuntime_$(Build.BuildNumber).zip. Se puede dejar sin la parte de Packages pero entonces habra que cambiar el campo Path to Publish de la tarea Publish Artifact: Package de la definición.

Tarea Add Licenses to Deployable Package

Esta tarea añade las licencias a un Deployable Package que ya existe. Recuerda que la ruta del DP tiene que ser la misma que hayas configurado en la tarea Create Deployable Package.

Azure hosted build para Dynamics 365 Finance & SCM

¡Contemplad #XppGroupies! ¡El día que tanto hemos estado esperando ha llegado! Las Azure hosted builds (me cuesta mucho decir Build hospedada en Azure) ya están en preview pública con el PU35!! Ya podemos dejar de preguntarle a Joris cuando estará disponible, porque ya lo está!! Leed los Docs!!

He podido escribir esto porque, gracias a Antonio Gilabert, hemos podido probarlo durante la preview privada en Axazure durante unos meses. Y por supuesto gracias a Joris por habernos invitado a la preview!

Azure hosted builds
Cabalgando los Azure Pipelines por Caza Pelusas

¿Qué significa esto? No necesitamos ya la VM para ejecutar pipelines! Es broma, sí la necesitamos! Si estámos ejecutando tests o sincronizando la DB como parte de nuestro pipeline todavía necesitamos la VM. Pero podemos mover las builds de CI al agente de Azure.

También puedes leer mi guía sobre MSDyn365FO y Azure DevOps ALM.

Recordar que esto esta en preview privada. Si queréis uniros a la preview primero necesitáis ser parte del Insider Program donde podéis uniros al «Dynamics 365 for Finance and Operations Insider Community«. Una vez invitados deberías ver un nuevo proyecto en LCS llamado PEAP Assets, y dentro de la Asset Library en la sección Nuget package encontraréis los nugets.

Agentes de Azure

Con la Azure hosted build obtenemos la capacidad de ejecutar un agente extra y podemos ejecutar múltiples pipelines a la vez. Pero todavía no serán pipelines paralelas porque sólo tenemos un agente sin VM. Esto quiere decir que podemos ejecutar una build con el agente hospedado y otra con el de Azure, pero no podemos ejecutar dos del mismo tipo a la vez. Si queremos eso necesitamos comprar agentes extra.

Con un proyecto privado de Azure DevOps obtenemos 2GB de espacio para Artefactos (los veremos luego) y un agente de Azure con 1800 minutos gratuitos:

08CEA665 618A 4F15 B9EC F86A405FA7D8
Azure hosted build: precio de proyectos de Azure DevOps

Seguiremos manteniendo la VM de build, así que es difícil decirle a un cliente que necesitamos pagar más sin poder deshacernos de su coste. Además que hemos estado haciendo todo con un solo agente y ha ido todo bien, no? Así que nos tomamos esto como capacidad extra, podemos dividir las builds entre ambos agentes y dejar el agente de Azure para builds más cortas para aprovechar los 1800 minutos gratuitos lo máximo posible.

¿Cómo funciona?

No es nada mágico. Nos movemos de un agente que se ejecuta en la VM a uno que se ejecuta en Azure.

Las Azure hosted builds se apoyan en paquetes nuget para compilar nuestro código X++. El contenido de PackagesLocalDirectory, la platforma y el compilador han sido empaquetados en nugets y lo que solemos tener en la VM ahora está en 3 nugets.

Cuando la build se ejecuta, se descarga e instalan los nugets y los usan para compilar nuestro código en la Azure hosted build junto con los paquetes estándar.

¿Qué necesito?

Para configurar la Azure hosted build necesitamos:

  • Los 3 nugets de LCS: compilador, X++ de plataforma y X++ de Aplicación.
  • Un usuario con derechos suficientes a nivel de organización para subir los nugets a Azure DevOps.
  • Algo de paciencia para poner todo en marcha 🙂

Así que el primer paso es ir a LCS, al proyecto de PEAP, Asset Library y descargar los 3 paquetes nuget:

Nugets for the Azure Hosted Build
Nugets para las Azure Hosted Build

Artefactos de Azure DevOps

Esto se puede hacer tanto desde tu PC como desde una VM de desarrollo, pero necesitaremos añadir algunos archivos y un proyecto de VS a tu source control así que necesitaras una máquina de desarrollo seguro.

Ve a tu proyecto de Azure DevOps y ve a la sección de Artifacts. Aquí crearemos un nuevo feed y le daremos un nombre:

Azure DevOps artifact feed
Azure DevOps artifact feed

Con el proyecto gratis tenemos 2GB de espacio, el tamaño de los 3 nugets es de sobre 500MB, no deberías tener problemas a no ser que tengas más artefactos de otra cosa, .NET por ej.

Ahora pulsa «Connect to feed» y selecciona nuget.exe. Ahí verás las instrucciones para continuar ahí, pero lo explicaré igualmente.

Necesitamos descargar el nuget.exe y ponerlo en el PATH de Windows. También puedes dejarlo donde estén los nugets y olvidarte del PATH. Tú mismo. Finalmente instala el credential provider: descarga este script de Powershell y ejecútalo. Si el script no deja de pedir las credenciales y falla sin parar, prueba a añadir -AddNetfx como parámetro. Gracias a Erik Norell por encontrar esto y compartirlo en los comentarios del post original!

Crea un archivo nuevo llamado nuget.config en la misma carpeta donde hayas descargado los nugets. Tiene que tener el contenido que viene en la página «Connect to feed», algo así:

El contenido del archivo tiene que ser el mismo que venga en la página «Connect to feed».

Y para acabar, publicaremos los nugets en nuestro feed de artefactos. Tenemos que hacer esto para los 3 nugets:

Os pedirá usuario y contraseña. Recordad que tiene que tener permisos suficientes.

Por supuesto, tienes que cambiar «AASBuild» por el nombre de tu feed. Y ya hemos acabado con los artefactos.

Prepara Azure DevOps

El nuevo agente necesita una solución para compilar tus paquetes. Esto quiere decir que hay que crear una solución vacía en Visual Studio y poner nuestro paquete como el que usa el proyecto. Tal que así:

2020 04 24 14 20 58
Solución Visual Studio

Si tienes más de un paquete o modelo, necesitarás crear un proyecto para cada uno dentro de la solución.

Tenemos que crear otro archivo llamado packages.config con el siguiente contenido:

El tag de versión dependerá de cuando leas esto, pero el de arriba es el correcto para el PU35. Tendremos que actualizar este archivo cuando salgan versiones nuevas de los nugets.

Y para terminar esta parte necesitamos añadir la solución, el nuget.config y el packages.config a nuestro source control. Esto es lo que he hecho yo:

2020 04 24 14 29 01
Azure DevOps

Podéis ver que he creado una carpeta Build en la raíz de mi proyecto de DevOps. Eso sólo es una preferencia mía, pero prefiero tener sólo código en mis ramas, incluso los proyectos están fuera de la rama, sólo código para branchear y mergear. Ponemos los archivo sy la solución dentro (o dónde quieras) y listo.

Configurar pipeline

Ahora tenemos que crear un pipeline nuevo, podemos simplemente importar este template del recién creado proyecto X++ (Dynamics 365) Samples and Tools de Github. Despues de importarlo lo modificaremos un poco, pero inicialmente será así:

2020 04 24 14 35 07 1
Azure hosted build: pipeline recién importada

Como podéis ver el pipeline tiene todos los pasos necesarios para generar el DP, pero algunos de ellos, los relacionados con las tasks de Dynamics 365, no cargan bien después de importar. Tenéis que añadir esos pasos a mano una vez importado el pipeline y configurarlos a mano.

Pipeline root

2020 04 24 14 38 27

Tienes que seleccionar para el Agent Pool: Hosted Azure Pipelines, y vs2017-win2016 como Agent Specification.

Get sources

DevOps mappings
Azure hosted build: Our mappings

He mapeado dos cosas aquí: nuestro código en el primer mapeo y la carpeta Build que hemos creado antes con la solución y los archivos config. Si has puesto estos archivos en tu carpeta Metadata no necesitas el segundo mapping.

NuGet install Packages

Este paso obtiene los nugets y los instala para su uso en cada ejecución. He tenido problemas con esta task.

2020 04 25 12 41 47 2
Azure hosted build: nuget install

El comando usa los archivos config que hemos puesto en la carpeta Build y, como podéis ver, está obteniendo los archivos de $(build.sourcesDirectory)\Build como hemos configurado en el paso Get sources. Si habéis puesto esos archivos en otro sitio tenéis que cambiar el path para que se ajuste a vuestra configuración.

Update Model Version

Este es uno de los pasos que muestran errores pese a que tengo instaladas las herramientas de Dynamics 365 del marketplace en el proyecto de DevOps. Si lo tienes bien seguramente no tienes que tocar nada. Si tienes el problema simplemente añade la tarea «Update Model Version» y cámbialo par que quede así:

Update Model Version
Azure hosted build: Update Model Version

Tarea Build solution

MSDyn365 y Azure DevOps ALM 14
Build solution step

En este paso pondremos un valor comodín en el campo «Solution»: **\\*.sln. Si dejamos este comodín compilará todos los proyectos que tengas en el repo, y dependiendo del número de proyectos que tengas, la build podría fallar por timeout.

Yo resuelvo esto seleccionando una solución en concreto que contiene todos los modelos que tengo en el código. Esta solución la he guiardado en la carpeta Build, y si se crean nuevos modelos sólo hay que editarla.

Gracias a Ievgen Miroshnikov por decirmelo!

Podría haber algún problema adicional con los archivos rnrproj tal y como comenta Josh Williams en un comentario. Si tu proyecto fue creado antes del PU27 mejor crea una solución nueva para evitar problemas.

Create Deployable Package

Este es otro de los pasos que no me cargaron bien. De nuevo, añádelo y cambia lo que necesites:

2020 04 24 14 55 32
Azure hosted build: Create Deployable Package

Add Licenses to Deployable Package

Otro paso que me daba problemas. Hacemos lo mismo.

2020 04 24 14 57 35
Azure hosted build: Add Licenses to Deployable Package

Y eso es todo. Puedes ejecutar las Azure hosted builds para probar si funciona. Para las primeras ejecuciones puedes deshabilitar los pasos posteriores a «Build solution» para ver si los nugets se descargan y instalan bien y se compila tu código. Una vez esté eso podemos seguir con la generación y publicación de DPs.

Ya has configurado tu Azure hosted build, ahora te toca a ti decidir en qué casos usarlas el viejo agente o el hospedado en Azure.

Builds con Azure DevTest Labs

Llega el fin de las máquinas Tier-1 gestionadas por Microsoft, y esto nos deja sin la capacidad de poder sincronizar la DB o ejecutar tests, a no ser que despleguemos una nueva máquina de build en nuestra suscripción o la de nuestro cliente. Por supuesto esto puede traer preocupación por los costes de esta máquina, y para eso tenemos Azure DevTest Labs.

He escrito este post gracias a la sesión de Joris de Gruyter en la pasada DynamicsCon: Azure Devops Automation for Finance and Operations Like You’ve Never Seen! Y también ha habido bastante investigación y (un monton de) prueba y error por mi parte hasta que todo ha funcionado.

Azure DevTest Labs
Configurando la VM de Build en Azure DevTest Labs

Si quieres saber más sobre builds, releases y el ALM de desarrollo de Dynamics 365 puedes leer mi guía sobre MSDyn365 y Azure DevOps ALM.

Pero primero…

Lo que enseño en este post no es una guía paso a paso perfecta. Hay una alta probabilidad que si haces exactamente lo que yo he hecho no obtengas el mismo resultado. Pero es una buena guía para empezar y que hagas un poco de investigación y aprendas.

Azure DevTest Labs

Azure DevTest Labs es un servicio/herramienta de Azure que nos permite desplegar máquinas virtuales e integrarlas con pipelines de Azure DevOps, y otras cosas, pero lo que voy a explicar es sólo la parte de la máquina virtual y pipeline.

¿Qué es lo que voy a contar aquí? Cómo preparar una imágen VHD de Dynamics 365 Finance and Operations para ser usada como base para crear una máquina de build desde un pipeline de Azure DevOps, compilar nuestro código, sincronizar la base de datos, ejecutar tests, e incluso desplegar los reports, generar el deployable package y borrar la máquina.

Obtener y prepara el VHD

Esto es de largo la parte más tediosa de todo el proceso porque necesitamos descargar 11 ZIPs desde la Shared Asset Library de LCS, y todos sabemos lo rápido que bajan las cosas desde LCS.

MSDyn365 y Azure DevOps ALM 15
¿Cómo se descargan las cosas desde LCS?

Así que para ir más rápidos podemos crear un blob en una cuenta de almacenamiento de Azure y, una vez más, usar las d365fo.tools de Mötz Jensen y su comando Invoke-D365AzCopyTransfer. Sólo hay que ir a LCS, hacer click en el botón «Generate SAS link» para cada archivo, usarlo como el parámetro de origen en el comando y la URL SAS de tu blob en el de destino. Una vez tenemos todos los archivos en el blob los podemos descargar en nuestra máquina local a una velocidad más decente.

Cuando tenemos el VHD descomprimido tenemos que cambiar su tipo de Dynamic a Fixed usando este comando de PowerShell:

La razón para hacer esto es que no podemos crear una VM de Azure a partir de un VHD de tamaño dinámico. Y me costó unos cuantos intentos descubrir esto 🙂

Crear una cuenta de DevTest Labs

Para hacer esto necesitáis una cuenta de Azure. Si no tenéis una podéis conseguir una gratuita con 180 euros de crédito (200 dólares) para usar en 30 días, y muchos otros servicios gratuitos durante 12 meses.

Buscamos DevTest Labs en la barra superior y creamos un nuevo DevTest Lab. Una vez creado abrimos los detalles y deberíamos ver algo como esto:

Azure DevTest Labs
Azure DevTest Labs

Hacemos clic en el menú «Configuration and policies» en la parte inferior de la lista y bajamos hasta ver la sección «Virtual machine bases»:

DevTest Labs custom VHD image
Imagen personalizada de DevTest Labs

Y ahora viene la segunda parte más divertida del proceso: tenemos que subir los 130GB del VHD a un blob! Así que hacemos clic en el botón «Add» en la parte superior y en el nuevo diálogo que se abrirá seleccionamos «Upload a VHD using PowerShell». Esto nos generará un script de PowerShell para subir el VHD al blob de DevTest Labs. Por ejemplo:

DevTest Labs custom image upload
Subida del VHD a DevTest Labs

Como alternativa también podemos usar el Azure Storage Explorer como podemos ver en la imagen de la izquierda.

Habría que subirlo al blob uploads.

Cualquiera de los dos métodos es válido para subir el VHD y no sé si alguno de los dos es más rápido que el otro..

Una vez el VHD está subido abrimos la opción «Custom images» de nuevo y deberíamos ver el VHD en el desplegable:

DevTest Labs custom image
Imagen personalizada en DevTest Labs

Le damos un nombre y hacemos clic en OK.

Lo que tenemos ahora es una base para una máquina de desarrollo de Dynamics 365 Finance and Operations que tenemos que preparar para poder usar como máquina de build.

Creando la VM

Tenemos lo esencial, un VHD listo para usar de base para crear la VM en Azure. Nuestro siguiente paso tiene que ser encontrar una forma de hacer el despliegue de esta máquina predecible y automatizable. Conseguiremos esto gracias a los templates ARM de Azure.

Volvemos a la página inicial de DevTest Labs y hacemos clic en el botón «Add», en la opción «Choose base» seleccionamos la base que acabamos de crear, y en la siguiente pantalla el enlace «Add or Remove Artifacts»:

Add artifacts to the VM
Añadimos artefactos a la VM

Buscamos WinRM, seleccionamos «Configure WinRM», y en la siguiente pantalla ponemos «Shared IP address» como nombre de host y hacemos clic en «Add».

Nota: si al ejecutar la maquina los artefactos no se pueden ejecutar comprobad si el Azure VM Agent está instalado en el VHD base. Gracias a Joris por señalar este problema!

Configurar servicio del agente de Azure DevOps

Opción A: usar un artefacto

Actualización: gracias a Florian Hopfner por recordarme esto porque lo había olvidado… Si elegís la Opción A para instalar el servicio del agente tenemos que hacer unas cosas antes!

Lo primero es ejecutar unos scripts de PowerShell que crearán un entradas en el registro y variables de entorno. Vamos a C:\DynamicsSDK y ejecutamos esto:

El primer comando cargará las funciones y hará que las podamos ejecutar por línea de comando, los otros dos crean las entradas del registro y las variables de entorno.

Ahora tenemos que añadir un artefacto para el servicio del agente de Azure DevOps. Esto configurará el servicio del agente en la VM cada vez que se despliegue la máquina. Buscamos «Azure Pipelines Agent» y hacemos clic, veremos esto:

DevTest Labs Azure DevOps Agent
DevTest Labs Azure DevOps Agent

Tenemos que rellenar algo de información:

En «Azure DevOps Organization Name» tenemos que poner el nombre de la organización. Por ejemplo si vuestra URL de AZDO es https://dev.azure.com/blackbeltcorp tenemos que poner blackbeltcorp.

En «AZDO Personal Access Token» tenemos que poner el token generado desde Azure DevOps.

En «Agent Name» le damos un nombre al agente, como por ejemplo DevTestAgent. Y en «Agent Pool» un nombre para el pool, como DevTestPool o uno que ya exista como Default.

En «Account Name» pondremos el mismo usuario que tendremos luego en nuestra pipeline. Recordad esto. Y en «Account Password» su password. Usar secrets con un KeyVaul es mejor, pero no lo voy a explicar aquí.

Y, para acabar, ponemos «Replace Agent» a true.

Opción B: Configurar el agente de Azure DevOps Agent en la VM

Para hacer esto tendremos que crear una VM a partir de la base que tenemos y después ir a C:\DynamicsSDK y ejecutar el script SetupBuildAgent con los parámetros necesarios:

ATENCIÓN: Si elegimos usar la opción B tenemos que crear una nueva imagen base a partir de esta VM en la que hemos ejecutado el script. Luego tendremos que repetir el paso de WinRM para generar el nuevo template ARM como veremos a continuación.

Template ARM

Después vamos a la pestaña «Advanced Settings» y hacemos clic en «View ARM template«:

Get the ARM template
Obtenemos el template ARM

Esto nos mostrará el template ARM que usaremos para crear la VM desde la pipeline. Es algo así:

NOTA: si te decantas por la opción B no tendrás el nodo del artefacto del agente de VSTS.

Este archivo JSON se usará como base para crear las máquinas desde la pipeline de Azure DevOps. Esto es conocido como Infrastructure as Code (IaC, Infraestructura como código) y es una forma de definir nuestra infraestructura en un fichero como si fuera código. Es otra parte de la práctica de DevOps que debería resolver el clásico «pues funciona en mi máquina».

Si le echamos un vistazo a los nodos de parámetros del JSON veremos que tienen la siguiente información:

  • newVMName y labName serán los nombres de la VM y el lab de DevTest Labs que usaremos. El nombre de la máquina en realidad no importa porque lo definiremos más tarde en la pipeline.
  • size es el tamaño de la VM, una D3 V2 en el ejemplo de arriba, pero lo podemos cambiar (y lo haremos) luego.
  • userName y passWord son las credenciales para acceder a la VM y que deben coincidir con las que hemos puesto para el agente de DevOps.
  • Configure_WinRM_hostName es el artefacto que hemos añadido en el template y que permite ejecutar parte de las pipelines en la máquina.

Para hacerlo más rápido y como es una demo voy a usar texto plano para las contraseñas en el template ARM, cambiando el nodo de password a algo así:

Haré lo mismo con todas los nodos de secureString, pero lo mejor, repito, es usar un Azure KeyVault que viene con la cuenta de Azure DevTest Labs.

Por supuesto nunca vamos a sabuir un template a Azure DevOps con la contraseña en texto plano. Hay multitud de recursos online que explican como usar parámetros, Azure KeyVault, etc. como por ejemplo este: 6 Ways Passing Secrets to ARM Templates.

Muy bien, ahora vamos a guardar el template y lo subiremos al repo de Azure DevOps. He creado una carpeta en la raíz de mi repositorio llamado ARM en el que he guardado todos mis templates ARM:

ARM templates on Azure DevOps
Templates ARM en Azure DevOps

Preparando la VM

La imagen VHD que hemos descargado se puede usar como máquina de desarrollo sin ningún paso adicional, se ejecuta Visual Studio, lo conectamos a nuestro proyecto de AZDO y listo. Pero si la queremos usar como máquina de build tenemos que hacer unas cuantas cosas primero.

Recordad que el usuario y contraseña por defecto de estas máquinas son Administrator y Pass@word1.

Deshabilitar servicios

Primero de todo pararemos y deshabilitaremos algunos servicios como el Batch, Management Reporter, SSAS, SSIS, etc. Cualquier cosa que no se necesite para ejecutar una build.

Crear un nuevo usuario de SQL

Abriremos SSMS (como administrador) y crearemos un nuevo usuario de SQL como una copia del axdbadmin. Después abriremos el archivo web.config y actualizaremos el usuario y contraseña de la DB con el que acabamos de crear.

Preparar SSRS (opcional)

Si váis a desplegar reports como parte de vuestra build tenéis que ir a SSMS y ejecutar la siguiente query contra la DB de reporting:

Scripts de PowerShell

La build por defecto que corre en la máquina de build usa varios scripts de PowerShell para ejecutar tareas. Voy a añadir un script adicional llamado PrepareForAgent.

Los scripts se encuentran en la carpeta C:\DynamicsSDK de la VM.

PrepareForBuild

Este script viene con la VM y debemos modificarlo para evitar una cosa: el backup de PackagesLocalDirectory que se hace en la primera ejecución de cada build. Tenemos que evitar que se haga o perderemos una hora en cada ejecución hasta que se termine.

No lo necesitamos porque nuestra máquina de build será nueva cada vez que ejecutemos la pipeline!

Así que abrimos el script, vamos a la línea 696 y buscamos esta parte del código:

Tenemos que cambiarlo para que sea así:

Simplemente dejaremos la parte de restaurar la DB y nos saltamos el backup, o perderemos 45 minutos por ejecución por algo que no necesitamos porque la VM se borra en cada ejecución.

Opcional (pero recomendado): instalar las d365fo.tools

Simplemente ejecutaremos esto:

Podemos usar las tools para hacer sincronizaciones parciales, por modelos o desplegar sólo nuestros reports en vez de todos.

Crear una nueva imagen

Una vez hayamos hecho todos estos pasos de preparación vamos a deslogearnos de la máquina y la vamos a detener. Pero no la borréis! Vamos a ir a «Create custom image», le daremos un nuevo nombre, seleccionamos «I have not generalized this virtual machine» y haremos clic en el botón de «OK».

Esto generará una nueva imagen con los cambios que hemos hecho al VHD original y que podemos usar como base.

Pipelines de Azure DevOps

Ya estamos listos para configurar nuestra pipeline en Azure DevOps. Esta pipeline va a consistir de tres pasos: creación de la VM, build, y borrado de la VM:

MSDyn365 y Azure DevOps ALM 16

Primero de todo comprobaremos que nuestra pipeline se ejecuta en Azure (Azure Pipelines):

DevTest Labs Azure Pipelines
DevTest Labs Azure Pipelines

Los pasos de crear y borrar se ejecutarán en una pipeline de Azure. El de build se ejecutará en el pool DevTestLabs que hemos creado, o el nombre que le hayáis dado al configurar el artefacto en DevTest Labs o el script en la VM.

Crear VM en Azure DevTest Labs

Creamos una nueva pipeline y elegimos «Use the classic editor». Nos aseguramos de que hemos seleccionado TFVC como origen y hacemos clic en «Continue» y «Empty job». Añadimos una nueva tarea a la pipeline y buscamos «Azure DevTest Labs Create VM». Sólo tenemos que rellenar la información necesaria con los parámetros de nuestra suscripción, lab, etc.

Create VM Azure DevTest Labs
Crear VM en Azure DevTest Labs

Recordad que este paso tiene que ejecutarse en una pipeline de Azure.

Build

Este es muy fácil. Exportamos una build y la importamos.

Este paso tiene que ejecutarse en vuestro pool:

Runs on self-hosted pool
Se ejecuta en el pool autohospedado
Opcional: usar SelectiveSync (no recomendado, ver siguiente apartado)

Podéis sustituir la tarea Database Sync por un script de PowerShell que solo sincroniza las tablas de vuestros modelos:

SelectiveSync.ps1
SelectiveSync.ps1

¡Muchas gracias a Joris por el consejo!

Opcional: usa las d365fo.tools para sincronizar tus paquetes/modelos

Esta opcion es mejor que usar SelectiveSync. Puedes sincronizar sólo tus paquetes o modelos y ganar algo de tiempo. Este comando usa sync.exe como Visual Studio y debería ser mejor que usar SelectiveSync.

Añadimos una nueva tarea de PowerShell, seleccionamos Inline Script y este es el comando:

Opcional: usa las d365fo.tools para publicar los reports de SSRS

Si quieres añadir el paso de desplegar los reports en tu pipeline puedes ahorrar un poco de tiempo más usando las d365fo.tools y desplegando sólo los reports de tus modelos como hemos hecho con la sincronización de la DB.

Ejecutaremos esto en una nueva task de PowerShell:

Borrar VM de Azure DevTest Labs

Es muy parecido al primer paso de crear, completamos los campos de suscripción, labs y VM y listo:

Delete VM
Borrar VM

Y este paso, igual que el de crear, se ejecutan en el pool de Azure.

Dependencias y condiciones

Cuando tengamos los 3 pasos configurados tenemos que añadir unas dependencias y condiciones a algunos de ellos. Por ejemplo, asegurarnos que el paso de borrado de la VM solo se ejecuta cuando falle el paso de build, pero que no lo haga cuando falle el de creación.

Build

El paso de build depende del de crear VM, y sólo debería ejecutarse si el paso previo ha terminado bien:

Build step dependencies and conditions
Dependencias y condiciones del paso de build
Borrar VM

El paso de borrado depende de los anteriores y sólo debe ejecutarse si el de creación termian bien. Si falla el primero no hay que borrar nada:

Dependencies and conditions on delete VM step
Dependencias y condiciones del paso de borrado

Esta es la condición personalizada que usaremos:

Si necesitáis saber cómo se llama el primer paso de vuestra pipeline, exportadla a YAML y lo encontraréis ahí:

Export pipeline to YAML
Exportar pipeline a YAML
Job name on YAML
Nombre del Job en el YAML

Si este paso fallase al ejecutar la build, no borréis la VM aun, primero cambiad el nombre de la VM en el paso de borrado, guardad la pipeline, y luego usad el desplegable para seleccionar la que queréis borrar.

Ejecutar la build

Y, creo, que ya estamos listos para ejecutar nuestra pipeline con Azure DevTest Labs para Dynamics 365 Finance and Operations… hacemos clic en «Run pipeline» y a esperar…

MSDyn365 y Azure DevOps ALM 17
Tadaaaa!!

Tiempos

La pipeline de la imagen superior es una real con código real de un cliente pero no puedo compararlo con las hospedadas en Azure porque en estas no hay sincronización, ni tests y necesitan instalar primero los nugets.

Pero una comparación que si hice fue esta:

Azure DevTest Labs B2ms vs B4ms
Azure DevTest Labs B2ms vs B4ms

Tarda sobre 1 hora en crearla VM, compilar, hacer una sincronización completa de la DB, desplegar informes, ejecutar tests, generar el Deployable Package y, finalmente, borrar la VM:

MSDyn365 y Azure DevOps ALM 18

Si no desplegamos reports ganaremos 15 minutos más y se quedará en unos 45 minutos.

Si usamos el partial sync en vez de hacer una sincronización completa ganaremos entre 5 y 7 minutos.

Esto nos dejaría con una build de 35-40 minutos.

Comparativa 1

MSDyn365 y Azure DevOps ALM 19
Sin DB

La imagen muestra un paquete simple que se compila, sin tablas, así que el selective sync termina muy rápido. Los tiempos de build mejoran con el tamaño de la VM.

Comparativa 2

MSDyn365 y Azure DevOps ALM 20
Mismo código pero Full DB Sync

Esta compila la misma base de código pero hace una sincronización de toda la DB. El tiempo de sincronización mejora en la B4ms respecto una B2ms, pero es casi el mismo en una B8ms. Los tiempos de compilación son mejores cuanto más potente es la VM.

Comparativa 3

MSDyn365 y Azure DevOps ALM 21
Código real + sincronización completa

Y la imagen de arriba muestra algo más realista. Hay mucho más código que compilar y hacemos una sincronización completo de la DB.

De forma similar a la anterior comparativa, hay una mejora notable en el salto de una B2ms a una B4ms, pero no tanto de una B4ms a una B8ms.

Show me the money!

Creo que esta es la comparación interesante. Cuanto costaba una VM Tier-1? Unos 400€? Cuanto es comparado con la alternativa con Azure DevTest Labs?

Sólo hay un sote fijo cuando usemos Azure DevTest Labs: el almacenamiento blob en el que se sube el VHD. El tamaño de la imagen es de unos 130GB y esto debería tener un coste de unos 5 euros/mes más o menos. Recordad que hay que limpiar las imágenes personalizadas cuando la vuestra esté prepada, las nuevas se guardan como snapshots y también consumen espacio de almacenamiento.

Después tenemos el coste variable que viene con el despliegue de cada VM pero es un coste ridículo. Imaginad que usamos una VM de tipo B4ms, con un SSD Premium de 256GB, pagaríamos 0.18€/hora por la VM más la parte proporcional de 35.26€/mes del disco SSD, que serían como… 5 céntimos/hora?

Pero es que esta build puede ejecutarse en una B2ms que cuesta la mitad, por 9 céntimos la hora.

Si ejecutamos esta build una vez al día, 30 veces, el coste de la B4ms sería de… 7 euros? Añadimos el almacenamiento de blob y estaríamos pagando 12€ al mes por ejecutar nuestras builds con sincronización de DB y tests.

Es mucho más barato que desplegar un entorno hospedado en la nube, iniciándolo y parándolo cada vez que lo ejecutamos con el nuevo Cmdlet de las d365fo.tools? Sí lo es. Porque si desplegamos una máquina desde LCS tenemos que pagar por el SSD durante todo el mes!

Algunas observaciones finales

  1. He conseguido esto mayormente a través de prueba y error. Estoy seguro de que hay muchas mejoras y mejores prácticas aplicables a todo el proceso, especialmente usar un Azure Key Vault para guardar los secretos en para los artefactos y la pipeline.
  2. Esto es otro ejemplo de que los desarrolladores de X++ tenemos que salir de X++ y Dynamics 365 FnO. Ya no somos sólo programadores de X++, tenemos mucha suerte de trabajar con un producto que usa Azure.
  3. Estoy seguro que hay escenarios en los que usar DevTest Labs para crear una máquina de build es útil. Quizás no para un partner de implantación, pero igual sí para un partner que hace verticales (ISV). Es una opción más.
  4. La única parte mala para mi de esto es que tenemos que aplicar los upgrades de versión manualmente porque el VHD sólo se publica dos veces al año.
  5. Como dije al principio del post, esto puede que me haya funcionado a mi con estos pasos, pero que si tú lo pruebas tengas que cambiar algunas cosas. Pero es una buena forma de empezar.

Añade y compila proyectos .NET a tu pipeline de Dynamics 365

Estoy seguro de que muchos de nosotros hemos tenido que desarrollar algún tipo de librería en .NET para solucionar algo en Dynamics 365 Finance and Operations. Creas un proyecto de C#, lo compilas y añades la referencia en tu proyecto de FnO. ¡Ya no tienes que hacer eso! Puedes añadir el proyecto a tu repositorio de código, compilarlo en tu pipeline y ¡la DLL se añade al deployable package!

He estado haciendo estas pruebas a raíz de una conversación en Yammer, y a pesar de que he conseguido compilar .NET y X++ en la misma pipeline sin problemas, he encontrado algun problema o limitación.

Si quieres leer más sobre builds, releases y el ALM de desarrollo de Dynamics 365 puedes leer mi guía sobre MSDyn365 y Azure DevOps ALM.

Compila .NET en tu pipeline

Nota: lo que muestro aquí está hecho con la pipeline hospedada en Azure pero debería ser posible hacerlo con el agente self-hosted (la vieja máquina de build vaya).

El paso de compilar de las pipelines llama a msbuild.exe que también puede compilar .NET. Si comprobamos los logs del paso lo veremos:

msbuild.exe builds C# projects and our X++ ones too!
msbuild.exe compila proyectos de C# y los de X++ también!

Hay que recordar que X++ es ahora parte de la familia de .NET… un primo segundo o algo asi.

MSDyn365 y Azure DevOps ALM 22
Carpeta Build

Si has leído mi post sobre las builds hospedadas en Azure habréis visto que pongo las soluciones que referencian todos mis modelos en una carpeta llamada Build en la raíz de mi árbol de código (imágen izquierda).

Esto es solo una preferencia personal que me ayuda a mantener los archivos .config y la solución que uso para compilar los modelos en un sitio único.

Al usar una solución y decirle al proceso de build que use sólo esa tengo todo más bajo control.

Añadir un proyecto de C# a FnO

El primer paso será crear un proyecto de Finance and Operations. Cuando lo tengamos hacemos clic derecho en la solución y seleccionamos «Add new project». Luego seleccionamos un proyecto de tipo Visual C# y Class Library:

MSDyn365 y Azure DevOps ALM 23
Proyecto de C# en Dynamics 365
MSDyn365 y Azure DevOps ALM 24

Ahora deberíamos tener una solución con un proyecto de FnO y otro de C# (imágen derecha).

Para hacer esta demo crearé una clase llamada Calculator con un único método con 2 parámetros de tipo decimal que devuelve su suma. Un método suma.

Ahora compilamos el proyecto de C# únicamente, no toda la solución. Esto creará la DLL en la carpeta bin del proyecto. Tenemos que hacerlo antes de añadir la referencia al proyecto de Dynamics 365.

Hacemos click derecho en el nodo References del proyecto de FnO y seleccionamos «Add Reference…»:

MSDyn365 y Azure DevOps ALM 25
Añadimos una referencia al proyectro de FnO

Se abrirá una ventana y tendríamos que ver el proyecto de C# en la pestaña «Projects»:

MSDyn365 y Azure DevOps ALM 26
Añadir referencia de C# a un proyecto de Dynamics 365

Lo seleccionamos y hacemos clic en el botón Ok. Esto añadirá el proyecto de C# como referencia a nuestro proyecto de FnO, pero todavía tenemos que hacer una cosita o la pipeline no compilará. Tenemos que añadir a mano la referencia que acabamos de crear. Así que hacemos clic derecho en la referencia y seleccionamos «Add to source control»:

MSDyn365 y Azure DevOps ALM 27
Añadimos la referencia al control de código

En el proyecto de FnO añadimos una Runnable Class, llamaremos a la librería de C# ahí:

Añadimos la solución al control de código si no lo hemos hecho y nos aseguramos que todos los objetos están añadidos también antes de hacer el check-in.

Build pipeline

Si voy a mi repositorio de Azure DevOps veremos esto:

MSDyn365 y Azure DevOps ALM 28
Proyectos y objetos

Podéis ver que he puesto la solución en la carpeta Build. Como decía al principio esto es mi preferencia personal, y lo hago para tener las soluciones que se usan para compilar el código bajo control y ordenaditas.

En mi pipeline de build me aseguro de que estoy usando esa solución:

MSDyn365 y Azure DevOps ALM 29
Compilar solución de Dynamics 365

Ejecuto la pipeline y cuando termina podemos ver una línea en el log del paso de compilar en la que pone:

Y si nos descargamos el DP, lo descomprimimos, vamos a AOSService\Packages\files y descomprimimos el archivo que hay ahí y abrimos su carpeta bin, veremos nuestra DLL:

MSDyn365 y Azure DevOps ALM 30
¡Victoria!

¡Trabajo hecho!

Cosas que no me gustan/entiendo/tengo que investigar

Siempre he hecho esto con una única solución y un único proyecto de C#. Tengo algunas dudas sobre cómo funcionará esto con varios proyectos de C#, modelos, soluciones, etc.

Por ejemplo, si un modelo tiene una dependecia de una DLL, pero se compila antes que se genere la DLL, la compilación fallará. Estoy seguro que hay una forma de resolver estas dependencias igual que la hay para las dependencias entre proyectos de FnO dentro de una solución.

O igual podría intentar compilar todo el código C#/.NET primero, empaquetarlo en un nuget y usar las DLLs más tarde en la build de FnO, algo parecido a lo que explica Paul Heisterkamp en su blog.

De todas formas, es vuestra elección el decidir como gestionar vuestros proyectos de C# y qué solución se ajusta más a vuestra forma de trabajar, pero por lo menos ya tenéis algo por lo que empezar 🙂

Configurar Pipelines de release

Hemos visto como se crea la definición de build por defecto y cómo modificarla. Ahora vamos a ver cómo configurar nuestras pipelies de release!

Estas pipelines nos permiten desplegar automáticamente nuestros Deployable Packages a un entorno Tier 2+. Esto es parte de la estrategia de entrega contínua (CD). Sólo lo podemos usar en entornos sandbox/UAT, no es posible hacerlo en producción.

Configurar Release en Azure DevOps para Dynamics 365 for Finance and Operations

Para configurar el release necesitaremos lo siguiente:

  • Aplicación de AAD
  • Datos del proyecto de LCS
  • Un proyecto de Azure DevOps que esté vinculado al anterior de LCS
  • Usuario tipo cuenta de servicio

Al usuario es recomendable que no le caduque la contraseña (de ahí la cuenta de servicio) y que tenga permisos suficientes tanto en LCS, Azure y Azure DevOps. Esto no es obligatorio, se puede configurar con un usuario normal para hacer pruebas sin ningún problema.

Crear la app de AAD

El primer paso es crear una aplicación de Azure Active Directory para poder subir el paquete generado por la build a LCS, así que nos dirigiremos al portal de Azure y una vez nos hayamos logueado iremos a Azure ActiveDirectory, luego a App Registrations y crearemos una nueva de tipo Native:

MSDyn365 y Azure DevOps ALM 31

A continuación vamos a «Settings» y «Required permissions» y añadimos la API de Dynamics Lifecycle Services:

MSDyn365 y Azure DevOps ALM 32

En el diálogo que se abre cambiamos a la pestaña “APIs my organization uses” y seleccionamos “Dynamics Lifecycle Services”:

MSDyn365 y Azure DevOps ALM 33

Seleccionamos los únicos permisos disponibles en la siguiente pantalla y clicamos en “Add permissions”. Finalmente, pulsamos en “Grant admin consent” para aplicar los campos. Cuidado que podemos olvidarnos de este paso fácilmente, y si no damos esta autorización la subida a LCS puede fallar. Nos guardamos el App Id que lo usaremos luego.

Crear release en Azure DevOps

Ahora iremos a Azure DevOps, Pipelines -> Releases. Seleccionamos «New release pipeline» y del listado que aparece elegimos el «Empty job».

Primero de todo asignaremos a qué build irá vinculada esa definición de release desde «Add an artifact»:

New release

En «Source» seleccionamos la definición de build que queremos usar, en «Default version» usaremos «Latest» y pulsamos «Add».

Subida a LCS

El siguiente paso es definir la Task con la extensión de release para Dynamics. Pulsamos en la pestaña Tasks y en el botón «+». Nos aparecerá una lista y buscaremos «Dynamics 365 Unified Operations Tools»:

Dynamics 365 Unified Operations Tools

Si no hemos añadido la extensión previamente lo podemos hacer desde esta misma pantalla. Para poderla añadir el usuario con el que estemos creando la release tiene que ser administrador de la cuenta de Azure DevOps en la que esté el proyecto, no es suficiente con que lo sea del proyecto.

Una vez añadida la tarea tenemos que rellenar una serie de parámetros:

Aplicar un deployable package

¡Después de esperar mucho por fin ha llegado! Si ya tienes esto configurado para un entorno normal puedes cambiar a la nueva versión de la tarea sin problemas.

Azure DevOps asset deployment
Despliegue de DP en Azure DevOps

La nueva versión 1 de la task funciona para ambos tipos de entornos: los gestionados por Microsoft (entornos normales) y los self-service. La versión 0 de la tarea es la vieja y solo funcionará para entornos normales. Puedes cambiar a la versión 1 en todas tus releases sin problemas.

¿Qué es diferente en la versión 1 de la task? Pues seguramente mucho trabajo por detrás que no vemos para que soporte los entornos self-service, pero a nivel de interfaz sólo vemos un nuevo campo llamado «Name for the update«.

MSDyn365 y Azure DevOps ALM 34

Campo Name for the update en la task

Este campo se necesita sólo para entornos self-service, se ignorará en los normales, y corresponde al campo con el mismo nombre que aparece en LCS cuando queremos actualizar un entorno sandbox:

MSDyn365 y Azure DevOps ALM 35

Name for this update en LCS

El valor por defecto de este campo es $(Release.ReleaseName), que es el nombre de la release, pero lo podemos cambiar. Por ejemplo yo uso un patrón del tipo PREFIJO RAMA $(Build.BuildNumber) para tener el mismo nombre que en la build y poder identificar más rápido qué vamos a desplegar en produción.

Y ya está, ya podemos sentarnos y relajarnos mientras nuestros entornos de test se actualizan gracias a los beneficios del CI/CD.

Crear la conexión a LCS

El primer paso es crear la conexión a LCS con la aplicación de AAD que hemos creado antes. Pulsamos New y se abrirá la siguiente ventana:

Coenxión LCS Azure DevOps

Sólo es necesario rellenar el nombre, usuario, contraseña y el Application (Client) ID con el App ID que tenemos del paso inicial en Azure, los campos de «Endpoint» deberían completarse solos. Pulsamos OK y ya tenemos la conexión lista.

En el campo LCS Project Id ponemos el ID que aparece en la URL del proyecto de LCS, por ej. en https://lcs.dynamics.com/V2/ProjectOverview/1234567 el Id es 1234567.

Pulsamos el botón al lado del campo de «File to upload» y seleccionaremos el archivo del deployable package que genera la build:

DP Generado

Dependiendo de si habéis modificado la definición de build o no, el archivo tendrá un nombre u otro, pero normalmente es del tipo AXDeployableRuntime_VERSION_NUMEROBUILD.zip. Cambiad el número fijo de build por la variable de la siguiente manera:

BUildNumber

En «LCS Asset Name» y «LCS Asset Description» se define el nombre y descripción que tendrá el paquete en LCS. Para estos campos podéis usar todas las variables predefinidas de build y de release que ofrece Azure DevOps. Siguiendo con el caso anterior del nombre del archivo, usaremos un prefijo que describa qué tipo de paquete es (para producción o pre-producción) seguido de $(Build.BuildNumber), generando por ejemplo un DP llamado Prod 2019.1.29.1 con la fecha de build.

Ahora ya sólo nos queda guardar y probar. En la pantalla de Releases seleccionamos la que acabamos de crear, le damos al botón «Create a release» y sin cambiar ninguna opción seleccionamos OK en la pantalla que se ha abierto. Se lanzará la release y si todo va bien podremos ver el paquete en LCS:

LCS Asset Library

Si queremos automatizar la parte de release y que se ejecute cada vez que termine una build sólo tenemos que activar el trigger en el artefacto:

Release trigger

Desde el botón del rayo se nos abre un diálogo y simplemente hay que marcar la opción que aparece.

Nada más, con estos pasos ya tendremos configuradas las builds (que pueden estar automatizadas también) y los releases. Sólo falta que los despliegues también se puedan automatizar y ya estaremos en la integración continua que comentaba al principio.

¡Y ya esta todo listo! Un pasito más cerca de deshacernos de las VM de build.

¡Más automatización!

Ya he explicado antes como automatizar las builds, crear builds de CI y crear las releases en Azure DevOps, así que de lo que quiero hablar en este post es de añadir un poco más de automatización.

Builds

En la definición de build vamos a la pestaña «Triggers» y activamos las builds programadas:

MSDyn365 y Azure DevOps ALM 36

Esto va a lanzar una build automaticamente los días que quieras y a la hora que le digas. En el ejemplo se lanza todos los días entre semana a las 16.30h. Pero… ¿todos los días? No. El check «Only schedule builds if the source or pipeline has changed» hace que sólo se lance la build si ha habido cambios en el código, así que si un día no hay changesets nuevos no se lanza la build.

Releases

Builds listas, a ver qué podemos hacer con las releases:

MSDyn365 y Azure DevOps ALM 37

La release de la imagen de arriba es la que se lanza automáticamente después de la build que he creado en el primer paso. Para esta release he añadido lo siguiente:

MSDyn365 y Azure DevOps ALM 38

El disparador de CD está activo, lo que hace que cada vez que una build termina se ejecuta automáticamente. Sin necesidad de definir una programación horaria, pero también se podría hacer.

MSDyn365 y Azure DevOps ALM 39

Como véis, la pantalla de la programación es exactamente igual que en las builds, incluso el check de cambios está ahí. Se pueden usar cualquiera de estas dos formas, CD o release programada, depende de las necesidades del proyecto o equipo.

Con estos dos pequeños pasos tenemos la estrategia de CI y CD completamente automatizada, y la actualización del entorno de UAT cada noche para tener los cambios hechos durante el día listos para testear, sin humanos de por medio!

Pero a mi me gusta un poco de toque humano

Si no te gusta no saber cuando se actualiza un entorno… bueno eso es IMPOSIBLE porque LCS te Spamea para que no te pierdas ni un despliegue. Pero si lo que no quieres es ser completamente reemplazado por robots puedes añadir aprobaciones a tu flujo de release:

MSDyn365 y Azure DevOps ALM 40

Haciendo clic en el icono izquierdo de relámpago + persona puedes configurar los aprobadores, una persona o grupo (cosa que es muy práctica), y el tipo de aprobación (única o múltiple) y timeout. También recibirás un correo con un enlace al formulario de aprobación:

MSDyn365 y Azure DevOps ALM 41

¡E incluso puedes posponer el despliegue! ¡Todo es fabuloso!

Extra bonus!

Un pequeño consejo. Imaginemos que tenemos esta release:

MSDyn365 y Azure DevOps ALM 42

Esto actualizaría 3 entornos, pero también haría 3 subidas del Deployable Package a LCS. ¿No estaría bien poder hacer una única subida y que los despliegues se hicieran usando ese archivo? Sí, pero no podemos pasar el valor de la variable de salida del stage de subida a los demas stages 🙁 Eso desgraciadamente es así. Pero podemos hacer algo con un poco de ayuda de nuestro amigo Powershell!

Actualizar una variable en una release

Lo que necesitamos es crear una variable en la definición de release y poner su ámbito como «Release»:

MSDyn365 y Azure DevOps ALM 43

Luego, por cada stage que tengamos, tenemos que activar este check en el agent job:

MSDyn365 y Azure DevOps ALM 44

Luego explicaré por qué. Ahora sólo tenemos que actualizar esta variable una vez subido el DP a LCS. Añadimos un Powershell de tipo inline después del paso de la subida y hacemos esto:

Necesitamos cambiar lo siguiente:

  • Línea 2: $assetId= «$(GoldenUpload.FileAssetId)». Cambiar $(GoldenUpload.FileAssetId) por tu variable de salida.
  • Línea 6: $ReleaseVariableName = ‘axzfileid’. Cambiar axzfileid por tu variable de Release.

Y listos. Este script usa la API REST de Azure DevOps para actualizar el valor de la variable con el file id, y hemos activado el check del token de OAuth para no tener que autenticarnos con ningún usuario. Esto no es idea mía, lo he hecho gracias a este post del blog de Stefan Stranger’s.

Ahora en los stages de despliegue recuperamos el valor de la variable así:

MSDyn365 y Azure DevOps ALM 45

No os olvidéis los ( ) o no funcionará!!!

Y con este pequeño cambio tenemos una release como esta:

MSDyn365 y Azure DevOps ALM 46

Con una única subida del DP a LCS y múltiples despliegues usando el archivo subida en el primer stage. Y con aprobaciones, y retrasos y correos y todo!

API de DB de LCS

Llamar a la API de movimiento de base de datos de LCS

¿Para qué?

Básicamente por automatización. Ahora mismo la API sólo permite el refresco de un entorno de Microsoft Dynamics 365 for Finance and Operations a otro, así que la idea es tener datos frescos de producción en nuestros entornos de UAT a diario. No sé qué nuevas operaciones soportará la API en el futuro pero otra idea sería añadir al pipeline la exportación de la DB (creando un bacpac) de un entorno de UAT para tener datos listos para restarurar en una máquina de desarrollo.

No olvidemos que la API tiene un límite de 3 operaciones de refresco por entorno por 24 horas. No hagáis esto en una build de integración contínua (tampoco tiene mucho sentido). Seguramente la mejor idea es que se ejecute una vez al día con los tests.

Todas estas operaciones/llamadas se pueden hacer también con las d365fo.tools de Mötz Jensen que dan soporte a la API de LCS. Pero si estás usando un agente de Azure en lugar de uno en una VM (como la máquina de build) no se pueden instalar. O por lo menos no he encontrado cómo hacerlo 🙂

Llamando a la API

Voy a usar PowerShell para las llamadas a la API desde DevOps. PowerShell tiene un comando llamado Invoke-RestMethod que hace peticiones HTTP/HTTPS. Es muy fácil usarlo y sólo necesitamos hacer lo mismo que en mi anterior post sobre la API.

Obtener el token

Para obtener el token usaremos este script. Sólo hay que cambiar los valores de las variables por los de tu proyecto, App de AAD, usuario (recuerda que tiene que estar dado de alta en la preview) y contraseña para ejecutarlo. Si todo está bien recibiremos un JSON con la respuesta en $tokenResponse y ahí podremos acceder al valor del token usando notación por puntos.

Solicitar el refresco de DB

Está es la llamada para lanzar el refresco. Necesitamos el token que hemos obtenido en el paso anterior para usarlo en la cabecera y los Ids de los entornos de origen y destino.

Si va bien recibiremos una respuesta 200 OK.

Añadirlo a tu pipeline

Añadir esto a un pipeline de Azure DevOps no tiene misterior. Selecciona y edita tu pipeline, yo lo hago en una build nocturna (aunque pone coninuous no lo es…) que se ejecuta después de la actualización de los entornos, y añadre una task de PowerShell:

MSDyn365 y Azure DevOps ALM 47

Selecciona la tarea y cambias el tipo a «Inline»:

MSDyn365 y Azure DevOps ALM 48

Y finalmente pega el script anterior en el campo Script y ¡listo! Tendrás un refresco de datos después de los tests.

También puedes ejecutar esto en tu pipeline de release PERO si lo haces después del paso de despliegue recuerda marcar la opción «Wait for Completion» o ¡la operación fallará al estar el entorno en servicio! E incluso podría fallar si el tiempo de mantenimiento del entorno sobrepasa el del timeout del pipeline. Bueno, mejor… ¡no hagas esto en el pipeline de release!

Y eso es todo, vamos a ver qué nuevas funcionalidades se añaden a la API y qué podemos hacer con ellas.

Usar d365fo.tools en tu Pipeline de Azure

Gracias al comentario de Mötz indicando como usar las d365fo.tools en un pipeline he creado una que instalará las herramientas y ejecutará los comandos. Es incluso más fácil que hacerlo con el Invoke-RestMethod.

Pero antes…

Aseguraros que en vuestra App de Azure Active Directory habéis seleccionado «Treat application as a public client» en el apartado de autenticación:

MSDyn365 y Azure DevOps ALM 49

La tarea

Primero necesitamos instalar las d365fo.tools y después podemos usar los comandos para llamar a la API de LCS:

Como podéis ver es un poco más fácil hacer el refresh usando las d365fo.tools. Obtenemos el token y lo que devuelve la operación lo pasamos al comando Set-D365LcsApiConfig que guardará el token (y otras cosas). Esto ayuda a no tener que andar poniendo varias veces el AppId, usuario, etc. Y ya véis lo fácil que es invocar el refresco de DB, sólo necesitamos el environment Id del entorno de origen y destino. Y listo!

LCS DB API: automatizando la copia de la DB de Prod a Dev

El nuevo endpoint de la LCS DB API para exportar una base de datos ha sido publicado! Con él ya tenemos una forma de automatizar el refresco de datos de tu Dynamics 365 FnO desde producción a un entorno de desarrollo Tier 1.

Usando la LCS DB API

Usando la LCS DB API

El problema con los bacpac

Uno de los mayores inconvenientes que tenemos ahora mismo con mover datos desde producción es que no es algo precisamente rápido porque tenemos que:

  • Refrescar un entorno Tier 2+ con los datos de Prod
  • Exportar un bacpac del entorno Tier 2+
  • Restaurar el bacpac en una VM Tier1

Esto es así porque los entornos Tier 2+ usan Azure SQL como motor de la DB, y las VM Tier 1 usan SQL Server.

El tiempo que tada todo el proceso depende del tamaño de la base de datos y del rendimiento de la VM en la que vas a restaurar el bacpac. Pero no es un proceso rápido para nada. Para una DB de 60GB obtendremos un bacpac de unos 7GB que tardará:

  • 1 a 2 horas en refrescar UAT con datos de Prod
  • 2 a 4 horas para exportar el bacpac
  • Por lo menos 4 horas en restaurar el bacpac en un Tier 1

Eso hace un total de entre 7 y 11 horas hasta que tienes la DB en una máquina de desarrollo. Una vez la tienes ahí puedes sacar el BAK rápido y compartirlo. Pero necesitas prácticamente el tiempo de un día de trabajo para tenerlo disponible. Como para tener prisa.

¡Sálvanos LCS DB API!

Gracias al nuevo endpoint de la LCS DB API podemos hacer todos estos pasos automáticamente, y con las d365fo.tools será incluso más fácil. Pero antes…

Debido a lo largo de todo el proceso primero tenemos que planificar como lo haremos (diariamente, semanalmente, etc.) y que ello sea compatible con nuestra cadencia de subidas a UAT/Prod, porque solo se puede ejecutar una operación a la vez.

Y hay otro problema, pero lo veremos después de los scripts.

Mi propuesta

Para poder hacer el último paso del flow de la LCS DB API de prod a dev necesitaremos una máquina virtual tipo Tier 1 donde restaurar el bacpac. Mi idea es usar la VM de build en la suscripción de Microsoft y un pipeline de Azure DevOps para ejecutar todos los scripts que restaurarán la DB en esa máquina. Es una máquina que tiene poco uso y nos va perfecta para esto.

Quiero aclarar por qué he pensado en hacer esto con la VM de build. En la mayoría de los casos esta VM no hace nada por la noche, como mucho ejecutar algunos tests, y es durante este período cuando sugiero hacer todo esto. Pero tened en cuenta que dependiendo del tamaño de vuestra DB esto no se podrá hacer o os quedaréis sin espacio en 2 o 3 copias.

Así que pensad si os va mejor desplegar otra VM y ponerle el agente, pero sobretodo no toqueteéis nada de la VM de build si no sabéis qué estáis haciendo! Probad esto en una VM de desarrollo o donde queráis si pensáis que podéis romper algo. Recordad que si rompéis la máquina de build os quedaréis sin la posibilidad de generar DPs o ejecutar pipelines!

Este post es solo un ejemplo de una posible solución, necesitais decidir qué os va mejor en vuestro proyecto! Fin de la actualización.

Como dije antes usaré las d365fo.tools de Mötz Jensen, podría hacerlo todo sin, usando llamadas REST pero sería un poco de tontos porque con las tools todo es más fácil, rápido y claro.

He separado los pasos en 3 scripts de Powershell: ejecutar el refresco, exportar el bacpac y restaurar el bacpac.

Refrescar datos

Este script lanza la copia de prod a un entorno Tier 2+:

Exportar la base de datos

Con este paso lanzamos la exportación del bacpac del entorno Tier 2+ donde hemos restaurado prod:

Restaurar el bacpac

Finalmente descargamos el bacpac en la VM de build y lo restauramos en una nueva base de datos:

Usandolos en un pipeline de Azure DevOps

Azure DevOps pipeline

Azure DevOps pipeline

Esto es. Creamos un script de Powershell, lo guardamos en la VM de build y lo llamamos en el pipeline. Esto es sólo válido para el agente hospedado en la VM de build. Seguramente se pueda ejecutar todo en un agente de Azure, pero no lo voy a explicar porque pienso que usando la VM de build, donde podemos restaurar los datos, nos será más útil.

Tiempos

Estos 3 scripts van a hacer las llamadas a la LCS DB API para refrescar, exportar y restaurar los datos. Pero tenemos un problema de tiempos.

Como hemos visto, refrescar la DB lleva un tiempo, igual que exportarla. Necesitas encontrar una forma de controlar el estado de las operaciones. La LCS DB API nos ofrece una operación que nos permite obtener el estado de una actividad en marcha. Usando las d365fo.tools:

Podemos controlar esto dentro de nuestro script de Powershell, pero su usamos el agente en la VM de build esto significa que no lo podemos usar para nada más mientras esto esté ejecutándose.

Por eso he separado el proceso en 3 pasos. Puedes programar manualmente 3 pipelines, una para cada paso a las horas que sepas que ha terminado cada stage. Y así también se puede elegir el orden: exportar, restaurar, refrescar o refrescar, exportar, restaurar.

También podríamos usar el Windows Task Scheduler y olvidarnos de los pipelines de AZDO, pero no lo haremos porque nos encantan los pipelines.

Y eso es todo, al fin tenemos una forma de mover datos sin tener que hacerlo a mano, podemos programarlo pero necesitamos tomar antes algunas decisiones sobre cómo lo haremos. Y eso os lo dejo a vosotros 🙂

Seguridad en Azure Pipelines con Azure Key Vault

Pero crear un pipeline con una contraseña a la vista no era muy seguro. ¿Cómo podemos añadir un extra de seguridad a pipelines? Una vez más podemos acudir a una herramienta de Azure para ayudarnos, Azure Key Vault.

Azure Key Vault

Key Vault es un servicio que nos permite guardar certificados o secretos de forma segura y usarlos en nuestras apps o servicios. Y como muchos otros servicios de Azure tiene un coste pero es muy bajo y, para un uso normal, la factura será de 1 céntimo o ninguno al mes. ¡No seáis rancios con la seguridad!

Quizás ya conozcáis Azure Key Vault porque lo podemos usar en Microsoft Dynamics 365 for Finance and Operations en Administración del sistema. Por ejemplo es como guardamos el certificado que se usa para el SII en España o el NF-e brasileño para luego usarlos en las llamadas a los servicios web.

Asegurando tus Pipelines de Azure DevOps

Gracias a la task de Azure Key Vault (que es open source como muchas otras tasks) recuperar un secreto de un Key Vault no tiene secreto (badum tssss).

Crear el Key Vault

Ve a tu suscripción de Azure y busca Key vaults en la barra de búsqueda superior. Si no tienes una puedes obtener una gratuita con un crédito de 170€/200$ durante 30 días y probar esto y más cosas.

En la página del Key Vault haz clic en «Create key vault» y rellena los campos:

MSDyn365 y Azure DevOps ALM 50

Puedes seguir con las otras pestañas pero no hace falta, con clicar en «Review & Create» tendremos el Key Vault listo.

Añadir la task a DevOps

Ahora vamos a Azure DevOps y creamos un pipeline nuevo o editamos uno que ya exista. Añadimos la tarea al agente y buscamos por azure key vault:

MSDyn365 y Azure DevOps ALM 51

Es posible que tengáis que añadir la tarea del marketplace primero, si es así recordad que necesitaréis permisos a nivel de organización y no sólo del proyecto de AZDO en el que estáis. Ahora vamos a la tarea y seleccionamos la suscripción:

MSDyn365 y Azure DevOps ALM 52

Una vez seleccionada hacemos clic en el botón «Authorize». Esto va a crear un service principal en vuestra suscripción, lo usaremos más adelante. Después de autorizar seleccionamos el key vault que hemos creado en el primer paso. Y ahora volvemos a Azure.

Configuración y creación de los secretos

Vamos al key vault, seleccionamos «Access policies» y hacemos clic en «Add Access Policy»:

MSDyn365 y Azure DevOps ALM 53

Cuando autorizamos la task de DevOps para que accediera a nuestra suscripción, se creó un service principal que ahora tenemos que seleccionar para que pueda leer y usar los secretos en nuestro pipeline. Hacemos clic en «Select principal»:

MSDyn365 y Azure DevOps ALM 54

Y en la barra de búsqueda ponemos el nombre de la suscripción, el principal debería empezar con ese nombre y terminar con el ID de la suscripción. Lo seleccionamos y hacemos clic en «Select»:

MSDyn365 y Azure DevOps ALM 55

Ahora desplegamos el lookup de «Secret permissions» y en «Secret Management Operations» seleccionamos Get y List:

MSDyn365 y Azure DevOps ALM 56

Si quisieramos usar también certificados o claves deberíamos hacer lo mismo. Finalmente hacemos clic en el botón «Add» y no os olvidéis de darle al «Save» o no se guardará nada!

MSDyn365 y Azure DevOps ALM 57

Ahora ya podemos crear el secreto en el key vault. Vamos a «Secrets» y hacemos clic en «Generate/Import», completamos los campos y le damos a «Create» para guardar:

MSDyn365 y Azure DevOps ALM 58

Usando los secretos en tus pipelines

Ya tenemos todo listo para usar el secreto en el pipeline. Voy a añadir una tarea de PowerShell para llamar a la API de DB de LCS usando d365fo.tools, pero cambiaré todas las variables por los secrets:

Como véis incluso he puesto el AAD AppId en un secreto.

Lo que hace la task de Azure Key Vault es obtener los secretos de Azure y guardarlos en variables cuando se ejecuta el pipeline:

MSDyn365 y Azure DevOps ALM 59

Así podemos acceder al valor con la notación $(variableName) en el script de PowerShell. Si intentas sacar el valor por pantalla usando el Write-Host todo lo que obtendrás son tres asteriscos. Si pudieramos ver el valor de los secretos de una forma tan fácil todo esto no tendría mucho sentido.

Si ahora comprobamos el resultado de ejecutar el comando Get-D365LcsDatabaseBackups veremos como de bien funciona:

MSDyn365 y Azure DevOps ALM 60

Incluso ahora el valor del Id de proyecto no se muestra porque era uno de nuestros secretos!

Y así es como podemos añadir un poco de seguridad extra a nuestro Dev ALM!u

¡Suscríbete!

Recibe un correo cuando se publique un nuevo post
ariste.info