Planeación de la estructura del archivo de Bicep

Completado

Bicep ofrece la flexibilidad de decidir cómo estructurar el código. En esta unidad, veremos las formas en que podemos estructurar el código de Bicep y la importancia de usar un estilo coherente y un código de Bicep claro y comprensible.

¿Qué orden debe seguir un código de Bicep?

Las plantillas de Bicep pueden contener muchos elementos, como parámetros, variables, recursos, módulos, salidas y un elemento targetScope para toda la plantilla. En Bicep, los elementos no deben seguir un orden obligatorio, si bien es importante tener en cuenta el orden de los elementos para procurar que la plantilla sea clara y comprensible.

Hay dos métodos principales para ordenar el código:

  • Agrupar los elementos por tipo de elemento
  • Agrupar los elementos por recurso

Dentro del equipo deberemos decidirnos por uno y usarlo de forma coherente.

Agrupar los elementos por tipo de elemento

Todos los elementos del mismo tipo se pueden agrupar. Todos los parámetros estarán en un mismo lugar, normalmente al principio del archivo. Luego van las variables, seguidas de los recursos y los módulos y, por último, las salidas. Por ejemplo, podemos tener un archivo de Bicep que implemente una base de datos de Azure SQL y una cuenta de almacenamiento.

Al agrupar los elementos por tipo, podrían tener este aspecto:

Diagrama que muestra elementos agrupados por tipo de elemento: primero se agrupan los parámetros, luego las variables, luego los recursos y, por último, las salidas

Sugerencia

Si sigue esta convención, considere la posibilidad de colocar el elemento targetScope al inicio del archivo.

Esta ordenación tiene sentido si estamos familiarizados con otros lenguajes de infraestructura como código (por ejemplo, el lenguaje de las plantillas de Azure Resource Manager). También puede hacer que la plantilla sea fácil de entender, ya que está claro dónde encontrar tipos específicos de elementos. Sin embargo, en plantillas más largas, puede ser difícil desplazarse por los elementos o ir de uno a otro.

Todavía queda por decidir cómo ordenar los elementos dentro de estas categorías. Agrupar los parámetros relacionados es buena idea. Por ejemplo, todos los parámetros que tengan que ver con una cuenta de almacenamiento van de la mano y ese es el caso de los parámetros de SKU de la cuenta de almacenamiento.

De forma similar, también podemos agrupar los recursos relacionados. Esto ayuda a que cualquiera que use la plantilla pueda desplazarse por ella rápidamente y entender las partes importantes de esta.

A veces, creamos una plantilla que implementa un recurso principal, con varios recursos secundarios complementarios. Por ejemplo, podríamos crear una plantilla para implementar un sitio web hospedado en Azure App Service. El recurso principal es la aplicación de App Service, mientras que los recursos secundarios de la misma plantilla podrían ser el plan de App Service, la cuenta de almacenamiento y la instancia de Application Insights, entre otros. Cuando tenga una plantilla como esta, es una buena idea colocar el recurso principal o los recursos en la parte superior de la sección de recursos de la plantilla, de modo que cualquier persona que abra la plantilla pueda identificar rápidamente el propósito de la plantilla y puede encontrar los recursos importantes.

Agrupar los elementos por recurso

Como alternativa, puede agrupar los elementos en función del tipo de recursos que va a implementar. Siguiendo con el ejemplo anterior, podríamos agrupar todos los parámetros, las variables, los recursos y las salidas que guarden relación con los recursos de base de datos de Azure SQL. Luego, podríamos agregar los parámetros, las variables, los recursos y las salidas de la cuenta de almacenamiento, como se muestra aquí:

Diagrama que muestra elementos agrupados por recurso: primero se agrupan los elementos de la cuenta de almacenamiento, seguidos de los elementos de base de datos de Azure SQL

Agrupar por recurso puede facilitar la lectura de la plantilla, ya que todos los elementos que necesitamos para un recurso específico se encuentran en el mismo sitio, Sin embargo, hace que sea más difícil comprobar rápidamente cómo se declaran tipos de elementos específicos si, por ejemplo, queremos revisar todos los parámetros.

