Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save virasak/8b2f543ab6422fe669400547917c9b2d to your computer and use it in GitHub Desktop.
Save virasak/8b2f543ab6422fe669400547917c9b2d to your computer and use it in GitHub Desktop.
<#
Create Unix VM's.
PREREQUISITES:
- A VM that has been installed with an OS that supports cloud-init
- cloud-init is installed on the vm
How it works:
- Asks for a bunch of parameters
- Creates a disk from parent vhdx for speed
- Gets the vm name from the params and the existing vm with matching name
- Creates a cloud-init config for the vm (currently sets only hostname and ansible user ssh key)
- Sets up everything then starts the vm
- if you ask for more than one, it creates in sequence
WHAT TO DO:
- Replace the values for baseVMpath, parentDiskPath and sshPubKey then run the script.
FUTURE THINGS TO WORK ON :
- Adding parameters for baseVMpath, parentDiskPath and sshPubKey
- Adding the option to pass the other info in parameters as well so you don't have to redo it by hand
-> with 2 parameter sets, depending on dynamic memory or not
Helpful links (much thanks to the people who wrote these) :
- https://computingforgeeks.com/how-to-create-centos-8-kvm-image-template-for-openstack/
- https://devops.ionos.com/tutorials/deploy-a-centos-server-customized-with-cloud-init/
- https://cloudinit.readthedocs.io/en/latest/topics/examples.html
- https://github.com/MicrosoftDocs/Virtualization-Documentation/blob/master/hyperv-samples/benarm-powershell/Ubuntu-VM-Build/BaseUbuntuBuild.ps1
#>
function Get-Info {
clear
$DynamicMemory = Read-Host "Dynamic Memory ? (y/n) "
Switch ($DynamicMemory)
{
Y {Write-host -foreground green "Yes, dynamic memory!`n"; $dynMem=$true}
N {Write-Host -foreground yellow "No, I want to set min and max and startup memory.`n"; $dynMem=$false}
Default {Write-Host -foreground yellow "Default is that we're using it, because we believe in the power of Myth !`n"; $dynMem=$true}
}
if($dynMem) {
$maxMem = Read-Host "Maximum memory ? (GB) "
$minMem = Read-Host "Minimum memory ? (GB) "
$startMem = Read-Host "Startup memory ? (GB) "
} else {
$startMem = Read-Host "Memory ? (GB) "
}
$cores = Read-Host "vCPU's ? (Cores) "
$prefix = Read-Host "Prefix to use for these ? (A string) "
$VMObject = New-Object -TypeName psobject
$VMObject | Add-Member -MemberType NoteProperty -Name "UseDynamicMemory" -Value $dynMem
$VMObject | Add-Member -MemberType NoteProperty -Name "maxMemory" -Value $([int]$maxMem)
$VMObject | Add-Member -MemberType NoteProperty -Name "minMemory" -Value $([int]$minMem)
$VMObject | Add-Member -MemberType NoteProperty -Name "startMemory" -Value $([int]$startMem)
$VMObject | Add-Member -MemberType NoteProperty -Name "cores" -Value $([int]$cores)
$VMObject | Add-Member -MemberType NoteProperty -Name "nestedVirtualization" -Value $false
$VMObject | Add-Member -MemberType NoteProperty -Name "prefix" -Value $prefix
return $VMObject
}
function Setup-VM {
param($VMObject, $basepath, $parent, $sshPubKey)
$vmname = Get-NextVMNumber($VMObject.prefix)
$path = "$basepath\$vmname"
write-host "Starting script...`n`nThis will create a VM with name $vmname.`nLet's go!`n`n"
Add-VMVHD -vmname $vmname -path $path -parent $parent
Create-VM -VMObject $VMObject -vmname $vmname -path $path
Create-CloudInit -vmname $vmname -path $path -sshPubKey $sshPubKey
Set-VMProperties -VMObject $VMObject -vmname $vmname
Set-VMNetworkProperties -vmname $vmname
Add-VMDvdDrive -VMName $vmname
Set-VMDvdDrive -VMName $vmname -path "$($path)\metadata.iso"
Start-VM $vmname
}
function Create-VM {
param($VMObject, $vmname, $path)
Write-host "Creating new virtual machine." -NoNewline
New-VM -Name $vmname -MemoryStartupBytes ([int]($VMInfo.startMemory)*1GB) -BootDevice VHD -VHDPath "$path\Virtual Hard Disks\$vmname.vhdx" -Path "$path\" -Generation 2 -SwitchName (Get-VMSwitch -SwitchType External).name | out-null
Write-Host -ForegroundColor Green " Done."
}
function Create-CloudInit {
param(
$vmname,
$path,
$sshPubKey
)
$oscdimgPath = "C:\Program Files (x86)\Windows Kits\8.1\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe"
$metaDataIso = "$($path)\metadata.iso"
$metadata = @"
instance-id: uuid-$([GUID]::NewGuid())
local-hostname: $($vmname)
"@
$userdata = @"
#cloud-config
users:
- name: ansible
gecos: ansible
sudo: ALL=(ALL) NOPASSWD:ALL
groups: users, admin
ssh_import_id: None
lock_passwd: true
ssh_authorized_keys:
- $sshPubKey
"@
# Output meta and user data to files
if(-not (Test-Path "$($path)\Bits")) {New-Item -ItemType Directory "$($path)\Bits" | out-null}
sc "$($path)\Bits\meta-data" ([byte[]][char[]] "$metadata") -Encoding Byte
sc "$($path)\Bits\user-data" ([byte[]][char[]] "$userdata") -Encoding Byte
# Create meta data ISO image - this thing apparently outputs in stderr so it shows as red but it's not errors, it's just the progress for it.
& $oscdimgPath "$($path)\Bits" $metaDataIso -j2 -lcidata | Out-null
}
function Get-NextVMNumber {
param($prefix)
if((Get-VM -name "$prefix*").count -gt 0){
$prefix += (([int](get-vm -name "$prefix*" | select @{ Label = 'Number' ;Expression = { $_.VMName.Substring($prefix.length,2) } } | sort number | select -Last 1).number) + 1).tostring().padleft(2,"0")
} else {
$prefix += "01"
}
return $prefix.ToUpper()
}
function Add-VMVHD {
param($vmname, $path, $parent)
if(-not (Test-Path "$path\Virtual Hard Disks\$vmname.vhdx")) {
if (-not (Test-Path "$path\Virtual Hard Disks")) { New-Item -Force -ItemType Directory -Path "$path\Virtual Hard Disks" | out-null }
Write-host "Creating new VHD from parent $parent." -NoNewline
New-VHD –Path "$path\Virtual Hard Disks\$vmname.vhdx" –ParentPath $parent –Differencing | Out-null
Write-Host -ForegroundColor Green " Done."
}
}
function Set-VMProperties {
param($VMObject, $vmname)
Write-host "Customizing virtual machine." -NoNewline
if($VMInfo.UseDynamicMemory) { Set-VMMemory -VMName $vmname -MaximumBytes ([int]($VMInfo.maxMemory)*1GB) -DynamicMemoryEnabled $true -MinimumBytes ([int]($VMInfo.minMemory)*1GB) }
Set-VM -VMname $vmname -ProcessorCount $VMInfo.cores -AutomaticStopAction ShutDown -AutomaticStartAction StartIfRunning -AutomaticStartDelay (Get-Random -Minimum 100 -Maximum 800)
Set-VMFirmware -VMName $vmname -EnableSecureBoot Off -FirstBootDevice (get-VMHardDiskDrive -VMName $vmname)
Get-VM -VMname $vmname | Enable-VMIntegrationService -Name *
if($VMInfo.nestedvirtualisation) { Set-VMProcessor -VMName $vmname -ExposeVirtualizationExtensions $true }
Write-Host -ForegroundColor Green " Done."
}
function Set-VMNetworkProperties {
param($vmname)
# This function sets the VLAN
Write-host "Customizing networking." -NoNewline
Set-VMNetworkAdapterVlan -VlanId 1 -VMName $vmname -Access
Write-Host -ForegroundColor Green " Done."
}
#=====================================
# The fun starts here
#=====================================
$baseVMpath = "D:\Virtual Machines\"
$parentDiskPath = "D:\Virtual Machines Templates\Centos8.vhdx"
# this is a full public key : ssh-rsa AAA[...]=="
$sshPubKey = ""
$VMInfo = Get-Info
$total = Read-Host "How many VM do you want ? (1-...) "
Write-Host "Starting timer.`n"
$stopwatch = [system.diagnostics.stopwatch]::StartNew()
For ($i=1; $i -le $total; $i++) {
Write-host "Creating VM $i of $total."
Setup-VM -VMObject $VMInfo -basepath $baseVMpath -parent $parentDiskPath -sshPubKey $sshPubKey
}
$stopwatch.Stop()
Write-host "Total time : $([math]::Round($stopwatch.Elapsed.TotalSeconds,0)) seconds for $many Virtual machines."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment