“Terraformando” nuestra infraestructura desde Azure Pipelines

La imagen muestra los logos de terraform, azure pipelines y azure cloud

En la última entrada, hablamos sobre Terraform como herramienta para para gestionar nuestra infraestructura mediante código. También hemos hablado hace poco sobre el despliegue continuo desde Azure Pipeline con ARM. Como pudimos comprobar, trabajar con Terraform nos facilita la vida respecto a lo que es el trabajo con ARM directamente. Hoy vamos a unir las dos cosas utilizando Terraform desde Azure Pipelines.

En su momento comentábamos que una de las grandes ventajas de Terraform es que mantiene el estado de los recursos. Esto es algo muy útil y que con trabajo local no supone ningún problema, pero eso se nos queda corto si trabajamos desde el agente de Azure Pipelines. Esto es porque cuando el agente termine va a borrar los recursos, y esto incluye el fichero de estado del despliegue. Para solucionar este “inconveniente” y persistir el estado entre despliegues, vamos a utilizar un Azure Storage donde guardar el fichero.

Una vez aclarado esto, ¡vamos a crear una WebApp en Linux y conectarla a una base de datos Sql Server!

Codificando la infraestructura

Para poder generar la infraestructura y conectarla entre si sin revisión manual, vamos a utilizar la potencia que nos ofrece Terraform para relacionar recursos y así añadir datos como la cadena de conexión. En GitHub está el proyecto completo, dividido en secciones para facilitar su mantenimiento, y utilizando la posibilidad de modular partes para crear la Web App y el Sql Server.

Utilizando estas ventajas, vamos a generar la cadena de conexión de la base de datos utilizando los datos de salida y se lo vamos a pasar a la configuración de la webapp. Para eso, vamos a crear una salida en el módulo de sql server donde vamos a generar la cadena de conexión:

output "conection_string_value" {
  description = "Sql Server ConnectionString"
  value = "Server=${azurerm_sql_server.sqlServer.fully_qualified_domain_name};Initial Catalog=${azurerm_sql_database.sqlServerDb.name};User Id=${var.SQL_ADMIN_ID};Password=${var.SQL_PASSWORD};"
}

Con esto, vamos a poder utilizarla más adelante:

module "linuxwebapp" {
  //...
  conection_string_value = "${module.sqlserver.conection_string_value}"
}

El código de la infraestructura es fácil de seguir, pero aun así recomiendo echarle un ojo en profundidad para entender el concepto anterior.

Creando la integración continua

Para poder desplegar Terraform desde Azure Pipelines, el primer paso es crear el pipeline de integración. De esto hablamos hace algún tiempo en una entrada sobre CI, pero esta vez vamos a utilizar la interfaz gráfica en vez de yml. Como se pueden ver, los pasos son los mismos que cuando utilizábamos yml:

La imagen muestra el pipeline de integración

Aquí solo hay un pequeño cambio respecto a lo que vimos anteriormente, y es que tenemos que meter los ficheros de Terraform al artefacto para poder utilizarlos más adelante:

La imagen señala los campos que hay que rellenar en la tarea de copiar archivos

Para configurar esta nueva tarea, basta con decirle el nombre de la carpeta donde está el código Terraform que queremos desplegar, ponerle el filtro de selección (** para seleccionarlo todo), y por último crear una carpeta en el directorio del artefacto y pegar los ficheros Terraform en él.

El hecho de utilizar la interfaz gráfica para la integración es solo por cambiar, pero para el despliegue solo se puede utilizar la interfaz gráfica de momento.

Ejecutar Terraform desde Azure Pipelines

Una vez que tenemos la integración lista, vamos a crear una Release para que despliegue la infraestructura y el proyecto. Para ahorrarnos trabajo, vamos a utilizar el Task “Terraform Build & Release Tasks“, así que vamos a instalarlo en el pipeline:

La imagen muestra el botón para instalar las Task de Terraform en Azure Pipelines

Una vez que lo tenemos instalado, vamos a crear el pipeline donde instalaremos Terraform, lo inicializaremos y aplicaremos para desplegar los recursos, y por último publicaremos la web:

La imagen muestra el pipeline de release

