Dans notre précédent article, nous avons vu comment Azure Machine Configuration permettait d’injecter dynamiquement des paramètres dans des configurations DSC génériques.
La mécanique fonctionne, mais personne n’utilise de template ARM pour gérer ses déploiements au quotidien!
Heureusement, la ressource azurerm_policy_virtual_machine_configuration_assignment du provider AzureRM de Terraform supporte également l’injection des paramètres via un ou plusieurs attributs « parameter » dans le bloc « configuration » :
resource "azurerm_policy_virtual_machine_configuration_assignment" "iis_config" {
name = "IIS_Standard"
location = azurerm_resource_group.rg.location
virtual_machine_id = azurerm_windows_virtual_machine.web_server.id
configuration {
assignment_type = "ApplyAndAutoCorrect"
version = "1.0"
content_uri = "https://mystorage.blob.core.windows.net/configs/IIS.zip"
content_hash = "A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0U1V2W3X4Y5Z6A7B8C9D0E1F2"
parameter {
name = "[WebSite]MyCompanySite;PhysicalPath"
value = "D:\\ProductionWeb\\MySite"
}
parameter {
name = "[WebSite]MyCompanySite;State"
value = "Started"
}
parameter {
name = "[WindowsFeature]AspNet45;Ensure"
value = "Present"
}
}
}
Le problème? Même si ce code est plus lisible qu’un template ARM en JSON, il souffre du même défaut structurel : la syntaxe name/value pour l’injection des paramètres est strictement la même, toujours aussi verbeuse et répétitive.
Le code n’en est pas moins fonctionnel, mais sa lisibilité devient douteuse lorsqu’il s’agit de configurer des déploiements complexes avec des dizaines de pramètres !
Mais contrairement à ARM, Terraform dispose d’une solution évidente pour améliorer la situation : les modules, qui permettent de simplifier la manipulation de structures complexes et répétitives. Mon objectif devient alors évident : créer un module qui permet la saisie de paramètres en suivant la syntaxe naturelle d’un script DSC, et laisser Terraform faire le travail de formatage complexe en coulisses.
Un exemple valant mieux qu’un long discours, voici la syntaxe souhaitée :
parameters = {
"[WebSite]MyCompanySite" = {
PhysicalPath = "D:\\ProductionWeb\\MySite"
State = "Started"
}
}
Voilà qui est bien plus simple, lisible et proche de la syntaxe DSC d’origine!
Pour que les informations contenues dans cette structure (techniquement un dictionnaire de dictionnaires, ou map(map(string)) soit compréhensible par la ressource azurerm_policy_virtual_machine_configuration_assignment, notre module doit la transformer. Terraform propose pour cela des outils de manipulation de données très puissants : les boucles for imbriquées et la fonction flatten.
Voici le cœur du moteur de notre module (dans le fichier main.tf) :
resource "azurerm_policy_virtual_machine_configuration_assignment" "this" {
for_each = local.guest_configurations_map
name = each.key
location = var.location
virtual_machine_id = var.virtual_machine_id
configuration {
assignment_type = var.assignment_type
version = var.configuration_version
content_uri = each.value.content_uri
content_hash = each.value.content_hash
dynamic "parameter" {
for_each = flatten([
for param_name, param_values in each.value.parameters : [
for key, value in param_values : {
name = "${param_name};${key}"
value = value
}
]
])
content {
name = parameter.value.name
value = parameter.value.value
}
}
}
}
Que se passe-t-il exactement ?
Terraform parcourt notre dictionnaire principal parameters (for param_name, param_values…).
Pour chaque ressource DSC trouvée (ex: [WebSite]MyCompanySite), il parcourt ses propriétés (for key, value…).
Il concatène dynamiquement la clé principale, un point-virgule et la sous-clé : name = « ${param_name};${key} ».
La fonction flatten « écrase » ces listes imbriquées pour en faire une liste simple à un seul niveau, qui va ensuite être parcourue de facon classique par le for_each du bloc dynamic et transformé en autant de blocs « parameter » que nécessaire.
Maintenant que notre module est prêt, son appel depuis notre code principal devient un jeu d’enfant. Non seulement nous pouvons gérer facilement les paramètres complexes, mais nous pouvons également assigner plusieurs packages DSC à la même machine virtuelle en une seule fois :
module "machine_configuration" {
source = "github.com/Cyr-Az/terraform-azurerm-cyraz-machine-configuration"
# version = "x.x.x"
virtual_machine_id = azurerm_windows_virtual_machine.example.id
location = azurerm_resource_group.example.location
guest_configurations =[
{
name = "IIS_Standard"
content_uri = "https://mystorage.blob.core.windows.net/configs/IIS_Standard.zip"
content_hash = "A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0U1V2W3X4Y5Z6A7B8C9D0E1F2"
parameters = {
"[WebSite]MyCompanySite" = {
PhysicalPath = "D:\\ProductionWeb\\MySite"
State = "Started"
}
"[WindowsFeature]AspNet45" = {
Ensure = "Present"
}
}
},
{
name = "Other_Config"
content_uri ="https://mystorage.blob.core.windows.net/configs/Other_Config.zip"
content_hash = "B2C3D4E5..."
parameters = {
"[SomeResource]Name" = {
Parameter = "Value"
}
}
}
]
}
Vous pouvez retrouver ce module complet avec un example plus détaillé sur mon Github : Cyr-Az/terraform-azurerm-cyraz-machine-configuration: Terraform module to simplify Machine Configuration assignments with custom parameters

