Exigences pour 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é sur les modules Terraform Azure.
Références:
Table des matières
- Références croisées de modules
- Exigences du fournisseur Azure
- Normes de style de code
- Exigences des variables
- Exigences des outputs
- Normes des valeurs locales
- Exigences de configuration Terraform
- Exigences de test
- Exigences de documentation
- Changements non rétrocompatibles et gestion des fonctionnalités
- Normes de contribution
- Liste de conformité
Références croisées de modules
Gravité: MUST | Exigence: TFFR1
Lors de la création de modules Resource ou Pattern, les propriétaires de modules MAY faire des références croisées à d'autres modules. Cependant:
- Les modules MUST être référencés en utilisant la référence du registre HashiCorp Terraform avec une version épinglée
- Exemple:
source = "Azure/xxx/azurerm"avecversion = "1.2.3"
- Exemple:
- Les modules MUST NOT utiliser de références git (par ex.,
git::https://xxx.yyy/xxx.gitougithub.com/xxx/yyy) - Les modules MUST NOT contenir de références à des modules non-AVM
Exigences du fournisseur Azure
Gravité: MUST | Exigence: TFFR3
Les auteurs MUST utiliser uniquement les fournisseurs Azure suivants:
| Fournisseur | Version min | Version max |
|---|---|---|
| azapi | >= 2.0 | < 3.0 |
| azurerm | >= 4.0 | < 5.0 |
Exigences:
- Les auteurs MAY choisir soit Azurerm, Azapi, ou les deux fournisseurs
- MUST utiliser le bloc
required_providerspour appliquer les versions des fournisseurs - SHOULD 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
Gravité: MUST | Exigence: TFNFR4
MUST utiliser la casse snake_case minuscule pour:
- Locals
- Variables
- Outputs
- Ressources (noms symboliques)
- Modules (noms symboliques)
Exemple: snake_casing_example
Ordre des ressources et sources de données
Gravité: SHOULD | Exigence: TFNFR6
- Les ressources dépendantes SHOULD venir en premier
- Les ressources avec dépendances SHOULD être définies à proximité les unes des autres
Utilisation de count et for_each
Gravité: MUST | Exigence: TFNFR7
- Utiliser
countpour la création conditionnelle de ressources - MUST utiliser
map(xxx)ouset(xxx)comme collectionfor_eachde la ressource - La clé de la map ou l'élément du set MUST ê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"]
}
Ordre interne des blocs ressource et données
Gravité: SHOULD | Exigence: TFNFR8
Ordre au sein des blocs ressource/données:
-
Méta-arguments (haut):
providercountfor_each
-
Arguments/blocs (milieu, alphabétique):
- Arguments requis
- Arguments optionnels
- Blocs imbriqués requis
- Blocs imbriqués optionnels
-
Méta-arguments (bas):
depends_onlifecycle(avec sous-ordre:create_before_destroy,ignore_changes,prevent_destroy)
Séparez les sections par des lignes vides.
Ordre des blocs de modules
Gravité: SHOULD | Exigence: TFNFR9
Ordre au sein des blocs de modules:
-
Méta-arguments du haut:
sourceversioncountfor_each
-
Arguments (alphabétique):
- Arguments requis
- Arguments optionnels
-
Méta-arguments du bas:
depends_onproviders
Syntaxe de lifecycle ignore_changes
Gravité: MUST | Exigence: TFNFR10
L'attribut ignore_changes MUST NOT être entre guillemets doubles.
Bon:
lifecycle {
ignore_changes = [tags]
}
Mauvais:
lifecycle {
ignore_changes = ["tags"]
}
Comparaison Null pour création conditionnelle
Gravité: SHOULD | Exigence: TFNFR11
Pour les paramètres nécessitant une création conditionnelle de ressources, encapsuler avec le type object pour éviter les problèmes "known after apply" pendant la phase de plan.
Recommandé:
variable "security_group" {
type = object({
id = string
})
default = null
}
Blocs dynamiques pour objets imbriqués optionnels
Gravité: MUST | Exigence: TFNFR12
Les blocs imbriqués sous conditions MUST utiliser ce motif:
dynamic "identity" {
for_each = <condition> ? [<some_item>] : []
content {
# contenu du bloc
}
}
Valeurs par défaut avec coalesce/try
Gravité: 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
Gravité: MUST | Exigence: TFNFR27
providerMUST NOT être déclaré dans les modules (sauf pourconfiguration_aliases)- Les blocs
providerdans les modules MUST utiliser uniquementalias - Les configurations de fournisseur SHOULD être transmises par les utilisateurs du module
Exigences des variables
Variables non autorisées
Gravité: MUST | Exigence: TFNFR14
Les propriétaires de modules MUST NOT ajouter des variables comme enabled ou module_depends_on pour contrôler l'opération du module entier. Les basculements de fonctionnalités booléennes pour des ressources spécifiques sont acceptables.
Ordre de définition des variables
Gravité: SHOULD | Exigence: TFNFR15
Les variables SHOULD suivre cet ordre:
- Tous les champs requis (alphabétique)
- Tous les champs optionnels (alphabétique)
Règles de nommage des variables
Gravité: SHOULD | Exigence: TFNFR16
- Suivre les règles de nommage de HashiCorp
- Les commutateurs de fonctionnalités SHOULD utiliser des déclarations positives:
xxx_enabledau lieu dexxx_disabled
Variables avec descriptions
Gravité: SHOULD | Exigence: TFNFR17
descriptionSHOULD décrire avec précision l'objectif du paramètre et le type de données attendu- Le public cible est les utilisateurs du module, pas les développeurs
- Pour les types
object, utiliser le format HEREDOC
Variables avec types
Gravité: MUST | Exigence: TFNFR18
typeMUST être défini pour chaque variabletypeSHOULD être aussi précis que possibleanyMAY être utilisé seulement avec des raisons adéquates- Utiliser
boolau lieu destring/numberpour les valeurs vrai/faux - Utiliser un
objectconcret au lieu demap(any)
Variables de données sensibles
Gravité: SHOULD | Exigence: TFNFR19
Si le type d'une variable est object et contient des champs sensibles, la variable entière SHOULD être sensitive = true, ou extraire les champs sensibles dans des variables séparées.
Valeurs par défaut non nullables pour les collections
Gravité: SHOULD | Exigence: TFNFR20
Nullable SHOULD être défini à false pour les valeurs de collection (sets, maps, lists) lors de leur utilisation dans les boucles. Pour les valeurs scalaires, null peut avoir une signification sémantique.
Décourager la nullabilité par défaut
Gravité: MUST | Exigence: TFNFR21
nullable = true MUST être évité sauf s'il y a un besoin sémantique spécifique de valeurs null.
Éviter sensitive = false
Gravité: MUST | Exigence: TFNFR22
sensitive = false MUST être évité (c'est la valeur par défaut).
Conditions de valeur par défaut sensible
Gravité: MUST | Exigence: TFNFR23
Une valeur par défaut MUST NOT être définie pour les entrées sensibles (par ex., les mots de passe par défaut).
Gestion des variables obsolètes
Gravité: MUST | Exigence: TFNFR24
- Déplacer les variables obsolètes vers
deprecated_variables.tf - Annoter avec
DEPRECATEDau début de la description - Déclarer le nom du remplacement
- Nettoyer lors des versions majeures
Exigences des outputs
Outputs Terraform supplémentaires
Gravité: SHOULD | Exigence: TFFR2
Les auteurs SHOULD NOT produire des objets ressources entiers car ceux-ci peuvent contenir des données sensibles et le schéma peut changer avec les versions API ou du fournisseur.
Meilleures pratiques:
- Produire les attributs calculés des ressources en tant qu'outputs discrets (motif de couche anti-corruption)
- SHOULD NOT produire les valeurs qui sont déjà des entrées (sauf
name) - Utiliser
sensitive = truepour les attributs sensibles - Pour les ressources déployées avec
for_each, produire les attributs calculés dans une structure map
Exemples:
# Attribut calculé d'une 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
Gravité: MUST | Exigence: TFNFR29
Les outputs contenant des données confidentielles MUST être déclarées avec sensitive = true.
Gestion des outputs obsolètes
Gravité: MUST | Exigence: TFNFR30
- Déplacer les outputs obsolètes 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
Gravité: MAY | Exigence: TFNFR31
locals.tfSHOULD contenir uniquement les blocslocals- MAY déclarer les blocs
localsà côté des ressources pour les scénarios avancés
Arrangement alphabétique des locals
Gravité: MUST | Exigence: TFNFR32
Les expressions dans les blocs locals MUST être arrangées alphabétiquement.
Types locaux précis
Gravité: SHOULD | Exigence: TFNFR33
Utiliser des types précis (par ex., number pour l'âge, pas string).
Exigences de configuration Terraform
Exigences de version Terraform
Gravité: MUST | Exigence: TFNFR25
Exigences de terraform.tf:
- MUST contenir un seul bloc
terraform - La première ligne MUST définir
required_version - MUST inclure une contrainte de version minimale
- MUST inclure une contrainte de version majeure maximale
- SHOULD utiliser le format
~> #.#ou>= #.#.#, < #.#.#
Exemple:
terraform {
required_version = "~> 1.6"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
Fournisseurs dans required_providers
Gravité: MUST | Exigence: TFNFR26
- Le bloc
terraformMUST contenir le blocrequired_providers - Chaque fournisseur MUST spécifier
sourceetversion - Les fournisseurs SHOULD être triés alphabétiquement
- Inclure uniquement les fournisseurs directement requis
sourceMUST être au formatnamespace/nameversionMUST inclure des contraintes de version majeure minimale et maximale- SHOULD utiliser le format
~> #.#ou>= #.#.#, < #.#.#
Exigences de test
Outils de test
Gravité: 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
Gravité: SHOULD | Exigence: TFNFR36
Pour des tests robustes, prevent_deletion_if_contains_resources SHOULD être explicitement défini à false dans les configurations de fournisseur de test.
Exigences de documentation
Génération de documentation de module
Gravité: MUST | Exigence: TFNFR2
- La documentation MUST être générée automatiquement via Terraform Docs
- Un fichier
.terraform-docs.ymlMUST être présent à la racine du module
Changements non rétrocompatibles et gestion des fonctionnalités
Utilisation de basculements de fonctionnalités
Gravité: MUST | Exigence: TFNFR34
Les nouvelles ressources ajoutées dans les versions mineures/patch MUST 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 non rétrocompatibles potentiels
Gravité: MUST | Exigence: TFNFR35
Changements non rétrocompatibles nécessitant une prudence:
Blocs de ressources:
- Ajouter une nouvelle ressource sans création conditionnelle
- Ajouter des arguments avec des valeurs non-défaut
- Ajouter des blocs imbriqués sans
dynamic - Renommer des ressources sans blocs
moved - Changer
countenfor_eachou vice-versa
Blocs variable/output:
- Supprimer/renommer des variables
- Changer le
typede variable - Changer les valeurs
defaultde variable - Changer
nullableà false - Changer
sensitivede false à true - Ajouter des variables sans
default - Supprimer des outputs
- Changer la
valueoutput - Changer la valeur
sensitiveoutput
Normes de contribution
Protection des branches du référentiel GitHub
Gravité: MUST | Exigence: TFNFR3
Les propriétaires de modules MUST définir des politiques de protection de branche sur la branche par défaut (généralement main):
- Exiger une Pull Request avant la fusion
- Exiger l'approbation du push examinable le plus récent
- Rejeter les approbations PR obsolètes lorsque de nouveaux commits sont poussés
- Exiger un historique linéaire
- Empêcher les forces push
- Ne pas autoriser les suppressions
- Exiger l'examen CODEOWNERS
- Aucun contournement des paramètres autorisé
- Appliquer pour les administrateurs
Liste de conformité
Utilisez 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 du module utilisent les sources de registre avec des versions épinglées
- [ ] Les versions des fournisseurs Azure (azurerm/azapi) respectent les exigences AVM
- [ ]
.terraform-docs.ymlpré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_eachutilisemap()ouset()avec des clés statiques - [ ] Les blocs ressource/données/module suivent l'ordre interne approprié
- [ ]
ignore_changesn'est pas entre guillemets - [ ] Les blocs dynamiques sont utilisés pour les objets imbriqués conditionnels
- [ ]
coalesce()outry()est utilisé pour les valeurs par défaut
Variables
- [ ] Aucune variable
enabledoumodule_depends_on - [ ] Variables ordonnées: requis (alphabétique) puis optionnel (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 obsolètes sont déplacées vers
deprecated_variables.tf
Outputs
- [ ] Les outputs utilisent le motif de couche anti-corruption (attributs discrets)
- [ ] Les outputs sensibles sont marqués
sensitive = true - [ ] Les outputs obsolètes sont déplacés vers
deprecated_outputs.tf
Configuration Terraform
- [ ]
terraform.tfa des contraintes de version (format~>) - [ ] Le bloc
required_providersest présent avec tous les fournisseurs - [ ] Aucune déclaration
providerdans le module (sauf les alias) - [ ] Les locals sont arrangés alphabétiquement
Test et qualité
- [ ] Les outils de test requis sont configurés
- [ ] Les nouvelles ressources ont des basculements de fonctionnalités
- [ ] Les changements non rétrocompatibles sont examinés et documentés
Statistiques récapitulatives
- Exigences fonctionnelles: 3
- Exigences non fonctionnelles: 34
- Exigences totales: 37
Par gravité
- MUST: 21 exigences
- SHOULD: 14 exigences
- MAY: 2 exigences
Basé sur: Azure Verified Modules - Terraform Requirements