Al igual que hacíamos con ARM, lo ideal sería tener diferentes ranuras donde tengamos las diferentes etapas de dev, pre, pro, etc.

Además, vamos a necesitar diferentes variables, que vamos a registrar también en el pipeline, para eso, vamos a la pestaña “Variables”:

La imagen señala la pestaña "Variables"

Y vamos a registrar las variables que necesitamos para nuestro Terraform:

La imagen muestra las variables del pipeline

Por convención, el pipeline le pasa directamente las variables que coincidan y empiecen por TF_VAR a Terraform siempre que no sean secretos. Esto es algo a tener en cuenta para evitarnos un comando apply larguísimo donde le pasemos muchísimas variables.

Volviendo al pipeline, lo primero que tenemos que hacer es instalar Terraform, para eso, vamos a utilizar la Task “Terraform Installer” y le vamos a indicar la versión de Terraform que queremos utilizar:

La imagen muestra donde indicar la versión

El siguiente paso, es configurar una Task de tipo “Terraform CLI” para ejecutar el comando init:

La imagen muestra la interfaz de la Task Terraform CLI

Dentro de esta Task, vamos a seleccionar el comando “init”, y vamos a indicar la ruta donde está el código Terraform, por último, vamos a seleccionar el tipo de backend, que como dijimos al principio, será en Azure Storage, por lo tanto, seleccionamos “azurerm”. Esto nos permite ampliar la configuración pulsando sobre “AzureRM Backend Configuration”, y así indicarle los datos de configuración:

La imagen muestra la configuración del backend en el Task

Con esto listo, el último paso con Terraform es crear una tercera Task de tipo “Terraform CLI”. Esta vez vamos a elegir el tipo de comando “apply”, le vamos a indicar la ruta a donde están los ficheros de código Terrafom, y por último le vamos a indicar las opciones. Entre las opciones, vamos a indicarle “-auto-approve” para que no pida confirmación antes de desplegar los cambios, y le vamos a pasar todas las variables de tipo “secrets” mediante “-var VARIABLE=VARIABLE_PIPELINE” (recordemos que las demás variables se le pasan por convención):

Un ejemplo de las opciones utilizadas es:

-auto-approve -var AZURE_SUBSCRIPTION_ID=$(TF_VAR_AZURE_SUBSCRIPTION_ID) -var AZURE_CLIENT_ID=$(TF_VAR_AZURE_CLIENT_ID) -var AZURE_CLIENT_SECRET=$(TF_VAR_AZURE_CLIENT_SECRET) -var AZURE_TENANT_ID=$(TF_VAR_AZURE_TENANT_ID) -var SQL_PASSWORD=$(TF_VAR_SQL_PASSWORD)

Con esto, y si todo ha ido bien, ya vamos a conseguir desplegar los recursos en Azure. Para desplegar la Web, solo nos queda añadir un Task de tipo “Azure App Service Deploy” tal cual hicimos en el caso de ARM para desplegar la web.

Tras lanzar una release, dentro de nuestro portal en azure podremos encontrar algo como esto:

La imagen muestra los recursos de Azure deplegados mediante Terraform y Azure Pipelines

Para este ejemplo, hemos utilizado el template de ASP NET Core y le hemos añadido que ejecute las migraciones de la base de datos al iniciar, para que la web este lista para funcionar directamente.

Como siempre, he dejado el código fuente completo (tanto Terraform como la Web) en GitHub.

Conclusión

Como hemos podido comprobar, Terraform es una herramienta muy potente que nos facilita mucho la vida manejando infraestructura como código (IaC), además de permitirnos trabajar con múltiples proveedores. Podemos integrar perfectamente Terraform y Azure Pipelines, o cualquier otro servicio CI/CD utilizando los comandos directamente, por lo que es algo que vale la pena revisar y conocer.

Terraform: Dando forma a nuestra infraestructura

La imagen muestra el logo de terraform y el de azure

Acabamos de terminar la serie sobre CI/CD, y en ella hemos hablado de lo importante que es mantener unas estrategias de trabajo que nos permitan la detección de errores rápidamente. Como colofón final, vimos como aprovisionar y desplegar en Azure Web Apps desde el Azure Pipelines (o desde otros sitios) y así despreocuparnos de esa parte utilizando las plantillas ARM.

