Elements are the smallest deployable units of solution add-on. They are entities managed by Cloud Director or 3-rd party systems uniquely identified across their solution add-on and its instances. Once an element is realized at the target system it defines a set of properties which values could be used by the other add-on elements for their realization activities, though there is a strict rule - an element properties can be consumed only by elements appearing after the definition of the element in its manifest. Elements can have associated actions to perform pre-and-post realization activities allowing the add-on vendor to gain certain control during the element realization. Such actions can also be defined on the add-on level allowing to perform generic pre-setup or clean-up activities.
Elements and their associated configuration are defined in a solution add-on manifest.yaml
file with the following sections.
Vendor section holds details about the add-on origin and its current version. This section is visible in the Solution Add-On Management UI.
It is recommended to use simple names, lower-case with hyphens for vendor and name properties as they will be used as part of the add-on identifier and the packaged ISO name.
vendor: <add-on vendor name>
name: <add-on name>
version: <add-on version (N.N.N[-W])>
vcdVersion: <minimal supported Cloud Director version of this add-on>
friendlyName: <name under which this add-on will appear in the management UI>
description: <short description of the add-on business function visible in add-on management UI>
IMPORTANT: Every solution add-on must have a unique vendor
and name
. Changing either of these at some point will result in a completely new add-on. Use the add-on version
property to manage the current and future add-on upgrades.
Inputs section is defined by the add-on vendor to request user input during the add-on installation and its associated day-2 operations. Inputs are referenced by add-on elements and their associated actions in the manifest.yaml
and their values are substituted at the time of add-on operation execution.
inputs:
- name: <the input field identifier used by elements for referencing the field concrete value>
title: <the display name of this input field, visible in add-on management UI>
description: <the description of this input field, visible in add-on management UI>
required: <optional, if set to 'true' will force the field to be set>
secure: <optional, if set to 'true' will encrypt the field value. Note, the value cannot be seen even by the user who specified it.>
type: <optional, defaults to 'String', denotes the type of this input filed>
default: <optional, if set the value will be used when consumer does not specify a value>
validation: <optional, regular expression validating the value for the field>
delete: <optional, if set to 'true' will make this input field available only to the add-on instance delete operation>
isArray: <optional, if set to 'true' the filed can accept a list of values>
values: <optional, the finite set of values that can be set to this variable>
Elements section contains a list of elements that compound the add-on. An element is defined via properties under the manifest.yaml
descriptor. Every element regardless of its type
has name
, description
, triggers
and policies
properties. The type
further defines whether the element will have a spec
property and what will be its properties’ schema.
Some elements require a filesystem artifacts for their realizations. These artifacts by convention are located under folder with name matching the name
property of its associated element. An element artifact could be an already bundled artifact or the source code and build script that will turn into bundle artifact via vcd-ext-shell
build
command.
elements:
- type: ui-plugin | user | role | rights-bundle | defined-entity | defined-entity-instance | vapp | network-policy | service
name:
# If missing description defaults to "name"
description: <User friendly name>
triggers: []
policies:
create: once|always|reuse
Policies reflect the element realization behavior. The create
policy controls whether the element have to be explicitly created or a pre-provisioned version of it can be reused.
When policies.supportsMultipleInstances
is set to false
- Default element policies.create
is set to once
- forcefully create the element resource - If policies.create
is set to reuse
- an existing resource with a similar spec will be associated with the element or if missing a new resource will be created
When policies.supportsMultipleInstances
is set to true
- Default element policies.create
is set to once
- forcefully create the element resource - If policies.create
is set to always
- every new instance of the add-on will create a separate resource
NOTE: During the add-on instance creation operation, elements are processed in ascending order one at a time. During the add-on instance deletion operation, elements are processed in reverse order.
UI Plugin type
represents a Cloud Director UI plugin. This element definition requires the name
property to point to a project root folder <element reference name>
where the source code of the plugin will be located, or its resulted artifact <element reference name>/dist/plugin.zip
in case of an external build tooling is used.
The type
further requires the spec
property describing the UI Plugin visibility rules.
elements:
- type: ui-plugin
name: <element reference name>
spec:
# Optional arguments below
publish:
provider: true | false
# tenantsAll and tenants are exclusive
tenantsAll: true | false
tenants:
- <tenant short name>
- ...
User type
represents a Cloud Director User entity. The element type
requires the spec
property, it outlines the details required for the user realization.
elements:
- type: user
spec:
username: text
# Optional arguments below
password: text
description: text
roleName: text
fullName: text
email: text
# Default: LOCAL
providerType: LOCAL | SAML | OAUTH
# True: user will be created in the Provider Portal
# False: user will be created in the organization specified in the Solution Landing zone
systemScope: true | false
Role type
represents a Cloud Director Role entity. The element type
requires the spec
property, it outlines the details required for the role realization.
elements:
- type: role
spec:
name:
# List of rights keys or ids
# Example id: urn:vcloud:type:vmware:demo_person
# Examples:
# - "API Tokens: Manage"
# - "Access All Organization VDCs"
# - "urn:vcloud:type:vmware:<DEFINED ENTITY TYPE>:full_access"
# - "urn:vcloud:type:vmware:<DEFINED ENTITY TYPE>:view"
# - "urn:vcloud:type:vmware:<DEFINED ENTITY TYPE>:modify"
rights:
- <RIGHT>
- ...
# Optional arguments below
description:
# Define right as system or global
global: true | false
# Define global right visibility
publish:
solution: true | false
# tenantsAll and tenants are exclusive
tenantsAll: true | false
tenants:
- <name>
- ...
Right Bundle type
represents a Cloud Director Right Bundle entity. The element type
requires the spec
property, it outlines the details required for the right bundle realization.
elements:
- type: rights-bundle
spec:
name:
# List of rights keys or ids
# Example id: urn:vcloud:type:vmware:demo_person
# Examples:
# - "API Tokens: Manage"
# - "Access All Organization VDCs"
# - "urn:vcloud:type:vmware:<DEFINED ENTITY TYPE>:full_access"
# - "urn:vcloud:type:vmware:<DEFINED ENTITY TYPE>:view"
# - "urn:vcloud:type:vmware:<DEFINED ENTITY TYPE>:modify"
rights:
- <RIGHT>
- ...
# Define right bundle visibility
description:
publish:
solution: true | false
# tenantsAll and tenants are exclusive
tenantsAll: true | false
tenants:
- <name>
- ...
Defined Entity type
represents a Cloud Director Runtime Defined Entity interfaces, types and behaviors. This element definition requires the name
property to point to a project root folder <element reference name>
where the source code of the Defined Entity definitions will be located, or their resulted artifacts <element reference name>/dist/types/*.json
and <element reference name>/dist/interfaces/*.json
in case of an external build tooling is used.
elements:
- type: defined-entity
- name: <element reference name>
Defined Entity Instance type
represents a Cloud Director instance of a Defined Entity Type. The element type
requires the spec
property, it outlines the details required for the instance realization.
elements:
- type: defined-entity-instance
spec:
vendor: <vendor defined by defined entity type>
nss: <namespace defined by defined entity type>
version: <version defined by defined entity type>
entity: <entity body defined by defined entity type>
# Optional arguments below
name: <element reference name>
# Defines what role should be able to manage this entity instance
accessControlRole: <cloud director role name>
owner:
username: <username>
password: <password>
# Optional arguments below
# The system scope has to be set to the one the user belongs to
systemScope: true | false
Vapp type
represents a Cloud Director virtual appliance. The element type
requires the spec
property, it outlines the details required for the vApp realization.
elements:
- type: vapp
spec:
# Optional arguments below
# The name of the name pattern under which the vApp will appear in Cloud Director after addon installation.
name: <appliance name>
# Applicable when the vApp descriptor contains OVF properties
ovfProperties:
- key:
value: number | string | boolean
- ...
# Further, customizes the vApp virtual machine before its power on operation invocation
guestCustomization:
changeSid: boolean
joinDomainEnabled: boolean
useOrgSettings: boolean
domainName: string
domainUserName: string
domainUserPassword: string
machineObjectOU: string
adminPasswordEnabled: boolean
adminPasswordAuto: boolean
adminPassword: string
adminAutoLogonEnabled: boolean
adminAutoLogonCount: number
resetPasswordRequired: boolean
customizationScript: string
computerName: string
# Connects the vApp virtual machine to a network defined by the Solution Landing Zone matching the defined assignment and capabilities.
networks:
- assignment: auto | static | dynamic
primary: true | false
disconnected: true | false
capabilities:
- string
- ...
# Use storage policy defined by the Solution Landing Zone matching the defined capabilities.
storage:
capabilities:
- string
- ...
# Further, customize the vApp virtual machine hardware before its power on operation invocation
hardware:
numberOfCpus: number
coresPerSocket: number
# Memory size in MB
memorySize: number
# Terminate the vApp element installation when the timeout has reached.
timeoutMinutes: number
# Element installation is considered as successful when all conditions are met in the specified by the timeout duration.
readyCondition:
# The vApp virtual machine received an IP
"ip":
# The vApp virtual machine ExtraConfig contains a key
"<key>":
# The vApp virtual machine ExtraConfig contains a key with value
"<key>": "<value>"
Network Policy type
represents a Network Manager policy that defines a firewall rule. The element type
requires the spec
property, it outlines the details required for the policy realization.
elements:
- type: network-policy
spec:
type: vcenter | esxi | nsx | compute
# Optional arguments below
name: <element reference name>
sources:
- <IP>
- ...
destinations:
- <IP>
- ...
# Option: Named service
services:
type: HTTP| HTTPS | SSO | SSH | ICMP-ALL
# Option: Custom service
services:
protocol: TCP | UDP
# Optional
# Port must be in range 1-65535
sourcePort: number
targetPort: number
Create an outbound firewall rule from VM1 to VM2 on port 443
- name: my-policy
type: network-policy
spec:
type: compute
vdc: my-ovdc
sources:
- 192.168.1.1
destinations:
- 192.168.1.2
services:
- targetPort: 443
protocol: TCP
Create an inbound firewall rule to VM on port 8443
- name: my-policy
type: network-policy
spec:
type: compute
vdc: my-ovdc
destinations:
- 192.168.1.1
services:
- targetPort: 8443
protocol: TCP
Network Service type
represents a Network Manager service that defines a way to expose internal IP addresses to the outside world using public IPs. The element type
requires the spec
property, it outlines the details required for the service realization.
elements:
- type: service
spec:
# Option: Named binding
bindings:
type: HTTP| HTTPS | SSO | SSH | ICMP-ALL
# Option: Custom binding
bindings:
protocol: TCP | UDP
port: number
# Optional
targetPort: number
# Optional arguments below
name: text
privateIp: text
firewallStrategy: MATCH_INTERNAL_ADDRESS | MATCH_EXTERNAL_ADDRESS
Expose a private IP through a single public IP on port 22 and port 443->8443.
- name: my-service
type: service
spec:
privateIp: 192.168.1.1
vdc: my-vdc
bindings:
- port: 22
protocol: TCP
- port: 443
targetPort: 8443
protocol: TCP
Expose a private IP through a single public IP on port 8443->HTTPS(443)
- name: my-service
type: service
spec:
privateIp: 192.168.1.1
vdc: my-ovdc
bindings:
- port: 8443
type: HTTPS
Policy section is defined by the add-on vendor to state whether this add-on can install only one or many instance. This section is optional.
NOTE: The add-on management will treat the add-on as a single instance in case the section is missing from the manifest or supportsMultipleInstances
property is set to false
.
policies:
supportsMultipleInstances: true
Trigger section defines actions to be run as part of the add-on management lifecycle.
triggers:
- event: <event type>
action: <path under ISO>/<action handler executable>
timeout: <timeout in seconds>
- ...
The triggers can be implemented on any language as long as their build provides binaries for the three major operating systems for development.
<add-on project>/dist/windows.exe
<add-on project>/dist/linux
<add-on project>/dist/darwin
Triggers can be defined globally on the add-on object and locally on a particular element. This is a list of the supported events on which a trigger could be attached. Event Types - PreCreate (add-on) - PostCreate (add-on) - PreDelete (add-on) - PostDelete (add-on) - PreScope (add-on change scope day-1 and day-2 operations) - PostScope (add-on change scope day-1 and day-2 operations) - PreOperation (add-on day-2 operation) - PostOperation (add-on day-2 operation) - OnError (add-on on any failure regardless if the operation is day-1 or day-2)
Solution add-on operation executor invokes a trigger as a separate OS process and provides its execution context properties as JSON string into the standard output
. If the trigger wants to output a property which can be referenced by other triggers or elements it has to be printed into the standard output
following the log line format output:{"name": "<key>", "value": "value", "secure: true|false}
.
The vendor have the option to create a separate binary for every trigger definition in the manifest.yaml
or use one trigger with internal switch based on the execution context, or mixture of both. This is an example of a multipurpose
trigger with internal switch implemented on GoLang.
// Copyright 2023 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0
package main
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"os"
)
type InputProperties struct {
Element string `json:"element"`
Event string `json:"event"`
Properties map[string]any `json:"properties"`
}
type OutputProperty struct {
Name string `json:"name"`
Value any `json:"value"`
Secure bool `json:"secure"`
}
type OutputProperties []OutputProperty
func readPropertiesFromStandardInput() InputProperties {
scanner := bufio.NewScanner(os.Stdin)
if !scanner.Scan() {
exitIfErrorExists(errors.New("no standard input"), "error reading from standard input")
}
inputJson := scanner.Text()
// DEVELOPMENT ONLY! Print standard input for examination.
// Note all secrets will be visible in the standard output log.
fmt.Println(inputJson)
input := InputProperties{}
err := json.Unmarshal([]byte(inputJson), &input)
exitIfErrorExists(err, "error reading JSON from standard input")
return input
}
func (o OutputProperty) writePropertyIntoStandardOutput() error {
if variableJson, err := json.Marshal(o); err != nil {
return err
} else {
_, err = fmt.Println(fmt.Sprintf("output:%s", string(variableJson)))
return err
}
}
func (properties OutputProperties) writePropertiesIntoStandardOutput() {
for _, property := range properties {
if err := property.writePropertyIntoStandardOutput(); err != nil {
fmt.Errorf("failed serializing the output for variable %s:%v", property.Name, err)
os.Exit(1)
}
}
}
func exitIfErrorExists(err error, message string) {
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v", message, err)
os.Exit(1)
}
}
const eventPreCreate = "PreCreate"
const eventPostCreate = "PostCreate"
const eventPostDelete = "PostDelete"
const elementNone = ""
const elementCloudDirectorUser = "cloud-director-user"
// This is the body of the multipurpose action handler. It is going to be called multiple times with for various
// places where it is referenced by the manifest.yaml#triggers and manifest.yaml#element#triggers.
//
// Use multipurpose action pattern for convenience or source code size reduction and usability.
func main() {
fmt.Println("Solution add-on trigger has been called")
inputProperties := readPropertiesFromStandardInput()
// Example: Handle solution add-on global triggers
if inputProperties.Element == elementNone && inputProperties.Event == eventPreCreate {
fmt.Println("solution pre-create event")
// Example: set or update multiple solution add-on global properties at once
OutputProperties{
{Name: "exampleKeyMap", Value: map[string]any{"k1": "v1", "k2": "v2"}, Secure: false},
{Name: "exampleKeyArrayAny", Value: []any{1, "v", true, map[string]bool{"k": true}}, Secure: false},
}.writePropertiesIntoStandardOutput()
}
if inputProperties.Element == elementNone && inputProperties.Event == eventPostDelete {
fmt.Println("solution post-delete event")
}
// Example: Handle solution add-on trigger for specific element
if inputProperties.Element == elementCloudDirectorUser && inputProperties.Event == eventPostCreate {
fmt.Println("solution pre-create event")
// Example: Set or update a solution add-on global property
OutputProperty{
Name: "api-token", Value: "XXX API Token XXX", Secure: true,
}.writePropertyIntoStandardOutput()
}
fmt.Println("Solution add-on trigger terminated")
}
Solution add-on manifest.yaml
can benefit from the data-driven templates for generating textual output. It is fully compliant with GoLang Data-Driven Templates.
Inputs and elements properties can be referenced by other elements via the data-driven templates syntax. This way a user input or a property from an already realized element can be used by an element realization handler.
Use user input property into element
inputs:
- name: vapp-prefix
title: Backend vApp Prefix
required: true
description: The backend vApps will appear with the name prefix for all instances of this add-on.
elements:
- name: my-backend
type: vapp
spec:
name: '{{ property `vapp-prefix` }}-{{ random `type:number` `min:10000` `max:99999` }}'
networks:
- assignment: auto
primary: true
capabilities: []
readyCondition:
"ip":
"guestinfo.solution.key.public":
timeoutMinutes: 50
Use realized element property into another element
- name: "my-role"
type: "role"
spec:
name: "My Role"
description: "Used for administrative purposes"
global: false
systemScope: true
rights:
- "urn:vcloud:type:vmware:<RDE Type>:full_access"
- "urn:vcloud:type:vmware:<RDE Type>:view"
- "urn:vcloud:type:vmware:<RDE Type>:modify"
- name: "my-account"
type: "user"
spec:
username: "myaccount"
fullName: "My Service Account"
email: "[email protected]"
description: "My account to perform operations in vCD."
roleName: "{{ property `my-role.name` }}"
systemScope: true