Clonage de machine virtuelle

De Wiki de Jordan LE NUFF
Sauter à la navigation Sauter à la recherche

Présentation

Cette page a pour objet de montrer comment cloner des machines virtuelles à partir de PowerCLI.

Prérequis

Simple clonage

Script

Voici un script nommé vmware_clone_VM.ps1 qui permet de cloner une machine vers une autre :

#!/usr/bin/pwsh

# Penser à utiliser le script create_credentials.ps1 pour créer les credentials

<#
.SYNOPSIS
    Script pour exporter une VM depuis le vCenter
.DESCRIPTION
    Au préalable, enregistrer les credentials en lançant le script create_credentials.ps1
.PARAMETER VmNameSource
    Le nom de la VM à cloner. (obligatoire)
.PARAMETER VmNameTarget
    Le nom de la VM cible à créer par clonage. (obligatoire)
.PARAMETER VmHostNameTarget
    Le nom de l'ESXi qui hébergera la VM cible. (faculatif)
	Si absent, prendra la valeur de l'ESXi source.
.PARAMETER VmDatastoreNameTarget
    Le nom du datastore qui hébergera la VM cible. (faculatif)
	Si absent, prendra la valeur du datastore source.
	Si fourni, doit correspondre à un datastore de l'ESXi $VmHostNameTarget.
.PARAMETER VmLocationTarget
    Le dossier de VM dans lequel se trouvera la VM cible. (faculatif)
	Seul le nom du dossier sans son arborescence est attendu.
	Ainsi, si un nom de dossier existe plus d'une fois, le script s'arrêtera.
	Si absent, prendra la valeur du dossier de VM source.
.PARAMETER VmDescriptionTarget
    Description de la VM cible. (faculatif)
.PARAMETER DeleteTarget
    Confirmation de la suppression de la VM cible avant clonage si elle existe. (faculatif)
	Valeur possible : No ou Yes
	Si Yes, la VM cible est définitivement supprimée.
	Si No et si la VM cible n'existe pas, le script s'exécutera normalement et la VM cible sera créée.
	Si No et si la VM cible existe déjà, le script se terminera immédiatement sans réaliser le clonage.
.EXAMPLE
	./vmware_clone_VM.ps1 -DeleteTarget Yes myserver mybackupserver
.NOTES
    Author: LE NUFF Jordan
    Date:   Jan 11, 2021
#>

## Définition des paramètres d'entrée
Param
(
	[Parameter(Mandatory=$true, Position=0, HelpMessage="Nom de la VM source")]
	[string]$VmNameSource,
	[Parameter(Mandatory=$true, Position=1, HelpMessage="Nom de la VM cible")]
	[string]$VmNameTarget,
	[Parameter(Mandatory=$false, HelpMessage="ESXi cible")]
	[string]$VmHostNameTarget,
	[Parameter(Mandatory=$false, HelpMessage="Datastore cible")]
	[string]$VmDatastoreNameTarget,
	[Parameter(Mandatory=$false, HelpMessage="Dossier de VM cible")]
	[string]$VmLocationTarget,
	[Parameter(Mandatory=$false, HelpMessage="Description de VM cible")]
	[string]$VmDescriptionTarget="",
	[Parameter(Mandatory=$false, HelpMessage="Confirmation de la suppression de la VM cible : No ou Yes")]
	[ValidateSet('No','Yes')]
	[string]$DeleteTarget='No'
)
If ($VmNameSource -eq $VmNameTarget) {
	Write-Host "La VM cible ne peut pas être la même que la VM source '$VmNameSource'. Fin du script."
	exit
}

## Initialisation variables
# Dossier des identifiants
$CredsFileLocation="/data/scripts/.credentials"
# Adresse IP du vCenter
$UrlVcenter="vcenter01.groupegdb.local"
# Identifiant VMware à utiliser
$VmwareUser="expl@groupegdb.local"
# Construction du fichier d'identifiants à utiliser
$VcFileCreds=$($CredsFileLocation + "/" + $UrlVcenter + "_" + $VmwareUser + "_ps.xml")
# Importation des credentials
$VcCreds=Import-Clixml -Path $VcFileCreds

## Connection au VCenter
Connect-VIServer -Server $UrlVcenter -Credential $VcCreds | Out-Null

