Déployer un WS Server 2019 sur vSphere 7 via Packer/Terraform
Création d’un template via Packer, et déploiement via Terraform !
EDIT: comme dit dans le précédent article, je comptais créé un lab où je rajouterais au fur et à mesure divers services et divers serveurs… le soucis étant que les builders vmware-iso et vsphere-iso ne sont disponibles que si l’on utilise un vCenter/VCSA, et mon PC actuel ne dispose que de 24Go de RAM autrement dit impossible de faire tourner les multiples VMs en même temps… Donc cet article sera « à part » même si dans les faits il est tout à fait reproductible dans un lab avec deux pfSense faisant office de routeur/pare-feux.
Sur ce, faisons comme si de rien n’était !
Aujourd’hui nous allons donc réaliser plusieurs scripts permettant successivement de :
- Créer un template de VM Windows Server 2019 ;
- Utiliser ce template via Terraform pour déployer X machines virtuelles sur notre cluster vSphere ;
J’en parlerais en conclusion, mais sachez que cet article m’aura pris une dizaine de jours pour être réalisé, en particulier à cause de ce « fichu » fichier Autounattend pour ne pas dire autre chose, et aussi du fait que la plupart des documentations/articles parlant de ce genre de lab ne sont plus d’actualité/ne fonctionnent juste pas 🙃
I) Setup du Lab
Bien, je pars du principe que vous avez déjà votre ESXi installé ainsi que relié à un datastore en réseau (ici ce sera un TrueNAS avec un partage NFS, classique). Il vous faudra comme dit en introduction un VCSA/vCenter installé et configuré, avec une licence permettant d’utiliser l’API « write access ».
Ensuite, nous allons setup une simple VM Ubuntu en 21.04 dans notre LAN, puis on installe Packer et Terraform
Pour ce faire, rien de plus simple :
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install terraform && apt-get install packer
On ajoute donc la clé GPG du dépôt, puis le dépôt lui-même, et enfin on update le tout et on installe !
Bien entendu, ces commandes sont à adapter selon votre distribution (ici Ubuntu).
II) Packer, Terraform… un rapide rappel ?
C’est vrai qu’installer ces outils sans d’abord savoir exactement de quoi il en retourne…
Packer tout d’abord. C’est un utilitaire de la très connue suite de chez Hashicorp, tout comme Terraform :
Pour faire simple, nous allons installer Packer, puis installer si besoin un « builder », qui est une sorte de module permettant de communiquer avec X fournisseur (VirtualBox, ESXi, AWS, Azure, Proxmox… mais par défaut plusieurs sont déjà installés, dont le vsphere-iso), et ensuite nous allons créer un fichier JSON où nous allons décrire la configuration de notre template Windows Server 2019: nombre de vCPU, mémoire, taille du disque et emplacement sur tel ou tel datastore… pas mal de choses sont possibles !
Et enfin via Terraform, nous allons cloner notre template créé via Packer mais en lui renseignant une configuration spécifique, pour exemple :
- Packer créé un template de VM avec 2x vCPU, 2Go de RAM, et 10Go de HDD sur notre datastore ;
- Terraform va cloner cette template mais en modifiant la config à 4x vCPU, 8Go de RAM, et 5Go de HDD ;
C’est un exemple bien entendu, mais l’idée est là. Si vous ne cernez pas encore l’intérêt ou que tout ceci est encore un peu flou, n’ayez pas peur, vous verrez vite de quoi on parle avec les fichiers de configs plus bas 😉.
III) Choix du Builder et explications
Pour Packer comme pour Terraform, nous devons choisir un Builder à utiliser (voir l’article sur Terraform & Proxmox pour en savoir plus). Pour rappel, si vous désirez créer des templates Packer pour AWS, il vous faudra le Builder EC2, pour créer des templates pour VirtualBox, il vous faudra le builder « VirtualBox », etc.
Ici nous avons donc le choix entre soit :
- vmware-iso, qui va aller télécharger un ISO, vérifier son checksum, installer l’OS puis en faire un template ;
- vsphere-iso, qui va faire essentiellement la même chose, sauf en utilisant l’API vSphere au lieu de l’accès SSH.
Je pensais à tort que le builder vmware-iso permettrait de se passer d’un vCenter/VCSA, mais ce n’est pas le cas, donc comme dit en introduction nous allons devoir en installer un, mettre notre hôte ESXi en cluster, puis ensuite nous utiliserons l’utilitaire vsphere-iso.
III) Création du template Packer
Comme toujours et avant tout, les différents fichiers de configurations qui seront présentés ici seront disponibles sur mon Github.
La première étape est donc de créer un fichier winserv2019.json (qu’importe le nom, pourvu qu’il soit au format JSON). Ici étant donné que nous allons créer un template pour un Windows Server 2019, je trouve que c’est assez parlant.
A l’intérieur de ce fichier, nous aurons ceci :
{
"builders": [
{
"CPUs": "{{user `vm-cpu-num`}}",
"RAM": "{{user `vm-mem-size`}}",
"cluster": "{{user `vsphere-cluster`}}",
"convert_to_template": true,
"datacenter": "{{user `vsphere-datacenter`}}",
"datastore": "{{user `vsphere-datastore`}}",
"guest_os_type": "windows2019srv_64Guest",
"insecure_connection": "true",
"iso_paths": [
"{{user `iso_ws2019`}}",
"{{user `iso_vmtools`}}"
],
"network_adapters": [
{
"network": "{{user `vsphere-network`}}",
"network_card": "vmxnet3"
}
],
"password": "{{user `vsphere-password`}}",
"disk_controller_type": "lsilogic-sas",
"storage": [
{
"disk_size": "{{user `vm-disk-size`}}",
"disk_thin_provisioned": true
}
],
"floppy_files": [
"autounattend.xml",
"winrm.ps1",
"vmtools.cmd"
],
"type": "vsphere-iso",
"username": "{{user `vsphere-user`}}",
"vcenter_server": "{{user `vsphere-server`}}",
"vm_name": "{{user `vm-name`}}",
"communicator": "winrm",
"winrm_username": "Administrateur",
"winrm_password": "Password!",
"folder": "TemplatesWindows",
"ip_wait_timeout": "1h"
}
],
"variables": {
"iso_ws2019": "[truenas-01] WIN-ISOs/Windows_Server_2019_64Bits.ISO",
"iso_vmtools": "[truenas-01] WIN-ISOs/VMToolsWS2019.iso",
"vm-cpu-num": "2",
"vm-disk-size": "20000",
"vm-mem-size": "2048",
"vm-name": "Win2019",
"vsphere-cluster": "BE-01",
"vsphere-datacenter": "NOTAMAX-BRUXELLES",
"vsphere-datastore": "truenas-01",
"vsphere-network": "VM Network",
"vsphere-password": "Password!",
"vsphere-server": "192.168.1.90",
"vsphere-user": "administrator@notamax.local"
}
}
Alors, pour aller assez vite et résumer, nous avons une première partie builders, où nous allons décrire toutes les informations relatives au builder (si si). Nombre de CPU, ram, nom du cluster vSphere, le fait de convertir la vm créée en template, le nom du datacenter, du datastore… bref toutes des choses classiques. JSON oblige, je n’ai pas pu mettre de commentaires dans ce fichier, mais si vous avez une question, n’hésitez pas à aller voir mon Github ou à la poser en bas de l’article !
Petite note tout de même par rapport à la partie floppy_files, qui permet donc d’ajouter le script d’autounattend, puis le script d’activation de WinRM, et enfin le script permettant d’installer les vmware-tools ainsi que de lancer le script WinRM.
Ensuite dans la partie variables, nous avons les différentes variables que l’on peut modifier. D’ordinaire je l’aurais placé en haut du script, mais pour d’obscures raisons ce dernier préfère être en bas… soit. A noter donc que pour les deux fichiers ISOs, à savoir le média d’installation de Windows Server ainsi que le média contenant les vmware-tools, ces deux-ci se trouvent sur le partage réseau NFS TrueNAS.
IV) Création du fichier Autounattend.xml
La partie qui m’aura pris le plus de temps à faire… à base de « Mais pourquoi !? » ou encore « C’est mille fois plus facile via CloudInit bon sang !«
Sur Windows, vous pouvez inclure un fichier nommé autounattend.xml au démarrage de celui-ci, qui permet de réaliser une installation automatique: choix de la langue, du partitionnement, des commandes effectuées au démarrage, bref la total. Je vous le donne ici, et je vais tâcher de le commenter de manière très rapide.
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="windowsPE">
<component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SetupUILanguage>
<WillShowUI>OnError</WillShowUI>
<UILanguage>en-US</UILanguage>
</SetupUILanguage>
<InputLocale>fr-BE</InputLocale>
<SystemLocale>fr-FR</SystemLocale>
<UILanguage>fr-FR</UILanguage>
<UserLocale>fr-FR</UserLocale>
</component>
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DiskConfiguration>
<Disk wcm:action="add">
<CreatePartitions>
<CreatePartition wcm:action="add">
<Order>1</Order>
<Size>500</Size>
<Type>Primary</Type>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>2</Order>
<Extend>true</Extend>
<Type>Primary</Type>
</CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<ModifyPartition wcm:action="add">
<Order>1</Order>
<PartitionID>1</PartitionID>
<Format>NTFS</Format>
<Label>Boot</Label>
<Active>true</Active>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Order>2</Order>
<PartitionID>2</PartitionID>
<Format>NTFS</Format>
<Label>Windows</Label>
</ModifyPartition>
</ModifyPartitions>
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
</Disk>
</DiskConfiguration>
<ImageInstall>
<OSImage>
<InstallFrom>
<MetaData wcm:action="add">
<Key>/IMAGE/NAME</Key>
<Value>Windows Server 2019 SERVERSTANDARD</Value>
</MetaData>
</InstallFrom>
<InstallTo>
<DiskID>0</DiskID>
<PartitionID>2</PartitionID>
</InstallTo>
<WillShowUI>OnError</WillShowUI>
<InstallToAvailablePartition>false</InstallToAvailablePartition>
</OSImage>
</ImageInstall>
<UserData>
<AcceptEula>true</AcceptEula>
</UserData>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<TimeZone>Romance Standard Time</TimeZone>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AutoLogon>
<Password>
<Value>Password!</Value>
<PlainText>true</PlainText>
</Password>
<LogonCount>3</LogonCount>
<Username>Administrateur</Username>
<Enabled>true</Enabled>
</AutoLogon>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<CommandLine>a:\vmtools.cmd</CommandLine>
<Order>1</Order>
</SynchronousCommand>
</FirstLogonCommands>
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideLocalAccountScreen>true</HideLocalAccountScreen>
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<NetworkLocation>Work</NetworkLocation>
<ProtectYourPC>1</ProtectYourPC>
</OOBE>
<UserAccounts>
<AdministratorPassword>
<Value>Password!</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
</UserAccounts>
</component>
</settings>
<cpi:offlineImage cpi:source="wim:c:/wim/install.wim#Windows Server 2019 SERVERSTANDARD" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend>
On choisi la langue pour le système, on partitionne notre disque, on ne rentre pas de clé de licence, on lance l’installation sur le disque partitionné précédemment, puis on accepte les EULA et enfin on démarre le script vmtools.cmd et on renseigne quelques infos supplémentaires: ne pas afficher la plétore de pubs spécifications supplémentaires proposées par Microsoft, créer un utilisateur Administrateur avec un mot de passe, et c’est à peu près tout !
Et là vous allez sûrement vous demander comment obtenir un fichier de ce genre… à la différence de Linux, ou un simple éditeur de texte vous suffit à créer un fichier de démarrage automatique, chez Microsoft vous devez installer un utilitaire nommé Windows System Image Manager, puis ouvrir votre ISO d’installation Windows Server pour y récupérer un fichier install.wim et enfin vous pourrez configurer votre script comme bon vous semble… vous sentez la frustration qui émane de ces différentes lignes pas vrai haha ? Bref, je ne vais pas vous faire de tutoriel ici pour créer votre fichier d’install’ automatique, il y en a plusieurs sur Internet ou si vous souhaitez vous épargner plusieurs heures de souffrance, copiez-collez simplement mon fichier, ou bien rendez-vous sur ce site qui vous mâche déjà en partie le travail.
Sur ces belles paroles, nous poursuivons ! 😄
V) Création du fichier vmtools.cmd ainsi que winrm.p1
Courage, on touche (presque) à la fin concernant Packer ! Ici nous allons créer un simple fichier vmtools.cmd, qui sera donc lancé au démarrage et qui va nous permettre plusieurs choses :
- Démarrer l’installation des additions invités Vmware de manière silencieuse, et sans redémarrage ;
- Exécuter notre script d’activation WinRM ;
- Patienter 2 minutes si Packer met trop de temps à se connecter au service WinRM ;
@rem Installation silencieuse, sans redémarrage avec exec du WinRM
e:\setup64 /s /v "/qb REBOOT=R" & powershell a:\WINRM.ps1 & timeout /T 120 & shutdown /r
Pour la première partie concernant l’installation des vmtools, ce script a été repris de la doc’ officielle de Packer si j’ai bonne souvenance.
Ensuite, concernant le script d’activation WinRM, je ne saurais vous dire où je l’ai récupéré, je pense que c’était aussi dans un des templates officiels (pour ceux qui fonctionnent encore) :
$ErrorActionPreference = "Stop"
# Switch network connection to private mode
# Required for WinRM firewall rules
$profile = Get-NetConnectionProfile
Set-NetConnectionProfile -Name $profile.Name -NetworkCategory Private
# Enable WinRM service
winrm quickconfig -quiet
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
winrm set winrm/config/service/auth '@{Basic="true"}'
# Reset auto logon count
# https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-autologon-logoncount#logoncount-known-issue
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name AutoLogonCount -Value 0
Bien ! Nous avons désormais tout nos fichiers de disponibles !
VI) Exécution via Packer build
Maintenant que notre fichier de configuration Packer est terminé ainsi que nos différents scripts, on peut l’exécuter pour vérifier que tout est en ordre :
packer build winserv2019.json
Le script va donc créer la VM, lui monter ses ISOs, son support « floppy » contenant nos différents scripts, puis il va lancer l’installation automatique de notre Windows Server 2019… un vrai régal à regarder quand vous avez passé plusieurs heures/journées à réussir à tout faire fonctionner 🤡
Une fois la VM démarrée et le Windows Server installé, le script va installer les Vmware-tools puis exécuter le service WinRM et le tour est joué ! Votre VM ne devrait même pas avoir à redémarrer grâce au timeout de 2 minutes, et une fois que Packer obtiendra l’IP de la VM puis qu’il verra que le service WinRM est disponible, il éteindra la VM et en fera un template.
*Selon la documentation d’Hashicorp, il est possible de ne pas spécifier de « communicator », comme SSH ou WinRM (car nous n’en avons pas besoin ici en réalité). J’ai essayé en renseignant « none » mais dans ce cas le script plante… et plusieurs personnes semblent s’en plaindre sur le forum de Packer, donc j’ai préféré garder WinRM.
**Ici la plupart des captures d’écrans seront sous Powershell, même si j’ai parlé d’Ubuntu en début, cela vient simplement du fait que si je démarrais ma VM Ubuntu mon PC saturait au niveau de la RAM… « improvise, adapt, overcome » comme on dit !
Si l’on se rend ensuite sur notre VCSA, on peut voir que notre template est bien créé dans notre dossier TemplatesWindows. Hourra !
VI) Création du script Terraform
Ici on est en terrain un peu plus connu depuis l’article sur Proxmox. J’ai donc créé un script semblable, en utilisant le même builder (vsphere-iso), et en rajoutant une variable permettant de créer X vm selon le X passé en argument, par exemple si vous faites : terraform apply -var ‘nombre=3’, le script déploiera 3 vms, aussi simple que ça.
D’ailleurs le nom de la VM sera incrémenté par rapport au nombre à déployer, en commençant par « Win2019-00« , puis « Win2019-01 » etc.
# Script permettant de déployer X VMs sur hôte ESXi 7 via template créé par Packer
# Mairien Anthony - Notamax
# Mis à jour le 15-02-21
# Définition du provider (ici esxi-01)
provider "vsphere" {
user = "administrator@notamax.local"
password = "Password!"
vsphere_server = "192.168.1.90"
# Si certificat auto-signé
allow_unverified_ssl = true
}
# Création variable pour nombre de VMs à déployer (récupéré via l'argument -var 'nombre=X')
variable "nombre" {
type = number
}
data "vsphere_datacenter" "dc" {
name = "NOTAMAX-BRUXELLES"
}
data "vsphere_datastore" "datastore" {
name = "truenas-01"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
data "vsphere_compute_cluster" "cluster" {
name = "BE-01"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
data "vsphere_network" "network" {
name = "VM Network"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
data "vsphere_virtual_machine" "template" {
name = "Win2019"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
resource "vsphere_virtual_machine" "vm" {
count = var.nombre
name = "Win2019-0${count.index}"
resource_pool_id = "${data.vsphere_compute_cluster.cluster.resource_pool_id}"
datastore_id = "${data.vsphere_datastore.datastore.id}"
num_cpus = 2
memory = 1024
guest_id = "${data.vsphere_virtual_machine.template.guest_id}"
scsi_type = "${data.vsphere_virtual_machine.template.scsi_type}"
network_interface {
network_id = "${data.vsphere_network.network.id}"
adapter_type = "${data.vsphere_virtual_machine.template.network_interface_types[0]}"
}
disk {
label = "disk0"
size = "${data.vsphere_virtual_machine.template.disks.0.size}"
eagerly_scrub = "${data.vsphere_virtual_machine.template.disks.0.eagerly_scrub}"
thin_provisioned = "${data.vsphere_virtual_machine.template.disks.0.thin_provisioned}"
}
clone {
template_uuid = "${data.vsphere_virtual_machine.template.id}"
}
}
Le script étant commenté, je ne pense pas avoir à vous ré-expliquer ici de quoi il en retourne 😛
Il ne nous reste plus qu’à réaliser un petit terraform init, puis ensuite un terraform validate pour vérifier si il n’y a pas d’erreur dans notre fichier de configuration :
C’est bon ! Nous allons donc déployer une VM pour commencer, en passant en argument la variable « nombre=1« , à ajuster donc selon le nombre de VMs souhaité :
On valide, et c’est bon ! La création de notre VM depuis notre template se lance, et au bout d’une quinzaine de minutes environ (dépendant de votre infrastructure bien-entendu), notre VM démarre et le build est terminé !
Nous avons donc bien notre Windows Server 2019 comme neuf, avec les VMware Tools installés et déjà connecté en DHCP ! A noter que comme expliqué sur le Github, et comme ce sera dit en conclusion, il n’y a pas de Sysprep réalisé ici, mais rien ne vous empêche de rattacher un script ou de rajouter les commandes dans le fichier vmtools.cmd.
VII) Conclusion et explications
Avant de conclure, j’aimerai donc revenir sur quelques points de manière assez rapide.
Comme dit dans l’article, je comptais partir sur une VM Ubuntu et réaliser tout le lab dessus, mais mon ordinateur n’ayant pas assez de RAM, j’ai dû me rabattre sur mon poste local sous Windows (d’où le Powershell sur les captures d’écran) mais cela revient au même, il n’y a pas de différence avec Packer/Terraform entre Windows/Linux.
Concernant le Sysprep, je ne l’ai pas fait par manque de temps et de ressources, je m’explique: j’avais promis de sortir beaucoup plus d’articles sur le blog, et les jours passant je me suis rendu compte que nous étions déjà le 15… et que j’avais oublié le Sysprep. Le soucis, c’est qu’à chaque build via Packer, cela me prenait au bas mot une bonne demi-heure pour démarrer l’ESXi/TrueNAS et démarrer le VCSA, puis encore presque une heure pour l’installation du Windows Server… et ce à chaque essai si mon script ne fonctionnait pas, donc quand tout était enfin terminé et fonctionnel, je me suis permis de rajouter une note dans cet article/sur le Github expliquant que vous êtiez libre d’ajouter ce bout de code par vous-même.
Si plus tard j’ai le temps et l’envie de me replonger dedans, je le rajouterai sûrement car ce n’est clairement pas une montagne à faire non plus.
Avant dernièr chose, c’est concernant le script vmtools.bat, ce dernier démarre le script powershell winrm.ps1. Et là vous allez sûrement vous dire que j’aurais pu rajouter une seconde commande d’autoLogon dans mon fichier autounattend, que ça aurait fonctionner et que ça auraité été plus propre… sauf que non, pour une raison que j’ignore encore à l’heure où j’écris ces lignes, le script ne prenait pas, ou bien il était exécuté mais avec un retard assez accablant, donc bref ce n’était pas concluant. Si c’était un « vrai lab physique », peut être que cela aurait fonctionné, mais dans tous les cas le résultat est le même.
Et enfin la dernière petite chose, c’est que je me suis aperçu que beaucoup de repo’ Github justement où on peut trouver des templates de ce genre ne fonctionnent plus ; soit le fichier Autounattend.xml n’est pas fonctionnel, soit tel ou tel script ne fonctionne pas… donc j’espère que cet article vous aidera si comme moi vous êtiez bloqué à certains endroits ! J’ai pour ma part mis pas mal de temps à rendre le tout fonctionnel mais car je n’avais jamais trop touché à l’autounattend de Windows de manière général, et aussi car j’étais limité matériellement parlant, si j’avais un véritable serveur à ma disposition il va de soit que le délai aurait été bien plus court 😛
Sur ce, je n’ai plus qu’à vous souhaiter une bonne journée/bonne soirée !
1 commentaire