Hello Everyone,
On today’s post, I will focus on a Dynamic Multi-NIC configuration for a Cloud Template in vRA 8.x
This allows customers to reuse the same cloud templates for virtual machines that could have a different amount of NICs, and this amount is defined at the time of the request. If this wasn’t dynamic, then a cloud template with three networks, will always need to have three networks configured at the time of the request, which might not be the case.
Using a Dynamic construct allows for less cloud template sprawl, since multiple application configurations can use the same cloud template.
Since this configuration is not trivial, this post will be a step by step guide on how to achieve this result.
Current Environment
For this Lab demonstration, we will use a vSphere Cloud Account, 4 NSX-T segments that are part of a Network Profile with a capability tag named “env:production” – In doing so, when using that constraint tag in the cloud template, we can guarantee our deployment will use that specific network profile.
The 4 NSX-T segments also have a single tag that refers to the type of network it is. In this scenario, Application, Frontend, Database and Backup are our 4 networks.


Creating the Cloud Template
To get the Dynamic Multi-NIC configuration on the Cloud Template to work, we need the following things:
- Inputs for Network to NIC mapping based on tagging
- Inputs for NIC existence
- Network Resources
- VM Resource and Network Resource assignment
In addition to this, we can do customization in Service Broker to change the visibility of the fields. This is done to only allow the requester to choose a network mapping for a NIC what will actually be used.
Inputs for Network to NIC mapping based on tagging
This cloud template will allow for configurations of up to 4 NICs, and since we have 4 networks, we should let the requester select, for each NIC, what networks can be used.
This is what it looks like
Network1:
type: string
description: Select Network to Attach to
default: 'net:application'
title: Network 1
oneOf:
- title: Application Network
const: 'net:application'
- title: Frontend Network
const: 'net:frontend'
- title: Database Network
const: 'net:database'
- title: Backup Network
const: 'net:backup'
Network2:
type: string
description: Select Network to Attach to
default: 'net:frontend'
title: Network 2
oneOf:
- title: Application Network
const: 'net:application'
- title: Frontend Network
const: 'net:frontend'
- title: Database Network
const: 'net:database'
- title: Backup Network
const: 'net:backup'
Network3:
type: string
description: Select Network to Attach to
default: 'net:database'
title: Network 3
oneOf:
- title: Application Network
const: 'net:application'
- title: Frontend Network
const: 'net:frontend'
- title: Database Network
const: 'net:database'
- title: Backup Network
const: 'net:backup'
Network4:
type: string
description: Select Network to Attach to
default: 'net:backup'
title: Network 4
oneOf:
- title: Application Network
const: 'net:application'
- title: Frontend Network
const: 'net:frontend'
- title: Database Network
const: 'net:database'
- title: Backup Network
const: 'net:backup'
We can see that each of the inputs allows for any of the networks to be selected.
Inputs for NIC Existence
Other than the first NIC (which should always exist, otherwise our VM(s) wouldn’t have any network connectivity, we want to be able to deploy VMs with 1, 2, 3, and 4 NICs, using the same Cloud Template.
To achieve that, we will create 3 Boolean Inputs that will define if a NIC should be added or not.
needNIC2:
type: boolean
title: Add 2nd NIC?
default: false
needNIC3:
type: boolean
title: Add 3rd NIC?
default: false
needNIC4:
type: boolean
title: Add 4th NIC?
default: false
Network Resources
To manage the configuration of the NICs and networks, the network resources for NICs 2, 3 and 4 will use a count property, and this property’s result (either 0 if it doesn’t exist, or 1 if it does exist) will be based on the result of the inputs. Network 1 will not use that property
Also, we will use the deviceIndex property to maintain consistency with the numbering – So the network resources look like this
Network1:
type: Cloud.vSphere.Network
count: 1
properties:
networkType: existing
deviceIndex: 0
constraints:
- tag: '${input.Network1}'
- tag: 'env:production'
Network2:
type: Cloud.vSphere.Network
properties:
networkType: existing
count: '${input.needNIC2 == true ? 1 : 0}'
deviceIndex: 1
constraints:
- tag: '${input.Network2}'
- tag: 'env:production'
Network3:
type: Cloud.vSphere.Network
properties:
networkType: existing
count: '${input.needNIC3 == true ? 1 : 0}'
deviceIndex: 2
constraints:
- tag: '${input.Network3}'
- tag: 'env:production'
Network4:
type: Cloud.vSphere.Network
properties:
networkType: existing
count: '${input.needNIC4 == true ? 1 : 0}'
deviceIndex: 3
constraints:
- tag: '${input.Network4}'
- tag: 'env:production'
The constraint tags that are used are the Network Input (to choose a network) and the ‘env:production’ tag to make our deployment use the Network Profile we defined earlier.
VM Resource & Network Resource Assignment
This is the tricky part – Since our networks could be non-existent (if the needNic input is not selected) we cannot use the regular syntax to add a network, which would be something like:
networks:
- network: '${resource.Network1.id}'
assignment: static
deviceIndex: 0
- network: '${resource.Network2.id}'
assignment: static
deviceIndex: 1
...
This will fail on the Cloud Template validation because the count for Network2 could be zero, so to do the resource assignment, we need to use the map_by syntax.
Several other examples can be seen on the following link: https://docs.vmware.com/en/vRealize-Automation/8.5/Using-and-Managing-Cloud-Assembly/GUID-12F0BC64-6391-4E5F-AA48-C5959024F3EB.html
The VM resource uses a simple Ubuntu Image with a Small Flavor, so here is what it looks like once the map_by syntax is used for the assignment
Cloud_vSphere_Machine_1:
type: Cloud.vSphere.Machine
properties:
image: Ubuntu-TMPL
flavor: Small
customizationSpec: Linux
networks: '${map_by(resource.Network1[*] + resource.Network2[*] + resource.Network3[*] + resource.Network4[*], r => {"network":r.id, "assignment":"static", "deviceIndex":r.deviceIndex})}'
constraints:
- tag: 'env:production'
This allows for any combination of NICs, from 1 to 4, and if the count of one of the resources is 0, it won’t be picked up by the assignment expression.
This is what the Cloud Template looks on the canvas. You can see that Networks 2, 3 and 4 have the appearance of possible multiple instances. This is because we’re using the count parameter.

If we were to deploy this Cloud Template, it will look like this:

How do we fix this? We can leverage Service Broker to manage the visibility of the fields based on the boolean input!

So now, from Service Broker, it looks like this:


So if we deploy this, it should have three networks assigned. The first NIC should use the Application Network, the second NIC should use the Frontend Network and the 3rd NIC should use the Database Network.
Let’s test it!

We can see that even if the Cloud Template had 4 Network Resources, only 3 were instantiated for this deployment! And each network was mapped to a specific NSX-T segment, thanks to the constraint tags.
Closing Note
I hope this blog post was useful – The same assignment method can be used for other resources such as Disks or Volumes – the principle is still the same.
Feel free to share this if you found it useful, and leave your feedback in the comments.
Until the next time!
For anyone else, there is an error in this article (tested with vRA 8.4). The YAML should be:
networks: ‘${map_by(resource.Network1[*] + resource.Network2[*] + resource.Network3[*] + resource.Network4[*], r => {“network”:r.id, “assignment”:”static”, “deviceIndex”:r.deviceIndex})}’
LikeLiked by 1 person
Hello Steve,
You’re correct, will edit that.
Thanks!
LikeLike
Hello Lucho,
the map_by statement, as reported in Steve post, raises an error related to the first parameter. It expects an array or object, but the list of network resources, is interpreted as a string.
Below the line I added in my template
networks: ‘${map_by(resource.ProductionNetwork[*] + resource.ManagedNetwork[*],nic => {“network”:nic.id, “deviceIndex”:nic.deviceIndex})}’
ProductionNetwork:
type: Cloud.vSphere.Network
properties:
deviceIndex: 1
assignment: static
networkType: existing
constraints:
– tag: ‘${”env:” + input.networkProfile}’
– tag: ‘${”netProject:” + input.projectPlatform}’
– tag: ‘${”netZone:” + input.prodNetworkZone}’
ManagedNetwork:
type: Cloud.vSphere.Network
properties:
deviceIndex: 2
assignment: static
count: ‘${input.prodNetworkZone != “mgmt” ? 1 : 0}’
networkType: existing
constraints:
– tag: ‘${”env:” + input.networkProfile}’
– tag: ‘${”netProject:” + input.projectPlatform}’
– tag: ‘${”netZone:” + input.mgdNetworkZone}’
LikeLike
In my case it does not work, I have the following error
Cannot deserialize instance of `java.util.ArrayList` out of VALUE_STRING token at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.vmware.admiral.compute.content.TemplateComputeDescription[“networks”])
LikeLike
Lucho, this is a very well-written blog, I have a customer use case exactly like this and I plan to use this to demonstrate the capabilities. Thank you.
LikeLiked by 1 person