New-UDForm Question/Issue

Product: PowerShell Universal
Version: 3.x.x

Long time viewer first time poster…

My question… When working with New-UDForm are there any known issues or rather gotcha’s when using IF/Else inside the OnSubmit clause?

I’m building a web form for vCenter deployments, and its 99% complete aside from a checkbox I implemented to choose DHCP or NoDHCP. I can make it deploy and give feedback until I implement the If/Else inside the New-UDForm OnSubmit.

I would think it’s your code in this case. I have forms that use if/else and have not encountered any problems. Posting the code could help point you in the right direction :slight_smile:

Apologies, I was away. I can certainly provide more code as required but here are the contents of the OnSubmit clause.


-OnSubmit {
    $session:VMHost = Get-VMHost -location $datacenter -server $session:VC | Sort-Object $_.CPuUsageMhz -Descending | Select-Object -First 1
    $session:IsDHCP = (Get-UDElement -Id 'dhcpselector')
    If ($session:IsDHCP.Checked -eq $True) {
        Show-UDToast -message "DHCP: $($session:IsDHCP.Checked) Selected" -Duration 10000 -MessageColor RED
        $datacenter = $(get-udelement -Id datacenter).value
        $network = $(get-udelement -Id network).value
    
        $cluster = $(get-udelement -Id cluster).value
        $storagecluster = $(Get-udelement -id storagecluster).value
        $template = $(get-udelement -Id template).value
        $vmfolder = $(get-udelement -Id vmfolder).value
        $vmguestname = $(Get-UDElement -Id vmguestname).value
        $customization = $(Get-UDElement -Id customization).value
        $vcpu = $(get-udelement -Id cpu).value
        $vram = $(get-udelement -Id ram).value
        $vmguestip = $(get-udelement -Id vmguestip).value
        $vmguestgw = $(get-udelement -Id vmguestgateway).value
        $vmguestmask = $(get-udelement -Id vmguestmask).value
        $vmguestdns1 = $(get-udelement -Id vmguestdns1).value
        $vmguestdns2 = $(get-udelement -Id vmguestdns2).value
        # END DATA GATHER
        Set-UDElement -Id 'vHost' -content {
            New-UDTable -Data $session:VMHost -Columns $Hcolumns -Size small
        }
        Get-OSCustomizationSpec $customization -server $session:VC | New-OSCustomizationSpec -Name 'temp0001' -Type NonPersistent
        Get-OSCustomizationSpec 'temp0001' -server $session:VC | Get-OSCustomizationNicMapping | `
            Set-OSCustomizationNicMapping -server $session:VC -IpMode UseDhcp
        $Session:BuildTask = New-VM -server $session:VC -Name $vmguestname -Template $template -VMHost $session:VMHost `
            -Location $vmfolder -DiskStorageFormat thin -Datastore $storagecluster `
            -ResourcePool $cluster -OSCustomizationSpec 'temp0001' `
            -Confirm:$false -RunAsync
        Set-UDElement -Id 'DeployTask' -content {
            New-UDDynamic -Id 'percent' -AutoRefresh -Content {
                IF ($Session:BuildTask.PercentComplete -ne 100 ) {
                    $Session:BuildTask = Get-task -Id $Session:BuildTask.Id
                    New-UDTypography -Text "Deployment in progress"
                    New-UDProgress -PercentComplete $Session:BuildTask.PercentComplete
                }
                IF ($Session:BuildTask.PercentComplete -eq 100 ) {
                    New-UDTypography -Text "Deployment Complete"
                    New-UDProgress -PercentComplete 100
                }
            }
        }
        Wait-Task -Task $Session:BuildTask
        Remove-OSCustomizationSpec 'temp0001' -server $session:VC -Confirm:$false
        start-sleep -seconds 2
        $VMConfig = Get-VM -name $vmguestname -server $session:VC #| Where-object {$_.Name -eq $vmguestname -and $_.PowerState -eq 'PoweredOff'}
        $VMConfig | Set-VM -NumCpu $vcpu -MemoryGB $vram -Confirm:$false
        $VMConfig | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $network -Confirm:$false
        start-sleep -seconds 2
        Set-UDElement -Id 'vGuest' -content {
            write-output "$vmguestname"
            New-UDButton -id 'txtpoweron' -Text " PowerOn " -Size large -Color success -OnClick {
                $VMConfig | Start-VM
            }
        }
    
        start-sleep -seconds 2
    }
    If ($session:IsDHCP.Checked -eq $False) {
        Show-UDToast -message "DHCP: $($session:IsDHCP.Checked) Selected" -Duration 10000 -MessageColor RED
        $datacenter = $(get-udelement -Id datacenter).value
        $network = $(get-udelement -Id network).value
        $cluster = $(get-udelement -Id cluster).value
        $storagecluster = $(Get-udelement -id storagecluster).value
        $template = $(get-udelement -Id template).value
        $vmfolder = $(get-udelement -Id vmfolder).value
        $vmguestname = $(Get-UDElement -Id vmguestname).value
        $customization = $(Get-UDElement -Id customization).value
        $vcpu = $(get-udelement -Id cpu).value
        $vram = $(get-udelement -Id ram).value
        $vmguestip = $(get-udelement -Id vmguestip).value
        $vmguestgw = $(get-udelement -Id vmguestgateway).value
        $vmguestmask = $(get-udelement -Id vmguestmask).value
        $vmguestdns1 = $(get-udelement -Id vmguestdns1).value
        $vmguestdns2 = $(get-udelement -Id vmguestdns2).value

        # END DATA GATHER
        Set-UDElement -Id 'vHost' -content {
            New-UDTable -Data $session:VMHost -Columns $Hcolumns -Size small
        }
        Get-OSCustomizationSpec $customization -server $session:VC | New-OSCustomizationSpec -Name 'temp0001' -Type NonPersistent
        Get-OSCustomizationSpec 'temp0001' -server $session:VC | Get-OSCustomizationNicMapping | `
            Set-OSCustomizationNicMapping -server $session:VC -IpMode UseStaticIP `
            -IpAddress $vmguestip -SubnetMask $vmguestmask `
            -DefaultGateway $vmguestgw -Dns $vmguestdns1, $vmguestdns2
        $Session:BuildTask = New-VM -server $session:VC -Name $vmguestname -Template $template -VMHost $session:VMHost `
            -Location $vmfolder -DiskStorageFormat thin -Datastore $storagecluster `
            -ResourcePool $cluster -OSCustomizationSpec 'temp0001' `
            -Confirm:$false -RunAsync
        Set-UDElement -Id 'DeployTask' -content {
            New-UDDynamic -Id 'percent' -AutoRefresh -Content {
                IF ($Session:BuildTask.PercentComplete -ne 100 ) {
                    $Session:BuildTask = Get-task -Id $Session:BuildTask.Id
                    New-UDTypography -Text "Deployment in progress"
                    New-UDProgress -PercentComplete $Session:BuildTask.PercentComplete
                }
                IF ($Session:BuildTask.PercentComplete -eq 100 ) {
                    New-UDTypography -Text "Deployment Complete"
                    New-UDProgress -PercentComplete 100
                }
            }
        }
        Wait-Task -Task $Session:BuildTask
        Remove-OSCustomizationSpec 'temp0001' -server $session:VC -Confirm:$false
        start-sleep -seconds 2
        $VMConfig = Get-VM -name $vmguestname -server $session:VC #| Where-object {$_.Name -eq $vmguestname -and $_.PowerState -eq 'PoweredOff'}
        $VMConfig | Set-VM -NumCpu $vcpu -MemoryGB $vram -Confirm:$false
        $VMConfig | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $network -Confirm:$false
        start-sleep -seconds 2
        Set-UDElement -Id 'vGuest' -content {
            write-output "$vmguestname"
            New-UDButton -id 'txtpoweron' -Text " PowerOn " -Size large -Color success -OnClick {
                $VMConfig | Start-VM
            }
        }
    
        start-sleep -seconds 2
    }

    
}

