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
- 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 cassants et gestion des fonctionnalités
- Normes de contribution
- Liste de contrôle de conformité
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"avecversion = "1.2.3"
- Exemple:
- Les modules NE DOIVENT PAS utiliser de références git (ex:
git::https://xxx.yyy/xxx.gitougithub.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_providerspour 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
countpour la création conditionnelle de ressources - DOIVENT utiliser
map(xxx)ouset(xxx)comme collectionfor_eachde 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:
-
Métarguments (en haut):
providercountfor_each
-
Arguments/blocs (au milieu, alphabétique):
- Arguments requis
- Arguments optionnels
- Blocs imbriqués requis
- Blocs imbriqués optionnels
-
Métarguments (en bas):
depends_onlifecycle(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:
-
Métarguments en haut:
sourceversioncountfor_each
-
Arguments (alphabétique):
- Arguments requis
- Arguments optionnels
-
Métarguments en bas:
depends_onproviders
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
providerNE DOIT PAS être déclaré dans les modules (sauf pour lesconfiguration_aliases)- Les blocs
providerdans les modules DOIVENT utiliser uniquementalias - 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:
- Tous les champs requis (alphabétique)
- Tous les champs optionnels (alphabétique)
Règles de nommage des variables
Sévérité: SHOULD | Exigence: TFNFR16
- Suivre les règles de nommage de HashiCorp
- Les commutateurs de fonctionnalité DEVRAIENT utiliser des déclarations positives:
xxx_enabledau lieu dexxx_disabled
Variables avec descriptions
Sévérité: SHOULD | Exigence: TFNFR17
descriptionDEVRAIT 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
typeDOIT être défini pour chaque variabletypeDEVRAIT être aussi précis que possibleanyPEUT être utilisé uniquement 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
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
DEPRECATEDau 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 = truepour 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.tfDEVRAIT contenir uniquement des blocslocals- 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
terraformDOIT contenir le blocrequired_providers - Chaque fournisseur DOIT spécifier
sourceetversion - Les fournisseurs DEVRAIENT être triés alphabétiquement
- Inclure uniquement les fournisseurs directement requis
sourceDOIT être au formatnamespace/nameversionDOIT 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.ymlDOIT ê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:
- 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 la variable - Changer les valeurs
defaultde la variable - Changer
nullableen false - Changer
sensitivede false à true - Ajouter des variables sans
default - Supprimer des outputs
- Changer l'
valuede l'output - Changer la valeur
sensitivede 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):
- Exiger une Pull Request avant fusion
- Exiger l'approbation du push examinable le plus récent
- Rejeter les approbations de PR obsolètes lors du push de nouveaux commits
- Exiger un historique linéaire
- Prévenir les forçages
- Ne pas autoriser les suppressions
- Exiger l'examen de CODEOWNERS
- Pas de contournement des paramètres autorisé
- 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.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 le tri interne approprié
- [ ]
ignore_changesnon cité - [ ] Blocs dynamiques utilisés pour les objets imbriqués conditionnels
- [ ]
coalesce()outry()utilisés pour les valeurs par défaut
Variables
- [ ] Aucune variable
enabledoumodule_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.tfa des contraintes de version (format~>) - [ ] Le bloc
required_providersprésent avec tous les fournisseurs - [ ] Aucune déclaration
providerdans 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