PowerCLI

 View Only
  • 1.  Simple Deploy VM Script

    Posted Sep 03, 2019 11:23 AM

    I have found a very simple deployment script (will probably add more complex stuff in there as I get a little better at PowerShell/PowerCli, pretty new at this right now).

    I would like to add some more functions.

    1. I have multiple templates in vCenter (Windows 2019, Windows 2016 and Windows 2012). When I run the script I would like to being able to choose from the different templates. These templates will also use different VM Customization Specifications named in the same way as the templates. In this script I only use one template and one Customization Specification. How can I add this function to add more template (OS versions)?

    This is how it looks right now (may a bit messy I know):

    #### USER DEFINED VARIABLES ############################################################################################

    $Domain = "xxxxxxxxxxxxxxx"             #AD Domain to join

    $vCenter = "xxxxxxxxxxxxxxx"            #vCenter to deploy VM

    $Cluster = "xxxxxxxxxxxxxxx"            #vCenter cluster to deploy VM

    $VMTemplate = "Windows 2019 Server"     #vCenter template to deploy VM

    $CustomSpec = "Windows 2019 Server"     #vCenter customization to use for VM

    $Location = "Deploy"                    #Folderlocation in vCenter for VM

    $DataStore = "xxxxxxxxxxxxxxx"          #Datastore in vCenter to use for VM

    $DiskStorageFormat = "EagerZeroedThick" #Diskformtat to use (Thin / Thick) for VM

    $NetworkName = "xxxxxxxxxxxxxxx"        #Portgroup to use for VM

    $Memory =   "8"                         #Memory of VM In GB

    $CPU =  "2"                             #number of vCPUs of VM

    $DiskCapacity =  "80"                   #Disksize of VM in GB

    $SubnetLength = "xxxxxxxxxxxxxxx"       #Subnetlength IP address to use (24 means /24 or 255.255.255.0) for VM

    $GW = "xxxxxxxxxxxxxxx"                 #Gateway to use for VM

    $IP_DNS = "xxxxxxxxxxxxxxx"             #IP address DNS server to use

    ### FUNCTION DEFINITIONS ################################################################################################

    Function Check-CustomizationStarted([string] $VM)

    {

        Write-Host "Verifying that Customization for VM $VM has started"

        $i=60 #time-out of 5 min

    while($i -gt 0)

    {

    $vmEvents = Get-VIEvent -Entity $VM

    $startedEvent = $vmEvents | Where { $_.GetType().Name -eq "CustomizationStartedEvent" }

    if ($startedEvent)

    {

                Write-Host  "Customization for VM $VM has started"

    return $true

    }

    else

    {

    Start-Sleep -Seconds 5

                $i--

    }

    }

        Write-Warning "Customization for VM $VM has failed"

        return $false

    }

    Function Check-CustomizatonFinished([string] $VM)

    {

        Write-Host  "Verifying that Customization for VM $VM has finished"

        $i = 60 #time-out of 5 min

    while($true)

    {

    $vmEvents = Get-VIEvent -Entity $VM

    $SucceededEvent = $vmEvents | Where { $_.GetType().Name -eq "CustomizationSucceeded" }

            $FailureEvent = $vmEvents | Where { $_.GetType().Name -eq "CustomizationFailed" }

    if ($FailureEvent -or ($i -eq 0))

    {

    Write-Warning  "Customization of VM $VM failed"

                return $False

    }

    if ($SucceededEvent)

    {

                Write-Host  "Customization of VM $VM Completed Successfully"

                Start-Sleep -Seconds 30

                Write-Host  "Waiting for VM $VM to complete post-customization reboot"

                Wait-Tools -VM $VM -TimeoutSeconds 300

                Start-Sleep -Seconds 30

                return $true

    }

            Start-Sleep -Seconds 5

            $i--

    }

    }

    Function Restart-VM([string] $VM)

    {

        Restart-VMGuest -VM $VM -Confirm:$false | Out-Null

        Write-Host "Reboot VM $VM"

        Start-Sleep -Seconds 60

        Wait-Tools -VM $VM -TimeoutSeconds 300 | Out-Null

        Start-Sleep -Seconds 10

    }

    function Add-Script([string] $script,$parameters=@(),[bool] $reboot=$false){

        $i=1

        foreach ($parameter in $parameters)

        {

            if ($parameter.GetType().Name -eq "String") {$script=$script.replace("%"+[string] $i,'"'+$parameter+'"')}

            else                                        {$script=$script.replace("%"+[string] $i,[string] $parameter)}

            $i++

        }

        $script:scripts += ,@($script,$reboot)

    }

    Function Test-IP([string] $IP)

    {

      if (-not ($IP) -or (([bool]($IP -as [IPADDRESS])))) { return $true} else {return $false}

    }

    #### USER INTERACTIONS ##############################################################################################

    cls

    Write-host "Deploy Windows server" -foregroundcolor red

    $Hostname = Read-Host -Prompt "Hostname"

    If ($Hostname.Length -gt 15) {write-Host -ForegroundColor Red "$Hostname is an invalid hostname"; break}

    $IP = Read-Host -Prompt "IP Address (press ENTER for DHCP)"

    If (-not (Test-IP $IP)) {write-Host -ForegroundColor Red "$IP is an invalid address"; break}

    $JoinDomainYN = Read-Host "Join Domain $Domain (Y/N)"

    ### READ CREDENTIALS ########################################################################################################

    $User = "xxx"

    $PasswordFile = "AESpassword.txt"

    $KeyFile = "AES.key"

    $key = Get-Content $KeyFile

    $VIcred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, (Get-Content $PasswordFile | ConvertTo-SecureString -Key $key)

    ### CONNECT TO VCENTER ##############################################################################################

    Get-Module -ListAvailable VMware* | Import-Module | Out-Null

    Connect-VIServer $vCenter -Credential $VIcred

    $SourceVMTemplate = Get-Template -Name $VMTemplate

    $SourceCustomSpec = Get-OSCustomizationSpec -Name $CustomSpec

    ### DEFINE POWERSHELL SCRIPTS TO RUN IN VM AFTER DEPLOYMENT ############################################################################################################

    if ($IP) {

    Add-Script "New-NetIPAddress -InterfaceIndex 2 -IPAddress %1 -PrefixLength %2 -DefaultGateway %3" @($IP, $SubnetLength, $GW)

    Add-Script "Set-DnsClientServerAddress -InterfaceIndex 2 -ServerAddresses %1" @($IP_DNS) }

    if ($JoinDomainYN.ToUpper() -eq "Y") {

    Add-Script '$DomainUser = %1;

                $DomainPWord = ConvertTo-SecureString -String %2 -AsPlainText -Force;

                $DomainCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $DomainUser, $DomainPWord;

                 Add-Computer -DomainName %3 -Credential $DomainCredential' @("$Domain\$DomainAdmin",$DomainAdminPassword, $Domain) $true }

    ### DEPLOY VM ###############################################################################################################################################################

    Write-Host "Deploying Virtual Machine with Name: [$Hostname] using Template: [$SourceVMTemplate] and Customization Specification: [$SourceCustomSpec] on cluster: [$cluster]"

    New-VM -Name $Hostname -Template $SourceVMTemplate -ResourcePool $cluster -OSCustomizationSpec $SourceCustomSpec -Location $Location `

      -Datastore $Datastore -DiskStorageFormat $DiskStorageFormat | Out-Null

    Get-VM $Hostname | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $NetworkName -confirm:$false | Out-Null

    Set-VM -VM $Hostname -NumCpu $CPU -MemoryGB $Memory -Confirm:$false | Out-Null

    Get-VM $Hostname | Get-HardDisk | Where-Object {$_.Name -eq "Hard Disk 1"} | Set-HardDisk -CapacityGB $DiskCapacity -Confirm:$false | Out-Null

    Write-Host "Virtual Machine $Hostname Deployed. Powering On"

    Start-VM -VM $Hostname | Out-Null

    if (-not (Check-CustomizationStarted $Hostname)) { break }; if (-not (Check-CustomizatonFinished $Hostname)) { break }

    foreach ($script in $scripts)

    {

        Invoke-VMScript -ScriptText $script[0] -VM $Hostname -GuestCredential $VMLocalCredential | Out-Null

        if ($script[1]) {Restart-VM $Hostname}

    }

    ### END OF SCRIPT ##############################

    Write-Host "Deployment of VM $Hostname finished"



  • 2.  RE: Simple Deploy VM Script

    Posted Sep 03, 2019 11:42 AM

    Instead of assigning a static value, you could do  something like this

    $VMTemplate = Get-Template | Out-GridView -OutputMode Single | Select -ExpandProperty Name

    $CustomSpec = Get-OSCustomizationSpec | Out-GridView -OutputMode Single | select -ExpandProperty Name



  • 3.  RE: Simple Deploy VM Script

    Posted Sep 03, 2019 12:43 PM

    Yes, nice! But if I don´t want it to be OutGrid? Just want them numbered so I can select a number....."Ingrid" :-)



  • 4.  RE: Simple Deploy VM Script

    Posted Sep 03, 2019 12:54 PM

    You mean something along these lines?

    $i = 0

    $templates = Get-Template | select -ExpandProperty Name

    $templates | ForEach-Object -Process {

       $i++

       Write-Host "$i $_"

    }

    $answer = Read-Host -Prompt "`nSelect the template (1-$($templates.Count))"

    $VMTemplate = $templates[$answer-1]



  • 5.  RE: Simple Deploy VM Script

    Posted Sep 04, 2019 05:49 AM

    Yes exactly! Can I do the same to OSCustomizationSpec?



  • 6.  RE: Simple Deploy VM Script

    Posted Sep 04, 2019 06:19 AM

    Sure, just change the Get-Template cmdlet to Get-OSCustomizationSpec.

    And adapt the names of the variables of course.



  • 7.  RE: Simple Deploy VM Script

    Posted Sep 04, 2019 08:44 AM

    What have I missed?

    I get:

    New-VM : 2019-09-04 10:36:52 New-VM Could not find OSCustomizationSpec with name 'W'.

    At C:\SCRIPTS\Deploy-VM.ps1:146 char:1

    ### CONNECT TO VCENTER ##############################################################################################

    Get-Module -ListAvailable VMware* | Import-Module | Out-Null

    Connect-VIServer $vCenter -Credential $VIcred

    $i = 0

    $templates = Get-Template | select -ExpandProperty Name

    $templates | ForEach-Object -Process {

       $i++

       Write-Host "$i $_"

    }

    $answer = Read-Host -Prompt "`nSelect Template (1-$($templates.Count))"

    $SourceVMTemplate = $templates[$answer-1]

    $i = 0

    $customizations = Get-OSCustomizationSpec | select -ExpandProperty Name

    $customizations | ForEach-Object -Process {

       $i++

       Write-Host "$i $_"

    }

    $answer = Read-Host -Prompt "`nSelect the customization type (1-$($customizations.Count))"

    $SourceCustomSpec = $customizations[$answer-1]

    ### DEFINE POWERSHELL SCRIPTS TO RUN IN VM AFTER DEPLOYMENT ############################################################################################################

    if ($IP) {

    Add-Script "New-NetIPAddress -InterfaceIndex 2 -IPAddress %1 -PrefixLength %2 -DefaultGateway %3" @($IP, $SubnetLength, $GW)

    Add-Script "Set-DnsClientServerAddress -InterfaceIndex 2 -ServerAddresses %1" @($IP_DNS) }

    if ($JoinDomainYN.ToUpper() -eq "Y") {

    Add-Script '$DomainUser = %1;

                $DomainPWord = ConvertTo-SecureString -String %2 -AsPlainText -Force;

                $DomainCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $DomainUser, $DomainPWord;

                 Add-Computer -DomainName %3 -Credential $DomainCredential' @("$Domain\$DomainAdmin",$DomainAdminPassword, $Domain) $true }

    ### DEPLOY VM ###############################################################################################################################################################

    Write-Host "Deploying Virtual Machine with Name: [$Hostname] using Template: [$SourceVMTemplate] and Customization Specification: [$SourceCustomSpec] on cluster: [$cluster]"

    New-VM -Name $Hostname -Template $SourceVMTemplate -ResourcePool $cluster -OSCustomizationSpec $SourceCustomSpec -Location $Location -Datastore $Datastore -DiskStorageFormat $DiskStorageFormat | Out-Null



  • 8.  RE: Simple Deploy VM Script
    Best Answer

    Posted Sep 04, 2019 08:50 AM

    If there is only 1 template or 1 OSCustomizationSpec, the variables $templates and $customizations will not be arrays.
    In that case, the indexing ([$answer - 1]) takes 1 character from the string.

    We can force the variables to be arrays, een if there is only 1 entry.

    Like this

    $i = 0

    $templates = @(Get-Template | select -ExpandProperty Name)

    $templates | ForEach-Object -Process {

       $i++

       Write-Host "$i $_"

    }

    $answer = Read-Host -Prompt "`nSelect Template (1-$($templates.Count))"

    $SourceVMTemplate = $templates[$answer-1]


    $i = 0

    $customizations = @(Get-OSCustomizationSpec | select -ExpandProperty Name)

    $customizations | ForEach-Object -Process {

       $i++

       Write-Host "$i $_"

    }

    $answer = Read-Host -Prompt "`nSelect the customization type (1-$($customizations.Count))"

    $SourceCustomSpec = $customizations[$answer-1]

    ---------------------------------------------------------------------------------------------------------

    Was it helpful? Let us know by completing this short survey here.



  • 9.  RE: Simple Deploy VM Script

    Posted Sep 04, 2019 08:59 AM

    GREAT!! Thank  you for all the help!!!



  • 10.  RE: Simple Deploy VM Script

    Posted Mar 23, 2021 01:39 PM

    Would you please share your final script?