MSDyn365 y Azure DevOps ALM

En esta página podréis encontrar un resumen de los posts que he escrito sobre Dynamics 365 for Finance and Operations y Azure DevOps. Están ordenados de forma que te ayudarán a entender, configurar y usar Azure DevOps con Dynamics365. Azure DevOps y TFVC son unas herramientas magníficas y tenemos mucha suerte de poder usarlas al fin, pero parece que hay gente que sigue pensando que usarlos con MSDyn365FO es opcional o que sólo usa el control de versiones. Espero que esto sea de ayuda!

Contenido ocultar

Dynamics 365 for Finance & Operations y Azure DevOps

Con la llegada de Dynamics 365 el uso de un sistema de control de versiones se ha convertido en obligatorio. En anteriores versiones disponíamos de Morph VCS en AX 2009 y la posibilidad de integrar TFS en AX 2009 y AX 2012 (del que hay un completo curso en El rincón Dynamics), pero no existía ninguna obligación de usar ninguno de los dos. En realidad, siempre según mi experiencia, creo que la mayoría de proyectos se llevaban a cabo sin ningún tipo de control de versiones aparte de, con suerte, comentarios en el código.

El AOT antes de la llegada del control de versiones
El AOT antes de la llegada del control de versiones, de cazapelusas.com

Azure DevOps en MsDyn365FO

En Microsoft Dynamics 365 for Finance and Operations el control de versiones que nos ofrece Azure DevOps no es un simple control de versiones, sino una LA herramienta que hará un poco de Anillo Único en nuestros proyectos (solo que, espero, no para atarnos en las tinieblas). Y es que el cambio no solo afecta al equipo técnico. Desde dirección de proyecto a funcionales pueden implicarse en el uso de Azure DevOps para la gestión del proyecto.

La sincronización del BPM y creación de todas las tareas, planificación del equipo, gestión del código, builds automatizadas y releases que veíamos en un post anterior, son algunas de las herramientas que nos ofrece. Todos estos cambios requieren de un aprendizaje y adaptación por parte del equipo al completo, pero van a ayudarnos mucho en el control del proyecto.

Como decía, puede parecer que el equipo técnico sea el más afectado por la introducción de Azure DevOps por la «obligatoriedad» de usarlo en Visual Studio (bendita obligación), pero también es el que más provecho le va a sacar… 😉

Primeros pasos

Lo primero que tenemos que hacer cuando empezamos un nuevo proyecto de implantación es conectar LCS y el proyecto de Azure DevOps que vayamos a usar. Está todo muy bien explicado en la documentación.

Una vez configurada la conexión tenemos que desplegar el entorno de Build que vimos en el anterior post. Esto se hace habitualmente en la máquina de desarrollo disponible en la suscripción de Microsoft en el proyecto de LCS. Cuando se despliega esta máquina se va a crear la estructura básica de carpetas en el proyecto de DevOps:

Carpetas en proyecto de Azure DevOps

(Ignorad la carpeta CSProjects que es de la actuación cómica del pasado 365 Saturday con mi compañero Juanan)

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.

Carpeta convertida en rama

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 1

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!

La verdad es que me encantaría una sesión de FastTrack sobre esto y, creo que no la hay. EDIT: parece que no lo vi y sí que existe una sesión de FastTrack sobre esto que se llama Developer ALM. Gracias a Dag Calafell (twitter) por la información!

Como vimos en la primera parte cuando desplegamos la máquina de Build se crea la carpeta de Main. Lo normal es que en un proyecto de implantación se desarrolle sobre Main hasta el momento del arranque, y que justo antes del go live se cree una branch (rama) de desarrollo. El árbol de código quedaría así:

Ramas despues de branch

En ese momento los mapeos de las máquinas de desarrollo deben cambiarse para que apunten a esta nueva rama de dev. Esto permitirá seguir desarrollando mejoras o corrigiendo errores y decidir el momento en el que se van a mover a producción haciendo un merge a Main.

Esta estrategia es bastante sencilla y que no provoca muchos quebraderos de cabeza. En mi anterior trabajo en cliente final decidimos usar 3 ramas por peculiaridades de la empresa. Main, Dev y Test con merges de Dev a Test y de Test a Main. Un dolor de cabeza al final, gestionar las 3 ramas, con upgrades de versiones, decenas de changesets pendientes y un partner ISV que no ayudaba mucho era bastante divertido.  ¿Pero, y lo que aprendí? Buf.

En cualquier caso un consejo: intentad evitar que se queden changesets pendientes de mergear durante mucho tiempo. La cantidad de conflictos que aparecen y hay que resolver a mano es directamente proporcional a lo viejo que sea el changeset.

