Public/Import-VUMContent.ps1

function Import-VUMContent {
    <#
    .SYNOPSIS
        This function imports an ESXi image or patch to a VUM instance.
 
    .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 either a Windows instance or VCSA to a known location on the local file system.
        On Windows, this is done via UNC. On the VCSA, a VM tools file copy or a CURL command will be used to copy content.
        The VC Integrity API is then used to import the content to VUM.
 
    .PARAMETER VUMType
        Either Windows or VCSA. This will unlock a conditional parameter set to collect appropriate details for each.
 
    .PARAMETER FilePath
        The path to the file to be imported to VUM. This may be a local path or an HTTP URL (VCSA only).
 
    .PARAMETER ImportType
        Use Image to import an ISO, and Patch to import patch content.
 
    .PARAMETER WindowsHost
        Required if Windows was specified as VUM type. The hostname of the Windows system that hosts VUM.
 
    .PARAMETER WinCred
        Required if Windows was specified as VUM type. The credential object with appropriate permissions to perform a UNC copy.
 
    .PARAMETER VCSAVM
        Required if VCSA was specified as VUM type. The VM name of the VCSA hosting VUM.
 
    .PARAMETER VCSACred
        Required if VCSA was specified as VUM type. Root credentials for the VCSA VM to allow VM tools to perform a file copy.
 
    .PARAMETER vumVI
        Optional if VCSA was specified as VUM type. Should be used if there is more then 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 -ImageFile E:\VUM\Patches\ESXi650-201810001.zip -VUMType VCSA -ImportType Patch -VCSAVM VCSA60-02 -VCSACred (Get-Credential)
 
        Import a patch to a VCSA integrated VUM instance.
 
    .EXAMPLE
        Import-VUMContent -ImageFile E:\VUM\Patches\ESXi650-201810001.zip -VUMType Windows -ImportType Patch -WindowsHost WINVUM01 -WinCred (Get-Credential)
 
        Import a patch to a Windows VUM instance.
 
    .EXAMPLE
        Import-VUMContent -ImageFile E:\VUM\Images\VMware-VMvisor-Installer-6.0.0.update03-5050593.x86_64.iso -VUMType VCSA -ImportType Image -VCSAVM VCSA60-02 -VCSACred (Get-Credential)
 
        Import an image to a VCSA integrated VUM instance.
 
    .EXAMPLE
        Import-VUMContent -VUMType VCSA -FilePath E:\VUM\Images\VMware-VMvisor-Installer-6.7.0.update03-14320388.x86_64.iso -ImportType Image -VCSAVM DEVVCSA -VCSACred $rootCred -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 -VUMType VCSA -FilePath http://vumcontent.local/VMware-VMvisor-Installer-6.7.0.update03-14320388.x86_64.iso -ImportType Image -VCSAVM DEVVCSA -VCSACred $rootCred -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
    #>


    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$false,Position=0)]
        [ValidateSet("Windows","VCSA")]
        [string]$VUMType,
        [Parameter(Mandatory=$true,ValueFromPipeline=$false,Position=1)]
        [String]$FilePath,
        [Parameter(Mandatory=$true,ValueFromPipeline=$false,Position=2)]
        [ValidateSet("Patch","Image")]
        [string]$ImportType
    )


    DynamicParam {

        ## Dynamic paramter set used to handle different sceanrios between Windows and VCSA

        switch ($VUMType) {

           "Windows" {
                  ## Create a new ParameterAttribute Object
                  $WinAtrrib = New-Object System.Management.Automation.ParameterAttribute
                  $WinAtrrib.Mandatory = $true
                  $WinAtrrib.Position = 3
                  $WinCredAttrib = New-Object System.Management.Automation.ParameterAttribute
                  $WinCredAttrib.Mandatory = $true
                  $WinCredAttrib.Position = 4

                  ## Create an attributecollection object for the attribute we just created.
                  $attributeCollection1 = new-object System.Collections.ObjectModel.Collection[System.Attribute]
                  $attributeCollection2 = new-object System.Collections.ObjectModel.Collection[System.Attribute]

                  ## Add custom attribute
                  $attributeCollection1.Add($WinAtrrib)
                  $attributeCollection2.Add($WinCredAttrib)

                  ## Add our paramater specifying the attribute collection
                  $WindowsHost = New-Object System.Management.Automation.RuntimeDefinedParameter('WindowsHost', [string], $attributeCollection1)
                  $WinCred = New-Object System.Management.Automation.RuntimeDefinedParameter('WinCred', [PSObject], $attributeCollection2)

                  ## Expose the name of our parameter
                  $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
                  $paramDictionary.Add('WindowsHost', $WindowsHost)
                  $paramDictionary.Add('WinCred', $WinCred)
            } # Windows

            "VCSA" {
                  ## Create a new ParameterAttribute Object
                  $VCSAVMAtrrib = New-Object System.Management.Automation.ParameterAttribute
                  $VCSAVMAtrrib.Mandatory = $true
                  $VCSAVMAtrrib.Position = 3
                  $VCSACredAtrrib = New-Object System.Management.Automation.ParameterAttribute
                  $VCSACredAtrrib.Mandatory = $true
                  $VCSACredAtrrib.Position = 4
                  $vumVCSAAtrrib = New-Object System.Management.Automation.ParameterAttribute
                  $vumVCSAAtrrib.Mandatory = $false
                  $vumVCSAAtrrib.Position = 5


                  ## Create an attributecollection object for the attribute we just created.
                  $attributeCollection1 = new-object System.Collections.ObjectModel.Collection[System.Attribute]
                  $attributeCollection2 = new-object System.Collections.ObjectModel.Collection[System.Attribute]
                  $attributeCollection3 = new-object System.Collections.ObjectModel.Collection[System.Attribute]

                  ## Add custom attribute
                  $attributeCollection1.Add($VCSAVMAtrrib)
                  $attributeCollection2.Add($VCSACredAtrrib)
                  $attributeCollection3.Add($vumVCSAAtrrib)

                  ## Add our paramater specifying the attribute collection
                  $VCSAVM = New-Object System.Management.Automation.RuntimeDefinedParameter('VCSAVM', [string], $attributeCollection1)
                  $VCSACred = New-Object System.Management.Automation.RuntimeDefinedParameter('VCSACred', [PSObject], $attributeCollection2)
                  $vumVI = New-Object System.Management.Automation.RuntimeDefinedParameter('vumVI', [PSObject], $attributeCollection3)

                  ## Expose the name of our parameter
                  $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
                  $paramDictionary.Add('VCSAVM', $VCSAVM)
                  $paramDictionary.Add('VCSACred', $VCSACred)
                  $paramDictionary.Add('vumVI', $vumVI)
            } # VCSA

        } # switch

        return $paramDictionary

    } # DynamicParam


    process {

        Write-Verbose ("[Import-VUMContent]Function start.")

        ## Get a VUM service connection object
        try {
            $vumCon = Connect-VUM -vumVI $vumVI.Value -ErrorAction Stop
            Write-Verbose ("[Import-VUMContent]Got VUM connection.")
        } # try
        catch {
            Write-Debug ("[Import-VUMContent]Failed to connect to VUM instance.")
            throw ("Failed to connect to VUM instance. The CMDlet returned " + $_)
        } # catch


        ## Create import spec object
        [IntegrityApi.FileUploadManagerFileUploadSpec] $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


        ## Copy image file to Windows or VCSA
        switch ($VUMType) {

            "Windows" {

                Write-Verbose ("[Import-VUMContent]Windows VUM instance has been specified. Copy will take place via UNC.")

                Write-Verbose ("Copy will performed under user account " + $WinCred.username)

                ## Create new PS Drive object to Windows UNC
                try {
                    New-PSDrive VUMIMPORT -PSProvider FileSystem -Root ("\\" + $WindowsHost.value + "\c$") -Credential $WinCred.value -ErrorAction Stop | Out-Null
                    Write-Verbose ("[Import-VUMContent]Created new PS drive.")
                } # try
                catch {
                    Write-Debug ("[Import-VUMContent]Failed to create PS Drive to UNC.")
                    throw ("Failed to create PS drive to UNC. " + $_)
                } # catch


                ## Create remote VUMImport folder
                try {
                    New-Item -ItemType Directory -Path VUMIMPORT:\VUMImport -Force -ErrorAction Stop | Out-Null
                    Write-Verbose ("[Import-VUMContent]VUM import folder created.")
                } # try
                catch {
                    Write-Debug ("[Import-VUMContent]Failed to create VUM import folder." + $_)
                    throw ("Failed to create VUM import folder. " + $_)
                } # catch


                ## Copy file to import folder
                try {
                    Copy-Item -Path $FilePath -Destination ("\\" + $WindowsHost.value + "\c$\VUMImport\" + $FileName) -Force -ErrorAction Stop | Out-Null
                    Write-Verbose ("[Import-VUMContent]Content file copied.")
                } # try
                catch {
                    Write-Debug ("[Import-VUMContent]Failed to copy content file.")
                    throw ("Failed to copy image file. " + $_)
                } # catch


                ## Remove PS Drive
                try {
                    Remove-PSDrive -Name VUMIMPORT -ErrorAction Stop | Out-Null
                    Write-Verbose ("[Import-VUMContent]Removed PS drive.")
                } # try
                catch {
                    Write-Debug ("[Import-VUMContent]Failed to remove PS drive.")
                    throw ("Failed to remove PS drive. " + $_)
                } # catch


                ## Set file import spec path for Windows
                $importSpec.FilePath = ("c:\VUMImport\" + $FileName)
            } # Windows

            "VCSA" {

                Write-Verbose ("[Import-VUMContent]VCSA VUM instance has been specified. File copy will take place via VM tools.")

                ## Get VM object for VCSA
                try {
                    $VCSAVMObject = Get-VM -Name $VCSAVM.value -Erroraction Stop
                    Write-Verbose ("[Import-VUMContent]Got VM object for VCSA.")
                } # try
                catch {
                    Write-Debug ("[Import-VUMContent]Failed to get VM.")
                    throw ("Failed to get VM object for VCSA. " + $_)
                } # 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 ("[Import-VUMContent]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 $VCSACred.value
                            Write-Verbose ("[Import-VUMContent]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
                        try {
                            Copy-VMGuestFile -Source $FilePath -Destination "/storage/updatemgr/patch-store-temp/$($FileName)" -LocalToGuest -VM $VCSAVMObject -GuestCredential $VCSACred.value -force -ErrorAction Stop
                            Write-Verbose ("[Import-VUMContent]File copied to VCSA.")
                        } # try
                        catch {
                            Write-Debug ("[Import-VUMContent]Failed to copy file to VCSA.")
                            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)")
            } # VCSA

        } # switch


        ## Start import
        try {
            $taskMoRef = $vumCon.vumWebService.ImportFile_Task($vumCon.vumServiceContent.fileUploadManager, $importSpec)
            Write-Verbose ("[Import-VUMContent]Import task started.")
        } # try
        catch {
            Write-Debug ("[Import-VUMContent]Failed to import image file.")
            throw ("Failed to import file. " + $_)
        } # catch


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

        try {
            $Task = Get-Task -Id $taskId -ErrorAction Stop
            Write-Verbose ("[Import-VUMContent]Got task object for import.")
        } # try
        catch {
            Write-Debug ("[Import-VUMContent]Failed to get task.")
            throw ("Failed to get task object. " + $_)
        } # 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 ("[Import-VUMContent]Got task object, verifying status.")
        } # try
        catch {
            Write-Debug ("[Import-VUMContent]Failed to get task.")
            throw ("Failed to get task object. " + $_)
        } # catch


        ## Get task result
        if ($Task.state -ne "Success") {
            Write-Debug ("[Import-VUMContent]Import task failed.")
            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 ("[Import-VUMContent]Content type is Patch, confirming import.")

                ## Get vum task info so we can get associated session ID
                try {
                    $taskInfo = $vumCon.vumWebService.getVUMTaskInfo($vumCon.vumServiceContent.taskManager, $taskMoRef)
                    Write-Verbose ("[Import-VUMContent]Got VUM task.")
                } # try
                catch {
                    Write-Debug ("[Import-VUMContent]Failed to get VUM task.")
                    throw ("Failed to get VUM task object. " + $_)
                } # catch


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


                Write-Verbose ("[Import-VUMContent]Confirm spec set.")


                ## Confirm imported patches
                try {
                    $taskMoRef = $vumCon.vumWebService.ImportFile_Task($vumCon.vumServiceContent.fileUploadManager, $confirmSpec)
                    Write-Verbose ("[Import-VUMContent]Confirm task started.")
                } # try
                catch {
                    Write-Debug ("[Import-VUMContent]Failed to import patch file.")
                    throw ("Failed to import file. " + $_)
                } # catch


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

                try {
                    $Task = Get-Task -Id $taskId -ErrorAction Stop
                    Write-Verbose ("[Import-VUMContent]Got task.")
                } # try
                catch {
                    Write-Debug ("[Import-VUMContent]Failed to get task.")
                    throw ("Failed to get task object. " + $_)
                } # 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 ("[Import-VUMContent]Got task.")
                } # try
                catch {
                    Write-Debug ("[Import-VUMContent]Failed to get task.")
                    throw ("Failed to get task object. " + $_)
                } # catch


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

                Write-Verbose ("Import has completed.")

        } # if

    } # process

    end {

        ## Logoff session
        $vumCon.vumWebService.VciLogout($vumCon.vumServiceContent.sessionManager)

    } # end

} # function