Automatizando a criação de máquinas virtuais com PowerShell

Curry turnovers

Uma das coisas que eu acho entediante é montar laboratório para testes. Digo, no sentido de criar os servidores um a um, pois é uma tarefa demorada e trabalhosa. Muitos colegas utilizam o VMM (Virtual Machine Manager) em seus ambientes para facilitar nessa atividade e acho que é a solução ideal. Entretanto, nem sempre tenho disponível o VMM no ambiente do cliente e precisava  de algo mais simples para montar várias máquinas virtuais de uma só vez. Então resolvi montar um script para facilitar a minha vida. Geralmente precisamos montar vários servidores de uma só vez. Dado isso, o script lê um arquivo CSV que define as configurações mínimas das máquinas virtuais que serão montadas.

IMPORTANTE -  O script utiliza o modulo HyperV, que pode ser obtido em https://www.codeplex.com/psHyperV. Lá tem toda a informação de como instalar o módulo no computador onde o script vai rodar.

Abaixo a estrutura do arquivo CSV com três exemplos que irei comentar:

Name,CPU,Memory,NetName,IDEDisk,VHDParent
LAB01,4,4096,Rede Dados,W7Test.VHD,,
LAB02,1,1024,Rede Publica,new,,
LAB03,2,2048,Rede Dados,new,G:\VMs\Parent\parent.vhd
#LAB04,1,1024,Rede Dados,new,

O CSV tem 5 campos: Nome, CPU - quantidade de CPU, Memory - quantidade de memória em MB, NetName - nome da rede Hyper-V em que vai ser conectado, IDEDisk - Disco IDE que vai ser configurado na máquina virtual e por último VHDParent - o VHD do parente caso você escolha usar um diferential disk.

Analisando os registros do exemplo acima consigo explicar o funcionamento do script.

LAB01 – Quando fizer uma referência a um arquivo VHD explicitamente no campo IDEDisk, como na linha do LAB01, o mesmo deve existir no diretório padrão, dentro de um subdiretório com o mesmo nome da máquina virtual. Por exemplo, o VHD do LAB01, tendo como premissa o diretório padrão G:\VMs, deve estar em G:\VMs\LAB01\W7Test.VHD.
Esse é o caso de quando você criar uma VM base, faz um sysprep e copia a VM para um ou vários diretórios. A ferramenta mais indicada para copiar arquivos grande é o robocopy. Eu fiz um .bat que copia tudo que eu preciso, mas a estrutura principal é a linha abaixo:
robocopy [Diretório de Origem] [Diretório de Destino] *.vhd /mt:8

LAB02- Para criar um VHD novo basta colocar a palavra new no campo IDEDisk. Neste caso será criado o arquivo G:\VMs\LAB02\LAB-02-DriveC.VHD.

LAB03 – Quando for usar um disco diferencial, a referencia do VHDParent deve conter o caminho completo, pois muitas vezes os arquivos do tipo parente não ficam no mesmos discos que os arquivos das VMs.
Caso o nome da rede não corresponda a uma já existente no servidor Hyper-V, ou o arquivo VHD referenciado ou do parent não existam, o script não processa aquele registro e emite uma mensagem de Warning.

LAB04 - Não vai ser processado pois o caracter # na frente do nome diz ao script para pular o registro

Abaixo o script que eu chamei CreateVMs.ps1. Espero que ajude.

Import-Module 'C:\Program Files\modules\HyperV\HyperV.psd1'

Function Check-SwitchName([string]$strName, [string]$strComputerName) {
$VirtualServerSwitches = Get-WmiObject -NameSpace "root\virtualization" -Class "MsVM_VirtualSwitch" -ComputerName $strComputerName
foreach ($vmSwitch in $VirtualServerSwitches) {
if ($vmSwitch.ElementName -eq $strName) { return $true }
}
return $false
}

