Skip to content

Instantly share code, notes, and snippets.

@InputObject2
Last active November 12, 2025 04:27
Show Gist options
  • Save InputObject2/3a6fe586821a2ff84cd494eb897d3813 to your computer and use it in GitHub Desktop.
Save InputObject2/3a6fe586821a2ff84cd494eb897d3813 to your computer and use it in GitHub Desktop.

Revisions

  1. InputObject2 revised this gist Nov 12, 2025. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions Create-Unix-VM-cloud-init.ps1
    Original file line number Diff line number Diff line change
    @@ -86,7 +86,7 @@ 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
    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."
    }

    @@ -139,20 +139,20 @@ function Get-NextVMNumber {
    return $prefix.ToUpper()
    }

    function Add-VMVHD {
    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
    New-VHD -Path "$path\Virtual Hard Disks\$vmname.vhdx" -ParentPath $parent -Differencing | Out-null

    Write-Host -ForegroundColor Green " Done."
    }
    }

    function Set-VMProperties {
    function Set-VMProperties {
    param($VMObject, $vmname)

    Write-host "Customizing virtual machine." -NoNewline
    @@ -167,7 +167,7 @@ function Set-VMProperties {
    Write-Host -ForegroundColor Green " Done."
    }

    function Set-VMNetworkProperties {
    function Set-VMNetworkProperties {
    param($vmname)
    # This function sets the VLAN

  2. InputObject2 revised this gist May 16, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Create-Unix-VM-cloud-init.ps1
    Original file line number Diff line number Diff line change
    @@ -123,7 +123,7 @@ users:
    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
    # 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
    }

  3. InputObject2 revised this gist May 16, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions Create-Unix-VM-cloud-init.ps1
    Original file line number Diff line number Diff line change
    @@ -35,8 +35,8 @@ function Get-Info {
    $DynamicMemory = Read-Host "Dynamic Memory ? (y/n) "
    Switch ($DynamicMemory)
    {
    Y {Write-host -foreground green "Yes, let's use a modern OS!`n"; $dynMem=$true}
    N {Write-Host -foreground yellow "No, I'm gonna be using some weirdly done apps or a database.`n"; $dynMem=$false}
    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}
    }

  4. InputObject2 created this gist May 16, 2020.
    201 changes: 201 additions & 0 deletions Create-Unix-VM-cloud-init.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,201 @@
    <#
    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, let's use a modern OS!`n"; $dynMem=$true}
    N {Write-Host -foreground yellow "No, I'm gonna be using some weirdly done apps or a database.`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
    & $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."