How long does the wait-task and start-sleep part last? This is the only part that scares me, the rest seems normal. Perhaps you should find a way to decouple the tasks.

is tied to the Wait-Task, and timing all depends on the vCenter; the production vCenter is roughly 2-5 seconds for the Wait-Task to complete while my Dev vCenter is closer too 40-60 seconds. And the Start-Sleep are likely not needed, but there are some times during high I/O that vcenter isn’t actually done when it says it is. Kinda like rebooting your computer… it’s not quite ready “as soon” as you login, you wait for some apps to preload a couple seconds later. I’ll remove them and test again. Just to circle back: the removal of the entire IF seems to get around the issue, but then I lose the DHCP option. And for now, that may to suffice. Or at least move the question to a prior page.

All these are good questions to bring up, and I appreciate everyone’s time.

First, quality of life. Instead of doing $(get-udelement -Id $ID).value you have access to the $Eventdata variable.

For example…
$Eventdata.dhcpselector
returns “True” or “False”.

$Eventdata.datacenter
Will already contain the value of…
$(get-udelement -Id datacenter).value

Second, when you are running the same command twice, I recommend using splatting.

So you would do something like this…

$OSCNM = @{
    Server = $session:VC
}
If ($EventData.dhcpselector -eq $false) {
    $OSCNM.Add('IPMode', 'UseStaticIP')
    $OSCNM.Add('IpAddress', $EventData.vmguestip)
    $OSCNM.Add('SubnetMask', $EventData.vmguestmask)
    $OSCNM.Add('DefaultGateway', $EventData.vmguestgateway)
    $OSCNM.Add('Dns', @($EventData.vmguestdns1, $EventData.vmguestdns2) )
}
else {
    $OSCNM.Add('IPMode', 'UseDhcp')
}
Set-OSCustomizationNicMapping @OSCNM

