azure-verified-modules

Par hashicorp · agent-skills

Exigences et bonnes pratiques d'Azure Verified Modules (AVM) pour le développement de modules Azure Terraform certifiés. À utiliser lors de la création ou de la révision de modules Azure nécessitant une certification AVM.

npx skills add https://github.com/hashicorp/agent-skills --skill azure-verified-modules

Exigences Azure Verified Modules (AVM)

Ce guide couvre les exigences obligatoires pour la certification Azure Verified Modules. Ces exigences garantissent la cohérence, la qualité et la maintenabilité des modules Terraform Azure.

Références:

Table des matières


Référencement croisé de modules

Sévérité: MUST | Exigence: TFFR1

Lors de la création de modules Resource ou Pattern, les propriétaires de modules PEUVENT référencer d'autres modules. Cependant:

  • Les modules DOIVENT être référencés en utilisant le registry Terraform HashiCorp avec une version épinglée
    • Exemple: source = "Azure/xxx/azurerm" avec version = "1.2.3"
  • Les modules NE DOIVENT PAS utiliser de références git (ex: git::https://xxx.yyy/xxx.git ou github.com/xxx/yyy)
  • Les modules NE DOIVENT PAS contenir de références à des modules non-AVM

Exigences du fournisseur Azure

Sévérité: MUST | Exigence: TFFR3

Les auteurs DOIVENT utiliser uniquement les fournisseurs Azure suivants:

Fournisseur Version min Version max
azapi >= 2.0 < 3.0
azurerm >= 4.0 < 5.0

Exigences:

  • Les auteurs PEUVENT choisir soit Azurerm, Azapi, soit les deux fournisseurs
  • DOIVENT utiliser le bloc required_providers pour appliquer les versions de fournisseur
  • DEVRAIENT utiliser l'opérateur de contrainte de version pessimiste (~>)

Exemple:

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0"
    }
    azapi = {
      source  = "Azure/azapi"
      version = "~> 2.0"
    }
  }
}

Normes de style de code

Casse snake_case minuscule

Sévérité: MUST | Exigence: TFNFR4

DOIVENT utiliser la casse snake_case minuscule pour:

  • Locals
  • Variables
  • Outputs
  • Ressources (noms symboliques)
  • Modules (noms symboliques)

Exemple: snake_casing_example

Tri des ressources et sources de données

Sévérité: SHOULD | Exigence: TFNFR6

  • Les ressources dont d'autres dépendent DEVRAIENT venir en premier
  • Les ressources avec dépendances DEVRAIENT être définies à proximité les unes des autres

Utilisation de count et for_each

Sévérité: MUST | Exigence: TFNFR7

  • Utiliser count pour la création conditionnelle de ressources
  • DOIVENT utiliser map(xxx) ou set(xxx) comme collection for_each de la ressource
  • La clé de la map ou l'élément de l'ensemble DOIVENT être des littéraux statiques

Exemple:

resource "azurerm_subnet" "pair" {
  for_each             = var.subnet_map  # map(string)
  name                 = "${each.value}-pair"
  resource_group_name  = azurerm_resource_group.example.name
  virtual_network_name = azurerm_virtual_network.example.name
  address_prefixes     = ["10.0.1.0/24"]
}

Tri interne des blocs ressource et données

Sévérité: SHOULD | Exigence: TFNFR8

Ordre au sein des blocs ressource/données:

  1. Métarguments (en haut):

    • provider
    • count
    • for_each
  2. Arguments/blocs (au milieu, alphabétique):

    • Arguments requis
    • Arguments optionnels
    • Blocs imbriqués requis
    • Blocs imbriqués optionnels
  3. Métarguments (en bas):

    • depends_on
    • lifecycle (avec sous-ordre: create_before_destroy, ignore_changes, prevent_destroy)

Séparer les sections avec des lignes vides.

Tri des blocs module

Sévérité: SHOULD | Exigence: TFNFR9

Ordre au sein des blocs module:

  1. Métarguments en haut:

    • source
    • version
    • count
    • for_each
  2. Arguments (alphabétique):

    • Arguments requis
    • Arguments optionnels
  3. Métarguments en bas:

    • depends_on
    • providers

Syntaxe ignore_changes dans lifecycle

Sévérité: MUST | Exigence: TFNFR10