## Vérifications
# La VM source existe-t-elle ? Si non, fin du script
$VmSource=Get-VM -name $VmNameSource -ErrorAction SilentlyContinue
If (!$VmSource){
	Write-Host "La VM source $VmNameSource n'existe pas. Fin du script."
	exit
} ElseIf ($VmSource.Count -gt 1) {
	Write-Host "La VM source $VmNameSource fournie existe plus d'une fois. VM source ambigüe. Fin du script."
	exit
}
# La VM cible existe-t-elle ? Si oui et absence de la confirmation de la suppresion, fin du script
$VmTarget=Get-VM -name $VmNameTarget -ErrorAction SilentlyContinue
If ($VmTarget) {
	If ($VmTarget.Count -gt 1) {
		Write-Host "La VM cible $VmNameCible fournie existe plus d'une fois. VM cible ambigüe. Fin du script."
		exit
	} ElseIf ($DeleteTarget -eq 'No') {
		Write-Host "La VM cible $VmNameTarget existe et sa suppression n'a pas été confirmée en entrée de ce script avec l'option '-DeleteTarget Yes'. Fin du script."
		exit
	}
	If ( $VmTarget.PowerState -ne 'PoweredOff' ) 
	{
		$counter = 0
		Shutdown-VMGuest $VmNameTarget -confirm:$false | Out-Null
		do {
			Start-Sleep -s 1
			If ( $counter -ge 60 ) {
				Write-Host "La VM cible $VmNameTarget n'est toujours pas éteinte après un délai de 60 secondes. Fin du script."
				exit
			}
			$VmTarget = Get-VM $VmNameTarget
			$status = $VmTarget.PowerState
			$counter++
		} until ($status -eq "PoweredOff")
	}
}
# Si l'ESXi cible a été fourni, vérification de son existence
If ($VmHostNameTarget) {
	$VmHostTarget=Get-VMhost -Name $VmHostNameTarget -ErrorAction SilentlyContinue
	# Si inexistant, fin du script
	If (!$VmHostTarget){
		Write-Host "L'ESXi cible $VmHostNameTarget fourni n'existe pas. Fin du script."
		exit
	}
# Si l'ESXi n'a pas été fourni
} ElseIf (!$VmHostNameTarget) {
	# Utilisation du même ESXi que la VM source
	$VmHostTarget=Get-VMhost -VM $VmSource
}
# Si le datastore a été fourni, vérification de son existence
If ($VmDatastoreNameTarget) {
	$VmDatastoreTarget=Get-Datastore -Name $VmDatastoreNameTarget -ErrorAction SilentlyContinue
	# Si inexistant, fin du script
	If (!$VmDatastoreTarget){
		Write-Host "L'ESXi cible $VmDatastoreNameTarget fourni n'existe pas. Fin du script."
		exit
	# Si existant
	}
# Si le datastore n'a pas été fourni
} ElseIf (!$VmHostNameTarget) {
	# Utilisation du même ESXi que la VM source
	$VmDatastoreTarget=Get-Datastore -VM $VmSource
}
# Si le datastore cible n'est pas accessible depuis l'ESXi cible, fin du script
If (!$(Get-Datastore -VMHost $VmHostTarget | Where-Object {$_ -eq $VmDatastoreTarget})){
	Write-Host "Le datastore cible $VmDatastoreTarget n'est pas accessible depuis l'ESXi cible $VmHostTarget. Fin du script."
	exit
}
# Si le dossier de VM cible a été fourni, vérification de son existence
If ($VmLocationTarget) {
	$LocationTarget=Get-Folder -Name $VmLocationTarget -Type VM -ErrorAction SilentlyContinue
	# Si inexistant, fin du script
	If (!$LocationTarget){
		Write-Host "Le dossier de VM cible $VmLocationTarget fourni n'existe pas. Fin du script."
		exit
	} ElseIf ($LocationTarget.Count -gt 1) {
		Write-Host "Le dossier de VM cible $VmLocationTarget fourni existe plus d'une fois. Localisation cible ambigüe. Fin du script."
		exit
	}
# Si le dossier de VM cible n'a pas été fourni
} ElseIf (!$VmHostNameTarget) {
	# Utilisation du même dossier de VM que la VM source
	$LocationTarget=Get-Folder -Id $VmSource.FolderId
}

## Traitement
# Suppression de la VM cible
If ($VmTarget -And $DeleteTarget -eq 'Yes') {
	Write-Host "Suppression de la VM cible $VmTarget"
	Remove-VM -VM $VmTarget DeletePermanently confirm:$false
	Write-Host "La VM $VmTarget a été supprimée avec succès."
}
# Clonage de la machine
If (New-VM -Name $VmNameTarget -VM $VmSource -VMHost $VmHostTarget -Datastore $VmDatastoreTarget -Location $LocationTarget -Description $VmDescriptionTarget) {
	Write-Host "La VM $VmNameTarget a été clonée avec succès depuis la VM $VmSource. Fin du script."
} Else {
	Write-Host "Une erreur est survenue lors du clonage de la VM $VmNameTarget depuis la VM $VmSource Fin du script."
	exit
}

