How to add a VM to Content Library using PowerCLI

This task sounds very simple.  Target the VM, then import to the primary content library for replication to the subordinate libraries.  This task can be accomplished right now with a few clicks in the GUI.  I’m not a GUI kind of person, I like the mash the keys to get stuff done.  So after a lot of research, I found that the PowerCLI commands to perform this function just don’t exist at this point.

During my research, I came across a website (https://powercli.ideas.aha.io/ideas) from the VMware PowerCLI team, that is devoted to getting user suggestions for new features for PowerCLI. It seems that others have requested this feature for PowerCLI and the functions for the content library.  The original suggestion was submitted by Alan Renouf (http://www.virtu-al.net/).  So if Alan is asking for this feature, I might be out of luck, but I didn’t stop there.

I then came across a post from William Lam that looked promising.  The post titled vSphere Content Library PowerCLI community module (https://www.virtuallyghetto.com/2017/07/vsphere-content-library-powercli-community-module.html) seem to have everything that I needed, but not how to import to the content library.  This is getting me pointed into the correct direction for writing my own function.

More research on this topic has now has lead me to a post (https://communities.vmware.com/thread/598130) on the VMware communities forum. Where Luc Dekens (http://www.lucd.info/), replied about using a new function from William Lam.  The only issue is that it was wrote for VMC (VMware Cloud).  There was another reply on the same thread about a powerCLI script writen by a user named LokeshHK that he uploaded to the VMware {code} site (https://code.vmware.com/samples/4646/powercli-script-to-upload-library-item?h=Sample).  This was what helped me to get what I needed as it had the proper syntax calling the APIs via PowerCLI.

Developing the API Call

Using the API explorer (https://YourvCenterNameHere/apiexplorer/) built into vCenter you can found out information about the varies APIs that are available.  I need to find the API call that best fit the need I was looking for.

https://YourvCenterName/apiexplorer/

After looking through the API explorer, I found the ovf/library_item API.  This is used to create a library item in the content library from a virtual machine or virtual appliance. This one was perfect for what I needed.

vCenter API Explorer ovf/library_item API

Clicking on the API shows details about the API itself, like the syntax and how the API provides a response back.  Using the API explorer, you can make the API call by providing the needed information via the interface, and clicking the ‘TRY IT OUT!’ button.  It will then attempt to make the API call, and provide you with the fully formed API code.

Converting API to PowerCLI

PowerCLI has a cmdlet for using the API calls, Get-CisService.  So to begin, you will need to authentication to the vSphere Automation SDK server using Connect-CisServer.  Once connected you can call the API.

$ContentLibraryOvfService  = Get-CisService com.vmware.vcenter.ovf.library_item

This command starts the configuration of the API which is saved as the variable $ContentLibraryOvfService.  To be able to properly make this API call you have to gather some information to be passed with the final call.

Below is the information that the API call is looking to receive to process the call correctly.

{
  "source": {
  "type": "string",
  "id": "string"
   },
  "client_token": "string",
  "target": {
  "library_id": "string",
  "library_item_id": "string"
   },
  "create_spec": {
     "flags": [
     "string"
     ],
  "name": "string",
  "description": "string"
  }
}

So using the new variable above, I need to start to adding the data to it.

Getting the ‘client_token’ is by far the easiest.

$UniqueChangeId = [guid]::NewGuid().tostring()

Getting the ‘target’ information requires the id of the content library.  If I was updating the item already in the content library it would then need the id of the item that already exists in the content library as well.

$ContentLibraryService = Get-CisService com.vmware.content.library
$libaryIDs = $contentLibraryService.list()
foreach($libraryID in $libaryIDs){
    $library = $contentLibraryService.get($libraryID)
    if($library.name -eq $LibraryName){
        $library_ID = $libraryID
        break
    }
}
$createOvfTarget = $ContentLibraryOvfService.Help.create.target.Create()$createOvfTarget.library_id = $library_ID

Now to gather the needed info for the ‘source’, which requires 2 pieces of info, the type and ID of the Virtual Machine.

$createOvfSource = $ContentLibraryOvfService.Help.create.source.Create()
$createOvfSource.type = ((Get-VM $VMname).ExtensionData.MoRef).Type
$createOvfSource.id = ((Get-VM $VMname).ExtensionData.MoRef).Value

Now to gather the last of the information, the ‘create_spec’.  This consists of the name and description that the item will get once imported into the content library

$createOvfCreateSpec = $ContentLibraryOvfService.help.create.create_spec.Create()
$createOvfCreateSpec.name = $LibItemName
$createOvfCreateSpec.description = $Description

Now just to put all of this script together into a single function.

Function Add-TemplateToLibrary {
<#
.NOTES :
--------------------------------------------------------
Created by : Stuart Yerdon
Website : NotesofaScripter.com
--------------------------------------------------------
.DESCRIPTION
This function uploads a VM to the Content library.
.PARAMETER LibraryName
Name of the libray to which item needs to be uploaded.
.PARAMETER VMname
Name of the VM to upload.
.PARAMETER LibItemName
Name of the template after imported to library.
.PARAMETER Description
Description of the imported item.
.EXAMPLE
Add-TemplateToLibrary -LibraryName 'LibraryName' -VMname '2016 STD Template v1.0 VM' -LibItemName '2016 STD Template' -Description 'Uploaded via API calls'
#>

param(
[Parameter(Mandatory=$true)][string]$LibraryName,
[Parameter(Mandatory=$true)][string]$VMname,
[Parameter(Mandatory=$true)][string]$LibItemName,
[Parameter(Mandatory=$true)][string]$Description
)

$ContentLibraryService = Get-CisService com.vmware.content.library

$libaryIDs = $contentLibraryService.list()
foreach($libraryID in $libaryIDs) {
$library = $contentLibraryService.get($libraryID)
if($library.name -eq $LibraryName){
$library_ID = $libraryID
break
}
}

if(!$library_ID){
write-host -ForegroundColor red $LibraryName " -- is not exists.."
} else {
$ContentLibraryOvfService = Get-CisService com.vmware.vcenter.ovf.library_item
$UniqueChangeId = [guid]::NewGuid().tostring()

$createOvfTarget = $ContentLibraryOvfService.Help.create.target.Create()
$createOvfTarget.library_id = $library_ID

$createOvfSource = $ContentLibraryOvfService.Help.create.source.Create()
$createOvfSource.type = ((Get-VM $VMname).ExtensionData.MoRef).Type
$createOvfSource.id = ((Get-VM $VMname).ExtensionData.MoRef).Value

$createOvfCreateSpec = $ContentLibraryOvfService.help.create.create_spec.Create()
$createOvfCreateSpec.name = $LibItemName
$createOvfCreateSpec.description = $Description
#$createOvfCreateSpec.flags = ""

write-host "Creating Library Item -- " $LibItemName
$libraryTemplateId = $ContentLibraryOvfService.create($UniqueChangeId,$createOvfSource,$createOvfTarget,$createOvfCreateSpec)

}
}

So now that we have the function complete, we will just need to use the following to update the VM to the content library.

Add-TemplateToLibrary -LibraryName 'PrimaryContentLibrary' -VMname '2016 STD Template VM' -LibItemName '2016 STD Template' -Description 'Uploaded via PowerShell'

-Stuart

2 thoughts on “How to add a VM to Content Library using PowerCLI”

  1. Hello,
    I am trying to use your script to add VMs to an existing Content Library but are receiving the following error.
    Method invocation failed because [System.Management.Automation.PSCustomObject] does not contain a method named ‘Create’.
    At line:26 char:1
    + $createOvfTarget = $ContentLibraryOvfService.Help.create.target.Creat …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (Create:String) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

    The property ‘library_id’ cannot be found on this object. Verify that the property exists and can be set.
    At line:27 char:1
    + $createOvfTarget.library_id = $library_ID
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    Method invocation failed because [System.Management.Automation.PSCustomObject] does not contain a method named ‘Create’.
    At line:29 char:1
    + $createOvfSource = $ContentLibraryOvfService.Help.create.source.Creat …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (Create:String) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

    The property ‘type’ cannot be found on this object. Verify that the property exists and can be set.
    At line:30 char:1
    + $createOvfSource.type = ((Get-VM $VMname).ExtensionData.MoRef).Type
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    The property ‘id’ cannot be found on this object. Verify that the property exists and can be set.
    At line:31 char:1
    + $createOvfSource.id = ((Get-VM $VMname).ExtensionData.MoRef).Value
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    Method invocation failed because [System.Management.Automation.PSCustomObject] does not contain a method named ‘Create’.
    At line:33 char:1
    + $createOvfCreateSpec = $ContentLibraryOvfService.help.create.create_s …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (Create:String) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

    The property ‘name’ cannot be found on this object. Verify that the property exists and can be set.
    At line:34 char:1
    + $createOvfCreateSpec.name = $LibItemName
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    The property ‘description’ cannot be found on this object. Verify that the property exists and can be set.
    At line:35 char:1
    + $createOvfCreateSpec.description = $Description
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    Creating Library Item — win2k16-x64-std-test
    A server error occurred: ‘com.vmware.vapi.std.errors.invalid_argument’: Unable to validate input to method com.vmware.vcenter.ovf.library_item.create
    (Server error id: ‘vapi.invoke.input.invalid’)Structure ‘operation-input’ is missing a field: create_spec (Server error id:
    ‘vapi.data.structure.field.missing’). Check $Error[0].Exception.ServerError for more details.
    At line:39 char:1
    + $libraryTemplateId = $ContentLibraryOvfService.create($UniqueChangeId …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OperationStopped: (:) [], CisServerException
    + FullyQualifiedErrorId : VMware.VimAutomation.Cis.Core.Types.V1.CisServerException

    1. Hunter,
      I found some syntax errors with the script and I have resolved them. You might recopy the script and try again. If you still have issues, please provide the syntax that you are using to call the function so I can better troubleshoot this issue.

      -Stuart

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.