Jere's Techblog

PowerCLI: Create VM Engine

Here is an example of how to make automated VM’s with PowerCLI. You can customize the vLAN, DiskType, etc.

If you reuse the Script below, I would recommend to look at the parameters and adapt them to your environment, e.g. network/storage config like the vLan,ESXI Hosts or the adapter type you are using its VMWare.

example:

Create-CustomVirtualMachine -Hostname 'MyServer' -NumCPU 6 -DiskSize 80 -RAMinGB 12 -OS Srv2016

Ensure you are using this script with PowerCLI or with the PowerCLI Module / Assemblys.

The script can also be saved and imported as a module (.psm1).

Import-Module "Path:\to\your\module\share\Create-VM_onFreeDatastore.psm1" -Verbose

Script to create VMs on Hypervisor with enough free ressources

#by J.Kühnis 06.11.2019
    $VmHostArray = @()

    Class VMHost {
        [string]$Name
        [string]$State
        [string]$Parent
        [Array]$DatastoreIdList
        [int]$MemoryTotalGB
        [int]$MemoryUsageGB
        [int]$FreetoUseMemory
        [int]$ReservedVMmemory
        [String]$VMCreationState
        [String]$VMCacheDiskState
        [String]$ESXiHost
        [String]$PVSCollectionState
        [String]$CTXStudioState
        [String]$CTXMaintState
    }

Function Get-VMHostVirtualMachineVMs {
 
    [CmdletBinding()]
    Param(
        #[Parameter(Mandatory=$true)][string[]]$ServerName
        [Parameter(Mandatory = $true)][String] $VMhost,
        [bool]$ExtendedProperties
    )
      
    IF ($ExtendedProperties -eq $true) {
        IF ($global:VMhostsVM = (Get-VMHost -Name $VMhost | Get-VM | select MemoryGB, VMHost, Name)) {
            return $global:VMhostsVM
        }
                
    }
    Else {
        IF ($global:VMhostsVM = ((Get-VMHost -Name $VMhost | Get-VM).Name | sort)) {
            return $global:VMhostsVM
        }
    }
    return $false
}
Function Get-vmHostDataStore {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [String[]]$DatastoreIDList
    )
    $global:DatastoreID = (get-datastore -Id $DatastoreIDList)           
    return  $global:DatastoreID
    
}
  
Function Create-VMEngine {
    
    
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$VMName,
        [string]$EsxiHost,
        [string]$Datastore,
        [int]$NumCPUCore,
        [int]$MemoryGB,
        [int]$DiskSize,
        $Vlan,
        $HDFormat,
        [int]$TotalCPU,
        $OS
    )

    write-host "Hypervisor Parameters:" $VMName $EsxiHost $Datastore $MemoryGB $Vlan
    write-host (get-date -f HH:mm:ss)"Start VM Creation $VmName, please wait..."
    If (Get-VM *$VMName*) {
        Write-Host "VM $VMName already exists!!!, Skip this Hostname." -ForegroundColor RED -BackgroundColor Black
        break
    }

    IF ($OS -eq 'Win10') {
        New-VM -Name $VmName -ResourcePool $EsxiHost -Datastore $Datastore -NumCPU $NumCPUCore -MemoryGB $MemoryGB -NetworkName $Vlan -GuestID windows9_64Guest -DiskGB $DiskSize -DiskStorageFormat $HDFormat
    }
    Else {
        New-VM -Name $VmName -ResourcePool $EsxiHost -Datastore $Datastore -NumCPU $NumCPUCore -MemoryGB $MemoryGB -NetworkName $Vlan -GuestID windows9Server64Guest -DiskGB $DiskSize -DiskStorageFormat $HDFormat
    }

    # Set SCSI Controller
    Get-ScsiController -VM $VmName | Set-ScsiController -Type VirtualLsiLogicSAS >$NULL -WarningAction SilentlyContinue
    Write-host "Configure VM $VMName"
       
    # Configure vCPU & Core
    $VmName = Get-VM -name $VmName
    set-vm -vm $VmName -numcpu $TotalCpu -Confirm:$false >$NULL

    # Configure MAC-Address and Adapter Type
    $vm = Get-VM -Name $vmName
    $nic = Get-NetworkAdapter -VM $vm
    $global:spec = New-Object VMware.Vim.VirtualMachineConfigSpec
    $devSpec = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $devSpec.Device = $nic.ExtensionData
    $devSpec.operation = "edit"
    $spec.DeviceChange += $devSpec
    $vm.ExtensionData.ReconfigVM($spec)
    Set-NetworkAdapter -NetworkAdapter $nic -Type Vmxnet3 -Confirm:$false >$NULL

    $spec = New-Object VMware.Vim.VirtualMachineConfigSpec
    $spec.Firmware = [VMware.Vim.GuestOsDescriptorFirmwareType]::bios
    try {
        $VmName.ExtensionData.ReconfigVM($spec)
    }
    catch {
    }


    #special Parameters
    New-AdvancedSetting -Entity $vm -Name ethernet0.pciSlotNumber -Value 192 -Confirm:$false -Force:$true >$NULL -WarningAction SilentlyContinue
    Write-host "VM Config finished"
    write-host (get-date -f HH:mm:ss)"VM Creation $VmName finished!" -ForegroundColor "Green"

}


Function Create-CustomVirtualMachine {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$Hostname,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][ValidateRange(1, 16)][int]$NumCPU,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][ValidateRange(1, 250)][int]$DiskSize,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][ValidateRange(1, 32)][int]$RAMinGB,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][ValidateSet('Win10', 'Srv2016')][string]$OS
    )

    Write-host "------------------------"`n"| Start VmWare Section |"`n"------------------------"  -ForegroundColor WHITE
    
    (get-vmhost HYPERVISORFQDN1.f.q.d.n, HYPERVISORFQDN2.f.q.d.n | select Name, State, Parent, DatastoreIdList, MemoryTotalGB, MemoryUsageGB) | % {
        #$FreetoUseMemory = ("{0:N0}" -f $_.MemoryTotalGB) - ("{0:N0}" -f $_.MemoryUsageGB)
        $myhost = New-Object VMhost -Property @{Name = $_.Name; State = $_.State; Parent = $_.Parent; DatastoreIdList = $_.DatastoreIdList; MemoryTotalGB = $_.MemoryTotalGB; MemoryUsageGB = $_.MemoryUsageGB; }
        $VmHostArray += $myhost
    }
    
    Foreach ($VmWareHost in $VmHostArray) {
        $VmWareHost.ReservedVMmemory = ((Get-VMHostVirtualMachineVMs $VmWareHost.Name -ExtendedProperties $true).MemoryGB | Measure-Object -sum).sum
        $VmWareHost.FreetoUseMemory = ("{0:N0}" -f $VmWareHost.MemoryTotalGB) - ("{0:N0}" -f $VmWareHost.ReservedVMmemory);
    }

    $VmHostArray = $VmHostArray | Sort-Object -Property FreetoUseMemory -Descending
    

    #Check RAM Space on ESXI Host with 10GB reserves on Host
    IF (($VmHostArray[0].FreetoUseMemory - 10) -ge ($RAM)) {
        #Check if Disk has 10GB Free Storage
        IF (get-vmHostDataStore -DatastoreIDList $VmHostArray[0].DatastoreIdList) {
            $specVMDatastore = (get-vmHostDataStore -DatastoreIDList $VmHostArray[0].DatastoreIdList | Sort-Object -Property FreeSpaceGB -Descending)
            [int]$a = ("{0:N0}" -f $specVMDatastore[0].FreeSpaceGB).Replace("'", "")
            
            IF ($a -ge ($DiskSize + 10) ) {
                $specVMDatastore = $specVMDatastore[0].Name
    
                #StagingParameters
            
                $VMName = $Hostname
                $EsxiHost = $VmHostArray[0].Name
                $Datastore = $specVMDatastore
                [int]$NumCPUCore = 1
                $Vlan = 'Some_VlanName'
                $HDFormat = "Thin"
                [int]$TotalCPU = $NumCPU

                # Create VM on Host
                Create-VMEngine -VMName $VMName -EsxiHost $EsxiHost -Datastore $Datastore -MemoryGB $RAMinGB -Vlan $Vlan -NumCPUCore $NumCPUCore -HDFormat $HDFormat -TotalCPU $TotalCPU -DiskSize $DiskSize -OS $OS
                Write-host "----------------------"`n"| End VmWare Section |"`n"----------------------"  -ForegroundColor WHITE
            
            }
            Else {
                Write-host $Hostname "| not enough storage on ESXI Host! Action stopped." -ForegroundColor Yellow -BackgroundColor Black
            }
            
        }
        Else {
            Write-host $Hostname "| No Datastore found. Action stopped" -ForegroundColor Yellow -BackgroundColor Black
            
        }
    }
    Else {
        Write-Host $Hostname "| not enough on ESXI Host. Action stopped" -ForegroundColor Yellow -BackgroundColor Black

    }
}
Continue reading...