L'attribut ignore_changes NE DOIT PAS être entouré de guillemets doubles.

Bon:

lifecycle {
  ignore_changes = [tags]
}

Mauvais:

lifecycle {
  ignore_changes = ["tags"]
}

Comparaison Null pour la création conditionnelle

Sévérité: SHOULD | Exigence: TFNFR11

Pour les paramètres nécessitant une création conditionnelle de ressources, encapsuler avec un type object pour éviter les problèmes de "known after apply" lors de la phase de plan.

Recommandé:

variable "security_group" {
  type = object({
    id = string
  })
  default = null
}

Blocs dynamiques pour les objets imbriqués optionnels

Sévérité: MUST | Exigence: TFNFR12

Les blocs imbriqués sous conditions DOIVENT utiliser ce motif:

dynamic "identity" {
  for_each = <condition> ? [<some_item>] : []

  content {
    # contenu du bloc
  }
}

Valeurs par défaut avec coalesce/try

Sévérité: SHOULD | Exigence: TFNFR13

Bon:

coalesce(var.new_network_security_group_name, "${var.subnet_name}-nsg")

Mauvais:

var.new_network_security_group_name == null ? "${var.subnet_name}-nsg" : var.new_network_security_group_name

Déclarations de fournisseur dans les modules

Sévérité: MUST | Exigence: TFNFR27

  • provider NE DOIT PAS être déclaré dans les modules (sauf pour les configuration_aliases)
  • Les blocs provider dans les modules DOIVENT utiliser uniquement alias
  • Les configurations de fournisseur DEVRAIENT être transmises par les utilisateurs du module

Exigences des variables

Variables non autorisées

Sévérité: MUST | Exigence: TFNFR14

Les propriétaires de modules NE DOIVENT PAS ajouter de variables comme enabled ou module_depends_on pour contrôler l'opération entière du module. Les basculements de fonctionnalités booléens pour des ressources spécifiques sont acceptables.

Ordre de définition des variables

Sévérité: SHOULD | Exigence: TFNFR15

Les variables DEVRAIENT suivre cet ordre:

  1. Tous les champs requis (alphabétique)
  2. Tous les champs optionnels (alphabétique)

Règles de nommage des variables

Sévérité: SHOULD | Exigence: TFNFR16

Variables avec descriptions

Sévérité: SHOULD | Exigence: TFNFR17

  • description DEVRAIT décrire précisément le but et le type de données attendu du paramètre
  • Le public cible est les utilisateurs du module, non les développeurs
  • Pour les types object, utiliser le format HEREDOC

Variables avec types

Sévérité: MUST | Exigence: TFNFR18

  • type DOIT être défini pour chaque variable
  • type DEVRAIT être aussi précis que possible
  • any PEUT être utilisé uniquement avec des raisons adéquates
  • Utiliser bool au lieu de string/number pour les valeurs vrai/faux
  • Utiliser un object concret au lieu de map(any)

Variables de données sensibles

Sévérité: SHOULD | Exigence: TFNFR19

Si le type d'une variable est object et contient des champs sensibles, l'ensemble de la variable DEVRAIT être sensitive = true, ou extraire les champs sensibles en variables séparées.

Valeurs par défaut non nullables pour les collections

Sévérité: SHOULD | Exigence: TFNFR20

Nullable DEVRAIT être défini à false pour les valeurs de collection (ensembles, maps, listes) lorsqu'elles sont utilisées dans des boucles. Pour les valeurs scalaires, null peut avoir une signification sémantique.

Décourager la nullabilité par défaut

Sévérité: MUST | Exigence: TFNFR21

nullable = true DOIT être évité sauf s'il y a un besoin sémantique spécifique de valeurs nulles.

Éviter sensitive = false

Sévérité: MUST | Exigence: TFNFR22

