Author Posts

June 18, 2014 at 7:58 am

Hello,

I've written an advanced function that creates one or more VMs. I have a mandatory parameter and many optional, with some default values.
The problem is with the optional parameters that have no default values.
For example, there is a SwitchName parameter that is optional and has no default value, because I don't always want a new VM to connect to a switch.
However, if I don't supply any value to it, I get this error, even if I use [AllowEmptyString()] and [AllowNull()] in the PARAM block:

New-VM : Cannot validate argument on parameter 'SwitchName'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.

I want the VMs to connect to a switch only if I supply the name of a switch, otherwise I don't want them to connect anywhere. How can I do this?
These are the relevant snippets from my code (sorry for the formatting, it gets messed up whatever I do):

#Some of the parameters:

[CmdletBinding()]
param(
      [Parameter(Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName)]
      [string[]]
      $VMName,

      [Parameter(ValueFromPipeline,
            ValueFromPipelineByPropertyName)]
      [AllowEmptyString()]
      [AllowNull()]
      [string]
      $SwitchName,

#And so on
)

PROCESS
{
        foreach ($name in $VMName)
        {
                $params = @{Name                          = $name
                                         MemoryStartupBytes = $MemoryStartupBytes
                                         Generation                  = 2
                                         NewVHDPath             = "$VHDFolder\$name\$name.vhdx"
                                         NewVHDSizeBytes    = $VHDSizeBytes
                                         BootDevice                  = $BootDevice
                                         ComputerName         = $ComputerName
                                         Path                              = $VMFolder
                                         SwitchName               = $SwitchName}
                New-VM @params

                $params = @{VMName                              = $name
                                          DynamicMemoryEnabled = $true
                                          MinimumBytes                    = $MemoryMinimumBytes
                                          ComputerName                 = $ComputerName}
                Set-VMMemory @params
         }
}

June 18, 2014 at 8:02 am

You need to put some logic in your script to not provide the SwitchName parameter is $SwitchName is null.

if ($SwitchName) { $params.Add('SwitchName',$SwitchName) }

Something along those lines.

The [AllowNull()] means YOUR FUNCTION will accept a null value from $SwitchName. That doesn't mean New-VM will do so.

June 18, 2014 at 8:03 am

How do you call the function?

June 18, 2014 at 8:04 am

The problem is probably when you pass a null / empty value to the New-VM cmdlet, based on what I can see so far. You'd need to construct your $params hashtable so that only the defined parameters are in it when you call New-VM. Something like this:

$params = @{
    Name               = $name
    MemoryStartupBytes = $MemoryStartupBytes
    Generation         = 2
    NewVHDPath         = "$VHDFolder\$name\$name.vhdx"
    NewVHDSizeBytes    = $VHDSizeBytes
    BootDevice         = $BootDevice
    ComputerName       = $ComputerName
    Path               = $VMFolder
}
 
if ($SwitchName) { $params['SwitchName'] = $SwitchName }
 
New-VM @params

Also, you'll want to get away from using ValueFromPipeline in combination with String parameters. Any object can be represented as a string, which will give you some crazy results when you start trying to pipe objects to this function. Based on what I see so far, ValueFromPipelineByPropertyName should be all you need.

June 18, 2014 at 8:15 am

Thank you a lot, it works! I haven't encountered this amazing technique yet.
Thank you for the advice on pipeline imput too!