Utilizar ARM es muy potente, pero quizás es algo tedioso de utilizar… ahí es donde entra terraform. ¿Y que es terraform podrás preguntarte? Pues es una herramienta multiplataforma hecha en Go que nos permite una gran abstracción sobre ARM, pero no solo funciona con ARM, admite una lista de proveedores bastante larga y que puedes consultar aquí.

Además de permitirnos trabajar de forma declarativa, sin tener que bajar al fango, aporta una gran ventaja respecto a usar ARM, y es que terraform mantiene el estado. Es decir, la herramienta es capaz de almacenar el estado de las cosas que ya están desplegadas, evitando volver a desplegarlas si no hay cambios. Pero hablar es fácil, y que yo te cuente que algo es mejor o peor no sirve de mucho, así que vamos a probarlo para ver lo sencillo que es.

Instalando terraform

Vamos a ir a su web y descargarnos el binario para nuestro sistema operativo. En la web nos indican que tenemos que añadir la ruta donde hayamos puesto el binario al PATH. (En mi caso, “C:/Terraform”):

La imagen muestra la ruta de terraform en el PATH

Una vez que lo hayamos hecho, desde una terminal vamos a comprobar que todo esta bien ejecutando:

terraform version

Si todo está bien, debería devolvernos la versión actual. Si es así, ya tenemos terraform instalado. ¿A qué no ha sido tan difícil?

Aunque trabaja con ficheros *.tf que son texto plano, yo recomiendo utilizar Visual Studio Core con la extensión “Terraform“. Si tienes dudas sobre como instalar cualquiera de las dos, te dejo un enlace de CampusMVP donde explico cómo preparar un entorno de trabajo para .Net Core, al que solo hay que añadir la nueva extensión. Esto solo es una sugerencia, si prefieres utilizar cualquier otro entorno, o simplemente el block de notas y la terminal, todo funcionará igual de bien.

Creando nuestro código terraform

Para esta entrada, vamos a hacer un aprovisionamiento sencillo de una Azure Web App Windows, junto a su plan y su grupo de recursos desde terraform (para la siguiente vamos a hacer un escenario algo más complejo añadiendo un Sql Server a nuestra web). Para ello, vamos a crear un fichero al que llamaremos “main.tf” (el nombre no es importante). De hecho, terraform buscará todos los ficheros “*.tf” desde el nivel en el que se encuentre y unificará todos los archivos, por lo que es una buena idea separar los archivos por finalidades. En esta entrada vamos a utilizar un solo fichero para el código, pero una estructura más clara podría ser:

  • webapp.tf para el código de la web.
  • resourcegroup.tf para el código de grupo de recursos.
  • variables.tf para las variables que vamos a necesitar.

Definiendo el proveedor

Lo primero que vamos a hacer, es definir el proveedor junto a las 4 variables que necesita (para utilizar las variables, se hace con el formato “${var.NOMBRE_VARIABLE}” ):

provider "azurerm" {
  version         = ">= 1.6.0"
  subscription_id = "${var.ID_Suscripcion}"
  client_id       = "${var.ID_Aplicacion}"
  client_secret   = "${var.Password_Aplicacion}"
  tenant_id       = "${var.ID_Tenant}"
}
variable "ID_Suscripcion" {
  description = "ID de la suscripción de Azure"
}
variable "ID_Aplicacion" {
  description = "ID del la aplicación"
}
variable "Password_Aplicacion" {
  description = "Secreto de la aplicación"
}
variable "ID_Tenant" {
  description = "ID del directorio activo de Azure"
}

¿Y de dónde sacamos estos datos? Pues dos de ellas simplemente debemos consultarlas, para ello escribimos en la PowerShell:

az account list

Esto nos devolverá el ID de la suscripción y el directorio activo:

La imagen muestra el ID_Suscripcion y el ID_Tenant

Para los dos campos de aplicación, lo que vamos a hacer es registrar una aplicación en nuestro directorio activo. Esto se puede hacer desde el portal de Azure, o desde la propia terminal ejecutando:

az ad sp create-for-rbac -n "NombreAplicacion"

Eso nos va a devolver los datos que nos faltan:

La imagen muestra los datos de ID_Aplicacion y Password_Aplicacion

Si tienes problemas porque no se reconoce el comando “az”, sigue este enlace para instalar Azure CLI.

Definiendo el Grupo de Recursos

Una vez que lo tenemos, vamos a añadir un grupo de recursos para nuestra Web App:

resource "azurerm_resource_group" "webapp" {
  name     = "${var.resource_group_name}"
  location = "${var.location}"
}
variable "location" {
  description = "Region donde queremos que se cree"
}
variable "resource_group_name" {
  description = "Nombre del grupo de recursos"
}

Definiendo el Service Plan

Ahora vamos a añadir el service plan:

resource "azurerm_app_service_plan" "webserviceplan" {
  name                = "${var.service_plan_name}"
  location            = "${azurerm_resource_group.webapp.location}"
  resource_group_name = "${azurerm_resource_group.webapp.name}"

  kind = "${var.plan_settings["kind"]}"

  sku {
    tier     = "${var.plan_settings["tier"]}"
    size     = "${var.plan_settings["size"]}"
    capacity = "${var.plan_settings["capacity"]}"
  }
}
variable "plan_settings" {
  type        = "map"
  description = "Definimos el tipo de plan que vamos a utilizar"

  default = {
    kind     = "Windows"  # Linux or Windows
    size     = "S1"
    capacity = 1
    tier     = "Standard"
  }
}
variable "service_plan_name" {
  description = "Nombre del service plan"
}

Una de las ventajas que podemos ver, es que terraform nos permite utilizar datos de los propios recursos desplegados utilizando el formato “${TIPO_DE_RECURSO.NOMBRE_RECURSO.DATO}”. Por ejemplo, en:

location  = "${azurerm_resource_group.webapp.location}"

Le estamos indicando que la localización del service plan, queremos que sea la misma que la del recurso “azurerm_resource_group” con el nombre “webapp”. Trabajando de esta manera, terraform es capaz de calcular el grafo de dependencias para que todo se pueda aprovisionar sin problemas.

Definiendo la Web App

Por último, solo nos queda añadir el Web App:

resource "azurerm_app_service" "webapp" {
  name                = "${var.name}"
  location            = "${azurerm_resource_group.webapp.location}"
  resource_group_name = "${azurerm_resource_group.webapp.name}"
  app_service_plan_id = "${azurerm_app_service_plan.webserviceplan.id}"
}
variable "name" {
  description = "Nombre de la Web App"
}

Hay muchos parámetros, que podríamos poner en el propio código, pero entonces no sería reutilizable… Terarform nos permite indicarle todos estos parámetros cuando vayamos a hacer el despliegue en el propio comando, pero como puedes intuir, es muy tedioso. Así que vamos a utilizar otra de las opciones que nos da terraform, que es cargar tener un segundo fichero de variables con sus valores.

Si usas git, hay que añadirlo en el .gitignore o nuestras claves se irán al repositorio.

Para poder tener este fichero, creamos un segundo fichero con el formato “*.auto.tfvars”, por ejemplo, “variables.auto.tfvars”, donde iremos rellenando los valores de esta forma:

ID_Suscripcion = "----"
ID_Aplicacion = "----"
Password_Aplicacion = "----"
ID_Tenant = "----"
name = "postterraformbasic"
service_plan_name = "planpostterraformbasic"
resource_group_name = "postterraform"
location = "westeurope"

A la vista está que este código es más fácil de seguir y mantener que si fuese ARM directamente, aunque hay algunas cosas que no están soportadas aun, y puede hacer falta incluir algo de ARM en algunos casos…

Desplegando con terraform

Una vez que tenemos todo el código, y las variables listas, vamos a desplegar, para ello vamos a inicializar la herramienta con:

terraform init
La imagen muestra la salida del comando init

Una vez hecho esto, vamos a comprobar si existe algún error con:

terraform validate

En caso de que no exista ningún error, lo siguiente es calcular los cambios (aunque este paso se puede omitir):

terraform plan

La salida de este comando nos va a mostrar algo como esto:

La imagen muestra la salida del comando terraform plan

Como salida, nos da las acciones que va a ejecutar en detalle, pero solo son a nivel informativo, para ejecutar el despliegue, utilizamos:

terraform apply

Esto nos devuelve algo parecido al comando plan, donde nos dice que es lo que va a hacer, pero ahora, nos pide que confirmemos escribiendo “yes” si queremos que despliegue:

La imagen muestra la petición de confirmación de terraform

Tras escribir “yes”, se inicia el proceso. Una vez termine, la salida será algo asi:

La imagen muestra el resultado de finalización

Como terraform almacena el estado, si volvemos a intentar desplegar lo mismo, volviendo a ejecutar:

terraform apply

Obtenemos el resultado esperado, y nos dice que no hay ningún cambio pendiente:

La imagen muestra como el comando apply termina con 0 cambios

Si por el contrario hubiésemos cambiado algo en el código, el resultado hubiese sido un cambio en nuestra infraestructura. Para terminar con las opciones que tenemos, existe una manera de eliminar los recursos desplegados:

terraform destroy

Volverá a pedirnos confirmación, y basta con que volvamos a escribir “yes”:

La imagen muestra las acciones que va a hacer el comando terraform destroy
La imagen muestra el final del comando terraform destroy

Conclusión

Aunque ha sido un ejemplo simple, se puede ver que terraform es una herramienta muy potente que nos quita mucho trabajo a la hora de desplegar infraestructura desde código al ser declarativo, persistir el estado y encima calcular las dependencias automáticamente. Existe una lista detallada de las opciones disponibles en la documentación oficial, a la cual recomiendo echarle un ojo.

En la próxima entrada vamos a ver un ejemplo más en profundidad creando una web en Linux conectada a un Sql Server, de modo que tengamos una infraestructura funcional, y además desplegaremos la web desde Azure Pipelines para poder comparar el proceso en el caso de ARM y en el caso de Terraform.

Si quieres ver el código completo, dejo un gist. También os dejo el enlace a un repo muy interesante de un workshop sobre terraform de un compañero.

Herramientas de desarrollo: Azure Pipelines (CD) y ARM

La imagen muestra el logo de Azure Pipelines

Todo lo que empieza tiene un final, y hoy es el final de la serie sobre “Integración Continua y Despliegue Continuo” (CI/CD). Hoy vamos a hablar sobre cómo hacer una integración y despliegue completos sobe Azure Web Apps de un proyecto ASP NET Core con Azure Pipelines.

Para poder hacerlo, lo primero que necesitamos es aprovisionar los recursos en Azure, y hay varias maneras de hacerlo. Hablo de ellas en Variable Not Found:

En este caso, vamos a utilizar el sistema de aprovisionamiento desde AzureRM para crear el plan, la webapp y una segunda ranura que será donde hagamos el deploy de nuestro proyecto para cambiarlo después a producción. Utilizando este sistema vamos a conseguir que nuestro proyecto esté siempre online incluso mientras se hace la publicación, ya que vamos a publicar sobre una segunda ranura y solo después de que nuestro proyecto este publicado y online, haremos el cambio entre ranuras (internamente hace un cambio de DNS, por lo que el cambio es instantáneo). Una vez dicho esto, ¡vamos a meternos en faena!

Creando la integración continua

Dentro de esta serie, publique como hacer la integración de nuestro proyectos en Azure Pieplines, así que nos vamos a saltar esa parte. El fichero .yml podría ser algo como esto:

# Configuramos el pool con la imagen
pool:
  vmImage: 'ubuntu-16.04'

# Variables de trabajo
variables:
  buildConfiguration : 'Release'
  project : '**/*.csproj'
 
# Pasos
steps:
# Restore Nuget
- task: [email protected]
  displayName: Restore
  inputs:
    command: restore
    projects: '$(project)'
# Compilamos el proyecto
- task: [email protected]
  displayName: Build
  inputs:
    projects: '$(project)'
    arguments: '--configuration $(buildConfiguration)'

# Aquí van las pruebas unitarias
# ------