Verify Citrx HostedMachineID with VmWare Hypervisor

Sometimes the HostedMachineID of Citrix does not match that of VmWare. This is often the case when cloning or moving machines. In Citrix Studio you will see the Powerstate of this machines as unknow and you can’t do any VM actions like reboot in the console.

With this script snippet it can be checked. To do this the variable “Broker” must be adjusted in the Script and the PowerCli and CitrixModule (BrokerSnapIn) must be loaded. Furthermore, the connection to the vCenter must be initiated via “Connect-VIServer vCName“.

#25.09.2019by J.Kühnis - Verfiy HostedMachineID with VmWare ESXi Hypervisoer

$Broker = "Enter your BrokerName"
$Brokermachines = Get-BrokerMachine -AdminAddress $Broker | Select MachineName,DNSName,HostedMachineID

Foreach ($Machine in $Brokermachines){
    IF(get-vm $Machine.DnsName -ErrorAction SilentlyContinue){
        IF($Machine.HostedMachineID -eq (Get-View -id (get-vm $machine.DNSName).id).config.uuid){
            Write-Host $Machine.DnsName "HostedMachineID is matching" $Machine.HostedMachineId -ForegroundColor Green
        }Else{
            Write-host $Machine.DnsName "Mismatch ID: VmWare UUID =" (Get-View -id (get-vm $machine.DNSName).id).config.uuid "; Citrix HostedMachineID =" $Machine.HostedMachineID -ForegroundColor Yellow
        }

    }Else{
        Write-host $Machine.DnsName "MachineName not Found on ESXi" -ForegroundColor Yellow
    }
}

Now that the machines have been read out, the connections can be fixed.

vCenter Cert thumbprint update:

# Open an admin POSH console, load the Citrix Modules (asnp citrix*) and cd to XDHyp:/Connections and run ls. Check the SSLThumbprints entry.
asnp citrix*
cd XDHyp:/Connections
ls
Set-Item -LiteralPath "XDHyp:\Connections\vCenters Name" -sslthumbprint "123456789ABCD123456789ABCDE123456789ABCD" -hypervisorAddress https://vcenter-name/sdk

The letters in SSL-Thumbprint must be uppercase.

In this blog the problem is also discussed in detail:

Continue reading...

Get reserved Memory of ESXi Host

With the command Get-VMHost you can read values ​​such as the current memory consumption or the total number memory of a host. But I didn’t find a way to read out the value of the allocated memory of the subobjects (the VMs).

Here is an example of how this can be done. The script outputs a list of all hosts. In the attribute “AllocatedVMMemoryGB” you can see how much memory has been over-provisioned or it shows how much memory you could still use.

#by J.Kühnis 20.08.2019

#Class
Class VMHost{
    [string]$Name
    [string]$ConnectionState
    [string]$PowerState
    [int]$NumCpu
    [int]$MemoryUsageGB
    [int]$AllocatedVMMemoryGB
    [int]$MemoryTotalGB
    [string]$ParentCluster
    [string]$Id
    [string]$ProcessorType
}

