The Citrix XenApp Essentials Service UI provides a great way to create and manage customer catalogs. However, automated scripts can make it easy to perform some repetitive tasks around catalog management. One instance of such a task is updating the catalog image. Many customers update a catalog image often by applying Windows updates and security patches. Do you frequently update your catalog image often? If yes, we have some good news for you.
We are excited to announce the Tech Preview of Citrix XenApp Essentials Service REST APIs. The individual APIs can be browsed and tried out at Citrix XenApp Essentials Service REST APIs – Tech Preview
Complete API documentation with a detailed explanation of all REST APIs request and response is available here.
NOTE: The following code sample assumes that you already have a Citrix Cloud account and know how to get your customerId, clientId and clientSecret for API access. If you need help with that, please refer Getting Started with Citrix Cloud APIs.
Step 1: Get your Citrix Cloud bearer token
Add the bearer token in the Authorization request header for all API calls for authentication.
function GetBearerToken {
param (
[Parameter(Mandatory=$true)]
[string] $clientId,
[Parameter(Mandatory=$true)]
[string] $clientSecret
)
$postHeaders = @{“Content-Type”=”application/json”}
$body = @{
“ClientId”=$clientId;
“ClientSecret”=$clientSecret
}
$trustUrl = “https://trust.citrixworkspacesapi.net/root/tokens/clients”
$response = Invoke-RestMethod -Uri $trustUrl -Method POST -Body (ConvertTo-Json $body) -Headers $postHeaders
$bearerToken = $response.token
return $bearerToken;
}
$clientId = "b959ac67-..." #Replace with your clientId
$clientSecret = "D8..." #Replace with your clientSecret
$bearerToken = GetBearerToken $clientId $clientSecret
Step 2: Get your Site ID
Get the ID of the site where Citrix hosts your resources.
function GetSites {
param (
[Parameter(Mandatory=$true)]
[string] $customerId,
[Parameter(Mandatory=$true)]
[string] $bearerToken
)
$requestUri = [string]::Format("https://catalogs.apps.cloud.com/{0}/sites", $customerId)
$headers = @{"Accept"="application/json";
"Authorization"="CWSAuth bearer=$bearerToken"}
$response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers
return $response
}
#Call the GetSites function to get the site ID
$sites = GetSites $customerId $bearerToken
$siteId = $sites.Sites[0].Id
Step 3: Add the master image to your account
Add the new master image to update the catalog to your account.
function AddMasterImage {
param (
[Parameter(Mandatory=$true)]
[string] $customerId,
[Parameter(Mandatory=$true)]
[string] $siteId,
[Parameter(Mandatory=$true)]
[string] $bearerToken,
[Parameter(Mandatory=$true)]
[string] $jsonBody
)
$requestUri = [string]::Format("https://catalogs.apps.cloud.com/{0}/{1}/images", $customerId, $siteId)
$headers = @{"Accept"="application/json";
"Content-Type"="application/json"
"Authorization"="CWSAuth bearer=$bearerToken"}
$response = Invoke-RestMethod -Uri $requestUri -Method POST -Headers $headers -Body $jsonBody
return $response
}
#Post body to pass to the API
$body = @{
"Name" = "Finance Image";
"ResourceGroup" = "FinanceRG";
"StorageAccount" = "xaensfa5edge1gu4o1";
"SubscriptionId" = "c38c7a5c-...";
"Validate" = $true;
"VhdUrl" = "https://xaensfa5edge1gu4o1.blob.core.windows.net/vhds/XAEnsfa5-Findisk0.vhd"
}
#Call the API to Add the master image
$imageId = AddMasterImage $customerId $siteId $bearerToken (ConvertTo-Json $body)
Step 4: Wait for the master image to finish processing
After adding the image, XenApp Essentials service performs certain validations on the image to ensure that it is ready. Validations like the supported OS version, supported Citrix Server VDA, etc are done as part of this processing. Wait until all the validations are complete and the service indicates that the image is ready for use. If the image state is Failed after the processing, then please correct the error and rerun the script.
function GetMasterImage {
param (
[Parameter(Mandatory=$true)]
[string] $customerId,
[Parameter(Mandatory=$true)]
[string] $siteId,
[Parameter(Mandatory=$true)]
[string] $bearerToken,
[Parameter(Mandatory=$true)]
[string] $imageId
)
$requestUri = [string]::Format("https://catalogs.apps.cloud.com/{0}/{1}/images/{2}", $customerId, $siteId, $imageId)
$headers = @{"Accept"="application/json";
"Authorization"="CWSAuth bearer=$bearerToken"}
$response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers
return $response
}
#wait until the image finishes processing
while ($true)
{
$image = GetMasterImage $customerId $siteId $bearerToken $imageId
if ($image.State -eq "Ready")
{
Write-Host "The image is ready to be used by the catalog"
break;
}
elseif ($image.State -eq "Failed")
{
Write-Host "Image validation failed with error $($image.Status). Please correct the error and add the image again"
break;
}
Start-Sleep -Seconds 60
}
Step 5: Get the ID of the catalog to update
Skip this step if you already know the catalog ID.
function GetAllCatalogs {
param (
[Parameter(Mandatory=$true)]
[string] $customerId,
[Parameter(Mandatory=$true)]
[string] $siteId,
[Parameter(Mandatory=$true)]
[string] $bearerToken
)
$requestUri = [string]::Format("https://catalogs.apps.cloud.com/{0}/{1}/catalogs", $customerId, $siteId)
$headers = @{"Accept"="application/json";
"Authorization"="CWSAuth bearer=$bearerToken"}
$response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers
return $response
}
#Get all the catalogs and select the one you are interested in
$catalogs = GetAllCatalogs $customerId $siteId $bearerToken
$catalogName = “your catalog name”
$catalogToUpdate = $catalogs.Catalogs | where {$_.Name -eq $catalogName}
$catalogId = $catalogToUpdate.Id
Step 6: Update the catalog with your new master image
Once the master image is in Ready state, update the catalog using the image.
function UpdateCatalogImage {
param (
[Parameter(Mandatory=$true)]
[string] $customerId,
[Parameter(Mandatory=$true)]
[string] $siteId,
[Parameter(Mandatory=$true)]
[string] $bearerToken,
[Parameter(Mandatory=$true)]
[string] $catalogId,
[Parameter(Mandatory=$true)]
[string] $jsonBody
)
$requestUri = [string]::Format("https://catalogs.apps.cloud.com/{0}/{1}/catalogs/{2}/updateimage", $customerId, $siteId, $catalogId)
$headers = @{"Accept"="application/json";
"Content-Type"="application/json"
"Authorization"="CWSAuth bearer=$bearerToken"}
$response = Invoke-RestMethod -Uri $requestUri -Method POST -Headers $headers -Body $jsonBody
return $response
}
#Post body to pass to the API
$body = @{
"TemplateId" = $imageId;
"CitrixPrepared" = $false;
"VdaUpdateDelay" = "60"
}
#Update the catalog with the new master image
$response = UpdateCatalogImage $customerId $siteId $bearerToken $catalogId (ConvertTo-Json $body)
Step 7: Wait for the catalog update to complete
XenApp Essentials service updates the catalog VDAs with the new master image. This will take some time. Keep polling the state of the catalog until the processing is complete.
function GetCatalog {
param (
[Parameter(Mandatory=$true)]
[string] $customerId,
[Parameter(Mandatory=$true)]
[string] $siteId,
[Parameter(Mandatory=$true)]
[string] $bearerToken,
[Parameter(Mandatory=$true)]
[string] $catalogId
)
$requestUri = [string]::Format("https://catalogs.apps.cloud.com/{0}/{1}/catalogs/{2}", $customerId, $siteId, $catalogId)
$headers = @{"Accept"="application/json";
"Authorization"="CWSAuth bearer=$bearerToken"}
$response = Invoke-RestMethod -Uri $requestUri -Method GET -Headers $headers
return $response
}
#wait for the catalog update to finish
while ($true)
{
$catalog = GetCatalog $customerId $siteId $bearerToken $catalogId
if ($catalog.State -eq "Active" -or $catalog.State -eq "InputRequired")
{
Write-Host "Catalog update complete successfully"
break;
}
elseif ($catalog.State -eq "Error")
{
Write-Host "Catalog update failed with error $($catalog.StatusMessage). Please correct the error and try again"
break;
}
else
{
Write-Host "Catalog state is $($catalog.State) and substate is $($catalog.SubState)"
}
Start-Sleep -Seconds 60
}
Download the sample script discussed in this blog here.