Llegado a este punto no puedo hacer suficiente hincapié en lo de normal de más arriba. Como digo, esto lo escribo basado en mis experiencias. Está claro que no es lo mismo trabajar en un partner de implantación que en un ISV. Un ISV tiene la necesidad de mantener diferentes versiones de su producto y no va a usar una rama Main y otra Dev sinó que puede tener una (o varias) por versión a mantener para dar soporte a todos los clientes (aunque desde la 8.1 y el fin del overlayering ya no es necesario). Para más «ideas» el artículo que he enlazado al principio es perfecto para empezar.

El servidor de build

El servidor de build es una máquina un poco distinta a las demás de Microsoft Dynamics 365 for Finance and Operations. Aparentemente es exactamente igual que una VM de desarrollo, tiene Visual Studio, tiene un AosService/PackagesLocalDirectory con los modelos y XML de los objetos del AOT, un servidor de SQL y una AxDB y todos los servicios que usa MSDyn365FO (MR, Batch y IIS/IIS Express).

Pero no usamos nada de eso. El «corazón» del servidor de build en realidad es el build agent, una aplicación a la que accede Azure DevOps para realizar algunas de las tareas de cada build. Esto se enlaza al configurar el proyecto de DevOps en LCS como veremos en otro post 😉

En 2020 vamos a poder probar y usar el lanzar las builds usando los agentes hospedados en Azure, no hará falta la máquina virtual de build! Pero esto tiene solo un pequeño pero, seguiremos necesitando la máquina de build si tenemos que ejecutar tests.

Builds

La definición de build que se genera por defecto al desplegar el servidor es como la de la imagen inferior:

Pasos de la definición de build por defecto

Esta build tiene todos los pasos con los que se ha creado activos. Podemos desactivar (o borrar) los pasos que no necesitemos. Por ejemplo, los 3 de testeo si no tenemos tests creados, o la sincronización y despliegue de informes.

Podemos crear nuevas definiciones de build a partir de esta o de 0 (pero es más sencillo y rápido duplicar esta y modificarla) para que se apliquen a otras ramas u otros motivos.

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. Hasta la versión 8.0 era muy útil tener una definición de build únicamente para nuestros modelos y otra con todos los modelos. Con esto lo que se consigue es tener un DP en mucho menos tiempo que generándolo para todos los modelos. Si se aplica algún hotfix hay que generar el DP de la rama con todos los modelos, pero si sólo hay código propio no hace falta generar un paquete con todos los modelos.

Y hasta aquí la información desactualizada. A estas alturas todos los proyectos deberían estar en 8.1 o listos para estarlo, que en abril llega One Version!

Otra opción que es bastante útil es que, por ejemplo, podemos crear una nueva definición que lo único que haga es compilar una rama:

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.

Igual alguien con más conocimiento de esto piensa, pero eso no lo puedes hacer con…

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 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:

Nueva app azure AD

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

Permiso de LCS

Seleccionamos el único permiso disponible en el paso 2 y aceptamos hasta que aparezca el nuevo permiso en la sección «Required permissions». En este paso nos falta únicamente pulsar en «Grant permissions» para que se apliquen los cambios:

Grant permission

Sin este último paso la subida a LCS no se podrá realizar. Una vez hemos hecho esto guardamos el Application ID para usarlo más adelante.

Crear release en Azure DevOps

Antes de configurar nada en Azure DevOps tenemos que asegurarnos que el proyecto que vamos a usar esté vinculado en LCS. Esto lo podemos comprobar en el apartado de «Visual Studio Team Services» en la configuración del proyecto de LCS.

Una vez comprobado esto crearemos la definición de release en DevOps desde 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».

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:Release Dynamics Operations

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.

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 2

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 3

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 4

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.

¡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 5

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 6

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 7

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 8

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 9

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 10

¡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 11

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 12

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

MSDyn365 y Azure DevOps ALM 13

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 14

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

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

MSDyn365 y Azure DevOps ALM 15

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!

Ahora la mala noticia

La mala noticia es que, de momento, en los entornos de tipo self-service no se puede automatizar el despliegue. Y obviamente en los entornos de producción tampoco se puede automatizar nada, esa parte sigue siendo 100% manual.

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 16

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

MSDyn365 y Azure DevOps ALM 17

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 18

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!

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 19

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 20

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 21

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 22

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 23

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 24

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

MSDyn365 y Azure DevOps ALM 25

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 26

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 27

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 28

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 29

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!

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 🙂

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.

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

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.

¡Suscríbete!

Recibe un correo cuando se publique un nuevo post