# Publicamos la solución indicándole que queremos zipearla
- task: [email protected]
  displayName: Publish
  inputs:
    command: publish
    publishWebProjects: True
    arguments: '--configuration $(buildConfiguration) --output $(build.artifactstagingdirectory)'
    zipAfterPublish: True

# Subimos los artefactos
- task: [email protected]
  displayName: 'Publish Artifact'
  inputs:
    PathtoPublish: '$(build.artifactstagingdirectory)'

Creando el despliegue continuo en Azure Pipelines

Con esto, ya tenemos nuestra integración funcionando y subiendo los artefactos, ahora solo falta el despliegue. La manera más sencilla quizás sea desde la propia ventana de resultados de la integración, pulsando sobre el botón Release:

La imagen muestra el botón "Release" dentro de la ventana de resultados de la integración de Azure Pipelines

Eso nos va a lanzar a una nueva página donde vamos a crear el pipeline. Por defecto nos va a dar muchas opciones, pero vamos a hacerlo desde 0, así que elegimos “empty job”:

La imagen indica "empty job" en el asistente de Azure Pipelines

Esto va a crear un job vacío, en el cual vamos a añadir todos los pasos que necesitamos. En nuestro caso son 3:

  1. Aprovisionar los recursos.
  2. Desplegar nuestro proyecto a una ranura de staging
  3. Cambiar las ranuras de staging y producción.

Para eso, vamos a pulsar sobre el Stage 1, para añadir tareas al job:

La imagen muestra el enlace que hay que pulsar para abrir la configuración

Esto nos lleva a las tareas que tiene el job, y ahí vamos a pulsar en “+” para añadir las nuestras:

La imagen señala el botón "+" para añadir tareas al job

Aquí vamos a tener que añadir 3 tareas, una por cada una de las 3 acciones que queremos hacer. La primera, será una tarea de aprovisionamiento, así que vamos a la pestaña “Deploy” y seleccionamos “Azure Resurce Group Deployment”:

La imagen muestra el recurso Azure Resurce Group Deployment

El siguiente que vamos a introducir, es “Azure App Service Deploy”:

La imagen muestra el recurso Azure App Service Deploy

Y por último, vamos a añadir “Azure App Service Manage”:

La imagen muestra el recurso Azure App Service Manage

Una vez añadidos los 3 recursos, nos quedará algo como esto:

La imagen muestra el resultado de añadir las 3 tareas al job de Azure Pipelines

Ahora vamos a configurarlos, para ello, vamos al primero y al pulsar sobre él, nos aparecen sus opciones:

La imagen enseña el asistente de aprovisionamiento de Azure Pipelines

Simplemente, tenemos que seleccionar nuestra suscripción y grupo de recursos donde queremos que se añadan, así como la zona para el despliegue. Después le indicamos que el template va a estar en una url y le pasamos las direcciones “raw” de los ficheros. Por ejemplo, en GitHub existe un botón para ir al fichero raw y luego basta con copiar la url:

La imagen señala el botón raw de github

Como se puede intuir, estos ficheros los genera el proyecto de “Grupo de Recursos de Azure” que hemos creado en Visual Studio, y los parámetros que hayamos puesto al validar nuestro script de aprovisionamiento:

La imagen señala los valores indicados en Visual Studio para el aprovisionamiento

Con esto, ya tenemos listo el aprovisionamiento de recursos. Ahora, vamos a configurar el despliegue de nuestro proyecto. Azure Pipelines nos da un asistente como este:

La imagen señala los campos del asistente de publicación

Aquí tenemos que tener especial cuidado, ya que vamos a “ciegas”. Me explico, como los recursos aún no están creados en Azure, vamos a tener que introducir los datos a mano porque los desplegables van a estar vacíos. Para eso, vamos a escribir los datos que hemos puesto al validar nuestro script de despliegue. También hay que tener cuidado con cuál es el nombre real que aplica nuestro despliegue, por ejemplo, la plantilla que hemos seleccionado para el aprovisionamiento añade “Portal” al nombre que hallamos elegido:

"webAppPortalName": "[concat(parameters('baseResourceName'), 'Portal')]"