También debemos tener en cuenta cómo se van a tratar los parámetros y variables que son comunes a varios recursos, como un parámetro environmentType cuando usamos una asignación de configuración. Los parámetros y variables comunes deben estar en el mismo sitio, normalmente al inicio del archivo de Bicep.

Sugerencia

Valore si podría tener más sentido crear módulos de grupos de recursos relacionados y usar una plantilla más sencilla para combinar esos módulos. En las rutas de aprendizaje de Bicep se detallan los módulos de Bicep en mayor profundidad.

¿Cómo ayudan los espacios en blanco a crear una estructura?

Las líneas en blanco, o espacios en blanco, sirven para conferir una estructura visual a la plantilla. Si usamos los espacios en blanco con atención, podemos agrupar las secciones del código de Bicep lógicamente, lo que a su vez sirve para explicar las relaciones entre los recursos. Para ello, considere la posibilidad de agregar una línea en blanco entre las secciones principales, independientemente del estilo de agrupación que prefiera.

¿Cómo se definen varios recursos similares?

Con Bicep, podemos usar bucles para implementar recursos similares a partir de una misma definición. Si usamos la palabra clave for para definir bucles de recursos, conseguiremos que el código de Bicep sea más limpio y reduciremos la duplicación innecesaria de definiciones de recursos. Posteriormente, cuando necesitemos cambiar la definición de los recursos, solo tendremos que actualizar un único sitio. Cuando Azure Resource Manager implementa los recursos, los implementa todos a la vez en el bucle de forma predeterminada, con lo cual la implementación es lo más eficaz posible.

Busque sitios en los que haya definido varios recursos que sean idénticos o que apenas tengan diferencias en sus propiedades. Luego, agregue una variable para enumerar los recursos que se van a crear, junto con las propiedades que difieren de los demás recursos. En el siguiente ejemplo se usa un bucle para definir un conjunto de contenedores de Azure Cosmos DB, cada uno de los cuales tiene un nombre y una clave de partición propios:

var cosmosDBContainerDefinitions = [
  {
    name: 'customers'
    partitionKey: '/customerId'
  }
  {
    name: 'orders'
    partitionKey: '/orderId'
  }
  {
    name: 'products'
    partitionKey: '/productId'
  }
]

resource cosmosDBContainers 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2024-05-15' = [for cosmosDBContainerDefinition in cosmosDBContainerDefinitions: {
  parent: cosmosDBDatabase
  name: cosmosDBContainerDefinition.name
  properties: {
    resource: {
      id: cosmosDBContainerDefinition.name
      partitionKey: {
        kind: 'Hash'
        paths: [
          cosmosDBContainerDefinition.partitionKey
        ]
      }
    }
    options: {}
  }
}]

¿Cómo se implementan recursos únicamente en ciertos entornos?

A veces, definimos recursos que se deben implementar únicamente en entornos específicos o en determinadas condiciones. Si usamos la palabra clave if, podemos implementar recursos de forma selectiva en función de un valor de parámetro, una variable de asignación de configuración u otra condición. En el siguiente ejemplo se usa una asignación de configuración para implementar recursos de registro de entornos de producción, pero no de entornos de prueba:

var environmentConfigurationMap = {
  Production: {
    enableLogging: true
  }
  Test: {
    enableLogging: false
  }
}

resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = if (environmentConfigurationMap[environmentType].enableLogging) {
  name: logAnalyticsWorkspaceName
  location: location
}

resource cosmosDBAccountDiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (environmentConfigurationMap[environmentType].enableLogging) {
  scope: cosmosDBAccount
  name: cosmosDBAccountDiagnosticSettingsName
  properties: {
    workspaceId: logAnalyticsWorkspace.id
    // ...
  }
}

¿Cómo se expresan las dependencias entre los recursos?

En cualquier plantilla de Bicep compleja, hay que expresar las dependencias entre los recursos. Cuando Bicep comprende las dependencias entre los recursos, las implementa en el orden correcto.

Si bien Bicep permite especificar expresamente una dependencia a través de la propiedad dependsOn, la mayoría de las veces podemos dejar que Bicep detecte automáticamente las dependencias. Si usamos el nombre simbólico de un recurso dentro de una propiedad de otro recurso, Bicep detecta la relación. Lo mejor es dejar que Bicep se encargue este tipo de cuestiones siempre que sea posible, ya que de este modo, cuando la plantilla cambie, Bicep garantizará que las dependencias sean siempre correctas y no agregará código innecesario que haga que la plantilla sea más complicada y más difícil de leer.