Disconnect-VIServer -Confirm:$false

Utilisation

L'utilisation de ce script se fait de la façon suivante :

./vmware_clone_VM.ps1 [-VmNameSource] <string[]> [-VmNameTarget] <string[]> [-VmHostNameTarget <String>] [-VmDatastoreNameTarget <String>] [-VmLocationTarget <String>] [-VmDescriptionTarget <String[""]>] [-DeleteTarget <String["No"|"Yes"]="No">]

Voici le détail des options :

  • VmNameSource
Le nom de la VM à cloner. (obligatoire)
  • VmNameTarget
Le nom de la VM cible à créer par clonage. (obligatoire)
  • VmHostNameTarget
Le nom de l'ESXi qui hébergera la VM cible. (faculatif)
Si absent, prendra la valeur de l'ESXi source.
  • VmDatastoreNameTarget
Le nom du datastore qui hébergera la VM cible. (faculatif)
Si absent, prendra la valeur du datastore source.
Si fourni, doit correspondre à un datastore de l'ESXi $VmHostNameTarget.
  • VmLocationTarget
Le dossier de VM dans lequel se trouvera la VM cible. (faculatif)
Seul le nom du dossier sans son arborescence est attendu.
Ainsi, si un nom de dossier existe plus d'une fois, le script s'arrêtera.
Si absent, prendra la valeur du dossier de VM source.
  • VmDescriptionTarget
Description de la VM cible. (faculatif)
  • DeleteTarget
Confirmation de la suppression de la VM cible avant clonage si elle existe. (faculatif)
Valeur possible : No ou Yes
Si Yes, la VM cible est définitivement supprimée.
Si No et si la VM cible n'existe pas, le script s'exécutera normalement et la VM cible sera créée.
Si No et si la VM cible existe déjà, le script se terminera immédiatement sans réaliser le clonage.

Limitations

A ce jour, ce script possède des limitations liées aux noms qui existent en double.

Cela concerne les objets suivants :

  • Machine Virtuelle
Deux machines virtuelles peuvent porter le même nom. En revanche, leurs ID seront différents. Ce script se basant sur le nom d'une machine virtuelle, il tombera en erreur et s'arrêtera en cas de machine non-unique.
  • Dossier de VM
Un dossier de VM est défini par
  • Un ID et nom
  • Un ID de parent
Ainsi, l'arborescence d'un dossier n'est pas inclus dans son nom. De ce fait, il n'est pas possible d'indiquer un chemin complet en entrée de ce script. De la même manière que pour les machines virtuelles, Deux dossiers de VM peuvent porter le même nom. Ainsi, le script tombera en erreur et s'arrêtera en cas de dossier de VM non-unique.

Clonage multiple

Prérequis

  • Script vmware_clone_VM.ps1 présenté plus haut
  • Fichier CSV contenant les informations nécessaires des machines à cloner

Fichier CSV

Voici un exemple de fichier CSV nommé vms_list_to_clone.csv dont les champs sont séparés par des ; : Le fichier vmlist est manquant.

VmSource VmHostnameSource IpSource GwSource VmTarget IpTarget GwTarget ESX Datastore Dossier VLAN Utilisateur Description

Script de clonage multiple

