Getting Started: Cloud Assembly Code Examples [CB10094]

Introduction Starting your journey on vRA 8.x can be a little challenging. Out of numerous demanding areas, one of them is creating blueprints or do i say, cloud templates in Cloud Assembly. Converting your existing vRA 7.x blueprints to vRA 8.x cloud templates requires some clarity on YAML development. Even though, a good deal of…

By

min read

Getting Started: Cloud Assembly Code Examples [CB10094]

Introduction

Starting your journey on vRA 8.x can be a little challenging. Out of numerous demanding areas, one of them is creating blueprints or do i say, cloud templates in Cloud Assembly. Converting your existing vRA 7.x blueprints to vRA 8.x cloud templates requires some clarity on YAML development. Even though, a good deal of things are already provided out of the box, considering YAML as a Code which can single-handedly establish all your cloud deployments, getting the desired state out of it can be a trickster. Hence, I would like to share some quick bits on what we can do in vRA provided YAML editor to get the job done faster.

Samples

We will start with the basics and then will move to more advanced template options.

A blank template

name: templateName
formatVersion: 1
inputs: {}
resources: {}
YAML

Literals

The following literals are supported:

  • Boolean (true or false)
  • Integer
  • Floating point
  • StringBackslash escapes double quote, single quote, and backslash itself:" is escaped as \"' is escaped as \'\ is escaped as \\Quotes only need to be escaped inside a string enclosed with the same type of quote, as shown in the following example."I am a \"double quoted\" string inside \"double quotes\"."
  • Null

Environment variables

Environment names:

  • orgId
  • projectId
  • projectName
  • deploymentId
  • deploymentName
  • blueprintId
  • blueprintVersion
  • blueprintName
  • requestedBy (user)
  • requestedAt (time)
${env.blueprintId}

Resource variables

Resource variables let you bind to resource properties from other resources

format: resource.RESOURCE_NAME.PROPERTY_NAME