function Read-Default([string]$strmessage, [string]$strDefault)
{
Write-Host $strmessage -NoNewline -ForegroundColor White
if ($strDefault.length -gt 0) { Write-Host " [$strDefault] : " -NoNewline -ForegroundColor Yellow }
else { Write-Host ": " -NoNewline }
$result = Read-Host
if ($result.length -eq 0 ) {$result = $strDefault}
$result
}

[string]$defaultCSV = Get-Location
$defaultCSV += "\VMs.csv"
$csvFile = Read-Default "Please enter CSV file name" $defaultCSV
$fTest = Test-Path $csvFile
if ($fTest -eq $false) {
Write-Host $csvFile " File not found" -ForegroundColor Red -BackgroundColor black
Exit (1)
}
$strHostName = hostname
$VMHost = Read-Default "Please enter Hyper-V hostname" $strHostName

$strMachines = Import-CSV $csvFile

foreach ($strVm in $strMachines){
$vmName = $strVm.Name
$vmCPU = $strVm.CPU
$vmMemory = $strVm.Memory
$vmNetName = $strVm.NetName
$vmIDEDisk = $strVm.IDEDisk
$vmVHDParent = $strVm.VHDParent

if ($vmName.substring(0,1) -eq "#") { continue }

if ($vmName -eq $null -or $vmCPU -eq $null -or $vmMemory -eq $null -or $vmName -eq $null -or $vmIDEDisk -eq $null) {
write-host -ForegroundColor yellow -BackgroundColor black "Missing information, skipping record."
continue
}

if ($vmIDEDisk.contains(":")) {
write-host write-host -ForegroundColor yellow -BackgroundColor black "Skipping $vmName - Do not specify full path name for IDE disks."
continue
}

$bNew = $false
if ($vmIDEDisk.ToLower() -eq "new" ) {
$vmIDEDisk = $(get-VHDdefaultPath) + $vmName + "\" + $vmName + "-DriveC.VHD"
if ((Test-Path $vmIDEDisk) -eq $true) { Remove-Item $vmIDEDisk }

$bNew = $true}
else {
$vmIDEDisk = $(get-VHDdefaultPath) + $vmName + "\" + $vmIDEDisk
if ((Test-Path $vmIDEDisk) -eq $false) {
write-host -ForegroundColor yellow -BackgroundColor black "Skipping $vmName - Disk $vmIDEDisk not found"
continue
}
}

if ((Check-SwitchName $vmNetName $VMHost) -eq $false) {
write-host -ForegroundColor yellow -BackgroundColor black "Skipping $vmName - VirtualSwitch $vmNetName does not exist"
continue
}

$bParent = $false
if ($vmVHDParent -ne "" ) {
if ($vmVHDParent.IndexOf(":") -lt 1) {
write-host -ForegroundColor yellow -BackgroundColor black "Skipping $vmName - Please, specify full path name for VHD parent disk."
continue
}
else {
if ((Test-Path $vmVHDParent) -eq $false) {
write-host -ForegroundColor yellow -BackgroundColor black "Skipping $vmName - VHDParent $vmVHDParent not found."
continue
}
}
$bParent = $true
}

# create a new virtual machine
$vm = New-VM $vmName -server $VMHost
Set-VMCPUCount $vmName $vmCPU |Out-Null
Set-VMMemory $vmName -Memory $vmMemory |Out-Null
Add-vmNIC $vmName $vmNetName |Out-Null

if ($bParent -and $bNew) {
New-VHD -VHDPaths $vmIDEDisk -ParentVHDPath $vmVHDParent
Add-VMDisk $vmName 0 0 -Path $vmIDEDisk
}

if ($bParent -eq $false -and $bNew) {
New-VHD -VHDPaths $vmIDEDisk
Add-VMDisk $vmName 0 0 -Path $vmIDEDisk
}

if ($bParent -eq $false -and $bNew -eq $false) {
Add-VMDisk $vmName 0 0 $vmIDEDisk
}

#Adds a DVD drive to IDE O slot 1
Add-VMDRIVE $vmName -controllerID 0 -lun 1 -DVD
}