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

10 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

      1. Hi Stuart,

        I am also getting the same error . Can you please provide the link of updated scripts? Thanks in advance.

        Method invocation failed because [System.Management.Automation.PSCustomObject] does not contain a method named ‘Create’.
        At C:\Scripts\Add-TemplateToLibrary.psm1:46 char:1
        + $createOvfTarget = $ContentLibraryOvfService.Help.create.target.Creat …
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : InvalidOperation: (Create:String) [], RuntimeException
        + FullyQualifiedErrorId : MethodNotFound

        -Sub#

  2. This is great, and a huge help, thanks for sharing. However i’m having issues updating existing items in the content library, and can’t figure out what i should be doing. I can get the ID of the item i want to update (using get-contentlibraryitem), but how can i pass this ID in as an update, rather than a new item?

    1. AndyG,
      I’m working on doing an update of a content library item, but due to various work projects I haven’t found the time to finish working out all of the syntax on it. So its on the way. In the meantime you can look at the API explorer for vCenter via https://[DNS or IP of your vCenter]/apiexplorer/ then change the ‘Select API’ at the top to ‘content’ which might help to get to the resolution.

      -Stuart

      1. To update an existing item you first need to get the Item ID and store it into a variable.

        If the name of your existing CL item is in variable $LibItemName then you can use the following based on above to do this:
        # Check to see if item already exists
        $ContentLibraryService2 = Get-CisService com.vmware.content.library.item
        $libraryItems = $ContentLibraryService2.list($libraryID)
        foreach($libraryItem in $libraryItems) {
        $item = $ContentLibraryService2.get($libraryItem)
        if($item.name -eq $LibItemName){
        $item_ID = $libraryItem
        break
        }
        }

        Then add this line to the customization spec for com.vmware.vcenter.ovf.libraryf_item section above.
        $createOvfTarget.library_item_id = $item_ID

        1. Eric,
          Thanks for this information. This is going to be a great help for a script I have been writing that has been on the back burner for some time now.

          – Stuart

  3. Hi,

    I’m loving this, however I’ve encountered a small bug, or shall we say annoyance.

    When deploying a new VM from the newly created Content Library item (a Windows Server 2019 template in my case), the exported OVF template is missing the Guest OS and Guest OS version VM options, which in turn creates an alert when powering on the vm created from the template, and it won’t power on before manually changing the os options. The template itself has the correct operating system parameters specified. Any ideas?

    1. Jocke,
      I know the issue/bug you are speaking of, as I faced the same issue after we upgraded our environment from 6.7 to 7.0. The way that I was able to resolve this issue was to recreate a new template VM from scratch. I tried several other steps before I got to this, and it all was a waste of time for me. The new template VM worked great.

      I hope this helps, and resolves your issue as well.

      -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.