sensitive = false DOIT être évité (c'est la valeur par défaut).

Conditions de valeur par défaut sensible

Sévérité: MUST | Exigence: TFNFR23

Une valeur par défaut NE DOIT PAS être définie pour les entrées sensibles (ex: mots de passe par défaut).

Gestion des variables dépréciées

Sévérité: MUST | Exigence: TFNFR24

  • Déplacer les variables dépréciées vers deprecated_variables.tf
  • Annoter avec DEPRECATED au début de la description
  • Déclarer le nom du remplacement
  • Nettoyer lors des versions majeures

Exigences des outputs

Outputs Terraform supplémentaires

Sévérité: SHOULD | Exigence: TFFR2

Les auteurs NE DEVRAIENT PAS exporter des objets de ressources entiers car ils peuvent contenir des données sensibles et le schéma peut changer avec les versions d'API ou de fournisseur.

Bonnes pratiques:

  • Exporter des attributs calculés de ressources en tant qu'outputs discrets (motif de couche anti-corruption)
  • NE DEVRAIENT PAS exporter des valeurs qui sont déjà des entrées (sauf name)
  • Utiliser sensitive = true pour les attributs sensibles
  • Pour les ressources déployées avec for_each, exporter les attributs calculés dans une structure map

Exemples:

# Attribut calculé de ressource unique
output "foo" {
  description = "MyResource foo attribute"
  value       = azurerm_resource_myresource.foo
}

# Ressources for_each
output "childresource_foos" {
  description = "MyResource children's foo attributes"
  value = {
    for key, value in azurerm_resource_mychildresource : key => value.foo
  }
}

# Output sensible
output "bar" {
  description = "MyResource bar attribute"
  value       = azurerm_resource_myresource.bar
  sensitive   = true
}

Outputs de données sensibles

Sévérité: MUST | Exigence: TFNFR29

Les outputs contenant des données confidentielles DOIVENT être déclarés avec sensitive = true.

Gestion des outputs dépréciés

Sévérité: MUST | Exigence: TFNFR30

  • Déplacer les outputs dépréciés vers deprecated_outputs.tf
  • Définir les nouveaux outputs dans outputs.tf
  • Nettoyer lors des versions majeures

Normes des valeurs locales

Organisation de locals.tf

Sévérité: MAY | Exigence: TFNFR31

  • locals.tf DEVRAIT contenir uniquement des blocs locals
  • PEUT déclarer des blocs locals à côté des ressources pour les scénarios avancés

Arrangement alphabétique des locales

Sévérité: MUST | Exigence: TFNFR32

Les expressions dans les blocs locals DOIVENT être arrangées alphabétiquement.

Types locaux précis

Sévérité: SHOULD | Exigence: TFNFR33

Utiliser des types précis (ex: number pour l'âge, pas string).


Exigences de configuration Terraform

Exigences de version Terraform

Sévérité: MUST | Exigence: TFNFR25

Exigences de terraform.tf:

  • DOIT contenir un seul bloc terraform
  • La première ligne DOIT définir required_version
  • DOIT inclure la contrainte de version minimale
  • DOIT inclure la contrainte de version majeure maximale
  • DEVRAIT utiliser le format ~> #.# ou >= #.#.#, < #.#.#

Exemple:

terraform {
  required_version = "~> 1.6"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0"
    }
  }
}

Fournisseurs dans required_providers

Sévérité: MUST | Exigence: TFNFR26

  • Le bloc terraform DOIT contenir le bloc required_providers
  • Chaque fournisseur DOIT spécifier source et version
  • Les fournisseurs DEVRAIENT être triés alphabétiquement
  • Inclure uniquement les fournisseurs directement requis
  • source DOIT être au format namespace/name
  • version DOIT inclure les contraintes de version majeure minimale et maximale
  • DEVRAIT utiliser le format ~> #.# ou >= #.#.#, < #.#.#

Exigences de test

Outils de test

Sévérité: MUST | Exigence: TFNFR5

Outils de test requis pour AVM:

  • Terraform (terraform validate/fmt/test)
  • terrafmt
  • Checkov
  • tflint (avec ruleset azurerm)
  • Go (optionnel pour les tests personnalisés)

Configuration du fournisseur de test

Sévérité: SHOULD | Exigence: TFNFR36

Pour des tests robustes, prevent_deletion_if_contains_resources DEVRAIT être explicitement défini à false dans les configurations de fournisseur de test.


Exigences de documentation

Génération de documentation de module

Sévérité: MUST | Exigence: TFNFR2

  • La documentation DOIT être générée automatiquement via Terraform Docs
  • Un fichier .terraform-docs.yml DOIT être présent à la racine du module

Changements cassants et gestion des fonctionnalités

Utilisation des basculements de fonctionnalités

Sévérité: MUST | Exigence: TFNFR34

Les nouvelles ressources ajoutées dans les versions mineures/correctifs DOIVENT avoir une variable de basculement pour éviter la création par défaut:

