Templates ARM Azure : structure, paramètres et déploiement
À la fin de ce tutoriel, vous saurez lire l’anatomie d’un template ARM JSON, écrire des paramètres et un fichier de paramètres (sans secrets), déployer un lab Storage LRS cheap en
canadacentralavecaz deployment group what-if/create, brancher la tâche AzureResourceManagerTemplateDeployment@3, comparer Bicep et situer ARM/Bicep face à Terraform.Niveau : Débutant → intermédiaire · Temps estimé : 60–80 min · Versions testées : Azure CLI 2.60+, API Storage 2023-05-01, région
canadacentral· Dernière vérification : 2026-09-11Slug proposé :
azure-arm-templates· Série : Azure / DevOps (IaC) · Région lab Azure :canadacentral
Prérequis
- Azure Monitor & Application Insights (contexte série)
- Abonnement lab + Contributor (créer
rg-ado-lab-arm) - Azure CLI (
az version) ; extension VS Code ARM Tools recommandée - MFA ; lab cheap ; suppression en fin de session
- Bases JSON
Coût estimé : Storage Standard_LRS = quelques centimes. Pas d’App Service Plan payant (B1+ à l’heure) — note optionnelle plus bas. Pas de VM ni Premium GRS.
Ce que nous allons construire
Repo ado-lab
└── infra/
├── main.json (ARM — Storage LRS lab)
├── main.parameters.json (non-secrets uniquement)
└── main.bicep (équivalent pédagogique)
Azure (canadacentral)
└── rg-ado-lab-arm
└── stadolabXXXXX (préfixe court + uniqueString)
CLI / Pipeline
└── az deployment group what-if|create
└── AzureResourceManagerTemplateDeployment@3
(Schéma à remplacer par une image locale Excalidraw / draw.io, alt : « Déploiement ARM : resource group canadacentral, Storage Account lab low-cost, what-if puis create ».)
Fond EN conservé et enrichi : lab renommé, coût maîtrisé, quiz, erreurs, scénario terrain.
Étape 1 — Comment fonctionne un déploiement ARM
Toute ressource Azure passe par Azure Resource Manager. Un template ARM est un JSON déclaratif : ARM valide, calcule les dépendances et crée / met à jour (souvent en parallèle).
- Scopes : resource group (lab), subscription, management group, tenant.
- Idempotence : redéployer un template inchangé ne change rien.
- Modes : Incremental (défaut) ; Complete (supprime l’absent du template — prudence).
- what-if ≈
terraform plan: diff Create / Modify / Delete / NoChange avant d’agir.
Étape 2 — Anatomie d’un template ARM JSON
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": { },
"variables": { },
"functions": [ ],
"resources": [ ],
"outputs": { }
}
| Section | Rôle |
|---|---|
parameters |
Valeurs fournies au déploiement (région, SKU, préfixe). Types, defaultValue, allowedValues, minLength, secureString |
variables |
Valeurs dérivées (noms uniques, tags) pour éviter la répétition |
functions |
Fonctions utilisateur (rares en lab) |
resources |
Ressources : type, apiVersion, name, location, properties, dependsOn optionnel |
outputs |
Valeurs renvoyées (noms, IDs) — jamais de clés / connection strings dans un tutoriel public |
Les expressions vivent entre crochets : [parameters('x')], [uniqueString(resourceGroup().id)], [resourceId(...)].
Étape 3 — Resource group canadacentral et lab Storage cheap
az group create -n rg-ado-lab-arm -l canadacentral
Noms Storage : globalement uniques, 3–24 caractères, minuscules et chiffres. Stratégie lab : préfixe court + uniqueString(resourceGroup().id).
Fichier infra/main.json — Storage LRS, HTTPS only, TLS 1.2, pas d’accès blob public, pas de secret en output :
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": { "description": "Région lab — canadacentral via le RG" }
},
"storagePrefix": {
"type": "string",
"defaultValue": "stadolab",
"minLength": 3,
"maxLength": 11,
"metadata": { "description": "Préfixe court pour nom de storage" }
},
"storageSku": {
"type": "string",
"defaultValue": "Standard_LRS",
"allowedValues": [ "Standard_LRS", "Standard_GRS" ],
"metadata": { "description": "SKU Storage — LRS = cheap lab" }
}
},
"variables": {
"storageName": "[toLower(concat(parameters('storagePrefix'), uniqueString(resourceGroup().id)))]",
"tags": {
"environment": "lab",
"managedBy": "arm-template",
"series": "ado-lab-arm"
}
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-05-01",
"name": "[variables('storageName')]",
"location": "[parameters('location')]",
"tags": "[variables('tags')]",
"sku": { "name": "[parameters('storageSku')]" },
"kind": "StorageV2",
"properties": {
"minimumTlsVersion": "TLS1_2",
"allowBlobPublicAccess": false,
"supportsHttpsTrafficOnly": true
}
}
],
"outputs": {
"storageAccountName": {
"type": "string",
"value": "[variables('storageName')]"
},
"storageAccountId": {
"type": "string",
"value": "[resourceId('Microsoft.Storage/storageAccounts', variables('storageName'))]"
}
}
}
Points pédagogiques : (1) location hérite du RG → canadacentral ; (2) uniqueString stabilise le suffixe tant que le RG existe ; (3) zéro listKeys / SAS en output.
Expressions ARM utiles en lab
Les crochets [...] évaluent des fonctions au déploiement : parameters, variables, resourceGroup().location / .id, uniqueString, concat / format / toLower, resourceId, reference. dependsOn ordonne quand il n’y a pas de référence explicite ; avec resourceId (ou Bicep), la dépendance est souvent implicite.
Évitez les dépendances circulaires : ARM refuse le template. Séparez les ressources ou déployez en deux passes plutôt que A↔B.
Note optionnelle — App Service (coûteux)
L’exemple EN ajoute parfois App Service Plan + Web App (dependsOn, identité managée). Un plan B1+ facture dès la création : restez sur Storage LRS ici. Essai Free/F1 seulement si budgété, puis az group delete. Jamais de connection string en appSettings commités.
Pour illustrer dependsOn / identité managée avec Plan + Web App : Free/F1 seulement si disponible, validez, puis az group delete. Ne laissez jamais un B1/P1v3 oublié. Au quotidien, restez sur le lab Storage.
Étape 4 — Parameter files et références Key Vault (sans secrets)
infra/main.parameters.json — non-secrets uniquement :
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storagePrefix": { "value": "stadolab" },
"storageSku": { "value": "Standard_LRS" }
}
}
Attention : le $schema doit être deploymentParameters, pas celui du template.
Pour un secret (ex. MDP DB), référence Key Vault — ARM résout au déploiement, sans valeur en clair :
"dbPassword": {
"reference": {
"keyVault": {
"id": "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault>"
},
"secretName": "db-password"
}
}
L’identité de déploiement (SP / OIDC) doit pouvoir lire le vault. Jamais de secret en clair dans Git.
Paramètres sécurisés vs fichiers
Un paramètre secureString masque la valeur dans les logs du portail et de certains outils, mais ne remplace pas Key Vault : la valeur peut encore circuler dans le pipeline ou l’historique local. Bonne pratique 2026 : paramètres non-secrets dans main.parameters.json versionné ; secrets via référence Key Vault ou injection runtime OIDC ; outputs limités aux IDs / noms publics. Si une clé a fuité une seule fois, regénérez-la sur le Storage Account (az storage account keys renew) avant de continuer.
Étape 5 — Déployer avec Azure CLI (what-if puis create)
cd ~/labs/ado-lab
git checkout -b feature/arm-templates-lab
mkdir -p infra
# placez main.json et main.parameters.json
az deployment group what-if
--resource-group rg-ado-lab-arm
--template-file infra/main.json
--parameters @infra/main.parameters.json
az deployment group create
--name deploy-arm-lab-001
--resource-group rg-ado-lab-arm
--template-file infra/main.json
--parameters @infra/main.parameters.json
az deployment group show
-g rg-ado-lab-arm -n deploy-arm-lab-001
--query properties.outputs -o json
az storage account list -g rg-ado-lab-arm -o table
what-if est la commande la plus utile : lisez le diff avant le create. Mode Incremental recommandé en lab.
Relire un déploiement
Après un create réussi :
az deployment group list -g rg-ado-lab-arm -o table
az deployment operation group list -g rg-ado-lab-arm -n deploy-arm-lab-001 -o table
En cas d’échec (nom Storage, apiVersion, RBAC), lisez properties.statusMessage, corrigez, puis redéployez (nouveau --name ou Incremental). L’idempotence ARM évite de tout recréer.
Étape 6 — Pipeline Azure Pipelines (AzureResourceManagerTemplateDeployment@3)
Stage infra avant l’app (release pipeline) ; Environments / approvals s’appliquent.
steps:
- task: AzureResourceManagerTemplateDeployment@3
displayName: Deploy ARM infra lab
inputs:
deploymentScope: Resource Group
azureResourceManagerConnection: sc-ado-lab-oidc
subscriptionId: $(subscriptionId)
action: Create Or Update Resource Group
resourceGroupName: rg-ado-lab-arm
location: canadacentral
templateLocation: Linked artifact
csmFile: infra/main.json
csmParametersFile: infra/main.parameters.json
deploymentMode: Incremental
deploymentOutputs: armOutputs
- script: |
echo "$(armOutputs)" | jq -r '.storageAccountName.value'
displayName: Afficher nom Storage (pas de clés)
Ne loggez jamais de clés ; identité managée / Key Vault pour les secrets app.
Étape 7 — Équivalent Bicep (même lab, moins de bruit)
Microsoft recommande Bicep pour le neuf ; ARM JSON reste supporté (az bicep decompile / build).
// infra/main.bicep — équivalent lab ARM Storage
@description('Région lab P4 / IaC')
param location string = resourceGroup().location
@minLength(3)
@maxLength(11)
param storagePrefix string = 'stadolab'
@allowed(['Standard_LRS', 'Standard_GRS'])
param storageSku string = 'Standard_LRS'
var storageName = toLower('${storagePrefix}${uniqueString(resourceGroup().id)}')
resource stg 'Microsoft.Storage/storageAccounts@2023-05-01' = {
name: storageName
location: location
sku: { name: storageSku }
kind: 'StorageV2'
properties: {
allowBlobPublicAccess: false
minimumTlsVersion: 'TLS1_2'
supportsHttpsTrafficOnly: true
}
}
output storageAccountName string = stg.name
output storageAccountId string = stg.id
az deployment group what-if -g rg-ado-lab-arm --template-file infra/main.bicep
az deployment group create -g rg-ado-lab-arm --template-file infra/main.bicep
# optionnel pédagogique : az bicep build -f infra/main.bicep
Voir aussi le chapitre dédié Bicep : premier template.
Étape 8 — ARM / Bicep vs Terraform (2026)
| Critère | ARM / Bicep | Terraform (azurerm) |
|---|---|---|
| Cloud | Azure-only | Multi-cloud |
| État | Azure = source de vérité | State file (backend) |
| Nouveautés Azure | Jour 1 (ARM) | Provider / délai possible |
| Syntaxe | JSON ou DSL Bicep | HCL |
| Lab Azure-centré | Oui (cette série) | Valide si stack multi-cloud |
Les deux sont valides en 2026 : Azure-first → Bicep souvent ; multi-cloud / Azure DevOps tools → Terraform. Ce chapitre ancre ARM (plan de contrôle Azure).
Lab bonus (8–10 min) — checklist
- Second
what-ifaprèscreate(Incremental) — peu/pas de Create. - Région :
az group show -n rg-ado-lab-arm --query location -o tsv→canadacentral. - Outputs : nom + id seulement.
- Commit
main.json/main.parameters.json/main.bicepsans secrets. -
Anti-dérive :
Standard_LRS; HTTPS/TLS 1.2 ; blob public off ; params = non-secrets. -
Relancez
what-ifaprès une modification mineure de tag : vous devez voir un Modify sur le Storage, pas un Delete. - Vérifiez qu’aucun output ne contient
listKeys,connectionStringou SAS. - (Option)
az bicep decompile -f infra/main.jsonpour comparer le JSON au DSL — puis gardez Bicep comme source de vérité pour le neuf.
Checklist anti-dérive IaC (mémo)
- Source de vérité = template versionné (JSON aujourd’hui, Bicep demain) — pas le portail « cliqué ».
- SKU cheap
Standard_LRS; HTTPS only ; TLS 1.2 ; blob public désactivé. - Paramètres commités = non-secrets ; RG
rg-ado-lab-armencanadacentral. - Pipeline : Incremental + what-if (ou revue) avant apply ; service connection OIDC, pas de secret client long-lived si possible.
- Fin de session :
az group delete -n rg-ado-lab-arm -y --no-waitsi plus besoin.
Scénario terrain
« Un collègue a mis P1v3 et une connection string Storage en clair dans le template “pour aller vite”. » → Storage LRS (ou Free/F1 très court), retirez les secrets, Key Vault + identité managée, régénérez les clés si fuite, imposez what-if + revue PR avant create. Coût et fuite s’arrêtent avant la prod.
Variante : mode Complete « pour nettoyer » un RG partagé peut supprimer des ressources hors template. Réservez Complete derrière environment + approvers, et lisez le what-if (Delete) avant d’appliquer.
Points clés à retenir
- ARM est le plan de contrôle Azure : templates JSON déclaratifs, idempotents, déployés sur un scope.
parameters+variables+uniqueStringrendent un template réutilisable sans collision de noms.what-ifpuiscreate(Incremental) = boucle sûre en lab et en pipeline.- Secrets : Key Vault references / OIDC — jamais en clair dans Git ni en outputs publics.
- Pour le neuf, écrivez en Bicep ; comprendre ARM reste indispensable pour lire les erreurs, les tâches pipeline et le legacy.
Erreurs fréquentes
| Symptôme | Cause | Correction |
|---|---|---|
| StorageAccountAlreadyTaken / name invalid | Nom global / règles | Préfixe court + uniqueString |
| Location mismatch | RG hors canadacentral |
Recréez rg-ado-lab-arm en Canada Central |
| NoRegisteredProviderFound / apiVersion | Version API invalide | Doc resource reference ; API Storage 2023-05-01 |
| Circular dependency | Deux ressources se référencent | Casser la boucle / enfant / 2e déploiement |
| Parameter file rejected | Mauvais $schema |
Schema deploymentParameters |
| AuthorizationFailed | RBAC | Contributor sur le RG ; OIDC service connection |
| Secrets dans parameters.json | Copier-coller MDP | Key Vault reference ; rotate si fuite |
| Mode Complete surprenant | Ressources hors template supprimées | Preférez Incremental en lab |
| Facture App Service inattendue | Plan B1/P1v3 oublié | Lab Storage only ; az group delete |
uniqueString change après recreate RG |
Nouvel resourceGroup().id |
Attendu ; gardez le même RG ou mettez à jour les refs |
Quiz (5 questions)
1. Quel est le rôle principal d’un template ARM ?
– A. Décrire déclarativement des ressources Azure en JSON (ou via Bicep → ARM)
– B. Remplacer Azure AD et MFA
– C. Stocker les PAT Azure DevOps dans le state
2. Quelle pratique est correcte pour ce lab Storage ?
– A. SKU Standard_LRS, HTTPS/TLS 1.2, outputs sans clés
– B. Output listKeys dans le README public
– C. Déployer un plan P1v3 sans budget
3. À quoi sert az deployment group what-if ?
– A. Prévisualiser Create/Modify/Delete avant d’appliquer
– B. Supprimer automatiquement le Key Vault
– C. Compiler Terraform vers ARM
4. Comment injecter un secret sans le mettre en clair dans le parameter file ?
– A. Référence Key Vault (reference.keyVault + secretName)
– B. Hardcoder le MDP dans variables
– C. Le coller dans un commentaire JSON
5. Pourquoi la série recommande-t-elle Bicep pour les nouveaux templates tout en enseignant ARM ?
– A. Bicep compile vers ARM, plus lisible ; comprendre ARM reste utile (pipeline, debug, legacy)
– B. ARM JSON est interdit depuis 2025
– C. Terraform ne fonctionne plus sur Azure
Réponses : 1‑A · 2‑A · 3‑A · 4‑A · 5‑A
Pour aller plus loin
- Doc : ARM templates, functions, what-if, Bicep
- Suite : Bicep premier template, pipeline Bicep, App Service deploy
Nettoyage
az group delete -n rg-ado-lab-arm -y --no-wait
Gardez infra/ dans Git pour les labs pipeline même si le RG est détruit.
Maillage série
| ← Précédent | Azure Monitor / monitoring |
| → Suivant | Jenkins avec Azure Repos |
| Aussi | Release pipeline · Azure DevOps tools · Bicep : premier template |
Retour parcours Azure DevOps — hub de la série et leçons sœurs.



