Working with vCloud Metadata in PowerCLI – Part 1

Way back in 2012 Alan Renouf created a PowerCLI module to deal with manipulation of metadata entries for vCloud Director objects - this can be incredibly useful to track related information for these objects. The vCD metadata functionality was enhanced in v5.1 (and then later in 5.5, 5.6 and 8.0) - in particular typed values were added with functionality to use date/time, boolean and numeric values (as well as free-form string text). Also added were security levels so that metadata could be made read-only or hidden (from a tenant perspective) but still accessible/visible to system owners. I've taken the PowerShell module that Alan published here and updated it to cope with these enhancements. I've also updated the returned fields/views to include the extra attributes (where present) such as security levels of metadata entries.

Note that I am definitely not a professional developer (and most of my PowerShell knowledge comes from Google) so there's probably significant room for improvement in the code - comment back if you have suggestions for improvement and I'll update this post.

Use of the module requires a valid connection to a vCloud instance (using Connect-CIServer). This won't work for versions prior to v5.1 (most of my testing has been with PowerCLI 6 against a v8 vCD deployment) so please use at your own risk and make sure you thoroughly test your own scenarios. I'll write a follow-up post detailing some example code and usage scenarios which people may find useful in the next few days.

I'd suggest copy/pasting the code (below) into a PowerShell module (.psm1) file and including the module in your scripts as needed.

  1Function New-CIMetaData { 
  2    <# 
  3    .SYNOPSIS 
  4        Creates a Metadata Key/Value pair. 
  5    .DESCRIPTION 
  6        Creates a custom Metadata Key/Value pair on a specified vCloud object 
  7    .PARAMETER  Key 
  8        The name of the Metadata to be applied.
  9    .PARAMETER  Value
 10        The value of the Metadata to be applied, the string 'Now' can be used
 11        for the current date/time for values using the 'DateTime' type.
 12    .PARAMETER  Visibility
 13        The visibility of the Metadata entry (General, Private, ReadOnly)
 14    .PARAMETER  Type
 15        The type of the Metadata entry (String, Number, DateTime, Boolean)
 16        (these correspond to the types of: MetadataStringValue,
 17        MetadataNumberValue, MetadataDateTimeValue or MetadataBooleanValue
 18        respectively)
 19    .PARAMETER  CIObject
 20        The object on which to apply the Metadata.
 21    .EXAMPLE
 22        New-CIMetadata -Key "Owner" -Value "Alan Renouf" -CIObject (Get-Org Org1)
 23        Creates a new metadata value "Alan Renouf" in a key "Owner" on the Org1 object.
 24    .EXAMPLE
 25        New-CIMetadata -Key "Company" -Value "ABC Corp" -Visibility READONLY -CIObject (Get-CIVM 'client')
 26        Creates a new metadata value "ABC Corp" in a key "Company" on the 'client' VM object with the READONLY attribute set preventing changes by non-system users.
 27    .EXAMPLE
 28        New-CIMetadata -Key "Backup" -Value $false -Visibility Private -Type Boolean -CIObject (Get-CIVapp 'testvapp')
 29        Creates a new hidden metadata value $false in a key "Backup" on the vApp object with the 'Private' attribute set preventing visibility to non-system users.
 30    .NOTES
 31        NAME: Get-CIMetaData
 32        AUTHOR: Jon Waite based on code by Alan Renouf
 33        LASTEDIT: 2016-02-23
 34        KEYWORDS: metadata set vcloud director
 35    #Requires -Version 2.0
 36    #> 
 37     [CmdletBinding( 
 38         SupportsShouldProcess=$true, 
 39        ConfirmImpact="High" 
 40    )] 
 41    param( 
 42        [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 
 43            [PSObject[]]$CIObject, 
 44        [parameter(Mandatory=$true)]
 45            [String]$Key,
 46        [parameter(Mandatory=$true)]
 47            $Value,
 48        [ValidateSet('General','Private','ReadOnly')]
 49            [String]$Visibility = 'General',
 50        [ValidateSet('String','Number','DateTime','Boolean')]
 51            [String]$Type = "String"
 52        ) 
 53    Process { 
 54        Foreach ($Object in $CIObject) { 
 55            $Metadata = New-Object VMware.VimAutomation.Cloud.Views.Metadata 
 56            $Metadata.MetadataEntry = New-Object VMware.VimAutomation.Cloud.Views.MetadataEntry 
 57            
 58            $Metadata.MetadataEntry[0].Key = $Key
 59
 60            switch($Type) {
 61              'String'   { $Metadata.MetadataEntry[0].TypedValue = New-Object VMware.VimAutomation.Cloud.Views.MetadataStringValue }
 62              'Number'   { $Metadata.MetadataEntry[0].TypedValue = New-Object VMware.VimAutomation.Cloud.Views.MetadataNumberValue }
 63              'DateTime' { $Metadata.MetadataEntry[0].TypedValue = New-Object VMware.VimAutomation.Cloud.Views.MetadataDateTimeValue }
 64              'Boolean'  { $Metadata.MetadataEntry[0].TypedValue = New-Object VMware.VimAutomation.Cloud.Views.MetadataBooleanValue }
 65            }
 66
 67            if ($Type -eq 'DateTime' -and $Value -eq 'Now') {
 68                $Metadata.MetadataEntry[0].TypedValue.Value = [string](Get-Date).ToUniversalTime().GetDateTimeFormats('s')
 69            } else {
 70                $Metadata.MetadataEntry[0].TypedValue.Value = $Value
 71            }
 72            
 73            switch($Visibility) {
 74              'General'  { } #Default, don't need to change
 75              'Private'  { 
 76                $Metadata.MetadataEntry[0].Domain = New-Object VMware.VimAutomation.Cloud.Views.MetadataDomainTag
 77                $Metadata.MetadataEntry[0].Domain.Value = 'SYSTEM'
 78                $Metadata.MetadataEntry[0].Domain.Visibility = 'PRIVATE'
 79                }
 80              'ReadOnly' {
 81                $Metadata.MetadataEntry[0].Domain = New-Object VMware.VimAutomation.Cloud.Views.MetadataDomainTag
 82                $Metadata.MetadataEntry[0].Domain.Value = 'SYSTEM'
 83                $Metadata.MetadataEntry[0].Domain.Visibility = 'READONLY'
 84                }      
 85            }
 86
 87            $Object.ExtensionData.CreateMetadata($Metadata) 
 88            ($Object.ExtensionData.GetMetadata()).MetadataEntry | Where {$_.Key -eq $key } | Select @{N="CIObject";E={$Object.Name}},
 89            @{N="Type";E={$_.TypedValue.GetType().Name}},
 90            @{N="Visibility";E={ if ($_.Domain.Visibility) { $_.Domain.Visibility } else { "General" }}},
 91            Key -ExpandProperty TypedValue
 92        } 
 93    } 
 94} 
 95Function Get-CIMetaData {
 96    <#
 97    .SYNOPSIS
 98        Retrieves all Metadata Key/Value pairs.
 99    .DESCRIPTION
100        Retrieves all custom Metadata Key/Value pairs on a specified vCloud object
101    .PARAMETER  CIObject
102        The object on which to retrieve the Metadata.
103    .PARAMETER  Key
104        The key to retrieve.
105    .EXAMPLE
106        Get-CIMetadata -CIObject (Get-Org Org1)
107    #>
108    param(
109        [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
110            [PSObject[]]$CIObject,
111            $Key
112        )
113    Process {
114        Foreach ($Object in $CIObject) {
115            If ($Key) {
116                ($Object.ExtensionData.GetMetadata()).MetadataEntry | Where {$_.Key -eq $key } | Select @{N="CIObject";E={$Object.Name}},
117                    @{N="Type";E={$_.TypedValue.GetType().Name}},
118                    @{N="Visibility";E={ if ($_.Domain.Visibility) { $_.Domain.Visibility } else { "General" }}},
119                    Key -ExpandProperty TypedValue
120            } Else {
121                ($Object.ExtensionData.GetMetadata()).MetadataEntry | Select @{N="CIObject";E={$Object.Name}},
122                    @{N="Type";E={$_.TypedValue.GetType().Name}},
123                    @{N="Visibility";E={ if ($_.Domain.Visibility) { $_.Domain.Visibility } else { "General" }}},
124                    Key -ExpandProperty TypedValue
125            }
126        }
127    }
128}
129Function Remove-CIMetaData {
130    <#
131    .SYNOPSIS
132        Removes a Metadata Key/Value pair.
133    .DESCRIPTION
134        Removes a custom Metadata Key/Value pair on a specified vCloud object
135    .PARAMETER  Key
136        The name of the Metadata to be removed.
137    .PARAMETER  CIObject
138        The object on which to remove the Metadata.
139    .EXAMPLE
140        Remove-CIMetaData -CIObject (Get-Org Org1) -Key "Owner"
141    #>
142     [CmdletBinding(
143         SupportsShouldProcess=$true,
144        ConfirmImpact="High"
145    )]
146    param(
147        [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
148            [PSObject[]]$CIObject,
149            $Key
150        )
151    Process {
152        $CIObject | Foreach {
153            $metadataValue = ($_.ExtensionData.GetMetadata()).GetMetaDataValue($Key)
154            If($metadataValue) { $metadataValue.Delete() }
155        }
156    }
157}