variable "create_route_table" {
  type     = bool
  default  = false
  nullable = false
}

resource "azurerm_route_table" "this" {
  count = var.create_route_table ? 1 : 0
  # ...
}

Examen des changements cassants potentiels

Sévérité: MUST | Exigence: TFNFR35

Changements cassants nécessitant de la prudence:

Blocs de ressources:

  1. Ajouter une nouvelle ressource sans création conditionnelle
  2. Ajouter des arguments avec des valeurs non-défaut
  3. Ajouter des blocs imbriqués sans dynamic
  4. Renommer des ressources sans blocs moved
  5. Changer count en for_each ou vice versa

Blocs variable/output:

  1. Supprimer/renommer des variables
  2. Changer le type de la variable
  3. Changer les valeurs default de la variable
  4. Changer nullable en false
  5. Changer sensitive de false à true
  6. Ajouter des variables sans default
  7. Supprimer des outputs
  8. Changer l'value de l'output
  9. Changer la valeur sensitive de l'output

Normes de contribution

Protection des branches du repository GitHub

Sévérité: MUST | Exigence: TFNFR3

Les propriétaires de modules DOIVENT définir des politiques de protection de branche sur la branche par défaut (généralement main):

  1. Exiger une Pull Request avant fusion
  2. Exiger l'approbation du push examinable le plus récent
  3. Rejeter les approbations de PR obsolètes lors du push de nouveaux commits
  4. Exiger un historique linéaire
  5. Prévenir les forçages
  6. Ne pas autoriser les suppressions
  7. Exiger l'examen de CODEOWNERS
  8. Pas de contournement des paramètres autorisé
  9. Appliquer pour les administrateurs

Liste de contrôle de conformité

Utiliser cette liste de contrôle lors du développement ou de l'examen des Azure Verified Modules:

Structure du module

  • [ ] Les références croisées de modules utilisent des sources registry avec des versions épinglées
  • [ ] Les versions des fournisseurs Azure (azurerm/azapi) répondent aux exigences AVM
  • [ ] .terraform-docs.yml présent à la racine du module
  • [ ] Fichier CODEOWNERS présent

Style de code

  • [ ] Tous les noms utilisent la casse snake_case minuscule
  • [ ] Les ressources sont ordonnées avec les dépendances en premier
  • [ ] for_each utilise map() ou set() avec des clés statiques
  • [ ] Les blocs ressource/données/module suivent le tri interne approprié
  • [ ] ignore_changes non cité
  • [ ] Blocs dynamiques utilisés pour les objets imbriqués conditionnels
  • [ ] coalesce() ou try() utilisés pour les valeurs par défaut

Variables

  • [ ] Aucune variable enabled ou module_depends_on
  • [ ] Les variables sont ordonnées: requises (alphabétique) puis optionnelles (alphabétique)
  • [ ] Toutes les variables ont des types précis (éviter any)
  • [ ] Toutes les variables ont des descriptions
  • [ ] Les collections ont nullable = false
  • [ ] Aucune déclaration sensitive = false
  • [ ] Aucune valeur par défaut pour les entrées sensibles
  • [ ] Les variables dépréciées déplacées vers deprecated_variables.tf

Outputs

  • [ ] Les outputs utilisent le motif de couche anti-corruption (attributs discrets)
  • [ ] Les outputs sensibles marqués sensitive = true
  • [ ] Les outputs dépréciés déplacés vers deprecated_outputs.tf

Configuration Terraform

  • [ ] terraform.tf a des contraintes de version (format ~>)
  • [ ] Le bloc required_providers présent avec tous les fournisseurs
  • [ ] Aucune déclaration provider dans le module (sauf alias)
  • [ ] Les locales arrangées alphabétiquement

Test et qualité

  • [ ] Les outils de test requis configurés
  • [ ] Les nouvelles ressources ont des basculements de fonctionnalités
  • [ ] Les changements cassants examinés et documentés

Statistiques récapitulatives

  • Exigences fonctionnelles: 3
  • Exigences non-fonctionnelles: 34
  • Exigences totales: 37

Par sévérité

  • MUST: 21 exigences
  • SHOULD: 14 exigences
  • MAY: 2 exigences

Basé sur: Azure Verified Modules - Exigences Terraform

Skills similaires