Lo mismo pasa con la ranura (Slot) al que vamos a desplegar, realmente no existe mientras estamos configurando el despliegue, así que la escribimos a mano.

Este “problema” se puede solucionar si utilizamos las variables del pipeline, de manera que siempre sean los mismos valores. Si hacemos el aprovisionamiento de recursos antes de configurar esta parte también se evitaría porque si los recursos ya están creados, aparecerán todos correctamente en los desplegables.

Una vez que lo hemos terminado de configurar, lo último que nos falta es cambiar la ranura de “Staging” por la de “Produccion”, de manera que se haga un despliegue completo, y nuestro proyecto este accesible en producción. Para eso, configuramos el último apartado:

La imagen señala los campos del asistente de swap de ranuras

Igual que en el caso anterior, vamos a “ciegas” porque los recursos aun no existen. Sabiendo esto, escribimos manualmente el nombre del servicio que estamos operando, el grupo de recursos al que pertenece, y la ranura que queremos poner en producción. Puede que no queramos poner en producción la ranura y solo cambiarla por otra, esto lo podríamos conseguir si desmarcamos la casilla “Swap whit Production”, lo cual nos hace aparecer un segundo campo para rellenar con la ranura por la que queremos cambiar.

Una vez que hemos configurado las 3 tareas, pulsamos sobre el botón “Save” para guardar nuestro job:

La imagen muestra el botón de guardar el job

Ya tenemos nuestro despliegue listo. Ahora cada vez que hagamos un cambio en el repositorio, automáticamente se publicará. Antes de hacer cambios, podemos comprobar que nuestro pipeline funciona pulsando sobre el botón “Create a release”:

El la imagen señala el botón para lanzar un ciclo de release

Esto lanzará un ciclo de despliegue como podemos ver en la pestaña “Releases”:

La imagen muestra la nueva release en marcha

Una vez termine, si vamos a https://{nombre de nuestro servicio}.azurewebsites.net debería estar disponible nuestro proyecto. Si añadimos algún cambio y lo subimos a github, de que acabe la integración y el despliegue deberíamos poder ver en la url el cambio que hemos hecho. Por ejemplo:

La imagen muestra el resultado de hacer un cambio en el repositorio

Como es habitual en este tipo de entradas, el código esta disponible en GitHub para poder consultarlo.

Aviso a navegantes

No es oro todo lo que reluce, poner webs en producción de manera automática puede ser peligroso ya que puede haber errores de interfaz gráfica (o cualquier otro tipo) que no se hayan testeado mediante pruebas automáticas. Una mejor practica puede ser hacer despliegue sobre una ranura de “develop” donde poder comprobar la funcionalidad completa de la web, y una vez que estamos seguros de que todo esta bien, hacer el swap desde la ranura que hemos testeado de manera que los posibles errores estén comprobados ya.

Si por el contrario, lo único que estamos publicando es una API RESTful que hemos desarrollado con buenas prácticas y que no tiene estado (no hay datos que son necesarios mantener en memoria entre peticiones), no debería ser un problema hacer despliegue continuo automáticamente, ya que las peticiones de aquello que consuma nuestro servicio se lanzaran contra la versión establecida, y solo cuando cambiemos los clientes a la nueva versión se aplicará el cambio.

Conclusión sobre CI/CD

Aunque quizás el despliegue continuo a producción es algo idílico y difícil de alcanzar por la cantidad de posibles problemas y errores que podemos introducir, este solo es el paso final de una serie de buenas prácticas y metodologías de trabajo. Quedarse en cualquiera de los puntos anteriores al despliegue en producción sigue siendo algo muy válido, ya que estamos haciendo que no exista una dependencia tan fuerte entre código y programador, lo cual siempre facilita el trabajo y la mantenibilidad de los proyectos. Esto añadido a una buena política de pruebas unitarias,
evita los problemas asociados a realizar proyectos en equipos de trabajo, donde cada persona no tiene por qué conocer el alcance del trabajo del resto. Con la variedad de proveedores que ofrecen estos servicios, no buscar uno que se adapte a nuestras necesidades e implementarlo puede ser una decisión que a la larga salga cara.