This ultimately makes it easier to manage your code because you don’t have to repeat declaring variables and having 2 lines running the same command with different parameters. If you ever add more options, now you only have to add it once if you do it this way.

General troubleshooting of PSU Dashboards:

Comment out everything that “does” something (like Set-OSCustomizationNicMapping) and add the line…

Show-UDToast -message “$($OSCNM | ConvertTo-Json)” -Duration 50000

That lets you see the actual parameters you are passing. If you right click on the Toast, you can then click “Inspect” and drill down to see the JSON. From there you can copy it, paste it into Powershell and make it an object again. If you are using Powershell 7 you can also use ConvertFrom-Json -AsHashtable.

I’ve used this to manually run commands using the parameters as presented by the dashboard and have found it useful when something isn’t immediately obvious or the error messages PSU gives aren’t enough.

All this being said, be very careful when running things like Wait-Task and Start-Sleep in the dashboard itself. I don’t use VMWare, but try adding Wait-Task and the subsequent scriptblock to a New-UDDynamic.

It should still wait, as Wait-Task is tied to $Session:Buildtask, but it won’t stop the dashboard page from executing code.

1 Like

@Jori - Thats a lot to take in! Thank you for the response and I will start looking over what you said in greater detail. For clarity of something I hadn’t seen in the help docs. I was confused on the use of $Eventdata. I was under the impression that it was only used under the immediate OnSubmit or OnClick and then lost; but from how you explained it seems to indicate it contains all $Eventdata tied back to the Id of all input(s)? I may be saying that poorly, but I think I understand it now.
And splatting… I am terrible at it but in this scenario, I can see the benefit, I need to get better at it.
I will start rewriting and making adjustments taking into account your suggestions.

You’ve given me great advise, Thanks!

I was under the impression that it was only used under the immediate OnSubmit or OnClick and then lost; but from how you explained it seems to indicate it contains all $Eventdata tied back to the Id of all input(s)?

First few lines explain that :). But yeah, any controls that integrate with the form will pass their ID back to $EventData. So in the end, you’ll need to assign your controls an ID.

Though, if you were simply using all of the $EventData in a single command you could probably do something like this… (a little hackey, but I just love ConvertFrom-Json -AsHashtable :slight_smile:

$Splat = $EventData | ConvertTo-Json | ConvertFrom-Json -AsHashTable
Start-MyCommand @Splat

@Jori - Again thanks for the advice. I implemented your suggestions and firstly, the dashboard is at least 10times faster, and I cleaned up nearly 50+ lines of “duplicate” code due to my get-udelement calls. Not to mention changing over to splatting.
I am very pleased with the turnout and learned a lot in the process.

at this point the only thing I need to deal with is the dreaded:

:Error rendering component (mu-form)
Error: Objects are not valid as a React child (found: object with keys {......}) If you meant to render a collection of children, use an array instead.

All I found on google was the possibility of too many Get-UDSelect 's … But I’ll keep looking or see if it can be hidden.

Glad to hear it! Did moving Wait-Task to UDDynamic work like I thought it would? Trying to optimize dashboard performance is a hobby for me at this point.

For the current problem, is that form the original build form? If so, I’m assuming it is rendering correctly to begin with, but then breaking at some point after you submit it?

Yes indeed…

The way the dashboard is setup, Info stuff is on the left in a page, and it’s updated with info gathered from the right page. There’s a login Form (form1), once you get into vcenter, then it gathers data to populate into the next form which replaces form1 called form2. Form2 is where we were troubleshooting the original issue. Using Wait-Debugger on the last line of the form2, shows me that the error will not appear until I continue it. At which something from the form is throwing an error.

I am happy to post the entire dashboard, but I need to make it sterile first.