Overview
Citrix developed the Citrix Image Portability Service (IPS) to simplify moving workloads between different resource locations and hypervisor platforms. With IPS you can even move your workloads between on-premises and public cloud environments.
Citrix Image Portability Service provides Citrix administrators with a simple workflow to manage workloads between on-premises and public cloud platforms like:
- Microsoft Azure
- Google Cloud Platform (GCP)
- AWS (AWS)
- VMware vSphere
- Citrix XenServer
- Nutanix
Developed using App Layering cross-platform technology, the Citrix Image Portability Service uses Citrix DaaS REST-APIs to migrate on-premises Machine Creation or Provisioning Services Images.
The Image Portability workflow is the framework for a migration of an Image from your on-premises location to your public cloud subscription.
After exporting your Image, Image Portability Service helps you transfer the Image to your public cloud subscription and prepare it to run.
Finally, Citrix Provisioning or Machine Creation Services provisions the Image in your public cloud subscription.
The product documentation can be found at https://docs.citrix.com/en-us/citrix-daas/migrate-workloads.html.
Scope
In this guide, we show how to migrate workflows from on-premises (VMware vSphere 8) to public cloud (Microsoft Azure and AWS).
We use three methods:
- Pure API calls using Postman
- PowerShell (PoSH)
- A self-written Windows application that encapsulates all necessary API calls and PowerShell scripts into a GUI
Important Note:
The self-written .NET application only shows examples how to implement the API calls and PowerShell scripts into an application.
No warranty, no liability, and no support of any kind are given for the application.
Conceptual architecture
The Citrix Image Portability Service (IPS) is a REST-API with all the semantics and requirements of other Citrix DaaS and Virtual Apps and Desktops APIs.
The headers and authentication work the same as the other Citrix APIs do.
Citrix IPS relies on the Connector Appliance for Cloud Services to communicate with Citrix Cloud. IPS uses provisioning technology to create and manage resources in the resource location where the Connector Appliance is installed. More info: https://docs.citrix.com/en-us/tech-zone/learn/tech-briefs/Image-portability-service.html.
Image Portability Service components
- Citrix Cloud services
- Citrix Credential Wallet
- Citrix Connector Appliance
- Compositing Engine VM
- PowerShell modules
Citrix Cloud services
The Citrix Cloud Services API is a REST-API service that interacts with the Image Portability Service. Using the REST-API service, you can create and monitor Image Portability jobs.
For example, you make an API call to start an Image Portability job, such as to export a disk, and then make calls to get the status of the job.
Citrix Credential Wallet
The Citrix Credential Wallet service securely manages system credentials, allowing the Image Portability Service to interact with your assets.
For example, when exporting a disk from vSphere to an SMB share, the Image Portability Service requires credentials to open a connection to vSphere and to the SMB share to write the disk. The credentials are stored in the Credential Wallet and the Image Portability Service can retrieve and use those credentials. Create all necessary credentials in the right format before use IPS.
This service gives you the ability to fully manage your credentials. The Cloud Services API acts as an access point, giving you the ability to create, update, and delete credentials.
Compositing Engine
The Compositing Engine is the workhorse of the Image Portability Service. The Compositing Engine (CE) is a single VM created at the start of an Image Portability job. The CE is created in the jobs target hypervisor/hyperscaler.
For example, when exporting a disk from vSphere, the job creates the CE on the vSphere server. When a prepare job runs in Azure, AWS, or Google Cloud, it creates the CE in Azure, AWS, or Google respectively. The CE mounts your disk to itself, and then does the necessary manipulations to the disk. Upon completion of the preparation or export job, the CE VM and all of its components are deleted.
Connector Appliance
The Connector Appliance runs in your environment and acts as a controller for individual jobs. It receives job instructions from the Cloud service, and creates and manages the Compositing Engine VMs.
The Connector Appliance VM acts as a single, secure point of communication between the Citrix Cloud services. Deploy one or more Connector Appliances in each of your Resource Locations.
By co-locating the Connector Appliance and the Compositing Engine, the deployment’s security posture increases greatly. The Connector Appliance keeps all components and communications within the same Resource Location. It needs access to the following URLs to prepare Images in the Image Portability Service:
*.layering.cloud.com credentialwallet.citrixworkspaceapi.net graph.microsoft.com login.microsoftonline.com management.azure.com *.blob.storage.azure.net
PowerShell modules
We provide a collection of PowerShell modules for use within scripts as a starting point to develop your own custom automation. The supplied modules are supported as is, but you can modify them if necessary for your deployment.
The PowerShell automation uses supplied configuration parameters to compose a REST call to the Citrix Cloud API service to start the job. It provides you with periodic updates as the job progresses.
If you want to develop your own automation solution, you can make calls to the cloud service directly using your preferred programming language. See the API portal for detailed information about configuring and using the Image Portability Service REST endpoints and PowerShell modules https://developer.cloud.com/citrixworkspace/citrix-daas/Image-portability-service/docs/overview.
To use the PowerShell scripts, you need the following:
- The latest version of PowerShell
- Connectivity to the Microsoft PowerShell Gallery to download the required PowerShell libraries
Install-Module -Name PowerShellGet -Force -Scope CurrentUser -AllowClobber Install-Module -Name "Citrix.Workloads.Portability","Citrix.Image.Uploader" -Scope CurrentUser Update-Module -Name "Citrix.Workloads.Portability","Citrix.Image.Uploader" -Force Install-Module -Name Az.Accounts -Scope CurrentUser -AllowClobber -Force Install-Module -Name Az.Compute -Scope CurrentUser -AllowClobber -Force Install-Module -Name VMware.PowerCLI -Scope CurrentUser -AllowClobber -Force -SkipPublisherCheck Install-Module -Name Amazon AWS.Tools.Installer Install-Amazon AWSToolsModule Amazon AWS.Tools.EC2,Amazon AWS.Tools.S3
All PowerShell cmdlets have built-in help containing full syntax and examples:
Get-Help Start-IpsVsphereExportJob -Full
PS C:\TACG> Get-Help Start-IpsVsphereExportJob NAME Start-IpsVsphereExportJob OVERVIEW Starts an Image Portability Service job to export an Image from Vsphere. SYNTAX Start-IpsVsphereExportJob -CustomerId <String> -SmbHost <String> [-SmbPort <String>] -SmbShare <String> [-SmbPath <String>] -SmbDiskName <String> [-SmbDiskFormat <String>] -SmbCwId <String> [-Deployment <String>] -ResourceLocationId <String> -VsphereCwSecretId <String> -VsphereHost <String> [-VspherePort <Int32>][-VsphereSslCaCertificateFilePath <String>] [-VsphereSslCaCertificate <String>] [-VsphereSslFingerprint <String>] [-VsphereSslNoCheckHostname <Boolean>] -VsphereDataCenter <String> -VsphereDataStore <String> [-VsphereResourcePool <String>] -VsphereNetwork <String> [-VsphereHostSystem <String>] [-VsphereCluster <String>] -SourceDiskName <String> [-AssetsId <String>] [-Tags <Hashtable>] [-Timeout <Int32>] [-Prefix <String>] [-JobDebug <Boolean>] [-Flags <String[]>] [-DryRun <Boolean>] [-SecureClientId <String>] [-SecureSecret <String>] [-LogFileDir <String>] [-LogFileName <String>] [-OverwriteLog] [-Force] [<CommonParameters>] Start-IpsVsphereExportJob -ConfigJsonFile <String> [-SecureClientId <String>] [-SecureSecret <String>] [-LogFileDir <String>] [-LogFileName <String>] [-OverwriteLog] [-Force] [<CommonParameters>] DESCRIPTION Starts an Image Portability Service job to export an Image from Vsphere to a virtual disk file on a SMB fileshare. Start-IpsVspherePublishJob is an alias for this cmdlet. LINKS REMARKS To open the examples, type: "get-help Start-IpsVsphereExportJob -examples". Obtain more information by using: "get-help Start-IpsVsphereExportJob -detailed". Technical information can be be obtained by using: "get-help Start-IpsVsphereExportJob -full".
Requirements
- A Citrix Connector Appliance in all resource locations where IPS will be used.
- A Windows (SMB) file share must be locally accessible to any export, prepare, and publish job.
- A valid Citrix Cloud Customer ID and Citrix DaaS entitlement.
- On-premises master Machine Create Services (MCS) or Provisioning Services (PVS) Image.
- Access to public cloud resource location.
- A Citrix Machine Catalog Image - IPS requires using Images that have one of the following tested configurations:
- Windows Server 2016, 2019, and 2022H2
- Windows 10 or 11
- Provisioned using Machine Creation Services or Citrix Provisioning
- Citrix Virtual Apps and Desktops VDA version 1912CU6, 1912CU7, 2203CU1, 2203CU2, 2212, 2303, 2305, 2308
- Citrix PVS Agent version 1912CU6, 1912CU7, 2203CU1, 2203CU2, 2212, 2303, 2305, 2308
- Remote Desktop Services enabled for console access in Azure
Image Portability service supports the following hypervisors and cloud platforms
Source platforms:
- VMware vSphere 7.0 and 8.0
- Citrix Hypervisor/XenServer 8.2
- Nutanix Prism Element 3.x
- Microsoft Azure
- Google Cloud Platform
Destination platforms:
- VMware 8.0
- Microsoft Azure
- AWS
- Google Cloud Platform
For using Postman
Download the Postman app for API calls: https://www.postman.com/downloads/
For using PowerShell
Download the Remote PowerShell kit: https://docs.citrix.com/en-us/citrix-daas/sdk-api.html#citrix-virtual-apps-and-desktops-remote-powershell-sdk
Install-Module -Name "Citrix.Workloads.Portability","Citrix.Image.Uploader"
Add-PSSnapin Citrix.* Get-Module -ListAvailable "Citrix.*" Folder: C:\Program Files\WindowsPowerShell\Modules ModuleType Version Name ExportedCommands ---------- ------- ---- ---------------- Script 2.1.11.0 Citrix.Image.Uploader {Copy-ToAzDisk, Copy-ToAwsDisk, Get-VhdSize, Get-VhdConten... Script 2.3.1 Citrix.Workloads.Portability {Start-IpsAwsPrepareJob, Start-IpsVsphereExportJob, Start-...
For using the Windows .NET application
- Download and install the DaaS Remote PowerShell kit
- Download and install the latest version of PowerShell
- Download and install the latest version of Azure PowerShell
- Download and install .NET 7.0 Core
- Download and install Chilkatsoft Chilkat .NET Core
Important Note:
The self-written .NET application only shows examples how to implement the API calls and PowerShell scripts into an application. No warranty, no liability, and no support of any kind are given for the application.
The .NET application shows the possibility to automate workflows by invoking REST-API calls and PowerShell scripts. The source code implements all 4 steps of the Image Migration Workflow.
Complete Citrix Cloud Prerequisites
- Furthermore, some account information is necessary to obtain – the most important one is the Customer ID:
- After logging in to Citrix Cloud, open the Account Settings window of the Cloud portal:
In the Account Settings window you find the Customer ID. Write it down or save it for further use.
Create an API client
API clients in Citrix Cloud are always tied to one administrator and one customer. API clients are not visible to other administrators. If you want to access to more than one customer, you must create API clients within each customer.
API clients are automatically restricted to the rights of that administrator that created it. For example, if an administrator is restricted to access only notifications, then the administrator's API clients have the same restrictions:
- Reducing an administrator’s access also reduces the access of the API clients owned by that administrator.
- Removing an administrator’s access also removes the administrator's API clients.
- To create an API client, select the Identity and Access Management option from the menu. If this option does not appear, you may not have adequate permissions to create an API client. Contact your administrator to get the required permissions:
- In the next screen select the API Access tab:
Name your Secure Client and click Create Client.
- Now a message appears that ID and Secret have been created successfully. Download or copy the Client ID and Secret to access the APIs.
Accessing Citrix Cloud via API calls or PowerShell
To call APIs, you must also create a Bearer token. The Bearer token is used for API authentication and authorization. Tokens can be obtained using a standard OAuth 2.0 Client Credential grant flow. For more information about OAuth 2.0 Client Credential grant, see https://tools.ietf.org/html/rfc6749#section-4.4.
To get a bearer token, make a POST call to the trust service's authentication API:
POST https://api-us.cloud.com/cctrustoauth2/{customerid}/tokens/clients
Note:
Use one of the following endpoints based on the geographical region while creating the Citrix Cloud account:
api-ap-s.cloud.com – If your Citrix Cloud account is set to the Asia Pacific South region.
api-eu.cloud.com – If your Citrix Cloud account is set to the European Union region.
api-us.cloud.com – If your Citrix Cloud account is set to the United States region.
api.citrixcloud.jp - If your Citrix Cloud account is set to the Japan region.
Obtain a Bearer token for authentication
Flow to obtain a Bearer token using the Postman app
It is important to set the URI to call and the required parameters correct: The URI must follow this syntax: For example [https://api-us.cloud.com/cctrustoauth2/{customerid}/tokens/clients] where {customerid} is your Customer ID you obtained from the Account Settings page. If your Customer ID is for example 1234567890 the URI would be [https://api-us.cloud.com/cctrustoauth2/1234567890/tokens/clients]
- Paste the correct URI into Postman´s address bar and select
POST
as the method. Verify the correct settings of the API call – please check the Headers tab to reflect the settings:
- The next step is to enter your API credentials into the Body of the API call.
Make sure that the Parameter type is set to
x-www-form-encoded
:
- Now you are ready to call the API to get a Bearer token by pressing the
Send
button:
If everything is set correctly, Postman shows a Response containing a JSON-formatted file containing the Bearer token in the field access-token
:
The token is normally valid for 3600 seconds.
- If an error occured, the Response contains some hints about the error:
To get a bearer token, make a POST call to the trust service's authentication API:
POST https://api-us.cloud.com/cctrustoauth2/{customerid}/tokens/clients
Response:
{ "token_type": "bearer", "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…pZWFOBHuZ63tvGvRA", "expires_in": "3600" }
Now you have a valid Bearer token for further API calls.
Flow to obtain a Bearer token using PowerShell
PS C:\TACG> $tokenUrl = 'https://api-eu.cloud.com/cctrustoauth2/1234567890/tokens/clients' PS C:\TACG> $response = Invoke-WebRequest $tokenUrl -Method POST -Body @{ >> grant_type = "client_credentials" >> client_id = "50XXXXXXXX-XXXXX-XXXXX-XXXXXXXXXXX7" >> client_secret = "8MXXXXXXXXXXXXXXXXXXX==" >> } PS C:\TACG> $token = $response.Content | ConvertFrom-Json PS C:\TACG> $token | Format-List token_type : bearer access_token : J9.eyJzdWI ... BTp3KN5N_qr7Hjk-VTQy3Qp2dCId_cnagZaQleo1E98ifw2eUch1vu8tjYR-_NkA expires_in : 3600
Flow to obtain a Bearer token using PowerShell
- All relevant data is stored in an adjacent JSON file – you can follow the guide to obtain the Customer ID and the API Client values:
- After entering the correct values you can use the Windows application to create the Bearer token:
Now you have your Bearer token for authentication and authorization. It must be included in each API-/PoSH-Call!
Image Migration Workflow
Migrating Images with the Citrix Image Portability Service consists of a 4-stage workflow.
Export:
The export
stage exports Images from the on-premises hypervisor and preps them for upload to the public cloud resource location. This stage can include converting file system types to a common format for the cloud.
Upload:
The upload
stage uploads the Image to the target cloud subscription. This process is a point-to-point transfer using the configuration and credentials for the public cloud supplied by the administrator.
Prepare:
The prepare
stage is a complex set of steps:
- Removal of source platform components
- Injection of target platform components into the Image
- Reconfiguration of the VDA
- Rearm of the OS based on the supplied configuration properties
At the end of this phase, the Image boots once to allow Windows plug-and-play to run and configure the OS for the new platform. Once the preparation completes, the Image is ready for provisioning with MCS.
Publish: The final stage deploys the Image as a new machine catalog.
- In the case of MCS, the Citrix Virtual Apps and Desktops Remote PowerShell SDK can automate the creation of an MCS catalog from the migrated disk.
- In the case of PVS on a HyperScaler, the Image Portability Service provides a REST interface or a PowerShell cmdlet to publish the migrated disk directly into the PVS vDisk store.
Throughout the four stages, the Image Portability Service uses App Layering compositing engines in the background to modify the Image and drive the process.
All Citrix Image Portability workflows are based on the configuration of the source Image and provisioning targets, either MCS or PVS. The workflow chosen determines the steps required by the Image Portability Service.
Part 1: Exporting the Image - Prerequisites
Exporting the Image to an SMB file share is a multi-step process:
- Determine the ID of the Resource Location where the Image resides
- Add new credentials to the Credential Wallet
- Export the Image to the SMB share
In this guide we export the Image from VMware vSphere.
The following vCenter permissions are necessary to run the IPS export disk job in a VMware environment. For current permission requirements look at the product documentation https://docs.citrix.com/en-us/citrix-daas/migrate-workloads.html#vmware-vcenter-required-permissions.
- Cryptographic operations - Direct Access - Datastore - Allocate space - Browse datastore - Low level file operations - Remove file - Folder - Create folder - Delete folder - Network - Assign network - Resource - Assign virtual machine to resource pool - Virtual machine - Change Configuration - Add existing disk - Add new disk - Remove disk - Edit Inventory - Create from existing - Create new - Remove - Interaction - Power off - Power on
Determining the ID of the Resource Location using Postman
GET https://api-eu.cloud.com/resourcelocations Authorization: CWSAuth bearer= {{Bearer-Token-Value}} Citrix-CustomerId: {{Citrix-CustomerID}}
Response (shortened):
{ "items": [ { "id": "58110e95-XXXX-XXXX-XXXX-XXXXXXXXXX", "name": "The Austrian Citrix Guy - vSphere", "internalOnly": false, "timeZone": "GMT Standard Time", "readOnly": false }, { "id": "5faae27c-XXXX-XXXX-XXXX-XXXXXXXXXX", "name": "The Austrian Citrix Guy - Amazon AWS", "internalOnly": false, "timeZone": "GMT Standard Time", "readOnly": false }, { "id": "76918c03-XXXX-XXXX-XXXX-XXXXXXXXXX", "name": "The Austrian Citrix Guy - AzHib", "internalOnly": false, "timeZone": "GMT Standard Time", "readOnly": false }, ... ] }
Copy the value of the id
field as you need it for further calls.
Determining the ID of the Resource Location using PowerShell
PS C:\TACG> Get-ConfigZone | select Name, ExternalUid Name ExternalUid ---- ----------- Initial Zone 00000000-XXXX-XXXX-XXXX-XXXXXXXXXX The Austrian Citrix Guy - Amazon AWS 5faae27c-XXXX-XXXX-XXXX-XXXXXXXXXX The Austrian Citrix Guy - AzGPU b46c6873-XXXX-XXXX-XXXX-XXXXXXXXXX The Austrian Citrix Guy - AzHib 76918c03-XXXX-XXXX-XXXX-XXXXXXXXXX The Austrian Citrix Guy - AzStHCI local e344b537-XXXX-XXXX-XXXX-XXXXXXXXXX The Austrian Citrix Guy - Azure Connectorless eec20fa2-XXXX-XXXX-XXXX-XXXXXXXXXX The Austrian Citrix Guy - On-Prem dc4b9f88-XXXX-XXXX-XXXX-XXXXXXXXXX The Austrian Citrix Guy - vSphere 58110e95-XXXX-XXXX-XXXX-XXXXXXXXXX
Copy the value of the ExternalUid
field as you need it for further calls.
Determining the ID of the Resource Location using the Windows .NET Application
The .NET application requests all available Resource Locations automatically. You can choose the needed one using a drop-down field.
Add new credentials to the Credential Wallet using Postman
The correct types of Credentials are imperative for each operation - for example for Azure you need Azure-type credentials, for SMB SMB-type and so forth. The examples show the creation of UsernamePassword-type credentials.
More info about the different credential types and the creation can be found here: https://developer-docs.citrix.com/en-us/citrix-daas-service-apis/Image-portability-service/credentials.
POST https://api.eu.layering.cloud.com/credentials` Authorization: CWSAuth bearer= {{Bearer-Token-Value}} Citrix-CustomerId: {{Citrix-CustomerID}} Body (Raw): { "id": "vspXXXXXXXX", "type": "UsernamePassword", "username": "XXXXXXXXXXXX", "domain": "XXXXXXXX", "password": "XXXXXXXXXXXXX" }
Response (shortened):
{ "id": "vspXXXXXXXXX", "type": "UsernamePassword", "state": "Ok", "createdAt": "2023-11-13T09:00:18.2371226Z", "updatedAt": "2023-11-13T09:00:18.2371226Z" }
Add new credentials to the Credential Wallet using PowerShell
PS C:\TACG> $Params = @{ >> CustomerId = 'uzXXXXXXXXXXX' >> CredentialType = 'UsernamePassword' >> CredentialId = 'platformid' >> UserDomain = 'XXXXXXXXXXXXXXXXXXXX' >> UserName = 'XXXXXXXXXXXXXXXXXXXXXXX' >> UserPassword = 'XXXXXXXXXXXXXXXXXXXXXXXXX' >> } PS C:\TACG> New-IpsCredentials @Params Logging to C:\TACG\Credentials.log Verbose=False Interactively authenticating for Citrix customer uzXXXXXXXXXXX. Authenticated for Citrix customer uzXXXXXXXXXXX. Creating new UsernamePassword credential platformid geo EU api url https://api.eu.layering.cloud.com/ Created credential id platformid for name platformid platformid
Part 1: Exporting the Image from on-premises vSphere using Postman
Before we can export the Image we need more information from the vSphere platform: The following parameters must be obtained from vSphere:
vCenterHost vCenterPort datacenter datastore cluster network sourceDisk
The following parameters are also required:
- Prefix: name of the export job - ResourceLocationID: ID of the Resource Location of the Image - OutputStorageLocation: - Type (for example SMB) - Credential-ID (for example SMB) - Host-IP (for example 10.10.11.44): must be reachable by the Connector Appliance in the Resource Location of the Image - SharePath (for example SMB): must be reachable by the Connector Appliance in the Resource Location of the Image
When all values are set we can call the API to export the Image.
POST https://api.eu.layering.cloud.com/Images/$export?async=true` Authorization: CWSAuth bearer= {{Bearer-Token-Value}} Citrix-CustomerId: {{Citrix-CustomerID}} Body (Raw): { "platform": "vSphere", "platformCredentialId": "vsXXXXXXXXX", "vCenterHost": "(FQDN)", "vCenterPort": 443, "vCenterSslNoCheckHostname": "true", "datacenter": "XXXX", "datastore": "XXXXX", "cluster": "TACG", "network": "VM Network", "prefix": "my-cejob", "resourceLocationId": "58110e95-XXXX-XXXX-XXXX-XXXXXXXXXX", "outputStorageLocation": { "type": "SMB", "credentialId": "XXXXXXXXX", "host": "10.10.11.99", "sharePath": "_sw" }, "outputImageFilename": "my-exported-Image", "timeoutInSeconds": 3600, "sourceDiskName": "ds:///vmfs/volumes/65245805-XXXX-XXXX-XXXX-48210b5c6cd7/TACG-VSP-W11-M/TACG-VSP-W11-M.vmdk" }
Response:
{ "id": "abcXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX", "type": "exportImage", "overallProgressPercent": 0, "isCancellable": true, "parameters": [], "status": "notStarted", "resultLocation": null, "additionalInfo": null, "warnings": [], "error": [], "createdAt": "2023-10-31T14:05:15Z", "updatedAt": "2023-10-31T14:05:15Z", "startedAt": "2023-10-31T14:05:15Z" }
As the job runs asynchronously, we need another REST-API-call to get the status of the export job using the id
parameter from the response:
GET https://api.eu.layering.cloud.com/jobs/{job-id}` Authorization: CWSAuth bearer= {{Bearer-Token-Value}} Citrix-CustomerId: {{Citrix-CustomerID}}
The Response of the call contains all progress and error information. If the call is successful, we see the exported Image on the referenced SMB share.
Part 1: Exporting the Image from on-premises vSphere using PowerShell
Before we can export the Image we need more information from the vSphere platform: The following parameters must be obtained from vSphere:
vCenterHost vCenterPort datacenter datastore cluster network sourceDisk
The following parameters are also required:
- Prefix: name of the export job - ResourceLocationID: ID of the Resource Location of the Image - OutputStorageLocation: - Type (for example SMB) - Credential-ID (for example SMB) - Host-IP (for example 10.10.11.44): must be reachable by the Connector Appliance in the Resource Location of the Image - SharePath (for example SMB): must be reachable by the Connector Appliance in the Resource Location of the Image
When all values are set we can call the API to export the Image:
PS C:\TACG> $ExportParams = @{ >> CustomerId = 'XXXXXXXXXX' >> SecureClientId = '71XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXb' >> SecureSecret = 'd-XXXXXXXXXXXXXX==' >> SmbHost = '10.10.11.99' >> SmbShare = '_SW' >> SmbDiskName = 'ipsexp-ps' >> SmbDiskFormat = 'VhdDiskFormat' >> SmbCwId = 'SMB' >> ResourceLocationId = '58XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX2' >> VsphereCwSecretId = 'vsXXXXXXX' >> VsphereHost = '(FQDN)' >> VspherePort = '443' >> VsphereSslNoCheckHostname = $true >> VsphereDataCenter = 'TACG' >> VsphereDataStore = 'XXXXXXXXXXXX' >> VsphereNetwork = 'VM Network' >> VsphereCluster = 'TAXXXXXXXXXX' >> SourceDiskName = "ds:///vmfs/volumes/65245805-87e7af41-5aeb-48210b5c6cd7/TACG-VSP-W11-M/TACG-VSP-W11-M.vmdk" >> } PS C:\TACG> Start-IpsVsphereExportJob @ExportParams -Verbose | Wait-IpsJob Logging to C:\TACG\ExportVsphereToSmb.log Verbose=True Authenticating for Citrix customer XXXXXXXX using API key XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX. VERBOSE: apikey VERBOSE: __AllParameterSets VERBOSE: Get-XDAuthentication: Enter VERBOSE: invoking Get-XDAuthenticationEx: VERBOSE: Get-XDAuthentication: Exit Authenticated for Citrix customer XXXXXXXX. Starting export workflow ***** Call Method: ExportImageJob overwrite: False ***** geo EU api url https://api.eu.layering.cloud.com/ VERBOSE: POST with -1-byte payload VERBOSE: received 326-byte response of content type application/json; charset=utf-8 Image Export started with id 7a4XXXXXXX-XXXX-XXXX-XXXXXXXXXX Logging to C:\TACG\ExportVsphereToSmb.log Verbose=False Interactively authenticating for Citrix customer XXXXXXXX. Authenticated for Citrix customer XXXXXXXXX. geo EU api url https://api.eu.layering.cloud.com/ Job 7a4XXXXXXX-XXXX-XXXX-XXXXXXXXXX status: inProgress percent done: 0 parameters @{name=currentStep; value=GenerateSecurityData} Geo EU api url https://api.eu.layering.cloud.com/ Job 7a4XXXXXXX-XXXX-XXXX-XXXXXXXXXX status: inProgress percent done: 27 parameters @{name=currentStep; value=CreateCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 7a4XXXXXXX-XXXX-XXXX-XXXXXXXXXX status: inProgress percent done: 67 parameters @{name=currentStep; value=Wait ForExportImage} geo EU api url https://api.eu.layering.cloud.com/ Job7a4XXXXXXX-XXXX-XXXX-XXXXXXXXXX status: inProgress percent done: 67 parameters @{name=currentStep; value=Wait ForExportImage} geo EU api url https://api.eu.layering.cloud.com/ Job 7a4XXXXXXX-XXXX-XXXX-XXXXXXXXXX status: inProgress percent done: 73 parameters @{name=currentStep; value=GetCeLogs} geo EU api url https://api.eu.layering.cloud.com/ Job 7a4XXXXXXX-XXXX-XXXX-XXXXXXXXXX status: complete percent done: 100 parameters Job 7a4XXXXXXX-XXXX-XXXX-XXXXXXXXXX final status: complete Job profile @{created compositing engine=0:01:12.666709, prepared to create compositing engine=0:00:01.100751, deleted compositing engine=0:00:01.774440, uploaded export assets to compositing engine=00:00:03.0517790}) Artifacts Status LogFileDir LogFileName --------- ------ ---------- ----------- {output, tags, disk, temporary} complete ExportVsphereToSmb.log
If the PowerShell script completed successfully, we see the exported Image on the referenced SMB share.
Part 1: Exporting the Image from on-premises vSphere using the Windows .NET Application
The first step obtains a Bearer token automatically.
Before we can export the Image we need more information from the vSphere platform: The following parameters must be obtained from vSphere:
vCenterHost vCenterPort datacenter datastore cluster network sourceDisk
The following parameters are also required:
- Prefix: name of the export job - ResourceLocationID: ID of the Resource Location of the Image - OutputStorageLocation: - Type (for example SMB) - Credential-ID (for example SMB) - Host-IP (for example 10.10.11.44): must be reachable by the Connector Appliance in the Resource Location of the Image - SharePath (for example SMB): must be reachable by the Connector Appliance in the Resource Location of the Image
Parts of the parameters have to be stored in the adjacent JSON-files of the Windows application:
{ "vCenterHost": "vcenter.the-austrian-citrix-guy.at", "datacenter": "TACG", "datastore": "datastore1 (1)", "cluster": "TACG-CLUSTER", "network": "VM Network", "prefix": "my-cejob", "outputImageFilename": "my-exported-Image", "timeoutInSeconds": 36000, "sourceDiskName": "ds:///vmfs/volumes/65XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXd7/TACG-VSP-W11-M/TACG-VSP-W11-M.vmdk", "smb": { "type": "SMB", "host": "10.10.11.44", "sharePath": "_TRANS" } }
The Resource Location-ID
, the Platform-Credential
, and the SMB Share-Credential
are loaded from Citrix Cloud and can be selected at the adjacent drop-down boxes.
When all parameters are loaded, the export can be started by clicking the Export Image to SMB share
.
After starting the export the application requests a status update at regular intervals as the export process is an asynchronous task. Progress is shown until the export has been completed.
Part 2: Uploading the Image - Prerequisites
The upload stage uploads the Image to the target cloud subscription. This process is a point-to-point transfer using the configuration and credentials supplied.
Note:
IPS provides PowerShell-based support for automating the upload of the disk. Other third-party tools can also support automation for uploading disks, but Citrix cannot directly support them.
In this guide we upload the Image to Microsoft Azure.
The following Azure permissions are necessary for uploading the Image (the Azure Resource group must be set in the Parameters):
Microsoft.Compute/disks/beginGetAccess/action Microsoft.Compute/disks/endGetAccess/action Microsoft.Compute/disks/delete Microsoft.Compute/disks/read Microsoft.Compute/disks/write Microsoft.Compute/virtualMachines/delete Microsoft.Compute/virtualMachines/powerOff/action Microsoft.Compute/virtualMachines/read Microsoft.Compute/virtualMachines/write Microsoft.Network/networkInterfaces/delete Microsoft.Network/networkInterfaces/join/action Microsoft.Network/networkInterfaces/read Microsoft.Network/networkInterfaces/write Microsoft.Network/networkSecurityGroups/delete Microsoft.Network/networkSecurityGroups/join/action Microsoft.Network/networkSecurityGroups/read Microsoft.Network/networkSecurityGroups/write Microsoft.Resources/deployments/operationStatuses/read Microsoft.Resources/deployments/read Microsoft.Resources/deployments/write Microsoft.Resources/subscriptions/resourcegroups/read
For current permission requirements look at the product documentation https://docs.citrix.com/en-us/citrix-daas/migrate-workloads.html#microsoft-azure-required-permissions.
Part 2: Uploading the Image from an on-premises SMB share to Azure using PowerShell
Before we can upload the Image, at least the following parameters must be obtained:
Filename Filename of the disk to be uploaded ManagedDiskName Filename of the disk in Azure SubscriptionID Subscription-ID of the Azure tenant used Location Azure location ResourceGroup Azure Resource group Timeout Timeout before the script fails
When all values are set we can call the PowerShell script to export the Image. Be aware that the upload takes quite a while - in this example more than 16 hours…
PS C:\TACG> $Params = @{ >> Filename = 'C:\_SW\w11exp.vhd' >> ManagedDiskName = 'IPS_W11EXP.vhd' >> SubscriptionID = '58daXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' >> Location = 'eastus' >> ResourceGroup = 'CTX-HibTest2' >> Timeout = '86400' >> } PS C:\TACG> Copy-DiskToAzure @Params -Verbose Uploading Uploading VHD [ ] 15:06:19 689995776/85899345920 2023-11-13 11:53:30Z: VHD size for C:\_SW\w11exp.vhd is 85899346432 2023-11-13 11:53:30Z: Creating managed disk 'IPS_W11EXP.vhd' with size 85899346432 bytes in resource group CTX-HibTest2 location eastus 2023-11-13 11:54:05Z: Copying disk 'C:\_SW\w11exp.vhd' to managed disk 'IPS_W11EXP.vhd' (threads=default) 2023-11-13 11:54:05Z: Detecting source type 2023-11-13 11:54:05Z: Analyzing VHD 2023-11-13 11:54:06Z: Uploading VHD 2023-11-14 04:27:56Z: Copied disk to Azure managed disk 'IPS_W11EXP.vhd'
Part 2: Uploading the Image from an on-premises SMB share to Azure using REST-API
Note:
As mentioned, uploading the Image from an on-premises SMB to Azure is not possible using REST-API calls. You have to invoke PowerShell.
Part 2: Uploading the Image from an on-premises SMB share to Azure using REST-API
The Windows .NET application encapsulates REST-API calls and invokes PowerShell scripts. The VHD file must reside on the computer where the .NET application is run.
Before we can upload the Image, at least the following parameters must be obtained:
Filename Filename of the disk to be uploaded ManagedDiskName Filename of the disk in Azure SubscriptionID Subscription-ID of the Azure tenant used Location Azure location ResourceGroup Azure Resource group Timeout Timeout before the script fails
These parameters are set in a JSON-file or directly in the application.
When all values are set, the upload can be started by pressing the “Upload VHD...” button.
The upload is a long-lasting process depending on the size of the Image and the upload speed - in this example, it took over 16 hours to upload. Set the timeout parameter accordingly!
Note:
During the upload, the application does not refresh its state - it happens only after completion or occurrence of an error!
After a successful upload, the next step of the Image Migration workflow can be started.
Part 3: Preparing the Image - Prerequisites
The Prepare stage is a complex set of steps removing the source platform components and injecting the target platform components into the Image. An important prerequisite is a correct App registration in Azure and its IAM settings. The needed permissions can be found in the IPS documentation. Create the credential in the Citrix Credential Wallet using the PowerShell script or REST-API.
Example for creating Azure credentials in the Credential Wallet using a REST-API call:
Correct format:
{ "id": "azadminjson", "name": "My Azure credential SPN", "type": "Azure", "tenantId": "e85XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "clientId": "fd7XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "clientSecret": "8cD8QXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_" } The XXXXX-marked values are the corresponding values of the App registration in Azure.
Full REST-API-Call to create the Azure credential:
POST https://api.eu.layering.cloud.com/credentials Authorization: CWSAuth bearer= {{Bearer-Token-Value}} Citrix-CustomerId: {{Citrix-CustomerID}} Accept: application/json Content-Type: application/json Body (Raw): { "id": "azadminjson", "name": "My Azure credential SPN", "type": "Azure", "tenantId": "e85aXXXX-XXXX-XXXX-XXXXXXXXXXXX”, "clientId": "fd72XXXX-XXXX-XXXX-XXXXXXXXXXXX", "clientSecret": "8cDXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_" }
Response if credential was successfully created:
{ "id": "azadminjson, "name": "My Azure credential SPN", "type": "Azure", "state": "Ok", "createdAt": "2023-11-15T18:01:49.8436445Z", "updatedAt": "2023-11-15T18:01:49.8436445Z" }
Part 3: Preparing the Image on Azure using PowerShell
Before we can call the PowerShell script to prepare the Image, at least the following parameters must be obtained:
CustomerID CloudCwSecretId Azure credentials stored in the Credential Wallet ResourceLocationId ID of the Citrix Cloud Resource location AzureSubscriptionID Azure region where the prepared disk is placed TargetResourceGroup Resource group where the prepared disk is placed AzureVirtualNetworkResourceGroupName Resource group for placing the Compositing engine AzureVirtualNetworkName Network for placing the Compositing engine AzureVirtualNetworkSubnetName Subnet for placing the Compositing engine CloudProvisioningType Deployment with MCS or PVS DomainUnjoin Remove Image from Domain InstallMisa Install the Machine Identity Service Agent from VDA ForceMisa Install the latest Machine Identity Service Agent CloudDiskName Name of the disk to be prepared AzureLocation Azure region to deploy to XdReconfigure: ParameterName "controllers" "ParameterValue FQDN of the Cloud Controllers
When all values are set we can call the PowerShell script to prepare the Image:
PS C:\TACG> $PrepareParams = @{ >> CustomerId = 'uzyo2lp7eh7j' >> CloudProvisioningType = 'Mcs' >> CloudCwSecretId = 'azadXXXXXXXXX' >> DomainUnjoin = $true >> InstallMisa = $false >> ForceMisa = $false >> CloudDiskName = 'diskfromipsss' >> XdReconfigure = @( >> [pscustomobject]@{ >> ParameterName = 'controllers' >> ParameterValue = 'TACG-XXXXXX.hib.the-austrian-citrix-guy.at' >> } >> ) >> ResourceLocationId = '7691XXXX-XXXX-XXXX-XXXXXXXXXXXX' >> AzureSubscriptionID = '58daXXXX-XXXX-XXXX-XXXXXXXXXXXX' >> AzureLocation = 'eastus' >> TargetResourceGroup = 'CTX-XXXXXXXXXX >> AzureVirtualNetworkResourceGroupName = 'CTX-XXXXXXXXXX' >> AzureVirtualNetworkName = 'TACG-XXXXXXXXXX-vnet' >> AzureVirtualNetworkSubnetName = 'default' >> } PS C:\TACG> Start-IpsAzurePrepareJob @PrepareParams -Verbose | Wait-IpsJob VERBOSE: Initialize default drives. VERBOSE: Creating a new drive. VERBOSE: Creating a new drive. Logging to C:\TACG\PrepareAzure.log Verbose=True Interactively authenticating for Citrix customer uzyo2lp7eh7j. VERBOSE: __AllParameterSets VERBOSE: Get-XDAuthentication: Enter VERBOSE: invoking Get-XDAuthenticationEx: VERBOSE: Get-XDAuthentication: Exit Authenticated for Citrix customer XXXXXXXXXX. Starting prepare workflow ***** Call Method: PrepareImageJob ***** geo EU api url https://api.eu.layering.cloud.com/ VERBOSE: Cmdlet "Find-Package" is exported. VERBOSE: Cmdlet "Get-Package" is exported. VERBOSE: Cmdlet "Get-PackageProvider" is exported. VERBOSE: Cmdlet "Get-PackageSource" is exported. VERBOSE: Cmdlet "Install-Package" is exported. VERBOSE: Cmdlet "Import-PackageProvider" is exported. VERBOSE: Cmdlet "Find-PackageProvider" is exported. VERBOSE: Cmdlet "Install-PackageProvider" is exported. VERBOSE: Cmdlet "Register-PackageSource" is exported. VERBOSE: Cmdlet "Save-Package" is exported. VERBOSE: Cmdlet "Set-PackageSource" is exported. VERBOSE: Cmdlet "Uninstall-Package" is exported. VERBOSE: Cmdlet "Unregister-PackageSource" is exported. VERBOSE: Cmdlet "Find-Package" is imported. VERBOSE: Cmdlet "Find-PackageProvider" is imported. VERBOSE: Cmdlet "Get-Package" is imported. VERBOSE: Cmdlet "Get-PackageProvider" is imported. VERBOSE: Cmdlet "Get-PackageSource" is imported. VERBOSE: Cmdlet "Import-PackageProvider" is imported. VERBOSE: Cmdlet "Install-Package" is imported. VERBOSE: Cmdlet "Install-PackageProvider" is imported. VERBOSE: Cmdlet "Register-PackageSource" is imported. VERBOSE: Cmdlet "Save-Package" is imported. VERBOSE: Cmdlet "Set-PackageSource" is imported. VERBOSE: Cmdlet "Uninstall-Package" is imported. VERBOSE: Cmdlet "Unregister-PackageSource" is imported. VERBOSE: POST with -1-byte payload VERBOSE: received 327-byte response of content type application/json; charset=utf-8 Image Prepare started with id 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX Logging to C:\TACG\PrepareAzure.log Verbose=False Interactively authenticating for Citrix customer XXXXXXXXXX. Authenticated for Citrix customer XXXXXXXXXX. geo EU api url https://api.eu.layering.cloud.com/ Job 33743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 21 parameters @{name=currentStep; value=CreateCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 21 parameters @{name=currentStep; value=CreateCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 21 parameters @{name=currentStep; value=CreateCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 42 parameters @{name=currentStep; value=UploadAssetsToCe} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 42 parameters @{name=currentStep; value=UploadAssetsToCe} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 53 parameters @{name=currentStep; value=WaitForPrepareImage} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 58 parameters @{name=currentStep; value=GetCeLogs} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 68 parameters @{name=currentStep; value=WaitForTargetBoot} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 68 parameters @{name=currentStep; value=WaitForTargetBoot} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 68 parameters @{name=currentStep; value=WaitForTargetBoot} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 89 parameters @{name=currentStep; value=DeleteCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 89 parameters @{name=currentStep; value=DeleteCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 89 parameters @{name=currentStep; value=DeleteCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: complete percent done: 100 parameters Job 3743XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX final status: complete Job profile @{deleted compositing engine=0:03:17.912184, reconfigure VDA=00:02:45.0545592, created compositing engine=0:02:56.629924, installed windowsAzureVmAgent=00:00:56.9625675, prepared to create compositing engine=0:00:08.829692, uploaded prepare assets to compositing engine=00:00:58.2018150}) Artifacts Status LogFileDir LogFileName --------- ------ ---------- ----------- {output, tags, temporary} complete PrepareAzure.log
The preparation of the Image has been completed successfully.
Part 3: Preparing the Image on Azure using REST-API
An important prerequisite is a correct App registration in Azure and its IAM settings and a correct Azure-based credential. The needed permissions can be found in the IPS documentation.
Create the credential in the Citrix Credential Wallet using the PowerShell script or REST-API.
Before we can call the REST-API to prepare the Image, at least the following parameters must be obtained:
platform Azure platformCredentialId Azure credentials stored in the Credential Wallet resourceLocationId ID of the Citrix Cloud Resource location SubscriptionID Azure region where the prepared disk is placed targetDiskResourceGroupName Resource group where the prepared disk is placed virtualNetworkResourceGroupName Resource group for placing the Compositing engine virtualNetworkName Network for placing the Compositing engine virtualNetworkSubnetName Subnet for placing the Compositing engine provisioningType Deployment with MCS or PVS domainUnjoin Remove Image from Domain installMisa Install the Machine Identity Service Agent from VDA forceMisa Install the latest Machine Identity Service Agent installUpl Install User Personal Layer defrag Run DEFRAG chkdsk Run CHKDSK targetDiskName Name of the disk to be prepared outputDiskName Name of the prepared disk XdReconfigure: ParameterName "controllers" ParameterValue FQDN of the Cloud Controllers
JSON-Call to prepare the Image:
POST https://api.eu.layering.cloud.com/Images/$prepare?async=true ` Authorization: CWSAuth bearer= {{Bearer-Token-Value}} Citrix-CustomerId: {{Citrix-CustomerID}} Accept: application/json Content-Type: application/json Body (Raw): { "platform": "Azure", "platformCredentialId": "azadmXXXXXXX", "resourceLocationId": "7691XXXX-XXXX-XXXX-XXXX-XXXXXXXXXX", "SubscriptionID": "58daXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX", "azureRegion": "eastus", "targetDiskResourceGroupName": "CTX-XXXXXXXXX", "virtualNetworkResourceGroupName": "CTX-XXXXXXXXX", "virtualNetworkName": "TACG-XXXXXXXXXXXXX", "virtualNetworkSubnetName": "default", "provisioningType": "Mcs", "domainUnjoin": true, "installMisa": false, "forceMisa": false, "installUpl": false, "defrag": false, "chkdsk": false, "ceVmSku": "Standard_D2s_v5", "targetDiskName": "diskromXXXXXXXX", "outputDiskName": "procdiskXXXXXXXXX", "XdReconfigure": [{ "ParameterName":"controllers", "ParameterValue":"TACGXXXXXXXX.hib.the-austrian-citrix-guy.at" } ] }
Response:
{ "id": "6267XXXX-XXXX-XXXX-XXXX-XXXXXXXXXX", "type": "prepareImage", "overallProgressPercent": 0, "isCancellable": true, "parameters": [], "status": "notStarted", "resultLocation": null, "additionalInfo": null, "warnings": [], "error": [], "createdAt": "2023-11-15T19:07:09Z", "updatedAt": "2023-11-15T19:07:09Z", "startedAt": "2023-11-15T19:07:09Z" }
As the job runs asynchronously, we need another REST-API-call to get the status of the export job using the id
parameter from the response:
Example: 42% progress:
Example: 100% progress, preparation successful:
JSON-Call to get the status of the preparation job:
GET https://api.eu.layering.cloud.com/jobs/6267XXXX-XXXX-XXXX-XXXX-XXXXXXXXX Authorization: CWSAuth bearer= {{Bearer-Token-Value}} Citrix-CustomerId: {{Citrix-CustomerID}} Accept: application/json Content-Type: application/json Body (Raw): { }
Response - this example shows 100% progress and successful preparation
{ "id": "6267XXXX-XXXX-XXXX-XXXX-XXXXXXXXX ", "type": "prepareImage", "overallProgressPercent": 100, "isCancellable": true, "parameters": [], "status": "complete", "resultLocation": null, "additionalInfo": { "artifacts": { "tags": { "ctx-job-id": "6267XXXX-XXXX-XXXX-XXXX-XXXXXXXXXX", "component": "compositing" }, "output": [ { "description": "Managed disk containing the prepared Image", "name": "procdiskfromipsss", "resourceGroup": "CTX-XXXXXXX" } ], "temporary": [ { "description": "Temporary resource group", "name": "ctx-ce-50d0e27d" }, { "description": "Temporary Compositing Engine OS disk", "name": "ce-50d0e27d-os-disk", "resourceGroup": "ctx-ce-50d0e27d" }, { "description": "Temporary Compositing Engine NIC", "name": "ce-50d0e27d-nic", "resourceGroup": "ctx-ce-50d0e27d" }, { "description": "Temporary Compositing Engine network security group", "name": "ce-50d0e27d-nic-nsg", "resourceGroup": "ctx-ce-50d0e27d" }, { "description": "Temporary Compositing Engine VM", "name": "ce-50d0e27d", "resourceGroup": "ctx-ce-50d0e27d", "encryptionAtHost": false } ] }, "profile": { "prepared to create compositing engine": "0:00:09.071793", "created compositing engine": "0:02:58.483518", "uploaded prepare assets to compositing engine": "00:00:54.2715950", "installed windowsAzureVmAgent": "00:01:02.6609307", "reconfigure VDA": "00:03:54.8636235", "deleted compositing engine": "0:03:18.144080" }, "warnings": [], "errors": [], "telemetry": { "diskSize": "85899345920", "freeSpace": "1048576", "windowsVersion": "@{CurrentVersion=@{value = 6.3, type = REG_SZ}; UBR=@{value = 0x7c8, type = REG_DWORD}; BuildLab=@{value = 22621.ni_release.220506-1250, type = REG_SZ}; RegisteredOwner=@{value = admin, type = REG_SZ}; CurrentBuild=@{value = 22621, type = REG_SZ}; SystemRoot=@{value = C:\\Windows, type = REG_SZ}; DigitalProductId4=@{value = F804000004000000300033003600310032002D00300033003300310031002D003000300030002D003000300030003000300031002D00300033002D0031003000330031002D00320032003600320031002E0030003000300030002D003200380033003200300....000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, type = REG_BINARY}; ProductId=@{value = 00331-10000-00001-AA757, type = REG_SZ}; DigitalProductId=@{value = A40000000300000030303333312D31303030302D30303030312D414137353700EF0C00005B54485D5831392D3938373935000000EF0C10000000343DC5394EBD6E2F090000000000D51D25653D149BCE030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050F96851, type = REG_BINARY}; BuildBranch=@{value = ni_release, type = REG_SZ}; SoftwareType=@{value = System, type = REG_SZ}; CurrentType=@{value = Multiprocessor Free, type = REG_SZ}; DisplayVersion=@{value = 22H2, type = REG_SZ}; InstallDate=@{value = 0x65251dd7, type = REG_DWORD}; InstallTime=@{value = 0x1d9fb5edf5aef50, type = REG_QWORD}; InstallationType=@{value = Client, type = REG_SZ}; BuildLabEx=@{value = 22621.1.amd64fre.ni_release.220506-1250, type = REG_SZ}; ProductName=@{value = Windows 10 Pro, type = REG_SZ}; CompositionEditionID=@{value = Enterprise, type = REG_SZ}; CurrentBuildNumber=@{value = 22621, type = REG_SZ}; ReleaseId=@{value = 2009, type = REG_SZ}; PathName=@{value = C:\\Windows, type = REG_SZ}; EditionID=@{value = Professional, type = REG_SZ}; BuildGUID=@{value = ffffffff-ffff-ffff-ffff-ffffffffffff, type = REG_SZ}; CurrentMajorVersionNumber=@{value = 0xa, type = REG_DWORD}; BaseBuildRevisionNumber=@{value = 0x1, type = REG_DWORD}; PendingInstall=@{value = 0x0, type = REG_DWORD}; CurrentMinorVersionNumber=@{value = 0x0, type = REG_DWORD}}", "vdaVersions": "@{AppProtectionVC=@{value = 23.8.0.2, type = REG_SZ}; Citrix HDX Audio x64=@{value = 7.39.0.33, type = REG_SZ}; WmiSdk=@{value = 7.39.0.4, type = REG_SZ}; Citrix HDX Graphics x64=@{value = 7.39.0.36, type = REG_SZ}; WMI-Maschinenverwaltungsprovider=@{value = 7.39.0.4, type = REG_SZ}; (Default)=@{value = 7.39.0.0, type = REG_SZ}; Citrix Start Menu Disconnect Button=@{value = 7.39.0.41, type = REG_SZ}; Citrix Identity Assertion VDA Plugin=@{value = 10.15.0.4, type = REG_SZ}; Citrix Director VDA Plugin=@{value = 7.39.0.4, type = REG_SZ}; Citrix Überwachungsdienst-VDA-Plug-In=@{value = 7.39.0.10, type = REG_SZ}; Citrix WMI Proxy Plugin=@{value = 7.39.0.4, type = REG_SZ}; Citrix Browser Content Redirection=@{value = 15.45.0.12, type = REG_SZ}; Citrix Gruppenrichtlinie - clientseitige Erweiterung 7.39.0.13=@{value = 7.39.0.13, type = REG_SZ}; Citrix HDX Devices x64=@{value = 7.39.0.31, type = REG_SZ}; Machine Identity Service Agent=@{value = 7.39.0.4, type = REG_SZ}; Citrix HDX App Experience x64=@{value = 7.39.0.41, type = REG_SZ}; Citrix CDF Capture Service - x64=@{value = 7.39.0.4, type = REG_SZ}; Citrix HDX Printing x64=@{value = 7.39.0.26, type = REG_SZ}; UpmVDAPlugin=@{value = 23.8.0.7, type = REG_SZ}; Citrix HDX WS x64=@{value = 15.45.0.12, type = REG_SZ}; Citrix HDX IcaManagement x64=@{value = 7.39.0.47, type = REG_SZ}; Citrix Diagnostics Facility=@{value = 7.2.1.6, type = REG_SZ}; Citrix Telemetry Service - x64=@{value = 3.24.0.1, type = REG_SZ}; Citrix Universeller Druckclient=@{value = 7.39.0.26, type = REG_SZ}; Citrix Virtual Desktop Agent - x64=@{value = 7.39.0.11, type = REG_SZ}}", "partitionStyle": "GPT", "bootMode": "UEFI" } }, "warnings": [], "error": [], "createdAt": "2023-11-15T19:07:09Z", "updatedAt": "2023-11-15T19:37:29Z", "startedAt": "2023-11-15T19:07:09Z", "endedAt": "2023-11-15T19:37:29Z" }
The preparation of the Image has been completed successfully.
Part 3: Preparing the Image on Azure using the Windows .NET Application
The Windows .NET application encapsulates REST-API calls to prepare the Image on Azure. An important prerequisite is a correct App registration in Azure and its IAM settings and a correct Azure-based credential.
Before the application can prepare the Image, the following parameters must be obtained:
platform Azure platformCredentialId Azure credentials stored in the Credential Wallet resourceLocationId ID of the Citrix Cloud Resource location SubscriptionID Azure region where the prepared disk is placed targetDiskResourceGroupName Resource group where the prepared disk is placed virtualNetworkResourceGroupName Resource group for placing the Compositing engine virtualNetworkName Network for placing the Compositing engine virtualNetworkSubnetName Subnet for placing the Compositing engine provisioningType Deployment with MCS or PVS domainUnjoin Remove Image from Domain installMisa Install the Machine Identity Service Agent from VDA forceMisa Install the latest Machine Identity Service Agent installUpl Install User Personal Layer defrag Run DEFRAG chkdsk Run CHKDSK targetDiskName Name of the disk to be prepared outputDiskName Name of the prepared disk XdReconfigure: ParameterName "controllers" ParameterValue FQDN of the Cloud Controllers
These parameters are set in a JSON-file.
Note:
The parameter
overrideUploadedDiskName
determines the preparation of the correct disk:true
uses the disk name set in the adjacent JSON file,false
uses the disk name uploaded before.
When all parameters are set, the preparation of the uploaded Image can be started by pressing the “Prepare VHD...” button. The preparation process lasted in this example for about 35 minutes…
The application refreshes every 30 seconds the job status by calling a REST-API to determine the job status.
The preparation of the Image has been completed successfully.
Part 4: Publishing the Image - Prerequisites
In the Publish phase, the prepared Image is published and ready to be streamed with Citrix Provisioning Server (PVS). The Image Portability Service provides a REST interface and PowerShell scripts to publish the migrated disk directly into the PVS vDisk store.
Note:
If you are using Machine Creation Services (MCS) to provision your machine catalog, you do not need to run the “Publish” stage. You can use the prepared Image from Part 3 to create the Machine Catalog.
An example of creating a Machine Catalog using PowerShell can be found on the Tech Zone article https://docs.citrix.com/en-us/tech-zone/build/deployment-guides/citrix-azure-hibernation-posh#creating-a-hibernation-capable-machine-catalog-using-powershell.
An example of creating a Machine Catalog using REST-API calls can be found on the Tech Zone article https://docs.citrix.com/en-us/tech-zone/build/deployment-guides/citrix-azure-hibernation-api#creating-a-hibernation-capable-machine-catalog-using-rest-api.
You can use these guides as a reference for further automation of creating machines using MCS.
Part 4: Publishing the Image on Azure to an SMB share for PVS using PowerShell
An important prerequisite is a correct App registration in Azure and its IAM settings.
Before we can call the PowerShell script to publish the Image, at least the following parameters must be obtained:
CustomerID Citrix Customer-ID SecureClientID ID of the API client SecureSecret Secret of the API client SmbHost Hostname of SMB server where the exported disk will be stored SmbShare SMB server share name SmbPath Share path to the disk SmbDiskName Name of disk with no extension SmbDiskFormat Type of disk SmbCwId SMB Credential Wallet ID ResourceLocationID ID of the Resource location AzureSubscriptionID ID of the Azure subscription CloudCWSecretID ID of the Cloud credential in the Credential Wallet AzureLocation Azure location TargetResourceGroup Resource group for placing the Compositing engine AzureVirtualNetworkResourceGroupName Resource group of the Network for placing the Compositing engine AzureVirtualNetworkName Network for placing the Compositing engine AzureVirtualNetworkSubnetName Subnet for placing the Compositing engine CloudDiskName Name of the disk to be transferred to the SMB share AzureVmResourceGroup Resource group where the VM will be placed Timeout Timespan before the process times out LogFileName Name of the log file
When all values are set we can call the PowerShell script to publish the Image.
PS C:\TACG> $PublishParams = @{ >> CustomerId = "uzXXXXXXXXXXXX" >> SecureClientId = "26ecXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" >> SecureSecret = "vhOXXXXXXXXXXXXXXXXXXXXXXXXXXXX" >> SmbHost = "10.0.0.4" >> SmbShare = "_PVS" >> SmbDiskName = "ReadyForPVSPS" >> SmbDiskFormat = "VhdDiskFormat" >> SmbCwId = "azhibsmb" >> ResourceLocationId = "7691XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" >> AzureSubscriptionId = "58daXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" >> CloudCwSecretId = "azadminjson" >> AzureLocation = "eastus" >> TargetResourceGroup = "CTX-Hibtest2" >> AzureVirtualNetworkResourceGroupName = "CTX-Hibtest2" >> AzureVirtualNetworkName = "TACG-HibTest-DCCC-vnet" >> AzureVirtualNetworkSubnetName = "default" >> CloudDiskName = "procdiskfromipsss" >> AzureVmResourceGroup = "CTX-Hibtest2" >> Timeout = "36000" >> LogFileName = "AzurePublish.log" >> } PS C:\TACG> Start-IpsAzurePublishJob @PublishParams -Verbose | Wait-IpsJob VERBOSE: Initialize default drives. VERBOSE: Creating a new drive. VERBOSE: Creating a new drive. Logging to C:\TACG\AzurePublish.log Verbose=True Authenticating for Citrix customer uzXXXXXXXXXXXXXX using API key 26ecXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. VERBOSE: apikey VERBOSE: __AllParameterSets VERBOSE: Get-XDAuthentication: Enter VERBOSE: invoking Get-XDAuthenticationEx: VERBOSE: Get-XDAuthentication: Exit Authenticated for Citrix customer uzyXXXXXXXXXX. Starting export workflow ***** Call Method: ExportImageJob overwrite: False ***** geo EU api url https://api.eu.layering.cloud.com/ VERBOSE: Cmdlet "Find-Package" is exported. VERBOSE: Cmdlet "Get-Package" is exported. VERBOSE: Cmdlet "Get-PackageProvider" is exported. VERBOSE: Cmdlet "Get-PackageSource" is exported. VERBOSE: Cmdlet "Install-Package" is exported. VERBOSE: Cmdlet "Import-PackageProvider" is exported. VERBOSE: Cmdlet "Find-PackageProvider" is exported. VERBOSE: Cmdlet "Install-PackageProvider" is exported. VERBOSE: Cmdlet "Register-PackageSource" is exported. VERBOSE: Cmdlet "Save-Package" is exported. VERBOSE: Cmdlet "Set-PackageSource" is exported. VERBOSE: Cmdlet "Uninstall-Package" is exported. VERBOSE: Cmdlet "Unregister-PackageSource" is exported. VERBOSE: Cmdlet "Find-Package" is imported. VERBOSE: Cmdlet "Find-PackageProvider" is imported. VERBOSE: Cmdlet "Get-Package" is imported. VERBOSE: Cmdlet "Get-PackageProvider" is imported. VERBOSE: Cmdlet "Get-PackageSource" is imported. VERBOSE: Cmdlet "Import-PackageProvider" is imported. VERBOSE: Cmdlet "Install-Package" is imported. VERBOSE: Cmdlet "Install-PackageProvider" is imported. VERBOSE: Cmdlet "Register-PackageSource" is imported. VERBOSE: Cmdlet "Save-Package" is imported. VERBOSE: Cmdlet "Set-PackageSource" is imported. VERBOSE: Cmdlet "Uninstall-Package" is imported. VERBOSE: Cmdlet "Unregister-PackageSource" is imported. VERBOSE: POST with -1-byte payload VERBOSE: received 326-byte response of content type application/json; charset=utf-8 Image Export started with id 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX Logging to C:\TACG\AzurePublish.log Verbose=False Interactively authenticating for Citrix customer uzyXXXXXXXXXX. Authenticated for Citrix customer uzyXXXXXXXXXX. geo EU api url https://api.eu.layering.cloud.com/ Job 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 20 parameters @{name=currentStep; value=PrepareForCreateCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 20 parameters @{name=currentStep; value=PrepareForCreateCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 27 parameters @{name=currentStep; value=CreateCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 27 parameters @{name=currentStep; value=CreateCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 27 parameters @{name=currentStep; value=CreateCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 67 parameters @{name=currentStep; value=WaitForExportImage} geo EU api url https://api.eu.layering.cloud.com/ Job 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 67 parameters @{name=currentStep; value=WaitForExportImage} geo EU api url https://api.eu.layering.cloud.com/ Job 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 67 parameters @{name=currentStep; value=WaitForExportImage} geo EU api url https://api.eu.layering.cloud.com/ Job 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 87 parameters @{name=currentStep; value=DeleteCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: inProgress percent done: 87 parameters @{name=currentStep; value=DeleteCeVm} geo EU api url https://api.eu.layering.cloud.com/ Job 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX status: complete percent done: 100 parameters Job 467XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXfinal status: complete Job profile @{created compositing engine=0:03:01.698686, prepared to create compositing engine=0:00:07.768624, deleted compositing engine=0:01:35.971565, uploaded export assets to compositing eng ine=00:00:03.2609860}) Artifacts Status LogFileDir LogFileName --------- ------ ---------- ----------- {output, tags, disk, temporary} complete AzurePublish.log PS C:\TACG>
The publishing of the Image has been completed successfully. All steps of the IPS workflow have completed successfully.
The next step is deploying the Image using Citrix Provisioning Services (PVS). More information about Citrix Provisioning Services (PVS) and how to deploy Images/machines using PVS can be found here: https://docs.citrix.com/en-us/tech-zone/build/deployment-guides/citrix-azure-hibernation-api#creating-a-hibernation-capable-machine-catalog-using-rest-api and https://docs.citrix.com/en-us/tech-zone/learn/tech-briefs/citrix-provisioning.
Part 4: Publishing the Image on Azure to an SMB share for PVS using REST-API
An important prerequisite is a correct App registration in Azure and its IAM settings.
Before we can call the PowerShell script to publish the Image, at least the following parameters must be obtained:
platform Azure platformCredentialId Azure credentials stored in the Credential Wallet resourceLocationId ID of the Citrix Cloud Resource location SubscriptionID Azure region where the prepared disk is placed targetDiskResourceGroupName Resource group where the prepared disk is placed virtualNetworkResourceGroupName Resource group for placing the Compositing engine virtualNetworkName Network for placing the Compositing engine virtualNetworkSubnetName Subnet for placing the Compositing engine targetDiskName Name of the disk to be prepared outputImageFilename Name of the published disk resourceGroup Resource group where the process will run timeoutInSeconds Timespan before the process will time out outputDiskName: { Name of the prepared disk type "SMB” as we publish to a share credentialID Credential Wallet-ID of the SMB credential host FQDN/IP of the server hosting the SMB share sharePath Name of the share }
When all values are set we can call the REST-API script to publish the Image.
JSON-Call to start the publishing job:
POST https://api.eu.layering.cloud.com/Images/$publish?async=true` Authorization: CWSAuth bearer= {{Bearer-Token-Value}} Citrix-CustomerId: {{Citrix-CustomerID}} Accept: application/json Content-Type: application/json Body (Raw): { "platform": "Azure", "platformCredentialId": "azadminjson", "subscriptionId": "58dXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "resourceGroup": "CTX-Hibtest2", "virtualNetworkResourceGroupName": "CTX-Hibtest2", "virtualNetworkName": "TACG-HibTest-DCCC-vnet", "virtualNetworkSubnetName": "default", "timeoutInSeconds":86400, "targetDiskResourceGroupName": "CTX-Hibtest2", "targetDiskName": "procdiskfromipsss", "resourceLocationId": "769XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "outputStorageLocation": { "type": "SMB", "credentialId": "azhibsmb", "host": "10.0.0.4", "sharePath": "_PVS" }, "outputImageFilename": "ReadyForPVS" }
Response:
{ "id": "d44XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "type": "exportImage", "overallProgressPercent": 0, "isCancellable": true, "parameters": [], "status": "notStarted", "resultLocation": null, "additionalInfo": null, "warnings": [], "error": [], "createdAt": "2023-11-23T13:31:48Z", "updatedAt": "2023-11-23T13:31:48Z", "startedAt": "2023-11-23T13:31:48Z" }
As the job runs asynchronously, we need another REST-API-call to get the status of the export job using the id
parameter from the response:
Example: 100% progress, publishing successful:
JSON-Call to get the status of the publishing job:
GET https://api.eu.layering.cloud.com/jobs/6267XXXX-XXXX-XXXX-XXXX-XXXXXXXXX Authorization: CWSAuth bearer= {{Bearer-Token-Value}} Citrix-CustomerId: {{Citrix-CustomerID}} Accept: application/json Content-Type: application/json Body (Raw): { }
Response - this example shows 100% progress and successful publishing:
{ "id": "d443XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "type": "exportImage", "overallProgressPercent": 100, "isCancellable": true, "parameters": [], "status": "complete", "resultLocation": null, "additionalInfo": { "artifacts": { "tags": { "ctx-job-id": "d443XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "component": "compositing" }, "output": [ { "description": "Published disk", "path": "//10.0.0.4/_PVS/ReadyForPVS.vhd" } ], "temporary": [ { "description": "Temporary Compositing Engine OS disk", "name": "ce-f32672a0-os-disk", "resourceGroup": "CTX-Hibtest2" }, { "description": "Temporary Compositing Engine NIC", "name": "ce-f32672a0-nic", "resourceGroup": "CTX-Hibtest2" }, { "description": "Temporary Compositing Engine network security group", "name": "ce-f32672a0-nic-nsg", "resourceGroup": "CTX-Hibtest2" }, { "description": "Temporary Compositing Engine VM", "name": "ce-f32672a0", "resourceGroup": "CTX-Hibtest2", "encryptionAtHost": false }, { "description": "Temporary copy of the target disk", "name": "procdiskfromipsss-f5276f68", "resourceGroup": "CTX-Hibtest2" } ], "disk": { "diskPath": "\\\\10.0.0.4\\_PVS\\ReadyForPVS.vhd", "md5Hash": "f96519ee184ba47ba33f03d09384188b" } }, "profile": { "prepared to create compositing engine": "0:00:07.449354", "created compositing engine": "0:03:01.894559", "uploaded export assets to compositing engine": "00:00:04.7360280", "deleted compositing engine": "0:01:40.349551" }, "warnings": [], "errors": [] }, "warnings": [], "error": [], "createdAt": "2023-11-23T13:31:48Z", "updatedAt": "2023-11-23T14:39:54Z", "startedAt": "2023-11-23T13:31:48Z", "endedAt": "2023-11-23T14:39:54Z" }
The publishing of the Image has been completed successfully. All steps of the IPS workflow have completed successfully. The next step is deploying the Image using Citrix Provisioning Services (PVS). More information about Citrix Provisioning Services (PVS) and how to deploy Images/machines using PVS can be found here: https://docs.citrix.com/en-us/tech-zone/build/deployment-guides/citrix-azure-hibernation-api#creating-a-hibernation-capable-machine-catalog-using-rest-api and https://docs.citrix.com/en-us/tech-zone/learn/tech-briefs/citrix-provisioning.
Part 4: Publishing the Image on Azure to an SMB share for PVS using the .NET application
The Windows .NET application encapsulates REST-API calls to prepare the Image on Azure.
An important prerequisite is a correct App registration in Azure and its IAM settings.
Before we can use the application to publish the Image, at least the following parameters must be obtained:
platform Azure platformCredentialId Azure credentials stored in the Credential Wallet resourceLocationId ID of the Citrix Cloud Resource location SubscriptionID Azure region where the prepared disk is placed targetDiskResourceGroupName Resource group where the prepared disk is placed virtualNetworkResourceGroupName Resource group for placing the Compositing engine virtualNetworkName Network for placing the Compositing engine virtualNetworkSubnetName Subnet for placing the Compositing engine targetDiskName Name of the disk to be prepared timeoutInSeconds Timespan before the process will time out SmbHost FQDN/IP of the server hosting the SMB share SmbShare Name of the SMB share SmbDiskName Name of the published disk on the SMB share SmbDiskFormat Disk format
These parameters are set in a JSON-file:
When all parameters are set, the publishing of the uploaded Image can be started by pressing the “Publish VHD...” button. The publishing process lasted in this example for about 65 minutes…
The publishing of the Image has been completed successfully.
The next step is deploying the Image using Citrix Provisioning Services (PVS). More information about Citrix Provisioning Services (PVS) and how to deploy Images/machines using PVS can be found here: https://docs.citrix.com/en-us/tech-zone/build/deployment-guides/citrix-azure-hibernation-api#creating-a-hibernation-capable-machine-catalog-using-rest-api and https://docs.citrix.com/en-us/tech-zone/learn/tech-briefs/citrix-provisioning.
Appendix
Code Snippets
Important Note:
These code snippets show examples how to implement the API calls and PowerShell scripts into an application.
No warranty, no liability, and no support of any kind are given for the application.
Example of Objects
Public Class CCAPI_Token Public Property CustomerID As String Public Property ClientID As String Public Property ClientSecret As String Public Property GrantType As String End Class
Public Class CCAPI_ExportParameters 'Nested object Public Property platform As String Public Property platformCredentialId As String Public Property vCenterHost As String Public Property vCenterPort As Integer Public Property vCenterSslNoCheckHostname As String Public Property datacenter As String Public Property datastore As String Public Property cluster As String Public Property network As String Public Property prefix As String Public Property resourceLocationId As String Public Property outputStorageLocation As CCAPI_OutputStorageLocation Public Property outputImageFilename As String Public Property timeoutInSeconds As Integer Public Property sourceDiskName As String End Class
Public Class CCAPI_OutputStorageLocation Public Property type As String Public Property credentialId As String Public Property host As String Public Property sharePath As String End Class
Example of JSON-based variables
CCAPI-Token.json -> read into Class CCAPI_Token at startup:
{ "CustomerID": "uzXXXXXXXXXXXXX", "ClientID": "5075XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "ClientSecret": "8MXXXXXXXXXXXXXXXXXXXXX==", "GrantType": "client_credentials" }
Obtain a Bearer token from Citrix Cloud
Private Sub FetchBearerToken(ByVal obj_CCAPI_Token As CCAPI_Token) 'Create POST request to Citrix Cloud-API to retrieve BearerToken Dim rest As New Chilkat.Rest ' Connect to the CCAPI server. Dim bTls As Boolean = True Dim port As Integer = 443 Dim bAutoReconnect As Boolean = True Dim success As Boolean = rest.Connect("api-eu.cloud.com", port, bTls, bAutoReconnect) If (success <> True) Then Debug.WriteLine(rest.LastErrorText) Exit Sub End If success = rest.AddQueryParam("grant_type", obj_CCAPI_Token.GrantType) success = rest.AddQueryParam("client_id", obj_CCAPI_Token.ClientID) success = rest.AddQueryParam("client_secret", obj_CCAPI_Token.ClientSecret) Dim APIPostCallPath As String = "/cctrustoauth2/" & obj_CCAPI_Token.CustomerID & "/tokens/clients" Dim s_Response As String = Nothing obj_BearerToken = New CCAPI_BearerToken Try s_Response = rest.FullRequestFormUrlEncoded("POST", APIPostCallPath) obj_BearerToken = JsonConvert.DeserializeObject(Of CCAPI_BearerToken)(s_Response, JSONSettings) Catch exc As Exception Console.WriteLine(exc.ToString) End Try If s_Response <> "" Then Dim json As JObject = JObject.Parse(s_Response) obj_BearerToken.TokenType = json.SelectToken("token_type") obj_BearerToken.Expiry = json.SelectToken("expires_in") obj_BearerToken.AccessToken = json.SelectToken("access_token") txtParams(0) = txt_status txtParams(1) = "Successfully obtained Bearer-Token..." & vbCrLf Me.Invoke(New WriteTextDelegate(AddressOf WriteText), txtParams) txt_bearertoken.Text = obj_BearerToken.AccessToken.ToString txtParams(0) = txt_status txtParams(1) = "-------------------------------" & vbCrLf & vbCrLf Me.Invoke(New WriteTextDelegate(AddressOf WriteText), txtParams) End If End Sub
Set all Export parameters and export Image from vSphere
Private Sub SetAllExportParameters(ByVal obj_CCAPI_Token As CCAPI_Token) s_APIBody = New Chilkat.StringBuilder s_APIBody.AppendLine("{", True) obj_AllExportParameters = New CCAPI_ExportParameters obj_AllExportParameters.outputStorageLocation = New CCAPI_OutputStorageLocation obj_AllExportParameters.vCenterPort = 443 s_APIBody.AppendLine((Chr(34) & "vCenterPort" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.vCenterPort & Chr(34) & ","), True) obj_AllExportParameters.vCenterHost = obj_Export.vCenterHost s_APIBody.AppendLine((Chr(34) & "vCenterHost" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.vCenterHost & Chr(34) & ","), True) obj_AllExportParameters.vCenterSslNoCheckHostname = True s_APIBody.AppendLine((Chr(34) & "vCenterSslNoCheckHostname" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.vCenterSslNoCheckHostname & Chr(34) & ","), True) obj_AllExportParameters.platformCredentialId = s_VSPCredID s_APIBody.AppendLine((Chr(34) & "platformCredentialId" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.platformCredentialId & Chr(34) & ","), True) obj_AllExportParameters.datacenter = obj_Export.datacenter s_APIBody.AppendLine((Chr(34) & "datacenter" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.datacenter & Chr(34) & ","), True) obj_AllExportParameters.network = obj_Export.network s_APIBody.AppendLine((Chr(34) & "network" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.network & Chr(34) & ","), True) obj_AllExportParameters.datastore = obj_Export.datastore s_APIBody.AppendLine((Chr(34) & "datastore" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.datastore & Chr(34) & ","), True) obj_AllExportParameters.cluster = obj_Export.cluster s_APIBody.AppendLine((Chr(34) & "cluster" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.cluster & Chr(34) & ","), True) obj_AllExportParameters.outputImageFilename = obj_Export.outputImageFilename s_APIBody.AppendLine((Chr(34) & "outputImageFilename" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.outputImageFilename & Chr(34) & ","), True) s_APIBody.AppendLine((Chr(34) & "outputStorageLocation" & Chr(34) & ": {"), True) obj_AllExportParameters.outputStorageLocation.credentialId = s_SMBCredID s_APIBody.AppendLine((Chr(34) & "credentialId" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.outputStorageLocation.credentialId & Chr(34) & ","), True) obj_AllExportParameters.outputStorageLocation.type = obj_Export.smb.type s_APIBody.AppendLine((Chr(34) & "type" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.outputStorageLocation.type & Chr(34) & ","), True) obj_AllExportParameters.outputStorageLocation.host = obj_Export.smb.host s_APIBody.AppendLine((Chr(34) & "host" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.outputStorageLocation.host & Chr(34) & ","), True) obj_AllExportParameters.outputStorageLocation.sharePath = obj_Export.smb.sharePath s_APIBody.AppendLine((Chr(34) & "sharePath" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.outputStorageLocation.sharePath & Chr(34)), True) s_APIBody.AppendLine(("},"), True) obj_AllExportParameters.platform = "vSphere" s_APIBody.AppendLine((Chr(34) & "platform" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.platform & Chr(34) & ","), True) obj_AllExportParameters.prefix = obj_Export.prefix s_APIBody.AppendLine((Chr(34) & "prefix" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.prefix & Chr(34) & ","), True) obj_AllExportParameters.resourceLocationId = s_RLID s_APIBody.AppendLine((Chr(34) & "resourceLocationId" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.resourceLocationId & Chr(34) & ","), True) obj_AllExportParameters.sourceDiskName = obj_Export.sourceDiskName s_APIBody.AppendLine((Chr(34) & "sourceDiskName" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.sourceDiskName & Chr(34) & ","), True) obj_AllExportParameters.timeoutInSeconds = obj_Export.timeoutInSeconds s_APIBody.AppendLine((Chr(34) & "timeoutInSeconds" & Chr(34) & ": " & Chr(34) & obj_AllExportParameters.timeoutInSeconds & Chr(34)), True) s_APIBody.AppendLine(("}"), True) End Sub Private Sub but_export_Click(sender As Object, e As EventArgs) Handles but_export.Click Dim rest As New Chilkat.Rest ' Connect to the CCAPI server. Dim bTls As Boolean = True Dim port As Integer = 443 Dim bAutoReconnect As Boolean = True Dim success As Boolean = rest.Connect("api.eu.layering.cloud.com", port, bTls, bAutoReconnect) If (success <> True) Then Debug.WriteLine(rest.LastErrorText) Exit Sub End If success = rest.AddHeader("Citrix-CustomerId", obj_CCAPI_Token.CustomerID) success = rest.AddHeader("Authorization", "CWSAuth bearer=" & obj_BearerToken.AccessToken.ToString) success = rest.AddHeader("Accept", "application/json") success = rest.AddHeader("Content-Type", "application/json") Dim APIPostCallPath As String = "/Images/$export?async=true" Dim s_Response As String = Nothing s_APIResponse = New Chilkat.StringBuilder Dim s_REsp As String Try success = rest.FullRequestSb("POST", APIPostCallPath, s_APIBody, s_APIResponse) s_REsp = s_APIResponse.GetAsString obj_ExportResponse = New CCAPI_ExportResponse obj_ExportResponse = JsonConvert.DeserializeObject(Of CCAPI_ExportResponse)(s_REsp, JSONSettings) ‘Enable timer to get progress of asynchronous process timer_progress.Enabled = True Catch exc As Exception Console.WriteLine(exc.ToString) End Try End Sub
Invoke PowerShell to upload Image to Azure
Private Sub Invoke_PowerShellToUpload() Dim sessionState = InitialSessionState.CreateDefault() sessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted Using powershell As PowerShell = PowerShell.Create(sessionState) powershell.AddCommand("Copy-DiskToAzure") powershell.AddParameter("Filename", obj_AllUploadParams.Filename) powershell.AddParameter("ManagedDiskName", obj_AllUploadParams.ManagedDiskname) powershell.AddParameter("SubscriptionId", obj_AllUploadParams.SubscriptionID) powershell.AddParameter("Location", obj_AllUploadParams.Location) powershell.AddParameter("ResourceGroup", obj_AllUploadParams.ResourceGroup) powershell.AddParameter("Timeout", obj_AllUploadParams.Timeout) lbl_upload.Text = "Uploading..." pb_upload.Value = 25 ***** SNippet mus be completed, sync to GitHub failed ***** End Using End Sub
Start preparation of Image
Private Sub but_prepareVHD_Click(sender As Object, e As EventArgs) Handles but_prepareVHD.Click Dim rest As New Chilkat.Rest ' Connect to the CCAPI server. Dim bTls As Boolean = True Dim port As Integer = 443 Dim bAutoReconnect As Boolean = True Dim success As Boolean = rest.Connect("api.eu.layering.cloud.com", port, bTls, bAutoReconnect) If (success <> True) Then Debug.WriteLine(rest.LastErrorText) Exit Sub End If success = rest.AddHeader("Citrix-CustomerId", obj_CCAPI_Token.CustomerID) success = rest.AddHeader("Authorization", "CWSAuth bearer=" & obj_BearerToken.AccessToken.ToString) success = rest.AddHeader("Accept", "application/json") success = rest.AddHeader("Content-Type", "application/json") Dim APIPostCallPath As String = "/images/$prepare?async=true" Dim s_Response As String = Nothing txtParams(0) = txt_status txtParams(1) = "Sending REST-Call to prepare Image to SMB share..." & vbCrLf Me.Invoke(New WriteTextDelegate(AddressOf WriteText), txtParams) s_APIResponse = New Chilkat.StringBuilder Dim s_REsp As String Try success = rest.FullRequestSb("POST", APIPostCallPath, s_APIBody, s_APIResponse) s_REsp = s_APIResponse.GetAsString obj_PrepareResponse = New CCAPI_PrepareResponse obj_PrepareResponse = JsonConvert.DeserializeObject(Of CCAPI_PrepareResponse)(s_REsp, JSONSettings) timer_prepare.Enabled = True Catch exc As Exception Console.WriteLine(exc.ToString) End Try End Sub
Public Sub GetPrepareProgress(ByVal obj_CCAPI_Token As CCAPI_Token, ByVal obj_PrepareResponse As CCAPI_PrepareResponse) pb_prepare.Value = 5 Dim rest As New Chilkat.Rest ' Connect to the CCAPI server. Dim bTls As Boolean = True Dim port As Integer = 443 Dim bAutoReconnect As Boolean = True Dim success As Boolean = rest.Connect("api.eu.layering.cloud.com", port, bTls, bAutoReconnect) If (success <> True) Then Debug.WriteLine(rest.LastErrorText) Exit Sub End If success = rest.AddHeader("Citrix-CustomerId", obj_CCAPI_Token.CustomerID) success = rest.AddHeader("Authorization", "CWSAuth bearer=" & obj_BearerToken.AccessToken.ToString) success = rest.AddHeader("Accept", "application/json") success = rest.AddHeader("Content-Type", "application/json") Dim APIPostCallPath As String = "/jobs/" & obj_PrepareResponse.id Dim s_Response As String = Nothing Dim i_Progress As Integer Try s_Response = rest.FullRequestFormUrlEncoded("GET", APIPostCallPath) 's_REsp = s_APIResponse.GetAsString obj_JobProgressResponse = New cc obj_JobProgressResponse = JsonConvert.DeserializeObject(Of CCAPI_JobProgressResponse)(s_Response, JSONSettings) 'GetExportProgress(obj_CCAPI_Token, obj_ExportResponse) i_OverallProgress = CInt(obj_JobProgressResponse.overallProgressPercent) If i_OverallProgress <> 0 Then pb_prepare.Value = i_OverallProgress End If s_ProgressStatus = obj_JobProgressResponse.status lbl_progressstatus.Text = "Progress-Status: " & s_ProgressStatus If obj_JobProgressResponse.error.Length <> 0 Then Dim s_error As String s_error = obj_JobProgressResponse.error(0).ToString pb_prepare.BackColor = Color.Red pb_prepare.ForeColor = Color.Red pb_prepare.Value = 100 lbl_progressstatus.Text = "Error - see logs!" timer_progress.Enabled = False txtParams(0) = txt_status txtParams(1) = "Export failed - see logs!..." & vbCrLf Me.Invoke(New WriteTextDelegate(AddressOf WriteText), txtParams) txtParams(0) = txt_status txtParams(1) = "-------------------------------" & vbCrLf & vbCrLf Me.Invoke(New WriteTextDelegate(AddressOf WriteText), txtParams) End If If s_ProgressStatus = "Complete" Then pb_prepare.Value = 100 lbl_progressstatus.Text = s_ProgressStatus timer_progress.Enabled = False txtParams(0) = txt_status txtParams(1) = "Export successful!" & vbCrLf Me.Invoke(New WriteTextDelegate(AddressOf WriteText), txtParams) txtParams(0) = txt_status txtParams(1) = "-------------------------------" & vbCrLf & vbCrLf Me.Invoke(New WriteTextDelegate(AddressOf WriteText), txtParams) End If ' s_ProgressError = obj_JobProgressResponse.error Catch exc As Exception Console.WriteLine(exc.ToString) End Try End Sub
There are no comments to display.
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now