With PowerCLI’s ConnectCI-Server CmdLet it is pretty easy to login to a vCD host. And for most of the time the supplied CmdLets are sufficient enough to solve the problems we deal with it daily. However, sometimes we would like to make specific REST calls to vCD directly (for example when the Searc-Cloud CmdLet is not doing what we want). In this case you would normally make another connection to vCD. This commonly involves saving a cookie or session state, but moreover requries you to know how to make the login call. This seems an unneccessary burden, because you already logged in successfully earlier on via Connect-CiServer.
But there is help. When you save the session of your PowerCLI logon you will see it contains several properties with SessionSecret and SessionId looking quite promising:
PS > $SessionVcd | fl * -force IsConnected : True ServiceUri : https://vcloud.sharedop.com/api/ SessionId : rawCookieText User : edgar RefCount : 1 Org : System Port : 443 Version : 1.5.1.622844 ExtensionData : VMware.VimAutomation.Cloud.Views.VCloud Id : /CIServer=edgar:system@vcloud.sharedop.com:443/ Name : vcloud.sharedop.com Client : /CIServer=edgar:system@vcloud.sharedop.com:443/ Uid : /CIServer=edgar:system@vcloud.sharedop.com:443/CIServer=&slash;CIServer&eq;edgar:system@vcloud.sharedop.com:443&slash;/ Description : Represents a vCloud server session. Href : https://vcloud.sharedop.com:443/api/ SessionSecret : rawCookieText
With that you can perform an actual query to vCD without an extra login:
# create WebClient
$wc = New-Object System.Net.WebClient;
# extract version number from logged in PowerCLI session
$SessionVcd.Version -match '^(\d\.\d)\..+$'
$HeaderAcceptValue = [string]::Format("application/*+xml;version={0}", $Matches[1]);
$HeaderCookieValue = [string]::Format("vcloud-token={0}; Secure; Path=/", $SessionVcd.SessionSecret);
# add headers for request
$wc.Headers.Add("Accept", $HeaderAcceptValue);
$wc.Headers.Add("Cookie", $HeaderCookieValue);
# perform request
$aBytes = $wc.DownloadData($SessionVcd.ServiceUri.AbsoluteUri);
# transform to xml document
$xmlResult = [xml] [System.Text.Encoding]::ASCII.GetString($aBytes);
# extract org link (or whatever)
$xmlResult.Session.link[0].href
https://vcloud.sharedop.com/api/org/
When using the Invoke-RestMethod (available from PS v3) you have to fill the Session object first. Adding headers will not be enough. Note: The WebClient used in the example above is also available on PS v2.
As a side note for the WebClient object: You have to add the “Accept”-Header again for every call you make. The “Cookie” is persistent on the object, though.
As you can see it is pretty easy to re-use the log in from PowerCLI without having to worry about the version and login details of the vCD REST API. As an additional benefit for long running tasks. In case you timeout on a session all you need to do is refresh one connection and take the properties from the returned session object instead of doing two re-logins.
Below you find an example of a Cmdlet function that takes a VCD session and generates a REST call against vCD:
function Invoke-VcdRestCall {
<#
.SYNOPSIS
Performs a REST call against a vCD host and returns the XML result set.
.DESCRIPTION
Performs a REST call against a vCD host and returns the XML result set.
.OUTPUTS
This Cmdlet returns an XML document on success. On failure it returns $null.
.INPUTS
See PARAMETER section for a description of input parameters.
.PARAMETER CIServer
The vCD host to execute the REST call against. Default is '$global:DefaultCIServers[0]'.
.PARAMETER Method
The HTTP method of the REST call. Default is 'GET'. Possible values: 'GET', 'POST', 'DELETE'. 'PUT'.
Alias: m
.PARAMETER Api
The command part of the REST call. Default is 'query'. For possible values see the vCD REST reference.
Alias: a
.PARAMETER QueryParameters
The QueryString part of the REST call. For possible values see the vCD REST reference.
Alias: q
.PARAMETER Body
Optional body of the REST call when using a POST or PUT operation/method. Default is '$null'. For possible values see the vCD REST reference.
Alias: b
.EXAMPLE
Gets all possible 'query' operations of the vCD REST query service.
$xmlResponse = Invoke-VcdRestCall;
$xmlResponse.QueryList.Link;
.EXAMPLE
Gets all vCD Cells.
$xmlResponse = Invoke-VcdRestCall -Api "query" -QueryParameters "type=cell";
$xmlResponse.QueryResultRecords.CellRecord;
.LINK
Online Version: http://dfch.biz/PS/vCD/Utilities
.NOTES
Requires Powershell v3.
Requires module 'biz.dfch.PS.System.Logging'.
Requires a PowerCLI session to VCD.
#>
[CmdletBinding(
HelpURI='http://dfch.biz/PS/vCD/Utilities/Invoke-VcdRestCall'
)]
[OutputType([xml])]
Param (
[Parameter(Mandatory = $false, Position = 2)]
[alias("s")]
$CIServer = $global:DefaultCIServers[0]
,
[ValidateSet('GET', 'POST', 'PUT', 'DELETE')]
[Parameter(Mandatory = $false, Position = 1)]
[alias("m")]
[string] $Method = 'GET'
,
[Parameter(Mandatory = $false, Position = 0)]
[alias("a")]
[string] $Api = 'query'
,
[Parameter(Mandatory = $false, Position = 3)]
[alias("q")]
[string] $QueryParameters = $null
,
[Parameter(Mandatory = $false, Position = 4)]
[alias("b")]
[string] $Body = $null
) # Param
BEGIN {
$datBegin = [datetime]::Now;
[string] $fn = $MyInvocation.MyCommand.Name;
Log-Debug -fn $fn -msg "CALL. Api: '$Api'." -fac 1;
}
PROCESS {
[boolean] $fReturn = $false;
$retNull = $null;
try {
# Parameter validation
if( !$CIServer -or ($CIServer.GetType().FullName -ne "VMware.VimAutomation.Cloud.Impl.V1.CIServerImpl") ) {
Log-Error $fn "Invalid input parameter type specified: CIServer [$($CIServer.GetType().FullName)]. Aborting ...";
throw($gotoFailure);
} # if
if( !$CIServer.IsConnected ) {
Log-Error $fn "CIServer '$($CIServer.Name)' is not connected. Reconnect session before trying again. Aborting ...";
throw($gotoFailure);
} # if
if(!$Api) {
Log-Error $fn "Invalid or empty input parameter specified: Api. Aborting ...";
throw($gotoFailure);
} # if
# create WebClient
$SessionVcd = $CIServer;
$wc = New-Object System.Net.WebClient;
# extract version number from logged in PowerCLI session
$fReturn = $SessionVcd.Version -match '^(\d\.\d)\..+$';
if(!$fReturn) {
Log-Error $fn "CIServer version could not be matched: '$($SessionVcd.Version)'. Aborting ...";
} # if
$HeaderAcceptValue = [string]::Format("application/*+xml;version={0}", $Matches[1]);
$HeaderCookieValue = [string]::Format("vcloud-token={0}; Secure; Path=/", $SessionVcd.SessionSecret);
# add headers for request
$null = $wc.Headers.Add("Accept", $HeaderAcceptValue);
$null = $wc.Headers.Add("Cookie", $HeaderCookieValue);
[string] $Uri = [string]::Format("{0}{1}?{2}", $SessionVcd.ServiceUri.AbsoluteUri, $Api, $QueryParameters);
Log-Debug $fn "Invoking '$Method' '$Uri' ...";
[string] $response = '';
if('GET'.Equals($Method.ToUpper()) ) {
$response = $wc.DownloadString($Uri);
} else {
$response = $wc.UploadString($Uri, $Method.ToUpper(), $Body);
} # if
$null = $wc.Dispose();
# transform to xml document
$xmlResult = [xml] $response;
$OutputParameter = $xmlResult.Clone();
} # try
catch {
if($gotoSuccess -eq $_.Exception.Message) {
$fReturn = $true;
} else {
[string] $ErrorText = "catch [$($_.FullyQualifiedErrorId)]";
$ErrorText += (($_ | fl * -Force) | Out-String);
$ErrorText += (($_.Exception | fl * -Force) | Out-String);
$ErrorText += (Get-PSCallStack | Out-String);
if( ($_.Exception.InnerException) -and ([System.Net.WebException] -eq ($_.Exception.InnerException.GetType())) ) {
Log-Critical $fn "Operation '$Method' '$Api' with CIServer '$($CiServer.Name)' FAILED [$_].";
Log-Debug $fn $ErrorText -fac 3;
} # [System.Net.WebException]
else {
Log-Error $fn $ErrorText -fac 3;
if($gotoFailure -ne $_.Exception.Message) { Write-Verbose ("$fn`n$ErrorText"); }
} # other exceptions
$fReturn = $false;
$OutputParameter = $null;
} # !$gotoSuccess
} # catch
finally {
# Clean up
} # finally
return $OutputParameter;
} # PROCESS
END {
$datEnd = [datetime]::Now;
Log-Debug -fn $fn -msg "RET. fReturn: [$fReturn]. Execution time: [$(($datEnd - $datBegin).TotalMilliseconds)]ms. Started: [$($datBegin.ToString('yyyy-MM-dd HH:mm:ss.fffzzz'))]." -fac 2;
} # END
} # function
Set-Alias -Name Invoke-VcdCommand -Value Invoke-VcdRestCall;
Export-ModuleMember -Function Invoke-VcdRestCall -Alias Invoke-VcdCommand;
As a side note: you might want to use the same cookie value for access to vCD Administration GUI (via vcloud_session_id) as well. For further information see Login to vCloud Director 1.5 with a generated Cookie.
