thebart
Enthusiast
Enthusiast

Unable to deploy multiple VMs per line of the imported CSV

Jump to solution

The below works when I have a single line in the CSV and successfully deploys the VM with the variables. 

$BuildVar = Import-csv "D:\2019DatacenterTest.csv" -ErrorAction Stop
$BuildVar | Foreach-Object {
	$IPadd = $_.IPadd
    $Mask = $_.Mask
    $Gateway = $_.Gateway
    $DNS1 = $_.DNS1
    $DNS2 = $_.DNS2
	$Vlan = $_.Vlan
    $Tplate = $_.Tplate
    $CustOS = $_.CustOS
    $Cluster = $_.Cluster
	$VMserver = $_.VMserver
	$OU = $_.OU
	$Disk1 = $_.Disk1
	$Disk2 = $_.Disk2
    $WO = $_.WO
	$Notes = $_.Notes
    $Cores = $_.Cores
    $Memory = $_.Memory
    $vCenter = $_.vCenter
}


##########################################################################################################################################################
# Connect to vCenter                                                                                                                                     #
##########################################################################################################################################################
#add-pssnapin vmware.vimautomation.core
Connect-VIServer $vcenter

##########################################################################################################################################################
# Configuring Customization & Deploy VM                                                                                                                  #
##########################################################################################################################################################
$datastore = Get-Cluster -Name $Cluster | Get-Datastore | Sort-Object -Property FreeSpaceGB -Descending:$true | Select-Object -First 1
$vHost = Get-Cluster $Cluster | Get-VMHost | Sort-Object -Property MemoryUsageGB -Descending:$false | Select-Object -First 1

$OSSpecs = Get-OSCustomizationSpec -Name $CustOS

$NicMapping = Get-OSCustomizationNicMapping -OSCustomizationSpec $OSSpecs.Name

Remove-OSCustomizationNicMapping –OSCustomizationNicMapping $NicMapping -Confirm:$false

$NicMapping = Get-OSCustomizationNicMapping -OSCustomizationSpec $OSSpecs.Name

New-OSCustomizationNicMapping -OSCustomizationSpec $OSSpecs.Name | Set-OSCustomizationNicMapping -IpMode UseStaticIP -IpAddress $IPadd -SubnetMask $Mask -DefaultGateway $Gateway -Dns $DNS1, $DNS2

New-VM -Name $VMserver -Template $Tplate -OSCustomizationSpec $OSSpecs -VMHost $vhost -Datastore $datastore -ErrorAction Stop

$Nic = Get-NetworkAdapter -VM $VMserver
Set-NetworkAdapter -NetworkAdapter $Nic -NetworkName $Vlan -Confirm:$false

Start-VM $VMserver

 

When i try to nest it with a foreach () it never creates the second or third VM's in the CSV and the frustrating part is that there are no errors.

foreach ($line in $BuildVar) {

$datastore = Get-Cluster -Name $Cluster | Get-Datastore | Sort-Object -Property FreeSpaceGB -Descending:$true | Select-Object -First 1
$vHost = Get-Cluster $Cluster | Get-VMHost | Sort-Object -Property MemoryUsageGB -Descending:$false | Select-Object -First 1

$OSSpecs = Get-OSCustomizationSpec -Name $CustOS

$NicMapping = Get-OSCustomizationNicMapping -OSCustomizationSpec $OSSpecs.Name

Start-Sleep -Seconds 1

Remove-OSCustomizationNicMapping –OSCustomizationNicMapping $NicMapping -Confirm:$false

Start-Sleep -Seconds 1

$NicMapping = Get-OSCustomizationNicMapping -OSCustomizationSpec $OSSpecs.Name

New-OSCustomizationNicMapping -OSCustomizationSpec $OSSpecs.Name | Set-OSCustomizationNicMapping -IpMode UseStaticIP -IpAddress $IPadd -SubnetMask $Mask -DefaultGateway $Gateway -Dns $DNS1, $DNS2

New-VM -Name $VMserver -Template $Tplate -OSCustomizationSpec $OSSpecs -VMHost $vhost -Datastore $datastore -ErrorAction Stop

$Nic = Get-NetworkAdapter -VM $VMserver
Set-NetworkAdapter -NetworkAdapter $Nic -NetworkName $Vlan -Confirm:$false

Start-VM $VMserver

}
0 Kudos
2 Solutions