Voici un exemple de script de clonage multiple s'appuyant sur le script vmware_clone_VM.ps1 présenté plus haut, d'une part, et le fichier vms_list_to_clone.csv, d'autre part :

  1 #!/usr/bin/pwsh
  2 
  3 # Penser à utiliser le script create_credentials.ps1 pour créer les credentials
  4 
  5 ## Initialisation variables
  6 # Dossier des identifiants
  7 $CredsFileLocation="/data/scripts/.credentials"
  8 # Adresse du vCenter
  9 $UrlVcenter="myvcenter.mydomain.com"
 10 # Identifiant VMware à utiliser
 11 $VmwareUser="myaccount@mydomain.com"
 12 # Chemin du fichier CSV contenant les VMs à cloner
 13 $CsvVmsPath="/data/scripts/vms_list_to_clone.csv"
 14 
 15 Import-Csv $CsvVmsPath -Delimiter ";"| Foreach-Object { 
 16 
 17 	Write-Host $_.VmTarget
 18 
 19 	# VM d'origine
 20 	$VmNameSource=$_.VmSource
 21 	$VmHostnameSource=$_.VmHostnameSource
 22 	$VmIpSource=$_.IpSource
 23 	$VmGwSource=$_.GwSource
 24 
 25 	# VM cible
 26 	$VmNameTarget=$_.VmTarget
 27 	$VmIpTarget=$_.IpTarget
 28 	$VmGwTarget=$_.GwTarget
 29 	$VmHostTarget=$_.ESX
 30 	$VmDatastoreTarget=$_.Datastore
 31 	$VmLocationTarget=$_.Dossier
 32 	$VmVlanNameTarget=$_.VLAN
 33 	$VmDescriptionTarget=$_.Description
 34 
 35 	# Utilisateur à utiliser
 36 	$VmUser=$_.Utilisateur
 37 	$UserTemplate="root" # <== A variabiliser
 38 
 39 	# Construction des fichiers d'identifiants à utiliser
 40 	$VcFileCreds=$($CredsFileLocation + "/" + $UrlVcenter + "_" + $VmwareUser + "_ps.xml")
 41 	$VmFileCredsSource=$($CredsFileLocation + "/" + $VmHostnameSource + "_" + $VmUser + "_ps.xml")
 42 	$FileCredsTemplate=$($CredsFileLocation + "/template_mysql_" + $UserTemplate + "_ps.xml")
 43 	$VmFileCredsTarget=$($CredsFileLocation + "/" + $VmNameTarget + "_" + $VmUser + "_ps.xml")
 44 
 45 	# Importation des credentials
 46 	$VcCreds=Import-Clixml -Path $VcFileCreds
 47 	$VmCredsSource=Import-Clixml -Path $VmFileCredsSource
 48 	$CredsTemplate=Import-Clixml -Path $FileCredsTemplate
 49 	$VmCredsTarget=Import-Clixml -Path $VmFileCredsTarget
 50 
 51 	# Récupération en clair du mot de passe de l'utilisateur cible
 52 	$PasswdTemplate=$CredsTemplate.GetNetworkCredential().Password
 53 	$PasswdTarget=$VmCredsTarget.GetNetworkCredential().Password
 54 
 55 	## Pré-traitement
 56 	# Clonage de la VM
 57 	& /data/scripts/vmware_clone_VM.ps1 "$VmNameSource" $VmNameTarget -VmHostNameTarget $VmHostTarget -VmDatastoreNameTarget $VmDatastoreTarget -VmLocationTarget "$VmLocationTarget" -VmDescriptionTarget "$VmDescriptionTarget" -DeleteTarget Yes
 58 
 59 	## Connection au VCenter
 60 	Connect-VIServer -Server $UrlVcenter -Credential $VcCreds | Out-Null
 61 
 62 	## Vérifications
 63 	# La VM cible a-t-elle bien été créée ? Si non, fin du script
 64 	$VmTarget=Get-VM -name $VmNameTarget -ErrorAction SilentlyContinue
 65 	If (!$VmTarget){
 66 		Write-Host "La VM cible $VmNameTarget n'existe pas. Fin du script."
 67 		exit
 68 	} ElseIf ($VmTarget.Count -gt 1) {
 69 		Write-Host "La VM cible $VmNameTarget fournie existe plus d'une fois. VM cible ambigüe. Fin du script."
 70 		exit
 71 	}
 72 
 73 	# Le VLAN source est-il unique ? Si non, fin du script
 74 	$VmSource=Get-VM -name "$VmNameSource" -ErrorAction SilentlyContinue
 75 	$VmVlanSource=Get-VirtualPortGroup -VM $VmSource
 76 	If (!$VmVlanSource){
 77 		Write-Host "Il n'y a pas de VLAN source existant. Fin du script."
 78 		exit
 79 	} ElseIf ($VmVlanSource.Count -gt 1) {
 80 		Write-Host "Il existe plus d'un VLAN source. VLAN source ambigu. Fin du script."
 81 		exit
 82 	}
 83 
 84 	# Le VLAN cible existe-t-il ? Si non, fin du script
 85 	$VmVlanTarget=Get-VMHost -VM $VmTarget | Get-VirtualPortGroup -Name $VmVlanNameTarget
 86 	If (!$VmVlanTarget){
 87 		Write-Host "Le VLAN cible $VmVlanNameTarget n'existe pas. Fin du script."
 88 		exit
 89 	} ElseIf ($VmVlanTarget.Count -gt 1) {
 90 		Write-Host "Le VLAN cible $VmVlanNameTarget fourni existe plus d'une fois. VLAN cible ambigu. Fin du script."
 91 		exit
 92 	}
 93 
 94 	## Traitement
 95 	# Déconnection de la carte réseau
 96 	$VmNicTarget=Get-NetworkAdapter -VM $VmTarget | Set-NetworkAdapter -StartConnected:$false confirm:$false
 97 	If (!$VmNicTarget){
 98 		Write-Host "Une erreur s'est produite lors de la coniguration de la carte réseau en mode déconnecté au démarrage. Fin du script."
 99 		exit
100 	}
101 
102 	# Allumage de la VM cible
103 	If ($VmTarget.PowerState -eq 'PoweredOff') {
104 		Start-VM -VM $VmTarget confirm:$false | Wait-Tools | Out-Null
105 	}
106 
107 	# Désactivation Zabbix, renommage des fichiers portant le nom de la machine, changement adresse IP, réinitialisation des clés SSH
108 	$script=@"
109 systemctl enable zabbix-agent.service
110 sleep 10
111 export MYSQL_PWD=$PasswdTemplate
112 mysql -uroot -e "ALTER USER 'pma_user'@'192.168.0.40' IDENTIFIED BY '$PasswdTarget';"
113 mysql -uroot -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$PasswdTarget';"
114 systemctl stop mysqld
115 sed -i -e 's/'$VmHostnameSource'/'$VmNameTarget'/g' /etc/hostname
116 sed -i -e 's/'$VmHostnameSource'/'$VmNameTarget'/g' /etc/zabbix/zabbix_agentd.conf
117 /usr/bin/sed -i -e 's/'$VmIpSource'/'$VmIpTarget'/g' /etc/sysconfig/network-scripts/ifcfg-ens192
118 /usr/bin/sed -i -e 's/'$VmGwSource'/'$VmGwTarget'/g' /etc/sysconfig/network-scripts/ifcfg-ens192
119 rm -rf /etc/ssh/ssh_host_*
120 ssh-keygen -A -q
121 rm -f /root/.ssh/*rsa*
122 >/root/.bash_history
123 "@
124 	Invoke-VMScript -VM $VmTarget -ScriptType Bash -ScriptText $script -GuestCredential $VmCredsSource
125 
126 	# Mise à jour du mot de passe
127 	Invoke-VMScript -VM $VmTarget -ScriptType Bash -ScriptText "echo $PasswdTarget | passwd --stdin $VmUser" -GuestCredential $VmCredsSource -ErrorAction SilentlyContinue
128 
129 	# Redémarrage de la machine cible
130 	Restart-VMGuest -VM $VmTarget | Wait-Tools | Out-Null
131 
132 	# Changement de VLAN de la carte réseau
133 	If ( $VmVlanSource -ne $VmVlanTarget ) {
134 		If (!$(Set-NetworkAdapter -NetworkAdapter $VmNicTarget -Portgroup $VmVlanTarget confirm:$false)) {
135 			Write-Host "Une erreur s'est produite lors du changement de VLAN de la carte réseau. Fin du script."
136 			exit
137 		}
138 	}
139 
140 	# Reconnection de la carte réseau
141 	If (!$(Set-NetworkAdapter -NetworkAdapter $VmNicTarget -StartConnected:$true -Connected:$true confirm:$false)) {
142 		Write-Host "Une erreur s'est produite lors de la coniguration de la carte réseau en mode connecté en l'état et au démarrage. Fin du script."
143 		exit
144 	}
145 	Disconnect-VIServer -Confirm:$false
146 }

Explications des options :

  • Ligne 7 :
Localisation du dossier contenant les identifiants précédemment créés avec le script create_credentials.ps1
  • Ligne 9 et 11 :
Adresse et identifiant du vCenter à utiliser
  • Ligne 13 :
Localisation du fichier CSV vms_list_to_clone.csv précédemment créé
  • Ligne 57 :
Appel du script vmware_clone_VM.ps1 précédemment présenté dans la partie "Simple clonage" de cette page.
  • Lignes 109 à 122 :
Script personnalisé contenant les commandes à passer sur la VM
En l'occurrence, dans cet exemple, les commandes font les actions suivantes :
  • Activation du service de supervision Zabbix
  • Attente que le serveur MySQL soit bien lancé
  • Réinitialisation de mots de passe de certains utilisateurs MySQL
  • Renommage de la machine
  • Changement d'adresse IP
  • Réinitialisation des clés SSH
  • Suppression de l'historique des commandes de l'utilisateur utilisé pour passer les commandes (effacement des mots de passe en clair)
  • Ligne 127
Réinitialisation du mot de passe de l'administrateur de la machine virtuelle