#Vars
$VmHostArray =@()

#MainScript
Foreach($server in Get-VMHost){
    $a = (($server | get-vm).MemoryGB | Measure-Object -sum).sum
    $server = Get-vmHost -name $server.name
    $a = ("{0:N0}" -f $server.MemoryTotalGB) - ("{0:N0}" -f $a);

    $vmhost = New-Object VMHost -Property @{Name=$server.name;ConnectionState=$server.ConnectionState;Powerstate=$server.ConnectionState;NumCpu=$server.NumCpu;MemoryUsageGB=$server.MemoryUsageGB;AllocatedVMMemoryGB=$a;MemoryTotalGB=$server.MemoryTotalGB;ParentCluster=$server.parent;ID=$server.Id;ProcessorType=$server.ProcessorType}
    
    $VmHostArray += $vmhost

    Clear-Variable -Name a,vmhost
}

#output
$VmHostArray | Format-Table

            
Continue reading...

Hyper-V Create Machine

I’d like to share a mini Script to create VirtualMachines with Hyper-V and Powershell. It is certainly not enterprise diveable. But it’s enough to create a VM for testing.

#by JKU 02.08.2019

#VM Base Information
$VMName = "TestVM2"
$DataPath = "C:\temp"
$DataVMPath = $DataPath + "\" + $VMName
$DiskDataPath = $DataPath + "\" + $VMName + "\" + $VMName + ".vhdx"
$Memory = 2GB
$Disk = 12GB
$CPUCores = 4
$VMGeneration = 2   #Options are "1","2"   -> If you don't have any idea, please let this at Value 2

#Options
$MountIso = "true"
$StartVMaftreCreation = "true"
$IsoPath = "C:\Cloud_JK\NextCloud\jere@nx2309.your-next.cloud\OS\win10\SW_DVD5_Win_Pro_10_64BIT_German_MLF_X20-25597.ISO"




#Script
IF(Get-VM -Name $VMName -ErrorAction SilentlyContinue){
    Write-Warning "$VMName : Machine already exists! Please verify your Values."
    return
}
IF(Test-Path $DataVMPath){
    Write-Warning "$DataVMPath : Folder already exists! Please verify your Values."
    return
}

Write-Host "Start to create VM $VMName" -foregroudcolor yellow
New-VM -Name $VMName -path $DataPath -MemoryStartupBytes $Memory -NewVHDSizeBytes $Disk -NewVHDPath $DiskDataPath -Generation $VMGeneration

$Vm = (Get-VM -Name $VMName)
Set-VM -VM $Vm -ProcessorCount $CPUCores


IF($MountIso -eq "true"){
    $DVD = Add-VMDvdDrive -VMName ($Vm).Name -Path $ISOPath -Passthru
    Set-VMFirmware -VM $VM -FirstBootDevice $DVD
}
IF($StartVMaftreCreation -eq "true"){
    Start-VM -Name $VMName
}

Write-Host "Script End"

Below are two links to similar topics.

To create a virtual machine from a specific template (Hyper V)

Continue reading...

Delete Citrix Worker from Studio and vCenter

With this script one or more servers can be deleted from the Citrix DeliveryController (Citrix Studio) and from the ESXi/vCenter.

To use The Script some variables and values need to be adjusted like the name of the Citrix DeliveryController and vCenter.
Vmware (PowerCLI) and Citrix (SDK) powershellmodules need to be installed.

This only works if the VM name is identical to the Worker Server DNS name. If this is the case, the following string can be deleted in the script [-replace “.FQDN.address”,””]

In my case, the name of the VM is only the “hostname” of the machine and not the DNSname. So the script removes the FQDN name, in order to use the script successfully, this must also be adjusted.

Import-Module *
Add-PSSnapin *

$DeliveryController = "someBrokerDNSName"
Connect-viserver "some vCenter"


