Add fixes for Linux environment and add Windows environment
This commit is contained in:
@@ -216,10 +216,10 @@ source "vsphere-iso" "debian" {
|
|||||||
# 4. Navigate down to the linux kernel line (3rd line in the entry) and
|
# 4. Navigate down to the linux kernel line (3rd line in the entry) and
|
||||||
# jump to its end, then append preseed parameters.
|
# jump to its end, then append preseed parameters.
|
||||||
# 5. Press Ctrl+X to boot.
|
# 5. Press Ctrl+X to boot.
|
||||||
boot_wait = "10s"
|
boot_wait = "20s"
|
||||||
boot_command = [
|
boot_command = [
|
||||||
"<down><wait>",
|
"<down><wait2>",
|
||||||
"e<wait2>",
|
"e<wait3>",
|
||||||
"<down><down><down><end>",
|
"<down><down><down><end>",
|
||||||
" auto=true priority=critical",
|
" auto=true priority=critical",
|
||||||
" locale=en_US.UTF-8",
|
" locale=en_US.UTF-8",
|
||||||
|
|||||||
@@ -216,10 +216,10 @@ source "vsphere-iso" "debian" {
|
|||||||
# 4. Navigate down to the linux kernel line (3rd line in the entry) and
|
# 4. Navigate down to the linux kernel line (3rd line in the entry) and
|
||||||
# jump to its end, then append preseed parameters.
|
# jump to its end, then append preseed parameters.
|
||||||
# 5. Press Ctrl+X to boot.
|
# 5. Press Ctrl+X to boot.
|
||||||
boot_wait = "10s"
|
boot_wait = "20s"
|
||||||
boot_command = [
|
boot_command = [
|
||||||
"<down><wait>",
|
"<down><wait2>",
|
||||||
"e<wait2>",
|
"e<wait3>",
|
||||||
"<down><down><down><end>",
|
"<down><down><down><end>",
|
||||||
" auto=true priority=critical",
|
" auto=true priority=critical",
|
||||||
" locale=en_US.UTF-8",
|
" locale=en_US.UTF-8",
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ $ServerTemplateName Template VM used for server and router clones.
|
|||||||
$GuiTemplateName Template VM used for workstation clones.
|
$GuiTemplateName Template VM used for workstation clones.
|
||||||
|
|
||||||
This script expects these port groups to already exist:
|
This script expects these port groups to already exist:
|
||||||
HQ-SERVER-Linux
|
PG-VLAN10 (HQ-SERVER)
|
||||||
HQ-DMZ-Linux
|
PG-VLAN11 (HQ-DMZ)
|
||||||
HQ-CLIENT-Linux
|
PG-VLAN12 (HQ-CLIENT)
|
||||||
INET-HQ-Linux
|
PG-VLAN20 (INET-HQ)
|
||||||
INET-BRANCH-Linux
|
PG-VLAN21 (INET-BRANCH)
|
||||||
HOME-Linux
|
PG-VLAN23 (HOME)
|
||||||
BR-SERVER-Linux
|
PG-VLAN30 (BR-SERVER)
|
||||||
BR-CLIENT-Linux
|
PG-VLAN51 (BR-CLIENT)
|
||||||
|
|
||||||
What the script does:
|
What the script does:
|
||||||
- Creates the ES2025 Linux topology VMs as linked clones
|
- Creates the ES2025 Linux topology VMs as linked clones
|
||||||
@@ -33,8 +33,10 @@ param()
|
|||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$DatacenterName = 'Ilica'
|
||||||
$TemplateFolderName = 'Templates'
|
$TemplateFolderName = 'Templates'
|
||||||
$TargetParentFolderName = 'Euroskills 2025'
|
$SkillsFolderName = 'Skills'
|
||||||
|
$TargetParentFolderName = 'ES2025'
|
||||||
$TargetFolderName = 'Linux'
|
$TargetFolderName = 'Linux'
|
||||||
$ServerTemplateName = 'debian-13-template'
|
$ServerTemplateName = 'debian-13-template'
|
||||||
$GuiTemplateName = 'debian-13-gui-template'
|
$GuiTemplateName = 'debian-13-gui-template'
|
||||||
@@ -51,10 +53,14 @@ $CloudInitShutdownTimeoutSeconds = 900
|
|||||||
function Get-SingleFolder {
|
function Get-SingleFolder {
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$Name
|
[string]$Name,
|
||||||
|
|
||||||
|
$Location = $null
|
||||||
)
|
)
|
||||||
|
|
||||||
$folders = @(Get-Folder -Name $Name -ErrorAction SilentlyContinue)
|
$params = @{ Name = $Name; Type = 'VM'; ErrorAction = 'SilentlyContinue' }
|
||||||
|
if ($Location) { $params['Location'] = $Location }
|
||||||
|
$folders = @(Get-Folder @params)
|
||||||
|
|
||||||
if ($folders.Count -eq 0) {
|
if ($folders.Count -eq 0) {
|
||||||
throw "Folder '$Name' was not found."
|
throw "Folder '$Name' was not found."
|
||||||
@@ -76,7 +82,7 @@ function Get-SingleChildFolder {
|
|||||||
$ParentFolder
|
$ParentFolder
|
||||||
)
|
)
|
||||||
|
|
||||||
$folders = @(Get-Folder -Location $ParentFolder -Name $Name -ErrorAction SilentlyContinue)
|
$folders = @(Get-Folder -Location $ParentFolder -Name $Name -Type VM -ErrorAction SilentlyContinue)
|
||||||
|
|
||||||
if ($folders.Count -eq 0) {
|
if ($folders.Count -eq 0) {
|
||||||
throw "Folder '$Name' was not found under '$($ParentFolder.Name)'."
|
throw "Folder '$Name' was not found under '$($ParentFolder.Name)'."
|
||||||
@@ -280,8 +286,10 @@ function Wait-ForVmToPowerOff {
|
|||||||
throw "VM '$($VM.Name)' did not power off within $TimeoutSeconds seconds."
|
throw "VM '$($VM.Name)' did not power off within $TimeoutSeconds seconds."
|
||||||
}
|
}
|
||||||
|
|
||||||
$templateFolder = Get-SingleFolder -Name $TemplateFolderName
|
$datacenter = Get-Datacenter -Name $DatacenterName -ErrorAction Stop
|
||||||
$targetParentFolder = Get-SingleFolder -Name $TargetParentFolderName
|
$skillsFolder = Get-SingleFolder -Name $SkillsFolderName -Location $datacenter
|
||||||
|
$templateFolder = Get-SingleChildFolder -Name $TemplateFolderName -ParentFolder $skillsFolder
|
||||||
|
$targetParentFolder = Get-SingleChildFolder -Name $TargetParentFolderName -ParentFolder $skillsFolder
|
||||||
$targetFolder = Get-SingleChildFolder -Name $TargetFolderName -ParentFolder $targetParentFolder
|
$targetFolder = Get-SingleChildFolder -Name $TargetFolderName -ParentFolder $targetParentFolder
|
||||||
|
|
||||||
$serverTemplate = Get-SingleVmFromFolder -Name $ServerTemplateName -Folder $templateFolder
|
$serverTemplate = Get-SingleVmFromFolder -Name $ServerTemplateName -Folder $templateFolder
|
||||||
@@ -291,18 +299,18 @@ $serverReferenceSnapshot = Get-OrCreateReferenceSnapshot -VM $serverTemplate -Sn
|
|||||||
$guiReferenceSnapshot = Get-OrCreateReferenceSnapshot -VM $guiTemplate -SnapshotName $ReferenceSnapshotName
|
$guiReferenceSnapshot = Get-OrCreateReferenceSnapshot -VM $guiTemplate -SnapshotName $ReferenceSnapshotName
|
||||||
|
|
||||||
$vmDefinitions = @(
|
$vmDefinitions = @(
|
||||||
@{ Name = 'R-HQ'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('HQ-SERVER-Linux', 'HQ-DMZ-Linux', 'HQ-CLIENT-Linux', 'INET-HQ-Linux') },
|
@{ Name = 'R-HQ'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN10', 'PG-VLAN11', 'PG-VLAN12', 'PG-VLAN20') },
|
||||||
@{ Name = 'R-INT'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('INET-HQ-Linux', 'INET-BRANCH-Linux', 'HOME-Linux') },
|
@{ Name = 'R-INT'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN20', 'PG-VLAN21', 'PG-VLAN23') },
|
||||||
@{ Name = 'R-BR'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('BR-SERVER-Linux', 'BR-CLIENT-Linux', 'INET-BRANCH-Linux') },
|
@{ Name = 'R-BR'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN30', 'PG-VLAN51', 'PG-VLAN21') },
|
||||||
@{ Name = 'HQ-DC'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('HQ-SERVER-Linux') },
|
@{ Name = 'HQ-DC'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN10') },
|
||||||
@{ Name = 'HQ-SAM-1'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('HQ-SERVER-Linux') },
|
@{ Name = 'HQ-SAM-1'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN10') },
|
||||||
@{ Name = 'HQ-SAM-2'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('HQ-SERVER-Linux') },
|
@{ Name = 'HQ-SAM-2'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN10') },
|
||||||
@{ Name = 'HQ-DMZ-1'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('HQ-DMZ-Linux') },
|
@{ Name = 'HQ-DMZ-1'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN11') },
|
||||||
@{ Name = 'HQ-DMZ-2'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('HQ-DMZ-Linux') },
|
@{ Name = 'HQ-DMZ-2'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN11') },
|
||||||
@{ Name = 'BR-SRV'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('BR-SERVER-Linux') },
|
@{ Name = 'BR-SRV'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('PG-VLAN30') },
|
||||||
@{ Name = 'HQ-CL'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('HQ-CLIENT-Linux') },
|
@{ Name = 'HQ-CL'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('PG-VLAN12') },
|
||||||
@{ Name = 'HOME'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('HOME-Linux') },
|
@{ Name = 'HOME'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('PG-VLAN23') },
|
||||||
@{ Name = 'BR-CL'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('BR-CLIENT-Linux') }
|
@{ Name = 'BR-CL'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('PG-VLAN51') }
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach ($definition in $vmDefinitions) {
|
foreach ($definition in $vmDefinitions) {
|
||||||
@@ -332,6 +340,11 @@ foreach ($definition in $vmDefinitions) {
|
|||||||
Ensure-ExtraDisks -VM $vm -DiskCount $ExtraDiskCount -DiskSizeGB $ExtraDiskSizeGB
|
Ensure-ExtraDisks -VM $vm -DiskCount $ExtraDiskCount -DiskSizeGB $ExtraDiskSizeGB
|
||||||
Set-ExactNetworkAdapters -VM $vm -PortGroupNames $definition.Networks
|
Set-ExactNetworkAdapters -VM $vm -PortGroupNames $definition.Networks
|
||||||
Set-CloudInitUserData -VM $vm -Username $DefaultUsername -Password $DefaultPassword
|
Set-CloudInitUserData -VM $vm -Username $DefaultUsername -Password $DefaultPassword
|
||||||
|
$templateCdroms = @(Get-CDDrive -VM $definition.Template | Sort-Object Name)
|
||||||
|
$vmCdroms = @(Get-CDDrive -VM $vm | Sort-Object Name)
|
||||||
|
for ($i = 0; $i -lt $templateCdroms.Count; $i++) {
|
||||||
|
$vmCdroms[$i] | Set-CDDrive -IsoPath $templateCdroms[$i].IsoPath -StartConnected $true -Confirm:$false | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
$startSnapshot = Get-Snapshot -VM $vm -Name $StartSnapshotName -ErrorAction SilentlyContinue
|
$startSnapshot = Get-Snapshot -VM $vm -Name $StartSnapshotName -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
|||||||
291
es2025/windows/Create-VMs.ps1
Normal file
291
es2025/windows/Create-VMs.ps1
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
<#
|
||||||
|
Adjust these values only if your vCenter names are different:
|
||||||
|
$WinTemplateName Template VM used for all Windows Server VMs.
|
||||||
|
$LinuxTemplateName Template VM used for DEV-PC (Debian 13 GUI).
|
||||||
|
|
||||||
|
This script expects these port groups to already exist:
|
||||||
|
PG-VLAN10 (CPH-INT)
|
||||||
|
PG-VLAN11 (CPH-DEV)
|
||||||
|
PG-VLAN12 (INET)
|
||||||
|
PG-VLAN20 (AAL-INT)
|
||||||
|
|
||||||
|
What the script does:
|
||||||
|
- Creates the ES2025 Windows topology VMs as linked clones
|
||||||
|
- Sets each VM to 2 vCPU and 4 GB RAM
|
||||||
|
- Configures network adapters to match the topology
|
||||||
|
- Injects cloud-init userdata for DEV-PC only
|
||||||
|
- Windows VMs are snapshotted without powering on
|
||||||
|
- DEV-PC boots, runs cloud-init, powers off, then gets snapshotted
|
||||||
|
|
||||||
|
Run:
|
||||||
|
.\Create-VMs.ps1
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding()]
|
||||||
|
param()
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$DatacenterName = 'Ilica'
|
||||||
|
$SkillsFolderName = 'Skills'
|
||||||
|
$TargetParentFolderName = 'ES2025'
|
||||||
|
$TargetFolderName = 'Windows'
|
||||||
|
$TemplateFolderName = 'Templates'
|
||||||
|
$WinTemplateName = 'ws2022'
|
||||||
|
$LinuxTemplateName = 'debian-13-gui-template'
|
||||||
|
$ReferenceSnapshotName = 'Start'
|
||||||
|
$StartSnapshotName = 'Start'
|
||||||
|
$CpuCount = 2
|
||||||
|
$MemoryGB = 4
|
||||||
|
$DefaultUsername = 'user'
|
||||||
|
$DefaultPassword = 'Passw0rd!'
|
||||||
|
$CloudInitShutdownTimeoutSeconds = 900
|
||||||
|
|
||||||
|
function Get-SingleFolder {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Name,
|
||||||
|
|
||||||
|
$Location = $null
|
||||||
|
)
|
||||||
|
|
||||||
|
$params = @{ Name = $Name; Type = 'VM'; ErrorAction = 'SilentlyContinue' }
|
||||||
|
if ($Location) { $params['Location'] = $Location }
|
||||||
|
$folders = @(Get-Folder @params)
|
||||||
|
|
||||||
|
if ($folders.Count -eq 0) { throw "Folder '$Name' was not found." }
|
||||||
|
if ($folders.Count -gt 1) { throw "More than one folder named '$Name' was found." }
|
||||||
|
return $folders[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SingleChildFolder {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Name,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$ParentFolder
|
||||||
|
)
|
||||||
|
|
||||||
|
$folders = @(Get-Folder -Location $ParentFolder -Name $Name -Type VM -ErrorAction SilentlyContinue)
|
||||||
|
|
||||||
|
if ($folders.Count -eq 0) { throw "Folder '$Name' was not found under '$($ParentFolder.Name)'." }
|
||||||
|
if ($folders.Count -gt 1) { throw "More than one folder named '$Name' was found under '$($ParentFolder.Name)'." }
|
||||||
|
return $folders[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SingleVmFromFolder {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Name,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$Folder
|
||||||
|
)
|
||||||
|
|
||||||
|
$vms = @(Get-VM -Location $Folder -Name $Name -ErrorAction SilentlyContinue)
|
||||||
|
|
||||||
|
if ($vms.Count -eq 0) { throw "VM '$Name' was not found in folder '$($Folder.Name)'." }
|
||||||
|
if ($vms.Count -gt 1) { throw "More than one VM named '$Name' was found in folder '$($Folder.Name)'." }
|
||||||
|
return $vms[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-OrCreateReferenceSnapshot {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$VM,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$SnapshotName
|
||||||
|
)
|
||||||
|
|
||||||
|
$snapshot = Get-Snapshot -VM $VM -Name $SnapshotName -ErrorAction SilentlyContinue
|
||||||
|
if (-not $snapshot) {
|
||||||
|
Write-Host "Creating snapshot '$SnapshotName' on '$($VM.Name)'"
|
||||||
|
$snapshot = New-Snapshot -VM $VM -Name $SnapshotName -Description 'Base snapshot for linked clones' -Memory:$false -Quiesce:$false
|
||||||
|
}
|
||||||
|
return $snapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
function Set-ExactNetworkAdapters {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$VM,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string[]]$PortGroupNames
|
||||||
|
)
|
||||||
|
|
||||||
|
$adapters = @(Get-NetworkAdapter -VM $VM | Sort-Object Name)
|
||||||
|
|
||||||
|
while ($adapters.Count -gt $PortGroupNames.Count) {
|
||||||
|
Remove-NetworkAdapter -NetworkAdapter $adapters[-1] -Confirm:$false | Out-Null
|
||||||
|
$adapters = @(Get-NetworkAdapter -VM $VM | Sort-Object Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = 0; $i -lt $PortGroupNames.Count; $i++) {
|
||||||
|
if ($i -ge $adapters.Count) {
|
||||||
|
New-NetworkAdapter -VM $VM -Type Vmxnet3 -NetworkName $PortGroupNames[$i] -StartConnected:$true -Confirm:$false | Out-Null
|
||||||
|
$adapters = @(Get-NetworkAdapter -VM $VM | Sort-Object Name)
|
||||||
|
}
|
||||||
|
Set-NetworkAdapter -NetworkAdapter $adapters[$i] -NetworkName $PortGroupNames[$i] -StartConnected:$true -Confirm:$false | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Set-AdvancedSettingValue {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$Entity,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Name,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Value
|
||||||
|
)
|
||||||
|
|
||||||
|
$setting = Get-AdvancedSetting -Entity $Entity -Name $Name -ErrorAction SilentlyContinue
|
||||||
|
if ($setting) {
|
||||||
|
Set-AdvancedSetting -AdvancedSetting $setting -Value $Value -Confirm:$false | Out-Null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
New-AdvancedSetting -Entity $Entity -Name $Name -Value $Value -Confirm:$false | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Set-CloudInitUserData {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$VM,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Username,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Password
|
||||||
|
)
|
||||||
|
|
||||||
|
$userData = @"
|
||||||
|
#cloud-config
|
||||||
|
hostname: $($VM.Name)
|
||||||
|
users:
|
||||||
|
- default
|
||||||
|
- name: $Username
|
||||||
|
lock_passwd: false
|
||||||
|
plain_text_passwd: '$Password'
|
||||||
|
shell: /bin/bash
|
||||||
|
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||||
|
groups: sudo
|
||||||
|
ssh_pwauth: true
|
||||||
|
disable_root: false
|
||||||
|
chpasswd:
|
||||||
|
expire: false
|
||||||
|
list: |
|
||||||
|
root:$Password
|
||||||
|
${Username}:$Password
|
||||||
|
power_state:
|
||||||
|
mode: poweroff
|
||||||
|
timeout: 30
|
||||||
|
condition: true
|
||||||
|
"@
|
||||||
|
|
||||||
|
$encoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($userData))
|
||||||
|
Set-AdvancedSettingValue -Entity $VM -Name 'guestinfo.userdata' -Value $encoded
|
||||||
|
Set-AdvancedSettingValue -Entity $VM -Name 'guestinfo.userdata.encoding' -Value 'base64'
|
||||||
|
}
|
||||||
|
|
||||||
|
function Wait-ForVmToPowerOff {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$VM,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[int]$TimeoutSeconds
|
||||||
|
)
|
||||||
|
|
||||||
|
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
|
||||||
|
do {
|
||||||
|
if ((Get-VM -Id $VM.Id).PowerState -eq 'PoweredOff') { return }
|
||||||
|
Start-Sleep -Seconds 5
|
||||||
|
} while ((Get-Date) -lt $deadline)
|
||||||
|
|
||||||
|
throw "VM '$($VM.Name)' did not power off within $TimeoutSeconds seconds."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Folder and template lookup ────────────────────────────────────────────────
|
||||||
|
$datacenter = Get-Datacenter -Name $DatacenterName -ErrorAction Stop
|
||||||
|
$skillsFolder = Get-SingleFolder -Name $SkillsFolderName -Location $datacenter
|
||||||
|
$templateFolder = Get-SingleChildFolder -Name $TemplateFolderName -ParentFolder $skillsFolder
|
||||||
|
$targetParentFolder = Get-SingleChildFolder -Name $TargetParentFolderName -ParentFolder $skillsFolder
|
||||||
|
$targetFolder = Get-SingleChildFolder -Name $TargetFolderName -ParentFolder $targetParentFolder
|
||||||
|
|
||||||
|
$winTemplate = Get-SingleVmFromFolder -Name $WinTemplateName -Folder $templateFolder
|
||||||
|
$linuxTemplate = Get-SingleVmFromFolder -Name $LinuxTemplateName -Folder $templateFolder
|
||||||
|
|
||||||
|
$winSnapshot = Get-OrCreateReferenceSnapshot -VM $winTemplate -SnapshotName $ReferenceSnapshotName
|
||||||
|
$linuxSnapshot = Get-OrCreateReferenceSnapshot -VM $linuxTemplate -SnapshotName $ReferenceSnapshotName
|
||||||
|
|
||||||
|
# ── VM definitions ────────────────────────────────────────────────────────────
|
||||||
|
$vmDefinitions = @(
|
||||||
|
@{ Name = 'INET'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN12'); IsLinux = $false },
|
||||||
|
@{ Name = 'RTR-CPH'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN12', 'PG-VLAN10', 'PG-VLAN11'); IsLinux = $false },
|
||||||
|
@{ Name = 'RTR-AAL'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN12', 'PG-VLAN20'); IsLinux = $false },
|
||||||
|
@{ Name = 'DC'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN10'); IsLinux = $false },
|
||||||
|
@{ Name = 'SRV1'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN10'); IsLinux = $false },
|
||||||
|
@{ Name = 'SRV2'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN10'); IsLinux = $false },
|
||||||
|
@{ Name = 'RODC'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN20'); IsLinux = $false },
|
||||||
|
@{ Name = 'DEV-SRV'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN11'); IsLinux = $false },
|
||||||
|
@{ Name = 'DEV-PC'; Template = $linuxTemplate; Snapshot = $linuxSnapshot; Networks = @('PG-VLAN11'); IsLinux = $true },
|
||||||
|
@{ Name = 'CLIENT'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN20'); IsLinux = $false }
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($definition in $vmDefinitions) {
|
||||||
|
$vm = Get-VM -Name $definition.Name -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
if (-not $vm) {
|
||||||
|
Write-Host "Creating $($definition.Name)"
|
||||||
|
|
||||||
|
$resourcePool = Get-ResourcePool -Id $definition.Template.ExtensionData.ResourcePool
|
||||||
|
$vmHost = Get-VMHost -Id $definition.Template.ExtensionData.Runtime.Host
|
||||||
|
|
||||||
|
$vm = New-VM `
|
||||||
|
-Name $definition.Name `
|
||||||
|
-VM $definition.Template `
|
||||||
|
-ReferenceSnapshot $definition.Snapshot `
|
||||||
|
-LinkedClone `
|
||||||
|
-Location $targetFolder `
|
||||||
|
-ResourcePool $resourcePool `
|
||||||
|
-VMHost $vmHost
|
||||||
|
} else {
|
||||||
|
Write-Host "$($definition.Name) already exists, updating configuration"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-VM -VM $vm -NumCpu $CpuCount -MemoryGB $MemoryGB -Confirm:$false | Out-Null
|
||||||
|
Set-ExactNetworkAdapters -VM $vm -PortGroupNames $definition.Networks
|
||||||
|
|
||||||
|
$templateCdroms = @(Get-CDDrive -VM $definition.Template | Sort-Object Name)
|
||||||
|
$vmCdroms = @(Get-CDDrive -VM $vm | Sort-Object Name)
|
||||||
|
for ($i = 0; $i -lt $templateCdroms.Count; $i++) {
|
||||||
|
$vmCdroms[$i] | Set-CDDrive -IsoPath $templateCdroms[$i].IsoPath -StartConnected $true -Confirm:$false | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($definition.IsLinux) {
|
||||||
|
Set-CloudInitUserData -VM $vm -Username $DefaultUsername -Password $DefaultPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
$startSnapshot = Get-Snapshot -VM $vm -Name $StartSnapshotName -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
if (-not $startSnapshot) {
|
||||||
|
if ($definition.IsLinux) {
|
||||||
|
if ((Get-VM -Id $vm.Id).PowerState -eq 'PoweredOn') {
|
||||||
|
Stop-VMGuest -VM $vm -Confirm:$false -ErrorAction SilentlyContinue | Out-Null
|
||||||
|
Wait-ForVmToPowerOff -VM $vm -TimeoutSeconds $CloudInitShutdownTimeoutSeconds
|
||||||
|
}
|
||||||
|
Start-VM -VM $vm | Out-Null
|
||||||
|
Wait-ForVmToPowerOff -VM $vm -TimeoutSeconds $CloudInitShutdownTimeoutSeconds
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Snapshot -VM $vm -Name $StartSnapshotName -Description 'Initial state' -Memory:$false -Quiesce:$false | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "$($definition.Name) ready"
|
||||||
|
}
|
||||||
@@ -7,10 +7,9 @@ $ServerTemplateName Template VM used for server clones.
|
|||||||
$GuiTemplateName Template VM used for the workstation clone.
|
$GuiTemplateName Template VM used for the workstation clone.
|
||||||
|
|
||||||
This script expects these port groups to already exist:
|
This script expects these port groups to already exist:
|
||||||
WAN-Linux
|
PG-VLAN10 (WAN)
|
||||||
INT-Linux
|
PG-VLAN11 (INT)
|
||||||
DMZ-Linux
|
PG-VLAN12 (DMZ)
|
||||||
VPN-Linux
|
|
||||||
|
|
||||||
What the script does:
|
What the script does:
|
||||||
- Creates the WS2024 Linux topology VMs as linked clones
|
- Creates the WS2024 Linux topology VMs as linked clones
|
||||||
@@ -29,8 +28,10 @@ param()
|
|||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$DatacenterName = 'Ilica'
|
||||||
$TemplateFolderName = 'Templates'
|
$TemplateFolderName = 'Templates'
|
||||||
$TargetParentFolderName = 'Worldskills 2024'
|
$SkillsFolderName = 'Skills'
|
||||||
|
$TargetParentFolderName = 'WS2024'
|
||||||
$TargetFolderName = 'Linux'
|
$TargetFolderName = 'Linux'
|
||||||
$ServerTemplateName = 'debian-13-template'
|
$ServerTemplateName = 'debian-13-template'
|
||||||
$GuiTemplateName = 'debian-13-gui-template'
|
$GuiTemplateName = 'debian-13-gui-template'
|
||||||
@@ -47,10 +48,14 @@ $CloudInitShutdownTimeoutSeconds = 900
|
|||||||
function Get-SingleFolder {
|
function Get-SingleFolder {
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$Name
|
[string]$Name,
|
||||||
|
|
||||||
|
$Location = $null
|
||||||
)
|
)
|
||||||
|
|
||||||
$folders = @(Get-Folder -Name $Name -ErrorAction SilentlyContinue)
|
$params = @{ Name = $Name; Type = 'VM'; ErrorAction = 'SilentlyContinue' }
|
||||||
|
if ($Location) { $params['Location'] = $Location }
|
||||||
|
$folders = @(Get-Folder @params)
|
||||||
|
|
||||||
if ($folders.Count -eq 0) {
|
if ($folders.Count -eq 0) {
|
||||||
throw "Folder '$Name' was not found."
|
throw "Folder '$Name' was not found."
|
||||||
@@ -72,7 +77,7 @@ function Get-SingleChildFolder {
|
|||||||
$ParentFolder
|
$ParentFolder
|
||||||
)
|
)
|
||||||
|
|
||||||
$folders = @(Get-Folder -Location $ParentFolder -Name $Name -ErrorAction SilentlyContinue)
|
$folders = @(Get-Folder -Location $ParentFolder -Name $Name -Type VM -ErrorAction SilentlyContinue)
|
||||||
|
|
||||||
if ($folders.Count -eq 0) {
|
if ($folders.Count -eq 0) {
|
||||||
throw "Folder '$Name' was not found under '$($ParentFolder.Name)'."
|
throw "Folder '$Name' was not found under '$($ParentFolder.Name)'."
|
||||||
@@ -205,7 +210,9 @@ function New-CloudInitUserData {
|
|||||||
[string]$Username,
|
[string]$Username,
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$Password
|
[string]$Password,
|
||||||
|
|
||||||
|
[string]$NetworkConfig = ''
|
||||||
)
|
)
|
||||||
|
|
||||||
return @"
|
return @"
|
||||||
@@ -242,14 +249,49 @@ function Set-CloudInitUserData {
|
|||||||
[string]$Username,
|
[string]$Username,
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$Password
|
[string]$Password,
|
||||||
|
|
||||||
|
[string]$NetworkConfig = ''
|
||||||
)
|
)
|
||||||
|
|
||||||
$userData = New-CloudInitUserData -VmName $VM.Name -Username $Username -Password $Password
|
$userData = New-CloudInitUserData -VmName $VM.Name -Username $Username -Password $Password
|
||||||
$encodedUserData = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($userData))
|
$encodedUserData = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($userData))
|
||||||
|
|
||||||
Set-AdvancedSettingValue -Entity $VM -Name 'guestinfo.userdata' -Value $encodedUserData
|
Set-AdvancedSettingValue -Entity $VM -Name 'guestinfo.userdata' -Value $encodedUserData
|
||||||
Set-AdvancedSettingValue -Entity $VM -Name 'guestinfo.userdata.encoding' -Value 'base64'
|
Set-AdvancedSettingValue -Entity $VM -Name 'guestinfo.userdata.encoding' -Value 'base64'
|
||||||
|
|
||||||
|
if ($NetworkConfig) {
|
||||||
|
$metadata = @"
|
||||||
|
instance-id: $($VM.Name)
|
||||||
|
local-hostname: $($VM.Name)
|
||||||
|
$NetworkConfig
|
||||||
|
"@
|
||||||
|
$encodedMetadata = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($metadata))
|
||||||
|
Set-AdvancedSettingValue -Entity $VM -Name 'guestinfo.metadata' -Value $encodedMetadata
|
||||||
|
Set-AdvancedSettingValue -Entity $VM -Name 'guestinfo.metadata.encoding' -Value 'base64'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function New-NetConfig {
|
||||||
|
param([hashtable[]]$Nics)
|
||||||
|
|
||||||
|
$yaml = "network:`n version: 2`n ethernets:"
|
||||||
|
|
||||||
|
foreach ($nic in $Nics) {
|
||||||
|
$addrs = @($nic.Ipv4, $nic.Ipv6) | Where-Object { $_ } | ForEach-Object { "`"$_`"" }
|
||||||
|
$yaml += "`n $($nic.Name):"
|
||||||
|
$yaml += "`n addresses: [$($addrs -join ', ')]"
|
||||||
|
|
||||||
|
$routes = @()
|
||||||
|
if ($nic.Gw4) { $routes += "- to: default`n via: $($nic.Gw4)" }
|
||||||
|
if ($nic.Gw6) { $routes += "- to: `"::/0`"`n via: `"$($nic.Gw6)`"" }
|
||||||
|
if ($routes) {
|
||||||
|
$yaml += "`n routes:"
|
||||||
|
$routes | ForEach-Object { $yaml += "`n $_" }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $yaml
|
||||||
}
|
}
|
||||||
|
|
||||||
function Wait-ForVmToPowerOff {
|
function Wait-ForVmToPowerOff {
|
||||||
@@ -276,8 +318,10 @@ function Wait-ForVmToPowerOff {
|
|||||||
throw "VM '$($VM.Name)' did not power off within $TimeoutSeconds seconds."
|
throw "VM '$($VM.Name)' did not power off within $TimeoutSeconds seconds."
|
||||||
}
|
}
|
||||||
|
|
||||||
$templateFolder = Get-SingleFolder -Name $TemplateFolderName
|
$datacenter = Get-Datacenter -Name $DatacenterName -ErrorAction Stop
|
||||||
$targetParentFolder = Get-SingleFolder -Name $TargetParentFolderName
|
$skillsFolder = Get-SingleFolder -Name $SkillsFolderName -Location $datacenter
|
||||||
|
$templateFolder = Get-SingleChildFolder -Name $TemplateFolderName -ParentFolder $skillsFolder
|
||||||
|
$targetParentFolder = Get-SingleChildFolder -Name $TargetParentFolderName -ParentFolder $skillsFolder
|
||||||
$targetFolder = Get-SingleChildFolder -Name $TargetFolderName -ParentFolder $targetParentFolder
|
$targetFolder = Get-SingleChildFolder -Name $TargetFolderName -ParentFolder $targetParentFolder
|
||||||
|
|
||||||
$serverTemplate = Get-SingleVmFromFolder -Name $ServerTemplateName -Folder $templateFolder
|
$serverTemplate = Get-SingleVmFromFolder -Name $ServerTemplateName -Folder $templateFolder
|
||||||
@@ -286,15 +330,21 @@ $guiTemplate = Get-SingleVmFromFolder -Name $GuiTemplateName -Folder $templateFo
|
|||||||
$serverReferenceSnapshot = Get-OrCreateReferenceSnapshot -VM $serverTemplate -SnapshotName $ReferenceSnapshotName
|
$serverReferenceSnapshot = Get-OrCreateReferenceSnapshot -VM $serverTemplate -SnapshotName $ReferenceSnapshotName
|
||||||
$guiReferenceSnapshot = Get-OrCreateReferenceSnapshot -VM $guiTemplate -SnapshotName $ReferenceSnapshotName
|
$guiReferenceSnapshot = Get-OrCreateReferenceSnapshot -VM $guiTemplate -SnapshotName $ReferenceSnapshotName
|
||||||
|
|
||||||
|
# ── Network segment variables ─────────────────────────────────────────────────
|
||||||
|
$WanGw4 = '1.1.1.10'; $WanGw6 = '2001:db8:1111::10'
|
||||||
|
$IntGw4 = '10.1.10.1'; $IntGw6 = '2001:db8:1001:10::1'
|
||||||
|
$DmzGw4 = '10.1.20.1'; $DmzGw6 = '2001:db8:1001:20::1'
|
||||||
|
|
||||||
|
# ── VM definitions ────────────────────────────────────────────────────────────
|
||||||
$vmDefinitions = @(
|
$vmDefinitions = @(
|
||||||
@{ Name = 'fw'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('WAN-Linux', 'INT-Linux', 'DMZ-Linux', 'VPN-Linux') },
|
@{ Name = 'fw'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN10', 'PG-VLAN11', 'PG-VLAN12') },
|
||||||
@{ Name = 'int-srv01'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('INT-Linux') },
|
@{ Name = 'int-srv01'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN11') },
|
||||||
@{ Name = 'mail'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('DMZ-Linux') },
|
@{ Name = 'mail'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN12') },
|
||||||
@{ Name = 'ha-prx01'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('DMZ-Linux') },
|
@{ Name = 'ha-prx01'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('PG-VLAN12') },
|
||||||
@{ Name = 'ha-prx02'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('DMZ-Linux') },
|
@{ Name = 'ha-prx02'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN12') },
|
||||||
@{ Name = 'web01'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('DMZ-Linux') },
|
@{ Name = 'web01'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN12') },
|
||||||
@{ Name = 'web02'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('DMZ-Linux') },
|
@{ Name = 'web02'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('PG-VLAN12') },
|
||||||
@{ Name = 'jamie-ws01'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('WAN-Linux', 'VPN-Linux') }
|
@{ Name = 'jamie-ws01'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('PG-VLAN10') }
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach ($definition in $vmDefinitions) {
|
foreach ($definition in $vmDefinitions) {
|
||||||
@@ -323,7 +373,12 @@ foreach ($definition in $vmDefinitions) {
|
|||||||
|
|
||||||
Ensure-ExtraDisks -VM $vm -DiskCount $ExtraDiskCount -DiskSizeGB $ExtraDiskSizeGB
|
Ensure-ExtraDisks -VM $vm -DiskCount $ExtraDiskCount -DiskSizeGB $ExtraDiskSizeGB
|
||||||
Set-ExactNetworkAdapters -VM $vm -PortGroupNames $definition.Networks
|
Set-ExactNetworkAdapters -VM $vm -PortGroupNames $definition.Networks
|
||||||
Set-CloudInitUserData -VM $vm -Username $DefaultUsername -Password $DefaultPassword
|
Set-CloudInitUserData -VM $vm -Username $DefaultUsername -Password $DefaultPassword -NetworkConfig $definition.NetworkConfig
|
||||||
|
$templateCdroms = @(Get-CDDrive -VM $definition.Template | Sort-Object Name)
|
||||||
|
$vmCdroms = @(Get-CDDrive -VM $vm | Sort-Object Name)
|
||||||
|
for ($i = 0; $i -lt $templateCdroms.Count; $i++) {
|
||||||
|
$vmCdroms[$i] | Set-CDDrive -IsoPath $templateCdroms[$i].IsoPath -StartConnected $true -Confirm:$false | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
$startSnapshot = Get-Snapshot -VM $vm -Name $StartSnapshotName -ErrorAction SilentlyContinue
|
$startSnapshot = Get-Snapshot -VM $vm -Name $StartSnapshotName -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
|||||||
293
ws2024/windows/Create-VMs.ps1
Normal file
293
ws2024/windows/Create-VMs.ps1
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
<#
|
||||||
|
Adjust these values only if your vCenter names are different:
|
||||||
|
$WinTemplateName Template VM used for all Windows Server VMs.
|
||||||
|
$LinuxTemplateName Template VM used for ANSIBLE-SRV (Debian 13 GUI).
|
||||||
|
|
||||||
|
This script expects these port groups to already exist:
|
||||||
|
PG-VLAN10 (Paris-Core)
|
||||||
|
PG-VLAN11 (Paris-Dist)
|
||||||
|
PG-VLAN12 (Paris-Client)
|
||||||
|
PG-VLAN20 (LA-Paris)
|
||||||
|
PG-VLAN21 (LA-Lyon)
|
||||||
|
PG-VLAN23 (Lyon-Remote)
|
||||||
|
|
||||||
|
What the script does:
|
||||||
|
- Creates the WS2024 Windows topology VMs as linked clones
|
||||||
|
- Sets each VM to 2 vCPU and 4 GB RAM
|
||||||
|
- Configures network adapters to match the topology
|
||||||
|
- Injects cloud-init userdata for ANSIBLE-SRV only
|
||||||
|
- Powers on the VM, waits for shutdown, then creates a snapshot named Start
|
||||||
|
|
||||||
|
Run:
|
||||||
|
.\Create-VMs.ps1
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding()]
|
||||||
|
param()
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$DatacenterName = 'Ilica'
|
||||||
|
$SkillsFolderName = 'Skills'
|
||||||
|
$TargetParentFolderName = 'WS2024'
|
||||||
|
$TargetFolderName = 'Windows'
|
||||||
|
$TemplateFolderName = 'Templates'
|
||||||
|
$WinTemplateName = 'ws2022'
|
||||||
|
$LinuxTemplateName = 'debian-13-gui-template'
|
||||||
|
$ReferenceSnapshotName = 'Start'
|
||||||
|
$StartSnapshotName = 'Start'
|
||||||
|
$CpuCount = 2
|
||||||
|
$MemoryGB = 4
|
||||||
|
$DefaultUsername = 'user'
|
||||||
|
$DefaultPassword = 'Skill39@Lyon'
|
||||||
|
$CloudInitShutdownTimeoutSeconds = 900
|
||||||
|
|
||||||
|
function Get-SingleFolder {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Name,
|
||||||
|
|
||||||
|
$Location = $null
|
||||||
|
)
|
||||||
|
|
||||||
|
$params = @{ Name = $Name; Type = 'VM'; ErrorAction = 'SilentlyContinue' }
|
||||||
|
if ($Location) { $params['Location'] = $Location }
|
||||||
|
$folders = @(Get-Folder @params)
|
||||||
|
|
||||||
|
if ($folders.Count -eq 0) { throw "Folder '$Name' was not found." }
|
||||||
|
if ($folders.Count -gt 1) { throw "More than one folder named '$Name' was found." }
|
||||||
|
return $folders[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SingleChildFolder {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Name,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$ParentFolder
|
||||||
|
)
|
||||||
|
|
||||||
|
$folders = @(Get-Folder -Location $ParentFolder -Name $Name -Type VM -ErrorAction SilentlyContinue)
|
||||||
|
|
||||||
|
if ($folders.Count -eq 0) { throw "Folder '$Name' was not found under '$($ParentFolder.Name)'." }
|
||||||
|
if ($folders.Count -gt 1) { throw "More than one folder named '$Name' was found under '$($ParentFolder.Name)'." }
|
||||||
|
return $folders[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SingleVmFromFolder {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Name,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$Folder
|
||||||
|
)
|
||||||
|
|
||||||
|
$vms = @(Get-VM -Location $Folder -Name $Name -ErrorAction SilentlyContinue)
|
||||||
|
|
||||||
|
if ($vms.Count -eq 0) { throw "VM '$Name' was not found in folder '$($Folder.Name)'." }
|
||||||
|
if ($vms.Count -gt 1) { throw "More than one VM named '$Name' was found in folder '$($Folder.Name)'." }
|
||||||
|
return $vms[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-OrCreateReferenceSnapshot {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$VM,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$SnapshotName
|
||||||
|
)
|
||||||
|
|
||||||
|
$snapshot = Get-Snapshot -VM $VM -Name $SnapshotName -ErrorAction SilentlyContinue
|
||||||
|
if (-not $snapshot) {
|
||||||
|
Write-Host "Creating snapshot '$SnapshotName' on '$($VM.Name)'"
|
||||||
|
$snapshot = New-Snapshot -VM $VM -Name $SnapshotName -Description 'Base snapshot for linked clones' -Memory:$false -Quiesce:$false
|
||||||
|
}
|
||||||
|
return $snapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
function Set-ExactNetworkAdapters {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$VM,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string[]]$PortGroupNames
|
||||||
|
)
|
||||||
|
|
||||||
|
$adapters = @(Get-NetworkAdapter -VM $VM | Sort-Object Name)
|
||||||
|
|
||||||
|
while ($adapters.Count -gt $PortGroupNames.Count) {
|
||||||
|
Remove-NetworkAdapter -NetworkAdapter $adapters[-1] -Confirm:$false | Out-Null
|
||||||
|
$adapters = @(Get-NetworkAdapter -VM $VM | Sort-Object Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = 0; $i -lt $PortGroupNames.Count; $i++) {
|
||||||
|
if ($i -ge $adapters.Count) {
|
||||||
|
New-NetworkAdapter -VM $VM -Type Vmxnet3 -NetworkName $PortGroupNames[$i] -StartConnected:$true -Confirm:$false | Out-Null
|
||||||
|
$adapters = @(Get-NetworkAdapter -VM $VM | Sort-Object Name)
|
||||||
|
}
|
||||||
|
Set-NetworkAdapter -NetworkAdapter $adapters[$i] -NetworkName $PortGroupNames[$i] -StartConnected:$true -Confirm:$false | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Set-AdvancedSettingValue {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$Entity,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Name,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Value
|
||||||
|
)
|
||||||
|
|
||||||
|
$setting = Get-AdvancedSetting -Entity $Entity -Name $Name -ErrorAction SilentlyContinue
|
||||||
|
if ($setting) {
|
||||||
|
Set-AdvancedSetting -AdvancedSetting $setting -Value $Value -Confirm:$false | Out-Null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
New-AdvancedSetting -Entity $Entity -Name $Name -Value $Value -Confirm:$false | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Set-CloudInitUserData {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$VM,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Username,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Password
|
||||||
|
)
|
||||||
|
|
||||||
|
$userData = @"
|
||||||
|
#cloud-config
|
||||||
|
hostname: $($VM.Name)
|
||||||
|
users:
|
||||||
|
- default
|
||||||
|
- name: $Username
|
||||||
|
lock_passwd: false
|
||||||
|
plain_text_passwd: '$Password'
|
||||||
|
shell: /bin/bash
|
||||||
|
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||||
|
groups: sudo
|
||||||
|
ssh_pwauth: true
|
||||||
|
disable_root: false
|
||||||
|
chpasswd:
|
||||||
|
expire: false
|
||||||
|
list: |
|
||||||
|
root:$Password
|
||||||
|
${Username}:$Password
|
||||||
|
power_state:
|
||||||
|
mode: poweroff
|
||||||
|
timeout: 30
|
||||||
|
condition: true
|
||||||
|
"@
|
||||||
|
|
||||||
|
$encoded = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($userData))
|
||||||
|
Set-AdvancedSettingValue -Entity $VM -Name 'guestinfo.userdata' -Value $encoded
|
||||||
|
Set-AdvancedSettingValue -Entity $VM -Name 'guestinfo.userdata.encoding' -Value 'base64'
|
||||||
|
}
|
||||||
|
|
||||||
|
function Wait-ForVmToPowerOff {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
$VM,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[int]$TimeoutSeconds
|
||||||
|
)
|
||||||
|
|
||||||
|
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
|
||||||
|
do {
|
||||||
|
if ((Get-VM -Id $VM.Id).PowerState -eq 'PoweredOff') { return }
|
||||||
|
Start-Sleep -Seconds 5
|
||||||
|
} while ((Get-Date) -lt $deadline)
|
||||||
|
|
||||||
|
throw "VM '$($VM.Name)' did not power off within $TimeoutSeconds seconds."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Folder and template lookup ────────────────────────────────────────────────
|
||||||
|
$datacenter = Get-Datacenter -Name $DatacenterName -ErrorAction Stop
|
||||||
|
$skillsFolder = Get-SingleFolder -Name $SkillsFolderName -Location $datacenter
|
||||||
|
$templateFolder = Get-SingleChildFolder -Name $TemplateFolderName -ParentFolder $skillsFolder
|
||||||
|
$targetParentFolder = Get-SingleChildFolder -Name $TargetParentFolderName -ParentFolder $skillsFolder
|
||||||
|
$targetFolder = Get-SingleChildFolder -Name $TargetFolderName -ParentFolder $targetParentFolder
|
||||||
|
|
||||||
|
$winTemplate = Get-SingleVmFromFolder -Name $WinTemplateName -Folder $templateFolder
|
||||||
|
$linuxTemplate = Get-SingleVmFromFolder -Name $LinuxTemplateName -Folder $templateFolder
|
||||||
|
|
||||||
|
$winSnapshot = Get-OrCreateReferenceSnapshot -VM $winTemplate -SnapshotName $ReferenceSnapshotName
|
||||||
|
$linuxSnapshot = Get-OrCreateReferenceSnapshot -VM $linuxTemplate -SnapshotName $ReferenceSnapshotName
|
||||||
|
|
||||||
|
# ── VM definitions ────────────────────────────────────────────────────────────
|
||||||
|
$vmDefinitions = @(
|
||||||
|
@{ Name = 'DC1'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN10'); IsLinux = $false },
|
||||||
|
@{ Name = 'NW-SRV'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN10'); IsLinux = $false },
|
||||||
|
@{ Name = 'FILE-SRV'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN11'); IsLinux = $false },
|
||||||
|
@{ Name = 'WEB-SRV'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN11'); IsLinux = $false },
|
||||||
|
@{ Name = 'PARIS-ROUTER'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN10', 'PG-VLAN11', 'PG-VLAN12', 'PG-VLAN20'); IsLinux = $false },
|
||||||
|
@{ Name = 'LA-ROUTER'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN20', 'PG-VLAN21'); IsLinux = $false },
|
||||||
|
@{ Name = 'LYON-ROUTER'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN21', 'PG-VLAN23'); IsLinux = $false },
|
||||||
|
@{ Name = 'DC2'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN23'); IsLinux = $false },
|
||||||
|
@{ Name = 'WIN-CLIENT1'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN12'); IsLinux = $false },
|
||||||
|
@{ Name = 'WIN-CLIENT2'; Template = $winTemplate; Snapshot = $winSnapshot; Networks = @('PG-VLAN23'); IsLinux = $false },
|
||||||
|
@{ Name = 'ANSIBLE-SRV'; Template = $linuxTemplate; Snapshot = $linuxSnapshot; Networks = @('PG-VLAN12'); IsLinux = $true }
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($definition in $vmDefinitions) {
|
||||||
|
$vm = Get-VM -Name $definition.Name -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
if (-not $vm) {
|
||||||
|
Write-Host "Creating $($definition.Name)"
|
||||||
|
|
||||||
|
$resourcePool = Get-ResourcePool -Id $definition.Template.ExtensionData.ResourcePool
|
||||||
|
$vmHost = Get-VMHost -Id $definition.Template.ExtensionData.Runtime.Host
|
||||||
|
|
||||||
|
$vm = New-VM `
|
||||||
|
-Name $definition.Name `
|
||||||
|
-VM $definition.Template `
|
||||||
|
-ReferenceSnapshot $definition.Snapshot `
|
||||||
|
-LinkedClone `
|
||||||
|
-Location $targetFolder `
|
||||||
|
-ResourcePool $resourcePool `
|
||||||
|
-VMHost $vmHost
|
||||||
|
} else {
|
||||||
|
Write-Host "$($definition.Name) already exists, updating configuration"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-VM -VM $vm -NumCpu $CpuCount -MemoryGB $MemoryGB -Confirm:$false | Out-Null
|
||||||
|
Set-ExactNetworkAdapters -VM $vm -PortGroupNames $definition.Networks
|
||||||
|
|
||||||
|
$templateCdroms = @(Get-CDDrive -VM $definition.Template | Sort-Object Name)
|
||||||
|
$vmCdroms = @(Get-CDDrive -VM $vm | Sort-Object Name)
|
||||||
|
for ($i = 0; $i -lt $templateCdroms.Count; $i++) {
|
||||||
|
$vmCdroms[$i] | Set-CDDrive -IsoPath $templateCdroms[$i].IsoPath -StartConnected $true -Confirm:$false | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($definition.IsLinux) {
|
||||||
|
Set-CloudInitUserData -VM $vm -Username $DefaultUsername -Password $DefaultPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
$startSnapshot = Get-Snapshot -VM $vm -Name $StartSnapshotName -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
if (-not $startSnapshot) {
|
||||||
|
if ($definition.IsLinux) {
|
||||||
|
if ((Get-VM -Id $vm.Id).PowerState -eq 'PoweredOn') {
|
||||||
|
Stop-VMGuest -VM $vm -Confirm:$false -ErrorAction SilentlyContinue | Out-Null
|
||||||
|
Wait-ForVmToPowerOff -VM $vm -TimeoutSeconds $CloudInitShutdownTimeoutSeconds
|
||||||
|
}
|
||||||
|
Start-VM -VM $vm | Out-Null
|
||||||
|
Wait-ForVmToPowerOff -VM $vm -TimeoutSeconds $CloudInitShutdownTimeoutSeconds
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Snapshot -VM $vm -Name $StartSnapshotName -Description 'Initial state' -Memory:$false -Quiesce:$false | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "$($definition.Name) ready"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user