Public/Import-VUMContent.ps1

function Import-VUMContent {
    <#
    .SYNOPSIS
        This function imports an ESXi image or patch to a VUM instance.
 
        With thanks to Lyuboslav Asenov @ VMWare for providing assistance with new Update Manager API.
 
    .DESCRIPTION
        This function uses a combination of file copy and VC Integrity API to perform content import on a VUM instance.
        The file is first copied to the VCSA to a known location on the local file system.
        File copy can either be by VM tools file copy or a CURL command for HTTP hosted content.
        The VC Integrity API is then used to import the content to VUM.
 
    .PARAMETER FilePath
        The path to the file to be imported to VUM. This may be a local path or an HTTP URL.
 
    .PARAMETER ImportType
        Use Image to import an ISO, and Patch to import patch content.
 
    .PARAMETER vcVM
        The VM name of the VCSA hosting VUM.
 
    .PARAMETER vcRootCredential
        Root credentials for the VCSA VM to allow VM tools to perform a file copy, or to allow CURL command to execute in case of HTTP hosted content.
 
    .PARAMETER vumVI
        Should be used if there is more than one VI connection.
        More than one VI connection can be used in scanarios where the VCSA does not manage it's own VM object.
 
    .EXAMPLE
        Import-VUMContent -FilePath E:\VUM\Patches\ESXi650-201810001.zip -ImportType Patch -vcVM VCSA60-02 -vcRootCredential $vcRootcreds
 
        Import a patch to VUM.
 
    .EXAMPLE
        Import-VUMContent -FilePath E:\VUM\Images\VMware-VMvisor-Installer-6.0.0.update03-5050593.x86_64.iso -ImportType Image -vcVM VCSA60-02 -vcRootCredential $vcRootcreds
 
        Import an ESXi image to VUM.
 
    .EXAMPLE
        Import-VUMContent -FilePath E:\VUM\Images\VMware-VMvisor-Installer-6.7.0.update03-14320388.x86_64.iso -ImportType Image -vcVM DEVVCSA -vcRootCredential $vcRootcreds -vumVI devvcsa.lab.local -Verbose
 
        This is an example of a content import to a VUM instance where the vCenter does not manage it's own VM object.
        This command is run in the context of having 2 VI connections, one to the VCSA that manages DEVVCSA, and another VI connection devvcsa.lab.local
        This allows VM tools to import to DEVVCSA, and the API call to be made to devvcsa.lab.local
 
    .EXAMPLE
        Import-VUMContent -FilePath http://vumcontent.local/VMware-VMvisor-Installer-6.7.0.update03-14320388.x86_64.iso -ImportType Image -vcVM DEVVCSA -vcRootCredential $vcRootcreds -vumVI devvcsa.lab.local -Verbose
 
        This is an example of a content import to a VUM instance where the vCenter does not manage it's own VM object.
        This command is run in the context of having 2 VI connections, one to the VCSA that manages DEVVCSA, and another VI connection devvcsa.lab.local
        A remote script will be executed to CURL down the ISO file to the appliance where an it can then be imported.
 
    .LINK
        https://github.com/TheDotSource/VUMXtra
 
    .NOTES
        01 14/11/18 Initial version. A McNair
        02 29/11/18 Changed file copy for Windows VUM from UNC to PS Drive so a credential can be specifed. A McNair
        03 23/12/19 Tidied up synopsis and added verbose output. A McNair
                              Added additonal parameter vumVI to allow for content import to non-self managed VCSA's
        04 02/09/21 Added support for content from an HTTP location. A McNair
        05 30/11/22 Reworked for PowerCLI 12.7 and new API. A McNair
                              Removed support for Windows hosted vCenter.
    #>


    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [String]$FilePath,
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [ValidateSet("Patch","Image")]
        [string]$ImportType,
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [String]$vcVM,
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [System.Management.Automation.PSCredential]$vcRootCredential,
        [Parameter(Mandatory=$false,ValueFromPipeline=$false)]
        [String]$vumVi
    )

    process {

        Write-Verbose ("Function start.")

        ## Get a VUM service connection object
        try {
            $vumCon = Connect-VUM -vumVI $vumVI -ErrorAction Stop
            Write-Verbose ("Got VUM connection.")
        } # try
        catch {
            throw ("Failed to connect to VUM instance. " + $_.Exception.Message)
        } # catch


        ## Create import spec object
        $importSpec = New-Object IntegrityApi.FileUploadManagerFileUploadSpec
        $importSpec.SessionId = ""


        ## Change if patch or image
        switch ($ImportType) {

            "Image" {
                $importSpec.FileFunctionalType = "Upgrade"
                $importSpec.OpType = "UploadAndConfirm"
            } # Image

            "Patch" {
                $importSpec.FileFunctionalType = "Patch"
                $importSpec.OpType = "Upload"
            } # Patch

        } # switch


        ## Get filename from path
        $FileName = Split-Path $FilePath -Leaf

        ## Get VM object for VCSA
        try {
            $VCSAVMObject = Get-VM -Name $vcVM -Erroraction Stop
            Write-Verbose ("Got VM object for VCSA.")
        } # try
        catch {
            throw ("Failed to get VM object for VCSA. " + $_.Exception.Message)
        } # catch


        ## If path starts with HTTP or HTTPS, we'll use a remote CURL command to pull down content.
        ## If not, we'll use VM tools to invoke a file copy.

        switch -Wildcard ($FilePath) {

            ## Is an HTTP URL, use CURL
            "http*" {

                ## Issue a CURL command via VM tools to download the ISO to the patch import folder
                Write-Verbose ("HTTP path detected. Downloading ISO to target appliance from " + $FilePath)

                $remoteCmd = ("curl " + $FilePath + " --output /storage/updatemgr/patch-store-temp/" + $FileName + " --fail")

                try {
                    $scriptOutput = Invoke-VMScript -ScriptText $remoteCmd -VM $VCSAVMObject -GuestCredential $vcRootCredential
                    Write-Verbose ("Remote script execution completed.")
                } # try
                catch {
                    throw ("Attempt to execute remote script failed. " + $_.exception.message)
                } # catch

                ## Check script exit code is 0, i.e. no errors thrown.
                if ($scriptOutput.ExitCode -ne 0) {
                    throw ("Attempt to download ISO to appliance failed. CURL exit code was " + $scriptOutput.ExitCode + ". The script output was " + $scriptOutput.ScriptOutput)
                } # if

            } # http

            ## Is a conventional path, use VM tools file copy
            default {

                ## Copy file to VCSA path using VM tools
                Write-Verbose ("Copying content via VM Tools.")

                try {
                    Copy-VMGuestFile -Source $FilePath -Destination "/storage/updatemgr/patch-store-temp/$($FileName)" -LocalToGuest -VM $VCSAVMObject -GuestCredential $vcRootCredential -force -ErrorAction Stop
                    Write-Verbose ("File copied to VCSA.")
                } # try
                catch {
                    throw ("Failed to copy file to VCSA. " + $_.exception.message)
                } # catch

            } # default

        } # switch

        ## Set file import spec path for VCSA
        $importSpec.FilePath = ("/storage/updatemgr/patch-store-temp/$($FileName)")


        ## Start import
        try {
            $reqType = New-Object IntegrityApi.ImportFileRequestType
            $reqType._this = $vumCon.vumServiceContent.RetrieveVcIntegrityContentResponse.returnval.fileUploadManager
            $reqType.fileSpec = $importSpec
            $svcRefVum = New-Object IntegrityApi.ImportFile_TaskRequest($reqType)

            $taskMoRef = ($vumCon.vumWebService.ImportFile_Task($svcRefVum)).ImportFile_TaskResponse.returnval
            Write-Verbose ("Import task started.")
        } # try
        catch {
            throw ("Failed to import file. " + $_.Exception.Message)
        } # catch


        ## Get task
        $taskId = $taskMoRef.type + "-" + $taskMoRef.value

        try {
            $Task = Get-Task -Id $taskId -ErrorAction Stop
            Write-Verbose ("Got task object for import.")
        } # try
        catch {
            throw ("Failed to get task object. " + $_.Exception.Message)
        } # catch


        ## Wait for task to complete
        Write-Verbose ("Waiting on import task to complete.")
        Wait-Task -Task $Task | Out-Null


        ## Get task result
        try {
            $Task = Get-Task -Id $taskId -ErrorAction Stop
            Write-Verbose ("Got task object, verifying status.")
        } # try
        catch {
            throw ("Failed to get task object. " + $_.Exception.Message)
        } # catch


        ## Get task result
        if ($Task.state -ne "Success") {
            throw ("Image import task failed with status " + $Task.State)
        } # if


        Write-Verbose ("Import task was successful.")

        ## If image import, no further work is necessary. If patch import, we need to confirm imported patches.
        if ($ImportType -eq "Patch") {

            Write-Verbose ("Content type is Patch, confirming import.")

            ## Get vum task info so we can get associated session ID
            try {
                $reqType = New-Object IntegrityApi.getVUMTaskInfoRequestType -ErrorAction Stop
                $reqType._this = $vumCon.vumServiceContent.RetrieveVcIntegrityContentResponse.returnval.taskManager
                $reqType.taskMO = $taskMoRef

                $svcRefVum = New-Object IntegrityApi.getVUMTaskInfoRequest($reqType) -ErrorAction Stop
                $taskInfo = ($vumCon.vumWebService.getVUMTaskInfo($svcRefVum)).getVUMTaskInfoResponse.returnval

                Write-Verbose ("Got VUM task.")
            } # try
            catch {
                throw ("Failed to get VUM task object. " + $_.Exception.Message)
            } # catch


            ## Configure confirm spec
            $fileImportResponse = $taskInfo.result
            $sessionId = $fileImportResponse.sessionId
            $confirmSpec = New-Object IntegrityApi.FileUploadManagerFileUploadSpec
            $confirmSpec.FilePath = ""
            $confirmSpec.FileFunctionalType = "Patch"
            $confirmSpec.OpType = "Confirm"
            $confirmSpec.SessionId = $sessionId


            Write-Verbose ("Confirm spec set.")


            ## Confirm imported patches
            try {
                $reqType = New-Object IntegrityApi.ImportFileRequestType
                $reqType._this = $vumCon.vumServiceContent.RetrieveVcIntegrityContentResponse.returnval.fileUploadManager
                $reqType.fileSpec = $confirmSpec
                $svcRefVum = New-Object IntegrityApi.ImportFile_TaskRequest($reqType)

                $taskMoRef = ($vumCon.vumWebService.ImportFile_Task($svcRefVum)).ImportFile_TaskResponse.returnval
                Write-Verbose ("Import task started.")
            } # try
            catch {
                throw ("Failed to import file. " + $_.Exception.Message)
            } # catch


            ## Get task
            $taskId = $taskMoRef.type + "-" + $taskMoRef.value

            try {
                $Task = Get-Task -Id $taskId -ErrorAction Stop
                Write-Verbose ("Got task.")
            } # try
            catch {
                throw ("Failed to get task object. " + $_.Exception.Message)
            } # catch


            ## Wait for task to complete
            Write-Verbose ("Waiting for confirm task to complete.")
            Wait-Task -Task $Task | Out-Null


            ## Get task result
            try {
                $Task = Get-Task -Id $taskId -ErrorAction Stop
                Write-Verbose ("Got task.")
            } # try
            catch {
                throw ("Failed to get task object. " + $_.Exception.Message)
            } # catch


            ## Get task result
            if ($Task.state -ne "Success") {
                throw ("Image import task failed with status " + $Task.State)
            } # if

            Write-Verbose ("Import has completed.")

        } # if

    } # process

    end {

        ## Logoff session
        try {
            $reqType = New-Object IntegrityApi.VciLogoutRequestType -ErrorAction Stop
            $reqType._this = $vumCon.vumServiceContent.RetrieveVcIntegrityContentResponse.returnval.sessionManager
            $svcRefVum = New-Object IntegrityApi.VciLogoutRequest($reqType)
            $vumCon.vumWebService.VciLogout($svcRefVum) | Out-Null

            Write-Verbose ("Disconnected from VUM API.")
        } # try
        catch {
            Write-Warning ("Failed to disconnect from VUM API.")
        } # catch

    } # end

} # function