Get-BrokerMachine -DNSName anySevernames* -AdminAddress $DeliveryController |  %{
    
    #Delete & Remove From Citrix Studio
    Remove-BrokerMachine $_ -DesktopGroup $_.DesktopGroupName
    Remove-BrokerMachine $_ -Force

    #Delete Permanently from vCenter
    remove-vm ($_.DNSName -replace ".FQDN.Adress","") -DeletePermanently -Confirm:$false

    write-host $_.DNSName -ForegroundColor Green  #Write ServerName

}
Continue reading...

Rename vLan PowerCLI

This script changes the vLan name of each network adapter within a vCenter.
The script works with PowerCLI (tested with version 6.0 /6.5).

The following variables should be adjusted in the script.
$vcserver = “Specify FQDN.of.vcenter.”.
$VPGName = “Specify the current vLan name”.
$VPGNameNew = “Specify the new vLan name”.

# by Jeremias Kühnis
#check if vmware modules are loaded
function checkmodule {

    If (!(Get-PSSnapin * | where { $_.Name -eq 'VMware.VimAutomation.Core'})) {Add-PSSnapin *}


        if (-not (Get-PSSnapin -Name 'VMware.VimAutomation.Core')) {
            write-host "VMWare PSSnapin is not loaded - PSSession/Windows will be closed in 10 seconds" -backgroundcolor "Yellow" -ForegroundColor "red"
            sleep 10
            exit
            }
        else{
        Write-Host "VMWare PSSnapin loaded" -ForegroundColor "Green"
        }
}

# VCenter you are connecting too
function connectserver{

    $vcserver= 'any.vCenter.FQDN'
    Connect-VIServer $vcserver
}

function renamevpg{
# Change VirtualPortGroup / VLANS
    $VPGName = 'XD_2011' # Variable Vlan
    $NewVPGName ='XD_2011_new'#Variable new VLAN Name

    #Set the name of the "Standard-Virtual Switch"
    $VPG = Get-VirtualPortGroup -Name $VPGName
    Set-VirtualPortGroup -VirtualPortGroup $VPG -Name $NewVPGName
    Start-Sleep 30
   # Loop to make changes to new Network Adapter

    ForEach ($adapter in (Get-NetworkAdapter * | where {$_.NetworkName -eq $VPGName})){
    Set-NetworkAdapter -NetworkAdapter $adapter -NetworkName "$NewVPGName" -Confirm:$false
    Write-Host $adapter
    }
}

checkmodule
connectserver
renamevpg
Continue reading...

Bulk reboot Server with PowerCLI

Tested with PowerClI Version 6.5

This script allows you to restart an array of servers trough PowerCLI.
You will be prompted to specify your ESXi-Host /vCenter Environment. Ensure that you enther the FQDN.

The script will reboot your servers without confirmation.

#13.11.2018 Restart a list/array of Servers through vCenter/Powercli
 
IF(!(Get-Module vm* | where { $_.Name -eq 'VMware.VimAutomation.Core'})){
       (Get-Module –ListAvailable VMware.VimAutomation.Core | Import-Module)
         if (-not (Get-Module -Name 'VMware.VimAutomation.Core')){
               Write-Warning "Could not find/load 'PowerCLI Module.  Ensure that you are running this Script on Server with PowerCLI."
               return
         }
}

 
Write-Host "####################################" -ForegroundColor Yellow
$vCenter = Read-Host -prompt "Please enter the Name of your ESXi Host or vCenter" 

Connect-VIServer $vCenter
$server = @(
# Enter Servernames here -> Equivalent to the Name of the VM-Target                   
"Hostname-Server1"
"Hostname-Server2"
"Hostname-Server3"
)
 
 
foreach ($server in $server){
    try{
        Restart-VM -VM $server -Confirm:$false
        write-host "Reboot OK $server" -ForegroundColor Green
    }catch{
        write-host "Reboot NOT OK $server" -ForegroundColor yellow
          }
}

Disconnect-VIServer -Server $vCenter -Confirm:$false
Continue reading...