Add unfinished scripts for building Linux topology
This commit is contained in:
61
es2025/linux/Create-PortGroups.ps1
Normal file
61
es2025/linux/Create-PortGroups.ps1
Normal file
@@ -0,0 +1,61 @@
|
||||
<#
|
||||
Required parameter:
|
||||
-SwitchName Name of the existing standard vSwitch where the port groups will be created.
|
||||
|
||||
Optional parameter:
|
||||
-BaseVlanId Starting VLAN ID. Default is 100.
|
||||
The script creates:
|
||||
HQ-SERVER-Linux = BaseVlanId
|
||||
HQ-DMZ-Linux = BaseVlanId + 1
|
||||
HQ-CLIENT-Linux = BaseVlanId + 2
|
||||
INET-HQ-Linux = BaseVlanId + 3
|
||||
INET-BRANCH-Linux = BaseVlanId + 4
|
||||
HOME-Linux = BaseVlanId + 5
|
||||
BR-SERVER-Linux = BaseVlanId + 6
|
||||
BR-CLIENT-Linux = BaseVlanId + 7
|
||||
|
||||
Example:
|
||||
.\Create-PortGroups.ps1 -SwitchName vSwitch0
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$SwitchName,
|
||||
|
||||
[ValidateRange(1, 4087)]
|
||||
[int]$BaseVlanId = 100
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$switch = Get-VirtualSwitch -Name $SwitchName -ErrorAction Stop
|
||||
|
||||
$portGroups = @(
|
||||
@{ Name = 'HQ-SERVER-Linux'; VlanId = $BaseVlanId },
|
||||
@{ Name = 'HQ-DMZ-Linux'; VlanId = $BaseVlanId + 1 },
|
||||
@{ Name = 'HQ-CLIENT-Linux'; VlanId = $BaseVlanId + 2 },
|
||||
@{ Name = 'INET-HQ-Linux'; VlanId = $BaseVlanId + 3 },
|
||||
@{ Name = 'INET-BRANCH-Linux'; VlanId = $BaseVlanId + 4 },
|
||||
@{ Name = 'HOME-Linux'; VlanId = $BaseVlanId + 5 },
|
||||
@{ Name = 'BR-SERVER-Linux'; VlanId = $BaseVlanId + 6 },
|
||||
@{ Name = 'BR-CLIENT-Linux'; VlanId = $BaseVlanId + 7 }
|
||||
)
|
||||
|
||||
foreach ($portGroup in $portGroups) {
|
||||
$existing = Get-VirtualPortGroup -VirtualSwitch $switch -Name $portGroup.Name -ErrorAction SilentlyContinue
|
||||
|
||||
if ($existing) {
|
||||
if ($existing.VLanId -ne $portGroup.VlanId) {
|
||||
Set-VirtualPortGroup -VirtualPortGroup $existing -VLanId $portGroup.VlanId | Out-Null
|
||||
Write-Host "Updated $($portGroup.Name) VLAN to $($portGroup.VlanId)"
|
||||
}
|
||||
else {
|
||||
Write-Host "$($portGroup.Name) already exists"
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
New-VirtualPortGroup -VirtualSwitch $switch -Name $portGroup.Name -VLanId $portGroup.VlanId | Out-Null
|
||||
Write-Host "Created $($portGroup.Name) with VLAN $($portGroup.VlanId)"
|
||||
}
|
||||
350
es2025/linux/Create-VMs.ps1
Normal file
350
es2025/linux/Create-VMs.ps1
Normal file
@@ -0,0 +1,350 @@
|
||||
<#
|
||||
Adjust these values only if your vCenter names are different:
|
||||
$TemplateFolderName Folder that contains the source template VMs.
|
||||
$TargetParentFolderName Parent folder that contains the target folder.
|
||||
$TargetFolderName Folder where the linked clones will be created.
|
||||
$ServerTemplateName Template VM used for server and router clones.
|
||||
$GuiTemplateName Template VM used for workstation clones.
|
||||
|
||||
This script expects these port groups to already exist:
|
||||
HQ-SERVER-Linux
|
||||
HQ-DMZ-Linux
|
||||
HQ-CLIENT-Linux
|
||||
INET-HQ-Linux
|
||||
INET-BRANCH-Linux
|
||||
HOME-Linux
|
||||
BR-SERVER-Linux
|
||||
BR-CLIENT-Linux
|
||||
|
||||
What the script does:
|
||||
- Creates the ES2025 Linux topology VMs as linked clones
|
||||
- Sets each VM to 2 vCPU and 4 GB RAM
|
||||
- Adds 4 extra 10 GB thin disks
|
||||
- Configures network adapters to match the topology
|
||||
- Injects cloud-init userdata for root and user
|
||||
- Powers on the VM, waits for cloud-init shutdown, then creates a snapshot named start
|
||||
|
||||
Run:
|
||||
.\Create-VMs.ps1
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$TemplateFolderName = 'Templates'
|
||||
$TargetParentFolderName = 'Euroskills 2025'
|
||||
$TargetFolderName = 'Linux'
|
||||
$ServerTemplateName = 'debian-13-template'
|
||||
$GuiTemplateName = 'debian-13-gui-template'
|
||||
$ReferenceSnapshotName = 'start'
|
||||
$StartSnapshotName = 'start'
|
||||
$CpuCount = 2
|
||||
$MemoryGB = 4
|
||||
$ExtraDiskCount = 4
|
||||
$ExtraDiskSizeGB = 10
|
||||
$DefaultUsername = 'localadmin'
|
||||
$DefaultPassword = 'Passw0rd!'
|
||||
$CloudInitShutdownTimeoutSeconds = 900
|
||||
|
||||
function Get-SingleFolder {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name
|
||||
)
|
||||
|
||||
$folders = @(Get-Folder -Name $Name -ErrorAction SilentlyContinue)
|
||||
|
||||
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. Make the name unique before running this script."
|
||||
}
|
||||
|
||||
return $folders[0]
|
||||
}
|
||||
|
||||
function Get-SingleChildFolder {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
$ParentFolder
|
||||
)
|
||||
|
||||
$folders = @(Get-Folder -Location $ParentFolder -Name $Name -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 source VM '$($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) {
|
||||
$adapterToRemove = $adapters[-1]
|
||||
Remove-NetworkAdapter -NetworkAdapter $adapterToRemove -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 Ensure-ExtraDisks {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$VM,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]$DiskCount,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]$DiskSizeGB
|
||||
)
|
||||
|
||||
$hardDisks = @(Get-HardDisk -VM $VM | Sort-Object Name)
|
||||
$extraDisksNeeded = $DiskCount - ($hardDisks.Count - 1)
|
||||
|
||||
while ($extraDisksNeeded -gt 0) {
|
||||
New-HardDisk -VM $VM -CapacityGB $DiskSizeGB -StorageFormat Thin | Out-Null
|
||||
$extraDisksNeeded--
|
||||
}
|
||||
}
|
||||
|
||||
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 New-CloudInitUserData {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$VmName,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Username,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Password
|
||||
)
|
||||
|
||||
return @"
|
||||
#cloud-config
|
||||
hostname: $VmName
|
||||
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
|
||||
"@
|
||||
}
|
||||
|
||||
function Set-CloudInitUserData {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$VM,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Username,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Password
|
||||
)
|
||||
|
||||
$userData = New-CloudInitUserData -VmName $VM.Name -Username $Username -Password $Password
|
||||
$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.encoding' -Value 'base64'
|
||||
}
|
||||
|
||||
function Wait-ForVmToPowerOff {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$VM,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]$TimeoutSeconds
|
||||
)
|
||||
|
||||
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
|
||||
|
||||
do {
|
||||
$currentVm = Get-VM -Id $VM.Id
|
||||
|
||||
if ($currentVm.PowerState -eq 'PoweredOff') {
|
||||
return
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds 5
|
||||
} while ((Get-Date) -lt $deadline)
|
||||
|
||||
throw "VM '$($VM.Name)' did not power off within $TimeoutSeconds seconds."
|
||||
}
|
||||
|
||||
$templateFolder = Get-SingleFolder -Name $TemplateFolderName
|
||||
$targetParentFolder = Get-SingleFolder -Name $TargetParentFolderName
|
||||
$targetFolder = Get-SingleChildFolder -Name $TargetFolderName -ParentFolder $targetParentFolder
|
||||
|
||||
$serverTemplate = Get-SingleVmFromFolder -Name $ServerTemplateName -Folder $templateFolder
|
||||
$guiTemplate = Get-SingleVmFromFolder -Name $GuiTemplateName -Folder $templateFolder
|
||||
|
||||
$serverReferenceSnapshot = Get-OrCreateReferenceSnapshot -VM $serverTemplate -SnapshotName $ReferenceSnapshotName
|
||||
$guiReferenceSnapshot = Get-OrCreateReferenceSnapshot -VM $guiTemplate -SnapshotName $ReferenceSnapshotName
|
||||
|
||||
$vmDefinitions = @(
|
||||
@{ Name = 'R-HQ'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('HQ-SERVER-Linux', 'HQ-DMZ-Linux', 'HQ-CLIENT-Linux', 'INET-HQ-Linux') },
|
||||
@{ Name = 'R-INT'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('INET-HQ-Linux', 'INET-BRANCH-Linux', 'HOME-Linux') },
|
||||
@{ Name = 'R-BR'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('BR-SERVER-Linux', 'BR-CLIENT-Linux', 'INET-BRANCH-Linux') },
|
||||
@{ Name = 'HQ-DC'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('HQ-SERVER-Linux') },
|
||||
@{ Name = 'HQ-SAM-1'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('HQ-SERVER-Linux') },
|
||||
@{ Name = 'HQ-SAM-2'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('HQ-SERVER-Linux') },
|
||||
@{ Name = 'HQ-DMZ-1'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('HQ-DMZ-Linux') },
|
||||
@{ Name = 'HQ-DMZ-2'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('HQ-DMZ-Linux') },
|
||||
@{ Name = 'BR-SRV'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('BR-SERVER-Linux') },
|
||||
@{ Name = 'HQ-CL'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('HQ-CLIENT-Linux') },
|
||||
@{ Name = 'HOME'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('HOME-Linux') },
|
||||
@{ Name = 'BR-CL'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('BR-CLIENT-Linux') }
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
Ensure-ExtraDisks -VM $vm -DiskCount $ExtraDiskCount -DiskSizeGB $ExtraDiskSizeGB
|
||||
Set-ExactNetworkAdapters -VM $vm -PortGroupNames $definition.Networks
|
||||
Set-CloudInitUserData -VM $vm -Username $DefaultUsername -Password $DefaultPassword
|
||||
|
||||
$startSnapshot = Get-Snapshot -VM $vm -Name $StartSnapshotName -ErrorAction SilentlyContinue
|
||||
|
||||
if (-not $startSnapshot) {
|
||||
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 after cloud-init customization' -Memory:$false -Quiesce:$false | Out-Null
|
||||
}
|
||||
|
||||
Write-Host "$($definition.Name) ready"
|
||||
}
|
||||
53
ws2024/linux/Create-PortGroups.ps1
Normal file
53
ws2024/linux/Create-PortGroups.ps1
Normal file
@@ -0,0 +1,53 @@
|
||||
<#
|
||||
Required parameter:
|
||||
-SwitchName Name of the existing standard vSwitch where the port groups will be created.
|
||||
|
||||
Optional parameter:
|
||||
-BaseVlanId Starting VLAN ID. Default is 100.
|
||||
The script creates:
|
||||
WAN-Linux = BaseVlanId
|
||||
INT-Linux = BaseVlanId + 1
|
||||
DMZ-Linux = BaseVlanId + 2
|
||||
VPN-Linux = BaseVlanId + 3
|
||||
|
||||
Example:
|
||||
.\Create-PortGroups.ps1 -SwitchName vSwitch0
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$SwitchName,
|
||||
|
||||
[ValidateRange(1, 4094)]
|
||||
[int]$BaseVlanId = 100
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$switch = Get-VirtualSwitch -Name $SwitchName -ErrorAction Stop
|
||||
|
||||
$portGroups = @(
|
||||
@{ Name = 'WAN-Linux'; VlanId = $BaseVlanId },
|
||||
@{ Name = 'INT-Linux'; VlanId = $BaseVlanId + 1 },
|
||||
@{ Name = 'DMZ-Linux'; VlanId = $BaseVlanId + 2 },
|
||||
@{ Name = 'VPN-Linux'; VlanId = $BaseVlanId + 3 }
|
||||
)
|
||||
|
||||
foreach ($portGroup in $portGroups) {
|
||||
$existing = Get-VirtualPortGroup -VirtualSwitch $switch -Name $portGroup.Name -ErrorAction SilentlyContinue
|
||||
|
||||
if ($existing) {
|
||||
if ($existing.VLanId -ne $portGroup.VlanId) {
|
||||
Set-VirtualPortGroup -VirtualPortGroup $existing -VLanId $portGroup.VlanId | Out-Null
|
||||
Write-Host "Updated $($portGroup.Name) VLAN to $($portGroup.VlanId)"
|
||||
}
|
||||
else {
|
||||
Write-Host "$($portGroup.Name) already exists"
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
New-VirtualPortGroup -VirtualSwitch $switch -Name $portGroup.Name -VLanId $portGroup.VlanId | Out-Null
|
||||
Write-Host "Created $($portGroup.Name) with VLAN $($portGroup.VlanId)"
|
||||
}
|
||||
342
ws2024/linux/Create-VMs.ps1
Normal file
342
ws2024/linux/Create-VMs.ps1
Normal file
@@ -0,0 +1,342 @@
|
||||
<#
|
||||
Adjust these values only if your vCenter names are different:
|
||||
$TemplateFolderName Folder that contains the source template VMs.
|
||||
$TargetParentFolderName Parent folder that contains the target folder.
|
||||
$TargetFolderName Folder where the linked clones will be created.
|
||||
$ServerTemplateName Template VM used for server clones.
|
||||
$GuiTemplateName Template VM used for the workstation clone.
|
||||
|
||||
This script expects these port groups to already exist:
|
||||
WAN-Linux
|
||||
INT-Linux
|
||||
DMZ-Linux
|
||||
VPN-Linux
|
||||
|
||||
What the script does:
|
||||
- Creates the WS2024 Linux topology VMs as linked clones
|
||||
- Sets each VM to 2 vCPU and 4 GB RAM
|
||||
- Adds 4 extra 10 GB hard disks
|
||||
- Configures network adapters to match the topology
|
||||
- Injects cloud-init userdata for root and user
|
||||
- Powers on the VM, waits for cloud-init shutdown, then creates a snapshot named Start
|
||||
|
||||
Run:
|
||||
.\Create-VMs.ps1
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$TemplateFolderName = 'Templates'
|
||||
$TargetParentFolderName = 'Worldskills 2024'
|
||||
$TargetFolderName = 'Linux'
|
||||
$ServerTemplateName = 'debian-13-template'
|
||||
$GuiTemplateName = 'debian-13-gui-template'
|
||||
$ReferenceSnapshotName = 'Start'
|
||||
$StartSnapshotName = 'Start'
|
||||
$CpuCount = 2
|
||||
$MemoryGB = 4
|
||||
$ExtraDiskCount = 4
|
||||
$ExtraDiskSizeGB = 10
|
||||
$DefaultUsername = 'user'
|
||||
$DefaultPassword = 'Skill39@Lyon'
|
||||
$CloudInitShutdownTimeoutSeconds = 900
|
||||
|
||||
function Get-SingleFolder {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name
|
||||
)
|
||||
|
||||
$folders = @(Get-Folder -Name $Name -ErrorAction SilentlyContinue)
|
||||
|
||||
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. Make the name unique before running this script."
|
||||
}
|
||||
|
||||
return $folders[0]
|
||||
}
|
||||
|
||||
function Get-SingleChildFolder {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
$ParentFolder
|
||||
)
|
||||
|
||||
$folders = @(Get-Folder -Location $ParentFolder -Name $Name -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 source VM '$($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) {
|
||||
$adapterToRemove = $adapters[-1]
|
||||
Remove-NetworkAdapter -NetworkAdapter $adapterToRemove -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 Ensure-ExtraDisks {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$VM,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]$DiskCount,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]$DiskSizeGB
|
||||
)
|
||||
|
||||
$hardDisks = @(Get-HardDisk -VM $VM | Sort-Object Name)
|
||||
$extraDisksNeeded = $DiskCount - ($hardDisks.Count - 1)
|
||||
|
||||
while ($extraDisksNeeded -gt 0) {
|
||||
New-HardDisk -VM $VM -CapacityGB $DiskSizeGB -StorageFormat Thin | Out-Null
|
||||
$extraDisksNeeded--
|
||||
}
|
||||
}
|
||||
|
||||
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 New-CloudInitUserData {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$VmName,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Username,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Password
|
||||
)
|
||||
|
||||
return @"
|
||||
#cloud-config
|
||||
hostname: $VmName
|
||||
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
|
||||
"@
|
||||
}
|
||||
|
||||
function Set-CloudInitUserData {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$VM,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Username,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Password
|
||||
)
|
||||
|
||||
$userData = New-CloudInitUserData -VmName $VM.Name -Username $Username -Password $Password
|
||||
$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.encoding' -Value 'base64'
|
||||
}
|
||||
|
||||
function Wait-ForVmToPowerOff {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$VM,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]$TimeoutSeconds
|
||||
)
|
||||
|
||||
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
|
||||
|
||||
do {
|
||||
$currentVm = Get-VM -Id $VM.Id
|
||||
|
||||
if ($currentVm.PowerState -eq 'PoweredOff') {
|
||||
return
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds 5
|
||||
} while ((Get-Date) -lt $deadline)
|
||||
|
||||
throw "VM '$($VM.Name)' did not power off within $TimeoutSeconds seconds."
|
||||
}
|
||||
|
||||
$templateFolder = Get-SingleFolder -Name $TemplateFolderName
|
||||
$targetParentFolder = Get-SingleFolder -Name $TargetParentFolderName
|
||||
$targetFolder = Get-SingleChildFolder -Name $TargetFolderName -ParentFolder $targetParentFolder
|
||||
|
||||
$serverTemplate = Get-SingleVmFromFolder -Name $ServerTemplateName -Folder $templateFolder
|
||||
$guiTemplate = Get-SingleVmFromFolder -Name $GuiTemplateName -Folder $templateFolder
|
||||
|
||||
$serverReferenceSnapshot = Get-OrCreateReferenceSnapshot -VM $serverTemplate -SnapshotName $ReferenceSnapshotName
|
||||
$guiReferenceSnapshot = Get-OrCreateReferenceSnapshot -VM $guiTemplate -SnapshotName $ReferenceSnapshotName
|
||||
|
||||
$vmDefinitions = @(
|
||||
@{ Name = 'fw'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('WAN-Linux', 'INT-Linux', 'DMZ-Linux', 'VPN-Linux') },
|
||||
@{ Name = 'int-srv01'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('INT-Linux') },
|
||||
@{ Name = 'mail'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('DMZ-Linux') },
|
||||
@{ Name = 'ha-prx01'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('DMZ-Linux') },
|
||||
@{ Name = 'ha-prx02'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('DMZ-Linux') },
|
||||
@{ Name = 'web01'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('DMZ-Linux') },
|
||||
@{ Name = 'web02'; Template = $serverTemplate; Snapshot = $serverReferenceSnapshot; Networks = @('DMZ-Linux') },
|
||||
@{ Name = 'jamie-ws01'; Template = $guiTemplate; Snapshot = $guiReferenceSnapshot; Networks = @('WAN-Linux', 'VPN-Linux') }
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
Ensure-ExtraDisks -VM $vm -DiskCount $ExtraDiskCount -DiskSizeGB $ExtraDiskSizeGB
|
||||
Set-ExactNetworkAdapters -VM $vm -PortGroupNames $definition.Networks
|
||||
Set-CloudInitUserData -VM $vm -Username $DefaultUsername -Password $DefaultPassword
|
||||
|
||||
$startSnapshot = Get-Snapshot -VM $vm -Name $StartSnapshotName -ErrorAction SilentlyContinue
|
||||
|
||||
if (-not $startSnapshot) {
|
||||
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 after clone creation' -Memory:$false -Quiesce:$false | Out-Null
|
||||
}
|
||||
|
||||
Write-Host "$($definition.Name) ready"
|
||||
}
|
||||
Reference in New Issue
Block a user