¿Cómo se expresan las relaciones de elementos primarios y secundarios?

Azure Resource Manager y Bicep acuñan el concepto de recursos secundarios, algo que tiene sentido únicamente cuando se implementan dentro del contexto de su elemento primario correspondiente. Por ejemplo, una base de datos de Azure SQL es un elemento secundario de una instancia de SQL Server. Existen varias formas de definir recursos secundarios, pero casi siempre lo más conveniente es usar la propiedad parent. Esto ayuda a Bicep a comprender la relación para que pueda proporcionar validación en Visual Studio Code y hace que la relación sea clara para cualquier otra persona que lea la plantilla.

¿Cómo se establecen las propiedades de los recursos?

En los archivos de Bicep hay que especificar los valores de las propiedades de los recursos. Conviene tener cuidado a la hora de codificar valores de forma rígida en las definiciones de los recursos. Si sabemos que los valores no van a cambiar, la codificación rígida podría ser mejor que usar otro parámetro que dificulte la comprobación y facilidad de uso de la plantilla. Sin embargo, si existe la posibilidad de que los valores cambien, sopese la posibilidad de definirlos como parámetros o variables para que el código de Bicep sea más dinámico y reutilizable.

Cuando se usan valores de código rígido, no está de más asegurarse de que son comprensibles para otros usuarios. Por ejemplo, si una propiedad debe establecerse en un valor específico para que el recurso se comporte correctamente en nuestra solución, estudie la posibilidad de crear una variable con un nombre adecuado que sea explicativo y, tras ello, asignar el valor mediante la variable. Cuando un nombre de variable no aporte toda la información necesaria, plantéese incluir un comentario. Más adelante en este módulo nos centraremos en los comentarios.

En el caso de algunas propiedades de recursos, para generar valores automáticamente deberemos crear expresiones complejas que incluyan funciones e interpolación de cadenas. El código de Bicep suele ser más claro si declaramos variables y hacemos referencia a ellas en los bloques de código de los recursos.

Sugerencia

Al crear salidas, intente usar las propiedades de recursos siempre que pueda hacerlo. Evite incorporar sus propias suposiciones sobre cómo funcionan los recursos, ya que estas suposiciones pueden cambiar con el tiempo.

Por ejemplo, si necesita generar la dirección URL de una aplicación de App Service, evite construir una dirección URL:

output hostname string = '${app.name}.azurewebsites.net'

El enfoque anterior se interrumpirá si App Service cambia la manera en que se asignan nombres de host a las aplicaciones o si realiza la implementación en entornos de Azure que usan direcciones URL diferentes.

En su lugar, use la propiedad defaultHostname del recurso de aplicación:

output hostname string = app.properties.defaultHostname

¿Cómo se usa el control de versiones de manera eficaz?

Los sistemas de control de versiones, como Git, pueden ayudar a simplificar el trabajo al refactorizar código.

Los sistemas de control de versiones están diseñados para realizar un seguimiento de los cambios en los archivos, de modo que podemos usarlos para regresar sin problemas a una versión anterior del código si cometemos un error. Conviene confirmar el trabajo a menudo para poder volver al momento en el tiempo exacto que necesitemos.

El control de versiones también ayuda a quitar el código antiguo de los archivos de Bicep. ¿Qué ocurre si el código de Bicep incluye una definición de recursos que ya no necesitamos? Puede que en el futuro necesitemos la definición de nuevo y, aunque es tentador quitarle la marca de comentario y mantenerla solo en el archivo, lo único que conseguiremos será enmarañar el archivo de Bicep, y otros usuarios no entenderán por qué los recursos sin marca de comentario siguen apareciendo ahí.

Otro aspecto que hay que tener en cuenta es que alguien puede quitar la definición del comentario por error, lo que conlleva unos resultados impredecibles o potencialmente perjudiciales. Si usamos un sistema de control de versiones, simplemente se puede quitar la definición de recurso anterior. Si la necesitáramos más adelante, podemos recuperarla del historial de archivos.