Accepted Solutions
LucD
Leadership
Leadership

The basic problem in your code is that you populate variables from the Import-Csv.
But since you have nothing else in that loop, you will have the values of the last row of your CSV in your variables.
That's why it works with only 1 line in the CSV, but not with multiple lines.

The layout of your code should be, in skeleton format

# Preparation
Connect-VIServer ...

# Loop through CSV

Import-CSV ... |
Foreach-Object -Process {

# Use the properties in $_ to do the cmdlets

}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

mmcgill1
VMware Employee
VMware Employee

I would strongly advise on always taking LucD's suggestions over mine! ;-).  He is a rock star!

View solution in original post

9 Replies
thebart
Enthusiast
Enthusiast

I was attempting to deploy these in parallel with -async but i think i need to figure this out first

0 Kudos
mmcgill1
VMware Employee
VMware Employee

Part of the issue is that in your foreach($line in $BuildVar) the property values will need to be specified as $line.IPadd, $line.Mask, $line.Gateway, etc. 

So your first line to get your datastore would be:

$datastore = Get-Cluster -Name $line.Cluster | Get-Datastore | Sort-Object -Property FreeSpaceGB -Descending:$true | Select-Object -First 1

In your example, you are using just the stand alone variable ($IPadd, $Mask, $Gateway, etc), so you are calling the same variables for every new line of the foreach, so only the first one is being created.  The fact that one VM is created is most likely because you have already specified those variables in your session, otherwise they would be null.

You should be able to create the VMs asynchronously still, but if you want some success or failure/error notice, you will have to create tasks for each VM creation and monitor it for completion.  Not terribly complex to do, but you would definitely need to expand the script a bit!

LucD
Leadership
Leadership

The basic problem in your code is that you populate variables from the Import-Csv.
But since you have nothing else in that loop, you will have the values of the last row of your CSV in your variables.
That's why it works with only 1 line in the CSV, but not with multiple lines.

The layout of your code should be, in skeleton format

# Preparation
Connect-VIServer ...

# Loop through CSV

Import-CSV ... |
Foreach-Object -Process {

# Use the properties in $_ to do the cmdlets

}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

thebart
Enthusiast
Enthusiast

 

Import-csv "D:\DatacenterTest.csv" -ErrorAction Stop |
ForEach-Object -process {

$datastore = Get-Cluster -Name $_.Cluster | Get-Datastore | Sort-Object -Property FreeSpaceGB -Descending:$true | Select-Object -First 1
$vHost = Get-Cluster $_.Cluster | Get-VMHost | Sort-Object -Property MemoryUsageGB -Descending:$false | Select-Object -First 1

$_.Disk1
$_.Vlan
$vHost.Name
$datastore.Name

}

 

 

This worked perfectly

0 Kudos
thebart
Enthusiast
Enthusiast

Thank you mmcgill1, this worked as well but i'm assuming its best practice to use LucD's method.

0 Kudos
mmcgill1
VMware Employee
VMware Employee

I would strongly advise on always taking LucD's suggestions over mine! ;-).  He is a rock star!

thebart
Enthusiast
Enthusiast

@LucD @mmcgill1 - thanks again for the help with this item. Should i create a separate post to modify the behavior of this script to be able to deploy multiple vm's concurrently? 

Right now, it is working as should, but it would be much more efficient if it would be able to deploy multiple VMs at the same time.

Many thanks in advance!

Is it as simple as adding "-RunAsync" to the end of each loop?

Import-CSV ... |
Foreach-Object -Process { doing stuff } -RunAsync

 

0 Kudos
LucD
Leadership
Leadership

Yes and no.
With RunAsync you can indeed launch multiple tasks that will run in the background.
The issue might be that you could overload your system.
That's why most of the time a max counter is defined, to specify how many of such background tasks can run in parallel.

Another issue is how to check the outcome of such background tasks.
You do want to know if they were completed successfully or not.
You will have to retrieve, when you are sure a Task is finished, the Task object and check the result.

There are many sample scripts that do just that available in this community


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference