After reading PowerShell in a month of lunches I tend to write parameterized scripts with advanced [CmdletBinding()] method.
I hit a snag when trying to write a script to deploy multiple VMs to VMware vCloud Director. The cmdlet I am using (New-CIVM) seems to only have the functionality to create one VM. If I pass 2 computer names to it fails.
The only workaround I could get working was to use a foreach loop:
The script I wanted to write should work from the cli in this fashion:
.\New-CiVM.ps1 -vapp 'VDC1 servers' -name SRVR-TEST1,SRVR-TEST2 -hostname TEST-DC1,TEST-DC2 -os Win2019,win2016
Is the problem that VMware has not developed the cmdlet properly to work in this setup or is there another way in which I can collect the variables for multiple VMs without using a csv file?
Welcome to Powershell.org. Do not think you understand what [CmdLetBinding()] does. You can read the link below, but when you have that at the top of a function, that is just turning on some of the built in behaviors like Verbose and Debug. If you don’t have that defined and try to use Write-Verbose in a function, it won’t work because you have not ‘enabled’ that in the function:
From what you post says, there are several relevant things you need to understand. It’s hard to know exactly what because you did not post the function code, just the function calls and you said it failed but not what error occurred or the behavior observed. Here are key pieces:
String[] - This indicates the param is a string array. If this is defined as just string (e.g. [string]$Name), it's expecting one value.
for loop - Since you want to process each name, there should be a for loop to process for every name.
ValueFromPipeline=$true - This is getting a bit more advanced, but if you want to pipe the names to the function, you need to set this in the parameter block. You can only have one parameter set with ValueFromPipeline that will be processes in the PROCESS{} block.
Here is a shell function to play with:
function New-CIVM {
[CmdletBinding()]
param (
[Parameter(
Mandatory=$true,
ValueFromPipeline=$true
)]
[String[]]$Name
)
begin {}
process {
foreach ($n in $name) {
Write-Verbose "Processing $n"
}
}
end {}
}
New-CiVM -Name 'host1','host2' -Verbose
#ValueFromPipeline
'host1','host2' | New-CiVM -Verbose
Sorry, I should have posted the full code, but it’s not a function which I am using it is just a parameterized script with cmdletbinding set. I have tested functions before but seemed to have issues in getting them to load when moving the script around to different servers so I tend not to use them. I tested the script for creating one VM and it works. It is only when I changed it to for loop and string array it broke and does not report back any error.
Try to make a look on the supported parameter for the command from here
This command may not accept an array or it might need some additional input from a different command pipeline, to be able to provide better help, kindly share the error message you are getting when you run the script.
I agree with @Rob about the usage of cmdletbinding(), its totally fine and you can write your script without it, and just use the regular parameters and the validation set.
Yes, I think that it maybe doesn’t accept an array. The VMware documentation doesn’t have much information in and only one example. How do you confirm it accepts an array? There is no error message to share, the command runs and does nothing at all.
Here is the full help page:
PS S:\Scripts\Delivery\provisioning\vCloud> help new-civm -full
NAME
New-CIVM
SYNOPSIS
This cmdlet creates a new cloud virtual machine.
SYNTAX
New-CIVM [[-Name] <String>] [-ComputerName <String>] [-RunAsync] [-Server <CIServer[]>] -VApp <CIVApp> -VMTemplate <CIVMTemplate> [-Confirm] [-WhatIf]
[<CommonParameters>]
DESCRIPTION
This cmdlet creates a new cloud virtual machine into an existing vApp by using a specified virtual machine template.
PARAMETERS
-ComputerName <String>
Specifies the computer name to be applied to the cloud virtual machine with guest customization.
Required? false
Position? named
Default value None
Accept pipeline input? False
Accept wildcard characters? false
-Name <String>
Specifies the name of the newly created cloud virtual machine. If not specified, the name of the virtual machine template is used.
Required? false
Position? 1
Default value None
Accept pipeline input? False
Accept wildcard characters? false
-RunAsync [<SwitchParameter>]
Indicates that the command returns immediately without waiting for the task to complete. In this mode, the output of the cmdlet is a Task object. For more
information about the RunAsync parameter run "help About_RunAsync" in the VMware PowerCLI console.
Required? false
Position? named
Default value False
Accept pipeline input? False
Accept wildcard characters? false
-Server <CIServer[]>
Specifies the cloud servers on which you want to run the cmdlet. If no value is given to this parameter, the command runs on the default servers. For more
information about default servers, see the description of Connect-CIServer.
Required? false
Position? named
Default value None
Accept pipeline input? False
Accept wildcard characters? true
-VApp <CIVApp>
Specifies the vApp to which you want to add the cloud virtual machine.
Required? true
Position? named
Default value None
Accept pipeline input? True (ByValue)
Accept wildcard characters? false
-VMTemplate <CIVMTemplate>
Specifies the virtual machine template from which the new cloud virtual machine is created.
Required? true
Position? named
Default value None
Accept pipeline input? True (ByValue)
Accept wildcard characters? false
-Confirm [<SwitchParameter>]
If the value is $true, indicates that the cmdlet asks for confirmation before running. If the value is $false, the cmdlet runs without asking for user
confirmation.
Required? false
Position? named
Default value False
Accept pipeline input? False
Accept wildcard characters? false
-WhatIf [<SwitchParameter>]
Indicates that the cmdlet is run only to display the changes that would be made and actually no objects are modified.
Required? false
Position? named
Default value False
Accept pipeline input? False
Accept wildcard characters? false
<CommonParameters>
This cmdlet supports the common parameters: Verbose, Debug,
ErrorAction, ErrorVariable, WarningAction, WarningVariable,
OutBuffer, PipelineVariable, and OutVariable. For more information, see
about_CommonParameters (https:/go.microsoft.com/fwlink/?LinkID=113216).
INPUTS
OUTPUTS
The newly created CIVM object
NOTES
-------------------------- Example 1 --------------------------
$templateVM = Get-CIVMTemplate -Name "myTemplateVM"
Get-CIVApp "myCIVApp" | New-CIVM -Name "myVM" -VMTemplate $templateVM
Retrieves a virtual machine template named "myTemplateVM", creates a cloud virtual machine named "myVM" from the template, and adds it to a cloud vApp named
"myCIVApp".
RELATED LINKS
Online Version: https://code.vmware.com/doc/preview?id=6330#/doc/New-CIVM.html
Get-CIVM
Restart-CIVM
Start-CIVM
Suspend-CIVM
I don’t think this code will work, the Name should be a string and what you pass is not considered as a string. it should be an object.
As I saw on VMWare help, this command position is 1 try the following, I don’t think it will work as this command don’t accept input from pipeline, but give it a try
I tried another variation too by removing the non-mandatory hostname, but it didn’t work:
PS S:\Scripts\Delivery\provisioning\vCloud> "SRVR-JAMESTEST1","SRVR-JAMESTEST2" | New-CIVM -VApp 'PROV mkn1clouc2 VDC1 servers' -ComputerName JW-DC1,JW-DC2 -VMTemplate (Get-CIVMTemplate | Where-Object {$_.Name -clike "*2012*" -and $_.OrgVdc -clike "PEC2 mkn1clouc2 VDC"})
New-CIVM : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'ComputerName'. Specified method is not supported.
At line:1 char:99
+ ... pp 'PROV mkn1clouc2 VDC1 servers' -ComputerName JW-DC1,JW-DC2 -VMTemp ...
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [New-CIVM], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,VMware.VimAutomation.Cloud.Commands.Cmdlets.NewCIVM
PS S:\Scripts\Delivery\provisioning\vCloud> "SRVR-JAMESTEST1","SRVR-JAMESTEST2" | New-CIVM -VApp 'PROV mkn1clouc2 VDC1 servers' -VMTemplate (Get-CIVMTemplate | Where-Object {$_.Name -clike "*2012*" -and $_.OrgVdc -clike "PEC2 mkn1clouc2 VDC"})
New-CIVM : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do
not match any of the parameters that take pipeline input.
At line:1 char:39
+ ... MESTEST2" | New-CIVM -VApp 'PROV mkn1clouc2 VDC1 servers' -VMTemplate ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (SRVR-JAMESTEST1:String) [New-CIVM], ParameterBindingException
+ FullyQualifiedErrorId : InputObjectNotBound,VMware.VimAutomation.Cloud.Commands.Cmdlets.NewCIVM
New-CIVM : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do
not match any of the parameters that take pipeline input.
At line:1 char:39
+ ... MESTEST2" | New-CIVM -VApp 'PROV mkn1clouc2 VDC1 servers' -VMTemplate ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (SRVR-JAMESTEST2:String) [New-CIVM], ParameterBindingException
+ FullyQualifiedErrorId : InputObjectNotBound,VMware.VimAutomation.Cloud.Commands.Cmdlets.NewCIVM
Thread has gone very quiet. Am I correct in thinking this cmdlet is very limited in its options for creating multiple VMs and the only available option is to use a CSV file and for loop? Struggling to come up with any more creative solutions. Seems like a very poor effort from VMware
The short answer is probably yes, there are limited options. Typically, if you’re managing a single item, that is a limitation on the API in the backend. Even if you create a wrapper for the function or try to edit the function, you are essentially just adding a for loop around their call that is affecting a single item.
[quote quote=288373]Thread has gone very quiet. Am I correct in thinking this cmdlet is very limited in its options for creating multiple VMs and the only available option is to use a CSV file and for loop? Struggling to come up with any more creative solutions. Seems like a very poor effort from VMware
[/quote]
I am using also VMWare and got stuck in similar issues multiple time, I am using vCenter vSphere, not Cloud Infra
thanks, guys. Interesting you mention APIs Rob as this seems to be a common problem I run into. I often find I can’t do what I need to with Powershell then after searching online people start talking about interacting with APIs using Powershell. At this point is where I usually get lost. Is it common for Powershell users to learn APIs or is that typically for the hardcore programmers?
Not sure I would denote myself as a hard core programmer, but I do consulting for products and need product A to integrate with product B. Powershell has grown exponentially with core cmdlets and then with online repositories. When someone asks to perform Create Read Update Delete (CRUD) in an application, I start by searching for a module or code examples (no need to re-invent the wheel) and then go down the rabbit hole REST API, SOAP API, cmd, database until I get accomplish what is required. API’s are typically built to support a GUI function, not to support massive multi-threaded automation operations, hence why you see single operation methods from a vendor.