Invoke-vCloud.psm1

## Invoke-vCloud.psm1 - Wrapper for Invoke-RestMethod for consuming VMware vCloud Director API

# Main function to interact with the vCloud Director REST API from within PowerShell
# Note: Does not require PowerCLI, but without it you'll need to specify a valid x-vcloud-authorization token to authenticate
# against the vCloud API.

Function Invoke-vCloud(
[Parameter(Mandatory=$true)][uri]$URI,      # We must specify the API endpoint
[string]$ContentType,                       # Optional ContentType for returned XML
[string]$Method = 'GET',                    # HTTP verb to use (default 'GET')
[string]$ApiVersion = '27.0',               # vCloud Director API version (default to vCD 8.20 - version 27.0)
[string]$Body,                              # Any body document to be submitted to the API
[int]$Timeout = 40,                         # Timeout in seconds to wait for API response (by default)
[boolean]$WaitForTask = $false,             # Should we wait for task completion if reponse includes a task href?
[string]$vCloudToken                        # If not already authenticated using Connect-CIServer (PowerCLI) allow us to specify a token
)
{
<#
.SYNOPSIS
Provides a wrapper for Invoke-RestMethod for vCloud Director API calls
.DESCRIPTION
Invoke-vCloud provides an easy to use interface to the vCloud Director
API and provides sensible defaults for many parameters. It wraps the native
PowerShell Invoke-RestMethod cmdlet.
.PARAMETER URI
A mandatory parameter which provides the API URI to be accessed.
.PARAMETER ContentType
An optional parameter which provides the ContentType of any submitted data.
.PARAMETER Method
An optional parameter to specify the HTML verb to be used (GET, PUT, POST or
DELETE). Defaults to 'GET' if not specified.
.PARAMETER ApiVersion
An optional parameter to specify the API version to be used for the call.
Defaults to '27.0' if not specified which is the API version for vCloud
Director v8.20.
.PARAMETER Body
An optional parameter which specifies XML body to be submitted to the API
(usually for a PUT or POST action).
.PARAMETER Timeout
An optional parameter which specifies the time (in seconds) to wait for an API
call to complete. Defaults to 40 seconds if not specified.
.PARAMETER WaitForTask
If the API call we submit results in a Task object indicating an asynchronous
vCloud task, should we wait for this to complete before returning? Defaults to
$false.
.PARAMETER vCloudToken
An alternative method of passing a session token to Invoke-vCloud if there is
no current PowerCLI session established to a vCloud endpoint. The session must
have already been established and be still valid (not timed-out). The value
supplied is copied to the 'x-vcloud-authorization' header value in API calls.
.OUTPUTS
An XML document returned from the vCloud API (if not waiting for task), or a
success/failure indication if waiting for a task to complete.
.EXAMPLE
Returns list of Organizations to which my current vCloud Session has access
> $orgs = Invoke-vCloud -URI https://my.cloud.com/api/org
> $orgs.InnerXML
<?xml version="1.0" encoding="UTF-8"?>
<OrgList xmlns="http://www.vmware.com/vcloud/v1.5" href="https://my.cloud.com/api/org/" type="application/vnd.vmware.vcloud.orgList+xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.vmware.com/vcloud/v1.5 http://my.cloud.com/api/v1.5/schema/master.xsd">
    <Org href="https://my.cloud.com/api/org/6f14c081-3728-4918-a5cc-bb756452f485" name="MyDemoOrg" type="application/vnd.vmware.vcloud.org+xml" />
</OrgList>
.NOTES
You must either have an existing PowerCLI connection to vCloud Director
(Connect-CIServer) in your current PowerShell session, or provide a valid token
(using the -vCloudToken parameter) in order to authenticate against the API for
calls to suceed.
#>

    $mySessionID = ($Global:DefaultCIServers | Where-Object { $_.Name -eq $URI.Host }).SessionID
    if (!$mySessionID) {                    # If we didn't find an existing PowerCLI session for our URI
        if ($vCloudToken) {                 # But we have been passed a x-vcloud-authorization token, use that
            $mySessionID = $vCloudToken
        } else {                            # Otherwise we have no authentication mechanism available so quit
            Write-Error ("No existing session found and no vCloudToken specified, cannot authenticate, exiting.")
            Return
        }
    } # $mySessionID not set

    # If ContentType or Body are not specified, remove the variable definitions so they won't get passed to Invoke-RestMethod:
    if (!$ContentType) { Remove-Variable ContentType }
    if (!$Body) { Remove-Variable Body }

    # Configure HTTP headers for this request:
    $Headers = @{ "x-vcloud-authorization" = $mySessionID; "Accept" = 'application/*+xml;version=' + $ApiVersion }

    # Submit API request:
    Try {
        [xml]$response = Invoke-RestMethod -Method $Method -Uri $URI -Headers $Headers -Body $Body -ContentType $ContentType -TimeoutSec $Timeout
    }
    Catch {                                 # Something went wrong, so return error information and exit:
        Write-Warning ("Invoke-vCloud Exception: $($_.Exception.Message)")
        if ( $_.Exception.ItemName ) { Write-Warning ("Failed Item: $($_.Exception.ItemName)") }
        Return
    }

    if ($WaitForTask) {                     # If we've requested to wait for the task to complete
        if ($response.Task.href) {          # and we have a task href in the document returned

            Write-Host ("Task submitted successfully, waiting for result")
            Write-Host ("q=queued, P=pre-running, .=Task Running:")

            while($timeout -gt 0) {
                $taskxml = Invoke-RestMethod -Uri $response.Task.href -Method 'Get' -Headers $Headers -TimeoutSec 5        # Get/refresh our task status
                switch ($taskxml.Task.status) {
                    "success" { Write-Host " "; Write-Host "Task completed successfully"; return $true; break }
                    "running" { Write-Host -NoNewline "." }
                    "error" { Write-Host " "; Write-Warning "Error running task"; return $false; break }
                    "canceled" { Write-Host " "; Write-Warning "Task was cancelled"; return $false; break }
                    "aborted" { Write-Host " "; Write-Warning "Task was aborted"; return $false; break }
                    "queued" { Write-Host -NoNewline "q" }
                    "preRunning" { Write-Host -NoNewline "P" }
                } # switch on current task status
                $timeout -= 1                                           # Decrease our timeout
                Start-Sleep -s 1                                        # Pause 1 second
            } # Timeout expired
            Write-Warning "Task timeout reached (task may still be in progress)"
            return $false
        } else {
            Write-Warning 'Wait for task requested, but no task returned by vCloud API'
        }
    } # WaitForTask

    # Return API response to caller
    Return $response

} # Invoke-vCloud Function end

# Export function from module
Export-ModuleMember -Function Invoke-vCloud