${resource.db.id}
${resource.db.networks[0].address}
${resource.app.id} (Return the string for non-clustered resources, where count isn't specified. Return the array for clustered resources.)
${resource.app[0].id} (Return the first entry for clustered resources.)

Resource self variables

Resource self variables are allowed only for resources supporting the allocation phase. Resource self variables are only available (or only have a value set) after the allocation phase is complete.

${self.address} (Return the address assigned during the allocation phase.)

Note that for a resource named resource_xself.property_name and resource.resource_x.property_name are the same and are both considered self-references.

Cluster count index

${count.index == 0 ? "primary" : "secondary"} (Return the node type for clustered resources.)

Limitations:

Use of count.index for resource allocation is not supported. For example, the following capacity expression fails when it references the position within an array of disks created at input time.

inputs:
  disks:
    type: array
    minItems: 0
    maxItems: 12
    items:
      type: object
      properties:
        size:
          type: integer
          title: Size (GB)
          minSize: 1
          maxSize: 2048
resources:
  Cloud_vSphere_Disk_1:
    type: Cloud.vSphere.Disk
    properties:
      <kbd>capacityGb: '${input.disks[count.index].size}'</kbd>
      count: '${length(input.disks)}'
YAML

Conditions

  • Equality operators are == and !=.
  • Relational operators are < > <= and >=.
  • Logical operators are && || and !.
  • Conditionals use the pattern:condition-expression ? true-expression : false-expression
${input.count < 5 && input.size == 'small'}
${input.count < 2 ? "small" : "large"}

Arithmetic operators

Operators are +  / * and %.

${(input.count + 5) * 2}

String concatenation

${'ABC' + 'DEF'}

Operators [ ] and .

The expression follows ECMAScript in unifying the treatment of the [ ] and . operators.

So, expr.identifier is equivalent to expr["identifier"]. The identifier is used to construct a literal whose value is the identifier, and then the [ ] operator is used with that value.

${resource.app.networks[0].address}

In addition, when a property includes a space, delimit with square brackets and double quotes instead of using dot notation.

Incorrect:

input.operating system

Correct:

input["operating system"]

Construction of map

${{'key1':'value1', 'key2':input.key2}}

Construction of array

${['key1','key2']}

Functions

${function(arguments...)}

${to_lower(resource.app.name)}

List of supported functions

FunctionDescription
abs(number)Absolute number value
avg(array)Return average of all values from array of numbers
base64_decode(string)Return decoded base64 value
base64_encode(string)Return base64 encoded value
ceil(number)Returns the smallest (closest to negative infinity) value that is greater than or equal to the argument and is equal to a mathematical integer
contains(array, value)Check if array contains a value
contains(string, value)Check if string contains a value
digest(value, type)Return digest of value using supported type (md5, sha1, sha256, sha384, sha512)
ends_with(subject, suffix)Check if subject string ends with suffix string
filter_by(array, filter)Return only the array entries that pass the filter operationfilter_by([1,2,3,4], x => x >= 2 && x <= 3)returns [2, 3]filter_by({'key1':1, 'key2':2}, (k,v) => v != 1)returns [{"key2": 2}]
floor(number)Returns the largest (closest to positive infinity) value that is less than or equal to the argument and is equal to a mathematical integer
format(format, values…)Return a formatted string using Java Class Formatter format and values.
from_json(string)Parse json string
join(array, delim)Join array of strings with a delimiter and return a string
json_path(value, path)Evaluate path against value using XPath for JSON.
keys(map)Return keys of map
length(array)Return array length
length(string)Return string length
map_by(array, operation)Return each array entry with an operation applied to itmap_by([1,2], x => x * 10)returns [10, 20]map_by([1,2], x => to_string(x))returns ["1", "2"]map_by({'key1':1, 'key2':2}, (k,v) => {k:v*10})returns [{"key1":10},{"key2":20}]
map_to_object(array, keyname)Return an array of key:value pairs of the specified key name paired with values from another arraymap_to_object(resource.Disk[*].id, "source")returns an array of key:value pairs that has a key field called source paired with disk ID stringsNote thatmap_by(resource.Disk[*].id, id => {'source':id})returns the same result
matches(string, regex)Check if string matches a regex expression
max(array)Return maximum value from array of numbers
merge(map, map)Return a merged map
min(array)Return minimum value from array of numbers
not_null(array)Return the first entry which is not null
now()Return current time in ISO-8601 format
range(start, stop)Return a series of numbers in increments of 1 that begins with the start number and ends just before the stop number
replace(string, target, replacement)Replace string containing target string with target string
reverse(array)Reverse entries of array
slice(array, begin, end)Return slice of array from begin index to end index
split(string, delim)Split string with a delimiter and return array of strings
starts_with(subject, prefix)Check if subject string starts with prefix string
substring(string, begin, end)Return substring of string from begin index until end index
sum(array)Return sum of all values from array of numbers
to_json(value)Serialize value as json string
to_lower(str)Convert string to lower case
to_number(string)Parse string as number
to_string(value)Return string representation of the value
to_upper(str)Convert string to upper case
trim(string)Remove leading and trailing spaces
url_encode(string)Encode string using url encoding specification
uuid()Return randomly generated UUID
values(map)Return values of map

Referencing input parameters

In the resources section, you can reference an input parameter using ${input.property-name} syntax. If a property name includes a space, delimit with square brackets and double quotes instead of using dot notation: ${input["property name"]}.

inputs:
  sshKey:
    type: string
    maxLength: 500
resources:
  frontend:
    type: Cloud.Machine
    properties:
      remoteAccess:
        authentication: publicPrivateKey
        sshKey: <mark style="background-color:#ffffff" class="has-inline-color has-black-color">'${input.sshKey}'</mark>
YAML

Important In cloud template code, you cannot use the word input except to indicate an input parameter.


Optional Inputs

Inputs are usually required and marked with an asterisk. To make an input optional, set an empty default value as shown.

owner:
  type: string
  minLength: 0
  maxLength: 30
  title: Owner Name
  description: Account Owner
  <mark style="background-color:#ffffff" class="has-inline-color has-black-color">default: ''</mark>
YAML

List of available input properties

PropertyDescription
constUsed with oneOf. The real value associated with the friendly title.
defaultPrepopulated value for the input.The default must be of the correct type. Do not enter a word as the default for an integer.
descriptionUser help text for the input.
encryptedWhether to encrypt the input that the user enters, true or false.Passwords are usually encrypted.You can also create encrypted properties that are reusable across multiple cloud templates. See Secret Cloud Assembly properties.
enumA drop-down menu of allowed values.Use the following example as a format guide.enum: – value 1 – value 2
formatSets the expected format for the input. For example, (25/04/19) supports date-time.Allows the use of the date picker in Service Broker custom forms.
itemsDeclares items within an array. Supports number, integer, string, Boolean, or object.
maxItemsMaximum number of selectable items within an array.
maxLengthMaximum number of characters allowed for a string.For example, to limit a field to 25 characters, enter  maxLength: 25.
maximumLargest allowed value for a number or integer.
minItemsMinimum number of selectable items within an array.
minLengthMinimum number of characters allowed for a string.
minimumSmallest allowed value for a number or integer.
oneOfAllows the user input form to display a friendly name (title) for a less friendly value (const). If setting a default value, set the const, not the title.Valid for use with types string, integer, and number.
patternAllowable characters for string inputs, in regular expression syntax.For example, '[a-z]+' or '[a-z0-9A-Z@#$]+'
propertiesDeclares the key:value properties block for objects.
readOnlyUsed to provide a form label only.
titleUsed with oneOf. The friendly name for a const value. The title appears on the user input form at deployment time.
typeData type of number, integer, string, Boolean, or object.Important:A Boolean type adds a blank checkbox to the request form. Leaving the box untouched does not make the input False.To set the input to False, users must check and then clear the box.
writeOnlyHides keystrokes behind asterisks in the form. Cannot be used with enum. Appears as a password field in Service Broker custom forms.

Explicit dependencies

Sometimes, a resource needs another to be deployed first. For example, a database server might need to exist first, before an application server can be created and configured to access it.

An explicit dependency sets the build order at deployment time, or for scale in or scale out actions. You can add an explicit dependency using the graphical design canvas or the code editor.

  • Design canvas option—draw a connection starting at the dependent resource and ending at the resource to be deployed first.
  • Code editor option—add a dependsOn property to the dependent resource, and identify the resource to be deployed first.An explicit dependency creates a solid arrow in the canvas.
Explicit dependency

Property bindings

Sometimes, a resource property needs a value found in a property of another resource. For example, a backup server might need the operating system image of the database server that is being backed up, so the database server must exist first.

Also called an implicit dependency, a property binding controls build order by waiting until the needed property is available before deploying the dependent resource. You add a property binding using the code editor.

  • Edit the dependent resource, adding a property that identifies the resource and property that must exist first.A property binding creates a dashed arrow in the canvas.
Implicit dependency or property binding

Encrypt access credentials

resources:
  apitier:
    type: Cloud.Machine
    properties:
      cloudConfig: |
        #cloud-config
        runcmd:
          - export apikey=${base64_encode(input.username:input.password)}
          - curl -i -H 'Accept:application/json' -H 'Authorization:Basic :$apikey' http://example.com
YAML

Passing inputs to ABXs

resources:
  db-tier:
    type: Cloud.Machine
    properties:
      # Command to execute
      abxRunScript_script: mkdir bp-dir
      # Time delay in seconds before the script is run
      abxRunScript_delay: 120
      # Type of the script: shell (Linux) or powershell (Windows)
      abxRunScript_shellType: linux
      # Could be aws, azure, etc.
      abxRunScript_endpointType: '${self.endpointType}'
YAML

Fetching value from Property Groups

appSize: '${propgroup.propGroup_name.const_size}'
YAML

Adding a secret property

type: Cloud.Machine
properties:
  name: ourvm
  image: mint20
  flavor: small
  remoteAccess:
    authentication: publicPrivateKey
    sshKey: '${secret.ourPublicKey}'
    username: root
YAML

Enum & oneOf (Dropdowns in Input form)

enum and oneOf both are used to provide a set of default values in the input form. However, the only difference in them is that oneOf allows for a friendly title.

inputs:
  osversion:
    type: string
    title: Select OS Version
    default: SLES12
    enum:
      - SLES12
      - SLES15
      - RHEL7
inputs:
  platform:
    type: string
    title: Deploy to
    oneOf:
      # Title is what the user sees, const is the tag for the endpoints.
      - title: AWS
        const: aws
      - title: Azure
        const: azure
      - title: vSphere
        const: vsphere
    default: vsphere
YAML

Dynamic Enums (binding with vRO action)

input1:
    type: string
    title: Environment
    default: ''
    $dynamicEnum: '/data/vro-actions/com.org.utils/getEnvironment?environment={{vmenvironment}}'
.
.
.
.
input1:
    type: string
    title: podId
    $dynamicEnum: /data/vro-actions/com.org.helpers/getPod
YAML

This is calling a vRO action getEnvironments inside a action module com.org.utils with 1 input environment. Actions with no inputs can be called with without using quotes.

vRO Custom DataTypes

inputs:
  accountName:
    type: string
    title: Account name
    encrypted: true   
  displayName:
    type: string
    title: Display name   
  password:
    type: string
    title: Password
    encrypted: true 
  confirmPassword:
    type: string
    title: Password
    encrypted: true   
  ouContainer: 
    type: object
    title: AD OU container
    $data: 'vro/data/inventory/AD:OrganizationalUnit'
    properties:
        id:
            type: string
        type:
            type: string    
YAML

Resource Flags

Cloud Assembly includes several cloud template settings that adjust how a resource is handled at request time. Resource flag settings aren’t part of the resource object properties schema. For a given resource, you add the flag settings outside of the properties section as shown.

resources:
  Cloud_Machine_1:
    type: Cloud.Machine
    preventDelete: true
    properties:
      image: coreos
      flavor: small
      attachedDisks:
        - source: '${resource.Cloud_Volume_1.id}'
  Cloud_Volume_1:
    type: Cloud.Volume
    properties:
      capacityGb: 1
YAML

Available resource flags

Resource FlagDescription
allocatePerInstanceWhen set to true, resource allocation can be customized for each machine in a cluster.The default is false, which allocates resources equally across the cluster, resulting in the same configuration for each machine. In addition, day 2 actions might not be separately possible for individual resources.Per instance allocation allows count.index to correctly apply the configuration for individual machines. For code examples, see Machine and disk clusters in Cloud Assembly.
createBeforeDeleteSome update actions require that the existing resource be removed and a new one be created. By default, removal is first, which can lead to conditions where the old resource is gone but the new one wasn’t created successfully for some reason.Set this flag to true if you need to make sure that the new resource is successfully created before deleting the previous one.
createTimeoutThe Cloud Assembly default timeout for resource allocate, create, and plan requests is 2 hours (2h). In addition, a project administrator can set a custom default timeout for these requests, applicable throughout the project.This flag lets you override any defaults and set the individual timeout for a specific resource operation. See also updateTimeout and deleteTimeout.
deleteTimeoutThe Cloud Assembly default timeout for delete requests is 2 hours (2h). In addition, a project administrator can set a different default timeout for delete requests, applicable throughout the project.This flag lets you override any defaults and set the individual timeout for a specific resource delete operation. See also updateTimeout and createTimeout.
dependsOnThis flag identifies an explicit dependency between resources, where one resource must exist before creating the next one. For more information, see Creating bindings and dependencies between resources in Cloud Assembly.
dependsOnPreviousInstancesWhen set to true, create cluster resources sequentially. The default is false, which simultaneously creates all resources in a cluster.For example, sequential creation is useful for database clusters where primary and secondary nodes must be created, but secondary node creation needs configuration settings that connect the node to an existing, primary node.
forceRecreateNot all update actions require that the existing resource be removed and a new one be created. If you want an update to remove the old resource and create a new one, independent of whether the update would have done so by default, set this flag to true.
ignoreChangesUsers of a resource might reconfigure it, changing the resource from its deployed state.If you want to perform a deployment update but not overwrite the changed resource with the configuration from the cloud template, set this flag to true.
ignorePropertiesOnUpdateUsers of a resource might customize certain properties, and those properties might be reset to their original cloud template state during an update action.To prevent any properties from being reset by an update action, set this flag to true.
preventDeleteIf you need to protect a created resource from accidental deletion during updates, set this flag to true. If a user deletes the deployment, however, the resource is deleted.
recreatePropertiesOnUpdateUsers of a resource might reconfigure properties, changing the resource from its deployed state. During an update, a resource might or might not be recreated. Resources that aren’t recreated might remain with properties in changed states.If you want a resource and its properties to be recreated, independent of whether the update would have done so by default, set this flag to true.
updateTimeoutThe Cloud Assembly default timeout for update requests is 2 hours (2h). In addition, a project administrator can set a different default timeout for update requests, applicable throughout the project.This flag lets you override any defaults and set the individual timeout for a specific resource update operation. See also deleteTimeout and createTimeout.

cloudConfig (cloud-init & Cloudbase-Init)

You can add a cloudConfig section to Cloud Assembly template code, in which you add machine initialization commands that run at deployment time. cloudConfig command formats are:

  • Linux—initialization commands follow the open cloud-init standard.
  • Windows—initialization commands use Cloudbase-init.

Linux cloud-init and Windows Cloudbase-init don’t share the same syntax. A cloudConfig section for one operating system won’t work in a machine image of the other operating system.

To ensure correct interpretation of commands, always include the pipe character cloudConfig: | as shown. Learn more about cloud-config here.

cloudConfig: |
        #cloud-config
        repo_update: true
        repo_upgrade: all
        packages:
         - apache2
         - php
         - php-mysql
         - libapache2-mod-php
         - php-mcrypt
         - mysql-client
        runcmd:
         - mkdir -p /var/www/html/mywordpresssite && cd /var/www/html && wget https://wordpress.org/latest.tar.gz && tar -xzf /var/www/html/latest.tar.gz -C /var/www/html/mywordpresssite --strip-components 1
         - i=0; while [ $i -le 5 ]; do mysql --connect-timeout=3 -h ${DBTier.networks[0].address} -u root -pmysqlpassword -e "SHOW STATUS;" && break || sleep 15; i=$((i+1)); done
         - mysql -u root -pmysqlpassword -h ${DBTier.networks[0].address} -e "create database wordpress_blog;"
         - mv /var/www/html/mywordpresssite/wp-config-sample.php /var/www/html/mywordpresssite/wp-config.php
         - sed -i -e s/"define( 'DB_NAME', 'database_name_here' );"/"define( 'DB_NAME', 'wordpress_blog' );"/ /var/www/html/mywordpresssite/wp-config.php && sed -i -e s/"define( 'DB_USER', 'username_here' );"/"define( 'DB_USER', 'root' );"/ /var/www/html/mywordpresssite/wp-config.php && sed -i -e s/"define( 'DB_PASSWORD', 'password_here' );"/"define( 'DB_PASSWORD', 'mysqlpassword' );"/ /var/www/html/mywordpresssite/wp-config.php && sed -i -e s/"define( 'DB_HOST', 'localhost' );"/"define( 'DB_HOST', '${DBTier.networks[0].address}' );"/ /var/www/html/mywordpresssite/wp-config.php
         - service apache2 reload
YAML

If a cloud-init script behaves unexpectedly, check the captured console output in /var/log/cloud-init-output.log when troubleshooting. For more about cloud-init, see the cloud-init documentation.

Examples of vSphere resources

vSphere virtual machine with CPU, memory, and operating system

resources:
  demo-machine:
    type: Cloud.vSphere.Machine
    properties:
      name: demo-machine
      cpuCount: 1
      totalMemoryMB: 1024
      image: ubuntu
YAML

vSphere machine with a datastore resource

resources:
  demo-vsphere-disk-001:
    type: Cloud.vSphere.Disk
    properties:
        name: DISK_001
        type: 'HDD'
        capacityGb: 10
        dataStore: 'datastore-01'
        provisioningType: thick
YAML

vSphere machine with an attached disk

resources:
  demo-vsphere-disk-001:
    type: Cloud.vSphere.Disk
    properties:
      name: DISK_001
      type: HDD
      capacityGb: 10
      dataStore: 'datastore-01'
      provisioningType: thin
  demo-machine:
    type: Cloud.vSphere.Machine
    properties:
      name: demo-machine
      cpuCount: 2
      totalMemoryMB: 2048
      imageRef: >-
        https://packages.vmware.com/photon/4.0/Rev1/ova/photon-ova-4.0-ca7c9e9330.ova
      attachedDisks:
        - source: '${demo-vsphere-disk-001.id}'
YAML

vSphere machine with a dynamic number of disks

inputs:
  disks:
    type: array
    title: disks
    items:
      title: disks
      type: integer
    maxItems: 15
resources:
  Cloud_Machine_1:
    type: Cloud.vSphere.Machine
    properties:
      image: Centos
      flavor: small
      attachedDisks: '${map_to_object(resource.Cloud_Volume_1[*].id, "source")}'
  Cloud_Volume_1:
    type: Cloud.Volume
    allocatePerInstance: true
    properties:
      capacityGb: '${input.disks[count.index]}'
      count: '${length(input.disks)}'
YAML

vSphere machine from a snapshot image. Append a forward slash and the snapshot name. The snapshot image can be a linked clone.

resources:
  demo-machine:
    type: Cloud.vSphere.Machine
    properties:
      imageRef: 'demo-machine/snapshot-01'
      cpuCount: 1
      totalMemoryMB: 1024
YAML

vSphere machine in a specific folder in vCenter

resources:
  demo-machine:
    type: Cloud.vSphere.Machine
    properties:
      name: demo-machine
      cpuCount: 2
      totalMemoryMB: 1024
      imageRef: ubuntu
      resourceGroupName: 'myFolder'
YAML

vSphere machine with multiple NICs

resources:
  demo-machine:
    type: Cloud.vSphere.Machine
    properties:
      image: ubuntu
      flavor: small
      networks:
        - network: '${network-01.name}'
          deviceIndex: 0
        - network: '${network-02.name}'
          deviceIndex: 1
 network-01: 
    type: Cloud.vSphere.Network
    properties:
      name: network-01
  network-02:
    type: Cloud.vSphere.Network
    properties:
      name: network-02
YAML

vSphere machine with an attached tag in vCenter

resources:
  demo-machine:
    type: Cloud.vSphere.Machine
    properties:
      flavor: small
      image: ubuntu
      tags:
        - key: env
          value: demo
YAML

vSphere machine with a customization spec

resources:
  demo-machine:
      type: Cloud.vSphere.Machine
      properties:
        name: demo-machine
        image: ubuntu
        flavor: small
        customizationSpec: Linux
YAML

vSphere machine with remote access

inputs:
  username:
    type: string
    title: Username
    description: Username
    default: testUser
  password:
    type: string
    title: Password
    default: VMware@123
    encrypted: true
    description: Password for the given username
resources:
  demo-machine:
    type: Cloud.vSphere.Machine
    properties:
      flavor: small
      imageRef: >-
        https://cloud-images.ubuntu.com/releases/16.04/release-20170307/ubuntu-16.04-server-cloudimg-amd64.ova
      cloudConfig: |
        ssh_pwauth: yes
        chpasswd:
          list: |
            ${input.username}:${input.password}
          expire: false
        users:
          - default
          - name: ${input.username}
            lock_passwd: false
            sudo: ['ALL=(ALL) NOPASSWD:ALL']
            groups: [wheel, sudo, admin]
            shell: '/bin/bash'
        runcmd:
          - echo "Defaults:${input.username}  !requiretty" >> /etc/sudoers.d/${input.username}
YAML

Market Place

The Marketplace provides VMware Solution Exchange cloud templates and images that help you build your template library and access supporting OVA or OVFs.

Source: https://learncloudassembly.github.io/Marketplace/Cloud-Templates/

Other ways to create Cloud Assembly templates

Cloud template cloning

To clone a template, go to Design, select a source, and click Clone. You clone a cloud template to create a copy based on the source, then assign the clone to a new project or use it as starter code for a new application.

Uploading and downloading

You can upload, download, and share cloud template YAML code in any way that makes sense for your site. You can even modify template code using external editors and development environments.


Note A good way to validate shared template code is to inspect it in the Cloud Assembly code editor on the design page.


Integrating Cloud Assembly with a repository

An integrated git source control repository can make cloud templates available to qualified users as the basis for a new deployment. See How do I use Git integration in Cloud Assembly.

References

Leave a Reply

Related Posts

%d